一乐电子

一乐电子百科

 找回密码
 请使用微信账号登录和注册会员

QQ登录

只需一步,快速开始

快捷登录

手机号码,快捷登录

搜索
查看: 7126|回复: 10
收起左侧

GIF格式

[复制链接]
发表于 2009-3-14 15:28 | 显示全部楼层 |阅读模式
这段时间一值研究GIF的格式想把GIF格式用在FPGA上不知可不可行!以下是在网上找到的一些GIF的资料8 K: d) b( U" ]% }1 @9 ]( u
各位给些提议吧!谢谢5 @  g$ i* D4 C* k/ \+ ^9 h6 {

3 L2 ]8 W  A. O' y3 D/ h  _3 y
9 x& l& Y! x* }6 n
+ R$ k! t  a/ \$ n8 G
; n9 P8 O7 A- s0 \
1 e" F& l! E% U: q- H% @4 \2 e& A+ U
/ q5 f/ P7 D! g9 t0 Q. d

! t. ], x: n! M) B) x) L1 c( R$ d
! @6 `+ ?- u3 I. w+ {4 l# x9 t& K1 I" R6 N: ]5 _$ x
GIF文件格式
! U7 Q2 y1 B6 c; z0 Z$ `! c5 d7 w9 X2 J2 P7 q; V
6.2 GIF文件格式3 g  `' I" j& i2 Z/ {
6.2.1 简介2 V8 F5 B  R1 w
" o4 E, b# s9 V* y0 e
GIF(Graphics Interchange Format)是CompuServe公司开发的图像文件存储格式,1987年开发的GIF文件格式版本号是GIF87a,1989年进行了扩充,扩充后的版本号定义为GIF89a。
, _. \, n2 m0 L' X
% N2 \' \4 R: s1 TGIF图像文件以数据块(block)为单位来存储图像的相关信息。一个GIF文件由表示图形/图像的数据块、数据子块以及显示图形/图像的控制信息块组成,称为GIF数据流(Data Stream)。数据流中的所有控制信息块和数据块都必须在文件头(Header)和文件结束块(Trailer)之间。0 P$ p: A: ~3 D/ Y
GIF文件格式采用了LZW(Lempel-Ziv Walch)压缩算法来存储图像数据,定义了允许用户为图像设置背景的透明(transparency)属性。此外,GIF文件格式可在一个文件中存放多幅彩色图形/图像。如果在GIF文件中存放有多幅图,它们可以像演幻灯片那样显示或者像动画那样演示。
# t3 H: x0 N& Z1 [- d8 d
3 T  G! [* F5 ~# ^" R# g! o: ^6.2.2. 文件结构! R, N) p8 h( k1 n+ [. R" U

( S- W5 T" r) B- Q+ o! t( O& TGIF文件结构的典型结构如图6-01所示。为下文说明方便,在构件左边加了编号。
8 Y- p6 y" y& d& X8 phttp://www.qqread.com/ArtImage/20061023/ye364_1.gif* t# @9 e5 E/ i+ Y
图6-01 GIF文件结构6 @, c6 g& |+ J6 J
9 O2 |& H5 G, [
数据块可分成3类:控制块(Control Block),图形描绘块(Graphic-Rendering Block)和专用块(Special Purpose Block)。
: S/ l& y5 [8 V0 x& r1 I8 {. m( W, q. u: p5 C
(1) 控制块:控制块包含有用来控制数据流(Data Stream)或者设置硬件参数的信息,其成员包括:
# C; F: j: T( m; j  X8 N
4 R2 m" f  k: L/ v! VGIF文件头(Header)
& X' U" r5 i- y2 x逻辑屏幕描述块(Logical Screen Descriptor) $ v; k4 ^3 A. g: L; ?- c
图形控制扩展块(Graphic Control Extension) ( A7 r* G. i3 m; P( r
文件结束块(Trailer)& ~) i+ E. |2 _& p: @" y. y" E% ?
(2) 图形描绘块:包含有用来描绘在显示设备上显示图形的信息和数据,其成员包括:! L6 e( Z4 b' r6 C& Q
4 v' h, t7 z9 v2 ^& o, C
图像描述块(Image Descriptor) ( R) ~' b( L- I0 a6 z
无格式文本扩展块(Plain Text Extension)
1 @% U/ s) y3 J* C4 w3 E5 |4 I6 l(3) 特殊用途数据块;包含有与图像处理无关的信息,其成员包括:
3 G8 x/ F% X/ u  X5 I& J
4 L! u5 K2 \" b; n0 K9 l2 r注释扩展块(Comment Extension) 0 }$ f$ D" l9 c2 C5 i
应用扩展块(Application Extension)
( ?+ M; {, Y* R& H9 c+ B4 W除了在控制块中的逻辑屏幕描述块(Logical Screen Descriptor)和全局彩色表(Global Color Table)的作用范围是整个数据流(Data Stream)之外, 所有其他控制块仅控制跟在它们后面的图形描绘块。9 i9 B3 u$ \$ |* m4 D
+ `" u& {. |8 d
6.2.3 构件详解- l1 U8 i/ [$ I, c0 m* }) i% @9 s

