slot deposit pulsa slot mahjong slot gacor slot gacor slot gacor resmi slot gacor 2025 slot gacor terpercaya slot gacor 2025 slot gacor hari ini slot gacor hari ini slot gacor hari ini
庖丁解牛:GIF图片原理和储存结构
17611538698
webmaster@21cto.com

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

资讯 0 4343 2017-06-28 12:01:40
未标题-1.png

 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文件的内部结构,如下图所示:
/uploads/fox/28224627_0.jpg
Header:包含文件签名与版本号。
Trailer: 文件结束标识符。
GIF Data: Header 与 trailer 之间就是GIF文件的数据。
我们从一个简单的GIF图入手,它包含两张图像:
这是它的十六进制数据,我用颜色区分了不同的数据块:
/uploads/fox/28224627_1.jpg
Header
GIF文件的开头是 Header 数据块,长度为6字节,ASCII值为“GIF87a”或"GIF89a",前三位为GIF签名,后三位为不同年份的版本号。
/uploads/fox/28224627_2.jpg
利用这点,在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
/uploads/fox/28224627_3.jpg
全局颜色列表

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


由前面的逻辑屏幕描述符可知,全局颜色列表的大小是16,每个颜色占三个字节,按照RGB排列,所以它占有48个字节。数据流中,颜色是按照列表中的索引存储的。
应用程序扩展
21FF0B 4E455453 43415045 322E3003 01000000
GIF中扩展块都以0x21开始,后一个字节是扩展标签,标识扩展用途。
应用程序扩展的标签是0xFF,它包含有应用程序的标识信息和应用程序数据。其中 Netscape 应用程序扩展常用于控制GIF的动画循环次数。Netscape 扩展长19个字节,前14个是应用程序的ACSII信息,后四个是数据子块,用于指定GIF的循环次数, 按无符号整型存储,0表示无限循环。
/uploads/fox/28224627_4.jpg
图形控制扩展

21F90409 32000F00


图形控制扩展块属于"89a"版本的定义。它在一个图像数据块的最前端,用来指定图像的透明度与动画属性。图形控制扩展的开端两字节是0x21F9,其中0x21表示这是一个扩展,F9表示扩展用于图形控制。第三个字节是块大小(它到结束符之间的数据)。第四个字节是压缩字段,前三个Bit保留,四到六Bit是disposal method。第四、五个字节是图像控制扩展后面的图像的动画时间,以无符号整型存储。第六个字节是透明色索引,之后是块结束符0x00。
/uploads/fox/28224627_5.jpg
图像描述符

2C000000 000A000A 0000


图像描述符位于GIF中每一个图像数据的前端,由0x2C开始,长度为10个字节。第一个字节是图像描述符的标识0x2c,后面八个字节表示图像的frame(left, top, width, height),用来在动画中局部更新图像。最后一个是压缩字节,主要是关于局部颜色列表的信息,其中第二个Bit表示图像的存储方式是交织还是连续。
/uploads/fox/28224627_6.jpg
局部颜色列表
如果上面的局部颜色列表标志位为1,那么局部颜色列表会排列在图像描述符后面,它只对紧跟在它之后的图像数据有效。如果局部颜色列表标志位为0,那么图像数据将使用全局颜色列表索引颜色。局部颜色列表的大小计算方法和像素颜色格式与全局颜色列表相同。
图像数据

041C 1080472B 0549DA9B BAAE58E7 4D4F288E E629A519 697E1C0C 92DB1301 00


GIF的图像数据是经过LZW压缩的二进制流,通过解码可以将其按照颜色列表中的颜色进行像素填充。第一个字节是LZW最小编码大小,用来进行数据解码。第二个字节是图像数据的大小,之后的都是图像数据,直到块结束符。
结束符

3B


GIF的动画原理
GIF动画的循环次数是由应用扩展来控制的,而GIF动画每一帧的过渡方式是由图形控制扩展控制,图像描述符控制图像绘制的区域。
图形控制扩展中控制动画的参数分别是:disposal method,user input flag,delay time,transparency color。
disposal method
disposal method占3Bit,能够表示0-7。

disposal method = 1
解码器不会清理画布,直接将下一幅图像渲染上一幅图像上。

disposal method = 2
解码器会以背景色清理画布,然后渲染下一幅图像。背景色在逻辑屏幕描述符中设置。

disposal method = 3
解码器会将画布设置为上之前的状态,然后渲染下一幅图像。

disposal method = 4-7
保留值
user input flag
当user input flag为1时,GIF会在有用户输入事件(鼠标、键盘)时才会过渡到下一幅图像。
delay time
delay time占两个字节,为无符号整数,控制当前帧的展示时间,单位是0.01秒。
transparency color
如果图形控制扩展的透明色标志位为1,那么解码器会通过透明色索引在颜色列表中找到改颜色,标记为透明,当渲染图像时,标记为透明色的颜色将不会绘制,显示下面的背景。
图像渲染区域
GIF中图像描述符指定了当前帧需要渲染的区域,这样GIF的过渡动画就只用绘制两帧之间不同的区域,前提是diposal method的值为1。
实验
根据上面的知识,将第一帧的图形控制扩展改为:

21F90409 0A000900


这里将透明色改成了红色(红色在全局颜色列表中的索引是9),并将delay time改为0.1秒。修改完的GIF为:
 


来源:http://www.jianshu.com/p/d4db7e0e028a。技术原创及架构实践文章,您可通过公众号菜单联系我们或21CTO网站注册后发布文章。


评论