PNG 文件格式和压缩介绍

status
category
date
summary
slug
icon
tags
password
之前立的flag终归是要还的,为了更好地优化项目中的图片资源,提出最优的压缩方案,先针对PNG图片格式做一个相对深入一点的学习。

什么是PNG

PNG的全称是便携式网络图形(Portable Network Graphics),是目前最流行的网络传输和展示的图片格式,设计它的初衷是为了代替GIF,它的特点也正是相对于GIF的优势:
  • 免费:很久之前GIF专利是收费的,这也是PNG发展起来的原因。
  • 无损压缩:PNG采用了基于LZ77的压缩算法,使得它压缩率更高,且不损失数据。
  • 体积小:PNG利用特殊的编码方式标记重复出现的数据,使体积更小。可以联想一下计数排序。
  • 可变透明度:PNG独有的透明通道,支持对原图像添加256个透明层次,这个特性为web和移动应用的图标设计提供了极大的便利。

相关概念

透明度

透明度的实现主要有三种方式:
  • 不透明:最简单直接,整张图片每个像素都不透明
  • 索引透明:记录某个像素点是否透明,要么 100% 透明,要么完全不透明
  • Alpha 透明:可以指定像素点的透明度,例如 50% 透明

色彩

图像色深度指的是每个像素所使用的位数,用于确定彩色图片每个像素可能有的颜色数,或者灰度图像每个像素可能有的灰度级数。注意色深度和位深度是两个概念。若色彩深度为 n,则每个像素所用的位数为 n,每个像素就有2n 种颜色选择,例如:8 位图片指的是每个像素有 256 种可用的颜色。另外,所以谈到色深或者位深的时候,要尤其注意单位,是位深还是位深/通道,具体的区别可以参考下面的 PNG 类型。
色彩的实现由三种方式:
  • 灰度(greyscale):描述从白到黑的程度
  • 真色彩(true color):每个像素保存自己原始的、真实的 RGB 颜色值
  • 索引色(indexed color):为图片创建一张颜色表,其中保存了所有用到的颜色和对应的颜色值,并为它们编号,可以理解为颜色的宏定义。这样做可以减小图片的文件大小,但缺点是颜色种类的数量受到颜色表的限制。

PNG类型

我们一般谈论的PNG类型:
  • PNG 8:8 代表 8 位,每个像素支持存储 256种颜色。支持索引透明和 Alpha 透明。
  • PNG 24:24 代表 24 位,即 8位/通道,RGB 每个通道都可以表示 256 种颜色,因此总共可以表示的颜色为256 的三次方,即 16777216 种颜色。PNG 24能表示的颜色更多,也意味着文件会更大。PNG 24是不透明的
  • PNG 32:32 代表PNG 24再加上 8 位的透明通道,也就是说PNG 32能表示的颜色和PNG 24一样多,并且还支持 256 种透明颜色。
但是根据上面介绍的透明度和色彩模式,PNG 还支持很多种图片:调色板图片(包含 24 位 RGB 或 32 位 RGBA 的索引)、灰度图片(有或无 Alpha 通道)和真色彩图片(RBG 或 RBGA)。

隔行扫描

隔行扫描(Interlacing),也称交错扫描(interleaving),是一种位图编码的方式,编码时不严格按照相邻行进行。当连接速度比较慢时,采用隔行扫描的图片可以渐进地进行显示,即先显示较为模糊的版本,然后逐渐变清晰。如图:
notion image
我们常用的 GIF、PNG、JPEG 都采用了隔行扫描的方式,其中 PNG 采取的是二维、七通道的Adam7,二维表示扫描支持竖直和水平方向,7代表一张图片会被拆分为7个子图片。
notion image
和 GIF 采取的一维、四通道算法相比,PNG 在网速相同的情况下,尤其是网速较慢时,能够更快地显示出图片的基本外貌。

过滤

