1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/common/gifdecod.cpp 
   3 // Purpose:     wxGIFDecoder, GIF reader for wxImage and wxAnimation 
   4 // Author:      Guillermo Rodriguez Garcia <guille@iies.es> 
   7 // Copyright:   (c) Guillermo Rodriguez Garcia 
   8 // Licence:     wxWindows licence 
   9 ///////////////////////////////////////////////////////////////////////////// 
  11 // For compilers that support precompilation, includes "wx.h". 
  12 #include "wx/wxprec.h" 
  18 #if wxUSE_STREAMS && wxUSE_GIF 
  21     #include "wx/palette.h" 
  28 #include "wx/gifdecod.h" 
  29 #include "wx/scopedptr.h" 
  30 #include "wx/scopeguard.h" 
  34     GIF_MARKER_EXT       
= '!', // 0x21 
  35     GIF_MARKER_SEP       
= ',', // 0x2C 
  36     GIF_MARKER_ENDOFDATA 
= ';', // 0x3B 
  38     GIF_MARKER_EXT_GRAPHICS_CONTROL 
= 0xF9, 
  39     GIF_MARKER_EXT_COMMENT          
= 0xFE, 
  40     GIF_MARKER_EXT_APP              
= 0xFF 
  43 #define GetFrame(n)     ((GIFImage*)m_frames[n]) 
  45 //--------------------------------------------------------------------------- 
  47 //--------------------------------------------------------------------------- 
  49 // internal class for storing GIF image data 
  56     unsigned int w
;                 // width 
  57     unsigned int h
;                 // height 
  58     unsigned int left
;              // x coord (in logical screen) 
  59     unsigned int top
;               // y coord (in logical screen) 
  60     int transparent
;                // transparent color index (-1 = none) 
  61     wxAnimationDisposal disposal
;   // disposal method 
  62     long delay
;                     // delay in ms (-1 = unused) 
  63     unsigned char *p
;               // bitmap 
  64     unsigned char *pal
;             // palette 
  65     unsigned int ncolours
;          // number of colours 
  68     wxDECLARE_NO_COPY_CLASS(GIFImage
); 
  71 wxDECLARE_SCOPED_PTR(GIFImage
, GIFImagePtr
) 
  72 wxDEFINE_SCOPED_PTR(GIFImage
, GIFImagePtr
) 
  75 //--------------------------------------------------------------------------- 
  76 // GIFImage constructor 
  77 //--------------------------------------------------------------------------- 
  85     disposal 
= wxANIM_DONOTREMOVE
; 
  87     p 
= (unsigned char *) NULL
; 
  88     pal 
= (unsigned char *) NULL
; 
  92 //--------------------------------------------------------------------------- 
  93 // wxGIFDecoder constructor and destructor 
  94 //--------------------------------------------------------------------------- 
  96 wxGIFDecoder::wxGIFDecoder() 
 100 wxGIFDecoder::~wxGIFDecoder() 
 105 void wxGIFDecoder::Destroy() 
 107     wxASSERT(m_nFrames
==m_frames
.GetCount()); 
 108     for (unsigned int i
=0; i
<m_nFrames
; i
++) 
 110         GIFImage 
*f 
= (GIFImage
*)m_frames
[i
]; 
 121 //--------------------------------------------------------------------------- 
 122 // Convert this image to a wxImage object 
 123 //--------------------------------------------------------------------------- 
 125 // This function was designed by Vaclav Slavik 
 127 bool wxGIFDecoder::ConvertToImage(unsigned int frame
, wxImage 
*image
) const 
 129     unsigned char *src
, *dst
, *pal
; 
 137     wxSize sz 
= GetFrameSize(frame
); 
 138     image
->Create(sz
.GetWidth(), sz
.GetHeight()); 
 139     image
->SetType(wxBITMAP_TYPE_GIF
); 
 144     pal 
= GetPalette(frame
); 
 145     src 
