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

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