@@ -40,33 +40,35 @@ public override SimpleBitmap Decode(byte[] src) {
4040 if ( ! DetectHeader ( src ) ) Fail ( "sig invalid" ) ;
4141 AdvanceOffset ( gif87Sig . Length ) ;
4242
43- ReadGlobalHeader ( src ) ;
44- ReadMarkers ( src ) ;
45- Fail ( "GIF decoder unfinished" ) ;
46- return null ;
43+ SimpleBitmap bmp = new SimpleBitmap ( ) ;
44+ ReadGlobalHeader ( src , bmp ) ;
45+ ReadMarkers ( src , bmp ) ;
46+ return bmp ;
4747 }
4848
4949 const int LOGICAL_DESC_SIZE = 7 ;
50- void ReadGlobalHeader ( byte [ ] src ) {
50+ void ReadGlobalHeader ( byte [ ] src , SimpleBitmap bmp ) {
5151 // read logical screen descriptor
5252 int offset = AdvanceOffset ( LOGICAL_DESC_SIZE ) ;
5353
54- ushort width = MemUtils . ReadU16_LE ( src , offset + 0 ) ;
55- ushort height = MemUtils . ReadU16_LE ( src , offset + 2 ) ;
54+ bmp . Width = MemUtils . ReadU16_LE ( src , offset + 0 ) ;
55+ bmp . Height = MemUtils . ReadU16_LE ( src , offset + 2 ) ;
5656
5757 byte flags = src [ offset + 4 ] ;
5858 byte bgIndex = src [ offset + 5 ] ;
5959 // src[offset + 6] is pixel aspect ratio - not used
6060
6161 bool hasGlobalPal = ( flags & 0x80 ) != 0 ;
62- int globalPalSize = 1 << ( ( flags & 0x7 ) + 1 ) ;
63- if ( hasGlobalPal ) ReadGlobalPalette ( src , globalPalSize ) ;
62+ if ( hasGlobalPal )
63+ globalPal = ReadPalette ( src , flags ) ;
64+ if ( hasGlobalPal && bgIndex < globalPal . Length )
65+ bgColor = globalPal [ bgIndex ] ;
6466
65- if ( hasGlobalPal && bgIndex < globalPalSize )
66- bgColor = globalPal [ bgIndex ] ;
67+ bmp . AllocatePixels ( ) ;
6768 }
6869
69- void ReadGlobalPalette ( byte [ ] src , int size ) {
70+ Pixel [ ] ReadPalette ( byte [ ] src , byte flags ) {
71+ int size = 1 << ( ( flags & 0x7 ) + 1 ) ;
7072 Pixel [ ] pal = new Pixel [ size ] ;
7173 int offset = AdvanceOffset ( 3 * size ) ;
7274
@@ -77,15 +79,15 @@ void ReadGlobalPalette(byte[] src, int size) {
7779 pal [ i ] . B = src [ offset ++ ] ;
7880 pal [ i ] . A = 255 ;
7981 }
80- globalPal = pal ;
82+ return pal ;
8183 }
8284
8385
8486 const byte MARKER_EXTENSION = 0x21 ;
8587 const byte MARKER_IMAGE_END = 0x3B ;
8688 const byte MARKER_IMAGE_BEG = 0x2C ;
8789
88- void ReadMarkers ( byte [ ] src ) {
90+ void ReadMarkers ( byte [ ] src , SimpleBitmap bmp ) {
8991 for ( ; ; )
9092 {
9193 int offset = AdvanceOffset ( 1 ) ;
@@ -96,7 +98,7 @@ void ReadMarkers(byte[] src) {
9698 ReadExtension ( src ) ;
9799 break ;
98100 case MARKER_IMAGE_BEG :
99- ReadImage ( src ) ;
101+ ReadImage ( src , bmp ) ;
100102 return ; // NOTE: stops reading at first frame
101103 case MARKER_IMAGE_END :
102104 return ;
@@ -151,8 +153,13 @@ void SkipSubBlocks(byte[] src) {
151153 }
152154 }
153155
156+ const int MAX_CODE_LEN = 12 ;
157+ const int MAX_CODES = 1 << MAX_CODE_LEN ;
158+ byte curSubBlockLeft ;
159+ bool subBlocksEnd ;
160+ int subBlocksOffset ;
154161
155- void ReadImage ( byte [ ] src ) {
162+ void ReadImage ( byte [ ] src , SimpleBitmap bmp ) {
156163 // Read image descriptor header
157164 int offset = AdvanceOffset ( 2 + 2 + 2 + 2 + 1 ) ;
158165
@@ -162,68 +169,156 @@ void ReadImage(byte[] src) {
162169 ushort imageH = MemUtils . ReadU16_LE ( src , offset + 6 ) ;
163170 byte flags = src [ offset + 8 ] ;
164171
172+ if ( ( flags & 0x40 ) != 0 ) Fail ( "Interlaced GIF unsupported" ) ;
173+ if ( imageX + imageW > bmp . Width ) Fail ( "Invalid X dimensions" ) ;
174+ if ( imageY + imageH > bmp . Height ) Fail ( "Invalid Y dimensions" ) ;
175+
176+ bool hasLocalPal = ( flags & 0x80 ) != 0 ;
177+ Pixel [ ] localPal = null ;
178+ if ( hasLocalPal )
179+ localPal = ReadPalette ( src , flags ) ;
180+
181+ Pixel [ ] pal = localPal ?? globalPal ;
182+ int dst_index = 0 ;
183+
165184 // Read image data
166185 offset = AdvanceOffset ( 1 ) ;
167186 byte minCodeSize = src [ offset ] ;
187+ if ( minCodeSize >= MAX_CODE_LEN ) Fail ( "codesize too long" ) ;
188+
189+ curSubBlockLeft = 0 ;
190+ subBlocksEnd = false ;
168191
169192 // Init LZW variables
170193 int codeLen = minCodeSize + 1 ;
171194 int codeMask = ( 1 << codeLen ) - 1 ;
172195 int clearCode = ( 1 << minCodeSize ) + 0 ;
173196 int stopCode = ( 1 << minCodeSize ) + 1 ;
174- int dictEnd ;
197+ int prevCode , availCode ;
175198 DictEntry [ ] dict = new DictEntry [ 1 << codeLen ] ;
176199
177200 // Bit buffer state
178201 uint bufVal = 0 ;
179202 int bufLen = 0 ;
180203
181204 // Spec says clear code _should_ be sent first, but not required
182- for ( dictEnd = 0 ; dictEnd < ( 1 << minCodeSize ) ; dictEnd ++ )
205+ for ( availCode = 0 ; availCode < ( 1 << minCodeSize ) ; availCode ++ )
183206 {
184- dict [ dictEnd ] . value = ( byte ) dictEnd ;
185- dict [ dictEnd ] . prev = - 1 ;
186- dict [ dictEnd ] . len = 1 ;
207+ dict [ availCode ] . value = ( byte ) availCode ;
208+ dict [ availCode ] . prev = - 1 ;
209+ dict [ availCode ] . len = 1 ;
187210 }
188- dictEnd += 2 ; // "clear code" and "stop code" entries
211+
212+ availCode += 2 ; // "clear code" and "stop code" entries
213+ prevCode = - 1 ;
189214
190215 for ( ; ; )
191216 {
192- int code = 0 ;
193-
217+ // Refill buffer when needed
194218 if ( bufLen < codeLen ) {
219+ int read ;
220+ while ( bufLen <= 24 && ( read = ReadNextByte ( ) ) >= 0 ) {
221+ bufVal |= ( uint ) read << bufLen ;
222+ bufLen += 8 ;
223+ }
195224
225+ if ( bufLen < codeLen ) Fail ( "not enough bits for code" ) ;
196226 }
197227
198- code = ( int ) ( bufVal & codeMask ) ;
199- bufVal >>= codeMask ;
200- bufLen -= codeLen ;
228+ int code = ( int ) ( bufVal & codeMask ) ;
229+ bufVal >>= codeLen ;
230+ bufLen -= codeLen ;
201231
202232 if ( code == clearCode ) {
203233 codeLen = minCodeSize + 1 ;
204234 codeMask = ( 1 << codeLen ) - 1 ;
205235
206236 // Clear dictionary
207- for ( dictEnd = 0 ; dictEnd < ( 1 << minCodeSize ) ; dictEnd ++ )
237+ for ( availCode = 0 ; availCode < ( 1 << minCodeSize ) ; availCode ++ )
208238 {
209- dict [ dictEnd ] . value = ( byte ) dictEnd ;
210- dict [ dictEnd ] . prev = - 1 ;
211- dict [ dictEnd ] . len = 1 ;
239+ dict [ availCode ] . value = ( byte ) availCode ;
240+ dict [ availCode ] . prev = - 1 ;
241+ dict [ availCode ] . len = 1 ;
212242 }
213- dictEnd += 2 ; // "clear code" and "stop code" entries
243+
244+ availCode += 2 ; // "clear code" and "stop code" entries
245+ prevCode = - 1 ;
214246 } else if ( code == stopCode ) {
215247 break ;
216248 }
249+
250+ if ( code > availCode ) Fail ( "invalid code" ) ;
251+
252+ // Add new entry to code table unless it's full
253+ // GIF spec allows this as per 'deferred clear codes'
254+ if ( prevCode >= 0 && availCode < MAX_CODES ) {
255+ int firstCode = code == availCode ? prevCode : code ;
256+ // Follow chain back to find first value
257+ // TODO optimise this...
258+ while ( dict [ firstCode ] . prev != - 1 )
259+ {
260+ firstCode = dict [ firstCode ] . prev ;
261+ }
262+
263+ dict [ availCode ] . value = dict [ firstCode ] . value ;
264+ dict [ availCode ] . prev = ( short ) prevCode ;
265+ dict [ availCode ] . len = ( short ) ( dict [ prevCode ] . len + 1 ) ;
266+
267+ // Check if inserted code in last free entry of table
268+ // If this is the case, then the table is immediately expanded
269+ if ( availCode == codeMask && availCode != ( MAX_CODES - 1 ) ) {
270+ codeLen ++ ;
271+ codeMask = ( 1 << codeLen ) - 1 ;
272+ Array . Resize ( ref dict , 1 << codeLen ) ;
273+ }
274+ availCode ++ ;
275+ }
276+
277+ prevCode = code ;
278+ // TODO output code
279+
280+ // "top" entry is actually last entry in chain
281+ int chain_len = dict [ code ] . len ;
282+ for ( int i = chain_len - 1 ; i >= 0 ; i -- )
283+ {
284+ int index = dst_index + i ;
285+ byte palIndex = dict [ code ] . value ;
286+
287+ //int localX = index % imageW;
288+ //int localY = index / imageW;
289+ int globalX = imageX + ( index % imageW ) ;
290+ int globalY = imageY + ( index / imageW ) ;
291+ bmp . pixels [ globalY * bmp . Width + globalX ] = pal [ palIndex ] ;
292+
293+ code = dict [ code ] . prev ;
294+ }
295+
296+ dst_index += chain_len ;
217297 }
218-
219- //SkipSubBlocks(src);
220- Fail ( "GIF decoder unfinished" ) ;
221298 }
222299
223300 struct DictEntry
224301 {
225302 public byte value ;
226303 public short prev , len ;
227304 }
305+
306+ int ReadNextByte ( ) {
307+ if ( curSubBlockLeft == 0 ) {
308+ if ( subBlocksEnd ) return - 1 ;
309+
310+ subBlocksOffset = AdvanceOffset ( 1 ) ;
311+ curSubBlockLeft = buf_data [ subBlocksOffset ++ ] ;
312+
313+ // If sub block length is 0, then reached end of sub blocks
314+ if ( curSubBlockLeft == 0 ) {
315+ subBlocksEnd = true ;
316+ return - 1 ;
317+ }
318+ }
319+
320+ curSubBlockLeft -- ;
321+ return buf_data [ subBlocksOffset ++ ] ;
322+ }
228323 }
229324}
0 commit comments