9 @: E0 }$ h$ z5 e2 x1. GIF文件头- t& U1 ~3 x( A. j8 E0 B

! v! m% r" X4 ]文件头描述块(Header)定义GIF数据流(GIF Data Stream),它的结构如图6-02所示。文件头描述块(Header)由GIF标记域(Signature)和版本号(Version)域组成,是一个由6个固定字节组成的数据块,它们用来说明使用的文件格式是GIF格式及当前所用的版本号。GIF标记域(Signature)存放的是“GIF”,版本号域存放的是1987年5月发布的“87a”或者1989年7月发布的“89a”,或者更加新的版本号。
5 @( Z. [4 w) [: B7 J
& B. N6 }$ `. M5 ~+ }% N, t# _http://www.qqread.com/ArtImage/20061023/ye364_2.gif; A5 ^! B% k9 f" |2 A* e" t- p8 H
图6-02 标记/版本数据块的结构
: d: |6 y- w7 B$ S% m  g' g* {+ `! C4 t
2. 逻辑屏幕描述块
$ l! }* M+ S- j: C4 [8 ~; [
. _* M/ s8 k1 d6 K& E逻辑屏幕描述块(Logical Screen Descriptor)包含定义图像显示区域的参数,包括背景颜色信息。这个数据块中的坐标相对于虚拟屏幕的左上角,不一定是指显示屏的绝对坐标,这就意味可以参照窗口软件环境下的窗口坐标或者打印机坐标来设计图像显示程序。逻辑屏幕描述块的结构如图6-03所示:/ g; q. J4 B4 m* I7 J! K8 X
& I6 W5 |3 ~( W) T/ d$ X' K( a
http://www.qqread.com/ArtImage/20061023/ye364_3.gif# O  e, F. `" B2 f
图6-03 屏幕描述块的结构
4 t8 h. e9 T0 D) z0 @" Z' P  U: ^1 t0 [
逻辑描述块包含7个字节。字节0和字节1用来说明逻辑显示屏的宽度,字节3和字节4用来说明逻辑显示屏的高度,字节4用来描述彩色表的属性,字节5用来指定背景颜色索引,字节6用来计算像素的宽高比。现作如下说明:! q/ x/ |, v3 M4 [1 q
* ~7 |6 R6 g0 q% L: ]6 c3 W1 N2 u
(1) 屏幕描述块中的第5个字节称为包装域(Packed Fields),它的位结构如图6-04所示,它由4个子域组成:
# ?! [" o8 U3 h  j; c
4 A3 v& G# h6 T4 k9 W' U" i① 全局彩色表标志(Global Color Table Flag )域G用来说明是否有全局彩色表存在。如果G=1,表示有一个全局彩色表(Global Color Table)将紧跟在这个逻辑屏幕描述块(Logical Screen Descriptor)之后;这个标志也用来选择背景颜色索引(Background Color Index)。如果G=1,背景颜色索引(Background Color Index)域中的值就用作背景颜色的索引。: a; @6 @, I8 v! o

: E* m3 H( K4 }! A- R② 彩色分辨率(Color Resolution)域CR用来表示原始图像可用的每种基色的位数(实际值减1)。这个位数表示整个调色板的大小,而不是这幅图像使用的实际的颜色数。例如,如果该域的值CR=3,说明原始图像可用每个基色有4位的调色板来生成彩色图像。
. g* ~3 T7 e9 K; c. z) ?+ ~* j0 g+ Y/ Q# w
③ 彩色表排序标志(Sort Flag)域S用来表示全局彩色表(Global Color Table)中的颜色是否按重要性(或者称使用率)排序。如果S=0,表示没有重要性排序;如果S=1表示最重要的颜色排在前。这样做的目的是辅助颜色数比较少的解码器能够选择最好的颜色子集,在这种情况下解码器就可选择彩色表中开始段的彩色来显示图像。$ C: g4 C3 x5 T  b) @; H
- Y3 o3 x% s. p  X) N( B- U+ k0 q$ S
④ 全局彩色表大小(Size of Global Color Table)域Size表示表示每个像素的位数,它用来计算全局彩色表(Global Color Table)中包含的字节数。在全局彩色表标志(Global Color Table Flag)域G=0时就不需要计算,G=1时就要计算彩色表的大小,具体计算见下文的“3. 全局彩色表”。
% X2 f: g  \" y7 \, h4 l- v1 Q9 `' K$ Q1 ^. `, ?% P
http://www.qqread.com/ArtImage/20061023/ye364_4.gif* k4 u  d2 S4 t
图6-04 逻辑屏幕描述块中的包装域结构
" O3 L. v$ R4 }8 n4 N; p
7 o! Q/ a/ y* }, r' [(2) 屏幕描述块中的第6个字节是背景颜色索引(Background Color Index),它是彩色表的一个索引值,用来指定背景颜色。如果全局彩色表标志(Global Color Table Flag)域G=0,这个域的值也设置为0。' R7 w* M: {. T0 g# G% h
5 [6 u% \, y" T6 f8 r6 B. y3 M  v
(3) 像素宽高比(Pixel Aspect Ratio)域中的值是一个因数,是计算原始图像像素的宽高比的一个近似值。如果该域的值范围为1~255,如果不等于0,宽高比的近似值按下式计算:
( }- Q% T9 p. u1 I" ?0 l/ d. ?
* I' k8 b5 M1 ^+ k0 {* P- R8 zAspect Ratio = (Pixel Aspect Ratio + 15) / 64
% l  g/ f$ k% f) s5 y, x/ Q- ]像素宽高比(Pixel Aspect Ratio)定义成像素的宽度与高度之比,比值的范围在4:1~1:4之间,其增量为1/64。
% |1 ^( Q8 T8 {% f2 o3 C) B2 B1 F
$ D2 x6 i) q) C( x3. 全局彩色表
: f4 A$ Y$ V/ L/ g, P0 p# U1 _+ k0 y* x/ k) {- O" j3 e
由于一个GIF文件可以包含多幅彩色图像,每幅彩色图像也许都包含适合自身特点的彩色表,所以一个GIF文件可以有好几个彩色表。但归纳起来只有两类:全局彩色表(Global Color Table)或局部彩色表(Local Color Table)。全局彩色表可用于图像本身没有带彩色表的所有图像和无格式文本扩展块(Plain Text Extension),而局部彩色表只用于紧跟在它后面的一幅图像。在处理全局彩色表和局部彩色表时需要注意下面一些规则。
4 n9 m& Z& f  m% {
; A1 T( s1 p* B9 k8 h① 如果GIF文件包含全局彩色表(Global Color Table),而且要显示的图像本身又带有局部彩色表,那末显示该幅彩色图像时就用它自己的彩色表,而不用全局彩色表。在这种情况下,解码器就首先保存全局彩色表(Global Color Table),然后使用局部彩色表(Local Color Table)来显示图像,最后再回复全局彩色表(Global Color Table)。
1 k' f0 y. O! }. ~' y! L
8 j0 V9 C4 v# J) h. E; e5 k- k② 全局彩色表(Global Color Table)和局部彩色表(Local Color Table)都是可选择的。由于这个原因,解码器最好要保存全局彩色表(Global Color Table),一直到出现另一个全局彩色表(Global Color Table)为止。这样做之后,对于包含完全没有彩色表的一幅或者多幅彩色图像的GIF文件就可以使用最后保存的全局彩色表(Global Color Table)进行处理。+ J* X1 e6 K5 \1 C5 |$ P
$ K: @* ?) _$ i( k
③ 如果同类型的图像能够使用相同的彩色表来显示,编码器就要尽可能使用一个全局彩色表(Global Color Table);如果没有彩色表可用,解码器就可以使用计算机系统提供的彩色表或者解码器自身的彩色表。
4 z2 B1 n& C' K* v: L5 H2 h( V6 \+ l
④ 全局彩色表(Global Color Table)存在与否由逻辑屏幕描述块(Logical Screen Descriptor)中字节5的全局彩色表标志(Global Color Table Flag )域G的值确定。如果存在,彩色表就紧跟在逻辑屏幕描述块(Logical Screen Descriptor)之后。彩色表的表项数目等于2(n +1),其中n=b2b1b0,每个表项由3个字节组成,分别代表R、G、B的相对强度,因此彩色表的字节数就等于3×2(n +1)。彩色表的结构如图6-05所示。' z& g: s5 n0 c' Y0 m0 Q! s/ W8 b
) g; [  M9 j+ u6 U
http://www.qqread.com/ArtImage/20061023/ye364_5.gif
9 |7 n4 X$ e- x$ I$ }4 s& o& E' X图6-05 彩色表结构
3 z$ U" v2 P- S' U% K( k1 D: q  G: f: N
局部彩色表与全局彩色表有相同的存储格式。
9 o; Z7 l. d; l' o0 g' Z) F% B4 h/ H6 i7 v
4. 图像描述块; K+ {& B  g4 @3 X

( H; e* V6 O9 z( m* |' B5 ~: I8 BGIF图像文件格式可包含数量不限的图像,而且也没有一个固定的存放顺序,仅用一个字节的图像分隔符(Image Separator)来判断是不是图像描述块。每一幅图像都由一个图像描述块(Image Descriptor)、可有可无的局部彩色表(Local Color Table)和图像数据组成。每幅图像必须要落在逻辑屏幕描述块(Logical Screen Descriptor)中定义的逻辑屏(Logical Screen)尺寸范围里。
2 P% b: }* z# ?4 K! l( d
: Y8 z) v, H1 B图像描述块(Image Descriptor)之前可以有一个或者多个控制块,例如图形控制扩展块(Graphic Control Extension),其后可以跟着一个局部彩色表(Local Color Table)。无论前后是否有各种数据块,图像描述块(Image Descriptor)总是带有图像数据。& G, G( b% a# A7 Z3 s% ]9 Z

  p! B+ b7 [8 K1 k图像描述块(Image Descriptor)的结构如图6-06所示。
) S7 _. i7 M, K% k$ b( X* L" p# \$ w% |( O1 w+ d3 d
http://www.qqread.com/ArtImage/20061023/ye364_6.gif) i# ~7 m8 i! ^5 Q5 o" n& a% }
图6-06 图像描述块的结构
, |# g) t( R+ m6 }1 g* |1 j8 q: F( u! l' `
在图6-06中,图像分隔符(Image Separator)用来标识图像描述块的开始,该域包含固定的值:0x2C;图像左边位置(Image Left Position)是相对于逻辑屏幕(Logical Screen)最左边的列号,逻辑屏幕最左边的列好定义为0;图像顶部位置(Image Top Position) 是相对于逻辑屏幕(Logical Screen)顶部的行号,逻辑屏幕顶部的行号定义为0。
- O5 a6 T8 p' o2 s$ r) A, V+ |7 T3 C4 ?
http://www.qqread.com/ArtImage/20061023/ye364_7.gif
9 ^( e7 A/ G" h+ u' m图6-07 图像描述块中的包装域结构
& d; [7 e# n2 i# U9 ?* w% k% b; V* E! i
图像描述块(Image Descriptor)中的第9个字节称为包装域(Packed Fields)字节,它的位结构如图6-07所示,它由5个子域组成:: v& w$ v- `, N6 _/ F8 `/ d
; s3 i$ \, j6 X# k: y
① 局部彩色表标志(Local Color Table Flag )域L用来说明是否有局部彩色表存在。如果L=1,表示有一个局部彩色表(Local Color Table)将紧跟在这个图像描述块(Image Descriptor)之后;如果L=0,表示图像描述块(Image Descriptor)后面没有局部彩色表(Local Color Table),该图像要使用全局彩色表(Global Color Table)。
# G3 v. o3 I+ v9 ?5 e  u2 |; Y1 B
# C6 E$ @$ }8 n* r  }② 交插显示标志(Interlace Flag)域I用来表示该图像是不是交插图像(Interlaced Images)。如果I=0,表示该图像不是交插图像,如果I=1表示该图像是交插图像。使用该位标志可知道图像数据是如何存放的。GIF文件格式定义了两种数据存储方式:一种是按图像行连续顺序存储,这个顺序与显示器上显示行的顺序相同;另一种按交插方式存储。交插图像按行分成如下所示的4组(Group):
+ j1 s) s6 c3 v) L+ R$ \
0 q( @9 R7 |# Q! zGroup 1:每隔8行组成一组,从第0行开始显示 /第1遍交插# ]1 h3 d% x2 C0 }% j

: C2 }8 h# S1 r1 u7 ~9 SGroup 2:每隔8行组成一组,从第4行开始显示 /第2遍交插
- W& f! `! P. u- o! n5 |1 v, W8 M8 X  f
Group 3:每隔4行组成一组,从第2行开始显示 /第3遍交插
. R$ S# c* x+ u4 ^( j/ U0 \/ V1 X: e  u1 \+ B5 ^2 F# ^7 f
Group 4:每隔2行组成一组,从第1行开始显示 /第4遍交插
) a, G; Y2 {0 d% l+ S) k3 F( u* f" }1 Z) a
由于显示图像需要较长的时间,使用这种方法存放和显示图像数据,人们就可以在图像显示完成之前看到这幅图像的概貌,而不觉得显示时间长。图6-08说明了这种交插图像的存储和显示顺序。
( C; L% o# o" g, f8 k
4 n6 l8 D% X/ D: M+ u# z/ vhttp://www.qqread.com/ArtImage/20061023/ye364_8.gif
) t5 d1 Y3 S" M图6-08 交插图像显示顺序. L) J9 U7 Z' X8 f( x) R

) w1 Y: [/ E: g* C& l③ 彩色表排序标志(Sort Flag)域的含义与全局彩色表(Global Color Table)中(Sort Flag)域的含义相同。: R5 d4 u: O/ E* L9 p- a" e2 r  ?
3 j2 c/ X: L# q0 f
④ 保留(Reserved)。
: @% A4 ^3 s3 Q( P4 Z9 K% @9 K) x5 ?1 z& Z. }
⑤ 局部彩色表大小(Size of Local Color Table)域的值用来计算局部彩色表(Global Color Table)中包含的字节数。
. u! \8 R4 O" C+ k6 K: S, y0 _: ]4 u  }: B& ^7 o( w6 s* F
5. 局部彩色表' P8 v1 C, v  ]) Z6 ?
& ^  h4 m7 E' M) ^1 y6 E  `; a
局部彩色表(Local Color Table)用于紧跟在它后面的图像。彩色表是否存在取决于图像描述块(Image Descriptor)中局部彩色表标志(Local Color Table Flag)位的设置。彩色表的结构和大小与全局彩色表(Global Color Table)完全相同。" l5 {0 d9 L3 Y8 i0 w# Y

! X8 b0 @, n1 b6 f6 {5 m6. 表基图像数据
% M" ~' f( s5 T2 j) X5 @0 z6 N8 l8 b4 k! W
GIF图像采用了LZW算法对实际的图像数据进行压缩。为了提高压缩编码的效率,对LZW编码器输出的代码采用可变长度码VLC(variable-length-code),不是用位数高度的代码来表示输出,而且代表码字的位数是可变的。4 o. r1 M9 k$ @

; Q; c, E5 M/ @  |$ r表基图像数据(Table Based Image Data)由LZW最小代码长度(LZW Minimum Code Size)和图像数据(Image Data)组成,如图6-09所示。LZW最小代码长度域的值用来确定图像数据中LZW代码使用的初始位数。图像数据(Image Data)由数据子块(Data Sub-blocks)序列组成。
! f- _; \9 W  z- q1 R; H( c/ D% r$ M& }4 e  E# T
http://www.qqread.com/ArtImage/20061023/ye364_9.gif
* O. k3 Z1 c% `$ l# i; M图6-09 图像数据的存储格式3 X& [, n" _  W% i4 V. {0 v

3 M4 T1 t! {+ K, d/ o) W数据子块(Data Sub-blocks)的结构如图6-10所示,这是一个可变长度的数据块,其长度由块大小域(Block Size)域中的值确定,字节数在0~255之间。: Z0 \8 C: ?( k) x7 ~

: g: V4 X4 c# V4 T1 t  o' Dhttp://www.qqread.com/ArtImage/20061023/ye364_10.gif3 D# S+ ]. x3 _" _. S! H
图6-10 数据子块的结构! U0 C6 ^/ a: o0 A6 \- y; M

* a$ F3 v: b- M! y. {6 s) ~% y7. 图形控制扩展块
( y' g9 H8 o: a: ?; }$ `$ D
: J# H" ~' ?5 ]$ N# |  m3 |% w+ _图形控制扩展块(Graphic Control Extension)包含处理图形描绘块时要使用的参数,它的结构如图6-11所示。现说明如下:7 ^0 h% c- H0 t4 ?2 P7 b- Y
/ i1 _: L/ }/ Y- J! ?  D8 e
(1) 扩展导入符Extension Introducer)用于识别扩展块的开始,域中的值是一个数值等于0x21的固定值。; c* ?' z! }9 D6 g
9 t+ I4 B4 h9 e% R" F
(2) 图形控制标签(Graphic Control Label)用于标识当前块是一个图形控制扩展块,域中的值是一个数值等于0xF9的固定值。! z4 C7 W5 l* q+ J. P
9 m3 A- w# c1 H7 x
(3) 块大小(Block Size)用来说明该扩展块所包含字节数,该字节数是从这个块大小(Block Size)域之后到块结束符之间的字节数。
. U$ H7 t7 Y( M. e2 n! x6 \' N$ L! ]3 v9 `' x% q( N
http://www.qqread.com/ArtImage/20061023/ye364_11.gif
/ B) B2 r  G9 J- T图6-11 图像描述块的结构. N3 r$ x" `: U6 s8 o% j7 f
; W# M" a% J3 g4 j8 h: G  l
(4) 包装域的结构如图6-12所示。处理方法(Disposal Method)规定图形显示之后译码器要用表6-03中所述方法进行处理。
  E# {; q3 b. ]3 Q7 j1 u! x, C5 _" Q+ J
  w1 n- i3 _+ h# F4 Z3 j- shttp://www.qqread.com/ArtImage/20061023/ye364_12.gif; m7 V1 L# K' U, R
表6-03 包装域规定的处理方法7 l8 m# W, X; B1 |& a, |) D% t

; Q) o/ y0 p9 ?8 m* i( [用户输入标志(User Input Flag)域表示在继续处理之前是否需要用户输入响应。在延时时间(Delay Time)和用户输入标志(User Input Flag)都设置为1的情况下,继续处理的开始时间取决于用户响应输入在前还是延时时间结束在前。
# K- m- j7 Z$ x9 d/ t% i$ W9 A2 ~, ^4 F) m
http://www.qqread.com/ArtImage/20061023/ye364_13.gif0 Y6 w# D8 {8 L
图6-12 图形控制扩展块的包装结构1 ]' U/ p7 |- q2 Z2 {7 m
$ ]+ ?6 a/ M$ L) l
(5) 透明(Transparency Flag)表示是否给出透明索引(transparency index)
8 ~6 B) D. I/ {  A5 e; f4 j
3 e+ Z# V8 E  e; z8 @- L(6) 延时时间(Delay Time)用来指定在图形显示之后继续处理数据流之前的等待时间,一百分之一秒为单位。; u% y: f) ^% b- p! W; s7 A. \

/ i2 l/ R- P  m(7) 当且仅当透明标志位设置为1时,透明索引(Transparency Index)用来指示处理程序是否要修改显示设备上的相应象点。当且仅当透明标志位设置为1时,就要修改。1 C4 O" R' j2 n# S: T8 h1 j# y

/ H8 o/ b) {: o# L3 @! A, t(8) 块结束符(Block Terminator)表示该图形控制扩展块(Graphic Control Extension)结束,它是由一个字节组成的数据块,该域的值是一个固定的值:0x00,因此称为零长度数据子块(zero-length Data Sub-block)。
* v! i" B8 o: z/ e! {  I
5 }/ G. n' |1 I- X" j8. 无格式文本扩展块
# h4 w* s! T. R. A; u! u: w
, \2 W8 V3 e- h4 ]* w% K无格式文本扩展块(Plain Text Extension)包含文本数据和描绘文本所须的参数。文本数据用7位的ASCII字符编码并以图形形式显示。扩展块的结构如图6-13所示。9 W) s' W! Y8 _/ C# V! i$ k
7 u0 V5 h, K6 z
http://www.qqread.com/ArtImage/20061023/ye364_14.gif* Y4 g1 C4 T3 }8 N6 x
图6-13 无格式文本扩展块结构, B4 M+ A( {& ]$ ?% u1 M1 Z
5 G; N! H8 n- j$ y- z$ l
9. 注释扩展块
! `; v  c4 v/ v+ f& h: Z6 I" f2 C/ n9 ?
注释扩展块(Comment Extension)域的内容用来说明图形、作者或者其他任何非图形数据和控制信息的文本信息。
! }" n7 P9 q2 n4 S: M) g% O: q& x! f& j0 i' t3 C+ G) v
注释扩展块的结构如图6-14所示。其中的注释数据是序列数据子块(Data Sub-blocks),每块最多255个字节,最少1个字节。
7 q4 R9 g8 B4 x9 t& B7 K+ F
7 v1 r& U4 ]- ^* i; h8 khttp://www.qqread.com/ArtImage/20061023/ye364_15.gif
3 \/ U0 g) [! a3 u; Q2 h0 U图6-14 注释扩展块
1 y4 V0 j( R- S. t+ L
* _9 K& q. C4 f. x/ O7 I! @10. 应用扩展块
- g) h1 Z& _$ b
+ a& n4 N$ `5 S8 _+ _应用扩展块(Application Extension)包含制作该图像文件的应用程序的相关信息,它的结构如图6-15所示。4 n( W' |- t8 q" f. l" F

4 g7 y% W( i9 W' m$ vhttp://www.qqread.com/ArtImage/20061023/ye364_16.gif
. R. k2 V% z' b( T图6-15 应用扩展块
1 f- K8 X% ?7 j/ D: Y7 c/ U7 Z3 r2 |4 L
11. GIF文件结束块
# N' O: w, G, ]+ m- Y5 m/ k; J/ C. _0 N9 i
结束块(GIF Trailer)表示GIF文件的结尾,它包含一个固定的数值:0x3B。它具有如图6-16所示的结构。
: z9 K" |# E2 W: E+ M' V3 L* ?! _) B
http://www.qqread.com/ArtImage/20061023/ye364_17.gif- `9 F/ L% X  G$ w1 P+ u) T
图6-16 GIF文件结束块
& Q; G# _# Z: P5 |
+ V" S9 T& B7 z/ H6.2.4 速差表6 ^* X2 q, s, i: q- z
表6-04 GIF文件格式7 C! D4 x  _+ U$ {. R0 J& C0 a
http://www.qqread.com/ArtImage/20061023/ye364_18.gif
0 e0 b8 J6 |/ j# |" f; j" i表6-04 GIF文件格式3 A4 V$ p7 W) U; H2 G# ]6 u2 j1 S1 J
7 g: o0 P5 \8 r! G* S
[ 本帖最后由 kenson 于 2009-3-15 16:30 编辑 ]
 楼主| 发表于 2009-3-14 17:14 | 显示全部楼层
更多的请看这个网站:. H+ |2 H& m/ A" U$ J9 R7 p
http://eilat.sci.brooklyn.cuny.e ... if/gif89a.htm#specs
 楼主| 发表于 2009-3-16 20:27 | 显示全部楼层
简介
  b! D' W8 T! ~# }% VGIF(Graphics Interchange Format)是CompuServe公司开发的图像文件存储格式,1987年开发的GIF文件格式版本号是GIF87a,1989年进行了扩充,扩充后的版本号定义为GIF89a。
$ L/ i+ ~! K* `! C+ y2 W% TGFI图像文件以数据块(block)为单位来存储图像的相关信息。一个GIF文件由表示图形/图像的数据块、数据子块以及显示图形/图像的控制信息块组成,称为GIF数据流(Data Stream)。数据流中的所有控制信息块和数据块都必须在文件头(Header)和文件结束块(Trailer)之间。" q' |2 X9 Z) ~+ D
GIF文件格式采用了LZW(Lempel-Ziv Walch)压缩算法来存储图像数据,定义了允许用户为图像设置背景的透明(transparency)属性。此外,GIF文件格式可在一个文件中存放多幅彩色图形/图像。如果在GIF文件中存放有多幅图,它们可以像演幻灯片那样显示或者像动画那样演示。
% V' @8 L+ p3 ]+ V( m1 r/ s! V9 k5 Q7 q: D9 ^1 x% T* q0 B; x
2. 文件结构
: A/ ?- R$ W& W# `2 J; t9 H4 j: SGIF文件结构的典型结构如图01所示。为下文说明方便,在构件左边加了编号。
& S# X8 j; T( S4 l
+ ^* ?7 f& {9 I$ V
+ F: M$ w; I7 L/ V
15 `% {7 V4 ^: D3 [5 M3 G8 r; j
Header: O, l2 g( l: ~( r& h' D0 h8 l
 GIF文件头" H: \  i+ s( ~, A
 
2
5 o5 O& ^- V% `- H8 y: U3 i7 A
Logical Screen Descriptor
& u- `+ t) A- n+ z5 B9 _) ~5 W
 逻辑屏幕描述块
; B9 I" x: e0 g& V
 
3
: Q/ r. G# v: L/ d5 B
Global Color Table$ N7 U( _) `2 P5 a, n
 全局彩色表3 \/ g. }3 w" }9 ?6 x
 
 … 扩展模块(任选) …, ?" O/ }9 S. C/ q3 ], w
   
42 y! l& \- R) W& p
Image Descriptor
: G1 e- T# E6 z6 C3 D. I/ g7 r0 j! z
 图形描述块
3 C: D  M+ z6 }) l7 k
 
5; L( K5 m) [% r8 t+ u
Local Color Table6 ~) b( J; f0 e( [
 局部彩色表(可重复n次)
) V" L8 s6 V2 ?" y
6& ?2 f& V: y  ]& z& ?. V9 @
Table Based Image Data
0 {- y% M, m7 `1 _* a* ~( z2 I5 m
 表式压缩图像数据
9 G0 ?9 C- |6 c9 ]- r
7
, H$ d: q4 P" z( @
Graphic Control Extension
( Y6 o8 e& a2 c1 ]! q% q! S
 图像控制扩展块, a# L8 s% {( {( D+ j0 k
81 ^* ]+ t7 a& S$ ~4 o2 \4 C* t
Plain Text Extension3 l/ P1 c; g* y& X* F5 j
 无格式文本扩展块
- G4 U! F7 C3 r4 A6 {& m% A; O
n
9; O0 d  L% H- f8 }7 M
Comment Extension9 A7 A8 t7 l/ A- h2 L4 s. h4 N
 注释扩展块
  d, v! Y: |# w' f9 k
104 L4 p* T6 T3 f* A( q
Applicaton Extension
' X( g8 p- t0 j6 f( n
 应用程序扩展块
$ z' F- T% U8 L: G' F) p
 
 … 扩展模块(任选) …
# H3 v+ J* S: R7 }9 a( M  }
   
11
1 f! I% {! v1 W1 O. T+ l
GIF Trailer( k2 @. c4 E3 o  y5 j5 J% H: G
 GIF文件结束块
  C: f2 a7 _! Z6 `" [
 
7 w" r  N+ @  U3 c
+ G5 E; f$ N" i! c: R! b7 Z3 J
图01 GIF文件结构

+ I+ v) o& v& x* u9 |5 L数据块可分成3类:控制块(Control Block),图形描绘块(Graphic-Rendering Block)和专用块(Special Purpose Block)。
& Z( Y8 c  W7 A! B0 o# B(1) 控制块:* S6 B% _2 ?! D
  • GIF文件头(Header)
  • 逻辑屏幕描述块(Logical Screen Descriptor)
  • 图形控制扩展块(Graphic Control Extension)
  • 文件结束块(Trailer)
(2) 图形描绘块:# c' v4 c0 ^$ A# V
  • 图像描述块(Image Descriptor)
  • 无格式文本扩展块(Plain Text Extension)
(3) 特殊用途数据块;- u$ Y1 H) p2 k: O1 U9 y
  • 注释扩展块(Comment Extension)
  • 应用扩展块(Application Extension)
除了在控制块中的逻辑屏幕描述块(Logical Screen Descriptor)和全局彩色表(Global Color Table)的作用范围是整个数据流(Data Stream)之外, 所有其他控制块仅控制跟在它们后面的图形描绘块。
' }9 V4 o/ N$ Y# Z" U1 S
) k; n8 y; n( n" M1 J; o3 U  o
包含有与图像处理无关的信息,其成员包括:
包含有用来描绘在显示设备上显示图形的信息和数据,其成员包括:
控制块包含有用来控制数据流(Data Stream)或者设置硬件参数的信息,其成员包括:
3 构件详解 + p, @/ Q! d) V3 x$ |. G7 S: N) m9 `
1. GIF文件头  p3 f6 i* |5 e) I
文件头描述块(Header)定义GIF数据流(GIF Data Stream),它的结构如图02所示。文件头描述块(Header)由GIF标记域(Signature)和版本号(Version)域组成,是一个由6个固定字节组成的数据块,它们用来说明使用的文件格式是GIF格式及当前所用的版本号。GIF标记域(Signature)存放的是“GIF”,版本号域存放的是1987年5月发布的“87a”或者1989年7月发布的“89a”,或者更加新的版本号。+ `' G! g3 @2 N* N+ Z

