音视频入门-17-GIF文件格式详解 GIF 文件格式解析 (0) 准备 GIF 图片 & 十六进制查看工具 (1) 文件头(Header) (2) 逻辑屏幕标识符(Logical Screen Descriptor) (3) 全局颜色表(Global Color Table) (4) Application Extension (5) 图形控制扩展(Graphic Control Extension) (6) Comment Extension (7) 图像标识符(Image Descriptor) (8) 局部颜色表(Local Color Table) (9) 基于颜色表的图像数据(Image Data) (10) Plain Text Extension (11) 文件结尾(Trailer) 根据图像数据块 & 颜色表还原图像

音视频入门-17-GIF文件格式详解
GIF 文件格式解析
(0) 准备 GIF 图片 & 十六进制查看工具
(1) 文件头(Header)
(2) 逻辑屏幕标识符(Logical Screen Descriptor)
(3) 全局颜色表(Global Color Table)
(4) Application Extension
(5) 图形控制扩展(Graphic Control Extension)
(6) Comment Extension
(7) 图像标识符(Image Descriptor)
(8) 局部颜色表(Local Color Table)
(9) 基于颜色表的图像数据(Image Data)
(10) Plain Text Extension
(11) 文件结尾(Trailer)
根据图像数据块 & 颜色表还原图像

* 音视频入门文章目录 *

图像互换格式主要分为两个版本,即图像互换格式 87a 和图像互换格式 89a。
图像互换格式 87a:是在 1987 年制定的版本。
图像互换格式 89a:是在 1989 年制定的版本。在这个版本中,为图像互换格式文档扩充了图形控制区块、备注、说明、应用程序接口等四个区块,并提供了对透明色和多帧动画的支持。

现在我们一般所说的 GIF 动画都是指 89a 的格式。

音视频入门-17-GIF文件格式详解
GIF 文件格式解析
(0) 准备 GIF 图片 & 十六进制查看工具
(1) 文件头(Header)
(2) 逻辑屏幕标识符(Logical Screen Descriptor)
(3) 全局颜色表(Global Color Table)
(4) Application Extension
(5) 图形控制扩展(Graphic Control Extension)
(6) Comment Extension
(7) 图像标识符(Image Descriptor)
(8) 局部颜色表(Local Color Table)
(9) 基于颜色表的图像数据(Image Data)
(10) Plain Text Extension
(11) 文件结尾(Trailer)
根据图像数据块 & 颜色表还原图像

GIF 包含的数据块:

  • 文件头(Header)

  • 逻辑屏幕标识符(Logical Screen Descriptor)

  • 全局颜色表(Global Color Table)

  • 图形控制扩展(Graphic Control Extension)

  • 图像标识符(Image Descriptor)

  • 局部颜色表(Local Color Table)

  • 基于颜色表的图像数据(Image Data)

  • Plain Text Extension

  • Application Extension

  • Comment Extension

  • 文件结尾(Trailer)

(0) 准备 GIF 图片 & 十六进制查看工具

本文所有分析,都是基于下面这张 GIF 图片。

音视频入门-17-GIF文件格式详解
GIF 文件格式解析
(0) 准备 GIF 图片 & 十六进制查看工具
(1) 文件头(Header)
(2) 逻辑屏幕标识符(Logical Screen Descriptor)
(3) 全局颜色表(Global Color Table)
(4) Application Extension
(5) 图形控制扩展(Graphic Control Extension)
(6) Comment Extension
(7) 图像标识符(Image Descriptor)
(8) 局部颜色表(Local Color Table)
(9) 基于颜色表的图像数据(Image Data)
(10) Plain Text Extension
(11) 文件结尾(Trailer)
根据图像数据块 & 颜色表还原图像

用来分析的样例图片

十六进制编辑器

(1) 文件头(Header)

GIF 的前 6 个字节内容是 GIF 的署名和版本号。
我们可以通过前 3 个字节判断文件是否为 GIF 格式,后 3 个字节判断 GIF 格式的版本:

  • 87a:是在 1987 年制定的版本
  • 89a:是在 1989 年制定的版本

