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