成文迪, 在Web前端摸爬滚打的码农一枚,对技术充满热情的菜鸟,致力为手Q的建设添砖加瓦。
GIF格式的历史GIF(Graphics Interchange Format)原义是“图像互换格式”,是CompuServe公司在1987年开发出的图像文件格式,可以说是互联网界的老古董了。
GIF格式可以存储多幅彩色图像,如果将这些图像连续播放出来,就能够组成最简单的动画。所以常被用来存储“动态图片”,通常时间短,体积小,内容简单,成像相对清晰,适于在早起的慢速互联网上传播。
本来,随着网络带宽的拓展和视频技术的进步,这种图像已经渐渐失去了市场。可是,近年来流行的表情包文化,让老古董GIF图有了新的用武之地。
表情包通常来源于手绘图像,或是视频截取,目前有很多方便制作表情包的小工具。
这类图片通常具有文件体积小,内容简单,兼容性好(无需解码工具即可在各类平台上查看),对画质要求不高的特点,刚好符合GIF图的特性。
所以,老古董GIF图有了新的应用场景。
本文的应用场景新的应用场景带来新的需求,本文所探究的问题来自于某个业务场景下——为用户批量推送GIF表情包。
一批图像大约有200-500张,以缩略图列表的形式展示在客户端。
根据我们使用测试数据进行的统计GIF图表情包的尺寸大部分在200k-500k之间,批量推送的一个重要问题就是数据量太大,因此,我们希望能够在列表里展示体积较小的缩略图,用户点击后,再单独拉取原图。
传统的GIF缩略图是静态的,通常是提取第一帧,但在表情包的情形下,这种方式不足以表达出图片中信息。比如下面的例子
——第一帧完全看不出重点啊!
所以,我们希望缩略图也是动态的,并尽可能和原图相似。
对于传统图片来说,文件大小一般和图片分辨率(尺寸)正相关,所以,生成缩略图最直观的思路就是缩小尺寸,resize大法。
但是在GIF图的场合,这个方式不再高效,因为GIF图的文件大小还受到一个重要的因素制约——帧数
以这张柴犬表情为例,原图宽度200,尺寸1.44M,等比缩放到150之后,尺寸还是1.37M,等比缩放到100,相当于尺寸变为原来的四分之一,体积还是749K
可见,resize大法的压缩率并不理想,收效甚微。
而且,我们所得到的大部分表情图素材,分辨率已经很小了,为了保证客户端展示效果,不能够过度减少尺寸,不然图片会变得模糊。
所以,想要对GIF图进行压缩,只能从别的方向入手。
探寻GIF格式的存储想要压缩一个文件,首先要了解它是如何存储的。毕竟,编程的事,万变不离其宗嘛。
作为一种古老的格式,GIF的存储规则也相对简单,容易理解,一个GIF文件主要由以下几部分组成。
下面我们来分别探究每个部分。
文件头GIF格式文件头和一般文件头差别不大,也包含有
格式声明
Signature 为“GIF”3 个字符;Version 为“87a”或“89a”3 个字符。
逻辑屏幕描述块
前两字节为像素单位的宽、高,用以标识图片的视觉尺寸。
Packet里是调色盘信息,分别来看——
Global Color Table Flag为全局颜色表标志,即为1时表明全局颜色表有定义。
Color Resolution 代表颜色表中每种基色位长(需要+1),为111时,每个颜色用8bit表示,即我们熟悉的RGB表示法,一个颜色三字节。
Sort Flag 表示是否对颜色表里的颜色进行优先度排序,把常用的排在前面,这个主要是为了适应一些颜色解析度低的早期渲染器,现在已经很少使用了。
Global Color Table 表示颜色表的长度,计算规则是值+1作为2的幂,得到的数字就是颜色表的项数,取最大值111时,项数=256,也就是说GIF格式最多支持256色的位图,再乘以Color Resolution算出的字节数,就是调色盘的总长度。
这四个字段一起定义了调色盘的信息。
Background color Index 定义了图像透明区域的背景色在调色盘里的索引。
Pixel Aspect Ratio 定义了像素宽高比,一般为0。
什么是调色盘?我们先考虑最直观的图像存储方式,一张分辨率M×N的图像,本质是一张点阵,如果采用Web最常见的RGB三色方式存储,每个颜色用8bit表示,那么一个点就可以由三个字节(3BYTE = 24bit)表达,比如0xFFFFFF可以表示一个白色像素点,0x000000表示一个黑色像素点。
如果我们采用最原始的存储方式,把每个点的颜色值写进文件,那么我们的图像信息就要占据就是3×M×N字节,这是静态图的情况,如果一张GIF图里有K帧,点阵信息就是3×M×N×K。
下面这张兔子snowball的表情有18帧,分辨率是200×196,如果用上述方式计算,文件尺寸至少要689K。
但实际文件尺寸只有192K,它一定经历过什么……
我们可以使用命令行图片处理工具gifsicle来看看它的信息。
gifsicle -I snowball.gif > snowball.txt