为什么GIF文件是图像文件而不是视频文件?(GIF文件格式详解)

今天整理硬盘的时候想到一个问题:GIF 是放在静态图像文件里还是视频文件里?
因为放视频里吧,GIF 的分类是静态图像,而且由图像组成;放图像里吧,它又会动。

然后我就开始想:GIF 会动但为什么被归类在静态图像?

看了一下国内外的一些博客,得到的答案不能让我满足,就是感觉还差一点的感觉🤏;或者有些说到了一些点上,但是没有继续深入了。所以就自己做了点研究,写下本博客,希望能帮到和我一样有这个疑问的人。

本文按照“GIF 文件格式(技术原理)”->“GIF 的历史”->“结论”的顺序。一些关于技术的历史介绍也会放在“GIF 的历史”中。

GIF 文件格式(技术原理)

如果想深入解释这一点,需要先简单介绍一下 GIF 的文件格式,这样才能了解为什么历史上会出现 GIF 格式。如果你想深入了解 GIF 文件格式,这里有篇文章写的挺细致,也是本文的参考资料之一,如果你能阅读英文的话非常建议看看:《LZW and GIF explained----Steve Blackstock》

GIF 文件由 5 大部分组成:GIF 签名,屏幕描述符,全局颜色映射,图像描述符,本地颜色映射,光栅数据和 GIF 终止符。
请添加图片描述

为了方便理解,这里直接举个例子来详细说明。
请添加图片描述

以上面这个 GIF 为例,使用任何十六进制查看器查看可以得到如下内容:

请添加图片描述

GIF 签名(标头)

在 UNIX 系统中,文件类型是由标头表明的,与后缀无关。GIF 文件的标头则是 GIF 签名,有 6 个字节,有两个版本:“GIF87a(47h 49h 46h 38h 37h 48h)”和“GIF89a(47h 49h 46h 38h 39h 48h)”。
屏幕描述符中说明了图像的尺寸和一些图像相关的信息,是上图中图像“尺寸”、“信息”、“背景颜色”、“表示结束”四个框框的 7 个字节:

  • 尺寸信息占 4 个字节,长宽各一半,也就是长和宽的最大值为65536(2^16)。这个 GIF 尺寸为 72x72,72 的十六进制为 48h,可以看到上图中正是“48”。这个需要注意两点:GIF 使用的是大端,也就是说,这里的长应该是0048h,后面的00要放到前面去;第二,文档写的是屏幕尺寸,这是图形系统和计算机历史的问题,所以这里不过多解释。如果感兴趣可以看看The 8-Bit Guy 的一系列关于早期计算机图形系统的介绍,相对来说没有那么枯燥。
  • 图像信息占 1 个字节,但是包含了很多信息,所以我们要将其写成二进制讲解。上图中f6写成二进制为“1111 0110”。
    • 第 1 位为全局颜色映射符号,表示后面是否有全局颜色映射,这里1表示有;
    • 第 2~4 位的值加一表示色深,这里是111,也就是 7,加一就是 8,也就是说色深为 8 位,最多 256 个颜色(也是最大值了),该值是给颜色映射使用的;
    • 第 5 位是为未来保留的位,必须为0
    • 第 6~8 位加一表示图像中每个像素的位数,这里为110,也就是 6,加一为 7,也就是说这张图单个像素大小为 7 位,最多 128 个颜色,该值与图像有关。
  • 背景颜色为00,也就是白色。
  • 最后的00表示结束。

全局颜色映射

上图中第一行最后到00000180行之前的区域就是全局颜色映射的部分了。全局颜色映射虽然可选,但是现代的 GIF 基本上都有,是用来表示每个像素的颜色的。

何为颜色映射

