目录
前文
翻以前的文章,发现了一篇讲png格式的引用,再次阅读,发现并没有想象中的那么难。今天就试着解析下一个png图片的格式。为了简化图片,我生成了一张 1*1 和黑色不透明的基准式png,转化为十六进制进行解析。具体生成方法和图片 base64_url,可见后文。
块格式
png 以格式为单位,而块格式如下:
基准式
先处理成十六进制
89504e470d0a1a0a0000000d49484452
000000010000000108060000001f15c4
89000000097048597300000ec400000e
c401952b0e1b0000001049444154081d
010500faff00000000ff01040100a78f
db830000000049454e44ae426082
文件头标志
89504e470d0a1a0a
IHDR头块
块数据长度
0000000d
块类型 IHDR标识(ascii码为IHDR)
49484452
块类型内容
图像的宽
00000001
1像素,四字节无符号整数。零是无效值。
图像的高
00000001
1像素,四字节无符号整数。零是无效值。
色深
08
一个单字节整数,给出每个样本或每个调色板索引(而不是每个像素)的位数。有效值为 1、2、4、8 和 16,但并非所有颜色类型都允许使用所有值。2^8=256,即这是一个256色的图像,
颜色类型
06
一个单字节整数,用于定义 PNG 图像类型。有效值为 0、2、3、4 和 6。06为带α通道数据的真彩色图像
压缩方法
00
一个单字节整数,表示用于压缩图像数据的方法,有五种。
过滤方法
00
一个单字节整数,表示压缩前对图像数据应用的预处理方法
隔行方式
00
一个单字节整数,表示图像数据的传输顺序。本国际标准定义了两个值:0(无隔行)或 1(Adam7 隔行)
IHDR CRC校验
1f15c489
pHYs 物理像素尺寸
块数据长度
00000009
块类型
70485973
块内容——物理像素尺寸
的PHYS块指定预期的像素尺寸或纵横比用于图像的显示
字段信息 |
|
每单位像素,X 轴 |
4 字节(PNG 无符号整数) |
每单位像素,Y 轴 |
4 字节(PNG 无符号整数) |
单位说明符 |
1 字节 |
00000ec4
00000ec4
01
当单位说明符为 0 时,pHYs块只定义像素纵横比;像素的实际大小仍未指定。
如果pHYs块不存在,则假定像素为正方形,并且未指定每个像素的物理大小。
pHYs 块 CRC校验
952b0e1b
IDAT图像数据
块数据长度
00000010
块类型
49444154
块内容——图像数据
081d010500faff00000000ff01040100
涉及压缩和过滤,暂时看不懂
IDAT 块 CRC校验
a78fdb83
IEND数据块
块数据长度
00000000
块类型
49454e44
CRC校验
ae426082
因为块数据是空的,所以CRC校验是固定的。因为IEND数据块每个PNG都是一样的,
再把数据进行用表格记录,更加直观
数据 |
含义 |
89504e470d0a1a0a |
文件头标志 |
0000000d |
块长度 |
49484452 |
IHDR块 标志 |
00000001 |
图像的宽 |
00000001 |
图像的高 |
08 |
色深 |
06 |
颜色类型 |
00 |
压缩方法 |
00 |
过滤方法 |
00 |
隔行方式 |
1f15c489 |
IHDR块 CRC校验 |
00000009 |
块长度 |
70485973 |
pHYs块 |
00000ec4 |
每单位像素,X 轴 |
00000ec4 |
每单位像素,Y 轴 |
01 |
单位说明符 |
952b0e1b |
pHYs 块 CRC校验 |
00000010 |
块长度 |
49444154 |
IDAT块类型 |
081d010500faff00000000ff01040100 |
IDAT数据 |
a78fdb83 |
IDAT 块 CRC校验 |
00000000 |
IEND数据块长度 |
49454e44 |
IEND数据块类型 |
ae426082 |
IENDCRC校验 |
交错式(隔行扫描)
同一张png图片设置为交错式,数据如下
89504e470d0a1a0a0000000d49484452
000000010000000108060000016812f4
1f000000097048597300000ec400000e
c401952b0e1b0000001049444154081d
010500faff00000000ff01040100a78f
db830000000049454e44ae426082
其中 CRC校验码前的01则代表改图片为隔行扫描,其他数据一致
其他字段头补充
辅助块
透明度信息
tRNS 透明度
74524e53
色彩空间信息
CHRM 主要色度和白点
6348524d
GAMA 图像伽玛
67414d41
iCCP 嵌入式 ICC 配置文件
69434350
sBIT 有效位
73424954
sRGB 标准 RGB 色彩空间
73524742
文本信息
tEXT 文本数据
74455874
zTXt 压缩文本数据
7a545874
iTXt 国际文本数据
69545874
其他信息
本底 背景颜色
624b4744
HIST 图像直方图
68495354
pHYs 物理像素尺寸
70485973
sPLT 建议调色板
73504c54
时间戳信息
tIME 图像最后修改时间
74494d45
隔行扫描 C语言代码描述
当通过慢速传输链路接收图像时,用户可以通过逐步显示隔行图像来提高感知性能。这意味着当接收到每个缩小的图像时,会根据目前接收到的数据显示完整图像的近似值。通过扩展每个接收像素以填充覆盖接收像素下方和右侧的尚未传输像素位置的矩形,可以获得一种简单但令人欣喜的效果。
int starting_row[7] = { 0, 0, 4, 0, 2, 0, 1 };
int starting_col[7] = { 0, 4, 0, 2, 0, 1, 0 };
int row_increment[7] = { 8, 8, 8, 4, 4, 2, 2 };
int col_increment[7] = { 8, 8, 4, 4, 2, 2, 1 };
int block_height[7] = { 8, 8, 4, 4, 2, 2, 1 };
int block_width[7] = { 8, 4, 4, 2, 2, 1, 1 };
int pass;
long row, col;
pass = 0;
while (pass < 7)
{
row = starting_row[pass];
while (row < height)
{
col = starting_col[pass];
while (col < width)
{
visit(row, col,
min(block_height[pass], height - row),
min(block_width[pass], width - col));
col = col + col_increment[pass];
}
row = row + row_increment[pass];
}
pass = pass + 1;
}
函数visit(row,column,height,width)获取下一个传输的像素,并使用像素指示的颜色绘制一个指定高度和宽度的矩形,其左上角位于指定的行和列。请注意,行和列是从左上角的 0,0 开始测量的。
如果查看器将接收到的图像与背景图像合并,则仅绘制接收到的像素位置可能更方便(visit()函数仅设置指定行和列的像素,而不是整个矩形)。随着新图像逐渐取代旧图像,这会产生“淡入”效果。这种方法的一个优点是可以在替换每个像素时进行适当的 alpha 或透明度处理。如果最终为这些位置接收到的像素完全或部分透明,则如上所述绘制矩形将覆盖稍后可能需要的背景图像像素。只有当背景图像没有存储在屏幕外的任何地方时,这才是一个问题。
libpng.org 上的类型十进制处理成十六进制显示
(('137 80 78 71 13 10 26 10'.split(' ')).map(function(item){return (item*1).toString(16)})).join('')
生成图片代码
$image = new \Imagine\Gd\Imagine();
$size = new \Imagine\Image\Box(1,1);
ob_start();
$image->create($size,(new Imagine\Image\Palette\RGB())->color('#000', 100)) ->interlace(\Imagine\Image\ImageInterface::INTERLACE_NONE )->show('png',['quality'=>100]);
$content = ob_get_contents();
ob_clean();
header_remove();
$png_interlace_none = base64_encode($content);
用的是php Imagine类库,参考php图片处理Imagine常用例子
处理成十六进制代码
$png_interlace_none = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAEElEQVQIHQEFAPr/AAAAAP8BBAEAp4/bgwAAAABJRU5ErkJggg==';
$png_interlace_line = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAFoEvQfAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAEElEQVQIHQEFAPr/AAAAAP8BBAEAp4/bgwAAAABJRU5ErkJggg==';
echo 'fie_length '.strlen($png_interlace_line).' '.dechex(strlen($png_interlace_line)).PHP_EOL;
echo 'interlace_none'.PHP_EOL;
$str = unpack('H*',base64_decode($png_interlace_none))[1];
for ($i=0;$i<strlen($str);$i=$i+16){
echo substr($str,$i,16).PHP_EOL;
}
echo 'interlace_line'.PHP_EOL;
$str = unpack('H*',base64_decode($png_interlace_line))[1];
for ($i=0;$i<strlen($str);$i=$i+16){
echo substr($str,$i,16).PHP_EOL;
}
补充
2022年1月5日
IDAT 块 CRC校验有被用于 canvas fingerprinting,详情 网站追踪技术:“帆布指纹识别”canvas fingerprinting
参考
PNG文件格式详解
Progressive-display