我正在尝试构建一个快速的 8 位灰度 PNG 编码器。不幸的是我一定是误解了规范的一部分。较小的图像尺寸似乎可行,但较大的图像只能在某些图像查看器中打开。该图像(带有多个 DEFLATE block )给出了 我的图像查看器中出现“IDAT 解压缩错误”错误,但在浏览器中打开正常:
这张图片只有一个 DEFLATE block ,但也给出了一个错误:
下面我将概述我在 IDAT block 中放入的内容,以便您可以轻松发现任何错误(注意,图像和步骤已根据答案进行修改,但仍然存在问题):
IDAT长度
ascii 格式的“IDAT”(字面意思是字节 0x49 0x44 0x41 0x54)
Zlib header 0x78 0x01
步骤 4-7 适用于每个 deflate block ,因为数据可能需要分解:
字节 0x00 或 0x01,具体取决于它是中间 block 还是最后一个 block 。
存储为小端 16 位整数的 block 中的字节数(最多 2^16-1)
此整数表示形式的 1 的补码。
图像数据(对于 PNG 中的无滤镜选项,每条扫描线均以零字节开头,后跟灰度像素数据的宽度字节)
所有图像数据的 adler-32 校验和
所有 IDAT 数据的 CRC
我在 Linux 上尝试过 pngcheck
,它没有发现任何错误。如果没有人能看出问题所在,你能给我指出调试工具的正确方向吗?
我的最后手段是使用 libpng 库来制作我自己的解码器,并从那里进行调试。
有人建议这可能是我的 adler-32 函数计算:
static uint32_t adler32(uint32_t height, uint32_t width, char** pixel_array)
{
uint32_t a=1,b=0,w,h;
for(h=0;h<height;h++)
{
b+=a;
for(w=0;w<width;w++)
{
a+=pixel_array[h][w];
b+=a;
}
}
return (uint32_t)(((b%65521)*65536)|(a%65521));
}
请注意,由于传递给函数的 Pixel_array 在每个扫描线的开头不包含零字节(PNG 需要),因此在每次迭代的开头有一个额外的 b+=a (和隐式的 a+=0)外循环。
最佳答案
- The byte 0x00 or 0x80, depending on if it is a middle or the last block.
将0x80
更改为0x01
,一切都会好起来的。
0x80
显示为存储 block ,而不是最后一个 block 。我们所关注的只是低位,它为零,表示中间 block 。所有数据都位于该“中间” block 中,因此解码器将恢复完整图像。一些自由派 PNG 解码器可能会忽略在尝试解码下一个不存在的 block 时出现的错误,然后忽略丢失的校验值(Adler-32 和 CRC-32)等。这就是它出现的原因在浏览器中正常,即使它是无效的 PNG 文件。
您的 Adler-32 代码有两处错误。首先,您从 char
数组访问数据。 char
已签名,因此您的 0xff
字节将被添加为 -127,而不是 255。您需要先创建数组 unsigned char
或将其转换为该数组,然后再从中提取字节值。
其次,您进行模运算的时间太晚了。您必须在 uint32_t
溢出之前执行 % 65521
。否则,您将无法获得算法所需的总和的模。一个简单的修复方法是在宽度循环之后、高度循环内对 a
和 b
执行 % 65521
。只要您能保证宽度小于 5551 字节,此方法就可以工作。 (为什么 5551 留给读者作为练习。)如果您不能保证这一点,那么您将需要嵌入另一个循环来消耗该行中的字节,直到达到 5551 个字节,进行取模,然后继续线。或者,稍微慢一点,只需运行一个计数器并在达到极限时进行取模。
以下是适用于任何宽度的版本示例:
static uint32_t adler32(uint32_t height, uint32_t width, unsigned char ** pixel_array)
{
uint32_t a = 1, b = 0, w, h, k;
for (h = 0; h < height; h++)
{
b += a;
w = k = 0;
while (k < width) {
k += 5551;
if (k > width)
k = width;
while (w < k) {
a += pixel_array[h][w++];
b += a;
}
a %= 65521;
b %= 65521;
}
}
return (b << 16) | a;
}
关于compression - 构建快速 PNG 编码器问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25753695/