Compression 构建快速PNG编码器存在的问题
我试图建立一个快速的8位灰度PNG编码器。不幸的是,我一定是误解了规范的一部分。较小的图像大小似乎可行,但较大的图像大小只能在一些图像查看器中打开。此图像(具有多个放气块)提供了一个 “图像查看器中的“IDAT中的解压缩错误”错误,但在我的浏览器中可以正常打开: 此图像只有一个放气块,但也给出了一个错误: 下面我将概述我在IDAT块中的内容,以防您很容易发现任何错误(注意,图像和步骤已根据答案进行了修改,但仍然存在一个问题):Compression 构建快速PNG编码器存在的问题,compression,png,zlib,libpng,encoder,Compression,Png,Zlib,Libpng,Encoder,我试图建立一个快速的8位灰度PNG编码器。不幸的是,我一定是误解了规范的一部分。较小的图像大小似乎可行,但较大的图像大小只能在一些图像查看器中打开。此图像(具有多个放气块)提供了一个 “图像查看器中的“IDAT中的解压缩错误”错误,但在我的浏览器中可以正常打开: 此图像只有一个放气块,但也给出了一个错误: 下面我将概述我在IDAT块中的内容,以防您很容易发现任何错误(注意,图像和步骤已根据答案进行了修改,但仍然存在一个问题): IDAT长度 ascii格式的“IDAT”(字面上是字节0x49
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));
}
静态uint32 adler32(uint32高度、uint32宽度、字符**像素数组)
{
uint32_t a=1,b=0,w,h;
对于(h=0;h我确实在pngcheck
中遇到了一个错误:“zlib:inflate error=-3(数据错误)”。由于您的PNG脚手架结构看起来不错,是时候用十六进制查看器对IDAT
块进行低级查看了。(我将在处理它的同时键入此内容。)
标题看起来不错;IDAT
长度也不错。您的zlib标志是78 01
(“无/低压缩”,另请参见),其中我自己的一个工具使用了78 9C
(“默认压缩”),但这些标志只是信息性的
下一步:zlib的内部块(per)
在压缩标志(CMF
在RFC1950中)之后,它需要FLATE压缩数据,这是zlib支持的唯一压缩方案。这在另一个castle RFC中:
每个单独的压缩块前面都有一个字节:
3.2.3.块格式的详细信息
每个压缩数据块以3个头位开始
包含以下数据:
first bit BFINAL
next 2 bits BTYPE
…
BFINAL设置为当且仅当这是数据的最后一个块时
设定
BTYPE指定数据的压缩方式,如下所示:
00 - no compression
01 - compressed with fixed Huffman codes
10 - compressed with dynamic Huffman codes
11 - reserved (error)
因此,对于“非最后一个块,未压缩”可将该值设置为00
,对于“最后一个块,未压缩”可将该值设置为01
,紧跟其后的是长度(2字节)及其按位倒数,参见3.2.4。非压缩块(BTYPE=00):
3.2.4.非压缩块(BTYPE=00)
直到下一个字节边界的任何输入位都将被忽略。
块的其余部分由以下信息组成:
0 1 2 3 4...
+---+---+---+---+================================+
| LEN | NLEN |... LEN bytes of literal data...|
+---+---+---+---+================================+
LEN是块中的数据字节数。NLEN是
一个人的补语
它们是您的IDAT
段中的最后4个字节。为什么小图像有效,而大图像无效?因为长度只有2个字节。1您需要将图像分成不大于65535字节的块(在我自己的PNG创建者中,我似乎使用了32768,可能是“为了安全”)。如果是最后一个数据块,则写出01
,否则写出00
。然后添加两倍的LEN
字节,正确编码,后跟精确的LEN
数据字节。重复操作直到完成
Adler-32校验和不是此Flate压缩数据的一部分,不应计入LEN
数据块中。(但它仍然是IDAT
数据块的一部分。)
在重新阅读您的问题以验证我解决了您的所有问题(并确认我拼写的“Adler-32”正确)之后,我意识到您正确描述了所有步骤——除了“最后一个块”指示器是01
,而不是80
(稍后编辑:嗯,也许您是对的!)--但是它在这个示例PNG中没有显示。看看你是否能让它按照字母顺序完成所有步骤
这是一个很好的“遵循规范”的练习,如果你能做到这一点,尝试添加适当的压缩可能是值得的。我尽可能避免使用预先制作的库;我为自己的PNG编码器/解码器做的唯一考虑是使用Rich Geldreich,因为实现了适当的Fla编码/解码超出了我的知识范围
1这还不是全部。浏览器对HTML错误特别宽容;似乎他们对PNG错误也同样宽容。Safari可以很好地显示您的图像,预览也是如此。但他们可能只是在共享OS X的PNG解码器,因为Photoshop拒绝该文件
字节0x00或0x80,取决于它是中间块还是最后一块
将0x80
更改为0x01
,一切都会好起来
0x80
显示为不是最后一个块的存储块。所查看的是低位,即零,表示中间块。所有数据都位于该“中间”块,因此解码器将恢复完整图像。一些自由的PNG解码器可能会在尝试解码下一个块时忽略错误,而下一个块不存在,然后
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;
}