音视频入门-17-GIF文件格式详解
GIF 文件格式解析
(0) 准备 GIF 图片 & 十六进制查看工具
(1) 文件头(Header)
(2) 逻辑屏幕标识符(Logical Screen Descriptor)
(3) 全局颜色表(Global Color Table)
(4) Application Extension
(5) 图形控制扩展(Graphic Control Extension)
(6) Comment Extension
(7) 图像标识符(Image Descriptor)
(8) 局部颜色表(Local Color Table)
(9) 基于颜色表的图像数据(Image Data)
(10) Plain Text Extension
(11) 文件结尾(Trailer)
根据图像数据块 & 颜色表还原图像

(2) 逻辑屏幕标识符(Logical Screen Descriptor)

逻辑屏幕标识符配置了 GIF 一些全局属性,我们通过读取解析它,获取 GIF 全局的一些配置。

音视频入门-17-GIF文件格式详解
GIF 文件格式解析
(0) 准备 GIF 图片 & 十六进制查看工具
(1) 文件头(Header)
(2) 逻辑屏幕标识符(Logical Screen Descriptor)
(3) 全局颜色表(Global Color Table)
(4) Application Extension
(5) 图形控制扩展(Graphic Control Extension)
(6) Comment Extension
(7) 图像标识符(Image Descriptor)
(8) 局部颜色表(Local Color Table)
(9) 基于颜色表的图像数据(Image Data)
(10) Plain Text Extension
(11) 文件结尾(Trailer)
根据图像数据块 & 颜色表还原图像

逻辑屏幕标识符(7 个字节):

  • 屏幕逻辑宽度:定义了 GIF 图像的像素宽度,大小为 2 字节;

  • 屏幕逻辑高度:定义了 GIF 图像的像素高度,大小为 2 字节;

  • 打包值,大小为 1 字节

    • m - 全局颜色表标志(Global Color Table Flag),当置位时表示有全局颜色列表,pixel 值有意义;
    • cr - 颜色深度(Color ResoluTion),cr+1 确定图象的颜色深度;
    • s - 分类标志(Sort Flag),如果置位表示全局颜色列表分类排列;
    • pixel - 全局颜色列表大小,pixel+1 确定颜色列表的索引数(2^(pixel+1));
  • 背景颜色:背景颜色在全局颜色列表中的索引(PS:是索引而不是 RGB 值,所以如果没有全局颜色列表时,该值没有意义),大小为 1 字节;

  • 像素宽高比:全局像素的宽度与高度的比值,大小为 1 字节;

音视频入门-17-GIF文件格式详解
GIF 文件格式解析
(0) 准备 GIF 图片 & 十六进制查看工具
(1) 文件头(Header)
(2) 逻辑屏幕标识符(Logical Screen Descriptor)
(3) 全局颜色表(Global Color Table)
(4) Application Extension
(5) 图形控制扩展(Graphic Control Extension)
(6) Comment Extension
(7) 图像标识符(Image Descriptor)
(8) 局部颜色表(Local Color Table)
(9) 基于颜色表的图像数据(Image Data)
(10) Plain Text Extension
(11) 文件结尾(Trailer)
根据图像数据块 & 颜色表还原图像

从图中可以看出,这张 GIF 图片:

  • 宽度:700
  • 高度:700
  • 有全局颜色表
  • 颜色深度 8
  • 全局颜色表大小 8

PS: Glide 中在读取了全局的宽高之后,忽略了颜色深度和分类标志,像素宽高比也只是读取,后续并没有使用到

(3) 全局颜色表(Global Color Table)

全局颜色表,在逻辑屏幕标识之后,每个颜色索引由三字节组成,按 RGB 顺序排列。

(2) 逻辑屏幕标识符(Logical Screen Descriptor) 中得到,全局颜色表大小是 8 个颜色,每个颜色占 3 字节(R、G、B)。

音视频入门-17-GIF文件格式详解
GIF 文件格式解析
(0) 准备 GIF 图片 & 十六进制查看工具
(1) 文件头(Header)
(2) 逻辑屏幕标识符(Logical Screen Descriptor)
(3) 全局颜色表(Global Color Table)
(4) Application Extension
(5) 图形控制扩展(Graphic Control Extension)
(6) Comment Extension
(7) 图像标识符(Image Descriptor)
(8) 局部颜色表(Local Color Table)
(9) 基于颜色表的图像数据(Image Data)
(10) Plain Text Extension
(11) 文件结尾(Trailer)
根据图像数据块 & 颜色表还原图像

