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