' E5 {7 z4 \$ e/ F& l4 V/ b
7
6
5
4
3
2
1
0
字节号
域的名称
数据类型
 
0
  
Signature
1
GIF标记
3 Bytes
 
2
  
 
3
  
Version
4
版本号
3 Bytes
 
5
  

! p0 i. g+ r* T& d0 j3 p) {
/ g! m1 C, |+ d! ~4 }  ~" |
图02 标记/版本数据块的结构
 0 f. ]- j$ J& v# O' }
2. 逻辑屏幕描述块7 w) u$ ~& ?9 `0 v+ o7 E
逻辑屏幕描述块(Logical Screen Descriptor)包含定义图像显示区域的参数,包括背景颜色信息。这个数据块中的坐标相对于虚拟屏幕的左上角,不一定是指显示屏的绝对坐标,这就意味可以参照窗口软件环境下的窗口坐标或者打印机坐标来设计图像显示程序。逻辑屏幕描述块的结构如图03所示:
$ D8 M, c) ]/ P" H' c  l4 [

5 h7 ~, q6 G1 B; t) Y" w" u2 ?
7
6
+ }2 ^3 C4 Y/ ~  X3 W+ T- n8 K
5
* M/ z% }" @% i3 t& i' ?
47 `' ^! O* S- J
3' z$ P! l2 ~8 r
2( a& I5 M! L$ T; G, ?
1
0 a# `) o5 a9 _& g" d6 A1 L
0
* ~/ S5 G- `# R" L: s
字节号
域的名称
类型
Logical Screen Width
0
逻辑屏幕宽度5 n: a6 l6 g8 ], E
Unsigned' L- n: P: S6 I  D/ W
 
1
(以像素为定单位)! z$ Q% k5 s& n5 n
 
Logical Screen Height
2
逻辑屏幕高度
# z3 [; R- r4 U7 t
Unsigned7 ?9 [; l! [. Q
 
3
(以像素为定单位)
6 D: N! D2 f: r3 }
 
G
CR
S
Size
4
包装域2 q. E4 `) u& \4 w; Z8 z
见图6-04& m. g" P7 }5 d2 C
Background Color Index
5
背景颜色索引
' f* V3 E, P! k' K$ ?
Byte( b' b7 j5 Z" f. S$ }
Pixel Aspect Ratio
6
像素宽高比4 f% s% m  l& X9 g. ~
Byte
/ @6 N3 ]% V7 J; K$ T

* b  ]  x5 g; z/ T# x
图03 屏幕描述块的结构
逻辑描述块包含7个字节。字节0和字节1用来说明逻辑显示屏的宽度,字节3和字节4用来说明逻辑显示屏的高度,字节4用来描述彩色表的属性,字节5用来指定背景颜色索引,字节6用来计算像素的宽高比。现作如下说明:
% E5 D- N8 k! i(1) 屏幕描述块中的第5个字节称为包装域(Packed Fields),它的位结构如图6-04所示,它由4个子域组成:; f1 @7 J* |& A! e) e$ r
① 全局彩色表标志(Global Color Table Flag )域G用来说明是否有全局彩色表存在。如果G=1,表示有一个全局彩色表(Global Color Table)将紧跟在这个逻辑屏幕描述块(Logical Screen Descriptor)之后;这个标志也用来选择背景颜色索引(Background Color Index)。如果G=1,背景颜色索引(Background Color Index)域中的值就用作背景颜色的索引。4 j3 T; X. w6 t0 s; \1 b8 f9 t
② 彩色分辨率(Color Resolution)域CR用来表示原始图像可用的每种基色的位数(实际值减1)。这个位数表示整个调色板的大小,而不是这幅图像使用的实际的颜色数。例如,如果该域的值CR=3,说明原始图像可用每个基色有4位的调色板来生成彩色图像。
, s' L# F: v/ n- j  p: m% n* Z③ 彩色表排序标志(Sort Flag)域S用来表示全局彩色表(Global Color Table)中的颜色是否按重要性(或者称使用率)排序。如果S=0,表示没有重要性排序;如果S=1表示最重要的颜色排在前。这样做的目的是辅助颜色数比较少的解码器能够选择最好的颜色子集,在这种情况下解码器就可选择彩色表中开始段的彩色来显示图像。$ W" o* `% h7 a, d
④ 全局彩色表大小(Size of Global Color Table)域Size表示表示每个像素的位数,它用来计算全局彩色表(Global Color Table)中包含的字节数。在全局彩色表标志(Global Color Table Flag)域G=0时就不需要计算,G=1时就要计算彩色表的大小,具体计算见下文的“3. 全局彩色表”# D* A- p. |- A  i9 d* c$ g1 f, y
7
6
5
4
3
2
1
0
Global Color Table Flag
Color Resolution
Sort Flag
Size of Global Color Table
3 T+ A2 V. @( R/ W

5 R* w5 V5 I& }. m- U, t3 _
图04 逻辑屏幕描述块中的包装域结构
(2) 屏幕描述块中的第6个字节是背景颜色索引(Background Color Index),它是彩色表的一个索引值,用来指定背景颜色。如果全局彩色表标志(Global Color Table Flag)域G=0,这个域的值也设置为0。! ^" ~2 V$ I& p3 E% D& j0 d% R/ q
(3) 像素宽高比(Pixel Aspect Ratio)域中的值是一个因数,是计算原始图像像素的宽高比的一个近似值。如果该域的值范围为1~255,如果不等于0,宽高比的近似值按下式计算:, J* x! w6 |# l% {) _# o) G/ Z# S
Aspect Ratio = (Pixel Aspect Ratio + 15) / 64
3 Y- ?; i6 X1 q* I/ b6 E9 @, I像素宽高比(Pixel Aspect Ratio)定义成像素的宽度与高度之比,比值的范围在4:1~1:4之间,其增量为1/64。5 m6 J0 Y# p/ I  Q6 \
3. 全局彩色表% _2 s9 [, a* R3 f
由于一个GIF文件可以包含多幅彩色图像,每幅彩色图像也许都包含适合自身特点的彩色表,所以一个GIF文件可以有好几个彩色表。但归纳起来只有两类:全局彩色表(Global Color Table)或局部彩色表(Local Color Table)。全局彩色表可用于图像本身没有带彩色表的所有图像和无格式文本扩展块(Plain Text Extension),而局部彩色表只用于紧跟在它后面的一幅图像。在处理全局彩色表和局部彩色表时需要注意下面一些规则。8 Q) _3 s% i0 x; B, E! v7 b& `6 T
① 如果GIF文件包含全局彩色表(Global Color Table),而且要显示的图像本身又带有局部彩色表,那末显示该幅彩色图像时就用它自己的彩色表,而不用全局彩色表。在这种情况下,解码器就首先保存全局彩色表(Global Color Table),然后使用局部彩色表(Local Color Table)来显示图像,最后再回复全局彩色表(Global Color Table)。
, o, B; i2 H2 D$ _0 b7 H② 全局彩色表(Global Color Table)和局部彩色表(Local Color Table)都是可选择的。由于这个原因,解码器最好要保存全局彩色表(Global Color Table),一直到出现另一个全局彩色表(Global Color Table)为止。这样做之后,对于包含完全没有彩色表的一幅或者多幅彩色图像的GIF文件就可以使用最后保存的全局彩色表(Global Color Table)进行处理。
7 ?' f1 i: O& V! V1 ~% ?③ 如果同类型的图像能够使用相同的彩色表来显示,编码器就要尽可能使用一个全局彩色表(Global Color Table);如果没有彩色表可用,解码器就可以使用计算机系统提供的彩色表或者解码器自身的彩色表。9 T  M; _/ P+ d) W1 ?
④ 全局彩色表(Global Color Table)存在与否由逻辑屏幕描述块(Logical Screen Descriptor)中字节5的全局彩色表标志(Global Color Table Flag )域G的值确定。如果存在,彩色表就紧跟在逻辑屏幕描述块(Logical Screen Descriptor)之后。彩色表的表项数目等于2(n+1),其中n=b2b1b0,每个表项由3个字节组成,分别代表R、G、B的相对强度,因此彩色表的字节数就等于3×2(n+1)。彩色表的结构如图05所示。
5 V, m* K4 A5 F1 m
# a2 F0 g$ x: T. @
7 6 5 4 3 2 1 0
字节号
域的名称
数据类型
red intensity
0
红色索引 000
Byte
green intensity
1
绿色索引 000
Byte
blue intensity
2
蓝色索引 000
Byte
red intensity
3
红色索引 001
Byte
green intensity
4
绿色索引 001
Byte
blue intensity
5
蓝色索引 001
Byte
 
 
red intensity
745
红色索引 255
Byte
green intensity
746
绿色索引 255
Byte
blue intensity
767
蓝色索引 255
Byte
- G* h, [6 d/ S
图05 彩色表结构
局部彩色表与全局彩色表有相同的存储格式。
; @/ r; H% H% F1 U. y3 F' P
4. 图像描述块
( r. w; b  ]( l+ E; ~5 ^4 T$ J
GIF图像文件格式可包含数量不限的图像,而且也没有一个固定的存放顺序,仅用一个字节的图像分隔符(Image Separator)来判断是不是图像描述块。每一幅图像都由一个图像描述块(Image Descriptor)、可有可无的局部彩色表(Local Color Table)和图像数据组成。每幅图像必须要落在逻辑屏幕描述块(Logical Screen Descriptor)中定义的逻辑屏(Logical Screen)尺寸范围里。
+ E* G5 v; J/ }* j% c图像描述块(Image Descriptor)之前可以有一个或者多个控制块,例如图形控制扩展块(Graphic Control Extension),其后可以跟着一个局部彩色表(Local Color Table)。无论前后是否有各种数据块,图像描述块(Image Descriptor)总是带有图像数据。
( v: y$ e, B. m  N: n& }- B# b) y图像描述块(Image Descriptor)的结构如图06所示。) w+ h6 L2 S/ D  {

7 @- F- G+ {( p6 ~
7
6
5
4
3
2
1
0
字节号
域的名称
类型
Image Separator
0
图像分隔符
$ c$ ^' E& \' s' }- S4 `
Byte
) r. O5 _7 Z8 k# K) R
Image Left Position
1
图像左边位置- X5 z  D/ d/ t0 X. d
Unsigned0 j9 Z& w7 G/ p$ M" {
 
2
(以像素为定单位)
# x. a; Z4 O& ]2 P; t
 
Image Top Position
3
图像顶部位置1 D7 A, \/ N8 W: j/ K# e3 W
Unsigned7 \6 D, S9 D6 e0 N3 E* q
 
4
(以像素为定单位)
- M) o7 w' {" r* Y; p7 ~! [
 
Image Width
5
图像宽度# ]. V. [: d7 L+ {
Unsigned
5 u. h4 U) R+ B7 A
 
6
(以像素为定单位)
3 W+ t( k0 J+ E, p3 Q
 
Image Height
7
图像高度) [  }& d: n8 l
Unsigned0 M: A" G. \+ [5 r- W/ d0 T
 
