音视频入门文章目录 html
GIF 包含的数据块:git
今天的目标是作出一张尺寸 700x700、7 个颜色画面切换的 GIF 动画。 github
GIF 的前 6 个字节内容是 GIF 的署名和版本号。有两个版本 GIF87a
GIF89a
,GIF89a
版本才有多帧动画,全部这里使用 89a
版本。算法
示例代码:浏览器
// GIF 文件头,6 个字节内容是 GIF 的署名和版本号 uint8_t gif_header[] = {0x47, 0x49, 0x46, 0x38, 0x39, 0x61}; fwrite(gif_header, 6, 1, gif_file);
从上一篇 音视频入门-17-GIF文件格式详解 咱们知道:app
逻辑屏幕标识符(7 个字节):ide
打包值,大小为 1 字节post
示例代码:动画
// 逻辑屏幕标识符 uint16_t gif_width = 700; uint16_t gif_height = 700; // 0xF2 = 1 1 1 1 0 0 1 0 uint8_t gif_logical_screen_pack_byte = 0xF2; uint8_t gif_bg_color_index = 0; uint8_t gif_pixel_aspect = 0; fputc(gif_width >> 0, gif_file); // width low 8 fputc(gif_width >> 8, gif_file); // width high 8 fputc(gif_height >> 0, gif_file); // height low 8 fputc(gif_height >> 8, gif_file); // height high 8 fputc(gif_logical_screen_pack_byte, gif_file); fputc(gif_bg_color_index, gif_file); fputc(gif_pixel_aspect, gif_file);
每一个颜色索引由三字节组成,按 RGB 顺序排列。ui
由 【逻辑屏幕标识符】可知,颜色的索引数(2^(pixel+1))是 2 的倍数,若是图片颜色数目不够要补足。
好比,咱们的图片用了 7 个颜色,颜色索引数是 8,因此最后再加一个颜色(占位,不使用)。
示例代码:
// 颜色表 uint32_t rainbowColors[] = { 0XFF0000, // 赤 0XFFA500, // 橙 0XFFFF00, // 黄 0X00FF00, // 绿 0X007FFF, // 青 0X0000FF, // 蓝 0X8B00FF, // 紫 0X000000 // 黑 }; // 全局颜色表、 for(int i = 0; i < 8; i++) { // 根据颜色索引取出颜色表中的颜色 uint32_t color_rgb = rainbowColors[i]; // 当前颜色 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_file); fputc(G, gif_file); fputc(B, gif_file); }
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
示例代码:
// Application Extension uint8_t gif_application_extension[] = {0x21, 0xFF, 0x0B, 0x4E, 0x45, 0x54, 0x53, 0x43, 0x41, 0x50, 0x45, 0x32, 0x2E, 0x30, 0x03, 0x01, 0x00, 0x00, 0x00}; fwrite(gif_application_extension, 19, 1, gif_file);
这里容许你将 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
示例代码:
// Comment Extension // Created with ezgif.com GIF maker 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}; fwrite(gif_comment_extension, 36, 1, gif_file);
咱们的 GIF 不使用处置方法
不使用透明色
图像延迟 50
。
因此,这里就是 0x21, 0xF9, 0x04, 0x00, 0x32, 0x00, 0xFF, 0x00
。
示例代码:
// 图形控制扩展 uint8_t gif_graphic_control_extension[] = {0x21, 0xF9, 0x04, 0x00, 0x32, 0x00, 0xFF, 0x00}; fwrite(gif_graphic_control_extension, 8, 1, gif_file);
咱们的 GIF 没有局部颜色表
顺序排列
局部颜色表大小为 0
。
因此,这里就是 0x2C, 0x00, 0x00, 0x00, 0x00, 0xBC, 0x02, 0xBC, 0x02, 0x00
。
示例代码:
// 图像标识符 uint8_t gif_image_descriptor[] = {0x2C, 0x00, 0x00, 0x00, 0x00, 0xBC, 0x02, 0xBC, 0x02, 0x00}; fwrite(gif_image_descriptor, 10, 1, gif_file);
若是有局部颜色表,则跟 全局颜色表(Global Color Table) 同样的格式。
这里是最关键的图像数据,生成步骤以下:
咱们要生成的 GIF 尺寸 700x700,有 7 张图像,每张图像一个颜色 赤
橙
黄
绿
青
蓝
紫
;
颜色已经写入全局颜色表中;
每一个颜色索引 1 字节;
示例代码:
// 基于颜色表的图像数据 uint8_t *gif_one_frame_raw = malloc(700 * 700); memset(gif_one_frame_raw, i, 700*700);
LZW 压缩算法不在本次研究范围,直接用便可。
// GIF 一帧图像的数据压缩后大小 unsigned long compressed_size; // GIF 一帧图像的数据解压后的数据 unsigned char *img; lzw_compress_gif( 3, 700*700, gif_one_frame_raw, &compressed_size, &img );
第一个字节表示 LZW 编码初始表大小的位数,用于使用 LZW 算法解压数据。
后面的是图像数据块:
每一个数据块第一个字节表示数据块大小(不包括这个字节)
数据块后面的一个字节表示后续数据块大小
当数据块后面的一个字节是 0 ,表示数据结束了
示例代码:
fputc(0x03, gif_file); unsigned long current_index = 0; while (current_index < compressed_size) { if((current_index + 0xFF) >= compressed_size) { unsigned long diff = compressed_size - current_index; fputc(diff, gif_file); fwrite(img+current_index, diff, 1, gif_file); fputc(0x00, gif_file); current_index += diff; } else { fputc(0xFF, gif_file); fwrite(img+current_index, 0xFF, 1, gif_file); current_index += 0xFF; } }
这个特性不起做用; 浏览器和图片处理应用程序,如 Photoshop 忽略它, GIFLIB 并不试图解释它。
因此直接忽略。
标识 GIF 文件结束,固定值 0x3B。
当解析程序读到 0x3B 时,文件终结。
示例代码:
// GIF 文件结束: 0x3B fputc(0x3B, gif_file);
以上完整代码在 binglingziyu/audio-video-blog-demos 能够获取。
运行代码,生成 GIF 图片:
参考资料: