天天看點

Gif 檔案格式分析 —— (轉)

1.概述

~~~~~~~~

GIF(Graphics Interchange Format,圖形交換格式)檔案是由 CompuServe公司開發的圖形檔案格式,版權所有,任何商業目的使用均須 CompuServe公司授權。

GIF圖象是基于顔色清單的(存儲的資料是該點的顔色對應于顔色清單的索引值),最多隻支援8位(256色)。GIF檔案内部分成許多存儲塊,用來存儲多幅圖象或者是決定圖象表現行為的控制塊,用以實作動畫和互動式應用。GIF檔案還通過LZW壓縮算法壓縮圖象資料來減少圖象尺寸(關于LZW算法和GIF資料壓縮>>...)。

2.GIF檔案存儲結構

~~~~~~~~~~~~~~~~~~~

GIF檔案内部是按塊劃分的,包括控制塊( Control Block )和資料塊(Data Sub-blocks)兩種。控制塊是控制資料塊行為的,根據不同的控制塊包含一些不同的控制參數;資料塊隻包含一些8-bit的字元流,由它前面的控制塊來決定它的功能,每個資料塊大小從0到255個位元組,資料塊的第一個位元組指出這個資料塊大小(位元組數),計算資料塊的大小時不包括這個位元組,是以一個空的資料塊有一個位元組,那就是資料塊的大小0x00。下表是一個資料塊的結構:

BYTE     7     6     5     4     3     2     1     0     BIT

塊大小

    Block Size - 塊大小,不包括這個這個位元組(不計算塊大小自身)

1           Data Values - 塊資料,8-bit的字元串

2     

...     

254     

255     

一個GIF檔案的結構可分為檔案頭(File Header)、GIF資料流(GIF Data Stream)和檔案終結器(Trailer)三個部分。檔案頭包含GIF檔案署名(Signature)和版本号(Version);GIF資料流由控制辨別符、圖象塊(Image Block)和其他的一些擴充塊組成;檔案終結器隻有一個值為0x3B的字元(';')表示檔案結束。下表顯示了一個GIF檔案的組成結構:

      GIF署名     檔案頭     

      版本号

      邏輯螢幕辨別符     GIF資料流     

      全局顔色清單     

      ...     

      圖象辨別符     圖象塊                       

      圖象局部顔色清單圖

                      基于顔色清單的圖象資料     

      ...     

      GIF結尾     檔案結尾     

下面就具體介紹各個部分:

檔案頭部分(Header)

~~~~~~~~~~~~~~~~~

GIF署名(Signature)和版本号(Version)

~~~~~~~~~~~~~~~~~~~~~~~~~~~

GIF署名用來确認一個檔案是否是GIF格式的檔案,這一部分由三個字元組成:"GIF";檔案版本号也是由三個位元組組成,可以為"87a"或"89a".具體描述見下表:

BYTE     7     6     5     4     3     2     1     0     BIT

1     'G'     GIF檔案辨別

2     'I'

3     'F'

4     '8'     GIF檔案版本号:87a - 1987年5月

89a - 1989年7月

5     '7'或'9'

6     'a'

GIF資料流部分(GIF Data Stream)

~~~~~~~~~~~~~~~~~~~~~~~~~~~~

邏輯螢幕辨別符(Logical Screen Descriptor)

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

這一部分由7個位元組組成,定義了GIF圖象的大小(Logical Screen Width & Height)、顔色深度(Color Bits)、背景色(Blackground Color Index)以及有無全局顔色清單(Global Color Table)和顔色清單的索引數(Index Count),具體描述見下表:

BYTE     7     6     5     4     3     2     1     0     BIT     

1     邏輯螢幕寬度     像素數,定義GIF圖象的寬度

2

3     邏輯螢幕高度     像素數,定義GIF圖象的高度

4

5     m     cr     s     pixel     具體描述見下...

6     背景色     背景顔色(在全局顔色清單中的索引,如果沒有全局顔色清單,該值沒有意義)

