GIF 字節(jié)格式介紹

gif

最近花了點時間用 C++ 寫了一個 GIF 圖片的解析程序,在這一過程中我找了許多中文相關(guān)的材料于游,但沒有哪一篇是真正能夠讓讀者完全理解 GIF 的文件格式和 LZW 在 GIF 中的應(yīng)用(解析部分)鉴象。在查閱了一些官方文檔后我算是順利的將程序完成了,順道我就把 GIF 文件的解析在這兒講講清除阎肝,方便大家學習甩骏。

下面這兩個網(wǎng)頁是我參考的比較權(quán)威的資料窗市,大家也可以直接閱讀。

http://giflib.sourceforge.net/index.html
https://www.w3.org/Graphics/GIF/spec-gif89a.txt

GIF 文件格式

下圖是我從 http://giflib.sourceforge.net/index.html 拿過來的圖饮笛,從圖上可以很清晰的看到 GIF 文件的內(nèi)部組成咨察。

image

上圖中一個有十一塊,其中有八塊是 GIF 圖像必備的:Header(頭)福青、Logical Screen Descriptor(邏輯屏幕描述符)扎拣、Image Descriptor(圖像描述符)、Image Data(圖像數(shù)據(jù)流)素跺、Plain Text Extension(文本擴展)、Application Extension(應(yīng)用擴展)誉券、Comment Extension(注釋擴展)指厌、Trailer(尾部標記)。

另外還有三個可選塊:Global Color Table(全局顏色表)踊跟、Graphic Control Extension(圖形控制擴展)踩验、Local Color Table(本地顏色表)。

傳統(tǒng)的 Gif 解碼器正是通過上述的這些塊來對 GIF 文件進行解析商玫,下面我們就按照順序來詳細了解一下這些塊的內(nèi)部字節(jié)格式箕憾。

為了比較直觀的了解這些內(nèi)容,我還是從 http://giflib.sourceforge.net/index.html 拿了一個 GIF 圖過來拳昌,如下圖:

image

其放大后的效果圖如下:

image

上圖經(jīng)過放大處理袭异,其代表了一個 10*10 個像素的 GIF 圖,其內(nèi)部字節(jié)如下:

47 49 46 38 39 61 
0A 00 0A 00 91 00 00 
FF FF FF FF 00 00 00 00 FF 00 00 00 
21 F9 04 00 00 00 00 00 
2C 00 00 00 00 0A 00 0A 00 00 
02 16 8C 2D 99 87 2A 1C DC 33 A0 02 75 EC 95 FA A8 DE 60 8C 04 91 4C 01 00
3B

Header

GIF 文件由它的 Header 塊最為其文件的入口炬藤,Header 一共包含六個字節(jié)御铃,如下圖:

image

其中前三個字節(jié)對應(yīng) ASCII 碼中的 G、I沈矿、F 三個字符上真,后三個字節(jié)用于說明此 GIF 的版本號,目前的版本號有 87a 和 89a 兩個羹膳。

對于上面的示例圖來說睡互,前六個字節(jié)分別是 47、49、46就珠、38寇壳、39、61嗓违。 用 0xED 查看如下圖:

image

Logical Screen Descriptor

Logical Screen Descriptor(邏輯屏幕描述符)通常緊跟在 Header 后面九巡,它的作用是告訴解碼器 GIF 圖像的分辨率,背景色和 Global Color Table 等信息蹂季。先看一下其字節(jié)的組成:

image

按順序看冕广,

  • Canvas Width:表示 GIF 圖像的寬度,單位是像素偿洁。
  • Canvas Height:表示 GIF 圖像的高度撒汉,單位是像素。
  • Packed Field:這是一個包裝字段涕滋,內(nèi)部的不同 bit(位)表示有不同的含義
    • 從左邊數(shù)第一位表示 Global Color Table Flag睬辐,如果其為 1 ,則表示存在 Global Color Table宾肺。如果為 0溯饵,則沒有 Global Color Table。
    • 從左邊數(shù)第二锨用、三丰刊、四位表示 Color Resolution,用于表示色彩分辨率增拥,如果為 s啄巧,則 Global Color Table 的顏色數(shù)為 2^(s+1)個,如果這是 s = 1,則一共有 4 中顏色掌栅,即每個像素可以用 2位(二進制) 來表示秩仆。
    • 從左邊數(shù)第五位表示 Sort Flag,它有兩個值 0 或 1猾封。如果為 0 則 Global Color Table 不進行排序澄耍,為 1 則表示 Global Color Table 按照降序排列,出現(xiàn)頻率最多的顏色排在最前面晌缘。
    • 最右邊三位表示 Global Color Table 的顏色數(shù)逾苫,如其值為 s,則全局列表顏色個數(shù)的計算公式為 2^(s+1)枚钓。如 s = 1铅搓,則 Global Color Table 包含 4 個顏色。
  • Background Color Index:表示 GIF 的背景色在 Global Color Table 中的索引搀捷。
  • Pixel Aspect Ratio:表示用于計算原始圖像中像素寬高比的近似因子星掰,一般情況為 0多望,顧不做深入了解。

