GIF图片原理和储存结构深入解析

GIF是一种使用LZW压缩,支持多张图像的容器。支持256色,透明通道为1bit。作为互联网表情包的载体,GIF这项80年代的技术依然生生不息。
但它的弊端也是显而易见的:易出现毛边,色彩表现低劣,文件压缩比不高。针对这些问题,Mozilla发布了APNG来代替老旧的GIF技术,同时许多开源组件也用WebP格式来代替GIF。

GIF在iOS的尴尬处境

长久以来,iOS一直被吐槽不能用GIF。造成这一局面的主要原因是:

iOS关于照片的场景不会自动播放GIF,也没有角标。

一些应用将GIF视为静态图像去操作,导致用户保持了一个GIF后,结果应用将其保存成JPG。

iOS只能通过imageI/O去操作GIF数据,UIKit对其绝缘。

GIF的存储结构

由于历史的原因,GIF有两个版本,但它们的文件结构是一样的,都是由不同用途的数据块构成,可分为控制块和数据块。控制块里是决定GIF表现的参数,数据块里的数据由前面的控制块里的参数来解释。
一个GIF文件的内部结构如下图:

图0:庖丁解牛:GIF图片原理和储存结构

GIF结构

Header:包含文件签名与版本号。
Trailer: 文件结束标识符。
GIF Data: Header 与 trailer 之间就是GIF文件的数据。
我们从一个简单的GIF图入手,它包含两张图像:

这是它的十六进制数据,我用颜色区分了不同的数据块:

图2:庖丁解牛:GIF图片原理和储存结构

Paste_Image.png

Header

GIF文件的开头是 Header 数据块,长度为6字节,ASCII值为“GIF87a”或”GIF89a”,前三位为GIF签名,后三位为不同年份的版本号。

图3:庖丁解牛:GIF图片原理和储存结构

Paste_Image.png

利用这点,在iOS中判断二进制文件是否为GIF时,可以简单去判断它的前四位是否是”GIF8″。事实上绝大多数图像都可以用文件签名来判断类型。

GIF 内容

GIF数据包含多个数据块,其结构如下:
逻辑屏幕描述符

0A000A00 B30000

这一数据块由7个字节组成,前四个字节分别是图像渲染区域的宽高。GIF的数据是按照大端序存储的,0x0A00为10,所以这个GIF的宽高均为10。
接下来是一个压缩字节,第一个 Bit 为标志位,表示全局颜色列表是否存在。接下来三个Bit表示图像调色板中每个颜色的原色所占用的Bit数,011表示占用4个Bit,111占用8个Bit,以此类推。调色板最多只包含由24-Bit颜色中选出的256个颜色(实际有很多优化方案能提高颜色分辨率,如加入局部调色板)。第五个Bit为标志位,表示颜色列表排序方式。若为1,表示颜色列表是按照颜色在图像中出现的频率降序排列。随后三个Bit表示全局颜色列表的大小,计算方法是2^N+1 ,其中N为这三个Bit的二进制数值。
第六个字节是表示背景色在全局颜色列表中的索引,若无全局颜色列表则此字节无效。在GIF的图像数据中,没有被指定颜色的像素会被背景色填充。
最后一个字节是像素的宽高比,大多数时候这个值都是0,若值为N, 则图像的宽高比:

aspectRatio = (pixelAspectRatio + 15) / 64

图4:庖丁解牛:GIF图片原理和储存结构

全局颜色列表

000000 80000000 80008080 00000080 80008000 8080C0C0 C0808080 FF000000 FF00FFFF 000000FF FF00FF00 FFFFFFFF FF

由前面的逻辑屏幕描述符可知,全局颜色列表的大小是16,每个颜色占三个字节,按照RGB排列,所以它占有48个字节。数据流中,颜色是按照列表中的索引存储的。
应用程序扩展
>
21FF0B 4E455453 43415045 322E3003 01000000

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/13690.html