使用膨胀来解压缩 png 文件



我一直在解压缩 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.

最新更新