Yesterday I wanted to create a small PNG image to replace the crude textual version of the 'dragon' logo:
_ _
_| |_|_|
|_ |_
|_|
My first attempt was to draw the logo in OmniGraffle and then to export it as a PNG file. The result was less than great: OmniGraffle generated a 3 kilobyte PNG file with anti-aliased lines.
This logo shouldn't need to be larger than half a kilobyte. It could probably be even be much smaller than that, so I thought. Today I decided I wanted to solve it once and for all instead of keeping it a todo item. I recalled that there are programs to reduce the size of PGN files. Google found 'pngcrush' for me. Unfortunately it could only reduce the file size to something like 2,800 bytes.
Thinking the size was caused by the grayscales in the image, I downloaded Paintbrush and just redrew the logo pixel by pixel. Exporting to PNG from Painbrush still yielded a similar-sized file. What is going on? Let's take a peek inside. I downloaded 'pngcheck' to dump the file contents:
marcelk@solar:~/appl> ./pngcheck -v logo.png File: logo.png (3118 bytes) chunk IHDR at offset 0x0000c, length 13 77 x 47 image, 24-bit RGB, non-interlaced chunk iCCP at offset 0x00025, length 2884 profile name = ICC Profile, compression method = 0 (deflate) compressed profile = 2871 bytes chunk IDAT at offset 0x00b75, length 165 zlib: deflated, 16K window, default compression chunk IEND at offset 0x00c26, length 0 No errors detected in logo.png (4 chunks, 71.3% compression). marcelk@solar:~/appl>
Uh oh, these applications generate a bloated 'iCCP' chunck. I couldn't find a setting to bypass this, so I decided to generate the PNG myself, more or less from scratch (see the C source). Here is the result:
00000000 89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52 |.PNG........IHDR| 00000010 00 00 00 4d 00 00 00 2f 01 03 00 00 00 9c fb d5 |...M.../........| 00000020 a2 00 00 00 06 50 4c 54 45 ff ff ff 40 40 40 a3 |.....PLTE...@@@.| 00000030 fd 50 2b 00 00 00 01 74 52 4e 53 00 40 e6 d8 66 |.P+....tRNS.@..f| 00000040 00 00 00 54 49 44 41 54 28 cf 63 62 60 fc ff 9f |...TIDAT(.cb`...| 00000050 81 fd ff 1f 06 06 26 06 38 00 32 1b 59 18 fe 33 |......&.8.2.Y..3| 00000060 0a a0 8b 0e 10 f3 ff 3f 06 86 ff 3f ff 7f 40 15 |.......?...?..@.| 00000070 3d c8 d8 f0 97 91 9d 91 65 90 38 d2 fe 7f 03 03 |=.......e.8.....| 00000080 30 34 ff 30 48 20 8b 32 32 02 45 7f 0e 9a 90 84 |04.0H .22.E.....| 00000090 d2 cc ff 1f 20 89 02 00 1e 02 15 65 83 3b f9 20 |.... ......e.;. | 000000a0 00 00 00 00 49 45 4e 44 ae 42 60 82 |....IEND.B`.| 000000acmarcelk@solar:~/appl/pngcheck> ./pngcheck -v ~/dragon-logo.png File: /home/marcelk/dragon-logo.png (172 bytes) chunk IHDR at offset 0x0000c, length 13 77 x 47 image, 1-bit palette, non-interlaced chunk PLTE at offset 0x00025, length 6: 2 palette entries chunk tRNS at offset 0x00037, length 1: 1 transparency entry chunk IDAT at offset 0x00044, length 84 zlib: deflated, 1K window, maximum compression chunk IEND at offset 0x000a4, length 0 No errors detected in /home/marcelk/dragon-logo.png (5 chunks, 63.4% compression). marcelk@solar:~/appl/pngcheck>
Smaller than the favicon! I like it. Plus, the exercise tought me how to use:
Pngcrunch managed to squeeze out 20 more bytes, from 172 to 152:
pngcrunch
| pngcrush 1.6.4
| Copyright (C) 1998-2002,2006 Glenn Randers-Pehrson
| Copyright (C) 2005 Greg Roelofs
| This is a free, open-source program. Permission is irrevocably
| granted to everyone to use this version of pngcrush without
| payment of any fee.
| Executable name is pngcrush
| It was built with libpng version 1.2.11beta4, and is
| running with libpng version 1.2.11beta4 - June 7, 2006 (header)
| Copyright (C) 1998-2004,2006 Glenn Randers-Pehrson,
| Copyright (C) 1996, 1997 Andreas Dilger,
| Copyright (C) 1995, Guy Eric Schalnat, Group 42 Inc.,
| and zlib version 1.2.3, Copyright (C) 1998-2002 (or later),
| Jean-loup Gailly and Mark Adler.
| It was compiled with gcc version 4.0.0 (Apple Computer, Inc. build 5026) and gas version .
Recompressing dragon-logo.png
Total length of data found in IDAT chunks = 84
unknown chunk handling done.
IDAT length with method 11 (fm 0 zl 2 zs 2) = 164
IDAT length with method 12 (fm 1 zl 2 zs 2) = 241
IDAT length with method 13 (fm 2 zl 2 zs 2) = 129
[... snip ...]
IDAT length with method 59 (fm 0 zl 4 zs 1) = 64
[... snip ...]
IDAT length with method 134 (fm 3 zl 4 zs 3) = 268
IDAT length with method 135 (fm 4 zl 4 zs 3) = 106
IDAT length with method 136 (fm 5 zl 4 zs 3) = 105
Best pngcrush method = 59 (fm 0 zl 4 zs 1) for out.png
(23.81% IDAT reduction)
(11.63% filesize reduction)
CPU time used = 0.400 seconds (decoding 0.020,
encoding 0.090, other 0.290 seconds)
pngcheck
File: out.png (152 bytes )
chunk IHDR at offset 0x0000c, length 13
77 x 47 image, 1-bit palette, non-interlaced
chunk PLTE at offset 0x00025, length 6: 2 palette entries
chunk tRNS at offset 0x00037, length 1: 1 transparency entry
chunk IDAT at offset 0x00044, length 64
zlib: deflated, 1K window, fast compression
chunk IEND at offset 0x00090, length 0
No errors detected in out.png (5 chunks, 67.7% compression).
marcelk@solar:~>
hexdump
00000000 89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52 |.PNG........IHDR|
00000010 00 00 00 4d 00 00 00 2f 01 03 00 00 00 9c fb d5 |...M.../........|
00000020 a2 00 00 00 06 50 4c 54 45 ff ff ff 40 40 40 a3 |.....PLTE...@@@.|
00000030 fd 50 2b 00 00 00 01 74 52 4e 53 00 40 e6 d8 66 |.P+....tRNS.@..f|
00000040 00 00 00 40 49 44 41 54 28 53 cd c6 b1 09 c0 30 |...@IDAT(S.....0|
00000050 0c 00 c1 0f 06 bb 31 a4 f5 b8 1a 2d 63 a5 10 7c |......1....-c..||
00000060 9a 14 f6 06 ba ea e0 52 86 09 1c 8d 46 67 16 a9 |.......R....Fg..|
00000070 46 53 f3 ec 03 d0 a1 48 35 60 98 ac bd bf 59 ab |FS.....H5`....Y.|
00000080 b7 ef d6 0f 3c 21 35 b3 bb 50 03 f1 00 00 00 00 |....<!5..P......|
00000090 49 45 4e 44 ae 42 60 82 |IEND.B`.|
00000098