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