* 音视频入门文章目录 *

GIF 编码知识

GIF 包含的数据块:

  • 文件头(Header)

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

  • 全局颜色表(Global Color Table)

  • Application Extension

  • Comment Extension

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

  • 图像标识符(Image Descriptor)

  • 局部颜色表(Local Color Table)

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

  • Plain Text Extension

  • 文件结尾(Trailer)

GIF 编码步骤

今天的目标是做出一张尺寸 700x700、7 个颜色画面切换的 GIF 动画。

文件头(Header)

GIF 的前 6 个字节内容是 GIF 的署名和版本号。有两个版本 GIF87a GIF89aGIF89a 版本才有多帧动画,所有这里使用 89a 版本。

示例代码:

  1. // GIF 文件头,6 个字节内容是 GIF 的署名和版本号
  2. uint8_t gif_header[] = {0x47, 0x49, 0x46, 0x38, 0x39, 0x61};
  3. fwrite(gif_header, 6, 1, gif_file);

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

从上一篇 音视频入门-17-GIF文件格式详解 我们知道:

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

  • 屏幕逻辑宽度:2 字节;

  • 屏幕逻辑高度:2 字节;

  • 打包值,大小为 1 字节

    • m - 全局颜色表标志,1 bit;
    • cr - 颜色深度,3 bit;(x: 可忽略)
    • s - 分类标志, 1 bit; (x: 不使用,设为 0)
    • pixel - 全局颜色列表大小,3 bit;
  • 背景颜色索引: 1 字节;

  • 像素宽高比: 1 字节;(x: 不使用,设为 0)

示例代码:

  1. // 逻辑屏幕标识符
  2. uint16_t gif_width = 700;
  3. uint16_t gif_height = 700;
  4. // 0xF2 = 1 1 1 1 0 0 1 0
  5. uint8_t gif_logical_screen_pack_byte = 0xF2;
  6. uint8_t gif_bg_color_index = 0;
  7. uint8_t gif_pixel_aspect = 0;
  8. fputc(gif_width >> 0, gif_file); // width low 8
  9. fputc(gif_width >> 8, gif_file); // width high 8
  10. fputc(gif_height >> 0, gif_file); // height low 8
  11. fputc(gif_height >> 8, gif_file); // height high 8
  12. fputc(gif_logical_screen_pack_byte, gif_file);
  13. fputc(gif_bg_color_index, gif_file);
  14. fputc(gif_pixel_aspect, gif_file);

全局颜色表(Global Color Table)

每个颜色索引由三字节组成,按 RGB 顺序排列。

由 【逻辑屏幕标识符】可知,颜色的索引数(2^(pixel+1))是 2 的倍数,如果图片颜色数目不够要补足。

比如,我们的图片用了 7 个颜色,颜色索引数是 8,所以最后再加一个颜色(占位,不使用)。

示例代码:

  1. // 颜色表
  2. uint32_t rainbowColors[] = {
  3. 0XFF0000, // 赤
  4. 0XFFA500, // 橙
  5. 0XFFFF00, // 黄
  6. 0X00FF00, // 绿
  7. 0X007FFF, // 青
  8. 0X0000FF, // 蓝
  9. 0X8B00FF, // 紫
  10. 0X000000 // 黑
  11. };
  12. // 全局颜色表、
  13. for(int i = 0; i < 8; i++) {
  14. // 根据颜色索引取出颜色表中的颜色
  15. uint32_t color_rgb = rainbowColors[i];
  16. // 当前颜色 R 分量
  17. uint8_t R = (color_rgb & 0xFF0000) >> 16;
  18. // 当前颜色 G 分量
  19. uint8_t G = (color_rgb & 0x00FF00) >> 8;
  20. // 当前颜色 B 分量
  21. uint8_t B = color_rgb & 0x0000FF;
  22. fputc(R, gif_file);
  23. fputc(G, gif_file);
  24. fputc(B, gif_file);
  25. }

Application Extension

Application Extension 这 19 个字节基本上 GIF 都一样。

0x21, 0xFF, 0x0B, 0x4E, 0x45, 0x54, 0x53, 0x43, 0x41, 0x50, 0x45, 0x32, 0x2E, 0x30, 0x03, 0x01, 0x00, 0x00, 0x00

代表的内容是 NETSCAPE2.0

示例代码:

  1. // Application Extension
  2. uint8_t gif_application_extension[] = {0x21, 0xFF, 0x0B, 0x4E, 0x45, 0x54, 0x53, 0x43, 0x41, 0x50, 0x45, 0x32, 0x2E, 0x30, 0x03, 0x01, 0x00, 0x00, 0x00};
  3. fwrite(gif_application_extension, 19, 1, gif_file);

Comment Extension

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

0x21, 0xFE, 0x20, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x65, 0x7A, 0x67, 0x69, 0x66, 0x2E, 0x63, 0x6F, 0x6D, 0x20, 0x47, 0x49, 0x46, 0x20, 0x6D, 0x61, 0x6B, 0x65, 0x72, 0x00

