音視頻入門-17-GIF文件格式詳解

音視頻入門文章目錄 html

GIF 文件格式解析

圖像互換格式主要分爲兩個版本,即圖像互換格式 87a 和圖像互換格式 89a。
圖像互換格式 87a:是在 1987 年制定的版本。
圖像互換格式 89a:是在 1989 年制定的版本。在這個版本中,爲圖像互換格式文檔擴充了圖形控制區塊、備註、說明、應用程序接口等四個區塊,並提供了對透明色和多幀動畫的支持。git

如今咱們通常所說的 GIF 動畫都是指 89a 的格式。github

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

用來分析的樣例圖片

用來分析的樣例圖片瀏覽器

十六進制編輯器app

(1) 文件頭(Header)

GIF 的前 6 個字節內容是 GIF 的署名和版本號。
咱們能夠經過前 3 個字節判斷文件是否爲 GIF 格式,後 3 個字節判斷 GIF 格式的版本:編輯器

  • 87a:是在 1987 年制定的版本
  • 89a:是在 1989 年制定的版本

文件頭(Header)

(2) 邏輯屏幕標識符(Logical Screen Descriptor)

邏輯屏幕標識符配置了 GIF 一些全局屬性,咱們經過讀取解析它,獲取 GIF 全局的一些配置。ide

邏輯屏幕標識符(Logical Screen Descriptor)

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

  • 屏幕邏輯寬度:定義了 GIF 圖像的像素寬度,大小爲 2 字節;
  • 屏幕邏輯高度:定義了 GIF 圖像的像素高度,大小爲 2 字節;
  • 打包值,大小爲 1 字節oop

    • 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

內容有誤?聯繫做者:

聯繫做者

相關文章
相關標籤/搜索