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