Adam Coster
Adam Coster

Reputation: 1462

Why are PNG deflate-block lengths being ignored?

Summary: I need a PNG writer and for various reasons I had to make it from scratch. I don't need to compress the image data, so I implemented PNG using the no-compression deflate algorithm. Rendering fails for images requiring multiple deflate blocks, seemingly because it ignores block lengths.

Issue: When I have a tiny image written with a single deflate block, the resulting image is exactly what it is supposed to be (all pixels correct, no errors when checked with pngcheck). However, as soon as I use multiple deflate blocks the resulting image is wrong because deflate blocks are being read past their lengths (though pngcheck still shows no errors). Looking at the raw data in a hex editor seems to show that everything is to spec (according to libpng, RFC 1950, and RFC 1951).

Example Data: I generated a 3x1 RGBA image where the pixel colors are, from left to right, (fb,02,03,fa), (01,fc,03,fa), (01,02,fd,fa). I then wrote them to file with either an 8-byte or 64-byte maximum deflate block length. The 64-byte block length image is exactly correct and renders properly.

Complete image contents follow, as hex, where bold are the block lengths and italics are the block data.

8-byte block image (renders incorrectly):
89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52 00 00 00 03 00 00 00 01 08 06 00 00 00 1b e0 14 b4 00 00 00 1d 49 44 41 54 78 9c 0000 08ff f700 fb 02 03 fa 01 fc 030100 05ff fafa 01 02 fd fa05 ee ac ee a1 e1 2d b9 00 00 00 00 49 45 4e 44 ae 42 60 82

64-byte block image (renders correctly):
89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52 00 00 00 03 00 00 00 01 08 06 00 00 00 1b e0 14 b4 00 00 00 18 49 44 41 54 78 9c 0100 0dff f200 fb 02 03 fa 01 fc 03 fa 01 02 fd fa05 ee ac ee f8 dc a0 6c 00 00 00 00 49 45 4e 44 ae 42 60 82

The beginning of each deflate block consists of 5 bytes: 0 or 1 (depending on if it's the final block) followed by a 2-byte length (in bold above) and its 2-byte one's complement. In the 8-byte block image, the length for the first block is 8 just as it should be. The 8 bytes after the length complement (in italics above) are indeed the values they are supposed to be, and the 9th byte equals 1 (and starts the final block). However, that 1 is being interpreted as a color value (the alpha for the second pixel) instead of as the start of the next block!

Presumably there is something that I'm missing about the structure of deflate blocks. Am I being dumb and making a mistake, or did I misunderstand the specs?

Upvotes: 3

Views: 510

Answers (1)

Mark Adler
Mark Adler

Reputation: 112349

Both PNG files are invalid, even though one happens to render correctly (and even though pngcheck doesn't catch it). They both do not properly terminate the embedded deflate streams.

Looking at one of the deflate streams, the stored lengths and complements are big-endian instead of little endian. E.g. instead of 00 0d ff f2 it needs to be 0d 00 f2 ff.

Upvotes: 5

Related Questions