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