Skip to content

fdEC PNG chunk definition

Rich Geldreich edited this page Jan 5, 2022 · 60 revisions

fpng writes PNG files containing a special "fdEC" chunk that the decompressor looks for before it tries to decompress the file. This chunk indicates that the file follows a number of constraints which allow for fast Huffman decoding and less overall Huffman symbols to decode (all Deflate distance codes are always 1-bit with a value of 0), for ~2.5-3x faster decompression.

This chunk is marked as "ancillary", currently "private", and "do not copy" (see the PNG spec here). Other PNG readers will ignore this chunk, but fpng will only decompress PNG files that have this chunk and don't violate any of the implied constraints. Any PNG file that contains this chunk and follow these constraints will be compatible with fpng's fast decompressor.

The bytes of this PNG chunk (including the PNG chunk length/type and CRC-32 fields) are (from first to last in sequence, i.e. the first three bytes are 0,0,0):

0, 0, 0, 5, 
'f', 'd', 'E', 'C', 
82, 36, 147, 227, FPNG_FDEC_VERSION,   
0xE5, 0xAB, 0x62, 0x99

The length field is 5 bytes. The first four bytes of the chunk's data field are a four byte random number (82, 36, 147, 227), and the 5th byte is FPNG_FDEC_VERSION, which is currently always 0x00.

The fdEC chunk must appear before the first IDAT chunk, and can only appear once.

This chunk indicates that the PNG file conforms to the following constraints:

  • colour type must be either 2 (truecolor) or 6 (truecolor with alpha)
  • bit depth must be 8
  • compression, filter, and interlace methods must all be 0
  • No tRNS (transparency) chunk (the decompressor currently ignores it, and the compressor never writes it).
  • Only one IDAT chunk

The compressed zlib stream must conform to the following constraints:

  • The zlib stream (which appears at the start of the IDAT chunk) must start with the bytes 0x78, 0x01
  • The Deflate stream can only utilize one or more raw (uncompressed) blocks (BTYPE=0), or only a single dynamic block (BTYPE=2). Static or "fixed" Huffman tables (BTYPE=1) are not supported.

Constraints for uncompressed Huffman blocks:

  • All PNG filter bytes (that proceed each scanline) must be 0 (no filter).
  • All Deflate blocks in the zlib stream must be uncompressed blocks (i.e. mixing dynamic/raw blocks is not supported)

Constraints for the single dynamic Huffman block:

  • The first scanline's PNG filter byte must be 0 (no filtering). Subsequent scanlines must use PNG filter #2 (previous scanline).
  • A single LZ literal must be used to encode the PNG filter byte that proceeds each scanline's compressed data (i.e. it cannot be emitted using an LZ match).
  • Pixels must be coded using groups of 3 (24-bpp) or 4 (32-bpp) LZ literals only. LZ matches may never be used to partially code pixels.
  • The maximum permitted Huffman code size is 12 bits, not 15 bits as in standard Deflate. Use Length-Limited Prefix Codes.
  • The LZ match length Huffman symbol table cannot have non-zero entries for unsupported match lengths

For 24-bpp files, match lengths 4,5,7,10 must be unused (i.e. have code lengths of 0 bits).

For 32-bpp files, match lengths 3,5,6,7,9,10 must be unused.

  • The LZ match distance Huffman symbol table can only have one non-zero code length. For 24-bpp files, the distance symbol corresponding to match distance 3 must be 1 bit, and for 32-bpp files the symbol for distance 4 must be 1 bit. The value of this symbol is always 0, so an encoder can always write a single 0 bit for the match distance and the decoder can just skip the bit.

Update: wuffs decoder currently has a bug and won't accept distance tables with only one valid distance code (which is valid according to the Deflate spec). As a workaround, fpng now writes distance tables with two valid codes, and the Huffman code length for the only distance actually used (either 3 or 4) is always 1-bit and "0".

  • The first pixel of each scanline must be coded using 3 or 4 LZ literals, never a match.
  • LZ matches only occur against the previous pixel, using a match distance of 3 or 4 bytes only, and must be a multiple of 3/4 bytes.
  • LZ matches cannot cross scanlines.
  • The maximum match length is 258 bytes (86 pixels) for 24-bpp files, and 256 (64 pixels) for 32-bpp files. Note the current fpng encoder uses a maximum match length of 255 bytes (85 pixels) for 24-bpp files, and 252 bytes (63 pixels) for 32-bpp files.

fpng's decompressor validates all of these constraints during decompression even if the fdEC market is present, and if any of them are violated it will return FPNG_DECODE_NOT_FPNG. A fully compliant/general purpose PNG decoder should be used if the file violates any of these constraints.

Example pngcheck output on a 24-bpp fpng file (from the downsized version of "bridge" in the README.md file):

File: j:\dev\example.png (1479224 bytes)
  chunk IHDR at offset 0x0000c, length 13
    687 x 1012 image, 24-bit RGB, non-interlaced
  chunk fdEC at offset 0x00025, length 5
    unknown private, ancillary, unsafe-to-copy chunk
  chunk IDAT at offset 0x00036, length 1479150
    zlib: deflated, 32K window, superfast compression
    row filters (0 none, 1 sub, 2 up, 3 avg, 4 paeth):
      0 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
      2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
      2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
      2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
      2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
      2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
      2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
      2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
      2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
      2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
      2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
      2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
      2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
      2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
      2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
      2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
      2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
      2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
      2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
      2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
      2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
      2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
      2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
      2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
      2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
      2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
      2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
      2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
      2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
      2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
      2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
      2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
      2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
      2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
      2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
      2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
      2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
      2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
      2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
      2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
      2 2 2 2 2 2 2 2 2 2 2 2 (1012 out of 1012)
  chunk IEND at offset 0x169230, length 0
No errors detected in j:\dev\example.png (4 chunks, 29.1% compression).

License

This file uses the unlicense.

Clone this wiki locally