什么是PNG格式?
PNG的全称叫便携式网络图形(Portable Network Graphics)是目前最流行的网络传输和展示的图片格式,原因有如下几点:
- 无损压缩:PNG图片采取了基于LZ77派生算法对文件进行压缩,使得它压缩比率更高,生成的文件体积更小,并且不损失数据。
- 体积小:它利用特殊的编码方法标记重复出现的数据,使得同样格式的图片,PNG图片文件的体积更小。网络通讯中因受带宽制约,在保证图片清晰、逼真的前提下,优先选择PNG格式的图片。
- 支持透明效果:PNG支持对原图像定义256个透明层次,使得图像的边缘能与任何背景平滑融合,这种功能是GIF和JPEG没有的。
PNG的种类
PNG图片主要有三个类型,分别为 PNG-8
, PNG-24
, PNG-32
。
PNG-8:PNG-8中的8,其实指的是8位索引色位图,相当于用 2^8 大小来存储一张图片的颜色种类,2^8等于256,也就是说PNG-8能存储256种颜色,一张图片如果颜色种类很少,将它设置成PNG-8得图片类型是非常适合的。
PNG-24:PNG-24中的24,相当于3乘以8 等于 24,就是用三个8bits分别去表示 R(红)、G(绿)、B(蓝)。R(0 ~ 255),G(0 ~ 255),B(0 ~ 255),可以表达256乘以256乘以256=16777216 种颜色的图片,这样PNG-24就能比PNG -8表示色彩更丰富的图片。但是所占用的空间相对就更大了。
PNG-32:PNG-32中的32,相当于PNG-24 加上 8bits的透明颜色通道,就相当于R(红)、G(绿)、B(蓝)、A(透明)。R(0 ~ 255),G(0 ~ 255),B(0 ~ 255),A(0 ~ 255)。比PNG 24多了一个A(透明),也就是说PNG 32能表示跟PNG 24一样多的色彩,并且还支持256种透明的颜色,能表示更加丰富的图片颜色类型。
png8 和 png24 的区别
1.png8和png24的根本区别,不是颜色位的区别,而是存储方式不同。
2.png8有1位的布尔透明通道(要么完全透明,要么完全不透明),png24则有8位(256阶)的布尔透明通道(所谓半透明)。png-8 和 gif 有一些相似之处,模式都是索引颜色,只支持像素级的纯透明,不支持 alpha 透明。
我们通常说的“IE6 不支持 PNG 透明”,是指不支持 PNG-24 的透明。但是 IE6 支持 PNG-8 的透明,就像支持 gif 的透明一样。
PNG文件结构(重点内容)
用一张图来简单表示一下,PNG的文件结构大概是这样的:
文件头(Header)
文件标识
十六进制数据:89 50 4E 47 0D 0A 1A 0A
ASCII码数据: ‰PNG
数据块(Chunk)
PNG定义了两种类型的数据块,一种是称为重要数据块(critical chunk)
,这是标准的数据块,还有一种叫做辅助数据块(ancillary chunks)
,这是可选的数据块。重要数据块定义了4个
标准数据块,每一个PNG文件都必须包括它们,PNG读写软件也都必需要支持这些数据块。尽管PNG文件规范没有要求PNG编译码器对可选数据块进行编码和译码,但规范提倡支持可选数据块。
PNG文件中,每个数据块由4个部分组成,如下:
名称 | 字节数 | 说明 |
---|---|---|
Length (长度) | 4字节 | 指定数据块中数据域的长度,其长度不超过(231-1)字节 |
Chunk Type Code (数据块类型码) | 4字节 | 数据块类型码由ASCII字母(A-Z和a-z)组成 |
Chunk Data (数据块数据) | 可变长度 | 存储按照Chunk Type Code指定的数据 |
CRC (循环冗余检测) | 4字节 | 存储用来检测是否有错误的循环冗余码 |
重要数据块(critical chunk)
包括四个:IHDR
、PLET
、IDAT
、IEND
IHDR 数据块
IHDR(header chunk 文件头数据块)是第一块数据块,是重要数据块(critical chunk),包含PNG图像存储的图像数据基本信息。一个PNG数据流中只有一个文件头数据块。
IHDR数据块包含以下内容:
域的名称 | 字节数 | 说明 |
---|---|---|
Length (长度) | 4 bytes | 指定数据块中数据域的长度,其长度不超过(231-1)字节 |
Chunk Type Code (数据块类型码) | 4 bytes | 数据块类型码由ASCII字母(A-Z和a-z)组成 |
Width | 4 bytes | 图像宽度,以像素为单位 |
Height | 4 bytes | 图像高度,以像素为单位 |
Bit depth | 1 byte | 图像深度:索引彩色图像:1,2,4或8 灰度图像:1,2,4,8或16 真彩色图像:8或16 |
Colour Type | 1 byte | 颜色类型:0: 灰度图像, 1,2,4,8或16 2: 真彩色图像,8或16 3:索引彩色图像,1,2,4或8 4:带α通道数据的灰度图像,8或16 6:带α通道数据的真彩色图像,8或16 |
Compression method | 1 byte | 压缩方法(LZ77派生算法) |
Filter method | 1 byte | 滤波器方法 |
Interlace method | 1 byte | 隔行扫描方法:0:非隔行扫描 1: Adam7(由Adam M. Costello开发的7遍隔行扫描方法) |
CRC (循环冗余检测) | 4 bytes | 存储用来检测是否有错误的循环冗余码 |
PLET数据块 (palette chunk)
调色板数据块PLTE(palette chunk)包括有与索引彩色图像(indexed-colour image)相关的彩色变换数据,它仅与索引彩色图像有关,并且要放在图像数据块(image data chunk)之前。
PLTE数据块是定义图像的调色板信息,PLTE能够包括1~256个调色板信息,每个调色板信息由3个字节组成:
颜色 | 字节 | 意义 |
---|---|---|
Red | 1 byte | 0 = 黑色, 255 = 红 |
Green | 1 byte | 0 = 黑色, 255 = 绿色 |
Blue | 1 byte | 0 = 黑色, 255 = 蓝色 |
因此,调色板的长度应该是3的倍数,否则,这将是一个非法的调色板。
对于索引图像,调色板信息是必须的,调色板的颜色索引从0開始编号,然后是1、2……,调色板的颜色数不能超过色深中规定的颜色数(如图像色深为4的时候,调色板中的颜色数不能够超过2^4=16),否则,这将导致PNG图像不合法。
真彩色图像和带α通道数据的真彩色图像也能够有调色板数据块,目的是便于非真彩色显示程序用它来量化图像数据,从而显示该图像。
IDAT 数据块
存储实际的数据,在数据流中可包括多个连续顺序的图像数据块。
名称 | 字节数 | 说明 |
---|---|---|
Length (长度) | 4字节 | 除了最后一个IDAT数据块,每一个数据块长度相等 |
Chunk Type Code (数据块类型码) | 4字节 | IDAT |
Chunk Data (数据块数据) | / | 图像数据 |
CRC (循环冗余检测) | 4字节 | 存储用来检测是否有错误的循环冗余码 |
图像结束数据块
用来标记PNG文件或者数据流已经结束,并且必须要放在文件的尾部。
名称 | 字节数 | 说明 |
---|---|---|
Length (长度) | 4字节 | 总是 0( 00 00 00 00 ) ,除非被修改 |
Chunk Type Code (数据块类型码) | 4字节 | 总是IEND(49 45 4E 44),除非被修改 |
Chunk Data (数据块数据) | / | / |
CRC (循环冗余检测) | 4字节 | 总是 AE 42 60 82 ,除非被修改 |
辅助数据块
数据块符号 | 数据块名称 | 多数据块 | 位置限制 |
---|---|---|---|
cHRM | 基色和白色点数据块 | 否 | 在PLTE和IDAT之前 |
gAMA | 图像γ数据块 | 否 | 在PLTE和IDAT之前 |
sBIT | 样本有效位数据块 | 否 | 在PLTE和IDAT之前 |
bKGD | 背景颜色数据块 | 否 | 在PLTE之后IDAT之前 |
hIST | 图像直方图数据块 | 否 | 在PLTE之后IDAT之前 |
tRNS | 图像透明数据块 | 否 | 在PLTE之后IDAT之前 |
oFFs | (专用公共数据块) | 否 | 在IDAT之前 |
pHYs | 物理像素尺寸数据块 | 否 | 在IDAT之前 |
sCAL | (专用公共数据块) | 否 | 在IDAT之前 |
tIME | 图像最后改动时间数据块 | 否 | 无限制 |
tEXt | 文本信息数据块 | 是 | 无限制 |
zTXt | 压缩文本数据块 | 是 | 无限制 |
fRAc | (专用公共数据块) | 是 | 无限制 |
gIFg | (专用公共数据块) | 是 | 无限制 |
gIFt | (专用公共数据块) | 是 | 无限制 |
gIFx | (专用公共数据块) | 是 | 无限制 |
PNG的压缩
虽然说 png 是无损格式,但是并不代表png不能被压缩了,毕竟PNG-32的体积可不小。而且PNG的压缩过程是完全无损的,压缩过的文件可以准确的还原出原图。
PNG图片的压缩分为两个阶段:过滤 和 压缩
过滤
png图片用差分编码(Delta encoding)对图片进行预处理,处理每一个的像素点中每条通道的值,PNG允许5种不同的算法,由于每一行像素不同可能采取不同的过滤算法:
-
不过滤
-
X-A
-
X-B
-
X-(A+B)/2(又称平均值)
-
Paeth推断(这种比较复杂)
[五种算法示意图]
假设,一张png图片如下:
这张图片是一个红色逐渐增强的渐变色图,它的红色从左到右逐渐加强,映射成数组的值为[1,2,3,4,5,6,7,8],使用X-A的差分编码的话,那就是:
[2-1=1, 3-2=1, 4-3=1, 5-4=1, 6-5=1, 7-6=1, 8-7=1]
得到的结果为
[1,1,1,1,1,1,1]
最后的[1,1,1,1,1,1,1]这个结果出现了大量的重复数字,这样就非常适合进行压缩。
这就是为什么渐变色图片、颜色值变化不大并且颜色单一的图片更容易压缩的原理。
差分编码的目的,就是尽可能的将png图片数据值转换成一组重复的、低的值,这样的值更容易被压缩。
最后还要注意的是,差分编码处理的是每一个的像素点中每条颜色通道的值,R(红)、G(绿)、B(蓝)、A(透明)四个颜色通道的值分别进行处理。
压缩
在一行像素被过滤后,就会执行DEFLATE压缩,这是LZ77延伸出来的一种算法。该算法结合了LZ77编码和哈夫曼编码,它跟PKWARE、PKZIP、GZIP等差不多相同。这种实现方式虽然是现成的,但用在压缩图片数据上,还是有一些需要注意的点:
-
Deflate算法只能匹配3到258个之间符号,所以最大的压缩比只能到1035:1;
-
如果匹配到的符号小于3,那么你会产生一些额外的开销来表示这些符号;
上面的这两点意味着你的图片大小会受到每一行像素的匹配程度影响。
你可以看一下面这两张图片,左边那张270x90的图只有20k,而右边那张270x92的图是左边那张的2倍大。
这似乎不符合逻辑,一张图片多540像素在压缩率上就少了一半。不过我们看仔细点,就能知道原因了,下面这张图表示压缩器怎么去压缩一个给定的像素的。深蓝色代表压缩率很高的区域,黄色/红色代表没怎么被压缩的区域。
这是怎么出现的呢,原因是小图的每一行像素的匹配度更高,那么它的压缩率就更高。你要是调整了大小,匹配度一变化,这种情况就有可能出现。一些潜在的匹配对象不在压缩区域里,它们没有匹配到,这就又可能导致出现一张大图。
减小PNG图片体积的技巧
- 颜色简单的图片用位深度更低的png格式
可以用PS或者其他工具 - 删掉元数据