對于我們是示例圖氢烘,其 Logical Screen Descriptor 對應(yīng)的字節(jié)如下:

0A 00 0A 00 91 00 00

其中:
Canvas Width = 0A00 = 10(十進制)
Canvas Height = 0A00 = 10(十進制)
Packed Field = 10010001(二進制)怀偷,其中 Global Color Table 為 1,則存在 Global Color Table播玖。Color Resolution 為 1椎工,表示三原色分別用 2 位來表示。Sort Flag = 0蜀踏,不排序维蒙。Global Color Table 中顏色數(shù)為 4 。
Background Color Index = 0果覆,說明此 GIF 的背景色為 Global Color Table 中第一個顏色颅痊。
Pixel Aspect Ratio = 0,可忽略。

Global Color Table

Global Color Table 如果有的話就會跟在 Logical Screen Descriptor 塊后面局待。其塊中的字節(jié)格式如下:

image

在 Global Color Table 中每個字節(jié)僅代表一種顏色斑响,所以 Global Color Table 的字節(jié)數(shù) = 顏色數(shù) * 3.在 Logical Screen Descriptor 中我們知道示例中包含 4 中顏色,即 Global Color Table 的字節(jié)數(shù)為 12 钳榨。所以讀取接下的 12 個字節(jié)舰罚。其具體字節(jié)如下:

FF FF FF FF 00 00 00 00 FF 00 00 00 

根據(jù)上面的數(shù)據(jù)我們來構(gòu)建 Global Color Table

索引 字節(jié)組合 顏色
0 FFFFFF 白色
1 FF0000 紅色
2 0000FF 藍色
3 000000 黑色

Graphics Control Extension

這里先不說 Graphics Control Extension,我們先看 Global Color Table 后面緊跟的那個字節(jié)薛耻,從示例中可以看到的21营罢,21在 GIF 格式中是有特殊意義的,它表示 Extension Introducer(擴展入口)昭卓,即接下來的一段數(shù)據(jù)為最開始提到的這幾個擴展中的某一個擴展。

OK瘟滨,那我們接著 21 往后看候醒,下一個字節(jié)為F9,F(xiàn)9 也是有特殊含義的杂瘸,表示這是一個 Graphics Control Extension倒淫。

Graphics Control Extension 算上 21 和 F9 一共有八個字節(jié),其結(jié)構(gòu)如下圖:

image

其中前兩個字節(jié)上面已經(jīng)提到败玉,看接下來的幾個字節(jié)分別表示什么含義:

  • Byte Size:表示接下來的有效數(shù)據(jù)字節(jié)數(shù)敌土。
  • Packed Field:是一個包裝字段,內(nèi)部不同位的意義也不同运翼。
    • 從左邊數(shù)一返干,二,三位表示Reserved for Future Use血淌,即保留位,暫無用處。
    • 從左邊數(shù)四,五,六位表示 Display Method捧弃,表示在進行逐幀渲染時嘴办,前一幀留下的圖像作何處理:0:不做任何處理眼五。1:保留前一幀圖像诵姜,在此基礎(chǔ)上進行渲染。2:渲染前將圖像置為背景色瞎惫。3:將被下一幀覆蓋的圖像重置。
    • 從右數(shù)第二位表示 User Input Flag黍檩,表示是否需要在得到用戶的輸入時才進行下一幀的輸入(具體用戶輸入指什么視應(yīng)用而定)。0 表示無需用戶輸入刽酱。1 表示需要用戶輸入喳逛。
    • 最右邊一位,表示 Transparent Flag棵里,當該值為 1 時润文,后面的 Transparent Color Index 指定的顏色將被當做透明色處理。為 0 則不做處理殿怜。
  • Delay Time:表示 GIF 動圖每一幀之間的間隔典蝌,單位為百分之一秒。當為 0 時間隔由解碼器管理头谜。
  • Transparent Color Index:當 Transparent Flag 為 1 時骏掀,此字節(jié)有效,表示此索引在 Global Color Table 中對應(yīng)的顏色將被當做透明色做處理柱告。
  • Block Terminator:表示 Extension 到此結(jié)束截驮。

