A PNG’s pixels are stored in two steps. Each scanline is first filtered, which turns smooth areas into small numbers, and the filtered scanlines are then compressed with Deflate. Filtering is lossless, and neither step changes the pixels a reader ends up with.
The zlib stream
All the IDAT chunks of an image hold a single zlib stream (RFC 1950), split wherever the encoder chose. The chunk boundaries mean nothing to the compression: a decoder joins the chunks’ data, reads the two-byte zlib header and inflates the rest. The stream decompresses to the filtered scanlines, top row first, and ends with an Adler-32 of them.
An interlaced image (Adam7) is seven smaller images one after another, each with its own scanlines, and each starting afresh with no row above it.
Scanline filters
Each scanline starts with one byte, its filter type, and then the row’s bytes with that
filter applied. The filter works byte by byte, from the corresponding byte of the pixel
to the left (a), the byte above (b) and the byte up and to the left
(c). Bytes from outside the image, before the first pixel or above the first row,
count as zero. The comparison is done on bytes, so 16-bit and multi-channel pixels work the
same way, with the corresponding byte of the previous pixel used for a
and c.
| Type | Name | Filtered byte | Unfiltered byte |
|---|---|---|---|
| 0 | None | x | x |
| 1 | Sub | x − a | x + a |
| 2 | Up | x − b | x + b |
| 3 | Average | x − ⌊(a + b) ÷ 2⌋ | x + ⌊(a + b) ÷ 2⌋ |
| 4 | Paeth | x − Paeth(a, b, c) | x + Paeth(a, b, c) |
All of these are modulo 256. The Paeth predictor picks whichever of a,
b and c is closest to a + b − c, preferring
a, then b, on a tie.
An encoder can choose a different filter for every row. Choosing well matters more to the file’s size than Deflate’s settings do: a good row-by-row choice groups values near zero, which Deflate compresses well.
Looking at it in the explorer
In a PNG opened in the explorer, the compression table at the bottom of the Image tab has a Show button for the IDAT stream. It lists the blocks and tokens, the Huffman codes, what each output byte cost, and for every row its filter and its bytes before and after filtering.