june01 gif 的故事:解剖表情动图的构成
In 未分类 on 2017年09月24日 by view: 9,986
4

前言

在现在表情包横行的年代,相信大家对表情动图已经不陌生了,这些动图通常就是我们使用的 gif 图(比如下图)。所谓动图就是会动的图片,而其会动的原理跟动画、影视剧一样,通过连续播放图片让我们从视觉感官上认为图片会动。那么 gif 图片又是如何将动图中的每一帧存储下来的呢,这下就需要我们动起刀子,解剖一下 gif 图。

例子

简介

gif 图本身无需过多介绍,相信这个星球上的人类基本上没有没接触过的吧。但是从专业的角度上来介绍的话,gif 图是一种基于 LZW 算法的 8 位连续色调的无损压缩格式,某种程度上来说和 png 图的格式有点相似(索引、无损压缩等)。其中 LZW 算法上一种压缩算法,需要注意的是 gif 使用的 LZW 算法并不是原本的 LZW 算法,而是经过稍微改造后的 LZW 算法。

结构

我们还是使用块来区分 gif 图的内容,掐去头跟尾,剩下的块可分为九种:逻辑屏幕标识符、全局颜色列表、图形控制扩展块、图像标识符、局部颜色列表、图像数据、文本扩展块、应用扩展块和注释扩展块。

其中其中文本扩展块、应用扩展块和注释扩展块我们基本上用不着,所以这里不做介绍。整个解析流程如下图:

流程

文件头

gif 图文件头有两种:[0x47, 0x49, 0x46, 0x38, 0x37, 0x61][0x47, 0x49, 0x46, 0x38, 0x39, 0x61],分别对应 87a 版本(1987 年制定的版本)和 89a 版本(1989 年制定的版本)。这也是这个 gif 图的 “魔数”,我们通过 “魔数” 就可以判断一个文件是不是 gif 图,用这种方式比直接判断文件后缀会靠谱得多。当然,我们可以将这个文件头转成字符串,转换过后其实就是"GIF87a"和 “GIF89a”,这个文件头的来历其实就是这么的简单:D

逻辑屏幕标识符

逻辑屏幕标识符(Logical Screen Descriptor)存放的是整个图片的基础信息,总共 7 个字节,结构如下:

描述 长度
图像宽度 2 字节
图像高度 2 字节
全局颜色列表标志 1 位
图像深度 3 位
分类标志 1 位
全局颜色列表大小 3 位
背景颜色 1 字节
像素宽高比 1 字节

图像宽高不必说,这里要注意的是解析的方式。宽高都是两个字节的,以小端法存储,故解析的时候需要将这两个字节的数据调换一下再转成数字。比如图像宽度这里存放的数据是 00000000 00100000,假如我们直接转成数字是 32,但是不对的;正确的解析方式是将两个字节调换一下,变成 00100000 00000000 再去解析,得出的图像宽度实际是 8192 才对。

全局颜色列表标志和分类标志只用 1 位表示,相当于就是一个布尔值。其中全局颜色列表标志若为 1,则表示存在全局颜色列表块;分类标志若为 1,则表示全局颜色列表块里的颜色会按重要性进行排列(即使用的频率)。图像深度表示每个像素点占用的位数,其他几个字段看名称就能了解,这里就不做解释(像素宽高比目前也几乎不再支持,可忽略)。

全局颜色列表

全局颜色列表(Global Color Table)存放的是全局使用的颜色信息,相当于我们画画用的色板。是否需要解析这个块由逻辑屏幕标识符中的全局颜色列表标志决定,如果存在此块,那么此块的字节数等于 (2 ^ (全局颜色列表大小 + 1)) * 3。这里乘以 3 是因为每个颜色有 3 个通道,即我们熟悉的 RGB 三色通道。