音视频入门-17-GIF文件格式详解
GIF 文件格式解析
(0) 准备 GIF 图片 & 十六进制查看工具
(1) 文件头(Header)
(2) 逻辑屏幕标识符(Logical Screen Descriptor)
(3) 全局颜色表(Global Color Table)
(4) Application Extension
(5) 图形控制扩展(Graphic Control Extension)
(6) Comment Extension
(7) 图像标识符(Image Descriptor)
(8) 局部颜色表(Local Color Table)
(9) 基于颜色表的图像数据(Image Data)
(10) Plain Text Extension
(11) 文件结尾(Trailer)
根据图像数据块 & 颜色表还原图像

(4) Application Extension

接下来出现的是 21 FF,特定于应用程序的信息,这个并没有太大用处。唯一已知的公共文件是 Netscape 2.0 扩展(如下所述),用于循环动画GIF文件。

Netscape 2.0 循环块扩展必须立即出现在逻辑屏幕描述符的全局颜色表之后。它有19个字节长。

byte  1        : 33 (hex 0x21) GIF Extension code
byte  2        : 255 (hex 0xFF) Application Extension Label
byte  3        : 11 (hex 0x0B) Length of Application Block
                 (eleven bytes of data to follow)
bytes 4 to 11  : "NETSCAPE"
bytes 12 to 14 : "2.0"
byte  15       : 3 (hex 0x03) Length of Data Sub-Block
                 (three bytes of data to follow)
byte  16       : 1 (hex 0x01)
bytes 17 to 18 : 0 to 65535, an unsigned integer in
                 little-endian byte format. This specifies the
                 number of times the loop should
                 be executed.
byte  19       : 0 (hex 0x00) a Data Sub-Block Terminator.

音视频入门-17-GIF文件格式详解
GIF 文件格式解析
(0) 准备 GIF 图片 & 十六进制查看工具
(1) 文件头(Header)
(2) 逻辑屏幕标识符(Logical Screen Descriptor)
(3) 全局颜色表(Global Color Table)
(4) Application Extension
(5) 图形控制扩展(Graphic Control Extension)
(6) Comment Extension
(7) 图像标识符(Image Descriptor)
(8) 局部颜色表(Local Color Table)
(9) 基于颜色表的图像数据(Image Data)
(10) Plain Text Extension
(11) 文件结尾(Trailer)
根据图像数据块 & 颜色表还原图像

Application Extension 这 19 个字节基本上目前所有 GIF 都一样。

(5) 图形控制扩展(Graphic Control Extension)

在 89a 版本,GIF 添加了图形控制扩展块。放在一个图象块(图象标识符)的前面,用来控制它后面的第一个图象的显示。

音视频入门-17-GIF文件格式详解
GIF 文件格式解析
(0) 准备 GIF 图片 & 十六进制查看工具
(1) 文件头(Header)
(2) 逻辑屏幕标识符(Logical Screen Descriptor)
(3) 全局颜色表(Global Color Table)
(4) Application Extension
(5) 图形控制扩展(Graphic Control Extension)
(6) Comment Extension
(7) 图像标识符(Image Descriptor)
(8) 局部颜色表(Local Color Table)
(9) 基于颜色表的图像数据(Image Data)
(10) Plain Text Extension
(11) 文件结尾(Trailer)
根据图像数据块 & 颜色表还原图像

处置方法(Disposal Method):指出处置图形的方法:

  • 0 - 不使用处置方法
  • 1 - 不处置图形,把图形从当前位置移去
  • 2 - 回复到背景色
  • 3 - 回复到先前状态
  • 4-7 - 自定义用户输入标志(Use Input Flag):指出是否期待用户有输入之后才继续进行下去,置位表示期待,值否表示不期待。

用户输入可以是按回车键、鼠标点击等,可以和延迟时间一起使用,在设置的延迟时间内用户有输入则马上继续进行,或者没有输入直到延迟时间到达而继续。

透明颜色标志(Transparent Color Flag):置位表示使用透明颜色。

