]> git.saurik.com Git - wxWidgets.git/blame - src/common/gifdecod.cpp
added wxAnimationCtrl (patch 1570325)
[wxWidgets.git] / src / common / gifdecod.cpp
CommitLineData
464122b6 1/////////////////////////////////////////////////////////////////////////////
8898456d 2// Name: src/common/gifdecod.cpp
464122b6
JS
3// Purpose: wxGIFDecoder, GIF reader for wxImage and wxAnimation
4// Author: Guillermo Rodriguez Garcia <guille@iies.es>
9d0e21da
GRG
5// Version: 3.04
6// RCS-ID: $Id$
464122b6 7// Copyright: (c) Guillermo Rodriguez Garcia
65571936 8// Licence: wxWindows licence
464122b6
JS
9/////////////////////////////////////////////////////////////////////////////
10
464122b6
JS
11// For compilers that support precompilation, includes "wx.h".
12#include "wx/wxprec.h"
13
14#ifdef __BORLANDC__
8898456d 15 #pragma hdrstop
464122b6
JS
16#endif
17
8898456d
WS
18#if wxUSE_STREAMS && wxUSE_GIF
19
464122b6 20#ifndef WX_PRECOMP
8898456d 21 #include "wx/palette.h"
464122b6
JS
22#endif
23
464122b6
JS
24#include <stdlib.h>
25#include <string.h>
464122b6 26#include "wx/gifdecod.h"
2ce0a6e2 27
464122b6 28
72045d57
VZ
29
30//---------------------------------------------------------------------------
31// GIFImage
32//---------------------------------------------------------------------------
33
34// internal class for storing GIF image data
35class GIFImage
36{
37public:
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
65c36a73
VZ
57//---------------------------------------------------------------------------
58// GIFImage constructor
59//---------------------------------------------------------------------------
60GIFImage::GIFImage()
61{
62 w = 0;
63 h = 0;
64 left = 0;
65 top = 0;
66 transparent = 0;
72045d57 67 disposal = wxANIM_DONOTREMOVE;
65c36a73
VZ
68 delay = -1;
69 p = (unsigned char *) NULL;
70 pal = (unsigned char *) NULL;
57b5c44a 71 ncolours = 0;
65c36a73
VZ
72}
73
464122b6
JS
74//---------------------------------------------------------------------------
75// wxGIFDecoder constructor and destructor
76//---------------------------------------------------------------------------
77
72045d57 78wxGIFDecoder::wxGIFDecoder()
464122b6 79{
464122b6
JS
80}
81
82wxGIFDecoder::~wxGIFDecoder()
83{
84 Destroy();
85}
86
87void wxGIFDecoder::Destroy()
88{
72045d57
VZ
89 wxASSERT(m_nFrames==m_frames.GetCount());
90 for (size_t i=0; i<m_nFrames; i++)
464122b6 91 {
72045d57
VZ
92 GIFImage *f = (GIFImage*)m_frames[i];
93 free(f->p);
94 free(f->pal);
95 delete f;
464122b6 96 }
9742d3cc 97
72045d57
VZ
98 m_frames.Clear();
99 m_nFrames = 0;
464122b6
JS
100}
101
102
103//---------------------------------------------------------------------------
104// Convert this image to a wxImage object
105//---------------------------------------------------------------------------
106
107// This function was designed by Vaclav Slavik
108
72045d57 109bool wxGIFDecoder::ConvertToImage(size_t frame, wxImage *image) const
464122b6
JS
110{
111 unsigned char *src, *dst, *pal;
112 unsigned long i;
113 int transparent;
114
3c87527e
GRG
115 /* just in case... */
116 image->Destroy();
117
464122b6 118 /* create the image */
72045d57
VZ
119 wxSize sz = GetFrameSize(frame);
120 image->Create(sz.GetWidth(), sz.GetHeight());
464122b6
JS
121
122 if (!image->Ok())
5d3e7b52 123 return false;
464122b6 124
72045d57
VZ
125 pal = GetPalette(frame);
126 src = GetData(frame);
464122b6 127 dst = image->GetData();
72045d57 128 transparent = GetTransparentColour(frame);
464122b6
JS
129
130 /* set transparent colour mask */
131 if (transparent != -1)
132 {
72045d57 133 for (i = 0; i < GetNcolours(frame); i++)
b11e8fb6
VZ
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);
464122b6
JS
148 }
149 else
5d3e7b52 150 image->SetMask(false);
464122b6 151
b11e8fb6 152#if wxUSE_PALETTE
fbf1ee1d
VZ
153 unsigned char r[256];
154 unsigned char g[256];
155 unsigned char b[256];
b11e8fb6 156
fbf1ee1d
VZ
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];
3f4fc796 162 }
fbf1ee1d 163
72045d57 164 image->SetPalette(wxPalette(GetNcolours(frame), r, g, b));
b11e8fb6 165#endif // wxUSE_PALETTE
3f4fc796 166
464122b6 167 /* copy image data */
72045d57
VZ
168 unsigned long npixel = sz.GetWidth() * sz.GetHeight();
169 for (i = 0; i < npixel; i++, src++)
464122b6 170 {
b11e8fb6
VZ
171 *(dst++) = pal[3 * (*src) + 0];
172 *(dst++) = pal[3 * (*src) + 1];
173 *(dst++) = pal[3 * (*src) + 2];
464122b6
JS
174 }
175
5d3e7b52 176 return true;
464122b6
JS
177}
178
2ce0a6e2 179
464122b6
JS
180//---------------------------------------------------------------------------
181// Data accessors
182//---------------------------------------------------------------------------
183
72045d57 184#define GetFrame(n) ((GIFImage*)m_frames[n])
464122b6
JS
185
186
72045d57 187// Get data for current frame
464122b6 188
72045d57 189wxSize wxGIFDecoder::GetFrameSize(size_t frame) const
464122b6 190{
72045d57 191 return wxSize(GetFrame(frame)->w, GetFrame(frame)->h);
464122b6
JS
192}
193
72045d57 194wxPoint wxGIFDecoder::GetFramePosition(size_t frame) const
464122b6 195{
72045d57 196 return wxPoint(GetFrame(frame)->left, GetFrame(frame)->top);
464122b6
JS
197}
198
72045d57 199wxAnimationDisposal wxGIFDecoder::GetDisposalMethod(size_t frame) const
464122b6 200{
72045d57 201 return GetFrame(frame)->disposal;
2ce0a6e2 202}
464122b6 203
72045d57 204long wxGIFDecoder::GetDelay(size_t frame) const
464122b6 205{
72045d57 206 return GetFrame(frame)->delay;
464122b6
JS
207}
208
72045d57
VZ
209unsigned char* wxGIFDecoder::GetData(size_t frame) const { return (GetFrame(frame)->p); }
210unsigned char* wxGIFDecoder::GetPalette(size_t frame) const { return (GetFrame(frame)->pal); }
211unsigned int wxGIFDecoder::GetNcolours(size_t frame) const { return (GetFrame(frame)->ncolours); }
212int wxGIFDecoder::GetTransparentColour(size_t frame) const { return (GetFrame(frame)->transparent); }
464122b6 213
464122b6
JS
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//
72045d57 223int wxGIFDecoder::getcode(wxInputStream& stream, int bits, int ab_fin)
464122b6
JS
224{
225 unsigned int mask; /* bit mask */
226 unsigned int code; /* code (result) */
227
464122b6
JS
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 {
b11e8fb6
VZ
235 /* if no bytes left in this block, read the next block */
236 if (m_restbyte == 0)
237 {
72045d57 238 m_restbyte = (unsigned char)stream.GetC();
b11e8fb6
VZ
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 */
72045d57
VZ
252 stream.Read((void *) m_buffer, m_restbyte);
253 if (stream.LastRead() != m_restbyte)
65c36a73
VZ
254 {
255 code = ab_fin;
256 return code;
257 }
b11e8fb6
VZ
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;
464122b6 269 }
2ce0a6e2 270
464122b6
JS
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).
65c36a73
VZ
281// Returns wxGIF_OK (== 0) on success, or an error code if something
282// fails (see header file for details)
72045d57 283wxGIFErrorCode wxGIFDecoder::dgif(wxInputStream& stream, GIFImage *img, int interl, int bits)
464122b6 284{
65c36a73
VZ
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
7679ac63
GRG
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 */
464122b6
JS
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
8708a10f 331 /* reset decoder vars */
464122b6
JS
332 m_restbits = 0;
333 m_restbyte = 0;
334 m_lastbyte = 0;
335
336 do
337 {
b11e8fb6 338 /* get next code */
72045d57 339 readcode = code = getcode(stream, ab_bits, ab_fin);
b11e8fb6
VZ
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];
65c36a73
VZ
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 }
b11e8fb6 381 }
e34f4f19
VZ
382
383 if (pos >= allocSize)
384 {
385 delete[] ab_prefix;
386 delete[] ab_tail;
387 delete[] stack;
388 return wxGIF_INVFORMAT;
389 }
390
b11e8fb6
VZ
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 {
1819a5c1
VZ
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
b11e8fb6
VZ
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
e34f4f19 427 /* dump stack data to the image buffer */
b11e8fb6
VZ
428 while (pos >= 0)
429 {
e34f4f19
VZ
430 (img->p)[x + (y * (img->w))] = (char) stack[pos];
431 pos--;
b11e8fb6
VZ
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 }
e34f4f19
VZ
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
3103e8a9 451 decoding (At this point the image is successfully
e34f4f19
VZ
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))
b11e8fb6
VZ
459 {
460 switch (++pass)
461 {
462 case 2: y = 4; break;
463 case 3: y = 2; break;
464 case 4: y = 1; break;
e34f4f19
VZ
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;
b11e8fb6
VZ
491 }
492 }
493 }
494 else
495 {
496 /* non-interlaced */
497 y++;
6363699a
VZ
498/*
499Normally image decoding is finished when an End of Information code is
500encountered (code == ab_fin) however some broken encoders write wrong
501"block byte counts" (The first byte value after the "code size" byte),
502being one value too high. It might very well be possible other variants
503of this problem occur as well. The only sensible solution seems to
504be to check for clipping.
505Example of wrong encoding:
506(1 * 1 B/W image, raster data stream follows in hex bytes)
507
50802 << B/W images have a code size of 2
50902 << Block byte count
51044 << LZW packed
51100 << Zero byte count (terminates data stream)
512
513Because the block byte count is 2, the zero byte count is used in the
514decoding process, and decoding is continued after this byte. (While it
515should signal an end of image)
516
517It should be:
51802
51902
52044
52101 << When decoded this correctly includes the End of Information code
52200
523
524Or (Worse solution):
52502
52601
52744
52800
529(The 44 doesn't include an End of Information code, but at least the
530decoder correctly skips to 00 now after decoding, and signals this
531as an End of Information itself)
532*/
533 if (y >= img->h)
534 {
535 code = ab_fin;
536 break;
537 }
b11e8fb6
VZ
538 }
539 }
540 }
541
542 pos = 0;
543 lastcode = readcode;
464122b6
JS
544 }
545 while (code != ab_fin);
546
33ac7e6f
KB
547 delete [] ab_prefix ;
548 delete [] ab_tail ;
549 delete [] stack ;
7679ac63 550
65c36a73 551 return wxGIF_OK;
464122b6
JS
552}
553
554
3c87527e 555// CanRead:
5d3e7b52 556// Returns true if the file looks like a valid GIF, false otherwise.
3c87527e 557//
72045d57 558bool wxGIFDecoder::CanRead(wxInputStream &stream) const
3c87527e
GRG
559{
560 unsigned char buf[3];
561
72045d57 562 if ( !stream.Read(buf, WXSIZEOF(buf)) )
5d3e7b52 563 return false;
79fa2374 564
72045d57 565 stream.SeekI(-(wxFileOffset)WXSIZEOF(buf), wxFromCurrent);
3c87527e 566
79fa2374 567 return memcmp(buf, "GIF", WXSIZEOF(buf)) == 0;
3c87527e
GRG
568}
569
570
72045d57 571// LoadGIF:
464122b6
JS
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
8141573c 576// 256 colors, although some of them may be unused. Returns wxGIF_OK
e4b8154a
GRG
577// (== 0) on success, or an error code if something fails (see
578// header file for details)
464122b6 579//
72045d57 580wxGIFErrorCode wxGIFDecoder::LoadGIF(wxInputStream& stream)
464122b6 581{
57b5c44a 582 unsigned int global_ncolors = 0;
72045d57
VZ
583 int bits, interl, transparent, i;
584 wxAnimationDisposal disposal;
464122b6
JS
585 long size;
586 long delay;
3ca6a5f0 587 unsigned char type = 0;
464122b6
JS
588 unsigned char pal[768];
589 unsigned char buf[16];
72045d57 590 bool anim = true;
464122b6 591
3c87527e 592 /* check GIF signature */
72045d57 593 if (!CanRead(stream))
b11e8fb6 594 return wxGIF_INVFORMAT;
464122b6 595
8141573c 596 /* check for animated GIF support (ver. >= 89a) */
65c36a73
VZ
597
598 static const size_t headerSize = (3 + 3);
72045d57
VZ
599 stream.Read(buf, headerSize);
600 if (stream.LastRead() != headerSize)
65c36a73
VZ
601 {
602 return wxGIF_INVFORMAT;
603 }
464122b6 604
464122b6 605 if (memcmp(buf + 3, "89a", 3) < 0)
65c36a73 606 {
72045d57 607 anim = false;
65c36a73 608 }
464122b6
JS
609
610 /* read logical screen descriptor block (LSDB) */
65c36a73 611 static const size_t lsdbSize = (2 + 2 + 1 + 1 + 1);
72045d57
VZ
612 stream.Read(buf, lsdbSize);
613 if (stream.LastRead() != lsdbSize)
65c36a73
VZ
614 {
615 return wxGIF_INVFORMAT;
616 }
617
72045d57
VZ
618 m_szAnimation.SetWidth( buf[0] + 256 * buf[1] );
619 m_szAnimation.SetHeight( buf[2] + 256 * buf[3] );
464122b6 620
72045d57 621 if ((m_szAnimation.GetWidth() == 0) || (m_szAnimation.GetHeight() == 0))
525b0568
DS
622 {
623 return wxGIF_INVFORMAT;
624 }
625
464122b6
JS
626 /* load global color map if available */
627 if ((buf[4] & 0x80) == 0x80)
628 {
72045d57 629 int backgroundColIndex = buf[5];
464122b6 630
57b5c44a
RR
631 global_ncolors = 2 << (buf[4] & 0x07);
632 size_t numBytes = 3 * global_ncolors;
72045d57
VZ
633 stream.Read(pal, numBytes);
634 if (stream.LastRead() != numBytes)
65c36a73
VZ
635 {
636 return wxGIF_INVFORMAT;
637 }
72045d57
VZ
638
639 m_background.Set(pal[backgroundColIndex*3 + 0],
640 pal[backgroundColIndex*3 + 1],
641 pal[backgroundColIndex*3 + 2]);
464122b6
JS
642 }
643
644 /* transparent colour, disposal method and delay default to unused */
645 transparent = -1;
72045d57 646 disposal = wxANIM_UNSPECIFIED;
464122b6
JS
647 delay = -1;
648
5d3e7b52 649 bool done = false;
525b0568 650 while (!done)
464122b6 651 {
72045d57 652 type = (unsigned char)stream.GetC();
b11e8fb6 653
65c36a73
VZ
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 */
72045d57 662 if (stream.Eof())// || !stream.IsOk())
65c36a73
VZ
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
b11e8fb6
VZ
671 /* end of data? */
672 if (type == 0x3B)
673 {
5d3e7b52 674 done = true;
b11e8fb6
VZ
675 }
676 else
677 /* extension block? */
678 if (type == 0x21)
679 {
72045d57 680 if (((unsigned char)stream.GetC()) == 0xF9)
b11e8fb6
VZ
681 /* graphics control extension, parse it */
682 {
65c36a73 683 static const size_t gceSize = 6;
72045d57
VZ
684 stream.Read(buf, gceSize);
685 if (stream.LastRead() != gceSize)
65c36a73
VZ
686 {
687 Destroy();
688 return wxGIF_INVFORMAT;
689 }
b11e8fb6
VZ
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 */
72045d57 699 disposal = (wxAnimationDisposal)(((buf[1] & 0x1C) >> 2) - 1);
b11e8fb6
VZ
700 }
701 else
702 /* other extension, skip */
703 {
72045d57 704 while ((i = (unsigned char)stream.GetC()) != 0)
b11e8fb6 705 {
72045d57
VZ
706 stream.SeekI(i, wxFromCurrent);
707 if (stream.Eof())
65c36a73 708 {
5d3e7b52 709 done = true;
65c36a73
VZ
710 break;
711 }
b11e8fb6
VZ
712 }
713 }
714 }
715 else
716 /* image descriptor block? */
717 if (type == 0x2C)
718 {
719 /* allocate memory for IMAGEN struct */
72045d57 720 GIFImage *pimg = new GIFImage();
b11e8fb6
VZ
721
722 if (pimg == NULL)
723 {
724 Destroy();
725 return wxGIF_MEMERR;
726 }
727
728 /* fill in the data */
65c36a73 729 static const size_t idbSize = (2 + 2 + 2 + 2 + 1);
72045d57
VZ
730 stream.Read(buf, idbSize);
731 if (stream.LastRead() != idbSize)
65c36a73
VZ
732 {
733 Destroy();
734 return wxGIF_INVFORMAT;
735 }
736
b11e8fb6
VZ
737 pimg->left = buf[0] + 256 * buf[1];
738 pimg->top = buf[2] + 256 * buf[3];
bd52bee1 739/*
b11e8fb6
VZ
740 pimg->left = buf[4] + 256 * buf[5];
741 pimg->top = buf[4] + 256 * buf[5];
bd52bee1 742*/
b11e8fb6
VZ
743 pimg->w = buf[4] + 256 * buf[5];
744 pimg->h = buf[6] + 256 * buf[7];
65c36a73 745
72045d57
VZ
746 if ((pimg->w == 0) || (pimg->w > (unsigned int)m_szAnimation.GetWidth()) ||
747 (pimg->h == 0) || (pimg->h > (unsigned int)m_szAnimation.GetHeight()))
65c36a73
VZ
748 {
749 Destroy();
750 return wxGIF_INVFORMAT;
751 }
752
b11e8fb6
VZ
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;
b11e8fb6
VZ
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 {
57b5c44a
RR
773 unsigned int local_ncolors = 2 << (buf[8] & 0x07);
774 size_t numBytes = 3 * local_ncolors;
72045d57 775 stream.Read(pimg->pal, numBytes);
57b5c44a 776 pimg->ncolours = local_ncolors;
72045d57 777 if (stream.LastRead() != numBytes)
65c36a73
VZ
778 {
779 Destroy();
780 return wxGIF_INVFORMAT;
781 }
b11e8fb6
VZ
782 }
783 else
65c36a73 784 {
b11e8fb6 785 memcpy(pimg->pal, pal, 768);
57b5c44a 786 pimg->ncolours = global_ncolors;
65c36a73 787 }
b11e8fb6
VZ
788
789 /* get initial code size from first byte in raster data */
72045d57 790 bits = (unsigned char)stream.GetC();
525b0568
DS
791 if (bits == 0)
792 {
793 Destroy();
794 return wxGIF_INVFORMAT;
795 }
b11e8fb6
VZ
796
797 /* decode image */
72045d57 798 wxGIFErrorCode result = dgif(stream, pimg, interl, bits);
65c36a73
VZ
799 if (result != wxGIF_OK)
800 {
801 Destroy();
802 return result;
803 }
72045d57
VZ
804
805 /* add the image to our frame array */
806 m_frames.Add((void*)pimg);
807 m_nFrames++;
b11e8fb6
VZ
808
809 /* if this is not an animated GIF, exit after first image */
72045d57 810 if (!anim)
5d3e7b52 811 done = true;
b11e8fb6 812 }
464122b6
JS
813 }
814
72045d57 815 if (m_nFrames <= 0)
464122b6 816 {
65c36a73
VZ
817 Destroy();
818 return wxGIF_INVFORMAT;
464122b6
JS
819 }
820
8141573c
GRG
821 /* try to read to the end of the stream */
822 while (type != 0x3B)
823 {
72045d57 824 if (!stream.IsOk())
ef3a5e0a 825 return wxGIF_TRUNCATED;
65c36a73 826
72045d57 827 type = (unsigned char)stream.GetC();
b11e8fb6
VZ
828
829 if (type == 0x21)
830 {
831 /* extension type */
72045d57 832 (void) stream.GetC();
b11e8fb6
VZ
833
834 /* skip all data */
72045d57 835 while ((i = (unsigned char)stream.GetC()) != 0)
b11e8fb6 836 {
72045d57 837 stream.SeekI(i, wxFromCurrent);
b11e8fb6
VZ
838 }
839 }
840 else if (type == 0x2C)
841 {
842 /* image descriptor block */
65c36a73 843 static const size_t idbSize = (2 + 2 + 2 + 2 + 1);
72045d57
VZ
844 stream.Read(buf, idbSize);
845 if (stream.LastRead() != idbSize)
65c36a73
VZ
846 {
847 Destroy();
848 return wxGIF_INVFORMAT;
849 }
b11e8fb6
VZ
850
851 /* local color map */
852 if ((buf[8] & 0x80) == 0x80)
853 {
57b5c44a 854 unsigned int local_ncolors = 2 << (buf[8] & 0x07);
72045d57 855 wxFileOffset pos = stream.TellI();
57b5c44a 856 wxFileOffset numBytes = 3 * local_ncolors;
72045d57
VZ
857 stream.SeekI(numBytes, wxFromCurrent);
858 if (stream.TellI() != (pos + numBytes))
65c36a73
VZ
859 {
860 Destroy();
861 return wxGIF_INVFORMAT;
862 }
b11e8fb6
VZ
863 }
864
865 /* initial code size */
72045d57 866 (void) stream.GetC();
b11e8fb6
VZ
867
868 /* skip all data */
72045d57 869 while ((i = (unsigned char)stream.GetC()) != 0)
b11e8fb6 870 {
72045d57 871 stream.SeekI(i, wxFromCurrent);
b11e8fb6
VZ
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 }
8141573c
GRG
879 }
880
e4b8154a 881 return wxGIF_OK;
464122b6
JS
882}
883
7be110e3 884#endif // wxUSE_STREAMS && wxUSE_GIF