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