为什么PNG文件的压缩块长度会被忽略?

3

摘要:我需要一个PNG编写器,由于各种原因,我必须从头开始制作它。我不需要压缩图像数据,因此我使用了无压缩的deflate算法来实现PNG。对于需要多个deflate块的图像,渲染失败,似乎是因为它忽略了块长度。

问题:当我有一个用单个deflate块编写的微小图像时,生成的图像正是它应该是的(所有像素都正确,在使用pngcheck检查时没有错误)。然而,一旦我使用多个deflate块,生成的图像就会出错,因为deflate块被读取超过了它们的长度(尽管pngcheck仍然没有显示任何错误)。在十六进制编辑器中查看原始数据似乎显示一切都符合规范(根据libpng、RFC 1950和RFC 1951)。

示例数据:我生成了一个3x1 RGBA图像,其中像素颜色从左到右分别为(fb,02,03,fa)(01,fc,03,fa)(01,02,fd,fa)。然后我将它们写入文件,其中最大deflate块长度为8字节或64字节。64字节块长度的图像完全正确,可以正确地呈现。

以下是完整的图像内容,以十六进制表示,其中粗体是块长度,斜体是块数据。

8字节块图像(呈现不正确):
89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52 00 00 00 03 00 00 00 01 08 06 00 00 00 1b e0 14 b4 00 00 00 1d 49 44 41 54 78 9c 0000 08ff f700 fb 02 03 fa 01 fc 030100 05ff fafa 01 02 fd fa05 ee ac ee a1 e1 2d b9 00 00 00 00 49 45 4e 44 ae 42 60 82

64字节块图像(正确呈现):
89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52 00 00 00 03 00 00 00 01 08 06 00 00 00 1b e0 14 b4 00 00 00 18 49 44 41 54 78 9c 0100 0dff f200 fb 02 03 fa 01 fc 03 fa 01 02 fd fa05 ee ac ee f8 dc a0 6c 00 00 00 00 49 45 4e 44 ae 42 60 82

每个Deflate块的开头由5个字节组成:0或1(取决于是否为最终块),后跟2字节长度(上面是粗体)和它的2字节补码。在8字节块映像中,第一个块的长度为8,正如它应该的那样。在长度补码后的8个字节(上面是斜体)确实是它们应该是的值,并且第9个字节等于1(并开始最终块)。然而,那个1被解释为颜色值(第二个像素的alpha),而不是下一个块的起始位置!

可能我忽略了Deflate块的结构。我是很傻很天真,还是我误解了规格?

1个回答

5

两个PNG文件都是无效的,尽管其中一个恰好可以正确渲染(即使pngcheck也没有捕获到它)。 它们都没有正确终止嵌入式deflate流。

查看其中一个deflate流,存储的长度和补码是大端序而不是小端序。例如,需要将00 0d ff f2更改为0d 00 f2 ff


好的,谢谢!我怀疑我的Adler32实现有误,导致流校验和不正确。也许这会阻止流终止? - Adam Coster
是的,Adler-32也是错误的,但你的问题是由交换存储长度引起的。PNG渲染器显然无视了Adler-32错误。 - Mark Adler
我认为这些长度是错的字节顺序,但当我交换它们时,出现了一个错误。很可能这意味着它随后能够成功地到达其他错误。我相信这将是解决方法。如果是这样,将修复字节顺序和Adler32并标记为已解决。谢谢! - Adam Coster
2
是的,问题出在字节顺序和错误的校验和组合上。我才意识到您就是那个Mark Adler。现在我更为难以启齿,因为我实现了一个有缺陷的简单校验和算法... - Adam Coster

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接