Gif图片格式完全理解
Gif格式标准主要有87a和89a两个版本。
Gif由一个个的block组成,按照我的理解可以将Gif图数据分为两个部分,一个部分为头部,包含了Header(Gif的识 别),Logical Screen Descriptor(描述Gif图展示的逻辑屏幕的参数),Global Color Table(全局的调色板,这个为可选)。之后就是数据主体部分,可以是图像的数据以及各种扩展块。
介绍分为两个部分,一个部分是Gif中比较通用的一些数据组织形式介绍,一个部分是Gif图的数据块的介绍。
一下如果没有特殊说明,就是87a标准中就有的,有特殊说明,为89a标准中才出现的,主要为各种扩展块。
通用的数据组织形式
Data Sub-blocks 数据子块
数据子块是包含数据的单元,它们没有一个标签(label)。无论在何种格式中指定数据块,这些块在控制块的上下文中进行处理。数据子块的第一个字 节标识了跟随的数据字节的数量,一个数据子块能够包含0到255数据字节,块的大小并不包含指定大小的字节本身。空的子块指定大小的字节包含着0x00。
7 6 5 4 3 2 1 0 Field Name Type +---------------+ 0 | | Block Size Byte +---------------+ 1 | | +- -+ 2 | | +- -+ 3 | | +- -+ | | Data Values Byte +- -+ up | | +- . . . . -+ to | | +- -+ | | +- -+ 255 | | +---------------+
Block Terminator 块的终结标识
这个数据子块用来终结一系列的数据子块,其包含一个单独的字节指示块的大小为0,之后并不包含其它数据。
7 6 5 4 3 2 1 0 Field Name Type +---------------+ 0 | | Block Size Byte +---------------+
数据块
数据块分为四个部分介绍,第一部分为文件头和紧密跟随着文件头的相关数据块;第二部分为和数据流中图像相关的数据块;第三部分为各种扩展。最后一个部分为文件尾。
Header及Header相关
Header(文件头)
Header标识了文本中的GIF数据流,Signature字段标记了数据流的开始,Header字段是必须的,Header定义如下:
7 6 5 4 3 2 1 0 Field Name Type +---------------+ 0 | | Signature 3 Bytes +- -+ 1 | | +- -+ 2 | | +---------------+ 3 | | Version 3 Bytes +- -+ 4 | | +- -+ 5 | | +---------------+
依次为
- 3 Bytes Signature: “GIF”三个字节
- 3 Bytes Version: 版本号 “87a” “89a”等
Logical Screen Descriptor
Logical Screen Descriptor包含了描述了在设备上呈现图像区域所需的参数。Logical Screen Descriptor字段是必须的,并且必须紧跟在Header后面。Logical Screen Descriptor定义了依次为:
7 6 5 4 3 2 1 0 Field Name Type +---------------+ 0 | | Logical Screen Width Unsigned +- -+ 1 | | +---------------+ 2 | | Logical Screen Height Unsigned +- -+ 3 | | +---------------+ 4 | | | | | <Packed Fields> See below +---------------+ 5 | | Background Color Index Byte +---------------+ 6 | | Pixel Aspect Ratio Byte +---------------+
- Unsigned(2字节) Logical Screen Width 宽度,单位为像素
- Unsigned(2字节) Logical Screen Height 高度,单位为像素
- Packed Fields(1字节)
- 1 Bit Global Color Table Flag
- 3 Bits Color Resolution
- 1 Bit Sort Flag
- 3 Bits Size of Global Color Table
- 1 Byte Background Color Index
- 1 Byte Pixel Aspect Ratio
描述一下一些字段的意思:
- Global Color Table Flag 描述是否存在Global Color Table的标识,值分别为
- 0 – 不会在Logical Screen Descriptor后面跟随着Global Color Table, Background Color Index字段也就没有意义
- 1 – 在Logical Screen Descriptor后面跟随着Global Color Table,Background Color Index也就有意义
- Color Resolution:数值为原始图像的原色的位数减去1。这个值代表了整个调色板的大小,并不是图像中实际使用的颜色数量。例如若这个值为3,代表原始图像的调色盘每个原色有4位。
- Sort Flag 标识Global Color Table是否根据重要性递减被排序过了,数值分别为
- 0 – 没有排序过
- 1 – 递减排序,最重要的颜色优先
- Size of Global Color Table:如果Global Color Table Flag为1,这个字段的值用来统计Global Color Bytes中的byte数。即使没有指定全局颜色表,根据上述公式设置此字段,以便解码器可以选择显示流的最佳图形模式。(该字段由字节的3个最低有效位 组成)。
- Background Color Index 背景颜色在Global Color Table中的索引。背景颜色用于被没被图像覆盖的区域的像素。如果Global Color Table Flag是0,这个字段应为0并且被忽视。
- Pixel Aspect Ratio:用于计算原始图像中像素的宽高比的近似值。如果这个字段为0,则根据以下公式计算宽高比的近似值:
Aspect Ratio = (Pixel Aspect Ratio + 15) / 64
Pixel Aspect Ratio:像素的宽和高的商,这个值范围允许以1/64的增量从4:1到1:4
Global Color Table
该块包含一个color table,是一系列的bytes代表红-绿-蓝三原色。Global Color Table 用于没有Local Color Table(见后面介绍)或者Plain Text Extensions(见后面介绍)的图像。
只有在Logical Screen Descriptor中间的Global Color Table Flag为1的时候Global Color Table才存在,如果存在,其必须紧跟着Logical Screen Descriptor,其包含的bytes数量为
3*2^(Size of Global Color Table+1)
(Size of Global Color Table为Logical Screen Descriptor中间的字段)。
在每个数据流中最多只有一个Global Color Table。语法例子如下为:
7 6 5 4 3 2 1 0 Field Name Type +===============+ 0 | | Red 0 Byte +- -+ 1 | | Green 0 Byte +- -+ 2 | | Blue 0 Byte +- -+ 3 | | Red 1 Byte +- -+ | | Green 1 Byte +- -+ up | | +- . . . . -+ ... to | | +- -+ | | Green 255 Byte +- -+ 767 | | Blue 255 Byte +===============+
数据流中的图像
Image Descriptor
每个在数据流中的图像包含一个Image Descriptor,一个可选的Local Color Table,和图像数据。每个图像必须适应逻辑屏幕的边界。
Image Descriptor包含处理基于表的图像所需的参数,其中的坐标指的是在逻辑屏幕内的坐标,像素为单位。这个块是图像渲染块,可能在一个或者多个控制块 例如Graphic Control Extension之前,可能会有一个Local Color Table跟随其后。Image Descriptor之后总是会有图像数据。
对于一张图像该块是必须的,数据流中的每张图像有且仅有一个Image Descriptor。数据流中的图像数量没有限制。
7 6 5 4 3 2 1 0 Field Name Type +---------------+ 0 | | Image Separator Byte +---------------+ 1 | | Image Left Position Unsigned +- -+ 2 | | +---------------+ 3 | | Image Top Position Unsigned +- -+ 4 | | +---------------+ 5 | | Image Width Unsigned +- -+ 6 | | +---------------+ 7 | | Image Height Unsigned +- -+ 8 | | +---------------+ 9 | | | | | | <Packed Fields> See below +---------------+
总共包含如下的字段:
- 1 Byte Image Separator
- Unsigned(2字节)Image Left Position
- Unsigned(2字节)Image Top Position
- Unsigned(2字节)Image Width
- Unsigned(2字节)Image Height
- 1 字节 Packed Fields
- 1 Bit Local Color Table Flag
- 1 Bit Interlace Flag
- 1 Bit Sort Flag
- 2 Bits Reversed 保留字段
- 3 Bits Size of Local Color Table
具体介绍为:
- Image Separator:标识一张图像的开始,值为0x2C
- Image Left Position: 列数量,单位为像素,距离图像左边缘的列数
- Image Top Position: 行数量,单位为像素,距离顶部的列数
- Image Width:图像宽度像素数
- Image Height:图像高度像素数
- Packed Fields:
- Local Color Table Flag:作用和Global Color Table Flag相似,标记后面是否有Local Color Table,值为
- 0 – 没有Local Color Table,如果Global Color Table存在的话使用Global Color Table
- 1 – 有Local Color Table,紧紧跟随在Image Descriptor后面
- Interlace Flag:标识图像是否为隔行扫描,图像以四遍交错模式交织,具体见附录E,值为
- 0 – 图像没有隔行扫描
- 1 – 图像隔行扫描
- Sort Flag:标识Local Color Table是否被排序,值为
- 0 – 没有排序
- 1 – 按照重要次序递减排序
- Size of Local Color Table:如果Local Color Table Flag被设置为1,该字段被用来计算Local Color Table中间包含的字节数,如果没有Local Color Table,值为0.
- Local Color Table Flag:作用和Global Color Table Flag相似,标记后面是否有Local Color Table,值为
扩展和范围:这个块的范围包括跟随在其后的Table Based Color Image Data Block,这个块可能被Graphic Control Extension修改。
Local Color Table
该块包含颜色表,和Global Color Table相似。Local Color Table如果存在(Local Color Table Flag为1),则紧跟在Image Descriptor后面,是一些列代表红-绿-蓝颜色的字节,字节数量为
3*2^(Size of Local Color Table+1)
如果存在,该颜色表暂时变为激活的颜色表,并且用其来处理跟随的图像数据
7 6 5 4 3 2 1 0 Field Name Type +===============+ 0 | | Red 0 Byte +- -+ 1 | | Green 0 Byte +- -+ 2 | | Blue 0 Byte +- -+ 3 | | Red 1 Byte +- -+ | | Green 1 Byte +- -+ up | | +- . . . . -+ ... to | | +- -+ | | Green 255 Byte +- -+ 767 | | Blue 255 Byte +===============+
扩展和范围:这个块的范围包括紧紧跟随着其的Table Based Image Data块,这个块不能够被任何扩展修改。
Table Based Image Data
基于表的图像的数据包含一系列的子块。每个子块最多size为255字节,包含图像中每个像素到当前激活的颜色表的索引。像素的索引按照从左到右, 从上到下的顺序进行。每个索引的值应该在活动颜色表的大小范围内,从0开始。索引序列使用可变长编码的LZW算法进行编码,如索引F中描述。
7 6 5 4 3 2 1 0 Field Name Type +---------------+ | | LZW Minimum Code Size Byte +---------------+ +===============+ | | / / Image Data Data Sub-blocks | | +===============+
- Byte LZW Minimum Code Size
- Data Sub-blocks Image Data
LZW Minimum Code Size:这个字节决定了图像数据中的LZW码的初始字节数。
扩展和范围:这个块没有范围,其包含光栅数据。想要修改基于表的图像的扩展必须在其相关的Image Descriptor之前出现。
扩展
扩展主要在89a版本中引入,当然,我也遇到过变态的gif写着87a却用着扩展。
Graphic Control Extension
需要版本89a。
Graphic Control Extension包含在处理图像渲染块时候用到的参数。这个扩展的范围是跟随的第一张图像渲染块。扩展只包含一个数据子块。(graphic rendering block图像渲染块,我的理解是包含图像相关的数据块和Plain Text Extension)
这个块可选,在一个图像渲染块之前最多有一个Graphic Control Extension。这是在数据流中可能包含的唯一对Graphic Control Extension数量的限制。
7 6 5 4 3 2 1 0 Field Name Type +---------------+ 0 | | Extension Introducer Byte +---------------+ 1 | | Graphic Control Label Byte +---------------+ +---------------+ 0 | | Block Size Byte +---------------+ 1 | | | | | <Packed Fields> See below +---------------+ 2 | | Delay Time Unsigned +- -+ 3 | | +---------------+ 4 | | Transparent Color Index Byte +---------------+ +---------------+ 0 | | Block Terminator Byte +---------------+
- 1 Byte Extension Introducer
- 1 Byte Graphic Control Label
- 1 Byte Block Size
- 1 字节 Packed Fields
- Unsigned(2字节) Delay Time
- 1 Byte Transparent Color Index
- 1 Byte Block Terminator
这些字段作用如下:
- Extension Introducer – 标识一个extension块的开始,这个字段值为0x21。
- Graphic Control Label – 标识当前块为一个Graphic Control Extension。这个字段值为0xF9。
- Block Size – 在块中的字节数,在这个Block Size Field之后,直到但是并不包括Block Terminator,这个field包含固定的值4。
- Packed Fields包括:
- Reserved 3bits 保留字段。
- Disposal Method 3bits。
- User Input Flag 1bit。
- Transparent Color Flag 1bit。
- Disposal Method – 标识在展示之后图像的处理,值为:
- 0 – 未指定,解码器不需要做任何动作,这个选项可以将一个全尺寸,非透明框架替换为另一个。
- 1 – 不要处置,在此选项中,未被下一帧覆盖的任何像素继续显示。
- 2 – 还原为背景图像,被图像使用的区域必须还原为背景颜色。
- 3 – 还原为上一个,解码器必须还原被图像覆盖的区域为之前渲染的图像。
- 4-7 – 未定义。
- User Input Flag – 指示在继续之前是否需要用户输入,如果标识被设置,在用户输入之后会继续处理。用户输入的实质决定于应用(回车,用户点击等),值为:
- 0 – 不需要用户输入。
- 1 – 需要用户输入。
当Delay Time被使用并且用户输入设置为需要用户输入的时候,处理将会在得到用户输入或者延迟时间到期任一满足之后继续。
- Transparent Flag – 标识是否在Transparent Index字段给出一个透明度索引。值为:
- 0 – 不给出Transparent Index。
- 1 – 给出Transparent。
- Delay Time – 如果不是0,这个field指定了在继续处理数据流之前等待多少个百分之一秒。时钟在图形渲染之后立即开始计时。这个字段可以和User Input Flag字段结合使用。
- Transparency Index – 当遇到Transparency Index的时候,对应的显示设备的像素不被修改,并且处理继续进行到下一个像素。当且仅当Transparency Flag设置为1的时候才存在Transparency Index
- Block Terminator 这个0长度的数据块标记Graphic Control Extension的结束。
作用范围:这个扩展的作用范围是跟随者其的图像渲染块,可能有其他的扩展出现在这个块和其目标之间。这个块能够修改Image Descriptor Block和Plain Text Extension。
推荐:
- Disposal Method – 模式Restore To Previous目的在被部分的图像使用;使用这个模式给解码器提出了一些需求来存储需要保存的图像。因此,这个模式要谨慎使用。这个模式目的并不是存储 整个图像或者一张图像的大部分区域。在这种情况下,编码器应该尽最大努力尝试是图像的部分被恢复为数据流中的分离的图形。在解码器中不能够保存标记为“还 原为上一个”的图像的区域的情况下,建议解码器恢复为背景颜色。
- User Input Flag – 如果设置了这个标志,意味着需要用户输入。解码器需要发出铃声(0x07)来通知用户需要输入。在没有设置Delay Time的情况下,解码器需要无限期地等待用户输入。建议编码器不要在没有设置Delay Time的情况下单独设置User Input Flag。
Comment Extension
需求89a版本。
Comment Extension包含的文本信息并不是GIF数据流的实际图像中的一部分。它适合于包括图像,信用,描述或者其它任意类型的非控制,非图像信息的评论。 Comment Extension可能被解码器忽略,或者被保存等待后续处理。任何情况下,Comment Extension不会打断或者干扰数据流的处理。
这个块可选,任何数量的Comment Extension都可以出现在数据流中。
7 6 5 4 3 2 1 0 Field Name Type +---------------+ 0 | | Extension Introducer Byte +---------------+ 1 | | Comment Label Byte +---------------+ +===============+ | | N | | Comment Data Data Sub-blocks | | +===============+ +---------------+ 0 | | Block Terminator Byte +---------------+
- 1 Byte Extension Introducer
- 1 Byte Comment Label
- 1Data Sub-blocks Comment Data
- 1 Byte Block Terminator
介绍如下:
- Extension Introducer – 标识一个Extension块的开始,值为0x21。
- Comment Label – 标识这个块为一个Comment Extension,值为0xFE。
- Comment Data – 一系列的子块,没有最多255字节,最少1字节,在数据前面有1个字节的大小信息。系列子块被Block Terminator结束。
- Block Terminator – 这个0长度的数据块标志着Comment Extension的结束。
建议:
- 1)这个块为了给人看的,应该包含使用7bit ASCII字符集的文本。这个块不能够被用来存储控制信息。
- 2)这个块可以出现在数据流中一个块能够出现的任意地方。然而,推荐Comment Extension不要和控制或者数据模块搅合在一起。它们应当处于数据流的扩展能够出现的最开始或者最后的位置。
Plain Text Extension
需要89a版本。
Plain Text Extension包含文本数据和需要的能够将这些数据渲染为图像的信息的简单格式。文本数据应当编码为7字节能够打印出来的ASCII字符。文本数据使 用由块字段中的参数定义的字符单元网格来呈现。每个字符用一个单独的单元来渲染。快中的文本字符被渲染为单间隔字符,每个单元格一个字符,有最适合的字体 和大小。从块的数据部分顺序地去除数据自负并在单元内呈现,从网格中的坐上单元开始并且从左到右并从上到下地进行。呈现文本数据,直到到达数据的末尾或者 字符网格被填充完。在单元尺寸不允许非整数的时候,必须要丢弃分数单元。编码器必须要小心精确地指定网格单元,以避免这样的情况发生。
这个块需要Global Color Table存在,这个块使用的颜色来自流中的Global Color Table。如果其存在的话。这个块是一个图像渲染块,因此能够被一个Graphic Control Extension修改。这个块是可选的,任意数量的Plain Text Extension能够出现在数据流中。
7 6 5 4 3 2 1 0 Field Name Type +---------------+ 0 | | Extension Introducer Byte +---------------+ 1 | | Plain Text Label Byte +---------------+ +---------------+ 0 | | Block Size Byte +---------------+ 1 | | Text Grid Left Position Unsigned +- -+ 2 | | +---------------+ 3 | | Text Grid Top Position Unsigned +- -+ 4 | | +---------------+ 5 | | Text Grid Width Unsigned +- -+ 6 | | +---------------+ 7 | | Text Grid Height Unsigned +- -+ 8 | | +---------------+ 9 | | Character Cell Width Byte +---------------+ 10 | | Character Cell Height Byte +---------------+ 11 | | Text Foreground Color Index Byte +---------------+ 12 | | Text Background Color Index Byte +---------------+ +===============+ | | N | | Plain Text Data Data Sub-blocks | | +===============+ +---------------+ 0 | | Block Terminator Byte +---------------+
- 1 Byte Extension Introducer
- 1 Byte Plain Text Label
- 1 Byte Block SIze
- Unsigned(2字节)Text Grid Left Position
- Unsigned(2字节)Text Grid Top Position
- Unsigned(2字节)Text Grid Width
- Unsigned(2字节)Text Grid Height
- 1 Byte Character Cell Width
- 1 Byte Character Cell Height
- 1 Byte Text Foreground Color Index
- 1 Byte Text Background Color Index
- Data Sub-blocks Plain Text Data
- 1 Byte Block Terminator
介绍如下:
- Extension Introducer – 标识一个扩展块的开始,值为0x21。
- Plain Text Label – 标识当前块为Plain Text Extension 值为0x01。
- Block Size – 在扩展中的字节数,从Block Size字段后开始知道但是不包括data部分的开始。这个域值为12。
- Text Grid Left Position – 列数量,像素为单位,文本框左边缘到逻辑屏幕的左边缘的距离。
- Text Grid Top Position – 行数量,像素为单位,文本框上边缘到逻辑评语上边缘的距离。
- Image Grid With – 文本框宽度,像素为单位。
- Image Grid Height – 文本框高度,像素为单位。
- Character Cell Width – 文本框中每个字符单元的宽度。
- Character Cell Height – 文本框中每个字符单元的高度。
- Text Foreground Color Index – 到Global Color Table中的索引,用来渲染文本的前景。
- Text Background Color Index – 到Global Color Table中的索引,用来渲染文本的背景。
- Plain Text Data – 一系列的子块,每个大小最多为255,最少为1,在每个子块之前有一个字节标记块的大小,每个子块的结尾标记为BLock Terminator。
- Block Terminator – 这个0长度的数据块标记了Plain Text Data Block的结束。
扩展和范围:这个块的范围是其中包含的Plain Text Data子块,看这个快可能被Graphic Control Extension修改。
建议:在Plain Text Extension中的数据假设是预格式化的。对于字体和大小的选择交给解码器来自由控制。如果遇到字符为小于0x20或者大于0xf7,编码器应当使用 这样的网格和单元尺寸,单元能够水平或者垂直适应网格。为了尽可能地兼容,字符单元应当为大约8*8或者8*16(宽*高),考虑到非常规大小文本的图 像。
Application Extension
需要89a版本。
Application Extension包含应用特定信息,它符合扩展块的语法。
7 6 5 4 3 2 1 0 Field Name Type +---------------+ 0 | | Extension Introducer Byte +---------------+ 1 | | Extension Label Byte +---------------+ +---------------+ 0 | | Block Size Byte +---------------+ 1 | | +- -+ 2 | | +- -+ 3 | | Application Identifier 8 Bytes +- -+ 4 | | +- -+ 5 | | +- -+ 6 | | +- -+ 7 | | +- -+ 8 | | +---------------+ 9 | | +- -+ 10 | | Appl. Authentication Code 3 Bytes +- -+ 11 | | +---------------+ +===============+ | | | | Application Data Data Sub-blocks | | | | +===============+ +---------------+ 0 | | Block Terminator Byte +---------------+
- 1 Byte Extension Introducer
- 1 Byte Extension Label
- 1 Byte Block Size
- 8 Bytes Application Identifier
- 3 Bytes Application Authentication Code
- Data Sub-blocks Application Data
- 1 Byte Block Terminator
介绍如下:
- Extension Introducer – 标识这个块为扩展,值为0x21。
- Application Extension Label – 标识这个块为Application Extension,值为0xFF。
- Block Size – 在这个扩展块中的字节数量,从Block Size字段之下开始,直到但是并不包含Application Data的开始。这个字段值为固定的11。
- Application Identifier – 8个可以打印的ASCII字符用来标识拥有这个Application Extension的应用。
- Application Authentication Code – 3字节用来认证。
- Application Identifier。一个应用程序可以使用一个算法来计算一个二进制码唯一标识其为拥有这个Application Extension的应用。
文件尾
Trailer
这个块是一个单字段的块,标识着GIF数据流的结束,其值为固定的0x3B。
7 6 5 4 3 2 1 0 Field Name Type +---------------+ 0 | | GIF Trailer Byte +---------------+
为什么有的Gif不能循环播放
为什么有的Gif图能够循环播放但是有的Gif图播放一次就结束了?这个在协议中并没有介绍,其实原因在于一种Application Extension – Netscape Looping Application Extension。
其形式为
+---------------+ 0 | 0x21 | Extension Label +---------------+ 1 | 0xFF | Application Extension Label +---------------+ 2 | 0x0B | Block Size +---------------+ 3 | | +- -+ 4 | | +- -+ 5 | | +- -+ 6 | | +- NETSCAPE -+ Application Identifier (8 bytes) 7 | | +- -+ 8 | | +- -+ 9 | | +- -+ 10 | | +---------------+ 11 | | +- -+ 12 | 2.0 | Application Authentication Code (3 bytes) +- -+ 13 | | +===============+ --+ 14 | 0x03 | Sub-block Data Size | +---------------+ | 15 | 0x01 | Sub-block ID | +---------------+ | Application Data Sub-block 16 | | | +- -+ Loop Count (2 bytes) | 17 | | | +===============+ --+ 18 | 0x00 | Block Terminator +---------------+
这些字段大部分已经是写死了,只有一个Loop Count可以调整,如果Loop Count为0x00,就意味着无限循环播放,否则可以按照这个Loop Count的数值播放指定的次数。
如果数据流中有这个Netscape Looping Extension就可以控制播放几次甚至无限循环播放了。所以修复Gif不能够无限循环播放的问题就在于:
- 如果是87a版本Gif,要改成89a的版本,然后加上Netscape Looping Extension。
- 如果是89a版本Gif,那么加上这个Netscape Looping Extension就好了。
你也许感兴趣的:
- 庖丁解牛:GIF图片原理和储存结构
- 从菜鸟到巨星:程序员职业生涯的11个阶段
- 【外评】jpeg xl 图片格式和谷歌的阻击战
- Chrome已支持两年:Edge终于准备支持AVIF格式
- Chrome和Firefox浏览器将很快支持全新的AVIF图像格式
- 【外评】电脑从哪里获取时间?
- 【外评】为什么 Stack Overflow 正在消失?
- Android 全力押注 Rust,Linux 却在原地踏步?谷歌:用 Rust 重写固件太简单了!
- 【外评】哪些开源项目被广泛使用,但仅由少数人维护?
- 【外评】好的重构与不好的重构
你对本文的反应是: