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

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