@@ -24,15 +24,206 @@ namespace MCGalaxy.Util.Imaging
2424{
2525 public class GifDecoder : ImageDecoder
2626 {
27- static byte [ ] gifSig = new byte [ ] { 0x47 , 0x49 , 0x46 , 0x38 , 0x39 , 0x61 } ; // "GIF89a"
27+ static byte [ ] gif87Sig = new byte [ ] { 0x47 , 0x49 , 0x46 , 0x38 , 0x37 , 0x61 } ; // "GIF87a"
28+ static byte [ ] gif89Sig = new byte [ ] { 0x47 , 0x49 , 0x46 , 0x38 , 0x39 , 0x61 } ; // "GIF89a"
29+
30+ Pixel [ ] globalPal ;
31+ Pixel bgColor ;
2832
2933 public static bool DetectHeader ( byte [ ] data ) {
30- return MatchesSignature ( data , gifSig ) ;
34+ return MatchesSignature ( data , gif87Sig )
35+ || MatchesSignature ( data , gif89Sig ) ;
3136 }
3237
3338 public override SimpleBitmap Decode ( byte [ ] src ) {
39+ SetBuffer ( src ) ;
40+ if ( ! DetectHeader ( src ) ) Fail ( "sig invalid" ) ;
41+ AdvanceOffset ( gif87Sig . Length ) ;
42+
43+ ReadGlobalHeader ( src ) ;
44+ ReadMarkers ( src ) ;
3445 Fail ( "GIF decoder unfinished" ) ;
3546 return null ;
3647 }
48+
49+ const int LOGICAL_DESC_SIZE = 7 ;
50+ void ReadGlobalHeader ( byte [ ] src ) {
51+ // read logical screen descriptor
52+ int offset = AdvanceOffset ( LOGICAL_DESC_SIZE ) ;
53+
54+ ushort width = MemUtils . ReadU16_LE ( src , offset + 0 ) ;
55+ ushort height = MemUtils . ReadU16_LE ( src , offset + 2 ) ;
56+
57+ byte flags = src [ offset + 4 ] ;
58+ byte bgIndex = src [ offset + 5 ] ;
59+ // src[offset + 6] is pixel aspect ratio - not used
60+
61+ bool hasGlobalPal = ( flags & 0x80 ) != 0 ;
62+ int globalPalSize = 1 << ( ( flags & 0x7 ) + 1 ) ;
63+ if ( hasGlobalPal ) ReadGlobalPalette ( src , globalPalSize ) ;
64+
65+ if ( hasGlobalPal && bgIndex < globalPalSize )
66+ bgColor = globalPal [ bgIndex ] ;
67+ }
68+
69+ void ReadGlobalPalette ( byte [ ] src , int size ) {
70+ Pixel [ ] pal = new Pixel [ size ] ;
71+ int offset = AdvanceOffset ( 3 * size ) ;
72+
73+ for ( int i = 0 ; i < pal . Length ; i ++ )
74+ {
75+ pal [ i ] . R = src [ offset ++ ] ;
76+ pal [ i ] . G = src [ offset ++ ] ;
77+ pal [ i ] . B = src [ offset ++ ] ;
78+ pal [ i ] . A = 255 ;
79+ }
80+ globalPal = pal ;
81+ }
82+
83+
84+ const byte MARKER_EXTENSION = 0x21 ;
85+ const byte MARKER_IMAGE_END = 0x3B ;
86+ const byte MARKER_IMAGE_BEG = 0x2C ;
87+
88+ void ReadMarkers ( byte [ ] src ) {
89+ for ( ; ; )
90+ {
91+ int offset = AdvanceOffset ( 1 ) ;
92+ byte marker = src [ offset ] ;
93+ switch ( marker )
94+ {
95+ case MARKER_EXTENSION :
96+ ReadExtension ( src ) ;
97+ break ;
98+ case MARKER_IMAGE_BEG :
99+ ReadImage ( src ) ;
100+ return ; // NOTE: stops reading at first frame
101+ case MARKER_IMAGE_END :
102+ return ;
103+ default :
104+ Fail ( "unknown marker" ) ;
105+ break ;
106+ }
107+ }
108+ }
109+
110+ const byte EXT_GRAPHICS_CONTROL = 0xF9 ;
111+ void ReadExtension ( byte [ ] src ) {
112+ int offset = AdvanceOffset ( 1 ) ;
113+ byte type = src [ offset ++ ] ;
114+
115+ if ( type == EXT_GRAPHICS_CONTROL ) {
116+ ReadGraphicsControl ( src ) ;
117+ } else {
118+ SkipSubBlocks ( src ) ;
119+ }
120+ }
121+
122+ // Always a simple sub block
123+ void ReadGraphicsControl ( byte [ ] src ) {
124+ int offset = AdvanceOffset ( 1 ) ;
125+ byte length = src [ offset ] ;
126+ if ( length < 4 ) Fail ( "graphics control extension too short" ) ;
127+
128+ // Look for transparent colour index
129+ offset = AdvanceOffset ( length ) ;
130+ bool hasTrans = ( src [ offset + 0 ] & 0x01 ) != 0 ;
131+ byte tcIndex = src [ offset + 3 ] ;
132+
133+ Pixel [ ] pal = globalPal ;
134+ if ( hasTrans && pal != null && tcIndex < pal . Length )
135+ pal [ tcIndex ] . A = 0 ;
136+
137+ // should only be one sub block
138+ offset = AdvanceOffset ( 1 ) ;
139+ length = src [ offset ] ;
140+ if ( length != 0 ) Fail ( "graphics control should be one sub block" ) ;
141+ }
142+
143+ void SkipSubBlocks ( byte [ ] src ) {
144+ for ( ; ; )
145+ {
146+ int offset = AdvanceOffset ( 1 ) ;
147+ byte length = src [ offset ++ ] ;
148+
149+ if ( length == 0 ) return ;
150+ AdvanceOffset ( length ) ;
151+ }
152+ }
153+
154+
155+ void ReadImage ( byte [ ] src ) {
156+ // Read image descriptor header
157+ int offset = AdvanceOffset ( 2 + 2 + 2 + 2 + 1 ) ;
158+
159+ ushort imageX = MemUtils . ReadU16_LE ( src , offset + 0 ) ;
160+ ushort imageY = MemUtils . ReadU16_LE ( src , offset + 2 ) ;
161+ ushort imageW = MemUtils . ReadU16_LE ( src , offset + 4 ) ;
162+ ushort imageH = MemUtils . ReadU16_LE ( src , offset + 6 ) ;
163+ byte flags = src [ offset + 8 ] ;
164+
165+ // Read image data
166+ offset = AdvanceOffset ( 1 ) ;
167+ byte minCodeSize = src [ offset ] ;
168+
169+ // Init LZW variables
170+ int codeLen = minCodeSize + 1 ;
171+ int codeMask = ( 1 << codeLen ) - 1 ;
172+ int clearCode = ( 1 << minCodeSize ) + 0 ;
173+ int stopCode = ( 1 << minCodeSize ) + 1 ;
174+ int dictEnd ;
175+ DictEntry [ ] dict = new DictEntry [ 1 << codeLen ] ;
176+
177+ // Bit buffer state
178+ uint bufVal = 0 ;
179+ int bufLen = 0 ;
180+
181+ // Spec says clear code _should_ be sent first, but not required
182+ for ( dictEnd = 0 ; dictEnd < ( 1 << minCodeSize ) ; dictEnd ++ )
183+ {
184+ dict [ dictEnd ] . value = ( byte ) dictEnd ;
185+ dict [ dictEnd ] . prev = - 1 ;
186+ dict [ dictEnd ] . len = 1 ;
187+ }
188+ dictEnd += 2 ; // "clear code" and "stop code" entries
189+
190+ for ( ; ; )
191+ {
192+ int code = 0 ;
193+
194+ if ( bufLen < codeLen ) {
195+
196+ }
197+
198+ code = ( int ) ( bufVal & codeMask ) ;
199+ bufVal >>= codeMask ;
200+ bufLen -= codeLen ;
201+
202+ if ( code == clearCode ) {
203+ codeLen = minCodeSize + 1 ;
204+ codeMask = ( 1 << codeLen ) - 1 ;
205+
206+ // Clear dictionary
207+ for ( dictEnd = 0 ; dictEnd < ( 1 << minCodeSize ) ; dictEnd ++ )
208+ {
209+ dict [ dictEnd ] . value = ( byte ) dictEnd ;
210+ dict [ dictEnd ] . prev = - 1 ;
211+ dict [ dictEnd ] . len = 1 ;
212+ }
213+ dictEnd += 2 ; // "clear code" and "stop code" entries
214+ } else if ( code == stopCode ) {
215+ break ;
216+ }
217+ }
218+
219+ //SkipSubBlocks(src);
220+ Fail ( "GIF decoder unfinished" ) ;
221+ }
222+
223+ struct DictEntry
224+ {
225+ public byte value ;
226+ public short prev , len ;
227+ }
37228 }
38229}
0 commit comments