Most recent update (All By Hand(TM)): 31-Mar-2017 23:27

 

 

It's not who you are it's who you know
Others lives are the basis of your own
Burn your bridges build them back with wealth
Judge not lest ye be judged yourself
Metallica, Holier Than Thou

 

 

pngdefry ‒ Repairing -iPhone fried PNGs

PNG images for use in iPhone and iPad applications may have been optimized for performance with Apple's proprietary version of the public domain pngcrush tool. However, these modifications essentially invalidate the image as a PNG, according to "W3C: Portable Network Graphics (PNG) Specification":

 

  • A private yet critical data chunk named CgBI is inserted.
    A public chunk is one that is part of the PNG specification or is registered in the list of PNG special-purpose public chunk types. (3.3. Chunk naming conventions)
  • This chunk is put before the IHDR chunk.
    Chunks can appear in any order, subject to the restrictions placed on each chunk type. (One notable restriction is that IHDR must appear first and IEND must appear last; thus the IEND chunk serves as an end-of-file marker.) (3.2. Chunk layout)
  • The compressed image data stored in IDAT chunks is missing the zlib compression header and Adler-32 checksum.

    Deflate-compressed datastreams within PNG are stored in the "zlib" format, which has the structure:

    Compression method/flags code: 1 byte
    Additional flags/check bits:   1 byte
    Compressed data blocks:        n bytes
    Check value:                   4 bytes

    (5. Deflate/Inflate Compression)

  • Eight-bit true color images are stored in BGR/BGRA order, rather than in RGB and RGBA order as indicated in the IHDR chunk.

    Each pixel is an R,G,B triple.

    ...

    Each pixel is an R,G,B triple, followed by an alpha sample.

    (4.1.1. IHDR Image header)
  • Image pixels use premultiplied alpha.
    The color values stored for a pixel are not affected by the alpha value assigned to the pixel. [...] ([...] PNG does not use premultiplied alpha.) (2.4. Alpha channel)
  • The modified files use the file extension .png as well as the internal file structure as defined for valid images, but conforming PNG viewing and editing software is no longer capable of processing them.
    The PNG format provides a portable, legally unencumbered, well-compressed, well-specified standard for lossless bitmapped image files. (1. Introduction)

 

pngdefry converts a non-conforming PNG image into a conforming one by making the following changes:

  • the chunk at the start named CgBI is discarded
  • BGR(A) ordered pixel data is swapped into RGB(A) order
  • pre-multiplied alpha is converted to regular alpha
  • IDAT chunks are recompressed, adding zlib header and Adler-32 checksums
  • CRC32 checksums are recalculated (note: for all of the chunks, not only the modified ones)

 