这里本来想省略对与颜色映射的解释,但是由于这个技术有点太古早了,很多人可能不了解就详细说说。
现在很多人都知道,屏幕上的颜色大多是以红绿蓝三个颜色组合而成的(印刷行业不同)在表达的时候是用红绿蓝三个颜色的值这种格式。但是为什么我们确定一个“RGB”值就可以确定一个颜色呢?你可能会说:是去色域中按照这个值提取的颜色,然后显示出来。
这样说很接近了。现在麻烦您将自己想象成一个上世纪八十年代的游戏开发者,你需要只用用 8 bits 就显示出比较细腻的颜色。你心里会想 8 bits 也就 256 个值,还要三个颜色一起用,不可能显示出比较细腻的颜色的。(现在最常见的 8 bit 屏幕单个像素为 24 bits,也就是 16777216 个颜色)
但是你注意到没有,我一直说的是多少个颜色,这个颜色不一定是均匀分布的哦。比如说上面那个 GIF 的画面中,主要颜色为黑、深蓝、红、白,组成非常简单,并不是由很复杂的颜色组成的。那么我们就可以将 8 bit 转换成 256 个值,每个值对应映射一个图中会用到的颜色,这样就可以显示出来比较细腻的画面了。红白机这些早期游戏机上的游戏很多都是通过这种技术,从而达到了超出规格的画面效果。
可以把这个颜色映射理解成调色板,系统或显示器支持的颜色空间理解成颜料盒,颜料可能有几十万个颜色,但是调色板只能放几十个颜色。 比如说在24 位的 RGB 颜色空间中创建单像素大小为 8 位的 GIF 时,就是从 16777216 个颜料中取出 256 个颜色(局部颜色映射就是给每个图像一个调色板)。

那么继续介绍全局颜色映射。颜色映射一般情况下是按照“纯黑到纯白”的顺序排列的。也可以按照出现频率排列或者其他因素排列(详情请跳转到“图像描述符”部分)。
比如说上图中的03h 03h 03h,这个颜色是小人的发绳颜色最深处的颜色:
请添加图片描述
17h 17h 17h是第二深的颜色:
请添加图片描述

如何计算全局颜色映射区域的大小

需要注意的是:GIF 文件的全局颜色映射区域不是固定大小的,不同文件可能是不一样的。大小通过下面这个式子计算:

(1 <<((图像信息那个字节的值 & 0x07) + 1)) * 3

例如上面的f6就是:

(1 <<((0xf6 & 0x07) + 1)) * 3

0xf6 & 0x07 = 1111 0110b
			& 0000 0111b
			————————————
			  0000 0110b = 6
			  
1 <<((0xf6 & 0x07) + 1)) = 1 << 7 = 1000 0000b = 128

(1 <<((0xf6 & 0x07) + 1)) * 3 = 128*3=384

所以上面的全局颜色映射的数据块大小为 384 个字节。

扩展块

在这个地方,可能还会有一个或多个扩展块,以21h开头,00h结尾。可以用来说明动画间隔和背景透明度等很多相关信息。
扩展块的大小是固定的,不过 87a 和 89a 的不一样大。如果是 87a,那么这部分是图形控制扩展块(8 字节)。
如果是 89a,那么会有图形控制扩展块(8 字节)、纯文本扩展块(15 字节)、应用程序扩展块(14 字节)、注释扩展块(5~256 字节) 这 4 个扩展块,每个扩展块都以21h开头,区分在于第二个字节:

  • 如果是f9h,那么将数据块标识为图形控制扩展块;
  • 如果是01h,那么将数据块标识为纯文本扩展块;
  • 如果是ffh,那么将数据块标识为应用程序扩展块;

还是以上面的图为例,截取扩展块的部分:
请添加图片描述
两条红线之间的字节便是扩展块的部分。

首先21h ffh说明为应用程序扩展块。
0bh为应用程序标识符和 Authent Code 字段中的字节数(这个值是固定的,必须是0b)。AuthentCode 字段包含一个值,该值用于标识创建应用程序扩展块的软件应用程序。如果识别此标识符值成功,则读取数据块的剩余部分,并对其数据进行操作。如果无法识别标识符值,则读取数据块的剩余部分并丢弃其数据。
接下来的4e 45 54 53 43 41 50 45(十六进制)就是应用程序标识符,根据 ASCII 可以得到“NETSCAPE”。32 2e 30(十六进制)则为 Authent Code,根据 ASCII 可以得到“2.0”。这后面则是取决于应用程序和开发者。最后00h结束块。

