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