7     像素寬高比     像素寬高比(Pixel Aspect Radio)

m - 全局顔色清單标志(Global Color Table Flag),當置位時表示有全局顔色清單,pixel值有意義.

cr - 顔色深度(Color ResoluTion),cr+1确定圖象的顔色深度.

s - 分類标志(Sort Flag),如果置位表示全局顔色清單分類排列.

pixel - 全局顔色清單大小,pixel+1确定顔色清單的索引數(2的pixel+1次方).

全局顔色清單(Global Color Table)

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

全局顔色清單必須緊跟在邏輯螢幕辨別符後面,每個顔色清單索引條目由三個位元組組成,按R、G、B的順序排列。

BYTE     7     6     5     4     3     2     1     0     BIT

1     索引1的紅色值     

2     索引1的綠色值     

3     索引1的藍色值     

4     索引2的紅色值     

5     索引2的綠色值     

6     索引2的藍色值     

7     ...                       

圖象辨別符(Image Descriptor)

~~~~~~~~~~~~~~~~~~~~~~~~~

一個GIF檔案内可以包含多幅圖象,一幅圖象結束之後緊接着下是一幅圖象的辨別符,圖象辨別符以0x2C(',')字元開始,定義緊接着它的圖象的性質,包括圖象相對于邏輯螢幕邊界的偏移量、圖象大小以及有無局部顔色清單和顔色清單大小,由10個位元組組成:

BYTE     7     6     5     4     3     2     1     0     BIT     

1     0     0     1     0     1     1     0     0     圖象辨別符開始,固定值為','

2     X方向偏移量     必須限定在邏輯螢幕尺寸範圍内

3

4     Y方向偏移量

5

6     圖象寬度

7

8     圖象高度

9

10     m     i     s     r     pixel     m - 局部顔色清單标志(Local Color Table Flag)

                                          置位時辨別緊接在圖象辨別符之後有一個局部顔色清單,供緊跟在它之後的一幅圖象使用;值否時使用全局顔色清單,忽略pixel值。

i - 交織标志(Interlace Flag),置位時圖象資料使用交織方式排列(較長的描述...),否則使用順序排列。

s - 分類标志(Sort Flag),如果置位表示緊跟着的局部顔色清單分類排列.

r - 保留,必須初始化為0.

pixel - 局部顔色清單大小(Size of Local Color Table),pixel+1就為顔色清單的位數

局部顔色清單(Local Color Table)

~~~~~~~~~~~~~~~~~~~~~~~~~~

如果上面的局部顔色清單标志置位的話,則需要在這裡(緊跟在圖象辨別符之後)定義一個局部顔色清單以供緊接着它的圖象使用,注意使用前應線儲存原來的顔色清單,使用結束之後回複原來儲存的全局顔色清單。如果一個GIF檔案即沒有提供全局顔色清單,也沒有提供局部顔色清單,可以自己建立一個顔色清單,或使用系統的顔色清單。局部顔色清單的排列方式和全局顔色清單一樣:RGBRGB......

基于顔色清單的圖象資料(Table-Based Image Data)

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

由兩部分組成:LZW編碼長度(LZW Minimum Code Size)和圖象資料(Image Data)。

BYTE     7     6     5     4     3     2     1     0     BIT

1     LZW編碼長度     LZW編碼初始碼表大小的位數,較長的描述見LZW編碼...

...

    圖象資料,由一個或幾個資料塊(Data Sub-blocks)組成

資料塊

...

GIF圖象資料使用了LZW壓縮算法(詳細介紹請看後面的『LZW算法和GIF資料壓縮』),大大減小了圖象資料的大小。圖象資料在壓縮前有兩種排列格式:連續的和交織的(由圖象辨別符的交織标志控制)。連續方式按從左到右、從上到下的順序排列圖象的光栅資料;交織圖象按下面的方法處理光栅資料:

建立四個通道(pass)儲存資料,每個通道提取不同行的資料:

第一通道(Pass 1)提取從第0行開始每隔8行的資料;