Netscape 是第一个允许用户与图像交互的浏览器,比如说创新性地可以点击图像跳转页面。

然后21h f9h表示为图形控制扩展块。
接下来的04h是固定的值,为 Packed、DelayTime 和ColorIndex 字段的字节数总和,也就是一共 4 个字节。(如果是 GIF87a 则只有一个21h
Packed 字段有 4 个部分,上图中为04h,二进制为0000 0100

  • 第 1 位用来表示 ColorIndex 字段中颜色透明度字段是否有值,这里为0说明没有值。
  • 第 2 位是用户输入标志,如果进入下一个图像序列之前需要用户输入(按下某键或者点击鼠标),那么此处为1,否则为0。这里为0说明不需要用户操作即可进入下一图像。
  • 第 3~5 位表示图像显示后如何处理,(这里的数字为二进制)
    • 000(未指定处置方法)
    • 001(不处理图形),图中是001,所以就没有任何操作。
    • 010(用背景颜色覆盖图形)
    • 011(用以前的图形覆盖图形)
  • 第 6~8 位是保留的,不过 GIF89a 中不实用此子字段,所以始终为0

可以看到图形控制扩展块的内容可以让 GIF 变得可以像 PPT 一样。

接下来的 1 个字节便是 DelayTime 字段,内容为0ah,表示等待 0.1 秒再播放下一图像。0.1 秒也就是 1/10 秒,也就是可以理解成每秒显示 10 帧。
下面 2 个字节是颜色透明度,这里是00h,而且前面 Packed 字段中第一位是0。也就是说没有这个字段略过,也就是没有调整颜色透明度。
紧接着的ffh这个字节应该是00h,这里应该是软件自己的特色。
最后00h表示扩展块结束。

图像

然后便是 GIF 中每一张图像的内容了。GIF 中的图像数据使用 LZW(Lempel-Ziv-Welch)算法进行压缩,由2ch(ASCII 对应,)开始,并且由三部分以下组成:

  • 图像描述符;
  • 局部颜色映射(这个当然也是可选的了);
  • 光栅数据。
图像描述符(Local Image Descriptor)

2024 年 10 月 14 日更新:
非常感谢评论指出这里的一处错误:GIF 的图像描述符并没有结束符号。于是我重新看了一下文档,并进行了相应的修改。

图像描述符又被称为图像标题,以2ch开始,一共 10 个字节(含2ch),也就是下图中的2c 00 00 00 00 48 00 48 00 00(不含最后的07h):
请添加图片描述

这部分内容包含了图像描述符的分隔符2ch(一个字节),图像的坐标 X 和 Y(各两个字节)、图像的长和宽(各两个字节)、图像和颜色映射数据的信息(一个字节)。
由上图可知,图像的坐标 X 和 Y 都为 00h 00h。图像尺寸和前文一样,长宽都是 72x72 像素。

最后一个字节存储的图像和颜色映射数据的信息有 4 个子字段,这里的00h二进制为0000 0000,那么:

  • 第 1 位为局部颜色映射标志,如果此位为1,则图像有个局部颜色映射,使用局部颜色映射;如果为0,那么使用全局颜色映射。图中这里为0,说明使用全局颜色映射。
  • 第 2 位为隔行生成标志,如果此处为1,那么图像是隔行生成的,0反之。图中这里为0,说明不是隔行生成。
  • 第 3 位是分类标志,用于表示颜色映射是否按照出现频率排列(或者其他因素)。不过只适用于 GIF89a。如果是 GIF87a,这部分始终为0。图中这里为0,说明没有排序颜色映射。
  • 第 3~4 位是保留位,始终为0。
  • 第 5~8 位是局部颜色映射条目的数量,用来表示局部颜色映射条目的数量。这里为0,说明此图像没有局部颜色映射。

局部颜色映射

局部颜色映射和全局颜色映射是一样的。而且由上一节可知,这里没有局部颜色映射,所以没啥好说的了。

光栅数据

光栅数据(Raster data)就是图像数据(Image data)。
这部分数据由一块一块的数据构成。每一块由“块大小的计数字节”开始,值为 1~255(1h~ffh)。每一块的数据都使用 LZW(Lempel-Ziv-Welch)算法进行压缩。

还是以上面的例子说明一下,第一个图像的光栅数据是从1b3h的这个ff开始的,ff表示接下来的这块数据为 255 字节:
请添加图片描述

那么我们继续往下翻可以看到2b3h3b3h4b3h5b3h 都是ff,也就是往后挪 256 位:
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述

但是6b3h这个位置上是c3
请添加图片描述

那么就往后挪 196(c3h + 1 = 195 + 1=196)位看看,也就是777h(6b0h + c3h + 1h)这个位置:
请添加图片描述

可以看到这个位置为00,这表示当前图像块的光栅数据结束了(因为这里需要理解成下面的这块数据块有 0 个,然后就没有然后了)。

然后往后继续读取,在下一行780h的位置可以看到2c,表示新的图像描述符开始了。就这样循环往复,直到结束。

但是需要注意的是,不是每个块都是 255+255+…+xxx 这样美好的格式,有的是乱的,只要每个值在

GIF文件结尾

如果你了解一些底层的知识,会知道文件和数据可以没有开始符,但是需要结尾符。例如 C(或者 Python 这种 C 延伸出来的语言)的字符串都是以\0表示字符串结尾的。因为这样作为数据流在 UNIX 系统中就知道在哪里结束。那么 GIF 文件的结尾是什么呢?
GIF 的终止符是;,十六进制为3b
请添加图片描述

2024 年 10 月 14 日更新:

这里的02 12 50 a2 a9 32 90 01 44 42 a0 8b c4 18 c1 06 07 11 08 00是最后得到的 LZW 图像数据:

  • 第 1 个字节02是 LZW 最小码尺寸,这里为 2 字节。
  • 第 2 个字节12表示接下来有多少字节数据是图像数据,这里为18
  • 接下来的 18 个字节就是图像数据50 a2 a9 32 90 01 44 42 a0 8b c4 18 c1 06 07 11 08 00,其中最后的00是块终止符。

LZW 图像数据的细节可以看这篇文章:LZW Image Data - GIFLIB documentation

GIF的静态图像长什么样

在 GIF 诞生的头几年,GIF 只有静态图像这一个用法。
下面这个图像是一个 GIF 文件(源自《Encyclopedia of Graphics File Formats, 2nd Edition》,是 2005 年的一本书),只有一个图像,分辨率为 1419 × 1001,你可以右键下载看看原图:
请添加图片描述

GIF 的历史

诞生

背景知识

GIF 诞生在互联网出现之前。在互联网出现之前,有很多计算机厂商,这些厂商不像今天的厂商,只负责组装或者生产一部分硬件,当时大部分计算机厂商是要负责开发计算机的系统,甚至包括软件,例如现在大名鼎鼎的苹果就是当时众多计算机厂商中的一员。
不同的硬件、不同的目的导致设计出来的系统和应用也不是互相兼容的,当时买电脑会送一本书,这本书会告诉你如何在这台电脑上编程。比如下面的 Apple II 的说明书:

请添加图片描述

目的

当时CompuServe 的老板 Sandy Trevor 需要程序员为他解决两个主要问题:
首先,CompuServe 为需要访问电子邮件和传输文件的用户提供订阅(按小时计算),这需要一种可以在所有计算机上无缝显示的简单图形格式。也就是说,这个文件必须要通用。
其次,可能有些用电脑比较早的人还会记得,很早之前用拨号上网的时候,由于网速很慢,浏览网页会出现图片一点点往下刷新,如果网络出问题了,等了半天的图片就没了,需要刷新页面重新下载。所以这个文件必须要小。
最后,由于当时电脑内存太小了,所以不能占用太多的内存的同时,还要保持画面质量。

为了解决这个问题 CompuServe 就开发了 GIF。GIF 可以做到将数据流按照一块块地传输到客户端,然后逐步显示图片。或者隔行生成图像,那么就可以将图像隔行传输数据给客户端,这样虽然画面有损耗,但是可以脑补一下。比如说下面这样的图片隔行显示,看到下图中左边和中间的图像都可以大致脑补出图片内容,而逐行显示如果一点点从上到下显示是没办法脑补的。

请添加图片描述

替代品

GIF 虽然是无损压缩的,但是单张图像的颜色最多只有 256 色。如果想实现颜色细腻的话可以使用拼接法,这样会导致文件过大,以及过于复杂。再者说就算费大劲解决了前面的问题(这个时候文件大小会非常恐怖),最高只能支持 24 位的 RGB 色彩空间也使得 GIF 的色彩是不会特别细腻的(现在的屏幕)。
其他方面,GIF 理论上最多可以显示约 43 亿像素的图片(这点可以看下面“GIF 文件格式”的内容),这点直到现在都不落伍。
但是 GIF 的尺寸和颜色一直是两大致命问题,所以就发明了 PNG 格式用来淘汰 GIF,甚至 PNG 的全称就是“PNG’s not GIF”,不过这是 1995 年之后的故事了,此时 GIF 已经诞生了快十年。
现在的我们可以看到,PNG 作为无损压缩图片很成功地在静态图像方面替代了 GIF,全方位优于 GIF(而且当时 GIF 由于使用 LZW 算法的原因是有专利限制的,而 PNG 是完全开源的)。但是 GIF 的动态图像却一直没有可替代者。
1999年的互联网上还有过一段全方位销毁 GIF 的活动,但是没有成功,现在也还有这样的行为,感兴趣可以看看https://burnallgifs.org/archives/
没有成功是因为没有任何一个文件可以做到又容易实现、无声、又可以循环播放一个小动画。还有一个更重要的原因就是:GIF 是不需要任何解码器的就可以播放的。这样几乎所有的浏览器都可以播放 GIF 文件。
2001 年虽然围绕着 PNG 诞生了 MNG(Multiple-image Network Graphics)多图像文件,并且功能和画质远好于 GIF,但是还是没有战胜 GIF(个人猜测是因为过于复杂了,MNG 甚至可以加上音轨,这种程度不如直接使用 MP4 等视频格式)。

结论

GIF诞生之初是为了作为静态图片使用,就相当于今天的 PNG 和 JEPG。不过它的结构可以理解成一个简易版 PPT。pptx 文件是由 xml 和 图像文件组成的一个 tar 包,GIF 文件是由一些信息块、控制块和 LZW 压缩过后的光栅图像构成的,而且很巧合的是 Power Point 和 GIF 是同一年诞生的。但是可以通过控制显示 GIF 文件中每个光栅图像之间的间隔和动画,使得它可以呈现出动画/运动的效果(就像幻灯片可以做动画片,但是不能说幻灯片是视频一样)。

大胆的推测

由于时间太过久远,我有一个想法无法得到验证。就是那个时候是不是有一股技术热潮,内容就是如何在计算机上做出幻灯片;于此同时 GIF 的开发者既想展现图片,又想有一些特殊的功能(不过更大的可能是公司要求)。
因为 GIF 可以响应用户操作这个功能就可以达到幻灯片的效果,而至今为止最出名的幻灯片软件 Power Point 也是同年诞生的,发布在苹果的 Macintosh 上。如果只是为了解决图片显示的问题,那么没有必要给 GIF 添加一些没必要的功能。
这里要考虑到当时还没有互联网,计算机科学这个学科也才诞生 20 多年,各种技术和应用还不成熟,个人计算机才刚刚推广开来。一些现在看来自然存在的技术和应用,是在当时多方竞争下存活的,比如 GIF,再比如 Linux 内核。所以我觉得这真的挺奇妙的:GIF 可能是根据要求加了一些额外的功能,但是碰巧是这些功能,让其没有被淘汰掉。
话说是谁想到的 GIF 可以做动画呢?

希望本文能帮助到和我一样有疑惑的人哦~

参考资料

《LZW and GIF explained----Steve Blackstock》
《GIF File Format Summary》
《GIF Signature Format: Introduction & Recovery》
《THE HISTORY OF GIFS AND HOW TO USE THEM》

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值