]> git.saurik.com Git - wxWidgets.git/blame - src/common/gifdecod.cpp
send destroy events for children before they're fully destroyed; document SendDestroy...
[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"
56ba0394
VZ
27#include "wx/ptr_scpd.h"
28#include "wx/scopeguard.h"
2ce0a6e2 29
464122b6 30
72045d57
VZ
31
32//---------------------------------------------------------------------------
33// GIFImage
34//---------------------------------------------------------------------------
35
36// internal class for storing GIF image data
37class GIFImage
38{
39public:
40 // def ctor
41 GIFImage();
42
870cf35c
VZ
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
72045d57
VZ
53
54 DECLARE_NO_COPY_CLASS(GIFImage)
55};
56
56ba0394
VZ
57wxDECLARE_SCOPED_PTR(GIFImage, GIFImagePtr)
58wxDEFINE_SCOPED_PTR(GIFImage, GIFImagePtr)
72045d57
VZ
59
60
65c36a73
VZ
61//---------------------------------------------------------------------------
62// GIFImage constructor
63//---------------------------------------------------------------------------
64GIFImage::GIFImage()
65{
66 w = 0;
67 h = 0;
68 left = 0;
69 top = 0;
70 transparent = 0;
72045d57 71 disposal = wxANIM_DONOTREMOVE;
65c36a73
VZ
72 delay = -1;
73 p = (unsigned char *) NULL;
74 pal = (unsigned char *) NULL;
57b5c44a 75 ncolours = 0;
65c36a73
VZ
76}
77
464122b6
JS
78//---------------------------------------------------------------------------
79// wxGIFDecoder constructor and destructor
80//---------------------------------------------------------------------------
81
72045d57 82wxGIFDecoder::wxGIFDecoder()
464122b6 83{
464122b6
JS
84}
85
86wxGIFDecoder::~wxGIFDecoder()
87{
88 Destroy();
89}
90
91void wxGIFDecoder::Destroy()
92{
72045d57 93 wxASSERT(m_nFrames==m_frames.GetCount());
870cf35c 94 for (unsigned int i=0; i<m_nFrames; i++)
464122b6 95 {
72045d57
VZ
96 GIFImage *f = (GIFImage*)m_frames[i];
97 free(f->p);
98 free(f->pal);
99 delete f;
464122b6 100 }
9742d3cc 101
72045d57
VZ
102 m_frames.Clear();
103 m_nFrames = 0;
464122b6
JS
104}
105
106
107//---------------------------------------------------------------------------
108// Convert this image to a wxImage object
109//---------------------------------------------------------------------------
110
111// This function was designed by Vaclav Slavik
112
870cf35c 113bool wxGIFDecoder::ConvertToImage(unsigned int frame, wxImage *image) const
464122b6
JS
114{
115 unsigned char *src, *dst, *pal;
116 unsigned long i;
117 int transparent;
118
870cf35c 119 // just in case...
3c87527e
GRG
120 image->Destroy();
121
870cf35c 122 // create the image
72045d57
VZ
123 wxSize sz = GetFrameSize(frame);
124 image->Create(sz.GetWidth(), sz.GetHeight());
9d1c7e84 125 image->SetType(wxBITMAP_TYPE_GIF);
464122b6
JS
126
127 if (!image->Ok())
5d3e7b52 128 return false;
464122b6 129
72045d57
VZ
130 pal = GetPalette(frame);
131 src = GetData(frame);
464122b6 132 dst = image->GetData();
05a98b6d 133 transparent = GetTransparentColourIndex(frame);
464122b6 134
870cf35c 135 // set transparent colour mask
464122b6
JS
136 if (transparent != -1)
137 {
72045d57 138 for (i = 0; i < GetNcolours(frame); i++)
b11e8fb6
VZ
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);
464122b6
JS
153 }
154 else
5d3e7b52 155 image->SetMask(false);
464122b6 156
b11e8fb6 157#if wxUSE_PALETTE
fbf1ee1d
VZ
158 unsigned char r[256];
159 unsigned char g[256];
160 unsigned char b[256];
b11e8fb6 161
fbf1ee1d
VZ
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];
3f4fc796 167 }
fbf1ee1d 168
72045d57 169 image->SetPalette(wxPalette(GetNcolours(frame), r, g, b));
b11e8fb6 170#endif // wxUSE_PALETTE
3f4fc796 171
870cf35c 172 // copy image data
72045d57
VZ
173 unsigned long npixel = sz.GetWidth() * sz.GetHeight();
174 for (i = 0; i < npixel; i++, src++)
464122b6 175 {
b11e8fb6
VZ
176 *(dst++) = pal[3 * (*src) + 0];
177 *(dst++) = pal[3 * (*src) + 1];
178 *(dst++) = pal[3 * (*src) + 2];
464122b6
JS
179 }
180
5d3e7b52 181 return true;
464122b6
JS
182}
183
2ce0a6e2 184
464122b6
JS
185//---------------------------------------------------------------------------
186// Data accessors
187//---------------------------------------------------------------------------
188
72045d57 189#define GetFrame(n) ((GIFImage*)m_frames[n])
464122b6
JS
190
191
72045d57 192// Get data for current frame
464122b6 193
4c0ce682 194wxSize wxGIFDecoder::GetFrameSize(unsigned int frame) const
464122b6 195{
72045d57 196 return wxSize(GetFrame(frame)->w, GetFrame(frame)->h);
464122b6
JS
197}
198
870cf35c 199wxPoint wxGIFDecoder::GetFramePosition(unsigned int frame) const
464122b6 200{
72045d57 201 return wxPoint(GetFrame(frame)->left, GetFrame(frame)->top);
464122b6
JS
202}
203
870cf35c 204wxAnimationDisposal wxGIFDecoder::GetDisposalMethod(unsigned int frame) const
464122b6 205{
72045d57 206 return GetFrame(frame)->disposal;
2ce0a6e2 207}
464122b6 208
870cf35c 209long wxGIFDecoder::GetDelay(unsigned int frame) const
464122b6 210{
72045d57 211 return GetFrame(frame)->delay;
464122b6
JS
212}
213
870cf35c 214wxColour wxGIFDecoder::GetTransparentColour(unsigned int frame) const
05a98b6d
RR
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
870cf35c
VZ
226unsigned char* wxGIFDecoder::GetData(unsigned int frame) const { return (GetFrame(frame)->p); }
227unsigned char* wxGIFDecoder::GetPalette(unsigned int frame) const { return (GetFrame(frame)->pal); }
228unsigned int wxGIFDecoder::GetNcolours(unsigned int frame) const { return (GetFrame(frame)->ncolours); }
229int wxGIFDecoder::GetTransparentColourIndex(unsigned int frame) const { return (GetFrame(frame)->transparent); }
464122b6 230
464122b6
JS
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//
72045d57 240int wxGIFDecoder::getcode(wxInputStream& stream, int bits, int ab_fin)
464122b6 241{
870cf35c
VZ
242 unsigned int mask; // bit mask
243 unsigned int code; // code (result)
464122b6 244
870cf35c 245 // get remaining bits from last byte read
464122b6
JS
246 mask = (1 << bits) - 1;
247 code = (m_lastbyte >> (8 - m_restbits)) & mask;
248
870cf35c 249 // keep reading new bytes while needed
464122b6
JS
250 while (bits > m_restbits)
251 {
870cf35c 252 // if no bytes left in this block, read the next block
b11e8fb6
VZ
253 if (m_restbyte == 0)
254 {
72045d57 255 m_restbyte = (unsigned char)stream.GetC();
b11e8fb6
VZ
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
870cf35c 268 // prefetch data
72045d57
VZ
269 stream.Read((void *) m_buffer, m_restbyte);
270 if (stream.LastRead() != m_restbyte)
65c36a73
VZ
271 {
272 code = ab_fin;
273 return code;
274 }
b11e8fb6
VZ
275 m_bufp = m_buffer;
276 }
277
870cf35c 278 // read next byte and isolate the bits we need
b11e8fb6
VZ
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
870cf35c 284 // adjust total number of bits extracted from the buffer
b11e8fb6 285 m_restbits = m_restbits + 8;
464122b6 286 }
2ce0a6e2 287
870cf35c 288 // find number of bits remaining for next code
464122b6
JS
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).
65c36a73
VZ
298// Returns wxGIF_OK (== 0) on success, or an error code if something
299// fails (see header file for details)
870cf35c
VZ
300wxGIFErrorCode
301wxGIFDecoder::dgif(wxInputStream& stream, GIFImage *img, int interl, int bits)
464122b6 302{
65c36a73 303 static const int allocSize = 4096 + 1;
870cf35c 304 int *ab_prefix = new int[allocSize]; // alphabet (prefixes)
65c36a73
VZ
305 if (ab_prefix == NULL)
306 {
307 return wxGIF_MEMERR;
308 }
309
870cf35c 310 int *ab_tail = new int[allocSize]; // alphabet (tails)
65c36a73
VZ
311 if (ab_tail == NULL)
312 {
313 delete[] ab_prefix;
314 return wxGIF_MEMERR;
315 }
316
870cf35c 317 int *stack = new int[allocSize]; // decompression stack
65c36a73
VZ
318 if (stack == NULL)
319 {
320 delete[] ab_prefix;
321 delete[] ab_tail;
322 return wxGIF_MEMERR;
323 }
324
870cf35c
VZ
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
464122b6
JS
333
334 int code, readcode, lastcode, abcabca;
335
870cf35c 336 // these won't change
464122b6
JS
337 ab_clr = (1 << bits);
338 ab_fin = (1 << bits) + 1;
339
870cf35c 340 // these will change through the decompression proccess
464122b6
JS
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
870cf35c 349 // reset decoder vars
464122b6
JS
350 m_restbits = 0;
351 m_restbyte = 0;
352 m_lastbyte = 0;
353
354 do
355 {
870cf35c 356 // get next code
72045d57 357 readcode = code = getcode(stream, ab_bits, ab_fin);
b11e8fb6 358
870cf35c 359 // end of image?
b11e8fb6
VZ
360 if (code == ab_fin) break;
361
870cf35c 362 // reset alphabet?
b11e8fb6
VZ
363 if (code == ab_clr)
364 {
870cf35c 365 // reset main variables
b11e8fb6
VZ
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
870cf35c 372 // skip to next code
b11e8fb6
VZ
373 continue;
374 }
375
870cf35c 376 // unknown code: special case (like in ABCABCA)
b11e8fb6
VZ
377 if (code >= ab_free)
378 {
870cf35c
VZ
379 code = lastcode; // take last string
380 stack[pos++] = abcabca; // add first character
b11e8fb6
VZ
381 }
382
870cf35c 383 // build the string for this code in the stack
b11e8fb6
VZ
384 while (code > ab_clr)
385 {
386 stack[pos++] = ab_tail[code];
387 code = ab_prefix[code];
65c36a73
VZ
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 }
b11e8fb6 399 }
e34f4f19
VZ
400
401 if (pos >= allocSize)
402 {
403 delete[] ab_prefix;
404 delete[] ab_tail;
405 delete[] stack;
406 return wxGIF_INVFORMAT;
407 }
408
870cf35c
VZ
409 stack[pos] = code; // push last code into the stack
410 abcabca = code; // save for special case
b11e8fb6 411
870cf35c 412 // make new entry in alphabet (only if NOT just cleared)
b11e8fb6
VZ
413 if (lastcode != -1)
414 {
1819a5c1
VZ
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
b11e8fb6
VZ
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
870cf35c 445 // dump stack data to the image buffer
b11e8fb6
VZ
446 while (pos >= 0)
447 {
e34f4f19
VZ
448 (img->p)[x + (y * (img->w))] = (char) stack[pos];
449 pos--;
b11e8fb6
VZ
450
451 if (++x >= (img->w))
452 {
453 x = 0;
454
455 if (interl)
456 {
870cf35c 457 // support for interlaced images
b11e8fb6
VZ
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 }
e34f4f19
VZ
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
3103e8a9 469 decoding (At this point the image is successfully
e34f4f19
VZ
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))
b11e8fb6
VZ
477 {
478 switch (++pass)
479 {
480 case 2: y = 4; break;
481 case 3: y = 2; break;
482 case 4: y = 1; break;
e34f4f19
VZ
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;
b11e8fb6
VZ
509 }
510 }
511 }
512 else
513 {
870cf35c 514 // non-interlaced
b11e8fb6 515 y++;
6363699a
VZ
516/*
517Normally image decoding is finished when an End of Information code is
518encountered (code == ab_fin) however some broken encoders write wrong
519"block byte counts" (The first byte value after the "code size" byte),
520being one value too high. It might very well be possible other variants
521of this problem occur as well. The only sensible solution seems to
522be to check for clipping.
523Example of wrong encoding:
524(1 * 1 B/W image, raster data stream follows in hex bytes)
525
52602 << B/W images have a code size of 2
52702 << Block byte count
52844 << LZW packed
52900 << Zero byte count (terminates data stream)
530
531Because the block byte count is 2, the zero byte count is used in the
532decoding process, and decoding is continued after this byte. (While it
533should signal an end of image)
534
535It should be:
53602
53702
53844
53901 << When decoded this correctly includes the End of Information code
54000
541
542Or (Worse solution):
54302
54401
54544
54600
547(The 44 doesn't include an End of Information code, but at least the
548decoder correctly skips to 00 now after decoding, and signals this
549as an End of Information itself)
550*/
551 if (y >= img->h)
552 {
553 code = ab_fin;
554 break;
555 }
b11e8fb6
VZ
556 }
557 }
558 }
559
560 pos = 0;
561 lastcode = readcode;
464122b6
JS
562 }
563 while (code != ab_fin);
564
33ac7e6f
KB
565 delete [] ab_prefix ;
566 delete [] ab_tail ;
567 delete [] stack ;
7679ac63 568
65c36a73 569 return wxGIF_OK;
464122b6
JS
570}
571
572
3c87527e 573// CanRead:
5d3e7b52 574// Returns true if the file looks like a valid GIF, false otherwise.
3c87527e 575//
72045d57 576bool wxGIFDecoder::CanRead(wxInputStream &stream) const
3c87527e
GRG
577{
578 unsigned char buf[3];
579
72045d57 580 if ( !stream.Read(buf, WXSIZEOF(buf)) )
5d3e7b52 581 return false;
79fa2374 582
b81e4506
FM
583 if (stream.SeekI(-(wxFileOffset)WXSIZEOF(buf), wxFromCurrent) == wxInvalidOffset)
584 return false; // this happens e.g. for non-seekable streams
3c87527e 585
79fa2374 586 return memcmp(buf, "GIF", WXSIZEOF(buf)) == 0;
3c87527e
GRG
587}
588
589
72045d57 590// LoadGIF:
464122b6
JS
591// Reads and decodes one or more GIF images, depending on whether
592// animated GIF support is enabled. Can read GIFs with any bit
593// size (color depth), but the output images are always expanded
594// to 8 bits per pixel. Also, the image palettes always contain
8141573c 595// 256 colors, although some of them may be unused. Returns wxGIF_OK
e4b8154a
GRG
596// (== 0) on success, or an error code if something fails (see
597// header file for details)
464122b6 598//
72045d57 599wxGIFErrorCode wxGIFDecoder::LoadGIF(wxInputStream& stream)
464122b6 600{
57b5c44a 601 unsigned int global_ncolors = 0;
4c0ce682 602 int bits, interl, i;
72045d57 603 wxAnimationDisposal disposal;
464122b6
JS
604 long size;
605 long delay;
3ca6a5f0 606 unsigned char type = 0;
464122b6
JS
607 unsigned char pal[768];
608 unsigned char buf[16];
72045d57 609 bool anim = true;
464122b6 610
870cf35c 611 // check GIF signature
72045d57 612 if (!CanRead(stream))
b11e8fb6 613 return wxGIF_INVFORMAT;
464122b6 614
870cf35c 615 // check for animated GIF support (ver. >= 89a)
65c36a73 616
870cf35c 617 static const unsigned int headerSize = (3 + 3);
72045d57
VZ
618 stream.Read(buf, headerSize);
619 if (stream.LastRead() != headerSize)
65c36a73
VZ
620 {
621 return wxGIF_INVFORMAT;
622 }
464122b6 623
464122b6 624 if (memcmp(buf + 3, "89a", 3) < 0)
65c36a73 625 {
72045d57 626 anim = false;
65c36a73 627 }
464122b6 628
870cf35c
VZ
629 // read logical screen descriptor block (LSDB)
630 static const unsigned int lsdbSize = (2 + 2 + 1 + 1 + 1);
72045d57
VZ
631 stream.Read(buf, lsdbSize);
632 if (stream.LastRead() != lsdbSize)
65c36a73
VZ
633 {
634 return wxGIF_INVFORMAT;
635 }
636
72045d57
VZ
637 m_szAnimation.SetWidth( buf[0] + 256 * buf[1] );
638 m_szAnimation.SetHeight( buf[2] + 256 * buf[3] );
464122b6 639
551270a8 640 if (anim && ((m_szAnimation.GetWidth() == 0) || (m_szAnimation.GetHeight() == 0)))
525b0568
DS
641 {
642 return wxGIF_INVFORMAT;
643 }
644
870cf35c 645 // load global color map if available
464122b6
JS
646 if ((buf[4] & 0x80) == 0x80)
647 {
72045d57 648 int backgroundColIndex = buf[5];
464122b6 649
57b5c44a 650 global_ncolors = 2 << (buf[4] & 0x07);
870cf35c 651 unsigned int numBytes = 3 * global_ncolors;
72045d57
VZ
652 stream.Read(pal, numBytes);
653 if (stream.LastRead() != numBytes)
65c36a73
VZ
654 {
655 return wxGIF_INVFORMAT;
656 }
72045d57
VZ
657
658 m_background.Set(pal[backgroundColIndex*3 + 0],
659 pal[backgroundColIndex*3 + 1],
660 pal[backgroundColIndex*3 + 2]);
464122b6
JS
661 }
662
870cf35c 663 // transparent colour, disposal method and delay default to unused
4c0ce682 664 int transparent = -1;
72045d57 665 disposal = wxANIM_UNSPECIFIED;
464122b6
JS
666 delay = -1;
667
5d3e7b52 668 bool done = false;
525b0568 669 while (!done)
464122b6 670 {
72045d57 671 type = (unsigned char)stream.GetC();
b11e8fb6 672
65c36a73
VZ
673 /*
674 If the end of file has been reached (or an error) and a ";"
675 (0x3B) hasn't been encountered yet, exit the loop. (Without this
676 check the while loop would loop endlessly.) Later on, in the next while
677 loop, the file will be treated as being truncated (But still
678 be decoded as far as possible). returning wxGIF_TRUNCATED is not
679 possible here since some init code is done after this loop.
680 */
72045d57 681 if (stream.Eof())// || !stream.IsOk())
65c36a73
VZ
682 {
683 /*
684 type is set to some bogus value, so there's no
685 need to continue evaluating it.
686 */
687 break; // Alternative : "return wxGIF_INVFORMAT;"
688 }
689
870cf35c 690 // end of data?
b11e8fb6
VZ
691 if (type == 0x3B)
692 {
5d3e7b52 693 done = true;
b11e8fb6
VZ
694 }
695 else
870cf35c 696 // extension block?
b11e8fb6
VZ
697 if (type == 0x21)
698 {
72045d57 699 if (((unsigned char)stream.GetC()) == 0xF9)
870cf35c 700 // graphics control extension, parse it
b11e8fb6 701 {
870cf35c 702 static const unsigned int gceSize = 6;
72045d57
VZ
703 stream.Read(buf, gceSize);
704 if (stream.LastRead() != gceSize)
65c36a73
VZ
705 {
706 Destroy();
707 return wxGIF_INVFORMAT;
708 }
b11e8fb6 709
870cf35c 710 // read delay and convert from 1/100 of a second to ms
b11e8fb6
VZ
711 delay = 10 * (buf[2] + 256 * buf[3]);
712
870cf35c 713 // read transparent colour index, if used
4c0ce682 714 transparent = buf[1] & 0x01 ? buf[4] : -1;
b11e8fb6 715
870cf35c 716 // read disposal method
72045d57 717 disposal = (wxAnimationDisposal)(((buf[1] & 0x1C) >> 2) - 1);
b11e8fb6
VZ
718 }
719 else
870cf35c 720 // other extension, skip
b11e8fb6 721 {
72045d57 722 while ((i = (unsigned char)stream.GetC()) != 0)
b11e8fb6 723 {
b81e4506
FM
724 if (stream.Eof() || (stream.LastRead() == 0) ||
725 stream.SeekI(i, wxFromCurrent) == wxInvalidOffset)
65c36a73 726 {
5d3e7b52 727 done = true;
65c36a73
VZ
728 break;
729 }
b11e8fb6
VZ
730 }
731 }
732 }
733 else
870cf35c 734 // image descriptor block?
b11e8fb6
VZ
735 if (type == 0x2C)
736 {
870cf35c 737 // allocate memory for IMAGEN struct
56ba0394 738 GIFImagePtr pimg(new GIFImage());
b11e8fb6 739
56ba0394
VZ
740 wxScopeGuard guardDestroy = wxMakeObjGuard(*this, &wxGIFDecoder::Destroy);
741
742 if ( !pimg.get() )
b11e8fb6 743 return wxGIF_MEMERR;
b11e8fb6 744
870cf35c
VZ
745 // fill in the data
746 static const unsigned int idbSize = (2 + 2 + 2 + 2 + 1);
72045d57
VZ
747 stream.Read(buf, idbSize);
748 if (stream.LastRead() != idbSize)
65c36a73 749 return wxGIF_INVFORMAT;
65c36a73 750
b11e8fb6
VZ
751 pimg->left = buf[0] + 256 * buf[1];
752 pimg->top = buf[2] + 256 * buf[3];
bd52bee1 753/*
b11e8fb6
VZ
754 pimg->left = buf[4] + 256 * buf[5];
755 pimg->top = buf[4] + 256 * buf[5];
bd52bee1 756*/
b11e8fb6
VZ
757 pimg->w = buf[4] + 256 * buf[5];
758 pimg->h = buf[6] + 256 * buf[7];
65c36a73 759
4c0ce682
VZ
760 if ( anim && ((pimg->w == 0) || (pimg->w > (unsigned int)m_szAnimation.GetWidth()) ||
761 (pimg->h == 0) || (pimg->h > (unsigned int)m_szAnimation.GetHeight())) )
65c36a73 762 return wxGIF_INVFORMAT;
65c36a73 763
b11e8fb6
VZ
764 interl = ((buf[8] & 0x40)? 1 : 0);
765 size = pimg->w * pimg->h;
766
767 pimg->transparent = transparent;
768 pimg->disposal = disposal;
769 pimg->delay = delay;
b11e8fb6 770
870cf35c
VZ
771 // allocate memory for image and palette
772 pimg->p = (unsigned char *) malloc((unsigned int)size);
b11e8fb6
VZ
773 pimg->pal = (unsigned char *) malloc(768);
774
775 if ((!pimg->p) || (!pimg->pal))
b11e8fb6 776 return wxGIF_MEMERR;
b11e8fb6 777
870cf35c 778 // load local color map if available, else use global map
b11e8fb6
VZ
779 if ((buf[8] & 0x80) == 0x80)
780 {
57b5c44a 781 unsigned int local_ncolors = 2 << (buf[8] & 0x07);
870cf35c 782 unsigned int numBytes = 3 * local_ncolors;
72045d57 783 stream.Read(pimg->pal, numBytes);
57b5c44a 784 pimg->ncolours = local_ncolors;
72045d57 785 if (stream.LastRead() != numBytes)
65c36a73 786 return wxGIF_INVFORMAT;
b11e8fb6
VZ
787 }
788 else
65c36a73 789 {
b11e8fb6 790 memcpy(pimg->pal, pal, 768);
57b5c44a 791 pimg->ncolours = global_ncolors;
65c36a73 792 }
b11e8fb6 793
870cf35c 794 // get initial code size from first byte in raster data
72045d57 795 bits = (unsigned char)stream.GetC();
525b0568 796 if (bits == 0)
525b0568 797 return wxGIF_INVFORMAT;
b11e8fb6 798
870cf35c 799 // decode image
56ba0394 800 wxGIFErrorCode result = dgif(stream, pimg.get(), interl, bits);
65c36a73 801 if (result != wxGIF_OK)
65c36a73 802 return result;
56ba0394
VZ
803
804 guardDestroy.Dismiss();
72045d57 805
870cf35c 806 // add the image to our frame array
56ba0394 807 m_frames.Add(pimg.release());
72045d57 808 m_nFrames++;
b11e8fb6 809
870cf35c 810 // if this is not an animated GIF, exit after first image
72045d57 811 if (!anim)
5d3e7b52 812 done = true;
b11e8fb6 813 }
464122b6
JS
814 }
815
72045d57 816 if (m_nFrames <= 0)
464122b6 817 {
65c36a73
VZ
818 Destroy();
819 return wxGIF_INVFORMAT;
464122b6
JS
820 }
821
870cf35c 822 // try to read to the end of the stream
8141573c
GRG
823 while (type != 0x3B)
824 {
72045d57 825 if (!stream.IsOk())
ef3a5e0a 826 return wxGIF_TRUNCATED;
65c36a73 827
72045d57 828 type = (unsigned char)stream.GetC();
b11e8fb6
VZ
829
830 if (type == 0x21)
831 {
870cf35c 832 // extension type
72045d57 833 (void) stream.GetC();
b11e8fb6 834
870cf35c 835 // skip all data
72045d57 836 while ((i = (unsigned char)stream.GetC()) != 0)
b11e8fb6 837 {
b81e4506
FM
838 if (stream.Eof() || (stream.LastRead() == 0) ||
839 stream.SeekI(i, wxFromCurrent) == wxInvalidOffset)
fa24cfa0
MW
840 {
841 Destroy();
842 return wxGIF_INVFORMAT;
843 }
b11e8fb6
VZ
844 }
845 }
846 else if (type == 0x2C)
847 {
870cf35c
VZ
848 // image descriptor block
849 static const unsigned int idbSize = (2 + 2 + 2 + 2 + 1);
72045d57
VZ
850 stream.Read(buf, idbSize);
851 if (stream.LastRead() != idbSize)
65c36a73
VZ
852 {
853 Destroy();
854 return wxGIF_INVFORMAT;
855 }
b11e8fb6 856
870cf35c 857 // local color map
b11e8fb6
VZ
858 if ((buf[8] & 0x80) == 0x80)
859 {
57b5c44a 860 unsigned int local_ncolors = 2 << (buf[8] & 0x07);
57b5c44a 861 wxFileOffset numBytes = 3 * local_ncolors;
b81e4506
FM
862 if (stream.SeekI(numBytes, wxFromCurrent) == wxInvalidOffset)
863 {
864 Destroy();
865 return wxGIF_INVFORMAT;
866 }
b11e8fb6
VZ
867 }
868
870cf35c 869 // initial code size
72045d57 870 (void) stream.GetC();
fa24cfa0
MW
871 if (stream.Eof() || (stream.LastRead() == 0))
872 {
873 Destroy();
874 return wxGIF_INVFORMAT;
875 }
b11e8fb6 876
870cf35c 877 // skip all data
72045d57 878 while ((i = (unsigned char)stream.GetC()) != 0)
b11e8fb6 879 {
b81e4506
FM
880 if (stream.Eof() || (stream.LastRead() == 0) ||
881 stream.SeekI(i, wxFromCurrent) == wxInvalidOffset)
fa24cfa0
MW
882 {
883 Destroy();
884 return wxGIF_INVFORMAT;
885 }
b11e8fb6
VZ
886 }
887 }
870cf35c 888 else if ((type != 0x3B) && (type != 00)) // testing
b11e8fb6 889 {
870cf35c 890 // images are OK, but couldn't read to the end of the stream
b11e8fb6
VZ
891 return wxGIF_TRUNCATED;
892 }
8141573c
GRG
893 }
894
e4b8154a 895 return wxGIF_OK;
464122b6
JS
896}
897
7be110e3 898#endif // wxUSE_STREAMS && wxUSE_GIF