自定义Python Deflate实现(固定霍夫曼)PNG显示异常排查
Let's break down the issues causing your pngcheck errors and address your questions step by step:
1. Zlib Header Checksum Failure
Your zlib header [78, 1] (CMF=78, FLG=1) violates the RFC 1950 requirement that (CMF * 256 + FLG) must be divisible by 31. This is the direct cause of the header checksum failure.
- Calculate the correct FLG value:
- CMF=78 →
78 * 256 = 19968 19968 % 31 = 4(since 31 × 644 = 19964)- FLG needs to be
31 - 4 = 27(0x1B in hex) to make the total sum a multiple of 31:19968 + 27 = 19995 = 31 × 645
- CMF=78 →
- Update your header to
bytearray([78, 27])to fix this checksum error.
2. Incorrect Adler32 Calculation
Zlib's Adler32 checksum is computed over the entire uncompressed data stream, not just a single scanline. Your current code only calculates the checksum for one scanline, which guarantees a mismatch with the decompressor's expected value.
- Fix this by accumulating the checksum across all scanlines:
# Initialize Adler32 with the required starting value of 1 adler32Value = 1 for scanline in all_scanlines: # Process the scanline (LZ77 + Huffman encoding) # ... # Update Adler32 with the current uncompressed scanline adler32Value = zlib.adler32(bytearray(scanline), adler32Value) # After processing all scanlines, write the final big-endian checksum adler32Array = bytearray((adler32Value & 0xFFFFFFFF).to_bytes(4, 'big')) outputPNG.write(adler32Array) - Note: Your byte order for storing Adler32 is correct (big-endian), but the accumulated value itself was wrong.
3. Bit Packing Endianness Mistake
RFC 1951 (Deflate) specifies that bits are packed into bytes least significant bit (LSB) first. Your use of endian='big' in the bitarray reverses this order, corrupting the compressed bitstream and leading to inflate_error = -3.
- Switch to
endian='little'when creating your bitarray to match Deflate's rules:outputBitStream = ba.bitarray(endian='little') - This ensures the first bit in your bitarray is stored as the LSB of the first byte, which is how Deflate expects bits to be ordered.
4. Quick Block Header Sanity Check
Your block header logic (001 for non-final blocks, 101 for final blocks) is correct for fixed Huffman encoding (BTYPE=01):
- First bit = BFINAL (0 = not final, 1 = final)
- Next two bits = BTYPE (01 = fixed Huffman)
Just confirm this bit sequence is packed correctly with the LSB-first endianness fix above.
Final Notes
After fixing these issues, re-run pngcheck to verify the zlib header and inflate errors are resolved. If problems persist, double-check that your fixed Huffman encoding correctly emits the end-of-block code (256) and that your LZ77 output is properly formatted for Huffman encoding.
内容的提问来源于stack exchange,提问作者Diligent Hero