= GetData(frame
); 
 146     dst 
= image
->GetData(); 
 147     transparent 
= GetTransparentColourIndex(frame
); 
 149     // set transparent colour mask 
 150     if (transparent 
!= -1) 
 152         for (i 
= 0; i 
< GetNcolours(frame
); i
++) 
 154             if ((pal
[3 * i 
+ 0] == 255) && 
 155                 (pal
[3 * i 
+ 1] == 0) && 
 156                 (pal
[3 * i 
+ 2] == 255)) 
 158                 pal
[3 * i 
+ 2] = 254; 
 162         pal
[3 * transparent 
+ 0] = 255, 
 163         pal
[3 * transparent 
+ 1] = 0, 
 164         pal
[3 * transparent 
+ 2] = 255; 
 166         image
->SetMaskColour(255, 0, 255); 
 169         image
->SetMask(false); 
 172     unsigned char r
[256]; 
 173     unsigned char g
[256]; 
 174     unsigned char b
[256]; 
 176     for (i 
= 0; i 
< 256; i
++) 
 183     image
->SetPalette(wxPalette(GetNcolours(frame
), r
, g
, b
)); 
 184 #endif // wxUSE_PALETTE 
 187     unsigned long npixel 
= sz
.GetWidth() * sz
.GetHeight(); 
 188     for (i 
= 0; i 
< npixel
; i
++, src
++) 
 190         *(dst
++) = pal
[3 * (*src
) + 0]; 
 191         *(dst
++) = pal
[3 * (*src
) + 1]; 
 192         *(dst
++) = pal
[3 * (*src
) + 2]; 
 195     wxString comment 
= GetFrame(frame
)->comment
; 
 196     if ( !comment
.empty() ) 
 198         image
->SetOption(wxIMAGE_OPTION_GIF_COMMENT
, comment
); 
 205 //--------------------------------------------------------------------------- 
 207 //--------------------------------------------------------------------------- 
 209 // Get data for current frame 
 211 wxSize 
wxGIFDecoder::GetFrameSize(unsigned int frame
) const 
 213     return wxSize(GetFrame(frame
)->w
, GetFrame(frame
)->h
); 
 216 wxPoint 
wxGIFDecoder::GetFramePosition(unsigned int frame
) const 
 218     return wxPoint(GetFrame(frame
)->left
, GetFrame(frame
)->top
); 
 221 wxAnimationDisposal 
wxGIFDecoder::GetDisposalMethod(unsigned int frame
) const 
 223     return GetFrame(frame
)->disposal
; 
 226 long wxGIFDecoder::GetDelay(unsigned int frame
) const 
 228     return GetFrame(frame
)->delay
; 
 231 wxColour 
wxGIFDecoder::GetTransparentColour(unsigned int frame
) const 
 233     unsigned char *pal 
= GetFrame(frame
)->pal
; 
 234     int n 
