如何解读PNG解压后的IDAT块?多次查阅规范仍存困惑
解读PNG IDAT块的Sub滤镜(首字节为1)
我太懂这种翻了半天规范还是卡壳的感觉了——PNG的滤镜机制确实是新手拆解IDAT块时的常见坑。结合你做的3×2像素、每个像素颜色不同的示例图,我一步步给你讲明白怎么从解压后的Filtered字节还原出原始像素数据。
先明确Sub滤镜的核心公式
首先把PNG规范里的Sub滤镜公式掰碎了说:
Filtered(byte) = Original(byte) - Original(byte - bpp)
反过来还原原始数据的话,公式变形为:
Original(byte) = (Filtered(byte) + Original(byte - bpp)) % 256
这里的bpp是每个像素的字节数:
- 灰度图是1,RGB是3,RGBA是4,你可以对应自己在Krita里创建的图像模式。
- 所有计算都是基于无符号8位字节的,所以加减后要对256取模,避免溢出导致的错误。
针对你的3×2示例图的实操步骤
假设你做的是RGB模式(bpp=3),解压后的IDAT数据结构是:
- 每一行开头是1个滤镜字节(你的情况是1,代表Sub滤镜)
- 后面跟着该行所有像素的过滤后字节:3个像素×3字节/像素=9字节
关键特殊情况:行首的前bpp个字节
当处理当前行的前bpp个字节时,byte - bpp会是负数,这时候PNG规范规定把Original(byte - bpp)视为0。
举个具体的模拟例子(对应你示例图的原始像素):
假设第一行的3个原始像素是:
- 像素1:红色(0xFF, 0x00, 0x00)
- 像素2:绿色(0x00, 0xFF, 0x00)
- 像素3:蓝色(0x00, 0x00, 0xFF)
原始字节序列就是:0xFF 0x00 0x00 0x00 0xFF 0x00 0x00 0x00 0xFF
应用Sub滤镜后的Filtered字节计算
按照公式逐个推导:
- 第1个字节(像素1的R):
0xFF - 0 = 0xFF - 第2个字节(像素1的G):
0x00 - 0 = 0x00 - 第3个字节(像素1的B):
0x00 - 0 = 0x00 - 第4个字节(像素2的R):
0x00 - 0xFF = 0x01(无符号字节减法溢出,实际是(0x00 + 256) - 0xFF = 1) - 第5个字节(像素2的G):
0xFF - 0x00 = 0xFF - 第6个字节(像素2的B):
0x00 - 0x00 = 0x00 - 第7个字节(像素3的R):
0x00 - 0x00 = 0x00 - 第8个字节(像素3的G):
0x00 - 0xFF = 0x01 - 第9个字节(像素3的B):
0xFF - 0x00 = 0xFF
所以这一行的解压后数据就是:[0x01] + [0xFF, 0x00, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x01, 0xFF]
从Filtered字节还原原始数据
用变形后的公式逐个还原:
- 第1个字节:
0xFF + 0 = 0xFF(对应像素1的R) - 第2个字节:
0x00 + 0 = 0x00(对应像素1的G) - 第3个字节:
0x00 + 0 = 0x00(对应像素1的B) - 第4个字节:
0x01 + 0xFF = 0x100 → 0x100 % 256 = 0x00(对应像素2的R) - 第5个字节:
0xFF + 0x00 = 0xFF(对应像素2的G) - 第6个字节:
0x00 + 0x00 = 0x00(对应像素2的B) - 第7个字节:
0x00 + 0x00 = 0x00(对应像素3的R) - 第8个字节:
0x01 + 0xFF = 0x100 → 0x00(对应像素3的G) - 第9个字节:
0xFF + 0x00 = 0xFF(对应像素3的B)
完美还原出了原始像素字节!
额外注意事项
- 每行的滤镜是独立的,Sub滤镜只依赖当前行的原始字节,和上一行无关(其他滤镜比如Up会用到上一行的数据,但你的情况是Sub,不用考虑)
- 必须严格按照无符号字节的规则计算,别当成有符号数处理,不然溢出后结果会完全错误
- 你可以把自己示例图解压后的IDAT数据导出来,按这个方法计算,再和Krita里的原始像素对比,就能验证自己的解读是否正确
内容的提问来源于stack exchange,提问作者Xhendos
相关产品推荐
相关产品推荐

