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