下面看一下示例中 Graphics Control Extension 對應(yīng)的字節(jié):

21 F9 04 00 00 00 00 00

其中 21,F(xiàn)9 表示這是一個 Graphics Control Extension 塊际度。
Byte Size 為 4葵袭。
其它值都為 0 ,概括來講此 Graphics Control Extension 對應(yīng)下一幀的渲染無需任何處理甲脏,也不需要用戶輸入眶熬,也沒有需要做透明處理的顏色值妹笆。渲染器要做的就是直接把下一幀圖像渲染在畫布上即可块请。

Image Descriptor

上面講到 21 上 Extension 的標識符。這里的 Image Descriptor 也有自己的標識符拳缠,為 2C墩新。下面看一下 Image Descriptor 內(nèi)部字節(jié)結(jié)構(gòu):

image

其中 Image Seperator 為固定值 2C。

  • Image Left:該值表示下一幀圖像渲染位置離畫布左邊的距離(從 0 開始)窟坐。
  • Image Top:該值表示下一幀圖像渲染位置離畫布上邊的距離(從 0 開始)海渊。
  • Image Width:該值表示下一幀圖像的寬度绵疲。
  • Image Height:該值表示下一幀圖像的高度。
  • Packed Field:這是一個包裝字段臣疑,內(nèi)部不同位的意義也不同盔憨。
    • 從左數(shù)第一位:Local Color Table Flag,表示下一幀圖像是否需要一個獨立的顏色表讯沈。1 為需要郁岩,0 為不需要。
    • 從左數(shù)第二位:Interlace Flag缺狠,表示是否需要隔行掃描问慎。1 為需要,0 為不需要挤茄。
    • 從左數(shù)第三位:Sort Flag如叼,如果需要 Local Color Table 的話,這個字段表示其排列順序穷劈,同 Global Color Table笼恰。
    • 從左數(shù)第四、五位:Reserved For Future Use囚衔,保留位挖腰。
    • 從左數(shù)最后三位:Size of Local Color Table,同 Global Color Table 中的該位练湿。如需要本地顏色表猴仑,則該數(shù)有效。

接著看一下示例中的對應(yīng)字節(jié):

2C 00 00 00 00 0A 00 0A 00 00 

2C 表示 Image Descriptor
Image Left = 0
Image Top = 0
Image Width = 10
Image Height = 10
上面這四個數(shù)字表示即將渲染的一幀大小為 10*10 像素肥哎,正好與 GIF 圖的分辨率一致辽俗。
打包字段都為零,表示下一幀不需要 Local Color Table,也不需要進行隔行掃描竟终。

這里讀者可能會好奇我們不是在 Logical Screen Descriptor 中知道了圖像的分辨率嗎评腺,為什么還要在 Image Descriptor 中額外指定圖像的寬和高。其實 GIF 在進行編碼的時候并不一定對每一幀進行全尺寸的壓縮朱浴。因為有時候一個 GIF 圖只有中間區(qū)域是動的,四周都是靜止的达椰,那只需要對中間那部分進行壓縮編碼即可翰蠢。所以這里的 Image Left、Image Top啰劲、Image Width 和 Image Height正好可以指定一個小于等于 GIF 分辨率的圖像梁沧。

Local Color Table

local color table 在本示例中不涉及,我也不多介紹蝇裤,在處理的時候按照 Global Color Table 處理即可廷支。

Image Data

如果存在 Local Color Table频鉴,Image Data 就緊跟其后。如若不存在恋拍,則緊跟在 Image Descriptor 后垛孔。下面先看一下 Image Data 的內(nèi)部字節(jié)組成。

