我已经尝试“从头开始”创建一个 PNG 文件一段时间了,也就是说,逐字节操作数据字节以创建一个可行的文件。
在我的探索过程中,我遇到了一个奇怪的错误,每当原始像素数据以及过滤器类型字节达到256的长度时,图像无法渲染并且所有像素都变成黑色。只是为了澄清这一点似乎不会发生在其他尺寸上,我已经成功地“手工”创建了图像。
这里是图像颜色编码的十六进制数据,用于可视化不同的块:
十六进制数据
为了让我们达成共识,我将为每个块的内容写一个解释。
PNG 初始字节
- 0x89 0x50 0x4e 0x47 0x0d 0x0a 0x1a 0x0a 这些字节必须位于 PNG 文件的开头才能识别它。
IHDR 块
- 0x00 0x00 0x00 0x0d 这些字节的值是块的数据部分中包含的数据的长度。
- 0x49 0x48 0x44 0x52 这些字节是 IHDR 块的标识符。
- 0x00 0x00 0x00 0xff 这些字节的值是以像素为单位的图像的宽度 (255)。
- 0x00 0x00 0x00 0x01 这些字节的值是图像的高度(以像素 (1) 为单位)。
- 0x08 该字节的值是像素中存在的每个颜色通道的位深度(8 位)。
- 0x00 该字节表示图像的颜色类型,在本例中是一个简单的单通道,灰度。
- 0x00 该字节代表图像的压缩方法,0x00是唯一可能的值。
- 0x00 该字节代表图像的滤镜方法,0x00是唯一可能的值。
- 0x00 该字节表示隔行方式,值为 0x00 表示不隔行。
- 0x34 0xfb 0x27 0x7f 这些字节是从标识符开头到此处的字节的 CRC-32 值。
IDAT 块
- 0x00 0x00 0x01 0x0b 这些字节的值是块的数据部分包含的数据的长度。
- 0x49 0x44 0x41 0x54 这些字节是 IDAT 块的标识符。
- 0x08这个字节代表zLib压缩数据格式的CMF,指定这里,我用的是256的滑动窗口。
- 0x1d这个字节代表指定的zLib压缩数据格式的FLG,这里,我使用的FLEVEL为0,没有FDICT。
- 0x80 该字节表示这是最后一个要处理的压缩数据块,因为它是唯一的一个,并且未应用压缩。
- 0x01 0x00这些字节的值是压缩数据(256)过滤器类型字节+原始像素数据的长度。
- 0xfe 0xff 这些字节是前一个字节的否定。
- 0x00 该字节表示用于该扫描线的过滤器type,0x00表示无过滤器。
- 从0xff到地址末尾00000120我们有实际的像素数据,因为位深度是8并且颜色类型是灰度每个0xff是一个白色像素。
- 0x08 0xf1 0xfe 0x02 这些字节是从地址 00000030 开头到地址 00000120 结尾的字节的 ADLER-32 值。
- 0x33 0xed 0xdb 0x91 这些字节是从标识符开头到此处的十六进制的 CRC-32 值。
IEND 块
- 0x00 0x00 0x00 0x00 这些字节的值是块的数据部分中包含的数据的长度。 IEND 块中没有数据。
- 0x49 0x45 0x4e 0x44 这些字节是 IEND 块的标识符。
- 0xae 0x42 0x60 0x82 这些字节是标识符字节的 CRC-32 值,因为没有数据。
我尝试更改颜色类型和位深度,但只要压缩(实际上未压缩)数据的总和达到 256,文件就无法正确渲染,只是将每个像素替换为黑色像素。我尝试了所有可能的窗口,从 256 到 32768,但没有任何变化。
也许我需要包含一些辅助块,例如 sRGB,即使很难,我也不知道为什么这会有帮助。