8
(以像素为定单位)1 `2 F9 o- t5 Z: T( x6 o
 
 
9
包装域6 T1 C' F$ V: N
见图6-07
. d# b; A9 ]  [! x

8 X/ W: n( {1 I$ e
图06 图像描述块的结构
在图06中,图像分隔符(Image Separator)用来标识图像描述块的开始,该域包含固定的值:0x2C;图像左边位置(Image Left Position)是相对于逻辑屏幕(Logical Screen)最左边的列号,逻辑屏幕最左边的列好定义为0;图像顶部位置(Image Top Position) 是相对于逻辑屏幕(Logical Screen)顶部的行号,逻辑屏幕顶部的行号定义为0。
+ v" z3 ]( ~! }7 T# v. I+ z5 u8 I
7
6
5
4
3
2
1
0
Local Color Table Flag
Interlace Flag
Sort Flag
Reserved
Size of Local Color Table
' i9 y* R, U) f1 P' t: J
图07 图像描述块中的包装域结构
图像描述块(Image Descriptor)中的第9个字节称为包装域(Packed Fields)字节,它的位结构如图07所示,它由5个子域组成:* @8 X  P1 F) ?( ?
① 局部彩色表标志(Local Color Table Flag )域L用来说明是否有局部彩色表存在。如果L=1,表示有一个局部彩色表(Local Color Table)将紧跟在这个图像描述块(Image Descriptor)之后;如果G=0,表示图像描述块(Image Descriptor)后面没有局部彩色表(Local Color Table),该图像要使用全局彩色表(Global Color Table)。# z* O, {" u' f2 r
② 交插显示标志(Interlace Flag)域I用来表示该图像是不是交插图像(Interlaced Images)。如果I=0,表示该图像不是交插图像,如果I=1表示该图像是交插图像。使用该位标志可知道图像数据是如何存放的。GIF文件格式定义了两种数据存储方式:一种是按图像行连续顺序存储,这个顺序与显示器上显示行的顺序相同;另一种按交插方式存储。交插图像按行分成如下所示的4组(Group):
/ q0 c0 q" u9 X0 r& Y# A! JGroup 1:每隔8行组成一组,从第0行开始显示 /第1遍交插
9 b7 z( r6 ]& G5 I; i$ [Group 2:每隔8行组成一组,从第4行开始显示 /第2遍交插# z7 V+ \3 T: w2 U
Group 3:每隔4行组成一组,从第2行开始显示 /第3遍交插
: ]& l2 |2 E6 C) E" {0 M3 aGroup 4:每隔2行组成一组,从第1行开始显示 /第4遍交插2 G5 }# H& e, O7 `* [$ Q
由于显示图像需要较长的时间,使用这种方法存放和显示图像数据,人们就可以在图像显示完成之前看到这幅图像的概貌,而不觉得显示时间长。图08说明了这种交插图像的存储和显示顺序。
2 s* D; v, }+ _" P$ n

) s2 n# i5 T/ P* h2 I! d! k
行号
像 点
交插遍次
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
- @, m  F8 I! j  X# m2 V5 H
图08 交插图像显示顺序
, Z( t1 }$ f. L% G4 u
③ 彩色表排序标志(Sort Flag)域的含义与全局彩色表(Global Color Table)中(Sort Flag)域的含义相同。
' k( L/ D# {8 V/ D& Q④ 保留(Reserved)。' r9 T4 U0 f9 j) a
⑤ 局部彩色表大小(Size of Local Color Table)域的值用来计算局部彩色表(Global Color Table)中包含的字节数。
& O2 P5 k8 E$ @& P6 W, r0 E
5. 局部彩色表1 J8 {+ O7 [2 K
局部彩色表(Local Color Table)用于紧跟在它后面的图像。彩色表是否存在取决于图像描述块(Image Descriptor)中局部彩色表标志(Local Color Table Flag)位的设置。彩色表的结构和大小与全局彩色表(Global Color Table)完全相同。
. w8 H! X( ^( e0 f
6. 表基图像数据
% n( e+ ], C2 o$ b4 {5 f
GIF图像采用了LZW算法对实际的图像数据进行压缩。为了提高压缩编码的效率,对LZW编码器输出的代码采用可变长度码VLC(variable-length-code),不是用位数高度的代码来表示输出,而且代表码字的位数是可变的。
( n/ \8 J6 I# D" m表基图像数据(Table Based Image Data)由LZW最小代码长度(LZW Minimum Code Size)和图像数据(Image Data)组成,如图09所示。LZW最小代码长度域的值用来确定图像数据中LZW代码使用的初始位数。图像数据(Image Data)由数据子块(Data Sub-blocks)序列组成。
0 D8 U- ]: b, V- p+ R3 O- W

" B( {. s9 e* _+ |/ E0 w. L
7
6
5
4
3
2
1
0
 
域的名称
类型
LZW Minimum Code Size
 
LZW最小代码长度
5 K' \1 M% s8 G3 w8 m5 a- t6 O; P
Byte
+ T) O% i6 H. y/ M) Q
5 v+ r* k/ ~2 A" g2 k, m, a: H# y& \
7 e0 v+ \# S* X: m: T
Image Data
7 p2 P) A! m0 W% D, V% h
 
图像数据
Data
7 V' G8 n4 a/ `3 O! J2 ]) r# ~Sub-blocks

2 r$ E5 }. o( q; n
5 {& h2 s# K( h4 o6 q
图09 图像数据的存储格式
数据子块(Data Sub-blocks)的结构如图10所示,这是一个可变长度的数据块,其长度由块大小域(Block Size)域中的值确定,字节数在0~255之间。; }4 N/ Q" o, M6 F* X

# y; U7 }; ?# I, ]. ?
7 6 5 4 3 2 1 0
字节号
域的名称
数据类型
Block Size
0
块大小
Byte
 
1
 
Byte
   
Byte
Data Values
 
数值
Byte
   
Byte
 
 
 
 
   
Byte
 
 
Byte
 
 
Byte
 
255
 
Byte
, i9 \0 J0 ~- C$ a' U$ o6 i7 ?
; A) k( z% S; Q' ~( p
图10 数据子块的结构
 # o/ M  [; Z+ ?  U6 X& n( ^
7. 图形控制扩展块5 _# M# f# X1 a+ J8 n# r+ [
图形控制扩展块(Graphic Control Extension)包含处理图形描绘块时要使用的参数,它的结构如图11所示。现说明如下:
' B" l4 U7 u: f  p(1) 扩展导入符Extension Introducer)用于识别扩展块的开始,域中的值是一个数值等于0x21的固定值。
# t: w! H! [6 v. K  |4 n& U(2) 图形控制标签(Graphic Control Label)用于标识当前块是一个图形控制扩展块,域中的值是一个数值等于0xF9的固定值。
! Z1 ]# k3 y8 }1 ^(3) 块大小(Block Size)用来说明该扩展块所包含字节数,该字节数是从这个块大小(Block Size)域之后到块结束符之间的字节数。: y$ w) N# f' \0 ?: P$ S: Q
3 m8 G& T# f( b+ I) N4 t5 j
7
6
5
4
3
2
1
0
字节号
域的名称
类型
Extension Introducer
0
扩展导入符
/ `- |6 z. g1 V* P7 E
Byte5 [6 V; X. O/ g
Graphic Control Label
1
图形扩展标签$ i7 f2 ~* f  {  U0 c
Byte
! b3 Z6 v5 V# ~. d
    
Block Size
0
块大小
  b7 N: v( t0 @, S, l: q. y7 c
Byte
  u) p2 H- Q! X6 o' {
<Packed Fields>
1
包装域( Y9 e8 N# |+ j- Q
See below
. A* J  k% R: ]1 Y. r0 ?% P
Delay Time
2
延时时间- \* |, y4 K" A
Unsigned
9 Q! B; [. }+ K
    
Transparent Color Index
3
透明彩色索引2 Z0 o3 S" V' k- E/ O  P9 H7 F
Byte
' u7 M6 q# _; k% I: x7 a  v
    
Block Terminator
0
块结束符
% r- w8 P- a* w% K. L$ H
Byte
7 `9 M5 S# _8 x! C& G
7 z# j) f6 i5 s5 z
图11 图像描述块的结构
(4) 包装域的结构如图6-12所示。处理方法(Disposal Method)规定图形显示之后译码器要用表-03中所述方法进行处理。
2 _) H+ ^( K& s, f* o' c
表-03 包装域规定的处理方法

' h% [# I8 n6 y  q3 S
域值
处理方法
0. `8 k& F' u# x  P5 M2 x- p
没有指定要做任何处理
5 C1 ]$ Q- G* t3 f5 O, o
10 T( v! c* ]- F2 M
不处理,图形留在原处+ L. j: ^/ E* _; P
28 \, ~. [* |0 Z* @
显示图形的区域必须要恢复成背景颜色( a5 n$ z8 B) H! x5 [
31 Q; N/ \: j! ^% G/ I$ Y
恢复成以前显示的图形
; z$ x7 k' P6 a1 [+ c
4~70 l  a1 J/ `( D( W& E& [
(未定义)
$ ?1 d0 i# p& m, ?' _9 x

) y3 B6 T% l4 H" q  S" I用户输入标志(User Input Flag)域表示在继续处理之前是否需要用户输入响应。在延时时间(Delay Time)和用户输入标志(User Input Flag)都设置为1的情况下,继续处理的开始时间取决于用户响应输入在前还是延时时间结束在前。' o/ c0 r9 K: r0 ~4 Q* C
7
6
5
4
3
2
1
0
Reserved(保留)
Disposal Method(处理方法)
User Input Flag
Transparent Color Flag
) ]3 j) L( F* p7 d
图12 图形控制扩展块的包装结构
(5) 透明(Transparency Flag)表示是否给出透明索引(transparency index)
3 t0 m6 a; T1 R" I3 w* k, B8 n(6) 延时时间(Delay Time)用来指定在图形显示之后继续处理数据流之前的等待时间,一百分之一秒为单位。+ Y/ w3 ?. O7 Z  }( w
(7) 当且仅当透明标志位设置为1时,透明索引(Transparency Index)用来指示处理程序是否要修改显示设备上的相应象点。当且仅当透明标志位设置为1时,就要修改。. W3 O* U$ X* I6 b' V1 j3 K
(8) 块结束符(Block Terminator)表示该图形控制扩展块(Graphic Control Extension)结束,它是由一个字节组成的数据块,该域的值是一个固定的值:0x00,因此称为零长度数据子块(zero-length Data Sub-block)。' {* O4 R0 q, g6 |3 X0 r. ?9 \
8. 无格式文本扩展块/ o( {- Y( G: @! H
无格式文本扩展块(Plain Text Extension)包含文本数据和描绘文本所须的参数。文本数据用7位的ASCII字符编码并以图形形式显示。扩展块的结构如图13所示。
! U" E! V& i4 f( k6 m
3 ?, k! h+ y, y- Q+ p
7 6 5 4 3 2 1 0
字节号
域的名称
数据类型
Extension Introducer (0x21)
0
扩展导入符
Byte
Plain Text Label (0x01)
1
无格式文本标签
Byte
4 Y* Y6 Q6 F; I) N' w

