我一直在解压缩 png 文件时遇到问题。这是处理 IDAT 区块的代码:
case PNG_CHUNK_IDAT: {
z_stream Stream = {};
int Error = inflateInit(&Stream);
if(Error == Z_OK) {
Stream.avail_in = ChunkLength;
Stream.next_in = At;
Stream.avail_out = PngImage.Width * PngImage.Height * PngImage.Depth;
Stream.next_out = PngImage.Pixels;
do {
Error = inflate(&Stream, Z_NO_FLUSH);
if(Error != Z_OK) {
break;
}
} while(Stream.avail_out != 0);
inflateEnd(&Stream);
}
At += ChunkLength;
break;
}
其中At
是 png 文件缓冲区中的当前位置,PngImage
只是一个结构,它保存图像的宽度、高度和深度,并具有大小为width*height*depth
的无符号 char 数组。
我试图解压缩的图像是这样的:arial.png
它没有过滤 (0),是带有 alpha 的真彩色 png。它也只包含一个 IDAT 块。
但相反,我得到这样的东西:不正确的 png
我知道图像是颠倒的,这是我的渲染器是自下而上的,而png是自上而下的问题;这不是我现在关心的事情。
我还应该注意,inflate
实际上只运行一次并返回Z_OK,所以我认为问题不在于不处理扫描线过滤器。我尝试过每行添加一个字节,然后不将每行的第一个字节复制到Pixels
数组中,但这并没有太大区别。
知道我可能做错了什么吗?
首先,在最后一次调用时返回Z_OK
是不行的。如果它没有返回Z_STREAM_END
,则您没有解压缩并验证完整的压缩数据。
其次,您的avail_out
不允许在每个扫描行的开头使用筛选器字节。将高度添加到avail_out
并为此提供空间。
第三,我不知道您是如何从原始的512x512 RGBA图像中获得643x514 RGB图像的。您需要跳过每行的过滤器字节,然后每个像素是四个字节。
看起来您可能忽略了每条扫描线开头的过滤器方法字节。它就在那里,即使它是 0。
我认为你应该让它while(Stream.avail_out == 0);
只要z.avail_out
等于零,就意味着需要再次调用可用于解压缩数据和inflate()
的压缩数据填充字节。
始终考虑它的含义,当有可用(未填充)的输出字节时,我们应该停止,以便avail_out > 0
.