]> git.saurik.com Git - wxWidgets.git/blob - src/common/gifdecod.cpp
trying to resolve GSocketManager API mess: the meaning of Install/Uninstall_Callback...
[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 #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
37 class GIFImage
38 {
39 public:
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
57 wxDECLARE_SCOPED_PTR(GIFImage, GIFImagePtr)
58 wxDEFINE_SCOPED_PTR(GIFImage, GIFImagePtr)
59
60
61 //---------------------------------------------------------------------------
62 // GIFImage constructor
63 //---------------------------------------------------------------------------
64 GIFImage::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
82 wxGIFDecoder::wxGIFDecoder()
83 {
84 }
85
86 wxGIFDecoder::~wxGIFDecoder()
87 {
88 Destroy();
89 }
90
91 void 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
113 bool 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
194 wxSize wxGIFDecoder::GetFrameSize(unsigned int frame) const
195 {
196 return wxSize(GetFrame(frame)->w, GetFrame(frame)->h);
197 }
198
199 wxPoint wxGIFDecoder::GetFramePosition(unsigned int frame) const
200 {
201 return wxPoint(GetFrame(frame)->left, GetFrame(frame)->top);
202 }
203
204 wxAnimationDisposal wxGIFDecoder::GetDisposalMethod(unsigned int frame) const
205 {
206 return GetFrame(frame)->disposal;
207 }
208
209 long wxGIFDecoder::GetDelay(unsigned int frame) const
210 {
211 return GetFrame(frame)->delay;
212 }
213
214 wxColour 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
226 unsigned char* wxGIFDecoder::GetData(unsigned int frame) const { return (GetFrame(frame)->p); }
227 unsigned char* wxGIFDecoder::GetPalette(unsigned int frame) const { return (GetFrame(frame)->pal); }
228 unsigned int wxGIFDecoder::GetNcolours(unsigned int frame) const { return (GetFrame(frame)->ncolours); }
229 int 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 //
240 int 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)
300 wxGIFErrorCode
301 wxGIFDecoder::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 /*
517 Normally image decoding is finished when an End of Information code is
518 encountered (code == ab_fin) however some broken encoders write wrong
519 "block byte counts" (The first byte value after the "code size" byte),
520 being one value too high. It might very well be possible other variants
521 of this problem occur as well. The only sensible solution seems to
522 be to check for clipping.
523 Example of wrong encoding:
524 (1 * 1 B/W image, raster data stream follows in hex bytes)
525
526 02 << B/W images have a code size of 2
527 02 << Block byte count
528 44 << LZW packed
529 00 << Zero byte count (terminates data stream)
530
531 Because the block byte count is 2, the zero byte count is used in the
532 decoding process, and decoding is continued after this byte. (While it
533 should signal an end of image)
534
535 It should be:
536 02
537 02
538 44
539 01 << When decoded this correctly includes the End of Information code
540 00
541
542 Or (Worse solution):
543 02
544 01
545 44
546 00
547 (The 44 doesn't include an End of Information code, but at least the
548 decoder correctly skips to 00 now after decoding, and signals this
549 as 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 //
576 bool wxGIFDecoder::CanRead(wxInputStream &stream) const
577 {
578 unsigned char buf[3];
579
580 if ( !stream.Read(buf, WXSIZEOF(buf)) )
581 return false;
582
583 stream.SeekI(-(wxFileOffset)WXSIZEOF(buf), wxFromCurrent);
584
585 return memcmp(buf, "GIF", WXSIZEOF(buf)) == 0;
586 }
587
588
589 // LoadGIF:
590 // Reads and decodes one or more GIF images, depending on whether
591 // animated GIF support is enabled. Can read GIFs with any bit
592 // size (color depth), but the output images are always expanded
593 // to 8 bits per pixel. Also, the image palettes always contain
594 // 256 colors, although some of them may be unused. Returns wxGIF_OK
595 // (== 0) on success, or an error code if something fails (see
596 // header file for details)
597 //
598 wxGIFErrorCode wxGIFDecoder::LoadGIF(wxInputStream& stream)
599 {
600 unsigned int global_ncolors = 0;
601 int bits, interl, i;
602 wxAnimationDisposal disposal;
603 long size;
604 long delay;
605 unsigned char type = 0;
606 unsigned char pal[768];
607 unsigned char buf[16];
608 bool anim = true;
609
610 // check GIF signature
611 if (!CanRead(stream))
612 return wxGIF_INVFORMAT;
613
614 // check for animated GIF support (ver. >= 89a)
615
616 static const unsigned int headerSize = (3 + 3);
617 stream.Read(buf, headerSize);
618 if (stream.LastRead() != headerSize)
619 {
620 return wxGIF_INVFORMAT;
621 }
622
623 if (memcmp(buf + 3, "89a", 3) < 0)
624 {
625 anim = false;
626 }
627
628 // read logical screen descriptor block (LSDB)
629 static const unsigned int lsdbSize = (2 + 2 + 1 + 1 + 1);
630 stream.Read(buf, lsdbSize);
631 if (stream.LastRead() != lsdbSize)
632 {
633 return wxGIF_INVFORMAT;
634 }
635
636 m_szAnimation.SetWidth( buf[0] + 256 * buf[1] );
637 m_szAnimation.SetHeight( buf[2] + 256 * buf[3] );
638
639 if (anim && ((m_szAnimation.GetWidth() == 0) || (m_szAnimation.GetHeight() == 0)))
640 {
641 return wxGIF_INVFORMAT;
642 }
643
644 // load global color map if available
645 if ((buf[4] & 0x80) == 0x80)
646 {
647 int backgroundColIndex = buf[5];
648
649 global_ncolors = 2 << (buf[4] & 0x07);
650 unsigned int numBytes = 3 * global_ncolors;
651 stream.Read(pal, numBytes);
652 if (stream.LastRead() != numBytes)
653 {
654 return wxGIF_INVFORMAT;
655 }
656
657 m_background.Set(pal[backgroundColIndex*3 + 0],
658 pal[backgroundColIndex*3 + 1],
659 pal[backgroundColIndex*3 + 2]);
660 }
661
662 // transparent colour, disposal method and delay default to unused
663 int transparent = -1;
664 disposal = wxANIM_UNSPECIFIED;
665 delay = -1;
666
667 bool done = false;
668 while (!done)
669 {
670 type = (unsigned char)stream.GetC();
671
672 /*
673 If the end of file has been reached (or an error) and a ";"
674 (0x3B) hasn't been encountered yet, exit the loop. (Without this
675 check the while loop would loop endlessly.) Later on, in the next while
676 loop, the file will be treated as being truncated (But still
677 be decoded as far as possible). returning wxGIF_TRUNCATED is not
678 possible here since some init code is done after this loop.
679 */
680 if (stream.Eof())// || !stream.IsOk())
681 {
682 /*
683 type is set to some bogus value, so there's no
684 need to continue evaluating it.
685 */
686 break; // Alternative : "return wxGIF_INVFORMAT;"
687 }
688
689 // end of data?
690 if (type == 0x3B)
691 {
692 done = true;
693 }
694 else
695 // extension block?
696 if (type == 0x21)
697 {
698 if (((unsigned char)stream.GetC()) == 0xF9)
699 // graphics control extension, parse it
700 {
701 static const unsigned int gceSize = 6;
702 stream.Read(buf, gceSize);
703 if (stream.LastRead() != gceSize)
704 {
705 Destroy();
706 return wxGIF_INVFORMAT;
707 }
708
709 // read delay and convert from 1/100 of a second to ms
710 delay = 10 * (buf[2] + 256 * buf[3]);
711
712 // read transparent colour index, if used
713 transparent = buf[1] & 0x01 ? buf[4] : -1;
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