第二通道(Pass 2)提取從第4行開始每隔8行的資料;

第三通道(Pass 3)提取從第2行開始每隔4行的資料;

第四通道(Pass 4)提取從第1行開始每隔2行的資料;

下面的例子示範了提取交織圖象資料的順序:

行      通道1      通道2      通道3      通道4     

0  --------------------------------------------------------     1                       

1 --------------------------------------------------------                       4     

2  --------------------------------------------------------                 3           

3  --------------------------------------------------------                       4     

4  --------------------------------------------------------           2                 

5  --------------------------------------------------------                       4     

6  --------------------------------------------------------                 3           

7  --------------------------------------------------------                       4     

8  --------------------------------------------------------     1                       

9  --------------------------------------------------------                       4     

10 --------------------------------------------------------                 3           

11 --------------------------------------------------------                       4     

12 --------------------------------------------------------           2                 

13 --------------------------------------------------------                       4     

14 --------------------------------------------------------                 3           

15 --------------------------------------------------------                       4     

16 --------------------------------------------------------     1                       

17 --------------------------------------------------------                       4     

18 --------------------------------------------------------                 3           

19 --------------------------------------------------------                       4     

20 --------------------------------------------------------           2                 

圖形控制擴充(Graphic Control Extension)

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

這一部分是可選的(需要89a版本),可以放在一個圖象塊(圖象辨別符)或文本擴充塊的前面,用來控制跟在它後面的第一個圖象(或文本)的渲染(Render)形式,組成結構如下:

BYTE     7     6     5     4     3     2     1     0     BIT

1     擴充塊辨別     Extension Introducer - 辨別這是一個擴充塊,固定值0x21

2     圖形控制擴充标簽     Graphic Control Label - 辨別這是一個圖形控制擴充塊,固定值0xF9

3     塊大小     Block Size - 不包括塊終結器,固定值4

4     保留     處置方法     

i

t

    i - 使用者輸入标志;t - 透明色标志。較長的描述見下...

5     延遲時間     Delay Time - 機關1/100秒,如果值不為1,表示暫停規定的時間後再繼續往下處理資料流

6

7     透明色索引     Transparent Color Index - 透明色索引值

8     塊終結器     Block Terminator - 辨別塊終結,固定值0

處置方法(Disposal Method):指出處置圖形的方法,當值為:

                        0 - 不使用處置方法

                        1 - 不處置圖形,把圖形從目前位置移去

                        2 - 回複到背景色

                        3 - 回複到先前狀态

                      4-7 - 自定義

使用者輸入标志(Use Input Flag):指出是否期待使用者有輸入之後才繼續進行下去,置位表示期待,值否表示不期待。使用者輸入可以是按Enter鍵、滑鼠點選等,可以和延遲時間一起使用,在設定的延遲時間内使用者有輸入則馬上繼續進行,或者沒有輸入直到延遲時間到達而繼續

透明顔色标志(Transparent Color Flag):置位表示使用透明顔色

注釋擴充(Comment Extension)

~~~~~~~~~~~~~~~~~~~~~~~~~~~

這一部分是可選的(需要89a版本),可以用來記錄圖形、版權、描述等任何的非圖形和控制的純文字資料(7-bit ASCII字元),注釋擴充并不影響對圖象資料流的處理,解碼器完全可以忽略它。存放位置可以是資料流的任何地方,最好不要妨礙控制和資料塊,推薦放在資料流的開始或結尾。具體組成:

BYTE     7     6     5     4     3     2     1     0     BIT

1     擴充塊辨別     Extension Introducer - 辨別這是一個擴充塊,固定值0x21

2     注釋塊标簽     Comment Label - 辨別這是一個注釋塊,固定值0xFE

...

    Comment Data - 一個或多個資料塊(Data Sub-Blocks)組成

注釋塊

...

      塊終結器     Block Terminator - 辨別注釋塊結束,固定值0

圖形文本擴充(Plain Text Extension)

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