: I$ f! @2 [8 g0 O( r
Block Size
0
块大小
Byte
Text Grid Left Position
1
文本网格左列位置
Unsigned
 
2
  
Text Grid Top Position
3
文本网格顶行位置
Unsigned
 
4
  
Text Grid Width
5
文本网格宽度
Unsigned
 
6
  
Text Grid Height
7
文本网格高度
Unsigned
 
8
  
Character Cell Width
9
字符单元宽度
Byte
Character Cell Height
10
字符单元高度
Byte
Text Foreground Color Index
11
文本颜色索引
Byte
Text Background Color Index
12
文本背景颜色索引
Byte

( y5 a% B$ i1 k! c
$ ?% @, F$ B8 Z; R. R9 `! Q) k
    
Plain Text Data
 
无格式文本数据
Data Sub-blocks
    

* Z( u" Y+ X% J! H0 ~; _: ^' B( V  O" N
图13 无格式文本扩展块结构
 * g% t2 s5 T1 w$ G
9. 注释扩展块
3 }! f: A1 i' _- Z- c
注释扩展块(Comment Extension)域的内容用来说明图形、作者或者其他任何非图形数据和控制信息的文本信息。( s- d4 d) h/ R. H! v
注释扩展块的结构如图14所示。其中的注释数据是序列数据子块(Data Sub-blocks),每块最多255个字节,最少1个字节。
9 A  R+ k4 F$ L! `! M' a; A" p

3 L# K, |' s0 d
7 6 5 4 3 2 1 0
字节号
域的名称
数据类型
Extension Introducer (0x21)
0
扩展导入符
Byte
Comment Label (0xFE)
1
注释标签
Byte
. i6 U, A, i$ X+ g7 |( d- n

1 Z; p. f1 a' l* F
Comment Data
0
注释数据
 
   
Data Sub-blocks
 
  
 
N-1
  

- y0 y  r1 x$ O$ t# X) ~& a) R& d( s* M  M
Block Terminator
 
块结束符
Byte
4 M; d+ f; F' n) @
图14 注释扩展块
 * I) x0 r! B& h  T6 z- P
10. 应用扩展块
  Y! h  u0 N! h, k7 g% P, m+ p
应用扩展块(Application Extension)包含制作该图像文件的应用程序的相关信息,它的结构如图15所示。; a2 j5 [. G0 ], K# R- Y& K
; ^3 l$ P" v' [  ~# H, Z4 E
7 6 5 4 3 2 1 0
字节号
域的名称
数据类型
Extension Introducer (0x21)
0
扩展导入符
Byte
Extension Label (0xFF)
1
扩展标签
Byte
# E+ v" Q" g9 f: B0 Q
* D) I8 M  R: ~3 H( \# i
Block Size
0
块大小
Byte
 
1
  
 
2
  
 
3
  
Application Identifier
4
应用程序标识符
8 Bytes
 
5
(程序名称)
 
 
6
  
 
7
  
 
8
  
 
9
  
Appl. Authentication Code
10
应用程序识别码
3 Bytes
 
11
  

' k  x$ }1 E8 g* x* S7 ^" m) J/ i0 x; M( w9 I3 r
    
Application Data
 
应用数据
Data Sub-blocks
    

+ T7 r" h' w0 o5 w0 m3 c6 k- C0 A5 y: y$ a$ ~! V3 d5 Z7 M% x5 d( h
Block Terminator
0
 
Byte
8 _" j6 W0 `" k9 F5 Y
图15 应用扩展块
 ! T7 @+ k- y' T& l! k' X
11. GIF文件结束块
7 h  P! \1 [+ L  e% l
结束块(GIF Trailer)表示GIF文件的结尾,它包含一个固定的数值:0x3B。它具有如图16所示的结构。
4 X! C  u% B% d, g/ U) {1 b
" C& W5 Y+ y+ o. H
7 6 5 4 3 2 1 0
域的名称
数据类型
GIF Trailer = 0x3B
GFI文件结束块
Byte

: Q/ ^6 g% Y( w5 h# W8 o
图16 GIF文件结束块

& x. t5 U5 x" G5 b' Q$ J7 R
4 速差表 5 [/ y. ~! v  @# o
表04 GIF文件格式
1 B8 N- u! |  S8 V+ R
块的名称
需要
标签
扩展
版本号.
Application Extension(应用扩展)4 k1 L$ x# M7 `( u& }1 Y8 ^
Opt. (*)
# T: E# R0 ?+ i2 R2 d
0xFF (255)
$ w- O" \- H" n' S" P
yes- h. g# S) D4 j; N) x8 I; N7 H
89a
. M5 e; S" H9 G/ D
Comment Extension(注释扩展)& {! z( Y$ c7 W' J0 c  K
Opt. (*)
, g! U" b9 R. T
0xFE (254)
, g( m+ o$ M% w* Q3 w3 {) G1 r: ^
yes
/ a0 z' h6 Y. E3 ^5 g
89a
$ G: B+ x2 p/ j+ ~  Y8 U; N2 n
Global Color Table(全局彩色表)
# e8 C% d) v" C$ {8 h) K: q
Opt. (1)7 \5 S- L& f9 f2 Y/ [0 O# A# r; \
none6 M6 _9 S. T8 U+ b
no1 [* S3 L  Q. X0 N1 n
87a% b: K7 b8 ~3 [  v# H& M! h5 s9 d& ]
Graphic Control Extension(图形控制扩展), G7 F8 H8 b; }
Opt. (*)
# b5 u4 U) G: Q$ q3 d/ N
0xF9 (249)
7 s! v& S- u! P  `3 c3 J
yes0 Z* M' K0 t: i8 X
89a( K5 I) b5 w0 T# Y. C5 s* d- N1 q  H
Header(文件头)8 ?) P5 b0 N% s3 N* _$ q( ^- H
Req. (1)
! \! Z' w9 K- _  U
none( T+ }  S' o$ v' P; k% m$ |
no
) p8 `+ J" d  W# n$ x6 C
N/A* L  _+ `( E" d4 s$ Z
Image Descriptor(图像描述)
3 `  Z1 A6 S: J- m9 f, p
Opt. (*)
5 C2 X6 b: I+ h9 A3 s1 |
0x2C (044)( c  f, L/ d) k5 R% q( S- Q4 ~
no( a$ g1 H+ v* C& T
87a (89a)
" X3 |1 n* l- @' `/ p( _* D
Local Color Table(局部彩色表)
3 b5 g+ I( p$ u0 n( F% k6 N
Opt. (*)8 H8 U4 d# d1 l% y+ u  |/ p0 k
none6 ~5 F) _. w  `% E
no
8 F9 m2 Y# ^0 M+ p; D: E7 P& n' r  u8 M
87a% u* f7 G! x+ M! t7 @4 M, A
Logical Screen Descriptor(逻辑屏幕描述块): Z& X5 p4 J" s+ ]( u& s' `
Req. (1)
1 l1 t% E7 v' h$ N- A; Q3 T
none
# a" |! }& O" G: v/ f/ F  C
no
6 V! A! `! V) A
87a (89a)
# V  Y- M9 x# y) N- z0 u% |
Plain Text Extension(无格式文本扩展)
" [& Q" j$ {7 d: y6 i( m
Opt. (*)
; i& Q/ Z  ^7 d& w; c1 {
0x01 (001)8 c! c1 r) ^4 G# B% w, y2 p$ w$ g
yes5 K4 Y1 c0 v& z; h
89a5 ~. m7 n* J+ a$ |
Trailer(文件结束)
/ y) P! T  E6 V# M
Req. (1)
# _. v( i' E/ F' ?$ \6 p+ J
0x3B (059)
4 M2 o4 y( k  B- X" W: g# l
no
. N7 }; K) g1 e) d8 w% D
87a
# V, S0 w3 x" H, Q; p9 g% M' p

+ Q/ I% g% `( Q' N4 v. {Unlabeled Blocks(无标号块)
9 g( {$ m. H8 L, b' Z8 Q

3 u8 s/ v# q: k' }! Q* Y
Header(文件头)9 M8 K9 Y9 m( R. r2 I9 N
Req. (1)# N3 M4 `9 f% \9 g
none' i% L2 g# e  O$ ^3 B! O
no0 H; H5 a/ e" h7 \8 @# |8 ^8 j0 g; @
N/A3 j+ Z% q4 \% x5 i2 |0 B! t- q& t
Logical Screen Descriptor(逻辑屏幕描述块)
! [2 `. _/ b( \  K6 ?
Req. (1)
. m) O! t" v1 f8 \3 Y& b
none: _% k5 |# ]& X0 ^0 S1 K
no2 Z7 t- ^. v" {+ F* Y5 d: S
87a (89a)
5 I; s# G7 B- [3 E2 Z
Global Color Table(全局彩色表)8 g! A! y. {" c" k6 Y! i& A
Opt. (1)
  z( [& L* r$ s1 x+ p
none+ d& w; Y$ U' {
no
9 X+ x" W' ?2 f. I- R
87a
# q& S$ p  {$ r" |& R5 O* _1 |
Local Color Table(局部彩色表)3 e- K; u! L1 `5 h
Opt. (*)9 B$ f$ h6 T' j5 m
none4 e- Q) Z/ }& m
no2 s7 B' ~0 r/ t+ ?; ^
87a
( ~& V, R( Z& B' u# Q

; K( h4 O3 E7 PGraphic-Rendering Blocks(图像描绘块)
3 S6 I3 n7 U4 f8 \( @1 I
- ?/ b) y+ _: M% n7 \) {
Plain Text Extension(无格式文本扩展)
/ h! D8 e, U. u, y+ F
Opt. (*)
9 A5 p* E& Q$ |  a( ]. j# K4 g
0x01 (001)) |+ p3 C6 Q/ `/ W
yes; I( u) z( P8 {% R
89a0 U9 u7 i! m0 j& h" ?) Y
Image Descriptor(图像描述块); B& |" r0 w, F* A
Opt. (*)8 V% E2 a$ m0 m2 `# F, W+ h' z
0x2C (044)0 b7 W9 [  Y2 V3 a- R1 |0 W3 }
no
% f# T9 v, N$ A! h7 U) R, M1 f
87a (89a)  R8 t1 O4 K) w5 `0 ^7 v9 a

) n) u, L8 C0 a* h* r0 a8 dControl Blocks(控制块)1 H: i& _4 b+ \8 o! v5 J
# x7 k3 s, K- k" `) r' e3 y
Graphic Control Extension(图形控制扩展)
4 S& i, u  n4 {+ C- L$ u
Opt. (*)
4 B1 E; ^" I1 [2 l- G4 v* |* m
0xF9 (249)
& h; N+ U! @9 W5 @2 n, N
yes4 C# @; }  w9 z* U7 t' k
89a
3 S) @1 ~1 A' A# @+ {/ D6 W9 J

$ N- f; D7 z, S! {$ ?1 b; xSpecial Purpose Blocks(专用块)
! ^, ?' @1 E+ ]4 Q

2 Z, z9 Z1 B" w" }
Trailer(结束)
, l3 S# q9 j0 O  m8 e, L4 A0 W6 ^
Req. (1)
% |9 H- _5 U  Z
0x3B (059)0 k- \0 W4 Q: r$ e9 y
no# ?- p. A& o' p. \' t. e) U
87a
% y6 {# G- i& ]$ S1 O, \- q/ O
Comment Extension(注释扩展)
+ @, ?; A& g/ j' E5 d# I% U& ^* S
Opt. (*)
! `  U1 D. e7 u& Z9 `' l9 r
0xFE (254)# v; y: ^7 D( N. z
yes
: [& [" A9 T  t9 D
89a
: J$ @% ~' S6 S/ y5 p
Application Extension(应用程序扩展)
' B' t1 |. O; r2 h  D
Opt. (*)
7 Q. H3 W* V# l4 L/ L* j" B3 s/ s' U$ G
0xFF (255)
2 `$ ^/ a9 F' o6 K8 T& b3 H
yes
  {% _$ m# U# b) \- }3 H/ l
89a" j8 T$ S+ f' ?5 }

8 B7 ]4 c4 u. B+ i表中:Req. (1) 表示最多出现一次
9 E; d7 H' _  sOpt. (*) 出现次数大于等于0
) A- U% y- q: H4 C9 p( j: K1 U
 楼主| 发表于 2009-3-16 20:28 | 显示全部楼层
1.概述6 V; I0 z$ v4 I' q
~~~~~~~~
  GIF(Graphics Interchange Format,图形交换格式)文件是由 CompuServe公司开发的图形文件格式,版权所有,任何商业目的使用均须 CompuServe公司授权。6 Q/ y  P0 M  j2 {6 G, J
  GIF图象是基于颜色列表的(存储的数据是该点的颜色对应于颜色列表的索引值),最多只支持8位(256色)。GIF文件内部分成许多存储块,用来存储多幅图象或者是决定图象表现行为的控制块,用以实现动画和交互式应用。GIF文件还通过LZW压缩算法压缩图象数据来减少图象尺寸(关于LZW算法和GIF数据压缩>>...)。
2.GIF文件存储结构# `, D7 [$ q4 k6 Q# u- b
~~~~~~~~~~~~~~~~~~~
  GIF文件内部是按块划分的,包括控制块( Control Block )和数据块(Data Sub-blocks)两种。控制块是控制数据块行为的,根据不同的控制块包含一些不同的控制参数;数据块只包含一些8-bit的字符流,由它前面的控制块来决定它的功能,每个数据块大小从0到255个字节,数据块的第一个字节指出这个数据块大小(字节数),计算数据块的大小时不包括这个字节,所以一个空的数据块有一个字节,那就是数据块的大小0x00。下表是一个数据块的结构:& Z; G# w9 H5 N; W# Y
BYTE76543210BIT
0
块大小
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文件的组成结构:7 p4 q) g' T, G4 l5 \% g
GIF署名文件头
版本号
逻辑屏幕标识符GIF数据流
全局颜色列表
...
图象标识符图象块                             
图象局部颜色列表图
                           基于颜色列表的图象数据
...
GIF结尾文件结尾
  下面就具体介绍各个部分:
文件头部分(Header)4 _8 ^7 n5 l* {8 I  W) V8 Y, U
~~~~~~~~~~~~~~~~~
GIF署名(Signature)和版本号(Version)# e) W% r2 `( o/ i
~~~~~~~~~~~~~~~~~~~~~~~~~~~: z4 d. K7 U# B5 @! N* T! }$ U5 s
GIF署名用来确认一个文件是否是GIF格式的文件,这一部分由三个字符组成:"GIF";文件版本号也是由三个字节组成,可以为"87a"或"89a".具体描述见下表:
BYTE76543210BIT
1'G'GIF文件标识
2'I'
3'F'
4'8'GIF文件版本号:87a - 1987年5月9 k, Q$ W/ `! ?, ~: M# _( J
        89a - 1989年7月
5'7'或'9'
6'a'
GIF数据流部分(GIF Data Stream)
4 B& S+ c) s1 s/ v~~~~~~~~~~~~~~~~~~~~~~~~~~~~
逻辑屏幕标识符(Logical Screen Descriptor)
/ ~( p& G9 e& o/ K" Q- n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~5 B2 C$ ^/ L0 V% q
这一部分由7个字节组成,定义了GIF图象的大小(Logical Screen Width & Height)、颜色深度(Color Bits)、背景色(Blackground Color Index)以及有无全局颜色列表(Global Color Table)和颜色列表的索引数(Index Count),具体描述见下表:
BYTE76543210BIT
1逻辑屏幕宽度像素数,定义GIF图象的宽度
2
3逻辑屏幕高度像素数,定义GIF图象的高度
4
5mcrspixel具体描述见下...
6背景色背景颜色(在全局颜色列表中的索引,如果没有全局颜色列表,该值没有意义)
7像素宽高比像素宽高比(Pixel Aspect Radio)
m - 全局颜色列表标志(Global Color Table Flag),当置位时表示有全局颜色列表,pixel值有意义.
5 L" d7 T1 x5 \  C0 p3 Mcr - 颜色深度(Color ResoluTion),cr+1确定图象的颜色深度., N. [, }: i# c! w6 o
s - 分类标志(Sort Flag),如果置位表示全局颜色列表分类排列.
% J. @& ?+ T- D% H  ~' Q# @+ Xpixel - 全局颜色列表大小,pixel+1确定颜色列表的索引数(2的pixel+1次方)
.
全局颜色列表(Global Color Table)/ h1 x1 c( o* _, Z7 g" {; x  Q6 E
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
& K2 [# {8 N8 Q7 s0 L( G全局颜色列表必须紧跟在逻辑屏幕标识符后面,每个颜色列表索引条目由三个字节组成,按R、G、B的顺序排列。
BYTE76543210BIT
1索引1的红色值
2索引1的绿色值
3索引1的蓝色值
4索引2的红色值
5索引2的绿色值
6索引2的蓝色值
7...                            
图象标识符(Image Descriptor)) r' n2 r6 [& R/ p! q
~~~~~~~~~~~~~~~~~~~~~~~~~2 W' L, j- c7 s
一个GIF文件内可以包含多幅图象,一幅图象结束之后紧接着下是一幅图象的标识符,图象标识符以0x2C(',')字符开始,定义紧接着它的图象的性质,包括图象相对于逻辑屏幕边界的偏移量、图象大小以及有无局部颜色列表和颜色列表大小,由10个字节组成:

4 {+ k0 o& a) y+ K7 U6 ]
BYTE76543210BIT
100101100图象标识符开始,固定值为','
2X方向偏移量必须限定在逻辑屏幕尺寸范围内
3
4Y方向偏移量
5
6图象宽度
7
8图象高度
9
10misrpixelm - 局部颜色列表标志(Local Color Table Flag)
置位时标识紧接在图象标识符之后有一个局部颜色列表,供紧跟在它之后的一幅图象使用;值否时使用全局颜色列表,忽略pixel值。! ?/ r  Q& \0 S" n7 I& U
i - 交织标志(Interlace Flag),置位时图象数据使用交织方式排列(详细描述...),否则使用顺序排列。, g. e: w+ Q) O8 g5 S/ Q! ~
s - 分类标志(Sort Flag),如果置位表示紧跟着的局部颜色列表分类排列.
3 b) }( M+ M5 F- P- s' e
r - 保留,必须初始化为0.
& ?/ `- t8 X3 W1 C/ t6 Tpixel - 局部颜色列表大小(Size of Local Color Table),pixel+1就为颜色列表的位数
局部颜色列表(Local Color Table)8 K' I0 a4 f7 b) [3 }
~~~~~~~~~~~~~~~~~~~~~~~~~~8 [3 {9 ?# ?. O3 U1 [: {5 A- k1 y
如果上面的局部颜色列表标志置位的话,则需要在这里(紧跟在图象标识符之后)定义一个局部颜色列表以供紧接着它的图象使用,注意使用前应线保存原来的颜色列表,使用结束之后回复原来保存的全局颜色列表。如果一个GIF文件即没有提供全局颜色列表,也没有提供局部颜色列表,可以自己创建一个颜色列表,或使用系统的颜色列表。局部颜色列表的排列方式和全局颜色列表一样:RGBRGB......
) k1 y; `3 q; _1 Q* f/ w5 j基于颜色列表的图象数据(Table-Based Image Data)
0 P, O  V3 [2 W2 F
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~# P1 r( d/ t: h+ G5 P" [3 ]- L& ~
由两部分组成:LZW编码长度(LZW Minimum Code Size)和图象数据(Image Data)。
7 M; _+ N3 a( R9 v6 t
BYTE76543210BIT
1LZW编码长度LZW编码初始码表大小的位数,详细描述见LZW编码...
  1 @( }) c! d( t6 H" n
 

# N, r" K6 H# m...7 n& m1 @. r2 a' F! ~& }5 |
图象数据,由一个或几个数据块(Data Sub-blocks)组成

6 T9 W3 M/ ]7 ^# `5 c$ o- `数据块
, q8 {3 E% _$ q6 [/ R% L
+ S3 T" z# w4 n9 C/ X. Q4 e8 v. Z0 ?
...& @1 l' P9 z; u. |! o! T
GIF图象数据使用了LZW压缩算法(详细介绍请看后面的『LZW算法和GIF数据压缩』),大大减小了图象数据的大小。图象数据在压缩前有两种排列格式:连续的和交织的(由图象标识符的交织标志控制)。连续方式按从左到右、从上到下的顺序排列图象的光栅数据;交织图象按下面的方法处理光栅数据:
. k* ^( [7 z  w& U( G- Y创建四个通道(pass)保存数据,每个通道提取不同行的数据:& Z8 M8 K& X) N% n0 e7 I1 a
第一通道(Pass 1)提取从第0行开始每隔8行的数据;
2 I1 k9 Z3 C/ E& q第二通道(Pass 2)提取从第4行开始每隔8行的数据;; o0 l: ?+ E, T4 b1 A8 W
第三通道(Pass 3)提取从第2行开始每隔4行的数据;+ F$ z8 P2 T2 g" j
第四通道(Pass 4)提取从第1行开始每隔2行的数据;

" b% z/ \7 Q9 \6 t下面的例子演示了提取交织图象数据的顺序:# P2 E! E5 B$ ?5 X. Z
 通道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
 0 @: C( G6 R& O0 N$ Y# U" c
图形控制扩展(Graphic Control Extension)3 u% M! h* |5 ?, I, t
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~& G0 r; ?% `% w
这一部分是可选的(需要89a版本),可以放在一个图象块(图象标识符)或文本扩展块的前面,用来控制跟在它后面的第一个图象(或文本)的渲染(Render)形式,组成结构如下:
: o0 Z) X7 ]( ~! _# k# |
BYTE76543210BIT
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):指出处置图形的方法,当值为:
$ i* q' U/ x/ Q( S5 o, s- ?& r: d                        0 - 不使用处置方法; `( w5 T- d$ t# ^
                        1 - 不处置图形,把图形从当前位置移去
+ k3 U- |; h; F( F7 C* X                        2 - 回复到背景色
: M2 D4 r. {" F" g( V                        3 - 回复到先前状态" y6 R! s+ \+ V8 J; @& [8 t
                      4-7 - 自定义
3 y4 {/ E) d/ z0 T5 O7 i& J0 D6 Z/ J用户输入标志(Use Input Flag):指出是否期待用户有输入之后才继续进行下去,置位表示期待,值否表示不期待。用户输入可以是按回车键、鼠标点击等,可以和延迟时间一起使用,在设置的延迟时间内用户有输入则马上继续进行,或者没有输入直到延迟时间到达而继续
0 q% }  |7 [* P3 k* Y  h: Q3 p透明颜色标志(Transparent Color Flag):置位表示使用透明颜色

4 m6 `3 J+ O' v' {7 F6 a$ Z" V注释扩展(Comment Extension)
+ P' D/ m$ x  [
~~~~~~~~~~~~~~~~~~~~~~~~~~~4 `) v# g5 q) `
这一部分是可选的(需要89a版本),可以用来记录图形、版权、描述等任何的非图形和控制的纯文本数据(7-bit ASCII字符),注释扩展并不影响对图象数据流的处理,解码器完全可以忽略它。存放位置可以是数据流的任何地方,最好不要妨碍控制和数据块,推荐放在数据流的开始或结尾。具体组成:
5 h  D- A$ T" n$ n# a& P& d
BYTE76543210BIT
1扩展块标识Extension Introducer - 标识这是一个扩展块,固定值0x21
2注释块标签Comment Label - 标识这是一个注释块,固定值0xFE
" u8 H+ p7 W" s; p( n' B! b
...
) a! ?; R' f6 s* h# w
Comment Data - 一个或多个数据块(Data Sub-Blocks)组成

. d2 u% `* x6 t9 ]# E注释块. N% I/ O+ z, R) J' Y

! A; ]) B0 `( h' |1 J; N...
' T' w. J7 P2 z& V0 @$ w
块终结器Block Terminator - 标识注释块结束,固定值0
图形文本扩展(Plain Text Extension)& V6 s) {  {  c( |, M$ o- ^. S) R
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
9 S: p0 h, X7 {) z' p; T
这一部分是可选的(需要89a版本),用来绘制一个简单的文本图象,这一部分由用来绘制的纯文本数据(7-bit ASCII字符)和控制绘制的参数等组成。绘制文本借助于一个文本框(Text Grid)来定义边界,在文本框中划分多个单元格,每个字符占用一个单元,绘制时按从左到右、从上到下的顺序依次进行,直到最后一个字符或者占满整个文本框(之后的字符将被忽略,因此定义文本框的大小时应该注意到是否可以容纳整个文本),绘制文本的颜色索引使用全局颜色列表,没有则可以使用一个已经保存的前一个颜色列表。另外,图形文本扩展块也属于图形块(Graphic Rendering Block),可以在它前面定义图形控制扩展对它的表现形式进一步修改。图形文本扩展的组成:
/ s8 `. M5 G* D8 \; I
. b5 Y4 i8 R7 c
BYTE76543210BIT
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
1 s$ S2 P* z& @6 q...
( r5 `7 D. I4 c: r5 b
Plain Text Data - 一个或多个数据块(Data Sub-Blocks)组成,保存要在显示的字符串。
# ?$ M4 S1 ?* h4 b+ F+ x
文本数据块. S9 {5 c/ W$ B- z) t  G
  p7 s2 v7 s* a8 Z$ f" ?$ r+ e
...
* L8 f: j8 W8 _- V" w
N+1块终结Block Terminator - 标识注释块结束,固定值0
推荐:1.由于文本的字体(Font)和尺寸(Size)没有定义,解码器应该根据情况选择最合适的;% P( g! d. W8 G* H
2.如果一个字符的值小于0x20或大于0xF7,则这个字符被推荐显示为一个空格(0x20);
  \% P! ^& E% w& p5 G4 ^' X6 f3.为了兼容性,最好定义字符单元格的大小为8x8或8x16(宽度x高度)

) W+ R4 h% K% o0 H应用程序扩展(Application Extension)$ \" u  L& ?' v& e" B* B/ q8 A8 k
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

. q, U. }5 k7 g* g4 o! s这是提供给应用程序自己使用的(需要89a版本),应用程序可以在这里定义自己的标识、信息等,组成:+ L3 K* l* w# y8 E. H4 I$ e2 p" w

9 \) T$ K4 v8 i$ o5 Q- O. o! z
BYTE76543210BIT
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
+ P. z7 I$ U5 ?( e0 }...4 L' ]3 k% e9 P0 {4 ?. T1 a
应用程序自定义数据块 - 一个或多个数据块(Data Sub-Blocks)组成,保存应用程序自己定义的数据

) y+ |7 e0 {- S2 {应用程序数据" @- Q, g" p) h' v$ U; a

/ M3 `/ ]4 n5 M. Q: Q..., X$ g" h" D2 l$ l% @/ |. y2 W
N+1块终结器lock Terminator - 标识注释块结束,固定值0
文件结尾部分
, T- v; u2 y9 |5 K# h~~~~~~~~~~~

0 u3 S: Z* Y  ]文件终结器(Trailer)( n* H+ B! m  Z+ I- E/ Z
~~~~~~~~~~~~~~~~
5 |: V% T3 {7 Z3 n  b
这一部分只有一个值为0的字节,标识一个GIF文件结束.
4 C$ d  d- r% L/ L9 J
4 y( F" A& |4 A5 {6 D3 C5 K: x, s3 b
BYTE76543210
1
文件终结
GIF Trailer - 标识GIF文件结束,固定值0x3B
2.LZW算法和GIF数据压缩
% {8 `. K  v; w% X
~~~~~~~~~~~~~~~~~~~~~~~~~~~
  GIF文件的图象数据使用了可变长度编码的LZW压缩算法(Variable-Length_Code LZW Compression),这是从LZW(Lempel Ziv Compression)压缩算法演变过来的,通过压缩原始数据的重复部分来达到减少文件大小的目的。
9 Y) n% R& B9 L4 }0 W/ v9 G3 b标准的LZW压缩原理:
  D& O0 b7 C6 X" `& T/ k& j5 B/ e~~~~~~~~~~~~~~~~~~
( \4 o3 V& f! f( b先来解释一下几个基本概念:2 u+ C# E, |& e3 f, x( U
  LZW压缩有三个重要的对象:数据流(CharStream)、编码流(CodeStream)和编译表(String Table)。在编码时,数据流是输入对象(图象的光栅数据序列),编码流就是输出对象(经过压缩运算的编码数据);在解码时,编码流则是输入对象,数据流是输出对象;而编译表是在编码和解码时都须要用借助的对象。
. \4 N( f1 Q7 j5 [6 ?7 V  n: L) V  O% ^* {7 v. H# i' C
字符(Character):最基础的数据元素,在文本文件中就是一个字节,在光栅数据中就是一个像素的颜色在指定的颜色列表中的索引值

# {" w1 ^: p8 T) b( y* |* k6 ?字符串(String):由几个连续的字符组成;
$ P& A7 _+ W, `. @4 \9 x5 `) O9 A前缀(Prefix):也是一个字符串,不过通常用在另一个字符的前面,而且它的长度可以为0;8 Q. Z. h0 ^6 y! z9 `: j
(Root):单个长度的字符串;& [9 Q& j4 `! \: ~! [! I
编码(Code):一个数字,按照固定长度(编码长度)从编码流中取出,编译表的映射值;& M9 o. s: }( i! ?8 a
图案:一个字符串,按不定长度从数据流中读出,映射到编译表条目.
% b' j+ |: r1 o! C( u- m+ D
  LZW压缩的原理:提取原始图象数据中的不同图案,基于这些图案创建一个编译表,然后用编译表中的图案索引来替代原始光栅数据中的相应图案,减少原始数据大小。看起来和调色板图象的实现原理差不多,但是应该注意到的是,我们这里的编译表不是事先创建好的,而是根据原始图象数据动态创建的,解码时还要从已编码的数据中还原出原来的编译表(GIF文件中是不携带编译表信息的),为了更好理解编解码原理,我们来看看具体的处理过程:7 C& `4 X7 l, F8 i
编码器(Compressor)
7 {" {' U9 ?; u9 h~~~~~~~~~~~~~~~~
# l& o- ?* i: \- K$ ~/ T! \0 b) V* Q
  编码数据,第一步,初始化一个编译表,假设这个编译表的大小是12位的,也就是最多有4096个单位,另外假设我们有32个不同的字符(也可以认为图象的每个像素最多有32种颜色),表示为a,b,c,d,e...,初始化编译表:第0项为a,第1项为b,第2项为c...一直到第31项,我们把这32项就称为根。
9 q8 a- P: F1 s' k$ G* V+ P  开始编译,先定义一个前缀对象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 ,这样一直循环下去直到数据流结束。伪代码看起来就像下面这样:
1 j8 |' \5 x5 Y, H+ l5 l; k
编码器伪代码
Initialize String Table;" s8 b; i6 j1 Q, q3 o: a
[.c.] = Empty;
6 b" W; R- C& ?  h& ~[.c.]k = First Character in CharStream;
  t* }" }) k8 l2 s) `while ([.c.]k != EOF )
% F2 I) }+ W, z1 a% e4 ]& C% G8 C{& W# T7 Z+ W# B# B
  if ( [.c.]k is in the StringTable)" G, e4 e* A5 @5 S. K1 R
  {
: o/ y- c& a; C" Z6 L) R: p    [.c.] = [.c.]k;8 G. o0 V' y- c+ F
  }: @: B& d% k5 f  l. U; K& L" Q$ d
  else
, V1 ~' w  ?1 _& o8 D; D' t; i  {
; r( N, m2 S, a6 ^( a! R    add [.c.]k to the StringTable;' ^. M/ Z5 T  `
    Output the Index of [.c.] in the StringTable to the CodeStream;
$ ]0 }+ d3 z) N( o, Q0 a    [.c.] = k;: T# x( [) b. e0 J3 I
  }" Q6 i7 F) j0 C" w+ _( m3 Q' D: I! W
  [.c.]k = Next Character in CharStream;7 t1 p3 L) r2 r; A! }- T  T
}* u; }+ C- |8 p3 X
Output the Index of [.c.] in the StringTable to the CodeStream;
* ~/ o* a' n- z+ |9 M* S! S
来看一个具体的例子,我们有一个字母表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.
+ i- t8 e, A! t: s. t解码器(Decompressor)
3 K2 c" t: K4 g) e1 k' z% h~~~~~~~~~~~~~~~~~~
% p' H, o& i! b0 D6 ~+ q
  好了,现在来看看解码数据。数据的解码,其实就是数据编码的逆向过程,要从已经编译的数据(编码流)中找出编译表,然后对照编译表还原图象的光栅数据。
2 E! `$ T4 ?2 ]' y9 |: _  首先,还是要初始化编译表。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。事实上,这种猜测法是很准确(有点不好理解,仔细想一想吧)。上面的解码过程用伪代码表示就像下面这样:

: K, D1 ]0 W) I: q+ I! W3 k解码器伪代码
Initialize String Table;# ]% h- T: d- a$ L) ?; e
[code] = First Code in the CodeStream;: v9 g* u1 _9 J+ c
Output the String for [code] to the CharStream;2 x/ f  z$ x5 x' n5 O) ]% O5 I
[old] = [code];# M% D# b5 p+ d4 d
[code] = Next Code in the CodeStream;
. ?( C; S: g& R  E0 Fwhile ([code] != EOF ); f) ^4 Y6 i- {2 H0 y* b
{
/ Z3 U# |% }' E4 w  if ( [code] is in the StringTable)
7 y3 q# ^7 Q! i% ?9 c8 b7 w6 N' v9 o  {  ?4 o1 P1 s3 r, E
    Output the String for [code] to the CharStream; // 输出[code]所对应的字符串$ r( |9 V- m3 J) M. N& M. o
    [...] = translation for [old]; // [old]所对应的字符串4 U& N/ |2 T& F- v+ B% [: J
    k = first character of translation for [code]; // [code]所对应的字符串的第一个字符( d9 I2 ]( c/ {' Z1 J( g
    add [...]k to the StringTable;" Z6 d: i* L. f) Y
    [old] = [code];
, ?3 m9 H9 r$ q0 W! v  f0 b8 W  }/ m6 T2 J% Y# W! ^1 Y1 ^
  else
+ [& E+ L/ k) B: [9 k: b! t; B  l" u  {
9 E+ v  }7 H9 K! Q
    [...] = translation for [old]; ; k1 g+ a0 A: D0 w# S% j
    k = first character of [...]; 7 E' A4 B3 ]7 M5 ^
    Output [...]k to CharStream;0 R& o2 t% u4 c: ]- u1 h
    add [...]k to the StringTable;
% W/ d4 o: a* y0 B1 ?
    [old] = [code]; 8 [: I1 L3 ]/ q5 E! w7 |
  }
% z/ S4 H: h; B5 a" V  S& a% P  [code] = Next Code in the CodeStream;
- D5 P4 J. P& H7 O4 F}
9 I. H6 K* d2 K8 z5 o" i6 r5 Y
GIF数据压缩; D: e1 W# j# |5 S
~~~~~~~~~~~

% |' b2 P9 q. ?6 p! _) K下面是GIF文件的图象数据结构:
4 J% \: M- |* u7 Q$ E

9 B9 k' s+ p/ O' @( n
BYTE76543210BIT
1
编码长度
LZW Code Size - LZW压缩的编码长度,也就是要压缩的数据的位数
...数据块
块大小数据块,如果需要可重复多次
编码数据
...数据块
块终结器一个图象的数据编码结束,固定值0
把光栅数据序列(数据流)压缩成GIF文件的图象数据(字符流)可以按下面的步骤进行:! x5 A3 R7 g" u; s+ m" n) v
1.定义编码长度  E% i2 o) g: B2 V- e
GIF图象数据的第一个字节就是编码长度(Code Size),这个值是指要表现一个像素所需要的最小位数,通常就等于图象的色深;
0 i( Y& r' K# k2.压缩数据
+ F; W% `+ _. s4 [8 Y( u9 r! b通过LZW压缩算法将图象的光栅数据流压缩成GIF的编码数据流。这里使用的LZW压缩算法是从标准的LZW压缩算法演变过来的,它们之间有如下的差别:- k) Y( k% z9 u$ r
  [1]GIF文件定义了一个编码大小(Clear Code),这个值等于2的『编码长度』次方,在从新开始一个编译表(编译表溢出)时均须输出该值,解码器遇到该值时意味着要从新初始化一个编译表;
( I* f# z! R3 z6 B* {' M% Q  [2]在一个图象的编码数据结束之前(也就是在块终结器的前面),需要输出一个Clear Code+1的值,解码器在遇到该值时就意味着GIF文件的一个图象数据流的结束;7 t. V$ Q* ~2 m9 u
  [3]第一个可用到的编译表索引值是Clear Code+2(从0到Clear Code-1是根索引,再上去两个不可使用,新的索引从Clare Code+2开始添加);1 x* N* s/ J9 I6 k
  [4]GIF输出的编码流是不定长的,每个编码的大小从Code Size + 1位到12位
编码的最大值就是4095(编译表需要定义的索引数就是4096),当编码所须的位数超过当前的位数时就把当前位数加1,这就需要在编码或解码时注意到编码长度的改变。
3 t8 V& D; p: |1 E) T3.编译成字节序列
3 C# t. f% S. |" @因为GIF输出的编码流是不定长的,这就需要把它们编译成固定的8-bit长度的字符流,编译顺序是从右往左。下面是一个具体例子:编译5位长度编码到8位字符
/ E7 h/ P4 C2 |$ X8 ~
0bbbaaaaa
1dcccccbb
2eeeedddd
3ggfffffe
4hhhhhggg
...
N
 
( d+ F$ E& M% E4 @8 I4.打包
4 J% j: W5 g: S. f6 [, Q8 ~4 N  前面讲过,一个GIF的数据块的大小从0到255个字节,第一个字节是这个数据块的大小(字节数),这就需要将编译编后的码数据打包成一个或几个大小不大于255个字节的数据包。然后写入图象数据块中。
 楼主| 发表于 2009-4-13 20:46 | 显示全部楼层
研究了好一段时间了终于明白了8成的原理了!这个东西可能没有多少人会看上的可能太复杂的吧呵呵,没所谓吧反正这个以后可以自己重温一下!下面是这个的C语言:8 ?  B8 N, `6 U+ @4 B4 i

; D4 N: ?7 S8 `* P6 ^
$ k, f+ O! ^( K: d9 X. S/* DECODE.C - An LZW decoder for GIF. i) j  `2 z' z4 Z9 a0 R
* Copyright (C) 1987, by Steven A. Bennett6 x" p3 b( u+ K& l+ G/ A
*  {+ U* O, b) I% Y5 d
* Permission is given by the author to freely redistribute and include! }0 C# S  e# Z( R
* this code in any program as long as this credit is given where due.# i  n' w4 N2 _$ e1 m
*
: a/ P( N1 r; x1 _* Q5 h * In accordance with the above, I want to credit Steve Wilhite who wrote
6 N4 k/ l$ k- C" v * the code which this is heavily inspired by...3 g4 t# c* t; e+ B0 I
*% Z0 c* T7 Z- ^# B, R
* GIF and 'Graphics Interchange Format' are trademarks (tm) of
+ {, u5 k/ v6 s * Compuserve, Incorporated, an H&R Block Company.2 q4 X- Q8 B/ B/ N) z
*
) K% H5 m9 L. s * Release Notes: This file contains a decoder routine for GIF images
* t) a# p- M* O  C8 C$ I * which is similar, structurally, to the original routine by Steve Wilhite.
* B6 t1 Z3 A$ G+ L * It is, however, somewhat noticably faster in most cases.
( V( r7 w: x2 E( f; D$ R, ?7 I& q *0 \1 `4 l1 J& [$ B5 I% g' b& ^- R
*/  l; p, B1 }! @' W: v+ S0 j

4 s, u3 F/ t  l9 v#include "std.h". Q& I0 A4 E" i1 j3 D; Y
#include "errs.h"
% N! q" b" K: V: d
, F! L; p4 X* L, O% f0 QIMPORT TEXT *malloc();                 /* Standard C library allocation */' q4 S; j4 J: l3 J, e$ t

% o# E  [& u+ f- N/* IMPORT INT get_byte(): B( l7 c1 B: V; m- N$ V) U
*$ c, M3 T2 s1 W) q3 t3 m/ L
*   - This external (machine specific) function is expected to return
' x$ L0 B6 A: M, O! x) g; f * either the next byte from the GIF file, or a negative number, as
: |8 p- {  t, _+ y6 ?* K3 |3 P * defined in ERRS.H.
, T, [% U. {. U% \ */
  I: W7 G' H7 n- |* WIMPORT INT get_byte();
, L0 m- I6 I3 w. j% L) ?0 A( r5 v, W4 _
/* IMPORT INT out_line(pixels, linelen)
" b8 M4 ]; _: P5 Z* r. L: n; Y *     UBYTE pixels[];. }% P! r0 D7 E; {
*     INT linelen;
4 ]4 s7 X' H5 [7 o* X6 R% a' ]' | *
" i+ b9 |% S8 ]! F/ d3 L; V, E- A+ [ *   - This function takes a full line of pixels (one byte per pixel) and
' i/ u4 a! Z7 x * displays them (or does whatever your program wants with them...).  It
6 G$ Y6 c# l% c * should return zero, or negative if an error or some other event occurs
* G, P, d* |2 O  }: u7 S  P * which would require aborting the decode process...  Note that the length
- t* o4 @; x- W% B1 E9 g * passed will almost always be equal to the line length passed to the; q2 E- r0 S1 f/ l0 k- z  V5 ^0 J
* decoder function, with the sole exception occurring when an ending code. e3 `* O2 I9 Y4 c" L1 _
* occurs in an odd place in the GIF file...  In any case, linelen will be
" U' z& t& c% z# G% S * equal to the number of pixels passed...
, d" Y- _* R/ |2 C+ I, ^ */
5 A4 b& u1 b7 E- CIMPORT INT out_line();
$ k0 l) ?. t2 B
; R* i4 M7 v/ _, @: T( Z+ `/* IMPORT INT bad_code_count;+ s7 h- {& J' Z' a& Q+ R3 ^0 z0 O
*
" b* f6 m2 |) @: o * This value is the only other global required by the using program, and& u( \: X! o* ^, v
* is incremented each time an out of range code is read by the decoder.
3 V& |7 K& @: n# b( [ * When this value is non-zero after a decode, your GIF file is probably
5 T( h$ Z0 H0 ?/ J6 ^0 q2 i * corrupt in some way.... s8 c: Y: q+ x9 C# f9 W/ \
*/% _: Q0 Y+ U4 Y1 D1 b) D
IMPORT INT bad_code_count;
6 C% t1 z6 r4 ?' [4 p
) n9 f8 n4 @$ s" k#define NULL   0L
* p# i0 p- {6 x5 e% ^9 g5 Q#define MAX_CODES   40959 ?4 C& U" j' b# Q- T! _
, p! ]  ]! F, E5 ], @6 w
/* Static variables */
" \4 h' R. ?# r% j/ fLOCAL WORD curr_size;                     /* The current code size */
' V. Y! c: {6 _LOCAL WORD clear;                         /* Value for a clear code */2 a* l6 P. d9 u% @2 p% J2 i
LOCAL WORD ending;                        /* Value for a ending code */
9 Y5 J. L& L: d  N0 r$ Z5 |6 w7 FLOCAL WORD newcodes;                      /* First available code */- H% f& b1 B$ r5 |+ W, x
LOCAL WORD top_slot;                      /* Highest code for current size */$ Q) b! v( n. b- o. o
LOCAL WORD slot;                          /* Last read code */. ]4 C: ^6 ~2 \7 A3 E/ z! M
4 ^4 T6 v  j* t7 ~* H# j
/* The following static variables are used4 M; p$ a3 \; h. r  d$ z
* for seperating out codes& p. I1 j' r; G# X. a
*/
7 T3 ^) Q) x& HLOCAL WORD navail_bytes = 0;              /* # bytes left in block */
1 g5 }3 K2 r" D; m! d& t/ ^+ ]LOCAL WORD nbits_left = 0;                /* # bits left in current byte */
. {  b6 ]' G, l! _! N0 uLOCAL UTINY b1;                           /* Current byte */: j3 y/ b6 W: Q+ B
LOCAL UTINY byte_buff[257];               /* Current block */
, S( H3 ]) n: ELOCAL UTINY *pbytes;                      /* Pointer to next byte in block */$ ]6 S5 r' H% _% Z8 \
. t' p" h" a1 f
LOCAL LONG code_mask[13] = {
- \# E# h0 ~' x3 S2 F3 C     0,
* T+ e# p3 G, t' f5 u( Z     0x0001, 0x0003,& ^1 o( \  o2 b. J2 A
     0x0007, 0x000F,
% o- S: {0 O& V0 u' }" @$ ]     0x001F, 0x003F,
6 Q1 l9 a5 f6 ]9 L! q& q! z& [     0x007F, 0x00FF,
( n! j! b+ b' U/ L3 p: s9 V     0x01FF, 0x03FF,0 p, u) w6 o% q* J
     0x07FF, 0x0FFF! A: ]( q5 |$ _. P8 y6 U
     };  W* ?: X2 C! r1 g7 ~7 j2 s; S; v

! P- d- T7 r+ v3 f4 m1 Y: |5 Y
8 ?2 [2 v3 P3 z  n/* This function initializes the decoder for reading a new image.
9 ]- q2 c" f! C3 W( A */
8 f- i, w. B! K0 r+ WLOCAL WORD init_exp(size)
# ~1 v& t4 [& o+ |9 M( f2 f1 U   WORD size;
- U* \5 q$ W7 |! {" {; m! b1 c   {
  B  \0 C6 c! g- K9 s. a0 @3 x2 y0 M0 `9 @   curr_size = size + 1;
( ^. c( y) E# d1 G" c4 g0 B! B. G   top_slot = 1 << curr_size;' U2 X! h$ g9 k* X$ c( I0 n
   clear = 1 << size;
. `. C5 X% W9 y% T3 L7 `   ending = clear + 1;/ \; A! r1 F, m( J1 O' q% }# E
   slot = newcodes = ending + 1;* }4 a, u& C  E0 X+ z9 \
   navail_bytes = nbits_left = 0;
' |; o# V, C6 K9 |. D: k   return(0);
/ D) a- a: O  ?, x: |" I   }
% X% X- `( z6 I" @5 J2 r2 b. v# [! s4 ]0 F1 a
/* get_next_code()
, d5 q& Z: o$ D% I1 k' ^  X * - gets the next code from the GIF file.  Returns the code, or else% X, O' l, U& M5 X9 v, k
* a negative number in case of file errors...
" J! d& X/ E+ [5 q6 p */! c. h" T2 R% o
LOCAL WORD get_next_code()
, r: y0 K: h9 p- C# T   {- x& z4 R7 R4 y( j& o+ g
   WORD i, x;
+ t) R1 H4 q6 v3 [6 p7 e   ULONG ret;
/ [6 F8 i' @, Y0 g+ `8 c2 [% p' j/ x: t2 F
   if (nbits_left == 0): [* \. m5 z2 a0 x0 m& G
      {
  ?0 J# N7 ^6 E: o' c      if (navail_bytes <= 0)
0 x) b! X; G7 }( I2 K         {
4 G, {: N! ^$ B
$ s' S5 p1 X1 W         /* Out of bytes in current block, so read next block
" G$ a; D6 H1 m5 ^' q          */: ^( I: o+ V6 V: {
         pbytes = byte_buff;
, q! d- W  d: z         if ((navail_bytes = get_byte()) < 0)) U; ]3 c/ _2 J2 }
            return(navail_bytes);
- e" a& S6 {: U& q" j         else if (navail_bytes)3 v, Y' y0 Q$ G( u& h9 x
            {
( R2 a; v! d4 I' D            for (i = 0; i < navail_bytes; ++i)7 o6 o) `2 ^+ D
               {: o0 g  ~5 b) ~3 J3 ^+ G
               if ((x = get_byte()) < 0), E/ }# e0 c5 m- u
                  return(x);
; q$ B! E" j8 {# U1 x* ]               byte_buff = x;1 g+ _( e& {; D4 [
               }. H+ V" ?* N, O, \7 {" s
            }' l9 K. [+ |) K( n7 P* V& {
         }
" a. p. _6 B/ V- h      b1 = *pbytes++;# q+ \+ D& Q2 C8 A7 K
      nbits_left = 8;2 s4 D3 _. f' z, M% g# V  V9 l' b$ [
      --navail_bytes;
- \) |% j! e8 k$ K1 ?! }      }
2 ^7 G, k3 q1 @, l; v7 o& k' Y0 a$ V7 |; q3 Y
   ret = b1 >> (8 - nbits_left);
9 [: ]4 x. ?2 u8 z, L: ]: Z   while (curr_size > nbits_left)
5 S+ f1 y( t+ [" ~  C4 Q      {
$ Q7 i3 j6 n" G: b3 Y! `      if (navail_bytes <= 0)
7 t; ]; q6 u. }9 x) V7 L: j5 k         {! G( j3 |3 X. P. x& H/ ?( z$ l
& h1 y: I8 p' {6 V/ O
         /* Out of bytes in current block, so read next block5 G. y7 k- i/ F! i1 c
          */4 W4 M% R5 T5 ]6 C( }9 H
         pbytes = byte_buff;
, U" M4 \' T+ H         if ((navail_bytes = get_byte()) < 0)
8 j. u% K- G+ c4 Q- R            return(navail_bytes);6 Q  j# z$ X, o) P5 H
         else if (navail_bytes)0 g/ ^% I# q7 j+ P
            {
6 {' ~2 \; a. d% m            for (i = 0; i < navail_bytes; ++i)! ]8 k: B8 Q, O- O
               {
' D  a. r' O! }8 b1 \& P               if ((x = get_byte()) < 0)
6 |; `" M1 U- k* a- l) b                  return(x);" ^1 e7 }' ~: U. w; r; s5 q) M6 Q
               byte_buff = x;
: m0 B9 U1 m! O8 Q               }. e9 s' w$ }( z5 f$ W
            }
( r# f8 \; x$ v/ C- z: |         }& p2 O* V6 r9 O& k3 n# a6 U
      b1 = *pbytes++;
3 V' x) I1 }5 x      ret |= b1 << nbits_left;% q6 t8 e0 r3 L
      nbits_left += 8;
4 B2 @9 X1 x* q) r/ s      --navail_bytes;- j, ]# `" B" l% Z8 W7 a& v' h
      }
7 ]0 t' t3 J2 m8 A* S   nbits_left -= curr_size;4 _# [( o3 Q0 a  U/ l+ H! n
   ret &= code_mask[curr_size];, m) z' T0 X' h: o7 N8 T
   return((WORD)(ret));/ w/ v6 {: c; i; M
   }
. x  x: }% v; e5 u, L2 p5 c
" a/ o7 t6 F; @2 ]' O  d2 n) d" d! `. ]& M; M. a7 Q  ~. E4 r0 t
/* The reason we have these seperated like this instead of using' T# H+ K: `" L( G5 y$ M; M
* a structure like the original Wilhite code did, is because this0 I6 W6 G5 Y0 \1 }3 f6 r* p
* stuff generally produces significantly faster code when compiled...
+ H' i+ T$ B* o* O * This code is full of similar speedups...  (For a good book on writing$ ^% r  r" n/ l. ~  f3 Y
* C for speed or for space optomisation, see Efficient C by Tom Plum,
/ P3 ~7 k/ {/ H$ A5 l2 ]9 f  D * published by Plum-Hall Associates...)
! }2 r9 Y8 r! _. o& L */, [; Y5 A  A- Q6 @
LOCAL UTINY stack[MAX_CODES + 1];            /* Stack for storing pixels */
- O  N" ?* P  h2 Q" D( V2 f. aLOCAL UTINY suffix[MAX_CODES + 1];           /* Suffix table */3 d0 P8 P- o7 m8 G  t: ]6 h
LOCAL UWORD prefix[MAX_CODES + 1];           /* Prefix linked list */& ^* M( p+ K0 L% _$ M+ C( J* c
% d. K8 B- w# e- m& `: ]
/* WORD decoder(linewidth)
1 e; {/ t) d) m$ O0 G4 i *    WORD linewidth;               * Pixels per line of image *
* N. h1 ~0 A  B7 W5 G" g# T- ^) u *
. u; W( O6 Y; T& C) R * - This function decodes an LZW image, according to the method used
) O% P0 `5 M1 V * in the GIF spec.  Every *linewidth* "characters" (ie. pixels) decoded
; A- H, l. e/ Z5 g; {/ X1 y * will generate a call to out_line(), which is a user specific function
. q0 ]) G0 N( r: t- N * to display a line of pixels.  The function gets it's codes from6 r6 \! I" g$ I: S
* get_next_code() which is responsible for reading blocks of data and
8 m5 y0 [9 u7 o; ]' } * seperating them into the proper size codes.  Finally, get_byte() is& x* o9 j, @4 @$ R- J( Y+ J; _1 }
* the global routine to read the next byte from the GIF file.
" ~2 E& v# I: _1 N* \( q2 Y) Y- G *  `! k! m& \$ M* d* `
* It is generally a good idea to have linewidth correspond to the actual+ ~9 X) z3 J5 V
* width of a line (as specified in the Image header) to make your own
& Y4 Q6 n0 a7 r1 ` * code a bit simpler, but it isn't absolutely necessary.9 I. g9 [+ k% Y4 @2 D/ ^
*
1 n# f0 B9 x0 B, }( J * Returns: 0 if successful, else negative.  (See ERRS.H), z  O; F& W- ^$ Y. ^$ q
*
( y! a. B! C5 }* m4 n *// w4 |8 I( C/ k0 s3 r! a

6 B% K7 y% T" j  E. bWORD decoder(linewidth); T- o6 x6 L- k6 v4 F
   WORD linewidth;/ ]2 R/ I8 t% `. z8 ]) P
   {
- B  I0 P7 x( v& Z3 c0 `3 s   FAST UTINY *sp, *bufptr;. B1 ~; u' v9 A
   UTINY *buf;
/ F3 i3 j2 E1 z! j" w   FAST WORD code, fc, oc, bufcnt;
6 @5 U$ W' @1 ^% @  a$ x/ M   WORD c, size, ret;
* h5 G' {( x' q2 }) R
/ c$ d: w3 R% w! _. H; ?   /* Initialize for decoding a new image...
8 K1 o# T. p+ k2 ?: L1 [    */5 }2 f+ O3 \3 r& Z/ V
   if ((size = get_byte()) < 0)
! ?  `  d3 A" S/ p$ q0 F      return(size);
. r6 {9 \/ d% {* Q5 c1 w   if (size < 2 || 9 < size), g$ Y3 m0 ~! W% j( X+ z4 d7 C4 ^
      return(BAD_CODE_SIZE);
# l0 H7 T8 h/ C/ |8 H0 j" p   init_exp(size);: C# M9 a; t; [
: N/ \' A' o2 N8 J
   /* Initialize in case they forgot to put in a clear code.
' u) y4 `9 [; }  L6 y0 `# a    * (This shouldn't happen, but we'll try and decode it anyway...)
0 d# I+ v$ X! _2 t2 G9 @* t    */
( b5 P% k( ]3 C" @: N: v- i/ I7 N' Q   oc = fc = 0;
9 \. `; j7 Z/ R& p  f% w7 `; l6 t, J# v5 y/ i+ j
   /* Allocate space for the decode buffer$ N1 J: ]' j0 I6 ?' Z( ~, m
    */
) m1 [( z# Y8 V   if ((buf = (UTINY *)malloc(linewidth + 1)) == NULL). x# B& ^6 t1 t% W( A* X
      return(OUT_OF_MEMORY);
2 w4 O+ n7 u6 X3 b8 m5 ]
/ v9 E( b' ~( T0 A, R   /* Set up the stack pointer and decode buffer pointer' H; {+ b) \) m
    */1 }' {2 n& b1 I( X4 N
   sp = stack;+ ^. D) b+ }! }7 }: a2 V
   bufptr = buf;
1 o# U+ A8 _. A9 u   bufcnt = linewidth;: o( \( C% v" v  _/ c
& G, ?8 \2 U1 d8 ~8 X) d7 x7 Q, H
   /* This is the main loop.  For each code we get we pass through the3 j; K- K8 C5 b+ p  _
    * linked list of prefix codes, pushing the corresponding "character" for2 q0 w* S) M: T$ c
    * each code onto the stack.  When the list reaches a single "character"
  j; g. x( K8 n7 {8 l3 N    * we push that on the stack too, and then start unstacking each
: P7 x- k+ ?+ w  Y8 h3 D& J7 f    * character for output in the correct order.  Special handling is. P+ r0 L2 m- n' B+ [; E* Z, |
    * included for the clear code, and the whole thing ends when we get* n; r$ i: a9 J7 o% |, s# E
    * an ending code.
" J7 T) J, K8 m! X5 Y7 C1 W8 S2 f    */$ N  R7 y! j( y# C8 Q6 P" e8 L  v
   while ((c = get_next_code()) != ending)
  e$ I5 {1 K6 }  C0 U' @, S, f      {
$ P! U# J) Q+ |; G5 L8 `+ l" {1 t1 J, K( t3 y& f/ w
      /* If we had a file error, return without completing the decode
) \: D) w5 S5 N- x6 R       */2 |( j. p8 K: v+ y% n; f$ g
      if (c < 0)5 C5 `' h4 i' ]# \- \( u
         {
, |9 A2 T5 p& W. A         free(buf);) _* L8 S, h1 {' k/ B
         return(0);
, u7 I: x2 I* W. M( W         }
/ n$ C, g+ q+ \2 Z4 q0 ~9 v! d2 ]# W0 F5 Q' x
      /* If the code is a clear code, reinitialize all necessary items.
, ?/ _: k8 o! N, w       */
% t/ ]7 v9 f3 G0 _! N" z, x8 s/ _. {      if (c == clear)
% n1 W: B8 ?9 b3 s/ e1 u! U, \- R         {
% e3 s) {- `3 x+ Y, P         curr_size = size + 1;2 [* Y3 `9 \) `
         slot = newcodes;! i# t" l. P3 Q" [0 e2 Q" ^1 w
         top_slot = 1 << curr_size;- M% ^( o" V4 P% \5 t' \! M

7 c6 W  h, r: L2 S" V$ g4 A         /* Continue reading codes until we get a non-clear code
/ U. Q2 I& }$ T5 v  l' J: t          * (Another unlikely, but possible case...)4 U  \1 n; k. Y; v2 i
          */: y/ }5 f9 {  e& \9 J; a/ b; t
         while ((c = get_next_code()) == clear)
& Q: i2 }+ p* i+ Q+ _            ;
8 V: }! N- F8 h3 w' s" v$ I" F1 l9 T, T: ^2 q0 E* G9 P9 k: w) O
         /* If we get an ending code immediately after a clear code/ |( c$ q" G9 U0 A  ?% U( }
          * (Yet another unlikely case), then break out of the loop.
' O% K  A8 m3 F# ?# W          */& d1 L) ]  l: T% s
         if (c == ending): N& u5 S+ U, P1 s' {7 d2 ?
            break;
) K# f4 t5 z8 o' z6 _+ u0 E: w# E8 o8 G" i& `
         /* Finally, if the code is beyond the range of already set codes,- M2 A+ d. |  O" Q- S! ]: I* M  ^
          * (This one had better NOT happen...  I have no idea what will
! {/ `5 P- v$ W/ {9 C          * result from this, but I doubt it will look good...) then set it4 ]+ q1 T0 Q8 W6 @$ y1 {4 p/ x
          * to color zero.
. D8 z# h( B' t4 c          */
  ?/ [, M3 Z- [3 U         if (c >= slot)
, d& v8 Q- \0 [8 j9 P, y            c = 0;. B- C  ]1 P: C4 V( n' L( y

% J% s2 |8 s* a" G  z+ R2 S         oc = fc = c;
: X! t7 x/ f! R1 z# Y- D; s
. k+ H+ n- d9 |7 A7 `         /* And let us not forget to put the char into the buffer... And2 P4 |7 s. }2 Z6 U; b; _; k! a
          * if, on the off chance, we were exactly one pixel from the end
$ S( @( y# m# p6 h          * of the line, we have to send the buffer to the out_line()
* D" M; W+ b, L  P          * routine...
& Z, K0 F' {2 G8 r          */
# a! l- S0 O1 L# _         *bufptr++ = c;
: t% _! E6 H: Y, _( |! w         if (--bufcnt == 0)
7 H( F) n  b, M: J            {
- a5 i% f8 y1 G            if ((ret = out_line(buf, linewidth)) < 0)
9 X, J7 U6 Q6 P1 n               {
4 X& Q7 K  T5 ]: y. w2 `               free(buf);. @7 A1 i. V9 y2 ]7 o9 w
               return(ret);
5 @  J4 {( h+ S2 R5 w% O0 h3 ^               }9 y/ K+ F4 J& r6 Q
            bufptr = buf;3 s8 c8 e: w5 \. e( f; i
            bufcnt = linewidth;8 {5 p8 r* G# x/ x2 ^* w
            }8 e, W& n7 i; \/ a0 Q8 L) j2 b
         }
/ z  v4 M& d1 A% F$ A2 y- n& z: \+ H      else! _2 G0 Y  v1 Q1 r- @$ z$ `$ |
         {) R2 Y8 t" F, n' p, L
$ M& F* F7 h3 O% M
         /* In this case, it's not a clear code or an ending code, so0 y$ Y& j% N4 [9 R
          * it must be a code code...  So we can now decode the code into+ s2 v" u$ L* e# H& U  y
          * a stack of character codes. (Clear as mud, right?)0 W, O! g4 e* K; R4 m
          */
9 v1 G9 ]0 v% P         code = c;
3 O5 }/ {2 C  g9 n3 h$ j
4 |5 Z5 }$ |/ R3 V4 C1 [         /* Here we go again with one of those off chances...  If, on the2 l" V4 F4 ^7 A7 n
          * off chance, the code we got is beyond the range of those already6 R% I5 o  n+ U1 @7 u1 D/ k& L
          * set up (Another thing which had better NOT happen...) we trick
7 w4 s0 [  y. F2 L, S          * the decoder into thinking it actually got the last code read.8 Q7 @# e; C, T9 J9 N
          * (Hmmn... I'm not sure why this works...  But it does...)4 y0 B- H8 e7 ?" r& u
          */" d8 |6 i! ?/ R- E
         if (code >= slot)* ~9 m( }; T" [/ [
            {  f& z1 X/ m# Z. z
            if (code > slot)  l! ?3 a. F1 W7 I* C
               ++bad_code_count;  D9 q/ Y2 y$ e: y# x
            code = oc;- T0 x/ g, _: R7 V
            *sp++ = fc;  G5 V; c6 j( H) J* n7 r) S' ~
            }& v  E. N) Y$ J0 }, _2 \) Q
' o0 B7 \8 h. Y$ {- A4 O0 }$ {5 w3 o
         /* Here we scan back along the linked list of prefixes, pushing
# `& k# R) C5 [6 c; n6 z4 F0 E          * helpless characters (ie. suffixes) onto the stack as we do so.
1 y" ~; N- q7 Z7 a) R  e4 E          */
* B* `8 p7 \  H* N         while (code >= newcodes)
' c# X+ U. C' G; X            {: ~8 M" X4 B& ^" x/ S" Z9 U$ T1 p
            *sp++ = suffix[code];
8 B9 q: v1 @7 P7 G! e" i' v4 m            code = prefix[code];" \% n6 d% d2 [+ C9 N
            }  N3 c9 @- G6 y* ]1 A: O& x

3 E- \8 @# a" q: x& j         /* Push the last character on the stack, and set up the new
+ p  `- D* ^1 ~          * prefix and suffix, and if the required slot number is greater
5 D. {! M! R; e          * than that allowed by the current bit size, increase the bit
- n% M+ t8 Z: N4 C5 u4 q          * size.  (NOTE - If we are all full, we *don't* save the new# l. x& @* t. j, e; ~; \* ]* `3 |% D
          * suffix and prefix...  I'm not certain if this is correct...
( H4 U3 V+ O( r9 s6 y. V          * it might be more proper to overwrite the last code...
& o% T1 b4 M1 c$ b          */
& Q- U2 c9 A0 w9 ?* r8 W5 q6 v# x         *sp++ = code;
; Y% B6 X4 R) J4 N         if (slot < top_slot)8 @: G- S/ M7 |0 z
            {$ ?* k  a8 x3 V& @/ s
            suffix[slot] = fc = code;
& U5 G. _. J* a- ^            prefix[slot++] = oc;
7 w7 Z( L& ?- s            oc = c;' B( N- }9 c8 Z" H0 r2 U0 c  c! K1 y- ]+ E
            }
; @5 j  ^! s% P! d: y6 f         if (slot >= top_slot)& ^* w) y. d# u& e9 i7 }
            if (curr_size < 12)) i# }0 ~- n. W
               {
% y5 X/ K8 J, [. v  e5 O               top_slot <<= 1;
* a$ _' G' r. g+ f/ K+ f2 z               ++curr_size;
/ z; ^" z! H: r: x  K9 J               }
$ F2 X6 M: `8 [+ O2 U+ R# n+ S6 r5 \+ l5 p/ ^, }
         /* Now that we've pushed the decoded string (in reverse order)# w3 P7 K. M# q* f6 J6 {
          * onto the stack, lets pop it off and put it into our decode5 i, t$ p2 n1 j) G- z
          * buffer...  And when the decode buffer is full, write another
8 l( B9 P/ Z! z4 R9 U  L6 x          * line...
2 c$ x) n, Y) i0 s' m2 V          */' o$ ], R: B. K  {& T
         while (sp > stack)
, L9 ?- B( [% t9 ~) q2 A            {
! u1 s) f5 c! H. L: a5 s            *bufptr++ = *(--sp);
- {0 i/ ~# x8 s            if (--bufcnt == 0)+ r# G8 p) b- Z2 n9 M# `- [
               {( ?$ l& X/ N; w. v& A& @
               if ((ret = out_line(buf, linewidth)) < 0)0 l7 e& C+ @/ U$ _6 E
                  {
7 y4 o- Q8 x7 h# C                  free(buf);5 V+ ?0 V  l: Y
                  return(ret);5 X& {+ ~& R# ^
                  }
& v/ W# U. T, m; ~8 f# d% l               bufptr = buf;
8 k1 j  N4 U0 r% o+ d, H" M( Q  V               bufcnt = linewidth;, n+ x( w! C+ I# }0 \: z6 ]
               }
! y' v; q3 G0 ~, P2 c( i) Z" }            }: c& e9 [/ ^, r. V9 \. A) L
         }( ]8 ]$ h( ^+ w' k7 X% [- }
      }2 k5 n/ g9 N) [3 y0 q7 Y
   ret = 0;! \" Q; W  |9 @5 y- w+ J0 y, j
   if (bufcnt != linewidth)6 t9 @# H0 s% D  R- k% h) h9 E
      ret = out_line(buf, (linewidth - bufcnt));
8 e3 c- {( B+ r6 f   free(buf);
, |) J+ ^7 O' ~3 V   return(ret);1 X: f. I* D5 [2 z( c
   }
 楼主| 发表于 2009-4-19 13:38 | 显示全部楼层
实在没办法图片发不了现在改成PDF格式了
9 `" J! h9 q) T' e5 _9 o9 y# @7 G8 C2 B! \% K" g
[ 本帖最后由 kenson 于 2009-4-22 10:09 编辑 ]

SDRAM初始化.pdf

718.48 KB, 下载次数: 248, 下载积分: 一乐金币 -1

 楼主| 发表于 2009-4-21 20:10 | 显示全部楼层
SDRAM 用途可大了如果将GIF解出来全部放在SDRAM来做相当快的! 现在对SDRAM真的很便宜拆机的十几M的才两三块钱容量随你用相对SRAM来说贵得要死所以还是SDRAM的好呵呵
 楼主| 发表于 2009-11-24 22:49 | 显示全部楼层
补充一下我的GIF文档 以后给自己用!

GIF图形文件格式文档.rar

25.88 KB, 下载次数: 257, 下载积分: 一乐金币 -1

GIF文件结构与解码器.rar

12.27 KB, 下载次数: 222, 下载积分: 一乐金币 -1

发表于 2009-11-24 23:36 | 显示全部楼层
大佬一个人唱独角戏,好无聊!
 楼主| 发表于 2009-11-25 10:00 | 显示全部楼层
这个贴不需要回复!只是我在网上找到的一些资料给以后自己用的,不然放在电脑经常不知去了哪里的

本版积分规则

QQ|一淘宝店|手机版|商店|电子DIY套件|一乐电子 ( 粤ICP备09076165号 ) 公安备案粤公网安备 44522102000183号

GMT+8, 2024-5-19 01:40 , Processed in 0.084606 second(s), 34 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表