PNG hacking

Logo in OmniGraffle

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`.|
000000ac

Final result

marcelk@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:


Update: 2007-11-23 (marcelk) Some more improvement

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

Last update: 2007-11-23 (marcelk) Some more improvement