pngdefry will not process the file if it does not contain a chunk named CGbI as its very first one (unless it's forced to by a command line option). A file that is not processed does not get written to a new file. When forced to process a file, the RGB color ordering is not changed but IDAT chunks are still uncompressed then recompressed, and the output file is created as usual (note that this will almost certainly lead to a different file size).

Only IDAT chunks are modified, all others are copied as-is and are not checked for validity. pngdefry does some low-level integrity checking on the basic file structure (the PNG identification string, chunk lengths, IHDR length and values, IDAT chunk order, IEND at the very end). At that point the file may be rejected as a valid PNG and you should check it using pngcheck. If the file passes basic validity checks, an attempt is made to decompress the IDAT chunks. If this succeeds and the decompressed size is as expected by the image dimensions, color mode, and interlacing as specified by the image header IHDR, pixel color values are swapped, alpha is corrected, and the file is re-compressed and written to output. No further validity checks are done.

About pre-multiplied alpha

Reversing the order of colors in an -iphone optimized image is not enough to make it fully conform to the PNG standard. The color pixels have pre-multiplied alpha, and if the display software is not aware of this, it renders the image with the alpha applied again. This is visible as a dark fringe in the transparent areas. pngdefry removes the alpha premultiplication to prevent this.

(a) original image (b) -iphone optimized (c) defried, not alpha adjusted (d) defried, alpha adjusted

(a) original image (taken from Wikipedia on Portable Network Graphics; alpha transparency has been added to the bottom color bar).

(b) image after running Apple's pngcrush -iphone png-gradient.png png-gradient_iphone.png. Since it's an invalid PNG, this image will typically not be visible on your screen. (A notable expection is when you are using Safari on an iPad.)

(c) defried image, forced not to repair the alpha compositing. Note the black shadow where alpha transparency was used.

(d) defried image. Correct alpha compositing is restored.

Reversing pre-multiplied alpha to the regular kind is not an exact science ‒ you do not get the exact image back as it was before optimization. The error is something like (1/alpha)/2 ‒ so the biggest rounding errors are in the lowest alpha values, something you can see with software but hopefully not with the naked eye.

Other tools

I am aware of several other tools to repair iPhone optimized PNG images. However, as of this date (21-Jan-2012) they have some drawbacks, which I attempted to address with my own version.

pngcrush (as modified by Apple)

This is not the original pngcrush by Glenn Randers-Pehrson (http://pmt.sourceforge.net/pngcrush/) ‒ even though it still says it is ‒, this version has been modified by Apple and the modified source code is not in the public domain. It is included in Apple's iOS Developer SDK package and has the option -revert-iphone-optimizations. For this you need to download and install the iOS SDK.

Graphic Converter

This program by Lemkesoft GmbH can handle dozens of different file formats; however, the version I checked (7.6) does not re-order colors and it cannot handle multiple IDAT chunks. It does not fix pre-multiplied alpha.

ipin.py

A Python script by Axel E. Brzostowski (http://www.axelbrz.com.ar/) that removes the CgBI chunk, decompresses IDAT chunks, reverses color order, and recompresses them. It cannot handle multiple IDAT chunks, does not work with Adam7 interlaced images, and does not fix pre-multiplied alpha.

Using pngdefry

pngdefry options filename [filename ...]

All options should appear before the first filename. Wildcard expansion for filenames is supported.

  • -ssuffix    append suffix to the input file name before the file extension .png
  • -opath    write output file(s) to path
  • -a    do NOT fix pre-multiplied alpha; default is it does
  • -l    (lowercase 'L') list all chunks
  • -v    verbose processing
  • -ivalue    max IDAT chunk size in bytes (minimum: 1024; default: 524288)
  • -p    process all files, not just -iphone ones (for debugging purposed only)
  • -d    very verbose processing (for debugging purposes only)
  • -C    ignore bad CRC32 (recommended: do NOT use this, as a bad CRC32 may indicate a deliberately damaged file)

Note: without -s or -o, NO output will be created; files will still be processed but not written back to disk.

The options -s, -o, and -i support both styles of input: -osome_path is equal to -o some_path.

Some examples

Commands that were entered in Terminal are shown in bold.

$ pngdefry image.png
image.png : not an -iphone crushed PNG file
pngdefry : seen 1 file(s), wrote 0 file(s)
$ pngdefry iphoneimage.png
iphoneimage.png
pngdefry : seen 1 file(s), wrote 0 file(s)
$ pngdefry -s _test iphoneimage.png
iphoneimage.png : writing to file iphoneimage_test.png
pngdefry : seen 1 file(s), wrote 1 file(s)
$ pngdefry -s _test iphoneimage.png
iphoneimage.png : writing to file iphoneimage_test.png
pngdefry : seen 1 file(s), wrote 1 file(s)
$ mkdir unpacked
$ pngdefry -l -i1024 -ounpacked -s_unpacked iphoneimage.png
iphoneimage.png :
    chunk : CgBI  length      4  CRC32 10F3447C
    chunk : IHDR  length     13  CRC32 8656CF8C
    chunk : iCCP  length   2633  CRC32 D91FEE1D
    chunk : cHRM  length     32  CRC32 925FC546
    chunk : pHYs  length      9  CRC32 009A9C18
    chunk : IDAT  length   2124  CRC32 A3342117
    chunk : IEND  length      0  CRC32 AE426082
writing to file unpacked/iphoneimage_unpacked.png
pngdefry : seen 1 file(s), wrote 1 file(s)
$ pngdefry -ounpacked *.png
image_1.png : writing to file unpacked/image_1.png
image_2.png : not an -iphone crushed PNG file
image_3.png : writing to file unpacked/image_3.png
image_4.png : writing to file unpacked/image_4.png
pngdefry : seen 4 file(s), wrote 3 file(s)

 

Downloading pngdefry

Download location: pngdefry.zip (108KB; v1.2, dated 1-Apr-2017)

 

The ZIP file contains the following items:

  • bin/pngdefry, the precompiled executable for Mac OS X 10.7.2 (Lion)
  • man/pngdefry.1, a man page I wrote up quickly using ManDrake
  • source/pngdefry.c, the C source code
  • source/miniz.c, Rich Geldreich's C source for compressing and decompressing zlib files

Notes

The precompiled exectuable works correctly under Mac OS X 7.2 (Lion). I have no idea if it works under other versions; if it doesn't, well, the source code is also provided. The code has been written to be used under Mac OS X's Terminal and uses Unix-style headers. If you are going to re-compile it under Windows, you need to adjust the headers and add Unix-style command line processing.

 

This source code uses miniz.c by Rich Geldreich (for more information see http://code.google.com/p/miniz/). It should be possible to use standard zlib, as well as other zip capable libraries, as long as it can work around the missing compressed header information.

 

Warning: this program may create an invalid image, up to and including an entirely empty file. It is possible to use the command line options to overwrite the original file, but I strongly recommend not to do this. If you do and it fails, your original file will be lost.

Issues fixed in v1.2

pngdefry works correctly with well-formed PNG files (in the sense that it can correct the issues reported above), but it has been brought to my attention that deliberately malformed PNG files may cause unexpected crashes, and are likely to expose your system to vulnerabilities. Although it is beyond pngdefry's main purpose to check well-formedness, and there are other tools to check validity of PNG files, the following issues have been addressed in v1.2:

  • Reading a totally empty chunk was considered a valid "end of file", and reported to be okay. However, there should always be at least one chunk.
  • A bad CRC32 may be reported (if -v was used) but then fixed silently. Per recommendation in the specifications,
    It is strongly recommended that decoders should verify the CRC on each chunk. (http://www.w3.org/TR/PNG-Decoders.html)
    a bad CRC32 is reported and the file rejected by default. Following Andrew Udvare ("Tatsh")'s suggestion, a new flag -C has been added to optionally ignore bad CRC32's, but it must be noted this is not recommended.
  • An unsigned integer overflow may occur on very wide images. The width of an image is multiplied with the number of bits per pixel, and then divided by 8, to get the number of bytes per line. An overflow could occur when bpp * width was larger than a 32-bit wide MAX_UINT. This has been addressed by decreasing the maximum allowed image width to 67108863 pixels. A proper fix would have been to allow for a 64-bit wide calculation, but the current maximum width should be totally adequate for the intended use.

 

 


[Jongware]

iPhone and iPad are registered trademarks of Apple, Inc.