這一部分是可選的(需要89a版本),用來繪制一個簡單的文本圖象,這一部分由用來繪制的純文字資料(7-bit ASCII字元)和控制繪制的參數等組成。繪制文本借助于一個文本框(Text Grid)來定義邊界,在文本框中劃分多個單元格,每個字元占用一個單元,繪制時按從左到右、從上到下的順序依次進行,直到最後一個字元或者占滿整個文本框(之後的字元将被忽略,是以定義文本框的大小時應該注意到是否可以容納整個文本),繪制文本的顔色索引使用全局顔色清單,沒有則可以使用一個已經儲存的前一個顔色清單。另外,圖形文本擴充塊也屬于圖形塊(Graphic Rendering Block),可以在它前面定義圖形控制擴充對它的表現形式進一步修改。圖形文本擴充的組成:

BYTE     7     6     5     4     3     2     1     0     BIT

1     擴充塊辨別     Extension Introducer - 辨別這是一個擴充塊,固定值0x21

2     圖形控制擴充标簽     Plain Text Label - 辨別這是一個圖形文本擴充塊,固定值0x01

3     塊大小     Block Size - 塊大小,固定值12

4     文本框左邊界位置     Text Glid Left Posotion - 像素值,文本框離邏輯螢幕的左邊界距離

5

6     文本框上邊界位置     Text Glid Top Posotion - 像素值,文本框離邏輯螢幕的上邊界距離

7

8     文本框高度     Text Glid Width -像素值

9

10     文本框高度     Text Glid Height - 像素值

11

12     字元單元格寬度     Character Cell Width - 像素值,單個單元格寬度

13     字元單元格高度     Character Cell Height- 像素值,單個單元格高度

14     文本前景色索引     Text Foreground Color Index - 前景色在全局顔色清單中的索引

15     文本背景色索引     Text Blackground Color Index - 背景色在全局顔色清單中的索引

N     

...

    Plain Text Data - 一個或多個資料塊(Data Sub-Blocks)組成,儲存要在顯示的字元串。

文本資料塊

...

N+1     塊終結     Block Terminator - 辨別注釋塊結束,固定值0

推薦:1.由于文本的字型(Font)和尺寸(Size)沒有定義,解碼器應該根據情況選擇最合适的;

2.如果一個字元的值小于0x20或大于0xF7,則這個字元被推薦顯示為一個空格(0x20);

3.為了相容性,最好定義字元單元格的大小為8x8或8x16(寬度x高度)。

應用程式擴充(Application Extension)

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

這是提供給應用程式自己使用的(需要89a版本),應用程式可以在這裡定義自己的辨別、資訊等,組成:

BYTE     7     6     5     4     3     2     1     0     BIT

1     擴充塊辨別     Extension Introducer - 辨別這是一個擴充塊,固定值0x21

2     圖形控制擴充标簽     Application Extension Label - 辨別這是一個應用程式擴充塊,固定值0xFF

3     塊大小     Block Size - 塊大小,固定值11

4     應用程式辨別符     Application Identifier - 用來鑒别應用程式自身的辨別(8個連續ASCII字元)

5

6

7

8

9

10

11

12     應用程式驗證碼     Application Authentication Code - 應用程式定義的特殊辨別碼(3個連續ASCII字元)

13

14

N     

...

    應用程式自定義資料塊 - 一個或多個資料塊(Data Sub-Blocks)組成,儲存應用程式自己定義的資料

應用程式資料

...

N+1     塊終結器     lock Terminator - 辨別注釋塊結束,固定值0

檔案結尾部分

~~~~~~~~~~~

檔案終結器(Trailer)

~~~~~~~~~~~~~~~~

這一部分隻有一個值為0的位元組,辨別一個GIF檔案結束.

BYTE     7     6     5     4     3     2     1     0     

1     

檔案終結

    GIF Trailer - 辨別GIF檔案結束,固定值0x3B

2.LZW算法和GIF資料壓縮

