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