gif格式的图片要怎么弄 GIF表情包是如何动起来的( 二 )


文章插图
图片来源:What’s In A GIF
GIF 格式的文件按块存储 , 整体上分为三部分:
其中 , 数据流中的文本扩展块、应用扩展块和注释扩展块我们跳过不看 , 让图片“动”起来的秘诀就存在于 图形控制扩展(Graphic Control Extension)中 。下面让我们用一个栗子来一探究竟吧 。
样例准备
样例图片
本文分析数据 , 全部基于以该样例图片.
十六进制转换器
传送门
文件头
识别一张图是不是 GIF 并不只看图片扩展格式或者图片是否会动 , GIF 文件的前 6 个字节内容是 GIF 的署名和版本号 , 通过控制台打印我们可以得到:
【gif格式的图片要怎么弄 GIF表情包是如何动起来的】

gif格式的图片要怎么弄 GIF表情包是如何动起来的

文章插图
对照 ASCII 编码我们可以得到 47 49 46 38 39 61 对应 GIF 89a
简单!继续往下看↓
GIF 数据流
图形控制扩展
我们通过观察不难发现 , 图片会有瞬间闪烁的效果 , 对比文章开头表情包图 , 为什么有些 GIF 图可以一直循环播放 , 有些却是瞬间闪烁然后定格在第二帧呢?
在 89a 版本 , GIF 添加了图形控制扩展块 , 放在图像标识符(Image Descriptor)的前面 , 用来控制紧跟在它后面的第一个图象的显示 , 图形控制扩展块的结构如下图所示:
gif格式的图片要怎么弄 GIF表情包是如何动起来的

文章插图
由上图可见 , 整个扩展块结构如下:
描述
长度
扩展块标识符
1 字节、固定值 0x21
扩展块标识
1 字节、固定值 0xF9
扩展块子块长度
1 字节
保留位
3 位
处置方法
3 位
用户输入标志
1 位
透明颜色标志
1 位
延迟时间
2 字节
透明颜色索引
1 字节
扩展块尾
1 字节、固定值 0x00
找到它了!罪魁祸首就是**延迟时间!**延迟时间标记了需要暂停这个延迟时间后再继续往下处理数据流 , 这里可以理解为动图中每一帧的停留时间 , 其单位为 1/100 秒 。
分析到这里 , 有种茅塞顿开的感觉 , 回到代码中 , 我们通过控制台可以看到原图解析出来的数据是这样的:
gif格式的图片要怎么弄 GIF表情包是如何动起来的

文章插图
延迟时间:00 00 , 十六进制转换十进制为:0
我们通过手动设置延迟时间 , 就可以让一闪而过的图片“动”起来:
gif格式的图片要怎么弄 GIF表情包是如何动起来的

文章插图
手动修改后的延迟时间:32 00 , 十六进制转换十进制为:800
核心代码如下:
letp = 0; // 当前 Buffer 处理对应的下标while(notEndOfFile && p < contentBuffer.length) { ... switch(contentBuffer[p++]) { case0xf9: // Graphics Control Extensionif(contentBuffer[p++] !== 0x4|| contentBuffer[p+4] !== 0) thrownewError(\"Invalid graphics extension block.\"); p++; // graphicPackedFiledif(delay) { constdelayArr = numberToByteArr(delay); contentBuffer[p] = delayArr[delayArr.length - 1]; contentBuffer[p+1] = delayArr[delayArr.length - 2] || 0; } p = p + 4; // 略过 delay 2 字节, transparentIndex 1 字节 , 结束符号 1字节break; } } 复制代码 文件结尾
gif格式的图片要怎么弄 GIF表情包是如何动起来的

文章插图
当所有子图像数据解析完毕 , 就会遇到文件尾 , 这一部分只有一个值为 0 的字节 , 标识一个 GIF 文件结束 。文件尾固定为 0x3B