Search code examples
hexpngzlib

Issue with a hand-made PNG file


I have been trying for some time do create a PNG file "from scratch", that is to say to manipulate the data byte per byte in order to create a viable file.

During this quest of mine I have encountered a strange error, whenever the raw pixel data, along with the filter type byte, reaches a length of 256 the image fails to render and all the pixels become black. Just to clarify this doesn't seem to happen with other sizes and I have successfully created images "by hand".

Here is the HEX data of the image color coded to visualize the different chunks: HEX data

Just so that we are on the same page I am going the write an explanation for the contents of every chunk.

PNG INITIAL BYTES

  • 0x89 0x50 0x4e 0x47 0x0d 0x0a 0x1a 0x0a theese bytes must be at the start of a PNG file to identify it as such.

IHDR CHUNK

  • 0x00 0x00 0x00 0x0d the value of theese bytes is the length of the data contained in the data part of the chunk.
  • 0x49 0x48 0x44 0x52 these bytes are the identifier of the IHDR chunk.
  • 0x00 0x00 0x00 0xff the value of these bytes is the width of the image in pixel (255).
  • 0x00 0x00 0x00 0x01 the value of these bytes is the height of the image in pixel (1).
  • 0x08 the value of this byte is the bit depth of each color channel present in a pixel (8 bit).
  • 0x00 this byte represents the color type of the image, in this case a simple, one channel, grayscale.
  • 0x00 this byte represents the compression method of the image, 0x00 is the only possible value.
  • 0x00 this byte represents the filter method of the image, 0x00 is the only possible value.
  • 0x00 this byte represents the interlacing method, a value of 0x00 means no interlacing.
  • 0x34 0xfb 0x27 0x7f these bytes are the CRC-32 value of the bytes going from the start of the identifier to here.

IDAT CHUNK

  • 0x00 0x00 0x01 0x0b the value of these bytes is the length of the data contained in the data part of the chunk.
  • 0x49 0x44 0x41 0x54 these bytes are the identifier of the IDAT chunk.
  • 0x08 this byte represents the CMF of the zLib compressed data format, specified here, I use a sliding window of 256.
  • 0x1d this byte represents the FLG of the zLib compressed data format specified, here, I use an FLEVEL of 0, no FDICT.
  • 0x80 this byte represents the fact that this is the last compressed data chunk to process, since it's the only one and that no compression is applied.
  • 0x01 0x00 the value of these bytes is the length of the compressed data (256) filter type byte + raw pixel data.
  • 0xfe 0xff these bytes are the negation of the previous bytes.
  • 0x00 this byte represents the filter type used for this scanline, 0x00 means no filter.
  • from 0xff to the end of address 00000120 we have the actual pixel data, since the bit depth is 8 and the color type is grayscale each 0xff is a white pixel.
  • 0x08 0xf1 0xfe 0x02 these bytes are the Adler-32 value of the bytes going from the start of adress 00000030 to the end of the adress 00000120.
  • 0x33 0xed 0xdb 0x91 these bytes are the CRC-32 value of the HEXs going from the start of the identifier to here.

IEND CHUNK

  • 0x00 0x00 0x00 0x00 the value of these bytes is the length of the data contained in the data part of the chunk. There is no data in the IEND chunk.
  • 0x49 0x45 0x4e 0x44 these bytes are the identifier of the IEND chunk.
  • 0xae 0x42 0x60 0x82 these bytes are the CRC-32 value of the bytes of just the identifier, since there is no data.

I tried to change the color type and the bit depth but as long as the sum of the compressed, actually not compressed, data amounts to 256 the file doesn't render correctly and just replaces every pixel with black ones. I tried every possible window, from 256 to 32768, and nothing changed.

Maybe I need to include some ancillary chunk like sRGB, even tough I have no idea why that should help.


Solution

  • 0x80 this byte represents the fact that this is the last compressed data chunk to process

    No, it does not. The deflate bits start at the bottom, not the top. To do what you describe, that byte needs to be 0x01.

    0x01 0x00 the value of these bytes is the length of the compressed data (256)

    No, it is not. The length is represented in little-endian order, and so 256 would be 0x00 0x01.

    I have successfully created images "by hand".

    Note that just because an image renders does not mean that the PNG file is correct. Software that is trying to display PNG images can choose to be quite liberal in accepting erroneous input. Instead, consider using something like pngcheck to validate your manually-constructed PNG files. Your previous attempts undoubtedly had errors as well. What happened is that you eventually exceeded the ability of the PNG decoder to make some sort of sense of what you were giving it.