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