gif历史请参考上一篇《GIF图片格式详解(一)》,或直接访问博客地址:https://blog.whatsroot.xyz/2023/12/16/all-about-gif/
文件格式概述
GIF格式采用调色板模式,即有一个颜色表,每种颜色可以使用RGB24格式存储,最多可以有256种颜色。这256种颜色构成了GIF的调色板(RGB24每个颜色3字节,256颜色则3*256=3 * 0x100 = 0x300个byte)。之后对于原始图像中的每个像素,不再使用具体的像素值,而是直接使用这个颜色在调色板中的索引值(索引值应该是0-255之间的数字)。
这样,对于某个颜色种类小于256种的图片,则可以实现无损压缩。对于超过256种颜色的图片来说,无论如何也是会产生信息损失的,只不过可以使用一些技巧把这种信息损失降到最低,比如使用Dither方法具体的方法就不在这里讨论了,可参考链接。
对于一张分辨率较高的图片,由于像素数目比较多,而颜色又总共只有256种,必然又大量重复的索引值,GIF使用了LZW这种无损编码算法进一步对数据进行了压缩。关于LZW专利,这里面还有一场早期的互联网斗争。LZW是被Unisys这家公司申请了专利的,但是当时没人注意到这一点,或者没人在意。
等到1994年,Unisys这家忽然宣布要所有使用LZW的公司都要付费。甚至到1999年时又修改了许可条款,未经允许任何人都无法使用。最终引发了一场“Burn GIFs”的运动,即要把所有GIF图片转换为PNG格式,客观上也促进了PNG格式的发展。甚至GIF也推出过方案使得索引值可以不经过LZW压缩,以此来规避专利问题。当然Unisys的专利已于2004年到期,现在GIF真正属于互联网了。
在GIF的89a版本中,添加了对透明度及动态图的支持。这里的透明度,只有0跟1两个值,即完全透明跟完全不透明。对动态图的支持,表明GIF支持多帧图像,且GIF支持每帧图像都有自己的调色板,这样就可以通过把原图划分为多个区域,每个区域都用256种颜色来表示,最后再利用透明度这个功能在不同的帧中只显示自己的部分,最终把不同的区域叠加起来,原则上就可以创造出远多余256种颜色。只不过采用这种方法需要多帧,会增大GIF本身体积,虽然原理上可行,但是实用价值不大。下面的图片用来展示这个方法。
具体文件格式
下面介绍GIF的具体文件格式,以a89为例,GIF的格式组成由下图,
注意实线框为必须项,虚线框为可选项。
上图是个极其简单的GIF图,它的分辨率只有10x10,最多只支持4种颜色,每种颜色用2位二进制数表示。
上面是这个GIF图的二进制数据,我们分别来看GIF的各个字段:
- Header Bloack
数据为47 49 46 38 39 61
,其实就是GIF89a的ASCII码。
- Logical Screen Descriptor
数据为0A 00 0A 00 91 00 00
, 逻辑屏幕描述符紧随标题之后,总共7个字节。
-
Canvas width, Canvas height
这里的宽度跟高度不一定就是整个图像的宽度跟高度,如果分块的话,就是分块的宽度跟高度,这个字段的目的就是给分块提供可能。
-
Packed Filed
-
global color table flag
全局颜色表标志,为0,表示没有全局颜色表。为1,表示有全局颜色表(我们的例子中有一个全局颜色表 -
color resolution.
它代表的是颜色位深。只有在有全局颜色表时才有意义。如果这个字段的值是N,则位深为N+1, 能表示的颜色种类总数目将是2^(N+1)。
样本图像中的001代表2位/像素;111将代表8位/像素。 -
sort flag
非必须,如果为1,则表示全局颜色表中的颜色按照重要性递减的顺序排序,即图像中出现的频次递减。
对解码有帮助,但不是必须的。 -
size of Global Color Table
全局颜色表中的颜色个数,比如值为N,则颜色表个数为2^(N+1) -
background color index
背景颜色的索引,这个索引对应的全局颜色表中的颜色,将会被认为为背景
-
pixel aspect ratio
像素宽度与高度的比值。这个参数几乎被所有现代浏览器忽略,具体用处可能与当年的模拟电视图像有关。
- Global Color table
数据为FF FF FF FF 00 00 00 00 FF 00 00 00
,使用RGB24格式表示的调色板,个数根据上面的size of Global Color Tabl
来决定。
- Graphics Control Extension
数据为21 F9 04 00 00 00 00 00
,这部分是89a的扩展内容,目的是支持透明度以及动态图。本例中的图片没有透明度及动画,
这两方面的内容可参考Transparency and
Animation
- extension introducer
固定为0x21
- graphic control label
固定为0xF9
- block size
总共的字节数,通常四个字节
- Packed Filed
共1个字节,其中前三位保留,接下来的三位为disposal method, 用来制定切换到下一幅图像应该如何处理,共可表示0-7,动画图像的值是 1,这表明解码器应该保持当前图像不变,并在其上绘制下一幅图像。如果是 2,就意味着画布应恢复到背景色;3 则表示画布应恢复到绘制当前图像之前的状态。
据我所知,这个值并不被广泛支持。对于 4 到 7 的值,其行为还没有定义。如果这个图像不是动画,这些位通常会被设为 0,表示没有特定的处理方法。
第7位是用户输入标志,当其为 1 时,表示解码器会等待用户输入才会切换到下一幅图像。不过,在大多数情况下,这个位的值会是 0。最后一位是透明度标志。如果需要透明度,则需要设置为1,否则设置为0
- delay time
用来控制动态图的帧率,单位是百分之一秒,即10毫秒。
- Transparent Color index
透明颜色索引。指定哪个颜色为透明,哪个颜色就像涂了隐身药水变透明
- Block terminator
块结束符号,通常00
- Image Descriptor
数据为2C 00 00 00 00 0A 00 0A 00 00
,
第一个字节是图像分隔符,每个图像描述符块都以 2C 作为起始值。接下来的 8 个字节用于表示随后图像的位置和大小。一个 GIF 文件可以包含多幅图像。在最初的 GIF 设计中,这些图像是为了组合到一个更大的虚拟画布上。然而,如今多幅图像通常用于制作动画。
每幅图像都从同一个图像描述符块开始,本例中这个块的长度恰好为 10 字节。在 GIF 中,图像并不一定要占据逻辑屏幕描述符定义的整个画布大小。因此,图像描述符块会指定图像在画布上的起始左边距和上边距位置。但现代的查看器和浏览器通常会忽略这些字段。接下来,这个块会指定图像的宽度和高度。这些值都是使用两字节、无符号、小端格式表示的。我们的示例图像显示,图像从 (0,0) 开始,宽度和高度均为 10 像素(这意味着图像占据了整个画布)。
最后一个字节又是一个Packed Filed。在我们的示例文件中,这个字节是 0,因此所有的子值都将是零。
-
local color table flag
局部颜色表标志。将这个标志设置为 1 允许您指定随后的图像数据使用的颜色表与全局颜色表不同。 -
interlace flag
交错标志。交错改变了图像呈现在屏幕上的方式,可以减少令人讨厌的视觉闪烁,视觉闪烁的原因,又要追溯到上世纪模拟显示器的扫描方式。
交错在显示器上的效果是,先显示图像其中一部分,这样观众可以先看到一个模糊的图像,然后逐渐清晰,最后完全显示,也是在那个宽带极其有限的情况下的产物。
防止屏闪可以不考虑,因为现在的显示器不会有这个问题。关于显示效果举个例子一看就明白了,左边是顺序显示,右边是交错显示。
- 其余sort flag, size of local color table
与前文描述一致,只不过是局域颜色表
-
Local Color Table
与全局颜色表一致,需要local color table flag为1生效
-
Image Data
数据为02 16 8C 2D 99 87 2A 1C DC 33 A0 02 75 EC 95 FA A8 DE 60 8C 04 91 4C 01 00
,
通常情况下这部分才是GIF最主要的内容,只不过我们举的例子太小了所这这部分内容较少,
需要说明的是这部分需要LZW编码。具体编码方案这里不做介绍。
- Plain Text extension
数据内容为:21 01 0C 00 00 00 00 64 00 64 00 14 14 01 00 0B 68 65 6C 6C 6F 20 77 6F 72 6C 64 00
。
GIF 89a标准允许在接下来的图像上添加文本标题。然而,这个功能并没有广泛流行;像 Photoshop 这样的浏览器和图像处理软件通常会忽略这一功能。就像所有扩展块类型一样,这个块以扩展引入符开始,这个值总是 21。下一个字节是纯文本标签, 这个值 01, 用于将纯文本扩展与所有其他扩展区分开来。接下来的字节是块大小,表示实际文本数据开始之前有多少字节,或换句话说现在可以跳过多少字节。字节值可能是 0x0C,这意味着应该向前跳过 12 个字节。随后的文本被编码在数据子块中,当到达长度为 0 的子块时,该块结束。
- Application extension
数据内容为:21 FF 0B 4E 45 54 53 43 41 50 45 32 2E 30 03 01 05 00 00
,
GIF89规范允许在 GIF 文件本身中嵌入特定于应用程序的信息。这种能力并没有得到广泛使用。
大约唯一已知的公开使用是就是前面提到的 Netscape 2.0 扩展,它用于循环播放动画 GIF 文件
- Comment Extension
数据内容为:21 FE 09 62 6C 75 65 62 65 72 72 79 00
GIF89 规范中的最后一个扩展类型是评论扩展。这允许你在 GIF 文件中嵌入 ASCII 文本,通常用于添加图像描述、图像版权信息或其他人类可读的元数据,如图像捕获的 GPS 位置。这个扩展的第一个字节是扩展引入符,编号为 21。接下来的一个字节总是 FE,代表评论标签。然后我们直接跳到包含评论ASCII 字符代码的数据子块。从例子中我们可以看到,这里有一个长度为 9 字节的数据子块。如果你将这些字符代码翻译出来,会发现评论内容是“blueberry”。最后一个字节 00 表示一个没有后续字节的子块,标志着我们已经到达了这个块的末尾。
- Trailer
数据内容为:3B