音视频入门-18-手动生成一张GIF图片

摘要:
实例code://GIF文件头,6字节内容是GIF的签名和版本号uint8_tgif_header[]={0x47,0x49,0x46,0x38,0x39,0x61};fwrite;逻辑屏幕标识符从前面的音频和视频介绍-17-GIF文件格式中,我们知道:逻辑屏幕标识符:屏幕逻辑宽度:2字节;屏幕逻辑高度:2字节;封装值,大小为1字节m-全局颜色表标志,1位;Cr色深,3bit;S分类标记,1位;像素-全局颜色列表大小,3位;背景色索引:1字节;像素纵横比:1字节;实例code://Logical屏幕标识符uint16_ tgif_ width=700;uint16_ tgif_高度=700;//0xF2=11110010uint8_ tgif_逻辑_屏幕_包_字节=0xF2;uint8_ tgif_ bg_颜色_索引=0;uint8_ tgif_ pixel_ aspect=0;fputc;//widthlow8fputc;//宽度高8fputc;//高度低8fputc;//高度8fputc;fputc;fputc;全局颜色表的每个颜色索引由三个字节组成,并按RGB顺序排列。

* 音视频入门文章目录 *

GIF 编码知识

GIF File Format

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 动画。

example-rainbow.gif

文件头(Header)

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

示例代码:

// GIF 文件头,6 个字节内容是 GIF 的署名和版本号
uint8_t gif_header[] = {0x47, 0x49, 0x46, 0x38, 0x39, 0x61};
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)

示例代码:

// 逻辑屏幕标识符
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);

全局颜色表(Global Color Table)

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

由 【逻辑屏幕标识符】可知,颜色的索引数(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

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);

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

示例代码:

// 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);

图形控制扩展(Graphic Control Extension)

我们的 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);

图像标识符(Image Descriptor)

我们的 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);

局部颜色表(Local Color Table)

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

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

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

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

1.生成索引数据

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

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

每个颜色索引 1 字节;

示例代码:

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

2.LZW 压缩数据

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

//  GIF 一帧图像的数据压缩后大小
unsigned long compressed_size;
// GIF 一帧图像的数据解压后的数据
unsigned char *img;
lzw_compress_gif(
        3,
        700*700,
        gif_one_frame_raw,
        &compressed_size,
        &img
);

3.按照格式写入文件

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

后面的是图像数据块:

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

gif-block-image-data-hex.jpg

示例代码:

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;
    }
}

Plain Text Extension

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

所以直接忽略。

文件结尾(Trailer)

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

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

示例代码:

// GIF 文件结束: 0x3B
fputc(0x3B, gif_file);
查看 GIF

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

运行代码,生成 GIF 图片:

example-rainbow.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图片》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇基于时间系统的状态机VM虚拟机网络设置下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Android 播放Gif 动画

在Android 中是不支持直接使用Gif 图片关联播放帧动画,如下动画在Android 中是无法播放的: Android 提供了另外一种解决的办法,就是使用AnimationDrawable 这一函数使其支持逐帧播放,但是如何把gif 图片打散开来,成为每一帧的图片呢?下面介绍两种比较不错的软件,可以帮我们打散图片。 gifsplitter2.0...

IE下 GIF不动失效的奇葩问题

IE下(IE6~IE9都有该问题),对页面进行了某些操作之后,页面上的GIF动画就停留在某一帧不动了~~~ !! 我大IE 就是这么奇葩。 搜索了一下,搞了好久总算搞定。 下面说下目前了解的所有的可能情况 1. 首先是页面上进行的a标签的点击操作,若在a标签的 href属性写成 <a href="javascript:;">点我将导致GIF在I...

flash导出正常清晰(无色差)GIF图片

前言: 这枚GIF算是半临摹作品。我使用的FLASH制作这个小动画,其实这类型的动画用AE做会更便捷。 进入主题前,先摆出个成品 教程结束,以上就是flash制作出来的小动画怎么导出正常清晰(无色差)GIF图片方法介绍,大家学会了吗?希望这篇教程能对大家有所帮助!...

切图时图片的选择:JPG、PNG、GIF的区别

目前网站图片的采用一共有流行三种,分别是JPG、PNG、GIF,然而很多人并不知道三者在选择的时候究竟应该选谁。虽然都可以存储图片,但是如果要发布到网上,就必须考虑速度、大小和失真程度的问题。如果你运用得好,选对图片,那样便会使网站的整体体验上升,如果你运用得不好,就会引起反效果。下面我通过文件大小等多方面的元素来讲一下网站图片的合理选择。 PNG、GIF...

GIF/PNG/JPG和WEBP/base64/apng图片优点和缺点整理

  GIF/PNG/JPG/WEBP/APNG都是属于位图(位图 ,务必区别于矢量图);   GIF/PNG和JPG这三种格式的图片被广泛应用在现今的互联网中,gif曾在过去互联网初期慢速的情况下几乎是做到了大一统的地位,而现如今随着互联网技术应用和硬件条件的提高,png和jpg格式的图片越来越多的被应用,gif昔日的辉煌一去不复, webp图片格式现在还...

(转)了解一下,各种图片格式的区别

  在开发过程中,经常涉及到要用到图片,但是图片有很多不同的格式,他们之间有什么区别呢,我们在使用的时候又该如何选择呢?本文介绍和比较几种常见图片文件格式的优缺点,并介绍不同的文件格式对应用程序性能的影响。 有损vs无损 图片文件格式有可能会对图片的文件大小进行不同程度的压缩,图片的压缩分为有损压缩和无损压缩两种。 有损压缩:指在压缩文件大小的过程中,损...