最近花了點時間用 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)部組成咨察。
上圖中一個有十一塊,其中有八塊是 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 圖過來拳昌,如下圖:
其放大后的效果圖如下:
上圖經(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é)御铃,如下圖:
其中前三個字節(jié)對應(yīng) ASCII 碼中的 G、I沈矿、F 三個字符上真,后三個字節(jié)用于說明此 GIF 的版本號,目前的版本號有 87a 和 89a 兩個羹膳。
對于上面的示例圖來說睡互,前六個字節(jié)分別是 47、49、46就珠、38寇壳、39、61嗓违。 用 0xED 查看如下圖:
Logical Screen Descriptor
Logical Screen Descriptor(邏輯屏幕描述符)通常緊跟在 Header 后面九巡,它的作用是告訴解碼器 GIF 圖像的分辨率,背景色和 Global Color Table 等信息蹂季。先看一下其字節(jié)的組成:
按順序看冕广,
-
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 個顏色。
- 從左邊數(shù)第一位表示
-
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é)格式如下:
在 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)如下圖:
其中前兩個字節(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 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é)組成。
-
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ù)進行壓縮和解壓成肘。