1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:critical reason:data-parser
4
5#include "qgifhandler_p.h"
6
7#include <qimage.h>
8#include <qiodevice.h>
9#include <qloggingcategory.h>
10#include <qvariant.h>
11
12QT_BEGIN_NAMESPACE
13
14Q_LOGGING_CATEGORY(lcGif, "qt.gui.imageio.gif")
15
16#define Q_TRANSPARENT 0x00ffffff
17
18// avoid going through QImage::scanLine() which calls detach
19#define FAST_SCAN_LINE(bits, bpl, y) (bits + qptrdiff(y) * bpl)
20
21/*
22 Incremental image decoder for GIF image format.
23
24 This subclass of QImageFormat decodes GIF format images,
25 including animated GIFs. Internally in
26*/
27
28class QGIFFormat {
29public:
30 QGIFFormat();
31 ~QGIFFormat();
32
33 int decode(QImage *image, const uchar* buffer, int length,
34 int *nextFrameDelay, int *loopCount);
35 static void scan(QIODevice *device, QList<QSize> *imageSizes, int *loopCount);
36
37 bool newFrame;
38 bool partialNewFrame;
39
40private:
41 void fillRect(QImage *image, int x, int y, int w, int h, QRgb col);
42 inline QRgb color(uchar index) const;
43 static bool withinSizeLimit(int width, int height)
44 {
45 return quint64(width) * height < 16384 * 16384; // Reject unreasonable header values
46 }
47
48 // GIF specific stuff
49 QRgb* globalcmap;
50 QRgb* localcmap;
51 QImage backingstore;
52 unsigned char hold[16];
53 bool gif89;
54 int count;
55 int ccount;
56 int expectcount;
57 enum State {
58 Header,
59 LogicalScreenDescriptor,
60 GlobalColorMap,
61 LocalColorMap,
62 Introducer,
63 ImageDescriptor,
64 TableImageLZWSize,
65 ImageDataBlockSize,
66 ImageDataBlock,
67 ExtensionLabel,
68 GraphicControlExtension,
69 ApplicationExtension,
70 NetscapeExtensionBlockSize,
71 NetscapeExtensionBlock,
72 SkipBlockSize,
73 SkipBlock,
74 Done,
75 Error
76 } state;
77 int gncols;
78 int lncols;
79 int ncols;
80 int lzwsize;
81 bool lcmap;
82 int swidth, sheight;
83 int width, height;
84 int left, top, right, bottom;
85 enum Disposal { NoDisposal, DoNotChange, RestoreBackground, RestoreImage };
86 Disposal disposal;
87 bool disposed;
88 int trans_index;
89 bool gcmap;
90 int bgcol;
91 int interlace;
92 int accum;
93 int bitcount;
94
95 enum { max_lzw_bits=12 }; // (poor-compiler's static const int)
96
97 int code_size, clear_code, end_code, max_code_size, max_code;
98 int firstcode, oldcode, incode;
99 short* table[2];
100 short* stack;
101 short *sp;
102 bool needfirst;
103 int x, y;
104 int frame;
105 bool out_of_bounds;
106 bool digress;
107 void nextY(unsigned char *bits, int bpl);
108 void disposePrevious(QImage *image);
109};
110
111/*!
112 Constructs a QGIFFormat.
113*/
114QGIFFormat::QGIFFormat()
115{
116 globalcmap = nullptr;
117 localcmap = nullptr;
118 lncols = 0;
119 gncols = 0;
120 disposal = NoDisposal;
121 out_of_bounds = false;
122 disposed = true;
123 frame = -1;
124 state = Header;
125 count = 0;
126 lcmap = false;
127 newFrame = false;
128 partialNewFrame = false;
129 table[0] = nullptr;
130 table[1] = nullptr;
131 stack = nullptr;
132}
133
134/*!
135 Destroys a QGIFFormat.
136*/
137QGIFFormat::~QGIFFormat()
138{
139 if (globalcmap) delete[] globalcmap;
140 if (localcmap) delete[] localcmap;
141 delete [] stack;
142}
143
144void QGIFFormat::disposePrevious(QImage *image)
145{
146 if (out_of_bounds) {
147 // flush anything that survived
148 // ### Changed: QRect(0, 0, swidth, sheight)
149 }
150
151 // Handle disposal of previous image before processing next one
152
153 if (disposed) return;
154
155 int l = qMin(a: swidth-1,b: left);
156 int r = qMin(a: swidth-1,b: right);
157 int t = qMin(a: sheight-1,b: top);
158 int b = qMin(a: sheight-1,b: bottom);
159
160 switch (disposal) {
161 case NoDisposal:
162 break;
163 case DoNotChange:
164 break;
165 case RestoreBackground:
166 if (trans_index>=0) {
167 // Easy: we use the transparent color
168 fillRect(image, x: l, y: t, w: r-l+1, h: b-t+1, Q_TRANSPARENT);
169 } else if (bgcol>=0) {
170 // Easy: we use the bgcol given
171 fillRect(image, x: l, y: t, w: r-l+1, h: b-t+1, col: color(index: bgcol));
172 } else {
173 // Impossible: We don't know of a bgcol - use pixel 0
174 const QRgb *bits = reinterpret_cast<const QRgb *>(image->constBits());
175 fillRect(image, x: l, y: t, w: r-l+1, h: b-t+1, col: bits[0]);
176 }
177 // ### Changed: QRect(l, t, r-l+1, b-t+1)
178 break;
179 case RestoreImage: {
180 if (frame >= 0) {
181 for (int ln=t; ln<=b; ln++) {
182 memcpy(dest: image->scanLine(ln)+l*sizeof(QRgb),
183 src: backingstore.constScanLine(ln-t),
184 n: (r-l+1)*sizeof(QRgb));
185 }
186 // ### Changed: QRect(l, t, r-l+1, b-t+1)
187 }
188 }
189 }
190 disposal = NoDisposal; // Until an extension says otherwise.
191
192 disposed = true;
193}
194
195/*!
196 This function decodes some data into image changes.
197
198 Returns the number of bytes consumed.
199*/
200int QGIFFormat::decode(QImage *image, const uchar *buffer, int length,
201 int *nextFrameDelay, int *loopCount)
202{
203 // We are required to state that
204 // "The Graphics Interchange Format(c) is the Copyright property of
205 // CompuServe Incorporated. GIF(sm) is a Service Mark property of
206 // CompuServe Incorporated."
207
208 if (!stack) {
209 stack = new short[(1 << max_lzw_bits) * 4];
210 table[0] = &stack[(1 << max_lzw_bits) * 2];
211 table[1] = &stack[(1 << max_lzw_bits) * 3];
212 }
213
214 image->detach();
215 qsizetype bpl = image->bytesPerLine();
216 unsigned char *bits = image->bits();
217
218#define LM(l, m) (((m)<<8)|l)
219 digress = false;
220 const int initial = length;
221 while (!digress && length) {
222 length--;
223 unsigned char ch=*buffer++;
224 switch (state) {
225 case Header:
226 hold[count++]=ch;
227 if (count==6) {
228 // Header
229 gif89=(hold[3]!='8' || hold[4]!='7');
230 state=LogicalScreenDescriptor;
231 count=0;
232 }
233 break;
234 case LogicalScreenDescriptor:
235 hold[count++]=ch;
236 if (count==7) {
237 // Logical Screen Descriptor
238 swidth=LM(hold[0], hold[1]);
239 sheight=LM(hold[2], hold[3]);
240 gcmap=!!(hold[4]&0x80);
241 //UNUSED: bpchan=(((hold[4]&0x70)>>3)+1);
242 //UNUSED: gcmsortflag=!!(hold[4]&0x08);
243 gncols=2<<(hold[4]&0x7);
244 bgcol=(gcmap) ? hold[5] : -1;
245 //aspect=hold[6] ? double(hold[6]+15)/64.0 : 1.0;
246
247 trans_index = -1;
248 count=0;
249 ncols=gncols;
250 if (gcmap) {
251 ccount=0;
252 state=GlobalColorMap;
253 globalcmap = new QRgb[gncols+1]; // +1 for trans_index
254 globalcmap[gncols] = Q_TRANSPARENT;
255 } else {
256 state=Introducer;
257 }
258 }
259 break;
260 case GlobalColorMap: case LocalColorMap:
261 hold[count++]=ch;
262 if (count==3) {
263 QRgb rgb = qRgb(r: hold[0], g: hold[1], b: hold[2]);
264 if (state == LocalColorMap) {
265 if (ccount < lncols)
266 localcmap[ccount] = rgb;
267 } else {
268 globalcmap[ccount] = rgb;
269 }
270 if (++ccount >= ncols) {
271 if (state == LocalColorMap)
272 state=TableImageLZWSize;
273 else
274 state=Introducer;
275 }
276 count=0;
277 }
278 break;
279 case Introducer:
280 hold[count++]=ch;
281 switch (ch) {
282 case ',':
283 state=ImageDescriptor;
284 break;
285 case '!':
286 state=ExtensionLabel;
287 break;
288 case ';':
289 // ### Changed: QRect(0, 0, swidth, sheight)
290 state=Done;
291 break;
292 default:
293 digress=true;
294 // Unexpected Introducer - ignore block
295 state=Error;
296 }
297 break;
298 case ImageDescriptor:
299 hold[count++]=ch;
300 if (count==10) {
301 int newleft=LM(hold[1], hold[2]);
302 int newtop=LM(hold[3], hold[4]);
303 int newwidth=LM(hold[5], hold[6]);
304 int newheight=LM(hold[7], hold[8]);
305
306 // disbelieve ridiculous logical screen sizes,
307 // unless the image frames are also large.
308 if (swidth/10 > qMax(a: newwidth,b: 16384))
309 swidth = -1;
310 if (sheight/10 > qMax(a: newheight,b: 16384))
311 sheight = -1;
312
313 if (swidth <= 0)
314 swidth = newleft + newwidth;
315 if (sheight <= 0)
316 sheight = newtop + newheight;
317
318 QImage::Format format = trans_index >= 0 ? QImage::Format_ARGB32 : QImage::Format_RGB32;
319 if (image->isNull()) {
320 if (!withinSizeLimit(width: swidth, height: sheight)) {
321 state = Error;
322 return -1;
323 }
324 if (!QImageIOHandler::allocateImage(size: QSize(swidth, sheight), format, image)) {
325 state = Error;
326 return -1;
327 }
328 bpl = image->bytesPerLine();
329 bits = image->bits();
330 if (bits)
331 memset(s: bits, c: 0, n: image->sizeInBytes());
332 }
333
334 // Check if the previous attempt to create the image failed. If it
335 // did then the image is broken and we should give up.
336 if (image->isNull()) {
337 state = Error;
338 return -1;
339 }
340
341 disposePrevious(image);
342 disposed = false;
343
344 left = newleft;
345 top = newtop;
346 width = newwidth;
347 height = newheight;
348
349 right=qMax(a: 0, b: qMin(a: left+width, b: swidth)-1);
350 bottom=qMax(a: 0, b: qMin(a: top+height, b: sheight)-1);
351 lcmap=!!(hold[9]&0x80);
352 interlace=!!(hold[9]&0x40);
353 //bool lcmsortflag=!!(hold[9]&0x20);
354 lncols=lcmap ? (2<<(hold[9]&0x7)) : 0;
355 if (lncols) {
356 if (localcmap)
357 delete [] localcmap;
358 localcmap = new QRgb[lncols+1];
359 localcmap[lncols] = Q_TRANSPARENT;
360 ncols = lncols;
361 } else {
362 ncols = gncols;
363 }
364 frame++;
365 if (frame == 0) {
366 if (left || top || width<swidth || height<sheight) {
367 // Not full-size image - erase with bg or transparent
368 if (trans_index >= 0) {
369 fillRect(image, x: 0, y: 0, w: swidth, h: sheight, col: color(index: trans_index));
370 // ### Changed: QRect(0, 0, swidth, sheight)
371 } else if (bgcol>=0) {
372 fillRect(image, x: 0, y: 0, w: swidth, h: sheight, col: color(index: bgcol));
373 // ### Changed: QRect(0, 0, swidth, sheight)
374 }
375 }
376 }
377
378 if (disposal == RestoreImage) {
379 int l = qMin(a: swidth-1,b: left);
380 int r = qMin(a: swidth-1,b: right);
381 int t = qMin(a: sheight-1,b: top);
382 int b = qMin(a: sheight-1,b: bottom);
383 int w = r-l+1;
384 int h = b-t+1;
385
386 if (backingstore.width() < w
387 || backingstore.height() < h) {
388
389 if (!withinSizeLimit(width: w, height: h)) {
390 state = Error;
391 return -1;
392 }
393 // We just use the backing store as a byte array
394 QSize bsSize(qMax(a: backingstore.width(), b: w), qMax(a: backingstore.height(), b: h));
395 if (!QImageIOHandler::allocateImage(size: bsSize, format: QImage::Format_RGB32,
396 image: &backingstore)) {
397 state = Error;
398 return -1;
399 }
400 memset(s: backingstore.bits(), c: 0, n: backingstore.sizeInBytes());
401 }
402 const qsizetype dest_bpl = backingstore.bytesPerLine();
403 unsigned char *dest_data = backingstore.bits();
404 for (int ln=0; ln<h; ln++) {
405 memcpy(FAST_SCAN_LINE(dest_data, dest_bpl, ln),
406 FAST_SCAN_LINE(bits, bpl, t+ln) + l*sizeof(QRgb), n: w*sizeof(QRgb));
407 }
408 }
409
410 count=0;
411 if (lcmap) {
412 ccount=0;
413 state=LocalColorMap;
414 } else {
415 state=TableImageLZWSize;
416 }
417 x = left;
418 y = top;
419 accum = 0;
420 bitcount = 0;
421 sp = stack;
422 firstcode = oldcode = 0;
423 needfirst = true;
424 out_of_bounds = left>=swidth || y>=sheight;
425 }
426 break;
427 case TableImageLZWSize: {
428 lzwsize=ch;
429 if (lzwsize > max_lzw_bits) {
430 state=Error;
431 } else {
432 code_size=lzwsize+1;
433 clear_code=1<<lzwsize;
434 end_code=clear_code+1;
435 max_code_size=2*clear_code;
436 max_code=clear_code+2;
437 int i;
438 for (i=0; i<clear_code; i++) {
439 table[0][i]=0;
440 table[1][i]=i;
441 }
442 state=ImageDataBlockSize;
443 }
444 count=0;
445 break;
446 } case ImageDataBlockSize:
447 expectcount=ch;
448 if (expectcount) {
449 state=ImageDataBlock;
450 } else {
451 state=Introducer;
452 digress = true;
453 newFrame = true;
454 }
455 break;
456 case ImageDataBlock:
457 count++;
458 if (bitcount != -32768) {
459 if (bitcount < 0 || bitcount > 31) {
460 state = Error;
461 return -1;
462 }
463 accum |= (ch << bitcount);
464 bitcount += 8;
465 }
466 while (bitcount>=code_size && state==ImageDataBlock) {
467 int code=accum&((1<<code_size)-1);
468 bitcount-=code_size;
469 accum>>=code_size;
470
471 if (code==clear_code) {
472 if (!needfirst) {
473 code_size=lzwsize+1;
474 max_code_size=2*clear_code;
475 max_code=clear_code+2;
476 }
477 needfirst=true;
478 } else if (code==end_code) {
479 bitcount = -32768;
480 // Left the block end arrive
481 } else {
482 if (needfirst) {
483 firstcode=oldcode=code;
484 if (!out_of_bounds && image->height() > y && ((frame == 0) || (firstcode != trans_index)))
485 ((QRgb*)FAST_SCAN_LINE(bits, bpl, y))[x] = color(index: firstcode);
486 x++;
487 if (x>=swidth) out_of_bounds = true;
488 needfirst=false;
489 if (x>=left+width) {
490 x=left;
491 out_of_bounds = left>=swidth || y>=sheight;
492 nextY(bits, bpl);
493 }
494 } else {
495 incode=code;
496 if (code>=max_code) {
497 *sp++=firstcode;
498 code=oldcode;
499 }
500 while (code>=clear_code+2) {
501 if (code >= max_code) {
502 state = Error;
503 return -1;
504 }
505 *sp++=table[1][code];
506 if (code==table[0][code]) {
507 state=Error;
508 return -1;
509 }
510 if (sp-stack>=(1<<(max_lzw_bits))*2) {
511 state=Error;
512 return -1;
513 }
514 code=table[0][code];
515 }
516 if (code < 0) {
517 state = Error;
518 return -1;
519 }
520
521 *sp++=firstcode=table[1][code];
522 code=max_code;
523 if (code<(1<<max_lzw_bits)) {
524 table[0][code]=oldcode;
525 table[1][code]=firstcode;
526 max_code++;
527 if ((max_code>=max_code_size)
528 && (max_code_size<(1<<max_lzw_bits)))
529 {
530 max_code_size*=2;
531 code_size++;
532 }
533 }
534 oldcode=incode;
535 const int h = image->height();
536 QRgb *line = nullptr;
537 if (!out_of_bounds && h > y)
538 line = (QRgb*)FAST_SCAN_LINE(bits, bpl, y);
539 while (sp>stack) {
540 const uchar index = *(--sp);
541 if (!out_of_bounds && h > y && ((frame == 0) || (index != trans_index))) {
542 line[x] = color(index);
543 }
544 x++;
545 if (x>=swidth) out_of_bounds = true;
546 if (x>=left+width) {
547 x=left;
548 out_of_bounds = left>=swidth || y>=sheight;
549 nextY(bits, bpl);
550 if (!out_of_bounds && h > y)
551 line = (QRgb*)FAST_SCAN_LINE(bits, bpl, y);
552 }
553 }
554 }
555 }
556 }
557 partialNewFrame = true;
558 if (count==expectcount) {
559 count=0;
560 state=ImageDataBlockSize;
561 }
562 break;
563 case ExtensionLabel:
564 switch (ch) {
565 case 0xf9:
566 state=GraphicControlExtension;
567 break;
568 case 0xff:
569 state=ApplicationExtension;
570 break;
571#if 0
572 case 0xfe:
573 state=CommentExtension;
574 break;
575 case 0x01:
576 break;
577#endif
578 default:
579 state=SkipBlockSize;
580 }
581 count=0;
582 break;
583 case ApplicationExtension:
584 if (count<11) hold[count]=ch;
585 count++;
586 if (count==hold[0]+1) {
587 if (qstrncmp(str1: (char*)(hold+1), str2: "NETSCAPE", len: 8)==0) {
588 // Looping extension
589 state=NetscapeExtensionBlockSize;
590 } else {
591 state=SkipBlockSize;
592 }
593 count=0;
594 }
595 break;
596 case NetscapeExtensionBlockSize:
597 expectcount=ch;
598 count=0;
599 if (expectcount) state=NetscapeExtensionBlock;
600 else state=Introducer;
601 break;
602 case NetscapeExtensionBlock:
603 if (count<3) hold[count]=ch;
604 count++;
605 if (count==expectcount) {
606 *loopCount = hold[1]+hold[2]*256;
607 state=SkipBlockSize; // Ignore further blocks
608 }
609 break;
610 case GraphicControlExtension:
611 if (count<5) hold[count]=ch;
612 count++;
613 if (count==hold[0]+1) {
614 disposePrevious(image);
615 uint dBits = (hold[1] >> 2) & 0x7;
616 disposal = (dBits <= RestoreImage) ? Disposal(dBits) : NoDisposal;
617 //UNUSED: waitforuser=!!((hold[1]>>1)&0x1);
618 int delay=count>3 ? LM(hold[2], hold[3]) : 1;
619 // IE and mozilla use a minimum delay of 10. With the minimum delay of 10
620 // we are compatible to them and avoid huge loads on the app and xserver.
621 *nextFrameDelay = (delay < 2 ? 10 : delay) * 10;
622
623 bool havetrans=hold[1]&0x1;
624 trans_index = havetrans ? hold[4] : -1;
625
626 count=0;
627 state=SkipBlockSize;
628 }
629 break;
630 case SkipBlockSize:
631 expectcount=ch;
632 count=0;
633 if (expectcount) state=SkipBlock;
634 else state=Introducer;
635 break;
636 case SkipBlock:
637 count++;
638 if (count==expectcount) state=SkipBlockSize;
639 break;
640 case Done:
641 digress=true;
642 /* Netscape ignores the junk, so we do too.
643 length++; // Unget
644 state=Error; // More calls to this is an error
645 */
646 break;
647 case Error:
648 return -1; // Called again after done.
649 }
650 }
651 return initial-length;
652}
653
654/*!
655 Scans through the data stream defined by \a device and returns the image
656 sizes found in the stream in the \a imageSizes list.
657*/
658void QGIFFormat::scan(QIODevice *device, QList<QSize> *imageSizes, int *loopCount)
659{
660 if (!device)
661 return;
662
663 qint64 oldPos = device->pos();
664 if (device->isSequential() || !device->seek(pos: 0))
665 return;
666
667 int colorCount = 0;
668 int localColorCount = 0;
669 int globalColorCount = 0;
670 int colorReadCount = 0;
671 bool localColormap = false;
672 bool globalColormap = false;
673 int count = 0;
674 int blockSize = 0;
675 int imageWidth = 0;
676 int imageHeight = 0;
677 bool done = false;
678 uchar hold[16];
679 State state = Header;
680
681 const int readBufferSize = 40960; // 40k read buffer
682 QByteArray readBuffer(device->read(maxlen: readBufferSize));
683
684 if (readBuffer.isEmpty()) {
685 device->seek(pos: oldPos);
686 return;
687 }
688
689 // This is a specialized version of the state machine from decode(),
690 // which doesn't do any image decoding or mallocing, and has an
691 // optimized way of skipping SkipBlocks, ImageDataBlocks and
692 // Global/LocalColorMaps.
693
694 while (!readBuffer.isEmpty()) {
695 int length = readBuffer.size();
696 const uchar *buffer = (const uchar *) readBuffer.constData();
697 while (!done && length) {
698 length--;
699 uchar ch = *buffer++;
700 switch (state) {
701 case Header:
702 hold[count++] = ch;
703 if (count == 6) {
704 state = LogicalScreenDescriptor;
705 count = 0;
706 }
707 break;
708 case LogicalScreenDescriptor:
709 hold[count++] = ch;
710 if (count == 7) {
711 imageWidth = LM(hold[0], hold[1]);
712 imageHeight = LM(hold[2], hold[3]);
713 globalColormap = !!(hold[4] & 0x80);
714 globalColorCount = 2 << (hold[4] & 0x7);
715 count = 0;
716 colorCount = globalColorCount;
717 if (globalColormap) {
718 int colorTableSize = 3 * globalColorCount;
719 if (length >= colorTableSize) {
720 // skip the global color table in one go
721 length -= colorTableSize;
722 buffer += colorTableSize;
723 state = Introducer;
724 } else {
725 colorReadCount = 0;
726 state = GlobalColorMap;
727 }
728 } else {
729 state=Introducer;
730 }
731 }
732 break;
733 case GlobalColorMap:
734 case LocalColorMap:
735 hold[count++] = ch;
736 if (count == 3) {
737 if (++colorReadCount >= colorCount) {
738 if (state == LocalColorMap)
739 state = TableImageLZWSize;
740 else
741 state = Introducer;
742 }
743 count = 0;
744 }
745 break;
746 case Introducer:
747 hold[count++] = ch;
748 switch (ch) {
749 case 0x2c:
750 state = ImageDescriptor;
751 break;
752 case 0x21:
753 state = ExtensionLabel;
754 break;
755 case 0x3b:
756 state = Done;
757 break;
758 default:
759 done = true;
760 state = Error;
761 }
762 break;
763 case ImageDescriptor:
764 hold[count++] = ch;
765 if (count == 10) {
766 int newLeft = LM(hold[1], hold[2]);
767 int newTop = LM(hold[3], hold[4]);
768 int newWidth = LM(hold[5], hold[6]);
769 int newHeight = LM(hold[7], hold[8]);
770
771 if (imageWidth/10 > qMax(a: newWidth,b: 200))
772 imageWidth = -1;
773 if (imageHeight/10 > qMax(a: newHeight,b: 200))
774 imageHeight = -1;
775
776 if (imageWidth <= 0)
777 imageWidth = newLeft + newWidth;
778 if (imageHeight <= 0)
779 imageHeight = newTop + newHeight;
780
781 *imageSizes << QSize(imageWidth, imageHeight);
782
783 localColormap = !!(hold[9] & 0x80);
784 localColorCount = localColormap ? (2 << (hold[9] & 0x7)) : 0;
785 if (localColorCount)
786 colorCount = localColorCount;
787 else
788 colorCount = globalColorCount;
789
790 count = 0;
791 if (localColormap) {
792 int colorTableSize = 3 * localColorCount;
793 if (length >= colorTableSize) {
794 // skip the local color table in one go
795 length -= colorTableSize;
796 buffer += colorTableSize;
797 state = TableImageLZWSize;
798 } else {
799 colorReadCount = 0;
800 state = LocalColorMap;
801 }
802 } else {
803 state = TableImageLZWSize;
804 }
805 }
806 break;
807 case TableImageLZWSize:
808 if (ch > max_lzw_bits)
809 state = Error;
810 else
811 state = ImageDataBlockSize;
812 count = 0;
813 break;
814 case ImageDataBlockSize:
815 blockSize = ch;
816 if (blockSize) {
817 if (length >= blockSize) {
818 // we can skip the block in one go
819 length -= blockSize;
820 buffer += blockSize;
821 count = 0;
822 } else {
823 state = ImageDataBlock;
824 }
825 } else {
826 state = Introducer;
827 }
828 break;
829 case ImageDataBlock:
830 ++count;
831 if (count == blockSize) {
832 count = 0;
833 state = ImageDataBlockSize;
834 }
835 break;
836 case ExtensionLabel:
837 switch (ch) {
838 case 0xf9:
839 state = GraphicControlExtension;
840 break;
841 case 0xff:
842 state = ApplicationExtension;
843 break;
844 default:
845 state = SkipBlockSize;
846 }
847 count = 0;
848 break;
849 case ApplicationExtension:
850 if (count < 11)
851 hold[count] = ch;
852 ++count;
853 if (count == hold[0] + 1) {
854 if (qstrncmp(str1: (char*)(hold+1), str2: "NETSCAPE", len: 8) == 0)
855 state=NetscapeExtensionBlockSize;
856 else
857 state=SkipBlockSize;
858 count = 0;
859 }
860 break;
861 case GraphicControlExtension:
862 if (count < 5)
863 hold[count] = ch;
864 ++count;
865 if (count == hold[0] + 1) {
866 count = 0;
867 state = SkipBlockSize;
868 }
869 break;
870 case NetscapeExtensionBlockSize:
871 blockSize = ch;
872 count = 0;
873 if (blockSize)
874 state = NetscapeExtensionBlock;
875 else
876 state = Introducer;
877 break;
878 case NetscapeExtensionBlock:
879 if (count < 3)
880 hold[count] = ch;
881 count++;
882 if (count == blockSize) {
883 *loopCount = LM(hold[1], hold[2]);
884 state = SkipBlockSize;
885 }
886 break;
887 case SkipBlockSize:
888 blockSize = ch;
889 count = 0;
890 if (blockSize) {
891 if (length >= blockSize) {
892 // we can skip the block in one go
893 length -= blockSize;
894 buffer += blockSize;
895 } else {
896 state = SkipBlock;
897 }
898 } else {
899 state = Introducer;
900 }
901 break;
902 case SkipBlock:
903 ++count;
904 if (count == blockSize)
905 state = SkipBlockSize;
906 break;
907 case Done:
908 done = true;
909 break;
910 case Error:
911 device->seek(pos: oldPos);
912 return;
913 }
914 }
915 readBuffer = device->read(maxlen: readBufferSize);
916 }
917 device->seek(pos: oldPos);
918 return;
919}
920
921void QGIFFormat::fillRect(QImage *image, int col, int row, int w, int h, QRgb color)
922{
923 if (w>0) {
924 for (int j=0; j<h; j++) {
925 QRgb *line = (QRgb*)image->scanLine(j+row);
926 for (int i=0; i<w; i++)
927 *(line+col+i) = color;
928 }
929 }
930}
931
932void QGIFFormat::nextY(unsigned char *bits, int bpl)
933{
934 if (out_of_bounds)
935 return;
936 int my;
937 switch (interlace) {
938 case 0: // Non-interlaced
939 // if (!out_of_bounds) {
940 // ### Changed: QRect(left, y, right - left + 1, 1);
941 // }
942 y++;
943 break;
944 case 1: {
945 int i;
946 my = qMin(a: 7, b: bottom-y);
947 // Don't dup with transparency
948 if (trans_index < 0) {
949 for (i=1; i<=my; i++) {
950 memcpy(FAST_SCAN_LINE(bits, bpl, y+i)+left*sizeof(QRgb), FAST_SCAN_LINE(bits, bpl, y)+left*sizeof(QRgb),
951 n: (right-left+1)*sizeof(QRgb));
952 }
953 }
954
955 // if (!out_of_bounds) {
956 // ### Changed: QRect(left, y, right - left + 1, my + 1);
957 // }
958// if (!out_of_bounds)
959// qDebug("consumer->changed(QRect(%d, %d, %d, %d))", left, y, right-left+1, my+1);
960 y+=8;
961 if (y>bottom) {
962 interlace++; y=top+4;
963 if (y > bottom) { // for really broken GIFs with bottom < 5
964 interlace=2;
965 y = top + 2;
966 if (y > bottom) { // for really broken GIF with bottom < 3
967 interlace = 0;
968 y = top + 1;
969 }
970 }
971 }
972 } break;
973 case 2: {
974 int i;
975 my = qMin(a: 3, b: bottom-y);
976 // Don't dup with transparency
977 if (trans_index < 0) {
978 for (i=1; i<=my; i++) {
979 memcpy(FAST_SCAN_LINE(bits, bpl, y+i)+left*sizeof(QRgb), FAST_SCAN_LINE(bits, bpl, y)+left*sizeof(QRgb),
980 n: (right-left+1)*sizeof(QRgb));
981 }
982 }
983
984 // if (!out_of_bounds) {
985 // ### Changed: QRect(left, y, right - left + 1, my + 1);
986 // }
987 y+=8;
988 if (y>bottom) {
989 interlace++; y=top+2;
990 // handle broken GIF with bottom < 3
991 if (y > bottom) {
992 interlace = 3;
993 y = top + 1;
994 }
995 }
996 } break;
997 case 3: {
998 int i;
999 my = qMin(a: 1, b: bottom-y);
1000 // Don't dup with transparency
1001 if (trans_index < 0) {
1002 for (i=1; i<=my; i++) {
1003 memcpy(FAST_SCAN_LINE(bits, bpl, y+i)+left*sizeof(QRgb), FAST_SCAN_LINE(bits, bpl, y)+left*sizeof(QRgb),
1004 n: (right-left+1)*sizeof(QRgb));
1005 }
1006 }
1007 // if (!out_of_bounds) {
1008 // ### Changed: QRect(left, y, right - left + 1, my + 1);
1009 // }
1010 y+=4;
1011 if (y>bottom) { interlace++; y=top+1; }
1012 } break;
1013 case 4:
1014 // if (!out_of_bounds) {
1015 // ### Changed: QRect(left, y, right - left + 1, 1);
1016 // }
1017 y+=2;
1018 }
1019
1020 // Consume bogus extra lines
1021 if (y >= sheight) out_of_bounds=true; //y=bottom;
1022}
1023
1024inline QRgb QGIFFormat::color(uchar index) const
1025{
1026 if (index > ncols)
1027 return Q_TRANSPARENT;
1028
1029 QRgb *map = lcmap ? localcmap : globalcmap;
1030 QRgb col = map ? map[index] : 0;
1031 return index == trans_index ? col & Q_TRANSPARENT : col;
1032}
1033
1034//-------------------------------------------------------------------------
1035//-------------------------------------------------------------------------
1036//-------------------------------------------------------------------------
1037
1038QGifHandler::QGifHandler()
1039{
1040 gifFormat = new QGIFFormat;
1041 nextDelay = 100;
1042 loopCnt = -1;
1043 frameNumber = -1;
1044 scanIsCached = false;
1045}
1046
1047QGifHandler::~QGifHandler()
1048{
1049 delete gifFormat;
1050}
1051
1052// Does partial decode if necessary, just to see if an image is coming
1053
1054bool QGifHandler::imageIsComing() const
1055{
1056 const int GifChunkSize = 4096;
1057
1058 while (!gifFormat->partialNewFrame) {
1059 if (buffer.isEmpty()) {
1060 buffer += device()->read(maxlen: GifChunkSize);
1061 if (buffer.isEmpty())
1062 break;
1063 }
1064
1065 int decoded = gifFormat->decode(image: &lastImage, buffer: (const uchar *)buffer.constData(), length: buffer.size(),
1066 nextFrameDelay: &nextDelay, loopCount: &loopCnt);
1067 if (decoded == -1)
1068 break;
1069 buffer.remove(index: 0, len: decoded);
1070 }
1071 return gifFormat->partialNewFrame;
1072}
1073
1074bool QGifHandler::canRead() const
1075{
1076 if (canRead(device: device()) || imageIsComing()) {
1077 setFormat("gif");
1078 return true;
1079 }
1080
1081 return false;
1082}
1083
1084bool QGifHandler::canRead(QIODevice *device)
1085{
1086 if (!device) {
1087 qCWarning(lcGif, "QGifHandler::canRead() called with no device");
1088 return false;
1089 }
1090
1091 char head[6];
1092 if (device->peek(data: head, maxlen: sizeof(head)) == sizeof(head))
1093 return qstrncmp(str1: head, str2: "GIF87a", len: 6) == 0
1094 || qstrncmp(str1: head, str2: "GIF89a", len: 6) == 0;
1095 return false;
1096}
1097
1098bool QGifHandler::read(QImage *image)
1099{
1100 const int GifChunkSize = 4096;
1101
1102 while (!gifFormat->newFrame) {
1103 if (buffer.isEmpty()) {
1104 buffer += device()->read(maxlen: GifChunkSize);
1105 if (buffer.isEmpty())
1106 break;
1107 }
1108
1109 int decoded = gifFormat->decode(image: &lastImage, buffer: (const uchar *)buffer.constData(), length: buffer.size(),
1110 nextFrameDelay: &nextDelay, loopCount: &loopCnt);
1111 if (decoded == -1)
1112 break;
1113 buffer.remove(index: 0, len: decoded);
1114 }
1115 if (gifFormat->newFrame || (gifFormat->partialNewFrame && device()->atEnd())) {
1116 *image = lastImage;
1117 ++frameNumber;
1118 gifFormat->newFrame = false;
1119 gifFormat->partialNewFrame = false;
1120 return true;
1121 }
1122
1123 return false;
1124}
1125
1126bool QGifHandler::write(const QImage &image)
1127{
1128 Q_UNUSED(image);
1129 return false;
1130}
1131
1132bool QGifHandler::supportsOption(ImageOption option) const
1133{
1134 if (!device() || device()->isSequential())
1135 return option == Animation;
1136 else
1137 return option == Size
1138 || option == Animation;
1139}
1140
1141QVariant QGifHandler::option(ImageOption option) const
1142{
1143 if (option == Size) {
1144 if (!scanIsCached) {
1145 QGIFFormat::scan(device: device(), imageSizes: &imageSizes, loopCount: &loopCnt);
1146 scanIsCached = true;
1147 }
1148 // before the first frame is read, or we have an empty data stream
1149 if (frameNumber == -1)
1150 return (imageSizes.size() > 0) ? QVariant(imageSizes.at(i: 0)) : QVariant();
1151 // after the last frame has been read, the next size is undefined
1152 if (frameNumber >= imageSizes.size() - 1)
1153 return QVariant();
1154 // and the last case: the size of the next frame
1155 return imageSizes.at(i: frameNumber + 1);
1156 } else if (option == Animation) {
1157 return true;
1158 }
1159 return QVariant();
1160}
1161
1162void QGifHandler::setOption(ImageOption option, const QVariant &value)
1163{
1164 Q_UNUSED(option);
1165 Q_UNUSED(value);
1166}
1167
1168int QGifHandler::nextImageDelay() const
1169{
1170 return nextDelay;
1171}
1172
1173int QGifHandler::imageCount() const
1174{
1175 if (!scanIsCached) {
1176 QGIFFormat::scan(device: device(), imageSizes: &imageSizes, loopCount: &loopCnt);
1177 scanIsCached = true;
1178 }
1179 return imageSizes.size();
1180}
1181
1182int QGifHandler::loopCount() const
1183{
1184 if (!scanIsCached) {
1185 QGIFFormat::scan(device: device(), imageSizes: &imageSizes, loopCount: &loopCnt);
1186 scanIsCached = true;
1187 }
1188
1189 if (loopCnt == 0)
1190 return -1;
1191 else if (loopCnt == -1)
1192 return 0;
1193 else
1194 return loopCnt;
1195}
1196
1197int QGifHandler::currentImageNumber() const
1198{
1199 return frameNumber;
1200}
1201
1202QT_END_NAMESPACE
1203

source code of qtbase/src/plugins/imageformats/gif/qgifhandler.cpp