音视频入门-17-GIF文件格式详解
GIF 文件格式解析
(0) 准备 GIF 图片 & 十六进制查看工具
(1) 文件头(Header)
(2) 逻辑屏幕标识符(Logical Screen Descriptor)
(3) 全局颜色表(Global Color Table)
(4) Application Extension
(5) 图形控制扩展(Graphic Control Extension)
(6) Comment Extension
(7) 图像标识符(Image Descriptor)
(8) 局部颜色表(Local Color Table)
(9) 基于颜色表的图像数据(Image Data)
(10) Plain Text Extension
(11) 文件结尾(Trailer)
根据图像数据块 & 颜色表还原图像

从图中可以看出,这张 GIF 图片:

  • 不使用处置方法
  • 不使用透明色
  • 后面的图像延迟 50 (单位:1/100 秒)

(6) Comment Extension

接下来出现的是 21 FE,这允许你将 ASCII 文本嵌入到 GIF 文件,有时被用来图像描述、图像信贷或其他人类可读的元数据,如图像捕获的 GPS 定位。

音视频入门-17-GIF文件格式详解
GIF 文件格式解析
(0) 准备 GIF 图片 & 十六进制查看工具
(1) 文件头(Header)
(2) 逻辑屏幕标识符(Logical Screen Descriptor)
(3) 全局颜色表(Global Color Table)
(4) Application Extension
(5) 图形控制扩展(Graphic Control Extension)
(6) Comment Extension
(7) 图像标识符(Image Descriptor)
(8) 局部颜色表(Local Color Table)
(9) 基于颜色表的图像数据(Image Data)
(10) Plain Text Extension
(11) 文件结尾(Trailer)
根据图像数据块 & 颜色表还原图像

下面是这张图片的 Comment Extension:

音视频入门-17-GIF文件格式详解
GIF 文件格式解析
(0) 准备 GIF 图片 & 十六进制查看工具
(1) 文件头(Header)
(2) 逻辑屏幕标识符(Logical Screen Descriptor)
(3) 全局颜色表(Global Color Table)
(4) Application Extension
(5) 图形控制扩展(Graphic Control Extension)
(6) Comment Extension
(7) 图像标识符(Image Descriptor)
(8) 局部颜色表(Local Color Table)
(9) 基于颜色表的图像数据(Image Data)
(10) Plain Text Extension
(11) 文件结尾(Trailer)
根据图像数据块 & 颜色表还原图像

(7) 图像标识符(Image Descriptor)

一个 GIF 文件中可以有多个图像块,每个图像块就会有图像标识符,描述了当前帧的一些属性。下面我们来看看图像标识符中包含的一些信息。

音视频入门-17-GIF文件格式详解
GIF 文件格式解析
(0) 准备 GIF 图片 & 十六进制查看工具
(1) 文件头(Header)
(2) 逻辑屏幕标识符(Logical Screen Descriptor)
(3) 全局颜色表(Global Color Table)
(4) Application Extension
(5) 图形控制扩展(Graphic Control Extension)
(6) Comment Extension
(7) 图像标识符(Image Descriptor)
(8) 局部颜色表(Local Color Table)
(9) 基于颜色表的图像数据(Image Data)
(10) Plain Text Extension
(11) 文件结尾(Trailer)
根据图像数据块 & 颜色表还原图像

图像标识符以 ',' (0x2c) 作为开始标志。接着定义了当前帧的偏移量和宽高。

最后 5 个标志的意义分别为:

  • m - 局部颜色表标志(Local Color Table Flag)
    置位时标识紧接在图象标识符之后有一个局部颜色列表,供紧跟在它之后的一幅图象使用;值否时使用全局颜色列表,忽略 pixel 值。
  • i - 交织标志(Interlace Flag),置位时图象数据使用交织方式排列,否则使用顺序排列。
  • s - 分类标志(Sort Flag),如果置位表示紧跟着的局部颜色列表分类排列.
  • r - 保留,必须初始化为 0.
  • pixel - 局部颜色表大小(Size of Local Color Table),pixel+1 就为颜色表的大小

