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