过滤(filter)是将图片信息变得容易压缩的过程,整张图片只会使用一种过滤方法,而过滤类型会被插到每个扫描行的前面,以方便压缩。目前 PNG 图片指定的过滤方式只有一种(method 0),所以实际使用时,只需要指定过滤类型。过滤类型指的是像素基于相邻像素的变化规律,通过这个变化规律即可推断出某像素的预测值,为了便于描述,规定了相邻像素的方位:左(A)、上(B)、左上(C)。对 method 0 来说,,共有 5 种:
类型值
名称
预测值
0
None
0
1
Sub
A
2
Up
B
3
Average
A和B的平均值,向下取整
4
Paeth
A,B,C 中最接近 p = A + B -C 的一个
选择合理的过滤类型可以大大提高压缩效率,但如果采用了隔行扫描,每个阶段的子图片都会被分别过滤,即支持了渐进式传输,当然压缩效率也会下降。

文件结构

了解了 PNG 的基本类型和相关概念,我们可以深入了解一下 PNG 的文件结构,推荐使用 pngcheck 进行 PNG 图片格式的分析。
PNG 文件由一个文件署名(file signature,也叫文件头)和多个数据块(chunks)组成,其中数据块也分为关键数据块(critical)和辅助数据块(ancillary)。关键数据块共有 4 个,每个 PNG 文件必须包含 3 个或以上的关键数据块,而辅助数据块是可选的。

文件署名

文件署名由 8 个字节组成,是用来识别文件格式的,它的值为:89 50 4E 47 0D 0A 1A 0A,只要一个文件的署名为该值,就认为它是 PNG 格式的,这些值都有特殊的含义:
十六进制值
含义
89
该值可以检测当前环境是否支持八位编码,并且大于ASCII最大值,可以避免被识别为文本文件
50 4E 47
在 ASCII 中对应 PNG 三个字母,方便被文本编辑器识别
0D 0A
CRLF 换行符;0D代表 ASCII 13,即\r,CR;0A代表ASCII 10,即\n,LF
1A
在 DOS 系统下标志文件类型的结束
0A
LF 换行符

数据块

文件署名之后,紧接着就是一系列的数据块,每个数据块都包含图片的描述信息,数据块由四个部分组成:
名称
长度
说明
Length
4 bytes
指定 Chunk Data 的长度
Chunk Type
4 bytes
指定数据块类型
Chunk Data
可变
按照Chunk Type存放指定数据
CRC
4 bytes
检查是否有错误的循环冗余代码
这里要注意 Length 保存的是该数据块中 Chunk Data 的长度,也就是说,一个数据块的长度应该为 Length 的值加上12 bytes。
Chunk Type 由4个字符组成,:第一个字符是否大写(critical),决定了该数据块是否是关键(critical)数据块;第二个字符大写表示公开,小写表示私有;第三个字符规定必须大写;第四个字符大写则表示只能在关键数据块不变时被复制,小写则表示任何情况下能复制。
图片处理的相关软件或者解码器必须支持关键数据块,否则就不能正确地读取 PNG 文件,接下来介绍四个关键数据块和其他辅助数据块:

IHDR

这个数据块被称为文件头数据块,必须作为第一个数据块,并用13个字节按照顺序来表示图像数据的基本信息:
长度
说明
Width
4 bytes
图片宽度,以像素为单位
Height
4 bytes
图片高度,以像素为单位
Bit Depth
1 byte
图像深度
Color Type
1 byte
颜色类型
Compression Method
1 byte
压缩算法
Filter Method
1 byte
滤波方法
Interlace Method
1 byte
扫描方法
根据颜色类型和是否有 Alpha 通道,可以有多个 Color Type:
Color Type
可能的Bit Depth
说明
0(000)
1,2,4,8,16
灰度
2(010)
8,16
RGB
3(011)
1,2,4,8
索引色
4(100)
8,16
有 Alpha 通道的灰度
6(110)
8,16
RGBA
虽然这个数据块给 Color Type 预留了8位空间,但目前只有低三位被使用,并且只有上述五种类型是被允许的。实际上每一位的数据是有自己的含义的,1 表示支持该属性,0 表示不支持,从最低位开始:
  • 第一位:是否有调色板
  • 第二位:是否有三通道
  • 第三位:是否有 Alpha 通道

PLTE

