使用 inflate 解压缩 png 文件

Using inflate to decompress a png file

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

我要解压的图片是这样的:arial.png

它没有过滤 (0) 并且是带有 alpha 的真彩色 png。它还只包含一个 IDAT 块。

但是我得到的是这样的:incorrect png

我知道图像颠倒了,这是我的渲染器自下而上和 png 自上而下的问题;这不是我现在关心的事情。

我还应该注意 inflate 实际上只运行一次 return Z_OK,所以我认为问题不在于没有处理扫描线过滤器。我试过每行添加一个字节,然后不将每行的第一个字节复制到 Pixels 数组,但这并没有太大的区别。

知道我做错了什么吗?

看起来您可能忽略了每条扫描线开头的过滤方法字节。它就在那里,即使它是 0。

我认为你应该做到 while(Stream.avail_out == 0);

只要z.avail_out为零,就表示压缩后的数据填充了可供解压数据使用的字节,inflate()需要重新调用

总是想到它的意思,当有可用(未填充)字节输出时我们应该停止这样 avail_out > 0.

首先,在最后一次调用时 returning Z_OK 不正常。如果没有returnZ_STREAM_END,说明你没有解压验证完整的压缩数据

其次,您的 avail_out 不允许在每个扫描行的开头使用过滤字节。将高度添加到 avail_out 并为其留出空间。

第三,我不知道你是如何从原始的 512x512 RGBA 图像中得到一个 643x514 RGB 图像的。你需要跳过每一行的过滤器字节,然后每个像素是四个字节。