]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/common/gifdecod.cpp
DoGetBestSize fix, the lbWidth was not getting updated because of a
[wxWidgets.git] / src / common / gifdecod.cpp
... / ...
CommitLineData
1/////////////////////////////////////////////////////////////////////////////
2// Name: src/common/gifdecod.cpp
3// Purpose: wxGIFDecoder, GIF reader for wxImage and wxAnimation
4// Author: Guillermo Rodriguez Garcia <guille@iies.es>
5// Version: 3.04
6// RCS-ID: $Id$
7// Copyright: (c) Guillermo Rodriguez Garcia
8// Licence: wxWindows licence
9/////////////////////////////////////////////////////////////////////////////
10
11// For compilers that support precompilation, includes "wx.h".
12#include "wx/wxprec.h"
13
14#ifdef __BORLANDC__
15 #pragma hdrstop
16#endif
17
18#if wxUSE_STREAMS && wxUSE_GIF
19
20#ifndef WX_PRECOMP
21 #include "wx/palette.h"
22#endif
23
24#include <stdlib.h>
25#include <string.h>
26#include "wx/gifdecod.h"
27
28
29
30//---------------------------------------------------------------------------
31// GIFImage
32//---------------------------------------------------------------------------
33
34// internal class for storing GIF image data
35class GIFImage
36{
37public:
38 // def ctor
39 GIFImage();
40
41 unsigned int w; // width
42 unsigned int h; // height
43 unsigned int left; // x coord (in logical screen)
44 unsigned int top; // y coord (in logical screen)
45 int transparent; // transparent color index (-1 = none)
46 wxAnimationDisposal disposal; // disposal method
47 long delay; // delay in ms (-1 = unused)
48 unsigned char *p; // bitmap
49 unsigned char *pal; // palette
50 unsigned int ncolours; // number of colours
51
52 DECLARE_NO_COPY_CLASS(GIFImage)
53};
54
55
56
57//---------------------------------------------------------------------------
58// GIFImage constructor
59//---------------------------------------------------------------------------
60GIFImage::GIFImage()
61{
62 w = 0;
63 h = 0;
64 left = 0;
65 top = 0;
66 transparent = 0;
67 disposal = wxANIM_DONOTREMOVE;
68 delay = -1;
69 p = (unsigned char *) NULL;
70 pal = (unsigned char *) NULL;
71 ncolours = 0;
72}
73
74//---------------------------------------------------------------------------
75// wxGIFDecoder constructor and destructor
76//---------------------------------------------------------------------------
77
78wxGIFDecoder::wxGIFDecoder()
79{
80}
81
82wxGIFDecoder::~wxGIFDecoder()
83{
84 Destroy();
85}
86
87void wxGIFDecoder::Destroy()
88{
89 wxASSERT(m_nFrames==m_frames.GetCount());
90 for (unsigned int i=0; i<m_nFrames; i++)
91 {
92 GIFImage *f = (GIFImage*)m_frames[i];
93 free(f->p);
94 free(f->pal);
95 delete f;
96 }
97
98 m_frames.Clear();
99 m_nFrames = 0;
100}
101
102
103//---------------------------------------------------------------------------
104// Convert this image to a wxImage object
105//---------------------------------------------------------------------------
106
107// This function was designed by Vaclav Slavik
108
109bool wxGIFDecoder::ConvertToImage(unsigned int frame, wxImage *image) const
110{
111 unsigned char *src, *dst, *pal;
112 unsigned long i;
113 int transparent;
114
115 // just in case...
116 image->Destroy();
117
118 // create the image
119 wxSize sz = GetFrameSize(frame);
120 image->Create(sz.GetWidth(), sz.GetHeight());
121
122 if (!image->Ok())
123 return false;
124
125 pal = GetPalette(frame);
126 src = GetData(frame);
127 dst = image->GetData();
128 transparent = GetTransparentColourIndex(frame);
129
130 // set transparent colour mask
131 if (transparent != -1)
132 {
133 for (i = 0; i < GetNcolours(frame); i++)
134 {
135 if ((pal[3 * i + 0] == 255) &&
136 (pal[3 * i + 1] == 0) &&
137 (pal[3 * i + 2] == 255))
138 {
139 pal[3 * i + 2] = 254;
140 }
141 }
142
143 pal[3 * transparent + 0] = 255,
144 pal[3 * transparent + 1] = 0,
145 pal[3 * transparent + 2] = 255;
146
147 image->SetMaskColour(255, 0, 255);
148 }
149 else
150 image->SetMask(false);
151
152#if wxUSE_PALETTE
153 unsigned char r[256];
154 unsigned char g[256];
155 unsigned char b[256];
156
157 for (i = 0; i < 256; i++)
158 {
159 r[i] = pal[3*i + 0];
160 g[i] = pal[3*i + 1];
161 b[i] = pal[3*i + 2];
162 }
163
164 image->SetPalette(wxPalette(GetNcolours(frame), r, g, b));
165#endif // wxUSE_PALETTE
166
167 // copy image data
168 unsigned long npixel = sz.GetWidth() * sz.GetHeight();
169 for (i = 0; i < npixel; i++, src++)
170 {
171 *(dst++) = pal[3 * (*src) + 0];
172 *(dst++) = pal[3 * (*src) + 1];
173 *(dst++) = pal[3 * (*src) + 2];
174 }
175
176 return true;
177}
178
179
180//---------------------------------------------------------------------------
181// Data accessors
182//---------------------------------------------------------------------------
183
184#define GetFrame(n) ((GIFImage*)m_frames[n])
185
186
187// Get data for current frame
188
189wxSize wxGIFDecoder::GetFrameSize(unsigned int frame) const
190{
191 return wxSize(GetFrame(frame)->w, GetFrame(frame)->h);
192}
193
194wxPoint wxGIFDecoder::GetFramePosition(unsigned int frame) const
195{
196 return wxPoint(GetFrame(frame)->left, GetFrame(frame)->top);
197}
198
199wxAnimationDisposal wxGIFDecoder::GetDisposalMethod(unsigned int frame) const
200{
201 return GetFrame(frame)->disposal;
202}
203
204long wxGIFDecoder::GetDelay(unsigned int frame) const
205{
206 return GetFrame(frame)->delay;
207}
208
209wxColour wxGIFDecoder::GetTransparentColour(unsigned int frame) const
210{
211 unsigned char *pal = GetFrame(frame)->pal;
212 int n = GetFrame(frame)->transparent;
213 if (n == -1)
214 return wxNullColour;
215
216 return wxColour(pal[n*3 + 0],
217 pal[n*3 + 1],
218 pal[n*3 + 2]);
219}
220
221unsigned char* wxGIFDecoder::GetData(unsigned int frame) const { return (GetFrame(frame)->p); }
222unsigned char* wxGIFDecoder::GetPalette(unsigned int frame) const { return (GetFrame(frame)->pal); }
223unsigned int wxGIFDecoder::GetNcolours(unsigned int frame) const { return (GetFrame(frame)->ncolours); }
224int wxGIFDecoder::GetTransparentColourIndex(unsigned int frame) const { return (GetFrame(frame)->transparent); }
225
226
227
228//---------------------------------------------------------------------------
229// GIF reading and decoding
230//---------------------------------------------------------------------------
231
232// getcode:
233// Reads the next code from the file stream, with size 'bits'
234//
235int wxGIFDecoder::getcode(wxInputStream& stream, int bits, int ab_fin)
236{
237 unsigned int mask; // bit mask
238 unsigned int code; // code (result)
239
240 // get remaining bits from last byte read
241 mask = (1 << bits) - 1;
242 code = (m_lastbyte >> (8 - m_restbits)) & mask;
243
244 // keep reading new bytes while needed
245 while (bits > m_restbits)
246 {
247 // if no bytes left in this block, read the next block
248 if (m_restbyte == 0)
249 {
250 m_restbyte = (unsigned char)stream.GetC();
251
252 /* Some encoders are a bit broken: instead of issuing
253 * an end-of-image symbol (ab_fin) they come up with
254 * a zero-length subblock!! We catch this here so
255 * that the decoder sees an ab_fin code.
256 */
257 if (m_restbyte == 0)
258 {
259 code = ab_fin;
260 break;
261 }
262
263 // prefetch data
264 stream.Read((void *) m_buffer, m_restbyte);
265 if (stream.LastRead() != m_restbyte)
266 {
267 code = ab_fin;
268 return code;
269 }
270 m_bufp = m_buffer;
271 }
272
273 // read next byte and isolate the bits we need
274 m_lastbyte = (unsigned char) (*m_bufp++);
275 mask = (1 << (bits - m_restbits)) - 1;
276 code = code + ((m_lastbyte & mask) << m_restbits);
277 m_restbyte--;
278
279 // adjust total number of bits extracted from the buffer
280 m_restbits = m_restbits + 8;
281 }
282
283 // find number of bits remaining for next code
284 m_restbits = (m_restbits - bits);
285
286 return code;
287}
288
289
290// dgif:
291// GIF decoding function. The initial code size (aka root size)
292// is 'bits'. Supports interlaced images (interl == 1).
293// Returns wxGIF_OK (== 0) on success, or an error code if something
294// fails (see header file for details)
295wxGIFErrorCode
296wxGIFDecoder::dgif(wxInputStream& stream, GIFImage *img, int interl, int bits)
297{
298 static const int allocSize = 4096 + 1;
299 int *ab_prefix = new int[allocSize]; // alphabet (prefixes)
300 if (ab_prefix == NULL)
301 {
302 return wxGIF_MEMERR;
303 }
304
305 int *ab_tail = new int[allocSize]; // alphabet (tails)
306 if (ab_tail == NULL)
307 {
308 delete[] ab_prefix;
309 return wxGIF_MEMERR;
310 }
311
312 int *stack = new int[allocSize]; // decompression stack
313 if (stack == NULL)
314 {
315 delete[] ab_prefix;
316 delete[] ab_tail;
317 return wxGIF_MEMERR;
318 }
319
320 int ab_clr; // clear code
321 int ab_fin; // end of info code
322 int ab_bits; // actual symbol width, in bits
323 int ab_free; // first free position in alphabet
324 int ab_max; // last possible character in alphabet
325 int pass; // pass number in interlaced images
326 int pos; // index into decompresion stack
327 unsigned int x, y; // position in image buffer
328
329 int code, readcode, lastcode, abcabca;
330
331 // these won't change
332 ab_clr = (1 << bits);
333 ab_fin = (1 << bits) + 1;
334
335 // these will change through the decompression proccess
336 ab_bits = bits + 1;
337 ab_free = (1 << bits) + 2;
338 ab_max = (1 << ab_bits) - 1;
339 lastcode = -1;
340 abcabca = -1;
341 pass = 1;
342 pos = x = y = 0;
343
344 // reset decoder vars
345 m_restbits = 0;
346 m_restbyte = 0;
347 m_lastbyte = 0;
348
349 do
350 {
351 // get next code
352 readcode = code = getcode(stream, ab_bits, ab_fin);
353
354 // end of image?
355 if (code == ab_fin) break;
356
357 // reset alphabet?
358 if (code == ab_clr)
359 {
360 // reset main variables
361 ab_bits = bits + 1;
362 ab_free = (1 << bits) + 2;
363 ab_max = (1 << ab_bits) - 1;
364 lastcode = -1;
365 abcabca = -1;
366
367 // skip to next code
368 continue;
369 }
370
371 // unknown code: special case (like in ABCABCA)
372 if (code >= ab_free)
373 {
374 code = lastcode; // take last string
375 stack[pos++] = abcabca; // add first character
376 }
377
378 // build the string for this code in the stack
379 while (code > ab_clr)
380 {
381 stack[pos++] = ab_tail[code];
382 code = ab_prefix[code];
383
384 // Don't overflow. This shouldn't happen with normal
385 // GIF files, the allocSize of 4096+1 is enough. This
386 // will only happen with badly formed GIFs.
387 if (pos >= allocSize)
388 {
389 delete[] ab_prefix;
390 delete[] ab_tail;
391 delete[] stack;
392 return wxGIF_INVFORMAT;
393 }
394 }
395
396 if (pos >= allocSize)
397 {
398 delete[] ab_prefix;
399 delete[] ab_tail;
400 delete[] stack;
401 return wxGIF_INVFORMAT;
402 }
403
404 stack[pos] = code; // push last code into the stack
405 abcabca = code; // save for special case
406
407 // make new entry in alphabet (only if NOT just cleared)
408 if (lastcode != -1)
409 {
410 // Normally, after the alphabet is full and can't grow any
411 // further (ab_free == 4096), encoder should (must?) emit CLEAR
412 // to reset it. This checks whether we really got it, otherwise
413 // the GIF is damaged.
414 if (ab_free > ab_max)
415 {
416 delete[] ab_prefix;
417 delete[] ab_tail;
418 delete[] stack;
419 return wxGIF_INVFORMAT;
420 }
421
422 // This assert seems unnecessary since the condition above
423 // eliminates the only case in which it went false. But I really
424 // don't like being forced to ask "Who in .text could have
425 // written there?!" And I wouldn't have been forced to ask if
426 // this line had already been here.
427 wxASSERT(ab_free < allocSize);
428
429 ab_prefix[ab_free] = lastcode;
430 ab_tail[ab_free] = code;
431 ab_free++;
432
433 if ((ab_free > ab_max) && (ab_bits < 12))
434 {
435 ab_bits++;
436 ab_max = (1 << ab_bits) - 1;
437 }
438 }
439
440 // dump stack data to the image buffer
441 while (pos >= 0)
442 {
443 (img->p)[x + (y * (img->w))] = (char) stack[pos];
444 pos--;
445
446 if (++x >= (img->w))
447 {
448 x = 0;
449
450 if (interl)
451 {
452 // support for interlaced images
453 switch (pass)
454 {
455 case 1: y += 8; break;
456 case 2: y += 8; break;
457 case 3: y += 4; break;
458 case 4: y += 2; break;
459 }
460
461 /* loop until a valid y coordinate has been
462 found, Or if the maximum number of passes has
463 been reached, exit the loop, and stop image
464 decoding (At this point the image is successfully
465 decoded).
466 If we don't loop, but merely set y to some other
467 value, that new value might still be invalid depending
468 on the height of the image. This would cause out of
469 bounds writing.
470 */
471 while (y >= (img->h))
472 {
473 switch (++pass)
474 {
475 case 2: y = 4; break;
476 case 3: y = 2; break;
477 case 4: y = 1; break;
478
479 default:
480 /*
481 It's possible we arrive here. For example this
482 happens when the image is interlaced, and the
483 height is 1. Looking at the above cases, the
484 lowest possible y is 1. While the only valid
485 one would be 0 for an image of height 1. So
486 'eventually' the loop will arrive here.
487 This case makes sure this while loop is
488 exited, as well as the 2 other ones.
489 */
490
491 // Set y to a valid coordinate so the local
492 // while loop will be exited. (y = 0 always
493 // is >= img->h since if img->h == 0 the
494 // image is never decoded)
495 y = 0;
496
497 // This will exit the other outer while loop
498 pos = -1;
499
500 // This will halt image decoding.
501 code = ab_fin;
502
503 break;
504 }
505 }
506 }
507 else
508 {
509 // non-interlaced
510 y++;
511/*
512Normally image decoding is finished when an End of Information code is
513encountered (code == ab_fin) however some broken encoders write wrong
514"block byte counts" (The first byte value after the "code size" byte),
515being one value too high. It might very well be possible other variants
516of this problem occur as well. The only sensible solution seems to
517be to check for clipping.
518Example of wrong encoding:
519(1 * 1 B/W image, raster data stream follows in hex bytes)
520
52102 << B/W images have a code size of 2
52202 << Block byte count
52344 << LZW packed
52400 << Zero byte count (terminates data stream)
525
526Because the block byte count is 2, the zero byte count is used in the
527decoding process, and decoding is continued after this byte. (While it
528should signal an end of image)
529
530It should be:
53102
53202
53344
53401 << When decoded this correctly includes the End of Information code
53500
536
537Or (Worse solution):
53802
53901
54044
54100
542(The 44 doesn't include an End of Information code, but at least the
543decoder correctly skips to 00 now after decoding, and signals this
544as an End of Information itself)
545*/
546 if (y >= img->h)
547 {
548 code = ab_fin;
549 break;
550 }
551 }
552 }
553 }
554
555 pos = 0;
556 lastcode = readcode;
557 }
558 while (code != ab_fin);
559
560 delete [] ab_prefix ;
561 delete [] ab_tail ;
562 delete [] stack ;
563
564 return wxGIF_OK;
565}
566
567
568// CanRead:
569// Returns true if the file looks like a valid GIF, false otherwise.
570//
571bool wxGIFDecoder::CanRead(wxInputStream &stream) const
572{
573 unsigned char buf[3];
574
575 if ( !stream.Read(buf, WXSIZEOF(buf)) )
576 return false;
577
578 stream.SeekI(-(wxFileOffset)WXSIZEOF(buf), wxFromCurrent);
579
580 return memcmp(buf, "GIF", WXSIZEOF(buf)) == 0;
581}
582
583
584// LoadGIF:
585// Reads and decodes one or more GIF images, depending on whether
586// animated GIF support is enabled. Can read GIFs with any bit
587// size (color depth), but the output images are always expanded
588// to 8 bits per pixel. Also, the image palettes always contain
589// 256 colors, although some of them may be unused. Returns wxGIF_OK
590// (== 0) on success, or an error code if something fails (see
591// header file for details)
592//
593wxGIFErrorCode wxGIFDecoder::LoadGIF(wxInputStream& stream)
594{
595 unsigned int global_ncolors = 0;
596 int bits, interl, transparent, i;
597 wxAnimationDisposal disposal;
598 long size;
599 long delay;
600 unsigned char type = 0;
601 unsigned char pal[768];
602 unsigned char buf[16];
603 bool anim = true;
604
605 // check GIF signature
606 if (!CanRead(stream))
607 return wxGIF_INVFORMAT;
608
609 // check for animated GIF support (ver. >= 89a)
610
611 static const unsigned int headerSize = (3 + 3);
612 stream.Read(buf, headerSize);
613 if (stream.LastRead() != headerSize)
614 {
615 return wxGIF_INVFORMAT;
616 }
617
618 if (memcmp(buf + 3, "89a", 3) < 0)
619 {
620 anim = false;
621 }
622
623 // read logical screen descriptor block (LSDB)
624 static const unsigned int lsdbSize = (2 + 2 + 1 + 1 + 1);
625 stream.Read(buf, lsdbSize);
626 if (stream.LastRead() != lsdbSize)
627 {
628 return wxGIF_INVFORMAT;
629 }
630
631 m_szAnimation.SetWidth( buf[0] + 256 * buf[1] );
632 m_szAnimation.SetHeight( buf[2] + 256 * buf[3] );
633
634 if (anim && ((m_szAnimation.GetWidth() == 0) || (m_szAnimation.GetHeight() == 0)))
635 {
636 return wxGIF_INVFORMAT;
637 }
638
639 // load global color map if available
640 if ((buf[4] & 0x80) == 0x80)
641 {
642 int backgroundColIndex = buf[5];
643
644 global_ncolors = 2 << (buf[4] & 0x07);
645 unsigned int numBytes = 3 * global_ncolors;
646 stream.Read(pal, numBytes);
647 if (stream.LastRead() != numBytes)
648 {
649 return wxGIF_INVFORMAT;
650 }
651
652 m_background.Set(pal[backgroundColIndex*3 + 0],
653 pal[backgroundColIndex*3 + 1],
654 pal[backgroundColIndex*3 + 2]);
655 }
656
657 // transparent colour, disposal method and delay default to unused
658 transparent = -1;
659 disposal = wxANIM_UNSPECIFIED;
660 delay = -1;
661
662 bool done = false;
663 while (!done)
664 {
665 type = (unsigned char)stream.GetC();
666
667 /*
668 If the end of file has been reached (or an error) and a ";"
669 (0x3B) hasn't been encountered yet, exit the loop. (Without this
670 check the while loop would loop endlessly.) Later on, in the next while
671 loop, the file will be treated as being truncated (But still
672 be decoded as far as possible). returning wxGIF_TRUNCATED is not
673 possible here since some init code is done after this loop.
674 */
675 if (stream.Eof())// || !stream.IsOk())
676 {
677 /*
678 type is set to some bogus value, so there's no
679 need to continue evaluating it.
680 */
681 break; // Alternative : "return wxGIF_INVFORMAT;"
682 }
683
684 // end of data?
685 if (type == 0x3B)
686 {
687 done = true;
688 }
689 else
690 // extension block?
691 if (type == 0x21)
692 {
693 if (((unsigned char)stream.GetC()) == 0xF9)
694 // graphics control extension, parse it
695 {
696 static const unsigned int gceSize = 6;
697 stream.Read(buf, gceSize);
698 if (stream.LastRead() != gceSize)
699 {
700 Destroy();
701 return wxGIF_INVFORMAT;
702 }
703
704 // read delay and convert from 1/100 of a second to ms
705 delay = 10 * (buf[2] + 256 * buf[3]);
706
707 // read transparent colour index, if used
708 if (buf[1] & 0x01)
709 transparent = buf[4];
710
711 // read disposal method
712 disposal = (wxAnimationDisposal)(((buf[1] & 0x1C) >> 2) - 1);
713 }
714 else
715 // other extension, skip
716 {
717 while ((i = (unsigned char)stream.GetC()) != 0)
718 {
719 if (stream.Eof() || (stream.LastRead() == 0))
720 {
721 done = true;
722 break;
723 }
724 stream.SeekI(i, wxFromCurrent);
725 }
726 }
727 }
728 else
729 // image descriptor block?
730 if (type == 0x2C)
731 {
732 // allocate memory for IMAGEN struct
733 GIFImage *pimg = new GIFImage();
734
735 if (pimg == NULL)
736 {
737 Destroy();
738 return wxGIF_MEMERR;
739 }
740
741 // fill in the data
742 static const unsigned int idbSize = (2 + 2 + 2 + 2 + 1);
743 stream.Read(buf, idbSize);
744 if (stream.LastRead() != idbSize)
745 {
746 Destroy();
747 return wxGIF_INVFORMAT;
748 }
749
750 pimg->left = buf[0] + 256 * buf[1];
751 pimg->top = buf[2] + 256 * buf[3];
752/*
753 pimg->left = buf[4] + 256 * buf[5];
754 pimg->top = buf[4] + 256 * buf[5];
755*/
756 pimg->w = buf[4] + 256 * buf[5];
757 pimg->h = buf[6] + 256 * buf[7];
758
759 if (anim && ((pimg->w == 0) || (pimg->w > (unsigned int)m_szAnimation.GetWidth()) ||
760 (pimg->h == 0) || (pimg->h > (unsigned int)m_szAnimation.GetHeight())))
761 {
762 Destroy();
763 return wxGIF_INVFORMAT;
764 }
765
766 interl = ((buf[8] & 0x40)? 1 : 0);
767 size = pimg->w * pimg->h;
768
769 pimg->transparent = transparent;
770 pimg->disposal = disposal;
771 pimg->delay = delay;
772
773 // allocate memory for image and palette
774 pimg->p = (unsigned char *) malloc((unsigned int)size);
775 pimg->pal = (unsigned char *) malloc(768);
776
777 if ((!pimg->p) || (!pimg->pal))
778 {
779 Destroy();
780 return wxGIF_MEMERR;
781 }
782
783 // load local color map if available, else use global map
784 if ((buf[8] & 0x80) == 0x80)
785 {
786 unsigned int local_ncolors = 2 << (buf[8] & 0x07);
787 unsigned int numBytes = 3 * local_ncolors;
788 stream.Read(pimg->pal, numBytes);
789 pimg->ncolours = local_ncolors;
790 if (stream.LastRead() != numBytes)
791 {
792 Destroy();
793 return wxGIF_INVFORMAT;
794 }
795 }
796 else
797 {
798 memcpy(pimg->pal, pal, 768);
799 pimg->ncolours = global_ncolors;
800 }
801
802 // get initial code size from first byte in raster data
803 bits = (unsigned char)stream.GetC();
804 if (bits == 0)
805 {
806 Destroy();
807 return wxGIF_INVFORMAT;
808 }
809
810 // decode image
811 wxGIFErrorCode result = dgif(stream, pimg, interl, bits);
812 if (result != wxGIF_OK)
813 {
814 Destroy();
815 return result;
816 }
817
818 // add the image to our frame array
819 m_frames.Add((void*)pimg);
820 m_nFrames++;
821
822 // if this is not an animated GIF, exit after first image
823 if (!anim)
824 done = true;
825 }
826 }
827
828 if (m_nFrames <= 0)
829 {
830 Destroy();
831 return wxGIF_INVFORMAT;
832 }
833
834 // try to read to the end of the stream
835 while (type != 0x3B)
836 {
837 if (!stream.IsOk())
838 return wxGIF_TRUNCATED;
839
840 type = (unsigned char)stream.GetC();
841
842 if (type == 0x21)
843 {
844 // extension type
845 (void) stream.GetC();
846
847 // skip all data
848 while ((i = (unsigned char)stream.GetC()) != 0)
849 {
850 if (stream.Eof() || (stream.LastRead() == 0))
851 {
852 Destroy();
853 return wxGIF_INVFORMAT;
854 }
855 stream.SeekI(i, wxFromCurrent);
856 }
857 }
858 else if (type == 0x2C)
859 {
860 // image descriptor block
861 static const unsigned int idbSize = (2 + 2 + 2 + 2 + 1);
862 stream.Read(buf, idbSize);
863 if (stream.LastRead() != idbSize)
864 {
865 Destroy();
866 return wxGIF_INVFORMAT;
867 }
868
869 // local color map
870 if ((buf[8] & 0x80) == 0x80)
871 {
872 unsigned int local_ncolors = 2 << (buf[8] & 0x07);
873 wxFileOffset numBytes = 3 * local_ncolors;
874 stream.SeekI(numBytes, wxFromCurrent);
875 }
876
877 // initial code size
878 (void) stream.GetC();
879 if (stream.Eof() || (stream.LastRead() == 0))
880 {
881 Destroy();
882 return wxGIF_INVFORMAT;
883 }
884
885 // skip all data
886 while ((i = (unsigned char)stream.GetC()) != 0)
887 {
888 if (stream.Eof() || (stream.LastRead() == 0))
889 {
890 Destroy();
891 return wxGIF_INVFORMAT;
892 }
893 stream.SeekI(i, wxFromCurrent);
894 }
895 }
896 else if ((type != 0x3B) && (type != 00)) // testing
897 {
898 // images are OK, but couldn't read to the end of the stream
899 return wxGIF_TRUNCATED;
900 }
901 }
902
903 return wxGIF_OK;
904}
905
906#endif // wxUSE_STREAMS && wxUSE_GIF