代表的内容是 Created with ezgif.com GIF maker

示例代码:

  1. // Comment Extension
  2. // Created with ezgif.com GIF maker
  3. uint8_t gif_comment_extension[] = {0x21, 0xFE, 0x20, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x65, 0x7A, 0x67, 0x69, 0x66, 0x2E, 0x63, 0x6F, 0x6D, 0x20, 0x47, 0x49, 0x46, 0x20, 0x6D, 0x61, 0x6B, 0x65, 0x72, 0x00};
  4. fwrite(gif_comment_extension, 36, 1, gif_file);

图形控制扩展(Graphic Control Extension)

我们的 GIF 不使用处置方法 不使用透明色 图像延迟 50

所以,这里就是 0x21, 0xF9, 0x04, 0x00, 0x32, 0x00, 0xFF, 0x00

示例代码:

  1. // 图形控制扩展
  2. uint8_t gif_graphic_control_extension[] = {0x21, 0xF9, 0x04, 0x00, 0x32, 0x00, 0xFF, 0x00};
  3. fwrite(gif_graphic_control_extension, 8, 1, gif_file);

图像标识符(Image Descriptor)

我们的 GIF 没有局部颜色表 顺序排列 局部颜色表大小为 0

所以,这里就是 0x2C, 0x00, 0x00, 0x00, 0x00, 0xBC, 0x02, 0xBC, 0x02, 0x00

示例代码:

  1. // 图像标识符
  2. uint8_t gif_image_descriptor[] = {0x2C, 0x00, 0x00, 0x00, 0x00, 0xBC, 0x02, 0xBC, 0x02, 0x00};
  3. fwrite(gif_image_descriptor, 10, 1, gif_file);

局部颜色表(Local Color Table)

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

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

这里是最关键的图像数据,生成步骤如下:

  • 1.根据全局颜色表或者局部颜色表,生成一张图像的颜色索引数据
  • 2.使用 LZW 算法压缩上一步生成的数据
  • 3.将压缩后的数据按照格式写入文件

1.生成索引数据

我们要生成的 GIF 尺寸 700x700,有 7 张图像,每张图像一个颜色 绿

颜色已经写入全局颜色表中;

每个颜色索引 1 字节;

示例代码:

  1. // 基于颜色表的图像数据
  2. uint8_t *gif_one_frame_raw = malloc(700 * 700);
  3. memset(gif_one_frame_raw, i, 700*700);

2.LZW 压缩数据

LZW 压缩算法不在本次研究范围,直接用即可。

  1. // GIF 一帧图像的数据压缩后大小
  2. unsigned long compressed_size;
  3. // GIF 一帧图像的数据解压后的数据
  4. unsigned char *img;
  5. lzw_compress_gif(
  6. 3,
  7. 700*700,
  8. gif_one_frame_raw,
  9. &compressed_size,
  10. &img
  11. );

3.按照格式写入文件

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

后面的是图像数据块:

每个数据块第一个字节表示数据块大小(不包括这个字节)

数据块后面的一个字节表示后续数据块大小

当数据块后面的一个字节是 0 ,表示数据结束了

示例代码:

  1. fputc(0x03, gif_file);
  2. unsigned long current_index = 0;
  3. while (current_index < compressed_size) {
  4. if((current_index + 0xFF) >= compressed_size) {
  5. unsigned long diff = compressed_size - current_index;
  6. fputc(diff, gif_file);
  7. fwrite(img+current_index, diff, 1, gif_file);
  8. fputc(0x00, gif_file);
  9. current_index += diff;
  10. } else {
  11. fputc(0xFF, gif_file);
  12. fwrite(img+current_index, 0xFF, 1, gif_file);
  13. current_index += 0xFF;
  14. }
  15. }

Plain Text Extension

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

所以直接忽略。

文件结尾(Trailer)

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

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

示例代码:

  1. // GIF 文件结束: 0x3B
  2. fputc(0x3B, gif_file);

查看 GIF

以上完整代码在 binglingziyu/audio-video-blog-demos 可以获取。

运行代码,生成 GIF 图片:


代码:

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

内容有误?联系作者:


