美文网首页
音视频入门-17-GIF文件格式详解

音视频入门-17-GIF文件格式详解

作者: binglingziyu | 来源:发表于2020-06-08 01:57 被阅读0次

    * 音视频入门文章目录 *

    GIF 文件格式解析

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

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

    GIF File Format

    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 图片。

    用来分析的样例图片

    用来分析的样例图片

    十六进制编辑器

    (1) 文件头(Header)

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

    • 87a:是在 1987 年制定的版本
    • 89a:是在 1989 年制定的版本
    文件头(Header)

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

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

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

    逻辑屏幕标识符(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 字节;

    逻辑屏幕标识符(Logical Screen Descriptor

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

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

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

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

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

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

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

    (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.
    
    (4) Application Extension

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

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

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

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

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

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

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

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

    图形控制扩展-Hex

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

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

    (6) Comment Extension

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

    (6) Comment Extension

    下面是这张图片的 Comment Extension:

    Comment Extension Hex

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

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

    图像标识符(Image Descriptor)

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

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

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

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

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

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

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

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

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

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

    后面的是图像数据块:

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

    如上图所示:

    • 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\n", 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\n", 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
    
    gif-one-frame-rgb-file-preview.jpg

    代码:
    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文件格式详解

          本文链接:https://www.haomeiwen.com/subject/tvuktktx.html