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