image
  • LZW Minimum Code Size: GIF 在對每一幀的像素顏色在 Color Table 所對應(yīng)的索引進行 LZW 壓縮施敢,這里的 LZW Minimum Code Size 就是 LZW 壓縮中很關(guān)鍵的一個值似炎,不過目前這個值先放著,等后面講到對 LZW 解壓縮時再講悯姊。
  • Number of bytes of data in sun-blocks(01-FF):這個值表示在其后面的有效字節(jié)的個數(shù)羡藐。范圍為 01-FF,當其值為 0悯许,則表示 Image Data 到此為止仆嗦,后面就是其他塊的數(shù)據(jù)了。這里需要注意由于其最大值為 FF先壕,但圖像的像素個數(shù)可能會大于這個值瘩扼,所以從圖上也能知道這個 Data sub-Blocks是有可能接連出現(xiàn)很多個的。
  • Data Sub-Block(s):表示有效的字節(jié)塊垃僚。
  • Block Terminator:表示 Image Data 的結(jié)束部分集绰。

接著看一下示例中對應(yīng)的字節(jié):

02 16 8C 2D 99 87 2A 1C DC 33 A0 02 75 EC 95 FA A8 DE 60 8C 04 91 4C 01 00

02 表示 LZW Minimum Code Size。
16(十六進制) 表示后面緊跟著的 22 個字節(jié)用來表示下一幀的圖像數(shù)據(jù)谆棺。
00 表示 Image Data 到此為止栽燕。

Trailer

從上面的示例看,最后還剩下一個字節(jié) 3B改淑,這個在 GIF 中也有特殊含義碍岔,是尾部標記的意思,GIF 的字節(jié)內(nèi)容到此就結(jié)束了朵夏。

Plain Text Extension蔼啦、Application Extension和Comment Extension

最后還剩下上面三個 Extension,他們主要是為 GIF 提供一些額外的信息仰猖,本身的信息對實際的渲染沒有多少影響捏肢。所以這里我也不多介紹,想深入了解的可以閱讀一開始提到的兩個網(wǎng)址饥侵。

總結(jié)

本文主要以 GIF 內(nèi)部字節(jié)的格式作為出發(fā)點鸵赫,簡單介紹了十一種塊。只有充分理解了各個塊內(nèi)部的含義爆捞,才能為其編寫正確的解碼器奉瘤。但僅僅只了解各個塊還是不夠的勾拉,GIF 的圖像數(shù)據(jù)采用的是 LZW 算法進行壓縮煮甥,所以還需要對 LZW 有較深理解盗温。在下一篇《GIF 與 LWZ》中我將結(jié)合本文中的示例圖像,詳細講解如何通過 LWZ 對 GIF 的圖像數(shù)據(jù)進行壓縮和解壓成肘。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末卖局,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子双霍,更是在濱河造成了極大的恐慌砚偶,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件洒闸,死亡現(xiàn)場離奇詭異染坯,居然都是意外死亡,警方通過查閱死者的電腦和手機丘逸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進店門单鹿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人深纲,你說我怎么就攤上這事仲锄。” “怎么了湃鹊?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵儒喊,是天一觀的道長。 經(jīng)常有香客問我币呵,道長怀愧,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任余赢,我火速辦了婚禮掸驱,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘没佑。我一直安慰自己毕贼,他們只是感情好,可當我...
    茶點故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布蛤奢。 她就那樣靜靜地躺著鬼癣,像睡著了一般。 火紅的嫁衣襯著肌膚如雪啤贩。 梳的紋絲不亂的頭發(fā)上待秃,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天,我揣著相機與錄音痹屹,去河邊找鬼章郁。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的暖庄。 我是一名探鬼主播聊替,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼培廓!你這毒婦竟也來了惹悄?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤肩钠,失蹤者是張志新(化名)和其女友劉穎泣港,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體价匠,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡当纱,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了踩窖。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片惫东。...
    茶點故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖毙石,靈堂內(nèi)的尸體忽然破棺而出廉沮,到底是詐尸還是另有隱情,我是刑警寧澤徐矩,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布滞时,位于F島的核電站,受9級特大地震影響滤灯,放射性物質(zhì)發(fā)生泄漏坪稽。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一鳞骤、第九天 我趴在偏房一處隱蔽的房頂上張望窒百。 院中可真熱鬧,春花似錦豫尽、人聲如沸篙梢。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽渤滞。三九已至,卻和暖如春榴嗅,著一層夾襖步出監(jiān)牢的瞬間妄呕,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工嗽测, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留绪励,地道東北人。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像疏魏,于是被迫代替她去往敵國和親停做。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,685評論 2 360

推薦閱讀更多精彩內(nèi)容