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
51 QT_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
65 class QGIFFormat {
66 public:
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
77 private:
78 void fillRect(QImage *image, int x, int y, int w, int h, QRgb col);
79 inline QRgb color(uchar index) const;
withinSizeLimit(int width,int height)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 */
QGIFFormat()151 QGIFFormat::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 */
~QGIFFormat()174 QGIFFormat::~QGIFFormat()
175 {
176 if (globalcmap) delete[] globalcmap;
177 if (localcmap) delete[] localcmap;
178 delete [] stack;
179 }
180
disposePrevious(QImage * image)181 void 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(swidth-1,left);
193 int r = qMin(swidth-1,right);
194 int t = qMin(sheight-1,top);
195 int b = qMin(sheight-1,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, l, t, r-l+1, b-t+1, Q_TRANSPARENT);
206 } else if (bgcol>=0) {
207 // Easy: we use the bgcol given
208 fillRect(image, l, t, r-l+1, b-t+1, color(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, l, t, r-l+1, b-t+1, 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(image->scanLine(ln)+l*sizeof(QRgb),
220 backingstore.constScanLine(ln-t),
221 (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 */
decode(QImage * image,const uchar * buffer,int length,int * nextFrameDelay,int * loopCount)237 int 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(hold[0], hold[1], 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(newwidth,16384))
346 swidth = -1;
347 if (sheight/10 > qMax(newheight,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(swidth, 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(bits, 0, 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(0, qMin(left+width, swidth)-1);
384 bottom=qMax(0, qMin(top+height, 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, 0, 0, swidth, sheight, color(trans_index));
404 // ### Changed: QRect(0, 0, swidth, sheight)
405 } else if (bgcol>=0) {
406 fillRect(image, 0, 0, swidth, sheight, color(bgcol));
407 // ### Changed: QRect(0, 0, swidth, sheight)
408 }
409 }
410 }
411
412 if (disposal == RestoreImage) {
413 int l = qMin(swidth-1,left);
414 int r = qMin(swidth-1,right);
415 int t = qMin(sheight-1,top);
416 int b = qMin(sheight-1,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(w, h)) {
424 state = Error;
425 return -1;
426 }
427 // We just use the backing store as a byte array
428 backingstore = QImage(qMax(backingstore.width(), w),
429 qMax(backingstore.height(), h),
430 QImage::Format_RGB32);
431 if (backingstore.isNull()) {
432 state = Error;
433 return -1;
434 }
435 memset(backingstore.bits(), 0, 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), 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(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((char*)(hold+1), "NETSCAPE", 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 */
scan(QIODevice * device,QVector<QSize> * imageSizes,int * loopCount)693 void 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(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(readBufferSize));
718
719 if (readBuffer.isEmpty()) {
720 device->seek(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(newWidth,200))
807 imageWidth = -1;
808 if (imageHeight/10 > qMax(newHeight,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((char*)(hold+1), "NETSCAPE", 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(oldPos);
947 return;
948 }
949 }
950 readBuffer = device->read(readBufferSize);
951 }
952 device->seek(oldPos);
953 return;
954 }
955
fillRect(QImage * image,int col,int row,int w,int h,QRgb color)956 void 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
nextY(unsigned char * bits,int bpl)967 void 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(7, 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 (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(3, 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 (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(1, 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 (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
color(uchar index) const1059 inline 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
QGifHandler()1073 QGifHandler::QGifHandler()
1074 {
1075 gifFormat = new QGIFFormat;
1076 nextDelay = 100;
1077 loopCnt = -1;
1078 frameNumber = -1;
1079 scanIsCached = false;
1080 }
1081
~QGifHandler()1082 QGifHandler::~QGifHandler()
1083 {
1084 delete gifFormat;
1085 }
1086
1087 // Does partial decode if necessary, just to see if an image is coming
1088
imageIsComing() const1089 bool QGifHandler::imageIsComing() const
1090 {
1091 const int GifChunkSize = 4096;
1092
1093 while (!gifFormat->partialNewFrame) {
1094 if (buffer.isEmpty()) {
1095 buffer += device()->read(GifChunkSize);
1096 if (buffer.isEmpty())
1097 break;
1098 }
1099
1100 int decoded = gifFormat->decode(&lastImage, (const uchar *)buffer.constData(), buffer.size(),
1101 &nextDelay, &loopCnt);
1102 if (decoded == -1)
1103 break;
1104 buffer.remove(0, decoded);
1105 }
1106 return gifFormat->partialNewFrame;
1107 }
1108
canRead() const1109 bool QGifHandler::canRead() const
1110 {
1111 if (canRead(device()) || imageIsComing()) {
1112 setFormat("gif");
1113 return true;
1114 }
1115
1116 return false;
1117 }
1118
canRead(QIODevice * device)1119 bool QGifHandler::canRead(QIODevice *device)
1120 {
1121 if (!device) {
1122 qWarning("QGifHandler::canRead() called with no device");
1123 return false;
1124 }
1125
1126 char head[6];
1127 if (device->peek(head, sizeof(head)) == sizeof(head))
1128 return qstrncmp(head, "GIF87a", 6) == 0
1129 || qstrncmp(head, "GIF89a", 6) == 0;
1130 return false;
1131 }
1132
read(QImage * image)1133 bool QGifHandler::read(QImage *image)
1134 {
1135 const int GifChunkSize = 4096;
1136
1137 while (!gifFormat->newFrame) {
1138 if (buffer.isEmpty()) {
1139 buffer += device()->read(GifChunkSize);
1140 if (buffer.isEmpty())
1141 break;
1142 }
1143
1144 int decoded = gifFormat->decode(&lastImage, (const uchar *)buffer.constData(), buffer.size(),
1145 &nextDelay, &loopCnt);
1146 if (decoded == -1)
1147 break;
1148 buffer.remove(0, 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
write(const QImage & image)1161 bool QGifHandler::write(const QImage &image)
1162 {
1163 Q_UNUSED(image);
1164 return false;
1165 }
1166
supportsOption(ImageOption option) const1167 bool 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
option(ImageOption option) const1176 QVariant QGifHandler::option(ImageOption option) const
1177 {
1178 if (option == Size) {
1179 if (!scanIsCached) {
1180 QGIFFormat::scan(device(), &imageSizes, &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(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(frameNumber + 1);
1191 } else if (option == Animation) {
1192 return true;
1193 }
1194 return QVariant();
1195 }
1196
setOption(ImageOption option,const QVariant & value)1197 void QGifHandler::setOption(ImageOption option, const QVariant &value)
1198 {
1199 Q_UNUSED(option);
1200 Q_UNUSED(value);
1201 }
1202
nextImageDelay() const1203 int QGifHandler::nextImageDelay() const
1204 {
1205 return nextDelay;
1206 }
1207
imageCount() const1208 int QGifHandler::imageCount() const
1209 {
1210 if (!scanIsCached) {
1211 QGIFFormat::scan(device(), &imageSizes, &loopCnt);
1212 scanIsCached = true;
1213 }
1214 return imageSizes.count();
1215 }
1216
loopCount() const1217 int QGifHandler::loopCount() const
1218 {
1219 if (!scanIsCached) {
1220 QGIFFormat::scan(device(), &imageSizes, &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
currentImageNumber() const1232 int QGifHandler::currentImageNumber() const
1233 {
1234 return frameNumber;
1235 }
1236
1237 QT_END_NAMESPACE
1238