= GetFrame(frame
)->transparent
; 
 238     return wxColour(pal
[n
*3 + 0], 
 243 unsigned char* wxGIFDecoder::GetData(unsigned int frame
) const    { return (GetFrame(frame
)->p
); } 
 244 unsigned char* wxGIFDecoder::GetPalette(unsigned int frame
) const { return (GetFrame(frame
)->pal
); } 
 245 unsigned int wxGIFDecoder::GetNcolours(unsigned int frame
) const  { return (GetFrame(frame
)->ncolours
); } 
 246 int wxGIFDecoder::GetTransparentColourIndex(unsigned int frame
) const  { return (GetFrame(frame
)->transparent
); } 
 250 //--------------------------------------------------------------------------- 
 251 // GIF reading and decoding 
 252 //--------------------------------------------------------------------------- 
 255 //  Reads the next code from the file stream, with size 'bits' 
 257 int wxGIFDecoder::getcode(wxInputStream
& stream
, int bits
, int ab_fin
) 
 259     unsigned int mask
;          // bit mask 
 260     unsigned int code
;          // code (result) 
 262     // get remaining bits from last byte read 
 263     mask 
= (1 << bits
) - 1; 
 264     code 
= (m_lastbyte 
>> (8 - m_restbits
)) & mask
; 
 266     // keep reading new bytes while needed 
 267     while (bits 
> m_restbits
) 
 269         // if no bytes left in this block, read the next block 
 272             m_restbyte 
= stream
.GetC(); 
 274             /* Some encoders are a bit broken: instead of issuing 
 275              * an end-of-image symbol (ab_fin) they come up with 
 276              * a zero-length subblock!! We catch this here so 
 277              * that the decoder sees an ab_fin code. 
 286             stream
.Read((void *) m_buffer
, m_restbyte
); 
 287             if (stream
.LastRead() != m_restbyte
) 
 295         // read next byte and isolate the bits we need 
 296         m_lastbyte 
= (unsigned char) (*m_bufp
++); 
 297         mask       
= (1 << (bits 
- m_restbits
)) - 1; 
 298         code       
= code 
+ ((m_lastbyte 
& mask
) << m_restbits
); 
 301         // adjust total number of bits extracted from the buffer 
 302         m_restbits 
= m_restbits 
+ 8; 
 305     // find number of bits remaining for next code 
 306     m_restbits 
= (m_restbits 
- bits
); 
 313 //  GIF decoding function. The initial code size (aka root size) 
 314 //  is 'bits'. Supports interlaced images (interl == 1). 
 315 //  Returns wxGIF_OK (== 0) on success, or an error code if something 
 316 // fails (see header file for details) 
 318 wxGIFDecoder::dgif(wxInputStream
& stream
, GIFImage 
*img
, int interl
, int bits
) 
 320     static const int allocSize 
= 4096 + 1; 
 321     int *ab_prefix 
= new int[allocSize
]; // alphabet (prefixes) 
 322     if (ab_prefix 
== NULL
) 
 327     int *ab_tail 
= new int[allocSize
];   // alphabet (tails) 
 334     int *stack 
= new int[allocSize
];     // decompression stack 
 342     int ab_clr
;                     // clear code 
 343     int ab_fin
;                     // end of info code 
 344     int ab_bits
;                    // actual symbol width, in bits 
 345     int ab_free
;                    // first free position in alphabet 
 346     int ab_max
;                     // last possible character in alphabet 
 347     int pass
;                       // pass number in interlaced images 
 348     int pos
;                        // index into decompresion stack 
 349     unsigned int x
, y
;              // position in image buffer 
 351     int code
, readcode
, lastcode
, abcabca
; 
 353     // these won't change 
 354     ab_clr 
= (1 << bits
); 
 355     ab_fin 
= (1 << bits
) + 1; 
 357     // these will change through the decompression process 
 359     ab_free  
= (1 << bits
) + 2; 
 360     ab_max   
= (1 << ab_bits
) - 1; 
 366     // reset decoder vars 
 374         readcode 
= code 
= getcode(stream
, ab_bits
, ab_fin
); 
 377         if (code 
== ab_fin
) break; 
 382             // reset main variables 
 384             ab_free  
= (1 << bits
) + 2; 
 385             ab_max   
= (1 << ab_bits
) - 1; 
 393         // unknown code: special case (like in ABCABCA) 
 396             code 
= lastcode
;            // take last string 
 397             stack
[pos
++] = abcabca
;     // add first character 
 400         // build the string for this code in the stack 
 401         while (code 
> ab_clr
) 
 403             stack
[pos
++] = ab_tail
[code
]; 
 404             code         
= ab_prefix
[code
]; 
 406             // Don't overflow. This shouldn't happen with normal 
 407             // GIF files, the allocSize of 4096+1 is enough. This 
 408             // will only happen with badly formed GIFs. 
 409             if (pos 
>= allocSize
) 
 414                 return wxGIF_INVFORMAT
; 
 418         if (pos 
>= allocSize
) 
 423             return wxGIF_INVFORMAT
; 
 426         stack
[pos
] = code
;              // push last code into the stack 
 427         abcabca    
= code
;              // save for special case 
 429         // make new entry in alphabet (only if NOT just cleared) 
 432             // Normally, after the alphabet is full and can't grow any 
 433             // further (ab_free == 4096), encoder should (must?) emit CLEAR 
 434             // to reset it. This checks whether we really got it, otherwise 
 435             // the GIF is damaged. 
 436             if (ab_free 
> ab_max
) 
 441                 return wxGIF_INVFORMAT
; 
 444             // This assert seems unnecessary since the condition above 
 445             // eliminates the only case in which it went false. But I really 
 446             // don't like being forced to ask "Who in .text could have 
 447             // written there?!" And I wouldn't have been forced to ask if 
 448             // this line had already been here. 
 449             wxASSERT(ab_free 
< allocSize
); 
 451             ab_prefix
[ab_free
] = lastcode
; 
 452             ab_tail
[ab_free
]   = code
; 
 455             if ((ab_free 
> ab_max
) && (ab_bits 
< 12)) 
 458                 ab_max 
= (1 << ab_bits
) - 1; 
 462         // dump stack data to the image buffer 
 465             (img
->p
)[x 
+ (y 
* (img
->w
))] = (char) stack
[pos
]; 
 474                     // support for interlaced images 
 477                         case 1: y 
+= 8; break; 
 478                         case 2: y 
+= 8; break; 
 479                         case 3: y 
+= 4; break; 
 480                         case 4: y 
+= 2; break; 
 483                     /* loop until a valid y coordinate has been 
 484                     found, Or if the maximum number of passes has 
 485                     been reached, exit the loop, and stop image 
 486                     decoding (At this point the image is successfully 
 488                     If we don't loop, but merely set y to some other 
 489                     value, that new value might still be invalid depending 
 490                     on the height of the image. This would cause out of 
 493                     while (y 
>= (img
->h
)) 
 497                             case 2: y 
= 4; break; 
 498                             case 3: y 
= 2; break; 
 499                             case 4: y 
= 1; break; 
 503                                 It's possible we arrive here. For example this 
 504                                 happens when the image is interlaced, and the 
 505                                 height is 1. Looking at the above cases, the 
 506                                 lowest possible y is 1. While the only valid 
 507                                 one would be 0 for an image of height 1. So 
 508                                 'eventually' the loop will arrive here. 
 509                                 This case makes sure this while loop is 
 510                                 exited, as well as the 2 other ones. 
 513                                 // Set y to a valid coordinate so the local 
 514                                 // while loop will be exited. (y = 0 always 
 515                                 // is >= img->h since if img->h == 0 the 
 516                                 // image is never decoded) 
 519                                 // This will exit the other outer while loop 
 522                                 // This will halt image decoding. 
 534 Normally image decoding is finished when an End of Information code is 
 535 encountered (code == ab_fin) however some broken encoders write wrong 
 536 "block byte counts" (The first byte value after the "code size" byte), 
 537 being one value too high. It might very well be possible other variants 
 538 of this problem occur as well. The only sensible solution seems to 
 539 be to check for clipping. 
 540 Example of wrong encoding: 
 541 (1 * 1 B/W image, raster data stream follows in hex bytes) 
 543 02  << B/W images have a code size of 2 
 544 02  << Block byte count 
 546 00  << Zero byte count (terminates data stream) 
 548 Because the block byte count is 2, the zero byte count is used in the 
 549 decoding process, and decoding is continued after this byte. (While it 
 550 should signal an end of image) 
 556 01  << When decoded this correctly includes the End of Information code 
 564 (The 44 doesn't include an End of Information code, but at least the 
 565 decoder correctly skips to 00 now after decoding, and signals this 
 566 as an End of Information itself) 
 580     while (code 
!= ab_fin
); 
 582     delete [] ab_prefix 
; 
 591 //  Returns true if the file looks like a valid GIF, false otherwise. 
 593 bool wxGIFDecoder::DoCanRead(wxInputStream 
&stream
) const 
 595     unsigned char buf
[3]; 
 597     if ( !stream
.Read(buf
, WXSIZEOF(buf
)) ) 
 600     return memcmp(buf
, "GIF", WXSIZEOF(buf
)) == 0; 
 605 //  Reads and decodes one or more GIF images, depending on whether 
 606 //  animated GIF support is enabled. Can read GIFs with any bit 
 607 //  size (color depth), but the output images are always expanded 
 608 //  to 8 bits per pixel. Also, the image palettes always contain 
 609 //  256 colors, although some of them may be unused. Returns wxGIF_OK 
 610 //  (== 0) on success, or an error code if something fails (see 
 611 //  header file for details) 
 613 wxGIFErrorCode 
wxGIFDecoder::LoadGIF(wxInputStream
& stream
) 
 615     unsigned int  global_ncolors 
= 0; 
 617     wxAnimationDisposal disposal
; 
 620     unsigned char type 
= 0; 
 621     unsigned char pal
[768]; 
 622     unsigned char buf
[16]; 
 625     // check GIF signature 
 626     if (!CanRead(stream
)) 
 627         return wxGIF_INVFORMAT
; 
 629     // check for animated GIF support (ver. >= 89a) 
 631     static const unsigned int headerSize 
= (3 + 3); 
 632     stream
.Read(buf
, headerSize
); 
 633     if (stream
.LastRead() != headerSize
) 
 635         return wxGIF_INVFORMAT
; 
 638     if (memcmp(buf 
+ 3, "89a", 3) < 0) 
 643     // read logical screen descriptor block (LSDB) 
 644     static const unsigned int lsdbSize 
= (2 + 2 + 1 + 1 + 1); 
 645     stream
.Read(buf
, lsdbSize
); 
 646     if (stream
.LastRead() != lsdbSize
) 
 648         return wxGIF_INVFORMAT
; 
 651     m_szAnimation
.SetWidth( buf
[0] + 256 * buf
[1] ); 
 652     m_szAnimation
.SetHeight( buf
[2] + 256 * buf
[3] ); 
 654     if (anim 
&& ((m_szAnimation
.GetWidth() == 0) || (m_szAnimation
.GetHeight() == 0))) 
 656         return wxGIF_INVFORMAT
; 
 659     // load global color map if available 
 660     if ((buf
[4] & 0x80) == 0x80) 
 662         int backgroundColIndex 
= buf
[5]; 
 664         global_ncolors 
= 2 << (buf
[4] & 0x07); 
 665         unsigned int numBytes 
= 3 * global_ncolors
; 
 666         stream
.Read(pal
, numBytes
); 
 667         if (stream
.LastRead() != numBytes
) 
 669             return wxGIF_INVFORMAT
; 
 672         m_background
.Set(pal
[backgroundColIndex
*3 + 0], 
 673                          pal
[backgroundColIndex
*3 + 1], 
 674                          pal
[backgroundColIndex
*3 + 2]); 
 677     // transparent colour, disposal method and delay default to unused 
 678     int transparent 
= -1; 
 679     disposal 
= wxANIM_UNSPECIFIED
; 
 686         type 
= stream
.GetC(); 
 689         If the end of file has been reached (or an error) and a ";" 
 690         (GIF_MARKER_ENDOFDATA) hasn't been encountered yet, exit the loop. (Without this 
 691         check the while loop would loop endlessly.) Later on, in the next while 
 692         loop, the file will be treated as being truncated (But still 
 693         be decoded as far as possible). returning wxGIF_TRUNCATED is not 
 694         possible here since some init code is done after this loop. 
 696         if (stream
.Eof())// || !stream.IsOk()) 
 699             type is set to some bogus value, so there's no 
 700             need to continue evaluating it. 
 702             break; // Alternative : "return wxGIF_INVFORMAT;" 
 707             case GIF_MARKER_ENDOFDATA
: 
 711                 switch (stream
.GetC()) 
 713                     case GIF_MARKER_EXT_GRAPHICS_CONTROL
: 
 715                         // graphics control extension, parse it 
 717                         static const unsigned int gceSize 
= 6; 
 718                         stream
.Read(buf
, gceSize
); 
 719                         if (stream
.LastRead() != gceSize
) 
 722                             return wxGIF_INVFORMAT
; 
 725                         // read delay and convert from 1/100 of a second to ms 
 726                         delay 
= 10 * (buf
[2] + 256 * buf
[3]); 
 728                         // read transparent colour index, if used 
 729                         transparent 
= buf
[1] & 0x01 ? buf
[4] : -1; 
 731                         // read disposal method 
 732                         disposal 
= (wxAnimationDisposal
)(((buf
[1] & 0x1C) >> 2) - 1); 
 735                     case GIF_MARKER_EXT_COMMENT
: 
 737                         int len 
= stream
.GetC(); 
 746                             wxCharBuffer 
charbuf(len
); 
 747                             stream
.Read(charbuf
.data(), len
); 
 748                             if ( (int) stream
.LastRead() != len 
) 
 754                             comment 
+= wxConvertMB2WX(charbuf
.data()); 
 762                         // other extension, skip 
 763                         while ((i 
= stream
.GetC()) != 0) 
 765                             if (stream
.Eof() || (stream
.LastRead() == 0) || 
 766                                 stream
.SeekI(i
, wxFromCurrent
) == wxInvalidOffset
) 
 777                 // allocate memory for IMAGEN struct 
 778                 GIFImagePtr 
pimg(new GIFImage()); 
 780                 wxScopeGuard guardDestroy 
= wxMakeObjGuard(*this, &wxGIFDecoder::Destroy
); 
 786                 static const unsigned int idbSize 
= (2 + 2 + 2 + 2 + 1); 
 787                 stream
.Read(buf
, idbSize
); 
 788                 if (stream
.LastRead() != idbSize
) 
 789                     return wxGIF_INVFORMAT
; 
 791                 pimg
->comment 
= comment
; 
 793                 pimg
->left 
= buf
[0] + 256 * buf
[1]; 
 794                 pimg
->top 
= buf
[2] + 256 * buf
[3]; 
 796                 pimg->left = buf[4] + 256 * buf[5]; 
 797                 pimg->top = buf[4] + 256 * buf[5]; 
 799                 pimg
->w 
= buf
[4] + 256 * buf
[5]; 
 800                 pimg
->h 
= buf
[6] + 256 * buf
[7]; 
 804                     // some GIF images specify incorrect animation size but we can 
 805                     // still open them if we fix up the animation size, see #9465 
 806                     if ( m_nFrames 
== 0 ) 
 808                         if ( pimg
->w 
> (unsigned)m_szAnimation
.x 
) 
 809                             m_szAnimation
.x 
= pimg
->w
; 
 810                         if ( pimg
->h 
> (unsigned)m_szAnimation
.y 
) 
 811                             m_szAnimation
.y 
= pimg
->h
; 
 813                     else // subsequent frames 
 815                         // check that we have valid size 
 816                         if ( (!pimg
->w 
|| pimg
->w 
> (unsigned)m_szAnimation
.x
) || 
 817                                 (!pimg
->h 
|| pimg
->h 
> (unsigned)m_szAnimation
.y
) ) 
 819                             wxLogError(_("Incorrect GIF frame size (%u, %d) for " 
 821                                        pimg
->w
, pimg
->h
, m_nFrames
); 
 822                             return wxGIF_INVFORMAT
; 
 827                 interl 
= ((buf
[8] & 0x40)? 1 : 0); 
 828                 size 
= pimg
->w 
* pimg
->h
; 
 830                 pimg
->transparent 
= transparent
; 
 831                 pimg
->disposal 
= disposal
; 
 834                 // allocate memory for image and palette 
 835                 pimg
->p   
= (unsigned char *) malloc((unsigned int)size
); 
 836                 pimg
->pal 
= (unsigned char *) malloc(768); 
 838                 if ((!pimg
->p
) || (!pimg
->pal
)) 
 841                 // load local color map if available, else use global map 
 842                 if ((buf
[8] & 0x80) == 0x80) 
 844                     unsigned int local_ncolors 
= 2 << (buf
[8] & 0x07); 
 845                     unsigned int numBytes 
= 3 * local_ncolors
; 
 846                     stream
.Read(pimg
->pal
, numBytes
); 
 847                     pimg
->ncolours 
= local_ncolors
; 
 848                     if (stream
.LastRead() != numBytes
) 
 849                         return wxGIF_INVFORMAT
; 
 853                     memcpy(pimg
->pal
, pal
, 768); 
 854                     pimg
->ncolours 
= global_ncolors
; 
 857                 // get initial code size from first byte in raster data 
 858                 bits 
= stream
.GetC(); 
 860                     return wxGIF_INVFORMAT
; 
 863                 wxGIFErrorCode result 
= dgif(stream
, pimg
.get(), interl
, bits
); 
 864                 if (result 
!= wxGIF_OK
) 
 867                 guardDestroy
.Dismiss(); 
 869                 // add the image to our frame array 
 870                 m_frames
.Add(pimg
.release()); 
 873                 // if this is not an animated GIF, exit after first image 
 884         return wxGIF_INVFORMAT
; 
 887     // try to read to the end of the stream 
 888     while (type 
!= GIF_MARKER_ENDOFDATA
) 
 891             return wxGIF_TRUNCATED
; 
 893         type 
= stream
.GetC(); 
 899                 (void) stream
.GetC(); 
 902                 while ((i 
= stream
.GetC()) != 0) 
 904                     if (stream
.Eof() || (stream
.LastRead() == 0) || 
 905                         stream
.SeekI(i
, wxFromCurrent
) == wxInvalidOffset
) 
 908                         return wxGIF_INVFORMAT
; 
 914                 // image descriptor block 
 915                 static const unsigned int idbSize 
= (2 + 2 + 2 + 2 + 1); 
 916                 stream
.Read(buf
, idbSize
); 
 917                 if (stream
.LastRead() != idbSize
) 
 920                     return wxGIF_INVFORMAT
; 
 924                 if ((buf
[8] & 0x80) == 0x80) 
 926                     unsigned int local_ncolors 
= 2 << (buf
[8] & 0x07); 
 927                     wxFileOffset numBytes 
= 3 * local_ncolors
; 
 928                     if (stream
.SeekI(numBytes
, wxFromCurrent
) == wxInvalidOffset
) 
 931                         return wxGIF_INVFORMAT
; 
 936                 (void) stream
.GetC(); 
 937                 if (stream
.Eof() || (stream
.LastRead() == 0)) 
 940                     return wxGIF_INVFORMAT
; 
 944                 while ((i 
= stream
.GetC()) != 0) 
 946                     if (stream
.Eof() || (stream
.LastRead() == 0) || 
 947                         stream
.SeekI(i
, wxFromCurrent
) == wxInvalidOffset
) 
 950                         return wxGIF_INVFORMAT
; 
 956                 if ((type 
!= GIF_MARKER_ENDOFDATA
) && (type 
!= 00)) // testing 
 958                     // images are OK, but couldn't read to the end of the stream 
 959                     return wxGIF_TRUNCATED
; 
 968 #endif // wxUSE_STREAMS && wxUSE_GIF