Coverage Report

Created: 2026-08-11 07:29

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