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

GIFiOS的尴尬处境

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

  • iOS关于照片的场景不会自动播放GIF,也没有角标。
  • 一些应用将GIF视为静态图像去操作,导致用户保持了一个GIF后,结果应用将其保存成JPG。
  • iOS只能通过imageI/O去操作GIF数据,UIKit对其绝缘。

GIF的存储结构

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

GIF结构

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

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

Paste_Image.png

Header

GIF文件的开头是 Header 数据块,长度为6字节,ASCII值为“GIF87a”或”GIF89a”,前三位为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

全局颜色列表

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

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

GIF中扩展块都以0x21开始,后一个字节是扩展标签,标识扩展用途。
应用程序扩展的标签是0xFF,它包含有应用程序的标识信息和应用程序数据。其中 Netscape 应用程序扩展常用于控制GIF的动画循环次数。Netscape 扩展长19个字节,前14个是应用程序的ACSII信息,后四个是数据子块,用于指定GIF的循环次数, 按无符号整型存储,0表示无限循环。

图形控制扩展

21F90409 32000F00

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

图像描述符

2C000000 000A000A 0000

图像描述符位于GIF中每一个图像数据的前端,由0x2C开始,长度为10个字节。第一个字节是图像描述符的标识0x2c,后面八个字节表示图像的frame(left, top, width, height),用来在动画中局部更新图像。最后一个是压缩字节,主要是关于局部颜色列表的信息,其中第二个Bit表示图像的存储方式是交织还是连续。

局部颜色列表
如果上面的局部颜色列表标志位为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为:

GIF图片原理和储存结构深入解析相关推荐

  1. Android图片加载框架最全解析(五)

    由此我们可以得知,在没有明确指定的情况下,ImageView默认的scaleType是FIT_CENTER. 有了这个前提条件,我们就可以继续去分析Glide的源码了.当然,本文中的源码还是建在第二篇 ...

  2. java 栈内存结构_JVM内存结构概念解析

    一. Java 内存结构 Java代码运行在虚拟机上,虚拟机在运行过程将程序(也就是进程)所占有内存分为几个不同的数据区域.不同的区域有不同的职责. Java运行时内存结构图如下: Java运行时内存 ...

  3. Android图片加载框架最全解析(八),带你全面了解Glide 4的用法

    本文转载自郭神的Glide分析系列:http://blog.csdn.net/guolin_blog/article/details/78582548 本文同步发表于我的微信公众号,扫一扫文章底部的二 ...

  4. Android图片加载框架最全解析(二),从源码的角度理解Glide的执行流程

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/53939176 本文同步发表于我的微信公众号,扫一扫文章底部的二维码或在微信搜索 郭 ...

  5. Android图片加载框架最全解析(七),实现带进度的Glide图片加载功能

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/78357251 本文同步发表于我的微信公众号,扫一扫文章底部的二维码或在微信搜索 郭 ...

  6. Android图片加载框架最全解析(三),深入探究Glide的缓存机制

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/54895665 本文同步发表于我的微信公众号,扫一扫文章底部的二维码或在微信搜索 郭 ...

  7. [转]谈NAND Flash的底层结构和解析

    这里我想以一个纯玩家的角度来谈谈关于NAND Flash的底层结构和解析,可能会有错误的地方,如果有这方面专家强烈欢迎指正. NAND Flash作为一种比较实用的固态硬盘存储介质,有自己的一些物理特 ...

  8. 谈NAND Flash的底层结构和解析

    这里我想以一个纯玩家的角度来谈谈关于NAND Flash的底层结构和解析,可能会有错误的地方,如果有这方面专家强烈欢迎指正. NAND Flash作为一种比较实用的固态硬盘存储介质,有自己的一些物理特 ...

  9. 谈NANDnbsp;Flash的底层结构和解析

    这里我想以一个纯玩家的角度来谈谈关于NAND Flash的底层结构和解析,可能会有错误的地方,如果有这方面专家强烈欢迎指正. NAND Flash作为一种比较实用的固态硬盘存储介质,有自己的一些物理特 ...

最新文章

  1. py2exe——.py文件转换成exe
  2. poj 1860 拓扑。。
  3. Bootstrap 环境安装
  4. OpenGL纹理矩阵,alpha混合和丢弃
  5. css3 制作平滑过度动画
  6. Zabbix监控(十六):分布式监控-Zabbix Proxy
  7. 快手内测10分钟长视频 以吸引更多MCN、用户入驻
  8. linux 给权限命令,Linux小白实用命令--权限设置
  9. Ecshop去版权方法?如何去除Ecshop版权?
  10. Lucene 如何热备份
  11. Atitit Major island groups and archipelagos 主要的岛群和群岛目录资料目录1. 岛群 波利尼西亚(Polynesia, 美拉尼西亚(Melanesia,
  12. 随心测试_Python Se_007下拉列表操作2
  13. C++如何判断一个程序是 死锁 还是 死循环,如何进行问题定位与分析
  14. .NET 函数调用反转
  15. 关于web出现此问题:index:25 Uncaught ReferenceError: delFruit is not defined at HTMLImageElement.onclick
  16. 2013全年3GPP RAN1会议关于D2D(Device-to-Device)技术的提案分析
  17. 张凌杰 html5,武汉理工大学第四届学位评定委员会第三次会议授予博士、硕士学位名单...
  18. 直接在服务器上跑系统,在服务器上跑代码
  19. 【Shiro】6、Shiro实现限制密码错误次数从而限制用户登录
  20. MTK Android MCC(移动国家码)和 MNC(移动网络码)

热门文章

  1. git push代码到远程仓库,报错解决:fatal: unable to access ‘https://github.com/.......‘: OpenSSL SSL_read: Connec
  2. 得物客服IM全链路通信过程
  3. 边缘检测系列3:【HED】 Holistically-Nested 边缘检测
  4. 如何从0开始设计演讲稿
  5. 深度学习也可以取悦女友
  6. 360奇安信和SonarQube漏洞及bug修改
  7. 蓝氏兄弟依靠板栗东山再起,意外赚回八九万元
  8. c语言:自增自减运算符的操作详解
  9. 系统间通信方式之(Kafka的实际使用场景和使用方案)(二十三)
  10. 由git push -f引发的程序员枪击血案