根据PNG Deflate/Inflate Compression 文档
zlib 数据流中的压缩数据存储为一系列块,每个块可以表示原始(未压缩)数据、使用固定 Huffman 码编码的 LZ77 压缩数据或使用自定义 Huffman 码编码的 LZ77 压缩数据。 最后一个块中的标记位将其标识为最后一个块,从而允许解码器识别压缩数据流的结尾。有关压缩算法和编码的更多详细信息,请参见 deflate 规范 [RFC-1951]。
...
在 PNG 文件中,所有 IDAT 块的内容串联构成一个 zlib 数据流,如上所述。此数据流将解压缩为过滤后的图像数据,如本文档其他部分所述。
需要强调的是,IDAT 块之间的边界是任意的,可以落在 zlib 数据流中的任何位置。 IDAT 块边界和 deflate 块边界或 zlib 数据的任何其他特征之间不一定存在任何关联。例如,终止 zlib 检查值完全有可能跨 IDAT 块拆分。*"
根据RFC 1951:“DEFLATE 压缩数据格式规范 1.3 版”:
压缩数据集由一系列块组成,对应于输入数据的连续块。 块大小是任意的,但不可压缩块限制为 65,535 字节。
每个块都使用 LZ77 算法和 Huffman 编码的组合进行压缩。每个块的霍夫曼树独立于前一个或后一个块的霍夫曼树; LZ77 算法可能使用对出现在前一个块中的重复字符串的引用,之前最多 32K 输入字节。
每个块由两部分组成:一对描述压缩数据部分表示的霍夫曼代码树和一个压缩数据部分。(霍夫曼树本身使用霍夫曼编码进行压缩。 ) 压缩数据由两种类型的一系列元素组成:文字字节(在前 32K 输入字节中未被检测为重复的字符串)和指向重复字符串的指针,其中指针表示为一对 。 “deflate”格式中使用的表示将距离限制为 32K 字节,长度限制为 258 字节,但不限制块的大小,不可压缩块除外,如上所述受到限制。
压缩数据中的每种类型的值(文字、距离和长度)都使用 Huffman 代码表示,文字和长度使用一个代码树,距离使用单独的代码树。每个块的代码树以紧凑的形式出现在该块的压缩数据之前。
因此,要确定给定块的结尾,您必须解析块的霍夫曼代码以了解压缩数据中每个元素的位置和类型,然后您可以根据需要处理每个元素,直到找到块中最后一个元素的结尾。第 3.2 节详细介绍了如何格式化压缩块的技术细节,尤其是Section 3.2.3:
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)
两种压缩情况之间的唯一区别是如何定义文字/长度和距离字母的霍夫曼代码。
在所有情况下,实际数据的解码算法如下:
do
read block header from input stream.
if stored with no compression
skip any remaining bits in current partially
processed byte
read LEN and NLEN (see next section)
copy LEN bytes of data to output
otherwise
if compressed with dynamic Huffman codes
read representation of code trees (see
subsection below)
loop (until end of block code recognized)
decode literal/length value from input stream
if value < 256
copy value (literal byte) to output stream
otherwise
if value = end of block (256)
break from loop
otherwise (value = 257..285)
decode distance from input stream
move backwards distance bytes in the output
stream, and copy length bytes from this
position to the output stream.
end loop
while not last block
请注意,重复的字符串引用可能引用了前一个块中的字符串;即,后向距离可能跨越一个或多个块边界。但是距离不能超过输出流的开头。 (使用预设字典的应用程序可能会丢弃部分输出流;无论如何,距离都可以引用输出流的那部分)还要注意,引用的字符串可能与当前位置重叠;例如,如果解码的最后 2 个字节具有值 X 和 Y,则具有 的字符串引用会将 X,Y,X,Y,X 添加到输出流中。