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