音視頻入門-18-手動生成一張GIF圖片

音視頻入門文章目錄 html

GIF 編碼知識

GIF File Format

GIF 包含的數據塊:git

  • 文件頭(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 動畫。 github

example-rainbow.gif

文件頭(Header)

GIF 的前 6 個字節內容是 GIF 的署名和版本號。有兩個版本 GIF87a GIF89aGIF89a 版本纔有多幀動畫,全部這裏使用 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文件格式詳解 咱們知道:app

邏輯屏幕標識符(7 個字節):ide

  • 屏幕邏輯寬度:2 字節;
  • 屏幕邏輯高度:2 字節;
  • 打包值,大小爲 1 字節post

    • 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 順序排列。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

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

相關文章
相關標籤/搜索