音视频入门-17-GIF文件格式详解
GIF 文件格式解析
(0) 准备 GIF 图片 & 十六进制查看工具
(1) 文件头(Header)
(2) 逻辑屏幕标识符(Logical Screen Descriptor)
(3) 全局颜色表(Global Color Table)
(4) Application Extension
(5) 图形控制扩展(Graphic Control Extension)
(6) Comment Extension
(7) 图像标识符(Image Descriptor)
(8) 局部颜色表(Local Color Table)
(9) 基于颜色表的图像数据(Image Data)
(10) Plain Text Extension
(11) 文件结尾(Trailer)
根据图像数据块 & 颜色表还原图像

从图中可以看出,这张 GIF 图片:

  • 没有局部颜色表
  • 顺序排列
  • 局部颜色表大小为 0

(8) 局部颜色表(Local Color Table)

如果有局部颜色表,则跟 (3) 全局颜色表(Global Color Table) 一样的格式。

(9) 基于颜色表的图像数据(Image Data)

接下来就是图像数据(已使用 LZW 算法压缩,解压后才是真正的 基于颜色表的图像数据 )。

数据的第一个字节表示 LZW 编码初始表大小的位数,用于使用 LZW 算法解压数据。

后面的是图像数据块:

  • 每个数据块第一个字节表示数据块大小(不包括这个字节)
  • 数据块后面的一个字节表示后续数据块大小
  • 当数据块后面的一个字节是 0 ,表示数据结束了

音视频入门-17-GIF文件格式详解
GIF 文件格式解析
(0) 准备 GIF 图片 & 十六进制查看工具
(1) 文件头(Header)
(2) 逻辑屏幕标识符(Logical Screen Descriptor)
(3) 全局颜色表(Global Color Table)
(4) Application Extension
(5) 图形控制扩展(Graphic Control Extension)
(6) Comment Extension
(7) 图像标识符(Image Descriptor)
(8) 局部颜色表(Local Color Table)
(9) 基于颜色表的图像数据(Image Data)
(10) Plain Text Extension
(11) 文件结尾(Trailer)
根据图像数据块 & 颜色表还原图像

如上图所示:

  • LZW 编码初始表大小的位数:3
  • 标蓝色的所有字节就是完整图像块数据

最后一步,我们将使用 LZW 算法解压图像数据块,并根据颜色表还原出整张图像(GIF 的一帧)的 RGB 文件。
需要删除标蓝色以外的所有字节,保存为 rainbow-compressed.gif.frame

(10) Plain Text Extension

这个特性不起作用; 浏览器和图片处理应用程序,如 Photoshop 忽略它, GIFLIB 并不试图解释它。

(11) 文件结尾(Trailer)

标识 GIF 文件结束,固定值 0x3B。

当解析程序读到 0x3B 时,文件终结。

根据图像数据块 & 颜色表还原图像

根据 (9) 基于颜色表的图像数据(Image Data) ,可以得到 GIF 一帧图像的数据(已使用 LZW 算法压缩)。

文 件 名:rainbow-compressed.gif.frame
文件大小:3428字节

LZW 解压

这里直接使用 github.com/jefftime/lzw 这个库。

#include "stdio.h"
#include "stdlib.h"
#include "lzw/src/lzw.h"

