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