]> git.saurik.com Git - wxWidgets.git/blame - src/common/gifdecod.cpp
skip apple options
[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 5// Version: 3.04
464122b6 6// Copyright: (c) Guillermo Rodriguez Garcia
65571936 7// Licence: wxWindows licence
464122b6
JS
8/////////////////////////////////////////////////////////////////////////////
9
464122b6
JS
10// For compilers that support precompilation, includes "wx.h".
11#include "wx/wxprec.h"
12
13#ifdef __BORLANDC__
8898456d 14 #pragma hdrstop
464122b6
JS
15#endif
16
8898456d
WS
17#if wxUSE_STREAMS && wxUSE_GIF
18
464122b6 19#ifndef WX_PRECOMP
8898456d 20 #include "wx/palette.h"
910413e7
VZ
21 #include "wx/intl.h"
22 #include "wx/log.h"
464122b6
JS
23#endif
24
464122b6
JS
25#include <stdlib.h>
26#include <string.h>
464122b6 27#include "wx/gifdecod.h"
664e1314 28#include "wx/scopedptr.h"
56ba0394 29#include "wx/scopeguard.h"
2ce0a6e2 30
90e10cd1
DS
31enum
32{
33 GIF_MARKER_EXT = '!', // 0x21
34 GIF_MARKER_SEP = ',', // 0x2C
35 GIF_MARKER_ENDOFDATA = ';', // 0x3B
464122b6 36
90e10cd1
DS
37 GIF_MARKER_EXT_GRAPHICS_CONTROL = 0xF9,
38 GIF_MARKER_EXT_COMMENT = 0xFE,
39 GIF_MARKER_EXT_APP = 0xFF
40};
72045d57 41
15f345aa
DS
42#define GetFrame(n) ((GIFImage*)m_frames[n])
43
72045d57
VZ
44//---------------------------------------------------------------------------
45// GIFImage
46//---------------------------------------------------------------------------
47
48// internal class for storing GIF image data
49class GIFImage
50{
51public:
52 // def ctor
53 GIFImage();
54
870cf35c
VZ
55 unsigned int w; // width
56 unsigned int h; // height
57 unsigned int left; // x coord (in logical screen)
58 unsigned int top; // y coord (in logical screen)
59 int transparent; // transparent color index (-1 = none)
60 wxAnimationDisposal disposal; // disposal method
61 long delay; // delay in ms (-1 = unused)
62 unsigned char *p; // bitmap
63 unsigned char *pal; // palette
64 unsigned int ncolours; // number of colours
15f345aa 65 wxString comment;
72045d57 66
c0c133e1 67 wxDECLARE_NO_COPY_CLASS(GIFImage);
72045d57
VZ
68};
69
56ba0394
VZ
70wxDECLARE_SCOPED_PTR(GIFImage, GIFImagePtr)
71wxDEFINE_SCOPED_PTR(GIFImage, GIFImagePtr)
72045d57
VZ
72
73
65c36a73
VZ
74//---------------------------------------------------------------------------
75// GIFImage constructor
76//---------------------------------------------------------------------------
77GIFImage::GIFImage()
78{
79 w = 0;
80 h = 0;
81 left = 0;
82 top = 0;
83 transparent = 0;
72045d57 84 disposal = wxANIM_DONOTREMOVE;
65c36a73
VZ
85 delay = -1;
86 p = (unsigned char *) NULL;
87 pal = (unsigned char *) NULL;
57b5c44a 88 ncolours = 0;
65c36a73
VZ
89}
90
464122b6
JS
91//---------------------------------------------------------------------------
92// wxGIFDecoder constructor and destructor
93//---------------------------------------------------------------------------
94
72045d57 95wxGIFDecoder::wxGIFDecoder()
464122b6 96{
464122b6
JS
97}
98
99wxGIFDecoder::~wxGIFDecoder()
100{
101 Destroy();
102}
103
104void wxGIFDecoder::Destroy()
105{
72045d57 106 wxASSERT(m_nFrames==m_frames.GetCount());
870cf35c 107 for (unsigned int i=0; i<m_nFrames; i++)
464122b6 108 {
72045d57
VZ
109 GIFImage *f = (GIFImage*)m_frames[i];
110 free(f->p);
111 free(f->pal);
112 delete f;
464122b6 113 }
9742d3cc 114
72045d57
VZ
115 m_frames.Clear();
116 m_nFrames = 0;
464122b6
JS
117}
118
119
120//---------------------------------------------------------------------------
121// Convert this image to a wxImage object
122//---------------------------------------------------------------------------
123
124// This function was designed by Vaclav Slavik
125
870cf35c 126bool wxGIFDecoder::ConvertToImage(unsigned int frame, wxImage *image) const
464122b6
JS
127{
128 unsigned char *src, *dst, *pal;
129 unsigned long i;
130 int transparent;
131
870cf35c 132 // just in case...
3c87527e
GRG
133 image->Destroy();
134
870cf35c 135 // create the image
72045d57
VZ
136 wxSize sz = GetFrameSize(frame);
137 image->Create(sz.GetWidth(), sz.GetHeight());
9d1c7e84 138 image->SetType(wxBITMAP_TYPE_GIF);
464122b6 139
a1b806b9 140 if (!image->IsOk())
5d3e7b52 141 return false;
464122b6 142
72045d57
VZ
143 pal = GetPalette(frame);
144 src = GetData(frame);
464122b6 145 dst = image->GetData();
05a98b6d 146 transparent = GetTransparentColourIndex(frame);
464122b6 147
870cf35c 148 // set transparent colour mask
464122b6
JS
149 if (transparent != -1)
150 {
72045d57 151 for (i = 0; i < GetNcolours(frame); i++)
b11e8fb6
VZ
152 {
153 if ((pal[3 * i + 0] == 255) &&
154 (pal[3 * i + 1] == 0) &&
155 (pal[3 * i + 2] == 255))
156 {
157 pal[3 * i + 2] = 254;
158 }
159 }
160
161 pal[3 * transparent + 0] = 255,
162 pal[3 * transparent + 1] = 0,
163 pal[3 * transparent + 2] = 255;
164
165 image->SetMaskColour(255, 0, 255);
464122b6
JS
166 }
167 else
5d3e7b52 168 image->SetMask(false);
464122b6 169
b11e8fb6 170#if wxUSE_PALETTE
fbf1ee1d
VZ
171 unsigned char r[256];
172 unsigned char g[256];
173 unsigned char b[256];
b11e8fb6 174
fbf1ee1d
VZ
175 for (i = 0; i < 256; i++)
176 {
177 r[i] = pal[3*i + 0];
178 g[i] = pal[3*i + 1];
179 b[i] = pal[3*i + 2];
3f4fc796 180 }
fbf1ee1d 181
72045d57 182 image->SetPalette(wxPalette(GetNcolours(frame), r, g, b));
b11e8fb6 183#endif // wxUSE_PALETTE
3f4fc796 184
870cf35c 185 // copy image data
72045d57
VZ
186 unsigned long npixel = sz.GetWidth() * sz.GetHeight();
187 for (i = 0; i < npixel; i++, src++)
464122b6 188 {
b11e8fb6
VZ
189 *(dst++) = pal[3 * (*src) + 0];
190 *(dst++) = pal[3 * (*src) + 1];
191 *(dst++) = pal[3 * (*src) + 2];
464122b6
JS
192 }
193
15f345aa
DS
194 wxString comment = GetFrame(frame)->comment;
195 if ( !comment.empty() )
196 {
197 image->SetOption(wxIMAGE_OPTION_GIF_COMMENT, comment);
198 }
199
5d3e7b52 200 return true;
464122b6
JS
201}
202
2ce0a6e2 203
464122b6
JS
204//---------------------------------------------------------------------------
205// Data accessors
206//---------------------------------------------------------------------------
207
72045d57 208// Get data for current frame
464122b6 209
4c0ce682 210wxSize wxGIFDecoder::GetFrameSize(unsigned int frame) const
464122b6 211{
72045d57 212 return wxSize(GetFrame(frame)->w, GetFrame(frame)->h);
464122b6
JS
213}
214
870cf35c 215wxPoint wxGIFDecoder::GetFramePosition(unsigned int frame) const
464122b6 216{
72045d57 217 return wxPoint(GetFrame(frame)->left, GetFrame(frame)->top);
464122b6
JS
218}
219
870cf35c 220wxAnimationDisposal wxGIFDecoder::GetDisposalMethod(unsigned int frame) const
464122b6 221{
72045d57 222 return GetFrame(frame)->disposal;
2ce0a6e2 223}
464122b6 224
870cf35c 225long wxGIFDecoder::GetDelay(unsigned int frame) const
464122b6 226{
72045d57 227 return GetFrame(frame)->delay;
464122b6
JS
228}
229
870cf35c 230wxColour wxGIFDecoder::GetTransparentColour(unsigned int frame) const
05a98b6d
RR
231{
232 unsigned char *pal = GetFrame(frame)->pal;
233 int n = GetFrame(frame)->transparent;
234 if (n == -1)
235 return wxNullColour;
236
237 return wxColour(pal[n*3 + 0],
238 pal[n*3 + 1],
239 pal[n*3 + 2]);
240}
241
870cf35c
VZ
242unsigned char* wxGIFDecoder::GetData(unsigned int frame) const { return (GetFrame(frame)->p); }
243unsigned char* wxGIFDecoder::GetPalette(unsigned int frame) const { return (GetFrame(frame)->pal); }
244unsigned int wxGIFDecoder::GetNcolours(unsigned int frame) const { return (GetFrame(frame)->ncolours); }
245int wxGIFDecoder::GetTransparentColourIndex(unsigned int frame) const { return (GetFrame(frame)->transparent); }
464122b6 246
464122b6
JS
247
248
249//---------------------------------------------------------------------------
250// GIF reading and decoding
251//---------------------------------------------------------------------------
252
253// getcode:
254// Reads the next code from the file stream, with size 'bits'
255//
72045d57 256int wxGIFDecoder::getcode(wxInputStream& stream, int bits, int ab_fin)
464122b6 257{
870cf35c
VZ
258 unsigned int mask; // bit mask
259 unsigned int code; // code (result)
464122b6 260
870cf35c 261 // get remaining bits from last byte read
464122b6
JS
262 mask = (1 << bits) - 1;
263 code = (m_lastbyte >> (8 - m_restbits)) & mask;
264
870cf35c 265 // keep reading new bytes while needed
464122b6
JS
266 while (bits > m_restbits)
267 {
870cf35c 268 // if no bytes left in this block, read the next block
b11e8fb6
VZ
269 if (m_restbyte == 0)
270 {
90e10cd1 271 m_restbyte = stream.GetC();
b11e8fb6
VZ
272
273 /* Some encoders are a bit broken: instead of issuing
274 * an end-of-image symbol (ab_fin) they come up with
275 * a zero-length subblock!! We catch this here so
276 * that the decoder sees an ab_fin code.
277 */
278 if (m_restbyte == 0)
279 {
280 code = ab_fin;
281 break;
282 }
283
870cf35c 284 // prefetch data
72045d57
VZ
285 stream.Read((void *) m_buffer, m_restbyte);
286 if (stream.LastRead() != m_restbyte)
65c36a73
VZ
287 {
288 code = ab_fin;
289 return code;
290 }
b11e8fb6
VZ
291 m_bufp = m_buffer;
292 }
293
870cf35c 294 // read next byte and isolate the bits we need
b11e8fb6
VZ
295 m_lastbyte = (unsigned char) (*m_bufp++);
296 mask = (1 << (bits - m_restbits)) - 1;
297 code = code + ((m_lastbyte & mask) << m_restbits);
298 m_restbyte--;
299
870cf35c 300 // adjust total number of bits extracted from the buffer
b11e8fb6 301 m_restbits = m_restbits + 8;
464122b6 302 }
2ce0a6e2 303
870cf35c 304 // find number of bits remaining for next code
464122b6
JS
305 m_restbits = (m_restbits - bits);
306
307 return code;
308}
309
310
311// dgif:
312// GIF decoding function. The initial code size (aka root size)
313// is 'bits'. Supports interlaced images (interl == 1).
65c36a73
VZ
314// Returns wxGIF_OK (== 0) on success, or an error code if something
315// fails (see header file for details)
870cf35c
VZ
316wxGIFErrorCode
317wxGIFDecoder::dgif(wxInputStream& stream, GIFImage *img, int interl, int bits)
464122b6 318{
65c36a73 319 static const int allocSize = 4096 + 1;
870cf35c 320 int *ab_prefix = new int[allocSize]; // alphabet (prefixes)
65c36a73
VZ
321 if (ab_prefix == NULL)
322 {
323 return wxGIF_MEMERR;
324 }
325
870cf35c 326 int *ab_tail = new int[allocSize]; // alphabet (tails)
65c36a73
VZ
327 if (ab_tail == NULL)
328 {
329 delete[] ab_prefix;
330 return wxGIF_MEMERR;
331 }
332
870cf35c 333 int *stack = new int[allocSize]; // decompression stack
65c36a73
VZ
334 if (stack == NULL)
335 {
336 delete[] ab_prefix;
337 delete[] ab_tail;
338 return wxGIF_MEMERR;
339 }
340
870cf35c
VZ
341 int ab_clr; // clear code
342 int ab_fin; // end of info code
343 int ab_bits; // actual symbol width, in bits
344 int ab_free; // first free position in alphabet
345 int ab_max; // last possible character in alphabet
346 int pass; // pass number in interlaced images
347 int pos; // index into decompresion stack
348 unsigned int x, y; // position in image buffer
464122b6
JS
349
350 int code, readcode, lastcode, abcabca;
351
870cf35c 352 // these won't change
464122b6
JS
353 ab_clr = (1 << bits);
354 ab_fin = (1 << bits) + 1;
355
25e3f0c6 356 // these will change through the decompression process
464122b6
JS
357 ab_bits = bits + 1;
358 ab_free = (1 << bits) + 2;
359 ab_max = (1 << ab_bits) - 1;
360 lastcode = -1;
361 abcabca = -1;
362 pass = 1;
363 pos = x = y = 0;
364
870cf35c 365 // reset decoder vars
464122b6
JS
366 m_restbits = 0;
367 m_restbyte = 0;
368 m_lastbyte = 0;
369
370 do
371 {
870cf35c 372 // get next code
72045d57 373 readcode = code = getcode(stream, ab_bits, ab_fin);
b11e8fb6 374
870cf35c 375 // end of image?
b11e8fb6
VZ
376 if (code == ab_fin) break;
377
870cf35c 378 // reset alphabet?
b11e8fb6
VZ
379 if (code == ab_clr)
380 {
870cf35c 381 // reset main variables
b11e8fb6
VZ
382 ab_bits = bits + 1;
383 ab_free = (1 << bits) + 2;
384 ab_max = (1 << ab_bits) - 1;
385 lastcode = -1;
386 abcabca = -1;
387
870cf35c 388 // skip to next code
b11e8fb6
VZ
389 continue;
390 }
391
870cf35c 392 // unknown code: special case (like in ABCABCA)
b11e8fb6
VZ
393 if (code >= ab_free)
394 {
870cf35c
VZ
395 code = lastcode; // take last string
396 stack[pos++] = abcabca; // add first character
b11e8fb6
VZ
397 }
398
870cf35c 399 // build the string for this code in the stack
b11e8fb6
VZ
400 while (code > ab_clr)
401 {
402 stack[pos++] = ab_tail[code];
403 code = ab_prefix[code];
65c36a73
VZ
404
405 // Don't overflow. This shouldn't happen with normal
406 // GIF files, the allocSize of 4096+1 is enough. This
407 // will only happen with badly formed GIFs.
408 if (pos >= allocSize)
409 {
410 delete[] ab_prefix;
411 delete[] ab_tail;
412 delete[] stack;
413 return wxGIF_INVFORMAT;
414 }
b11e8fb6 415 }
e34f4f19
VZ
416
417 if (pos >= allocSize)
418 {
419 delete[] ab_prefix;
420 delete[] ab_tail;
421 delete[] stack;
422 return wxGIF_INVFORMAT;
423 }
424
870cf35c
VZ
425 stack[pos] = code; // push last code into the stack
426 abcabca = code; // save for special case
b11e8fb6 427
870cf35c 428 // make new entry in alphabet (only if NOT just cleared)
b11e8fb6
VZ
429 if (lastcode != -1)
430 {
1819a5c1
VZ
431 // Normally, after the alphabet is full and can't grow any
432 // further (ab_free == 4096), encoder should (must?) emit CLEAR
433 // to reset it. This checks whether we really got it, otherwise
434 // the GIF is damaged.
435 if (ab_free > ab_max)
436 {
437 delete[] ab_prefix;
438 delete[] ab_tail;
439 delete[] stack;
440 return wxGIF_INVFORMAT;
441 }
442
443 // This assert seems unnecessary since the condition above
444 // eliminates the only case in which it went false. But I really
445 // don't like being forced to ask "Who in .text could have
446 // written there?!" And I wouldn't have been forced to ask if
447 // this line had already been here.
448 wxASSERT(ab_free < allocSize);
449
b11e8fb6
VZ
450 ab_prefix[ab_free] = lastcode;
451 ab_tail[ab_free] = code;
452 ab_free++;
453
454 if ((ab_free > ab_max) && (ab_bits < 12))
455 {
456 ab_bits++;
457 ab_max = (1 << ab_bits) - 1;
458 }
459 }
460
870cf35c 461 // dump stack data to the image buffer
b11e8fb6
VZ
462 while (pos >= 0)
463 {
e34f4f19
VZ
464 (img->p)[x + (y * (img->w))] = (char) stack[pos];
465 pos--;
b11e8fb6
VZ
466
467 if (++x >= (img->w))
468 {
469 x = 0;
470
471 if (interl)
472 {
870cf35c 473 // support for interlaced images
b11e8fb6
VZ
474 switch (pass)
475 {
476 case 1: y += 8; break;
477 case 2: y += 8; break;
478 case 3: y += 4; break;
479 case 4: y += 2; break;
480 }
e34f4f19
VZ
481
482 /* loop until a valid y coordinate has been
483 found, Or if the maximum number of passes has
484 been reached, exit the loop, and stop image
3103e8a9 485 decoding (At this point the image is successfully
e34f4f19
VZ
486 decoded).
487 If we don't loop, but merely set y to some other
488 value, that new value might still be invalid depending
489 on the height of the image. This would cause out of
490 bounds writing.
491 */
492 while (y >= (img->h))
b11e8fb6
VZ
493 {
494 switch (++pass)
495 {
496 case 2: y = 4; break;
497 case 3: y = 2; break;
498 case 4: y = 1; break;
e34f4f19
VZ
499
500 default:
501 /*
502 It's possible we arrive here. For example this
503 happens when the image is interlaced, and the
504 height is 1. Looking at the above cases, the
505 lowest possible y is 1. While the only valid
506 one would be 0 for an image of height 1. So
507 'eventually' the loop will arrive here.
508 This case makes sure this while loop is
509 exited, as well as the 2 other ones.
510 */
511
512 // Set y to a valid coordinate so the local
513 // while loop will be exited. (y = 0 always
514 // is >= img->h since if img->h == 0 the
515 // image is never decoded)
516 y = 0;
517
518 // This will exit the other outer while loop
519 pos = -1;
520
521 // This will halt image decoding.
522 code = ab_fin;
523
524 break;
b11e8fb6
VZ
525 }
526 }
527 }
528 else
529 {
870cf35c 530 // non-interlaced
b11e8fb6 531 y++;
6363699a
VZ
532/*
533Normally image decoding is finished when an End of Information code is
534encountered (code == ab_fin) however some broken encoders write wrong
535"block byte counts" (The first byte value after the "code size" byte),
536being one value too high. It might very well be possible other variants
537of this problem occur as well. The only sensible solution seems to
538be to check for clipping.
539Example of wrong encoding:
540(1 * 1 B/W image, raster data stream follows in hex bytes)
541
54202 << B/W images have a code size of 2
54302 << Block byte count
54444 << LZW packed
54500 << Zero byte count (terminates data stream)
546
547Because the block byte count is 2, the zero byte count is used in the
548decoding process, and decoding is continued after this byte. (While it
549should signal an end of image)
550
551It should be:
55202
55302
55444
55501 << When decoded this correctly includes the End of Information code
55600
557
558Or (Worse solution):
55902
56001
56144
56200
563(The 44 doesn't include an End of Information code, but at least the
564decoder correctly skips to 00 now after decoding, and signals this
565as an End of Information itself)
566*/
567 if (y >= img->h)
568 {
569 code = ab_fin;
570 break;
571 }
b11e8fb6
VZ
572 }
573 }
574 }
575
576 pos = 0;
577 lastcode = readcode;
464122b6
JS
578 }
579 while (code != ab_fin);
580
33ac7e6f
KB
581 delete [] ab_prefix ;
582 delete [] ab_tail ;
583 delete [] stack ;
7679ac63 584
65c36a73 585 return wxGIF_OK;
464122b6
JS
586}
587
588
3c87527e 589// CanRead:
5d3e7b52 590// Returns true if the file looks like a valid GIF, false otherwise.
3c87527e 591//
8faef7cc 592bool wxGIFDecoder::DoCanRead(wxInputStream &stream) const
3c87527e
GRG
593{
594 unsigned char buf[3];
595
72045d57 596 if ( !stream.Read(buf, WXSIZEOF(buf)) )
5d3e7b52 597 return false;
79fa2374 598
79fa2374 599 return memcmp(buf, "GIF", WXSIZEOF(buf)) == 0;
3c87527e
GRG
600}
601
602
72045d57 603// LoadGIF:
464122b6
JS
604// Reads and decodes one or more GIF images, depending on whether
605// animated GIF support is enabled. Can read GIFs with any bit
606// size (color depth), but the output images are always expanded
607// to 8 bits per pixel. Also, the image palettes always contain
8141573c 608// 256 colors, although some of them may be unused. Returns wxGIF_OK
e4b8154a
GRG
609// (== 0) on success, or an error code if something fails (see
610// header file for details)
464122b6 611//
72045d57 612wxGIFErrorCode wxGIFDecoder::LoadGIF(wxInputStream& stream)
464122b6 613{
57b5c44a 614 unsigned int global_ncolors = 0;
4c0ce682 615 int bits, interl, i;
72045d57 616 wxAnimationDisposal disposal;
464122b6
JS
617 long size;
618 long delay;
3ca6a5f0 619 unsigned char type = 0;
464122b6
JS
620 unsigned char pal[768];
621 unsigned char buf[16];
72045d57 622 bool anim = true;
464122b6 623
870cf35c 624 // check GIF signature
72045d57 625 if (!CanRead(stream))
b11e8fb6 626 return wxGIF_INVFORMAT;
464122b6 627
870cf35c 628 // check for animated GIF support (ver. >= 89a)
65c36a73 629
870cf35c 630 static const unsigned int headerSize = (3 + 3);
72045d57
VZ
631 stream.Read(buf, headerSize);
632 if (stream.LastRead() != headerSize)
65c36a73
VZ
633 {
634 return wxGIF_INVFORMAT;
635 }
464122b6 636
464122b6 637 if (memcmp(buf + 3, "89a", 3) < 0)
65c36a73 638 {
72045d57 639 anim = false;
65c36a73 640 }
464122b6 641
870cf35c
VZ
642 // read logical screen descriptor block (LSDB)
643 static const unsigned int lsdbSize = (2 + 2 + 1 + 1 + 1);
72045d57
VZ
644 stream.Read(buf, lsdbSize);
645 if (stream.LastRead() != lsdbSize)
65c36a73
VZ
646 {
647 return wxGIF_INVFORMAT;
648 }
649
72045d57
VZ
650 m_szAnimation.SetWidth( buf[0] + 256 * buf[1] );
651 m_szAnimation.SetHeight( buf[2] + 256 * buf[3] );
464122b6 652
551270a8 653 if (anim && ((m_szAnimation.GetWidth() == 0) || (m_szAnimation.GetHeight() == 0)))
525b0568
DS
654 {
655 return wxGIF_INVFORMAT;
656 }
657
870cf35c 658 // load global color map if available
464122b6
JS
659 if ((buf[4] & 0x80) == 0x80)
660 {
72045d57 661 int backgroundColIndex = buf[5];
464122b6 662
57b5c44a 663 global_ncolors = 2 << (buf[4] & 0x07);
870cf35c 664 unsigned int numBytes = 3 * global_ncolors;
72045d57
VZ
665 stream.Read(pal, numBytes);
666 if (stream.LastRead() != numBytes)
65c36a73
VZ
667 {
668 return wxGIF_INVFORMAT;
669 }
72045d57
VZ
670
671 m_background.Set(pal[backgroundColIndex*3 + 0],
672 pal[backgroundColIndex*3 + 1],
673 pal[backgroundColIndex*3 + 2]);
464122b6
JS
674 }
675
870cf35c 676 // transparent colour, disposal method and delay default to unused
4c0ce682 677 int transparent = -1;
72045d57 678 disposal = wxANIM_UNSPECIFIED;
464122b6 679 delay = -1;
15f345aa 680 wxString comment;
464122b6 681
5d3e7b52 682 bool done = false;
525b0568 683 while (!done)
464122b6 684 {
90e10cd1 685 type = stream.GetC();
b11e8fb6 686
65c36a73
VZ
687 /*
688 If the end of file has been reached (or an error) and a ";"
90e10cd1 689 (GIF_MARKER_ENDOFDATA) hasn't been encountered yet, exit the loop. (Without this
65c36a73
VZ
690 check the while loop would loop endlessly.) Later on, in the next while
691 loop, the file will be treated as being truncated (But still
692 be decoded as far as possible). returning wxGIF_TRUNCATED is not
693 possible here since some init code is done after this loop.
694 */
72045d57 695 if (stream.Eof())// || !stream.IsOk())
65c36a73
VZ
696 {
697 /*
698 type is set to some bogus value, so there's no
699 need to continue evaluating it.
700 */
701 break; // Alternative : "return wxGIF_INVFORMAT;"
702 }
703
90e10cd1 704 switch (type)
b11e8fb6 705 {
90e10cd1
DS
706 case GIF_MARKER_ENDOFDATA:
707 done = true;
708 break;
709 case GIF_MARKER_EXT:
15f345aa 710 switch (stream.GetC())
65c36a73 711 {
15f345aa 712 case GIF_MARKER_EXT_GRAPHICS_CONTROL:
90e10cd1 713 {
15f345aa 714 // graphics control extension, parse it
b11e8fb6 715
15f345aa
DS
716 static const unsigned int gceSize = 6;
717 stream.Read(buf, gceSize);
718 if (stream.LastRead() != gceSize)
719 {
720 Destroy();
721 return wxGIF_INVFORMAT;
722 }
b11e8fb6 723
15f345aa
DS
724 // read delay and convert from 1/100 of a second to ms
725 delay = 10 * (buf[2] + 256 * buf[3]);
b11e8fb6 726
15f345aa
DS
727 // read transparent colour index, if used
728 transparent = buf[1] & 0x01 ? buf[4] : -1;
729
730 // read disposal method
731 disposal = (wxAnimationDisposal)(((buf[1] & 0x1C) >> 2) - 1);
732 break;
733 }
734 case GIF_MARKER_EXT_COMMENT:
65c36a73 735 {
15f345aa
DS
736 int len = stream.GetC();
737 while (len)
90e10cd1 738 {
15f345aa
DS
739 if ( stream.Eof() )
740 {
741 done = true;
742 break;
743 }
744
745 wxCharBuffer charbuf(len);
746 stream.Read(charbuf.data(), len);
747 if ( (int) stream.LastRead() != len )
748 {
749 done = true;
750 break;
751 }
752
753 comment += wxConvertMB2WX(charbuf.data());
754
755 len = stream.GetC();
90e10cd1 756 }
15f345aa
DS
757
758 break;
65c36a73 759 }
15f345aa
DS
760 default:
761 // other extension, skip
762 while ((i = stream.GetC()) != 0)
763 {
764 if (stream.Eof() || (stream.LastRead() == 0) ||
765 stream.SeekI(i, wxFromCurrent) == wxInvalidOffset)
766 {
767 done = true;
768 break;
769 }
770 }
771 break;
b11e8fb6 772 }
90e10cd1
DS
773 break;
774 case GIF_MARKER_SEP:
775 {
776 // allocate memory for IMAGEN struct
777 GIFImagePtr pimg(new GIFImage());
b11e8fb6 778
90e10cd1 779 wxScopeGuard guardDestroy = wxMakeObjGuard(*this, &wxGIFDecoder::Destroy);
56ba0394 780
90e10cd1
DS
781 if ( !pimg.get() )
782 return wxGIF_MEMERR;
b11e8fb6 783
90e10cd1
DS
784 // fill in the data
785 static const unsigned int idbSize = (2 + 2 + 2 + 2 + 1);
786 stream.Read(buf, idbSize);
787 if (stream.LastRead() != idbSize)
788 return wxGIF_INVFORMAT;
65c36a73 789
15f345aa
DS
790 pimg->comment = comment;
791 comment.clear();
90e10cd1
DS
792 pimg->left = buf[0] + 256 * buf[1];
793 pimg->top = buf[2] + 256 * buf[3];
794 /*
795 pimg->left = buf[4] + 256 * buf[5];
796 pimg->top = buf[4] + 256 * buf[5];
797 */
798 pimg->w = buf[4] + 256 * buf[5];
799 pimg->h = buf[6] + 256 * buf[7];
65c36a73 800
90e10cd1 801 if ( anim )
3b8ec767 802 {
90e10cd1
DS
803 // some GIF images specify incorrect animation size but we can
804 // still open them if we fix up the animation size, see #9465
805 if ( m_nFrames == 0 )
3b8ec767 806 {
90e10cd1
DS
807 if ( pimg->w > (unsigned)m_szAnimation.x )
808 m_szAnimation.x = pimg->w;
809 if ( pimg->h > (unsigned)m_szAnimation.y )
810 m_szAnimation.y = pimg->h;
811 }
812 else // subsequent frames
813 {
814 // check that we have valid size
815 if ( (!pimg->w || pimg->w > (unsigned)m_szAnimation.x) ||
816 (!pimg->h || pimg->h > (unsigned)m_szAnimation.y) )
817 {
818 wxLogError(_("Incorrect GIF frame size (%u, %d) for "
819 "the frame #%u"),
820 pimg->w, pimg->h, m_nFrames);
821 return wxGIF_INVFORMAT;
822 }
3b8ec767
VZ
823 }
824 }
65c36a73 825
90e10cd1
DS
826 interl = ((buf[8] & 0x40)? 1 : 0);
827 size = pimg->w * pimg->h;
b11e8fb6 828
90e10cd1
DS
829 pimg->transparent = transparent;
830 pimg->disposal = disposal;
831 pimg->delay = delay;
b11e8fb6 832
90e10cd1
DS
833 // allocate memory for image and palette
834 pimg->p = (unsigned char *) malloc((unsigned int)size);
835 pimg->pal = (unsigned char *) malloc(768);
b11e8fb6 836
90e10cd1
DS
837 if ((!pimg->p) || (!pimg->pal))
838 return wxGIF_MEMERR;
b11e8fb6 839
90e10cd1
DS
840 // load local color map if available, else use global map
841 if ((buf[8] & 0x80) == 0x80)
842 {
843 unsigned int local_ncolors = 2 << (buf[8] & 0x07);
844 unsigned int numBytes = 3 * local_ncolors;
845 stream.Read(pimg->pal, numBytes);
846 pimg->ncolours = local_ncolors;
847 if (stream.LastRead() != numBytes)
848 return wxGIF_INVFORMAT;
849 }
850 else
851 {
852 memcpy(pimg->pal, pal, 768);
853 pimg->ncolours = global_ncolors;
854 }
b11e8fb6 855
90e10cd1
DS
856 // get initial code size from first byte in raster data
857 bits = stream.GetC();
858 if (bits == 0)
859 return wxGIF_INVFORMAT;
b11e8fb6 860
90e10cd1
DS
861 // decode image
862 wxGIFErrorCode result = dgif(stream, pimg.get(), interl, bits);
863 if (result != wxGIF_OK)
864 return result;
56ba0394 865
90e10cd1 866 guardDestroy.Dismiss();
72045d57 867
90e10cd1
DS
868 // add the image to our frame array
869 m_frames.Add(pimg.release());
870 m_nFrames++;
b11e8fb6 871
90e10cd1
DS
872 // if this is not an animated GIF, exit after first image
873 if (!anim)
874 done = true;
875 break;
876 }
b11e8fb6 877 }
464122b6
JS
878 }
879
72045d57 880 if (m_nFrames <= 0)
464122b6 881 {
65c36a73
VZ
882 Destroy();
883 return wxGIF_INVFORMAT;
464122b6
JS
884 }
885
870cf35c 886 // try to read to the end of the stream
90e10cd1 887 while (type != GIF_MARKER_ENDOFDATA)
8141573c 888 {
72045d57 889 if (!stream.IsOk())
ef3a5e0a 890 return wxGIF_TRUNCATED;
65c36a73 891
90e10cd1 892 type = stream.GetC();
b11e8fb6 893
90e10cd1 894 switch (type)
b11e8fb6 895 {
90e10cd1
DS
896 case GIF_MARKER_EXT:
897 // extension type
898 (void) stream.GetC();
b11e8fb6 899
90e10cd1
DS
900 // skip all data
901 while ((i = stream.GetC()) != 0)
fa24cfa0 902 {
90e10cd1
DS
903 if (stream.Eof() || (stream.LastRead() == 0) ||
904 stream.SeekI(i, wxFromCurrent) == wxInvalidOffset)
905 {
906 Destroy();
907 return wxGIF_INVFORMAT;
908 }
fa24cfa0 909 }
90e10cd1
DS
910 break;
911 case GIF_MARKER_SEP:
b11e8fb6 912 {
90e10cd1
DS
913 // image descriptor block
914 static const unsigned int idbSize = (2 + 2 + 2 + 2 + 1);
915 stream.Read(buf, idbSize);
916 if (stream.LastRead() != idbSize)
b81e4506
FM
917 {
918 Destroy();
919 return wxGIF_INVFORMAT;
920 }
b11e8fb6 921
90e10cd1
DS
922 // local color map
923 if ((buf[8] & 0x80) == 0x80)
924 {
925 unsigned int local_ncolors = 2 << (buf[8] & 0x07);
926 wxFileOffset numBytes = 3 * local_ncolors;
927 if (stream.SeekI(numBytes, wxFromCurrent) == wxInvalidOffset)
928 {
929 Destroy();
930 return wxGIF_INVFORMAT;
931 }
932 }
b11e8fb6 933
90e10cd1
DS
934 // initial code size
935 (void) stream.GetC();
936 if (stream.Eof() || (stream.LastRead() == 0))
fa24cfa0
MW
937 {
938 Destroy();
939 return wxGIF_INVFORMAT;
940 }
90e10cd1
DS
941
942 // skip all data
943 while ((i = stream.GetC()) != 0)
944 {
945 if (stream.Eof() || (stream.LastRead() == 0) ||
946 stream.SeekI(i, wxFromCurrent) == wxInvalidOffset)
947 {
948 Destroy();
949 return wxGIF_INVFORMAT;
950 }
951 }
952 break;
b11e8fb6 953 }
90e10cd1
DS
954 default:
955 if ((type != GIF_MARKER_ENDOFDATA) && (type != 00)) // testing
956 {
957 // images are OK, but couldn't read to the end of the stream
958 return wxGIF_TRUNCATED;
959 }
960 break;
b11e8fb6 961 }
8141573c
GRG
962 }
963
e4b8154a 964 return wxGIF_OK;
464122b6
JS
965}
966
7be110e3 967#endif // wxUSE_STREAMS && wxUSE_GIF