int main () {
    // LZW 编码初始表大小的位数:3
    unsigned char code_size = 3;
    //  GIF 一帧图像的数据压缩文件(rainbow-compressed.gif.frame)大小
    long total_bytes;
    // GIF 一帧图像的数据压缩数据
    unsigned char *img_compressed;
    // GIF 一帧图像的数据解压后的数据
    unsigned char *img;
    //  GIF 一帧图像的数据解压后大小
    unsigned long decompressed_size;

    FILE *gif_compressed_frame = fopen("/Users/staff/Desktop/rainbow-compressed.gif.frame", "rb+");
    fseek(gif_compressed_frame, 0L, SEEK_END);
    total_bytes = ftell(gif_compressed_frame);
    fseek(gif_compressed_frame, 0L, SEEK_SET);
    printf("Gif 一帧压缩文件大小:%li
", total_bytes);

    img_compressed = malloc((unsigned long) total_bytes);
    fread(img_compressed, total_bytes, 1, gif_compressed_frame);

    // 进行 LZW 解压
    lzw_decompress(
        code_size,
        total_bytes,
        img_compressed,
        &decompressed_size,
        &img
    );

    printf("Gif 一帧解压文件大小:%li
", decompressed_size);

    FILE *gif_decompressed_frame = fopen("/Users/staff/Desktop/rainbow-decompressed.gif.frame", "wb+");
    fwrite(img, decompressed_size, 1, gif_decompressed_frame);
    fflush(gif_decompressed_frame);
    
    free(img_compressed);
    free(img);
    fclose(gif_compressed_frame);
    fclose(gif_decompressed_frame);

    return 0;
}

解压后得到解压文件:rainbow-decompressed.gif.frame
解压后文件大小:490000字节 (700x700x1)
解压后文件中每一个字节代表颜色表的一个颜色索引

还原出 RGB 文件

#include "stdio.h"
#include "stdlib.h"
#include "lzw/src/lzw.h"

// 颜色表
uint32_t rainbowColors[] = {
        0XFF0000, // 赤
        0X00FF00, // 绿
        0XFFA500, // 橙
        0XFFFF00, // 黄
        0X0000FF, // 蓝
        0X007FFF, // 青
        0X8B00FF, // 紫
        0X000000  // 黑
};

int main () {
    ......
    
    FILE *gif_frame_rgb = fopen("/Users/staff/Desktop/rainbow-decompressed.gif.frame.rgb", "wb+");
    for(int i = 0; i < decompressed_size; i++) {
        // 颜色索引值
        unsigned char color_index = img[i];
        // 根据颜色索引取出颜色表中的颜色
        uint32_t color_rgb = rainbowColors[color_index];
        // 当前颜色 R 分量
        uint8_t R = (color_rgb & 0xFF0000) >> 16;
        // 当前颜色 G 分量
        uint8_t G = (color_rgb & 0x00FF00) >> 8;
        // 当前颜色 B 分量
        uint8_t B = color_rgb & 0x0000FF;
        fputc(R, gif_frame_rgb);
        fputc(G, gif_frame_rgb);
        fputc(B, gif_frame_rgb);
    }
    fflush(gif_frame_rgb);
    
    ......
}

这一步,得到了 GIF 一帧图像的 RGB 文件:rainbow-decompressed.gif.frame.rgb
GIF 一帧图像的 RGB 文件大小为:1470000 字节(700x700x3)

查看还原出来的 RGB 文件

ffplay -f rawvideo -pixel_format rgb24 -s 700x700 rainbow-decompressed.gif.frame.rgb

音视频入门-17-GIF文件格式详解
GIF 文件格式解析
(0) 准备 GIF 图片 & 十六进制查看工具
(1) 文件头(Header)
(2) 逻辑屏幕标识符(Logical Screen Descriptor)
(3) 全局颜色表(Global Color Table)
(4) Application Extension
(5) 图形控制扩展(Graphic Control Extension)
(6) Comment Extension
(7) 图像标识符(Image Descriptor)
(8) 局部颜色表(Local Color Table)
(9) 基于颜色表的图像数据(Image Data)
(10) Plain Text Extension
(11) 文件结尾(Trailer)
根据图像数据块 & 颜色表还原图像


代码:
audio-video-blog-demos

参考资料:

What's In A GIF

Gif 89a specification

GIF 格式解析

GIF 图片原理和储存结构

Gif 图片格式完全理解

GIF 文件格式详解

GIF 图形文件格式文档

GIF 文件格式详解

LZW 压缩算法——简明原理与实现

github.com/jefftime/lzw

https://github.com/jcraveiro

LZW compressor / decompressor

ASCII Codes Table

内容有误?联系作者:

音视频入门-17-GIF文件格式详解
GIF 文件格式解析
(0) 准备 GIF 图片 & 十六进制查看工具
(1) 文件头(Header)
(2) 逻辑屏幕标识符(Logical Screen Descriptor)
(3) 全局颜色表(Global Color Table)
(4) Application Extension
(5) 图形控制扩展(Graphic Control Extension)
(6) Comment Extension
(7) 图像标识符(Image Descriptor)
(8) 局部颜色表(Local Color Table)
(9) 基于颜色表的图像数据(Image Data)
(10) Plain Text Extension
(11) 文件结尾(Trailer)
根据图像数据块 & 颜色表还原图像