PNG 解码的每一步 (2022)
Each step of decoding a PNG (2022)

原始链接: https://iter.ca/post/png/

PNG 文件由块组成,每个块包含长度、类型、数据和 CRC。关键块包括 IHDR(图像元数据)、IDAT(压缩图像数据)和 IEND(结束标记)。解码时,拼接所有 IDAT 数据并用 zlib 解压,得到带滤波的扫描行。每行扫描行以一个滤波字节开头;反滤波将其还原以恢复像素。交错 PNG 使用 Adam7 存储七次逐步更详细的遍历。块名称编码块是关键/辅助、公开/私有,以及是否可安全复制。示例是一个 256×256 的真彩色 RGB 图像。

黑客新闻 新 | 过去 | 评论 | 提问 | 展示 | 工作 | 提交 登录 解码 PNG 的每一步 (2022) (iter.ca) 5 分 由 jxmorris12 1 小时前 | 隐藏 | 过去 | 收藏 | 1 条评论 帮助 ranger_danger 23 分钟前 [–] 我觉得如果有这样一个网站,收集许多类似这样的页面,涵盖多种不同格式,会是个很棒的想法(如果它还不存在的话)。 回复 考虑申请 YC 2027 年冬季批次!申请开放至 11 月 2 日。 指南 | 常见问题 | 列表 | API | 安全 | 法律 | 申请 YC | 联系 搜索:
相关文章

原文

Let's look at the complete process of decoding a PNG file. Here's the image I'll be using to demonstrate:

Several letter 'S's on top of each other, each with a different color.

Chunking

PNG files are stored as a series of chunks. Each chunk has a 4 byte length, 4 byte name, the actual data, and a CRC. Most of the chunks contain metadata about the image, but the IDAT chunk contains the actual compressed image data. The capitalization of each letter of the chunk name determines how unexpected chunks should be handled:

UPPERCASElowercase
CriticalAncillaryIs this chunk required to display the image correctly?
PublicPrivateIs this a private, application-specific, chunk?
ReservedReserved
Unsafe to copySafe to copyDo programs that modify the PNG data need to understand this chunk?
c: Ancillary, H: Public, R: Reserved, M: Unsafe to copy

Here are the chunks in the image:

This identifies the image as a PNG. It contains newline characters and other characters that often get mangled to ensure that the binary image data didn't get corrupted.

89 50 4E 47 0D 0A 1A 0A

This contains metadata about the image.

Length 00 00 00 0D = 13 bytes long

Type 49 48 44 52 = IHDR

Data 00 00 01 00 00 00 01 00 08 02 00 00 00
Width256
Height256
Bit depth8
Color spaceTruecolor RGB
Compression method0
Fitler method0
Interlacingdisabled

CRC D3 10 3F 31

This contains the compressed image data.

Length 00 00 F5 51 = 62801 bytes long

Type 49 44 41 54 = IDAT

Data ... (the compressed and filtered bytes of the image)

CRC BF EB 1B 15

This empty chunk indicates the end of the PNG file.

Length00 00 00 00 = 0 bytes long

Type49 45 4E 44 = IEND

CRCAE 42 60 82

Extracting the image data

We take all of the IDAT chunks and concatenate them together. In this image, there is only one IDAT chunk. Some images have multiple IDAT chunks, and the contents are concatenated together by the PNG decoder. This is so streaming encoders don't need to know the total data length up front since the data length is at the beginning of each chunk. Multiple IDAT chunks are also needed for encoding images with image data having a length longer than the largest possible chunk size (2554) Here's what we get if treat the compressed data as raw image data:

Random noise on the top third, with the rest black.

It looks like random noise, and that means that the compression algorithm did a good job. Compressed data should look higher-entropy than the lower-entropy data it is encoding. Also, it's less than a third of the height of the actual image: that's some good compression!

Decompressing

The most important chunk is the IDAT chunk, which contains the actual image data. To get the filtered image data, we concatenate the data in the IDAT chunks, and decompress it using zlib. There is no image-specific compression mechanism in play here: just normal zlib compression.

The example image, increasingly skewed to right from top to bottom. It is mostly black and white, with pixels of intense color scattered throughout.

Aside from the colors looking all wrong, the image also appears to be skewed horizontally. This is because each line of the image has a filter as the first byte. Filters don't directly reduce the image size, but get it into a form that is more compressible by zlib. I have written about PNG filters before.

Defiltering

We take the decompressed data and undo the filter on each line. This gets us the decoded image, the same as the original! Here's the popularity of each filter type:

None 0

Subtract 23

Up 77

Average 134

Paeth 22

The example image

Interlacing

PNGs can optionally be interlaced, which splits the image into 7 different images, each of which covers a non-overlapping area of the image:

Diagram of the 7 passes in an 8x8 area. The first is a single pixel in the top-left, and the seventh is every other row.

Each of the 7 images is loaded in sequence, adding more detail to the image as it is loaded. This sequence of images is called Adam7.

The data for each of the 7 images are stored after each other in the image file. If we take the example image and enable interlacing, here's the raw uncompressed image data:

Same as the earlier skewed letter S, but there are multiple stacked on top of each other. The bottom half has 2, the third above that has 2 more, and above that it increasingly looks like random noise.

Here are the 7 passes that we can extract from that image data, which look like downscaled versions of the image:

Since some of those sub-images are rectangles but the actual image is square, there will be more details horizontally than vertically when loading the image, since horizontal detail is more important than vertical detail for human visual perception.

Bonus: bugs

Here's what you get when you have a bug in the Average filter that causes it to treat overflows incorrectly (the way integer overflow in filter value calculation is specified to be a bit different than the rest):

The example image, but it looks glitchy starting a quarter of the way down.

联系我们 contact @ memedata.com