Search code examples
macossafaripngphotoshopzopfli

PNG format images do not display on Mac Safari


Images from our website do not display in Safari for some Mac users and they report seeing either no image or a black image. Here is an example:

http://s3-eu-west-2.amazonaws.com/bp18.boxcleverpress.com/Boxclever_logo_chartreuse.png

What I have discovered is:

  • Images display on PC
  • Images display on SOME Macs (I have an older one that is OK)
  • Images display on iPhones and iPads
  • Images are PNG
  • I have optimised the images with pngtastic
  • When images are copied to the Mac and opened with Adobe Photoshop they give the error: the file format module cannot parse the file
  • When I tried to open a pngtastic optimised file in Photoshop Elements on Windows I also get that error
  • When I tried to open the optimised file in Photoshop on Windows I get the error IDAT: incorrect data check

I will replace the optimised images with unoptimised ones but I am not sure if this problem is with pngtastic or Adobe image libraries or something else.


Solution

  • The problem lies in Zopfli.java, included by pngtastic.

    It uses this Java code to calculate the Adler-32 checksum:

    /**
     * Calculates the adler32 checksum of the data
     */
    private static int adler32(byte[] data) {
        int s1 = 1;
        int s2 = 1 >> 16;
        int i = 0;
        while (i < data.length) {
            int tick = Math.min(data.length, i + 1024);
            while (i < tick) {
                s1 += data[i++];
                s2 += s1;
            }
            s1 %= 65521;
            s2 %= 65521;
        }
    
        return (s2 << 16) | s1;
    }
    

    However, bytes in Java are always signed, and so it may return a wrong checksum value for some data inputs. Also, the bare int declarations for s1 and s2 cause further complications.

    With (my C version of) the same code and data explicitly declared as signed char and both s1 and s2 as signed int, I get a wrong checksum FFFF9180 – exactly the one in your damaged PNG.

    If I change the declaration to use unsigned char and unsigned int, it returns the correct checksum 1BCD6EB2 again.


    The original C code for the Adler-32 checksum in zopfli uses unsigned types throughout, so it's just the Java implementation that suffers from this.