不少图片处理软件都有一种功能:用户上传一张静态照片,然后可以从软件提供的一些动态图片(gif)选择一种,常见的是相框之类的,和静态图片合成,得到新的动态图片。这就是GIF和静态图片的叠加。
要实现上面的功能,思路还是比较清晰的。GIF图片格式可以看成是多个单帧图片的组合,从GIF图中提取每一帧图片,和静态图混合,最终再将混合后的n张图片组合成GIF。
在实际操作中,涉及到GIF的格式,GIF的拆分,图片混合,真彩色转256色,图片效果优化,GIF组合等。
首先介绍下GIF的格式
GIF图象是基于颜色列表的(存储的数据是该点的颜色对应于颜色列表的索引值),最多只支持8位(256色)。GIF文件内部分成许多存储块,用来存储多幅图象或者是决定图象表现行为的控制块,用以实现动画和交互式应用。GIF文件还通过GIF-LZW压缩算法压缩图象数据来减少图象尺寸。
GIF图像格式

大致分为三部分:文件头、GIF数据流、文件结尾
1.文件头
1.1GIF署名:由三个字节组成,固定为G、I、F,表示这是一张GIF图
1.2版本号:由三个字节组成,值为87a或89a。89a版本增加了一些扩展块,现在使用的GIF是89a版本
2.文件结尾
2.1文件终结器:1个字节,固定值0x3b,标识GIF图结束。
3.GIF数据流
这是最大的一块,描述了GIF的全局信息,以及每一帧GIF的信息
3.1控制块
3.1.1逻辑屏幕标识符
共7个字节,记录一些全局的信息。wWidth:GIF图像宽度,2Byte
wDepth:GIF图像高度,2Byte
Globalflag:1Byte
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
GlobalPal |
ColorRes |
SortFlag |
PalBits | ||||
是否有全局调色板 |
ColorRes+1表示色彩深度 |
全局调色板中的RGB颜色值是否按照使用率进行从高到底的 次序排序的 |
全局调色板的位数 |
byBackground:1Byte,逻辑屏幕的背景颜色,画布颜色。当图像长宽小于逻辑屏幕的大小 时,未被图像覆盖部分的颜色值由该值对应的全局调色板中的索引颜色值确定。如果没有全局调色板,该值无效,默认背景颜色为黑色。
byAspect: 1Byte,逻辑屏幕的像素的长宽比例。
3.1.2全局颜色列表
如果有它的话(是否有由逻辑屏幕标识符中的Globalflag的最高bit位GlobalPal决定),必须紧跟在逻辑屏幕标识符后面,每个颜色列表索引条目由三个字节组成,按R、G、B的顺序排列。全局颜色列表的Size = 3 * 2 ^ (Globalflag & 0x07 + 1)
图像块开始就是每一帧的信息了,包括每一帧的控制信息、数据信息、扩展信息(89a版本才有扩展信息)
3.2.1图形控制扩展
这是一个扩展块,89a版本才有的,也是89a版本加入的四个扩展块中最重要的一个,其他三个可以忽略了。
这个扩展块可以有也可以没有,如果有,一般是放在每一个图像块的开头,即图像标识符之前,用来控制跟在它后面的第一个图象的渲染形式。它有8个字节组成:
第1字节 |
扩展块标识 |
固定值0x21 |
第2字节 |
图形控制扩展标签 |
固定值0xF9 |
第3字节 |
块大小 |
固定值4,说明之后4个字节的数据是当前扩展块的有效数据 |
第4字节0bit |
是否有透明性 |
如果该位为1,表明图像有透明属性,透明色颜色由参数byTransparencyIndex指定。 |
第4字节1bit |
是否有用户输入 |
如果该位为1,则表示在显示一副图像后,需要用户输入再进行下一个动作。 |
第4字节2~4bit |
图像 显示后的处理方式 |
0 - 不使用处置方法 |
第4字节5~7bit |
保留 | |
第5、6字节 |
帧间隔时间 |
单位10ms |
第7字节 |
透明色索引值 | |
第8字节 |
当前扩展块终结标识 |
固定为0 |
3.2.2图像标识符
标识一帧图像的信息,由0x2C这个开始位标识它的开始,连开始标识位一共10个字节。(解析的时候读到0x2c说名接下来的10个字节就是图像标识符啦)
wLeft:2Byte,图像相对逻辑屏幕左上角的X偏移像素
wTop:2Byte,图像相对逻辑屏幕左上角的Y偏移像素
wWidth:2Byte,图像的宽度
wDepth:2Byte,图像的高度
Gifimage:1Byte,局部颜色列表标志
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
LocalPal |
Interlace |
SortFlag |
Reserved |
PalBits | |||
是否有局部调色板 |
置位时图象数据使用交织方式排列 |
如果置位表示紧跟着的局部颜色列表分类排列 |
保留为,暂时无用 |
局部调色板的位数 |
3.2.3局部颜色列表
如果上面的局部颜色列表标志置位的话,则需要在这里(紧跟在图象标识符之后)定义一个局部颜色列表以供紧接着它的图象使用。注意使用前应先保存原来的颜色列表,使用结束之后回复原来保存的全局颜色列表。如果一个GIF文件即没有提供全局颜色列表,也没有提供局部颜色列表,可以自己创建一个颜色列表,或使用系统的颜色列表。局部颜色列表的排列方式和全局颜色列表一样:RGBRGB......
3.2.4基于颜色列表的图象数据
图像压缩数据是按照GIF-LZW压缩编码后存储的。GIF-LZW编码是一种经过改良的LZW编码方式,它是一种无损压缩的编码方法。GIF-LZW编码方法是将原始数据中的重复字符串建立一个字符串表,然后用该重复字符串在字符串表中的索引来替代原始数据以达到压缩的目的。由于GIF-LZW压缩编码的需要,必须首先存储GIF-LZW的最小编码长度以供解码程序使用,然后再存储编码后的图像数据。编码后的图像数据是一个个数据子块的方式存储的,每个数据子块的最大长度为256字节。数据子块的第一个字节指定该数据子块的长度,接下来的数据为数据子块的内容。如果某个数据子块的第一个字节数值为0,即该数据子块中没有包含任何有用数据,则该子块称 为块终结符,用来标识数据子块到此结束。
最小编码长度 | |
子块长度 |
数据块1 |
数据 | |
...... | |
子块长度 |
数据块n |
数据 |
3.2.5注释扩展
89a版本可选。可以用来记录图形、版权、描述等任何的非图形和控制的纯文本数据(7-bit ASCII字符),注释扩展并不影响对图象数据流的处理,解码器完全可以忽略它。存放位置可以是数据流的任何地方,最好不要妨碍控制和数据块,推荐放在数据流的开始或结尾。
第1字节 |
扩展块标识 |
固定值0x21 |
第2字节 |
注释扩展标签 |
固定值0xFE |
第3字节开始 |
注释块 |
一个或多个数据块 |
第n字节 |
注释扩展块结束标识 |
固定为0 |
3.2.6图形文本扩展
89a版本可选。这一部分是可选的(需要89a版本),用来绘制一个简单的文本图象,这一部分由用来绘制的纯文本数据(7-bit ASCII字符)和控制绘制的参数等组成。绘制文本借助于一个文本框(Text Grid)来定义边界,在文本框中划分多个单元格,每个字符占用一个单元,绘制时按从左到右、从上到下的顺序依次进行,直到最后一个字符或者占满整个文本框(之后的字符将被忽略,因此定义文本框的大小时应该注意到是否可以容纳整个文本),绘制文本的颜色索引使用全局颜色列表,没有则可以使用一个已经保存的前一个颜色列表。另外,图形文本扩展块也属于图形块(Graphic Rendering Block),可以在它前面定义图形控制扩展对它的表现形式进一步修改。图形文本扩展的组成:
第1字节 |
扩展块标识 |
固定值0x21 |
第2字节 |
图形控制扩展标签 |
固定值0x01 |
第3字节 |
块大小 |
固定值12,说明之后12个字节的数据是当前扩展块的有效数据 |
第4、5字节 |
文本框左边界位置 |
文本框离逻辑屏幕的左边界距离 |
第6、7字节 |
文本框上边界位置 |
文本框离逻辑屏幕的上边界距离 |
第8、9字节 |
文本框高度 | |
第10、11字节 |
文本框宽度 | |
第12字节 |
文本单元格宽度 |
单个单元格宽度 |
第13字节 |
文本单元格高度 |
单个单元格高度 |
第14字节 |
文本前景色索引 | |
第15字节 |
文本背景色索引 | |
第16字节开始 |
文本数据块 |
要显示的字符串 |
第n字节 |
注释扩展块结束标识 |
固定为0 |
1、由于文本的字体(Font)和尺寸(Size)没有定义,解码器应该根据情况选择最合适的。
2、如果一个字符的值小于0x20或大于0xF7,则这个字符被推荐显示为一个空格(0x20)。
3、为了兼容性,最好定义字符单元格的大小为8x8或8x16(宽度x高度)。
3.2.7应用程序扩展
89a版本可选。这是提供给应用程序自己使用的,应用程序可以在这里定义自己的标识、信息等。
第1字节 |
扩展块标识 |
固定值0x21 |
第2字节 |
图形控制扩展标签 |
固定值0xFF |
第3字节 |
块大小 |
固定值11,说明之后11个字节的数据是当前扩展块的有效数据 |
第4~11字节 |
应用程序标识符 |
鉴别应用程序自身的表示 |
第12~14字节 |
鉴别码 |
应用程序定义的特殊标识码 |
第15字节开始 |
应用程序数据块 |
一个或多个数据块组成,保存应用程序自己定义的数据 |
第n字节 |
注释扩展块结束标识 |
固定为0 |
从上面的GIF图片格式可以了解到,GIF的基本格式是全局信息+每帧图像的信息,帧之间的时间间隔可以从每帧的图形控制扩展中获得。因此,拆分GIF、合并GIF都是不困难的。
此外,GIF和单帧GIF的色深都是8位,最多支持256中颜色,采用调色板+索引的方式记录每个像素的颜色值,单帧GIF支持透明(图形控制扩展中标识)。
基于单帧GIF的透明属性,可以实现图片叠加效果:
先建立一张新的画板,在画板上画静态图,再画单帧的GIF,这样基于透明属性,就可以将一部分静态图透过来。
但是因为GIF是8位的,而现在的大部分常见图是24位的,那么在叠加并生成最终的GIF时,必然需要将24位(2^24种颜色)降为8位(2^8种颜色)。这个过程中色彩空间的减小将导致色彩丢失,因此需要采用一些处理手段尽可能的提升图片质量。