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