调色板(palette)数据块,包含了索引色图像(indexed color image)的相关数据,如果存在,要放在图像数据块(IDAT)之前。调色板实际上是一个彩色索引表,表的项目数范围是 1~256,每个项有 3 个字节分别用来储存RGB 三个值,所以调色板数据块最大字节数为 768。而调色板数据块只能有一个,所以调色板严重限制了图片的颜色种类。
PLTE的存在需要根据上面的 Color Type 来决定:
  • 索引色(Type 3)必须有该数据块
  • 真色彩(Type 2,6)图片也可以有该数据块的相关内容,目的是便于非真色彩显示程序量化图像数据
  • 灰度(Type 0,4)图片不允许出现该数据块

IDAT

存储实际的图片数据,一个图片数据流可能包含多个连续顺序的 IDAT 数据块。创建 IDAT 数据块的步骤:
  1. 将扫描获得的图片信息及其大小储存,具体的图片信息和大小规则在 IHDR 中保存
  1. 采取 IHDR 指定的过滤方法,对图像数据进行过滤。目前只有定义了method 0
  1. 采取 IHDR 指定的压缩方法,对过滤的数据进行压缩
可以看到 IHDR 非常重要,存储了很多关键信息,包括输出的数据和压缩算法。如果想要读取图片信息,将上面三个步骤逆序执行即可。
IDAT 数据块不是必要的,也可以同时存在多个,但是它们必须是连续排列的。

IEND

表示 PNG 文件结束,必须放在最后面,且 Chunk Data 为空。

辅助数据块

接下来用表格形式简单介绍一下各个辅助数据块:
符号
说明
bKGD
指定默认的背景颜色
cHRM
校准色度
dSIG
数字签名
eXIf
Exif metadata 信息
gAMA
指定亮度的伽马校准值
hIST
色彩直方图,估算颜色的使用频率
iCCP
iTXt
以键值对方式存储可能的压缩文本和翻译信息
pHYs
指定像素大小或图片比例
sBIT
除了指定位深度(8,16 等等)外,保存原始数据以便恢复
sPLT
提出建议使用的调色板,可能有多个
sRGB
指明是否使用sRGB color space
sTER
储存立体视图(stereoscopic)的相关信息,
tEXt
保存作者等键值对的文本信息
tIME
上次修改时间
tRNS
保存透明信息,分为不透明、全透明、Alpha
zTXt
保存压缩后的文本信息以及对应的压缩方式标识

压缩

上面介绍了一些关于 PNG 的基本概念,但了解这些都是为了一个终极目的:压缩。我个人的理解是,世界上没有真正的无损压缩,压缩代表占用的空间更小,必定损失了信息。只不过有些损失的信息不需要、不重要,甚至可以恢复,那么我们就说这时无损压缩。例如,显示在 web 页面和移动端的图标,它们的作用只是为了构建界面,如果我们不影响肉眼可辨别范围内的色彩、透明度等信息,仅仅删除了它附带的文本信息(如由Photoshop制作),这可以理解为无损压缩。
压缩的本质是对信息的浓缩,用更少的空间表达更多的信息。什么样的图片最适合压缩呢?抛开色彩丰富度不谈,就是最有规律的图片,例如一张纯色的图片。假设本来它需要为每个像素储存RBGA这些信息,现在我只需要保存一个像素,然后说明整张图片都由这个像素组成即可。这样的要求未免太过苛刻,不过规律还可以是变化的规律。
PNG的压缩分为两个步骤:预解析(过滤)和压缩。开始我们提到了过滤,它是通过寻找相邻像素的规律把图片变得容易压缩,例如对于下面这张图片:
notion image
这是一张渐变图片,从左到右依次变深,即使规律性不如纯色图片明显,但依然有规律可循。如果我们定义白色为 0,红色为 10 ,并且假定这张图片宽度只有 10 个像素,那么可以用数组表示出依次的颜色:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
我们希望找到相邻像素的规律,这里选择用数组记录和左边像素的颜色差值,可以表示为:[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
又找到了差值相同的规律,于是我们只需要说明每个像素的颜色都是左边像素加一,这样就只需要记录1这一个数字,再记录该规律就可以表达图片的信息了,节省了很多空间。
正式的压缩阶段会把预处理得到的数据进行 Deflate 压缩,它是一种流式压缩方法,同时使用了 LZ77 算法和 哈夫曼编码。它也是一种免费的压缩算法,我们常用的 zip、zlib、zopfli 等压缩方式都使用了 Deflate 算法。
参考资料:
Loading...

© 刘口子 2018-2025