音视频入门-18-手动生成一张GIF图片的更多相关文章

  1. 音视频入门-12-手动生成一张PNG图片

    * 音视频入门文章目录 * 预热 上一篇 [PNG文件格式详解]详细介绍了 PNG 文件的格式. PNG 图像格式文件由一个 8 字节的 PNG 文件署名域和 3 个以上的后续数据块(IHDR.IDA ...

  2. 音视频入门-13-使用开源库生成PNG图片

    * 音视频入门文章目录 * RGB-to-PNG 回顾 上一篇 [手动生成一张PNG图片] 根据 [PNG文件格式详解] 一步一步地手动实现了将 RGB 数据生成了一张 PNG 图片. 有许多开源的 ...

  3. 音视频入门-19-使用giflib处理GIF图片

    * 音视频入门文章目录 * GIFLIB The GIFLIB project 上一篇 [手动生成一张GIF图片], 自己生成了一张 GIF 动态图 rainbow.gif. 下面,使用 GIFLIB ...

  4. 音视频入门-20-BMP、PNG、JPG、GIF静态图生成GIF动态图

    * 音视频入门文章目录 * 静态图 -> 动态图 前面 [18-手动生成一张GIF图片] 和 [19-使用giflib处理GIF图片] 生成的 GIF 每一帧都是一个颜色,平时用到的 GIF 每 ...

  5. 音视频入门-11-PNG文件格式详解

    * 音视频入门文章目录 * PNG 文件格式解析 PNG 图像格式文件由一个 8 字节的 PNG 文件署名域和 3 个以上的后续数据块(IHDR.IDAT.IEND)组成. PNG 文件包括 8 字节 ...

  6. 音视频入门-14-JPEG文件格式详解

    * 音视频入门文章目录 * JPEG 文件格式解析 JPEG 文件使用的数据存储方式有多种.最常用的格式称为 JPEG 文件交换格式(JPEG File Interchange Format,JFIF ...

  7. 音视频入门-08-RGB&YUV

    * 音视频入门文章目录 * YUV & RGB 相互转换公式 YCbCr 的 Y 与 YUV 中的 Y 含义一致,Cb 和 Cr 与 UV 同样都指色彩,Cb 指蓝色色度,Cr 指红色色度,在 ...

  8. 音视频入门-04-BMP图像四字节对齐的问题

    * 音视频入门文章目录 * BMP 图像四字节对齐 表示 BMP 位图中像素的位元是以行为单位对齐存储的,每一行的大小都向上取整为4字节(32 位 DWORD)的倍数.如果图像的高度大于 1,多个经过 ...

  9. 音视频入门-03-RGB转成BMP图片

    * 音视频入门文章目录 * BMP 文件格式解析 BMP 文件由文件头.位图信息头.颜色信息和图形数据四部分组成. 位图文件头(14个字节) 位图信息头(40个字节) 颜色信息 图形数据 文件头与信息 ...

随机推荐

  1. 初步配置基于Struts的maven的Web项目demo

    1.创建mavenweb项目 eclipse | Idea 中创建maven项目,具体步骤PASS. 2.导入Struts2核心架包 Idea中在maven项目上,选择ADD FRAMEWORK ec ...

  2. hdu3974 Assign the task线段树 dfs序

    题意: 无序的给编号为1-n的员工安排上下级, 操作一:给一个员工任务C,则该员工以及他的下级任务都更换为任务C 操作二:询问一个员工,返回他的任务   题解: 给一个员工任务,则他所在组都要改变,联 ...

  3. 02_tcp_deadlock

    # 这个程序我们是测试客户端和服务端在进行通信的过程中,可能会产生死锁的情况. # 这是因为缓冲区,和TCP协议的可靠性连接导致的. # 在程序中我们可以看到,客户端先向服务端发送数据,然后服务端就收 ...

  4. c++11-17 模板核心知识(二)—— 类模板

    类模板声明.实现与使用 Class Instantiation 使用类模板的部分成员函数 Concept 友元 方式一 方式二 类模板的全特化 类模板的偏特化 多模板参数的偏特化 默认模板参数 Typ ...

  5. 详解Git

    版本控制 基本概念 版本控制是一个非常简单的概念,我举个例子简单说明一下你就能大概明白它是一个什么东西,当你不会版本控制的时候: 你的BOSS决定做一款灰常牛逼的软件,找你进行开发 当你做好之后,老板 ...

  6. 痞子衡嵌入式:RT-UFL - 一个适用全平台i.MXRT的超级下载算法设计

    大家好,我是痞子衡,是正经搞技术的痞子.今天给大家带来的是痞子衡的开源项目 RT-UFL. 痞子衡在近两年多的i.MXRT客户项目支持过程中,遇到的一个相当高频的问题就是制作i.MXRT下载算法.我们 ...

  7. Cassandra数据模型和模式(Schema)的配置检查

    免责声明 本文档提供了有关DataStax Enterprise(DSE)和Apache Cassandra的常规数据建模和架构配置建议.本文档需要DSE / Cassandra基本知识.它不能代替官 ...

  8. Python学习笔记4:函数

    1.函数 函数就是一段具有特点功能的.可重用的语句组. 在Python中函数是以关键词 def 开头,空格之后连接函数名和圆括号(),最后一个冒号:结尾. 函数名只能包含字符串.下划线和数字且不能以数 ...

  9. linux 内存泄露 valgrind

    内泄漏工具 valgrind: https://linux.die.net/man/1/valgrind www.valgrind.org/docs/manual/index.html www.val ...

  10. NO.A.0001——day01——Java概述/进制间的转换

    一.什么是JAVA语言:        JAVA语言是美国sun公司(Stanford University Network)在1995年推出的高级编程语言.所谓编程语言,是计算机的语言,人们可以使用 ...