~~~~~~~~~~~~~~~~~~~~~~~~~~~

GIF檔案的圖象資料使用了可變長度編碼的LZW壓縮算法(Variable-Length_Code LZW Compression),這是從LZW(Lempel Ziv Compression)壓縮算法演變過來的,通過壓縮原始資料的重複部分來達到減少檔案大小的目的。

标準的LZW壓縮原理:

~~~~~~~~~~~~~~~~~~

先來解釋一下幾個基本概念:

LZW壓縮有三個重要的對象:資料流(CharStream)、編碼流(CodeStream)和編譯表(String Table)。在編碼時,資料流是輸入對象(圖象的光栅資料序列),編碼流就是輸出對象(經過壓縮運算的編碼資料);在解碼時,編碼流則是輸入對象,資料流是輸出對象;而編譯表是在編碼和解碼時都須要用借助的對象。

字元(Character):最基礎的資料元素,在文本檔案中就是一個位元組,在光栅資料中就是一個像素的顔色在指定的顔色清單中的索引值;

字元串(String):由幾個連續的字元組成;

字首(Prefix):也是一個字元串,不過通常用在另一個字元的前面,而且它的長度可以為0;

根(Root):單個長度的字元串;

編碼(Code):一個數字,按照固定長度(編碼長度)從編碼流中取出,編譯表的映射值;

圖案:一個字元串,按不定長度從資料流中讀出,映射到編譯表條目.

LZW壓縮的原理:提取原始圖象資料中的不同圖案,基于這些圖案建立一個編譯表,然後用編譯表中的圖案索引來替代原始光栅資料中的相應圖案,減少原始資料大小。看起來和調色闆圖象的實作原理差不多,但是應該注意到的是,我們這裡的編譯表不是事先建立好的,而是根據原始圖象資料動态建立的,解碼時還要從已編碼的資料中還原出原來的編譯表(GIF檔案中是不攜帶編譯表資訊的),為了更好了解編解碼原理,我們來看看具體的處理過程:

編碼器(Compressor)

~~~~~~~~~~~~~~~~

編碼資料,第一步,初始化一個編譯表,假設這個編譯表的大小是12位的,也就是最多有4096個機關,另外假設我們有32個不同的字元(也可以認為圖象的每個像素最多有32種顔色),表示為a,b,c,d,e...,初始化編譯表:第0項為a,第1項為b,第2項為c...一直到第31項,我們把這32項就稱為根。

開始編譯,先定義一個字首對象Current Prefix,記為[.c.],現在它是空的,然後定義一個目前字元串Current String,标記為[.c.]k,[.c.]就為Current Prefix,k就為目前讀取字元。現在來讀取資料流的第一個字元,假如為p,那麼Current String就等于[.c.]p(由于[.c.]為空,實際上值就等于p),現在在編譯表中查找有沒有Current String的值,由于p就是一個根字元,我們已經初始了32個根索引,當然可以找到,把p設為Current Prefix的值,不做任何事繼續讀取下一個字元,假設為q,Current String就等于[.c.]q(也就是pq),看看在編譯表中有沒有該值,當然。沒有,這時我們要做下面的事情:将Current String的值(也就是pq)添加到編譯表的第32項,把Current Prefix的值(也就是p)在編譯表中的索引輸出到編碼流,修改Current Prefix為目前讀取的字元(也就是q)。繼續往下讀,如果在編譯表中可以查找到Current String的值([.c.]k),則把Current String的值([.c.]k)賦予Current Prefix;如果查找不到,則添加Current String的值([.c.]k)到編譯表,把Current Prefix的值([.c.])在編譯表中所對應的索引輸出到編碼流,同時修改Current Prefix為k ,這樣一直循環下去直到資料流結束。僞代碼看起來就像下面這樣:

編碼器僞代碼

        Initialize String Table;

        [.c.] = Empty;

        [.c.]k = First Character in CharStream;

        while ([.c.]k != EOF )

        {

        if ( [.c.]k is in the StringTable)

        {

        [.c.] = [.c.]k;

        }

        else

        {

        add [.c.]k to the StringTable;

        Output the Index of [.c.] in the StringTable to the CodeStream;

        [.c.] = k;

        }

        [.c.]k = Next Character in CharStream;

        }

        Output the Index of [.c.] in the StringTable to the CodeStream;

來看一個具體的例子,我們有一個字母表a,b,c,d.有一個輸入的字元流abacaba。現在來初始化編譯表:#0=a,#1=b,#2=c,#3=d.現在開始讀取第一個字元a,[.c.]a=a,可以在在編譯表中找到,修改[.c.]=a;不做任何事繼續讀取第二個字元b,[.c.]b=ab,在編譯表中不能找,那麼添加[.c.]b到編譯表:#4=ab,同時輸出[.c.](也就是a)的索引#0到編碼流,修改[.c.]=b;讀下一個字元a,[.c.]a=ba,在編譯表中不能找到:添加編譯表#5=ba,輸出[.c.]的索引#1到編碼流,修改[.c.]=a;讀下一個字元c,[.c.]c=ac,在編譯表中不能找到:添加編譯表#6=ac,輸出[.c.]的索引#0到編碼流,修改[.c.]=c;讀下一個字元a,[.c.]c=ca,在編譯表中不能找到:添加編譯表#7=ca,輸出[.c.]的索引#2到編碼流,修改[.c.]=a;讀下一個字元b,[.c.]b=ab,編譯表的#4=ab,修改[.c.]=ab;讀取最後一個字元a,[.c.]a=aba,在編譯表中不能找到:添加編譯表#8=aba,輸出[.c.]的索引#4到編碼流,修改[.c.]=a;好了,現在沒有資料了,輸出[.c.]的值a的索引#0到編碼流,這樣最後的輸出結果就是:#0#1#0#2#4#0.

解碼器(Decompressor)

~~~~~~~~~~~~~~~~~~

好了,現在來看看解碼資料。資料的解碼,其實就是資料編碼的逆向過程,要從已經編譯的資料(編碼流)中找出編譯表,然後對照編譯表還原圖象的光栅資料。

首先,還是要初始化編譯表。GIF檔案的圖象資料的第一個位元組存儲的就是LZW編碼的編碼大小(一般等于圖象的位數),根據編碼大小,初始化編譯表的根條目(從0到2的編碼大小次方),然後定義一個目前編碼Current Code,記作[code],定義一個Old Code,記作[old]。讀取第一個編碼到[code],這是一個根編碼,在編譯表中可以找到,把該編碼所對應的字元輸出到資料流,[old]=[code];讀取下一個編碼到[code],這就有兩種情況:在編譯表中有或沒有該編碼,我們先來看第一種情況:先輸出目前編碼[code]所對應的字元串到資料流,然後把[old]所對應的字元(串)當成字首prefix [...],目前編碼[code]所對應的字元串的第一個字元當成k,組合起來目前字元串Current String就為[...]k,把[...]k添加到編譯表,修改[old]=[code],讀下一個編碼;我們來看看在編譯表中找不到該編碼的情況,回想一下編碼情況:如果資料流中有一個p[...]p[...]pq這樣的字元串,p[...]在編譯表中而p[...]p不在,編譯器将輸出p[...]的索引而添加p[...]p到編譯表,下一個字元串p[...]p就可以在編譯表中找到了,而p[...]pq不在編譯表中,同樣将輸出p[...]p的索引值而添加p[...]pq到編譯表,這樣看來,解碼器總比編碼器『慢一步』,當我們遇到p[...]p所對應的索引時,我們不知到該索引對應的字元串(在解碼器的編譯表中還沒有該索引,事實上,這個索引将在下一步添加),這時需要用猜測法:現在假設上面的p[...]所對應的索引值是#58,那麼上面的字元串經過編譯之後是#58#59,我們在解碼器中讀到#59時,編譯表的最大索引隻有#58,#59所對應的字元串就等于#58所對應的字元串(也就是p[...])加上這個字元串的第一個字元(也就是p),也就是p[...]p。事實上,這種猜測法是很準确(有點不好了解,仔細想一想吧)。上面的解碼過程用僞代碼表示就像下面這樣:

解碼器僞代碼

    Initialize String Table;

    [code] = First Code in the CodeStream;

    Output the String for [code] to the CharStream;

    [old] = [code];

    [code] = Next Code in the CodeStream;

    while ([code] != EOF )

    {

    if ( [code] is in the StringTable)

    {

    Output the String for [code] to the CharStream; // 輸出[code]所對應的字元串

    [...] = translation for [old]; // [old]所對應的字元串

    k = first character of translation for [code]; // [code]所對應的字元串的第一個字元

    add [...]k to the StringTable;

    [old] = [code];

    }

    else

    {

    [...] = translation for [old];

    k = first character of [...];

    Output [...]k to CharStream;

    add [...]k to the StringTable;

    [old] = [code];

    }

    [code] = Next Code in the CodeStream;

    }

GIF資料壓縮

~~~~~~~~~~~

下面是GIF檔案的圖象資料結構:

BYTE     7     6     5     4     3     2     1     0     BIT

1     

編碼長度

    LZW Code Size - LZW壓縮的編碼長度,也就是要壓縮的資料的位數

      ...     資料塊

      塊大小     資料塊,如果需要可重複多次

      編碼資料

      ...     資料塊

      塊終結器     一個圖象的資料編碼結束,固定值0

把光栅資料序列(資料流)壓縮成GIF檔案的圖象資料(字元流)可以按下面的步驟進行:

1.定義編碼長度

GIF圖象資料的第一個位元組就是編碼長度(Code Size),這個值是指要表現一個像素所需要的最小位數,通常就等于圖象的色深;

2.壓縮資料

通過LZW壓縮算法将圖象的光栅資料流壓縮成GIF的編碼資料流。這裡使用的LZW壓縮算法是從标準的LZW壓縮算法演變過來的,它們之間有如下的差别:

[1]GIF檔案定義了一個編碼大小(Clear Code),這個值等于2的『編碼長度』次方,在從新開始一個編譯表(編譯表溢出)時均須輸出該值,解碼器遇到該值時意味着要從新初始化一個編譯表;

[2]在一個圖象的編碼資料結束之前(也就是在塊終結器的前面),需要輸出一個Clear Code+1的值,解碼器在遇到該值時就意味着GIF檔案的一個圖象資料流的結束;

[3]第一個可用到的編譯表索引值是Clear Code+2(從0到Clear Code-1是根索引,再上去兩個不可使用,新的索引從Clare Code+2開始添加);

[4]GIF輸出的編碼流是不定長的,每個編碼的大小從Code Size + 1位到12位,編碼的最大值就是4095(編譯表需要定義的索引數就是4096),當編碼所須的位數超過目前的位數時就把目前位數加1,這就需要在編碼或解碼時注意到編碼長度的改變。

3.編譯成位元組序列

因為GIF輸出的編碼流是不定長的,這就需要把它們編譯成固定的8-bit長度的字元流,編譯順序是從右往左。下面是一個具體例子:編譯5位長度編碼到8位字元

0     b     b     b     a     a     a     a     a

1     d     c     c     c     c     c     b     b

2     e     e     e     e     d     d     d     d

3     g     g     f     f     f     f     f     e

4     h     h     h     h     h     g     g     g

      ...

N                                               

4.打包

前面講過,一個GIF的資料塊的大小從0到255個位元組,第一個位元組是這個資料塊的大小(位元組數),這就需要将編譯編後的碼資料打包成一個或幾個大小不大于255個位元組的資料包。然後寫入圖象資料塊中。

---------------------  

作者:xlvector  

來源:CSDN  

原文:https://blog.csdn.net/xlvector/article/details/589214  

版權聲明:本文為部落客原創文章,轉載請附上博文連結!

繼續閱讀