Coverage Report

Created: 2026-02-26 07:48

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
11.3k
#define Q_TRANSPARENT 0x00ffffff
17
18
// avoid going through QImage::scanLine() which calls detach
19
2.32M
#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
1.41k
    {
45
1.41k
        return quint64(width) * height < 16384 * 16384; // Reject unreasonable header values
46
1.41k
    }
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
2.08k
{
116
2.08k
    globalcmap = nullptr;
117
2.08k
    localcmap = nullptr;
118
2.08k
    lncols = 0;
119
2.08k
    gncols = 0;
120
2.08k
    disposal = NoDisposal;
121
2.08k
    out_of_bounds = false;
122
2.08k
    disposed = true;
123
2.08k
    frame = -1;
124
2.08k
    state = Header;
125
2.08k
    count = 0;
126
2.08k
    lcmap = false;
127
2.08k
    newFrame = false;
128
2.08k
    partialNewFrame = false;
129
2.08k
    table[0] = nullptr;
130
2.08k
    table[1] = nullptr;
131
2.08k
    stack = nullptr;
132
2.08k
}
133
134
/*!
135
    Destroys a QGIFFormat.
136
*/
137
QGIFFormat::~QGIFFormat()
138
2.08k
{
139
2.08k
    if (globalcmap) delete[] globalcmap;
140
2.08k
    if (localcmap) delete[] localcmap;
141
2.08k
    delete [] stack;
142
2.08k
}
143
144
void QGIFFormat::disposePrevious(QImage *image)
145
2.71k
{
146
2.71k
    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
2.71k
    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
36.1M
{
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
36.1M
    if (!stack) {
209
2.08k
        stack = new short[(1 << max_lzw_bits) * 4];
210
2.08k
        table[0] = &stack[(1 << max_lzw_bits) * 2];
211
2.08k
        table[1] = &stack[(1 << max_lzw_bits) * 3];
212
2.08k
    }
213
214
36.1M
    image->detach();
215
36.1M
    qsizetype bpl = image->bytesPerLine();
216
36.1M
    unsigned char *bits = image->bits();
217
218
36.1M
#define LM(l, m) (((m)<<8)|l)
219
36.1M
    digress = false;
220
36.1M
    const int initial = length;
221
79.7M
    while (!digress && length) {
222
43.6M
        length--;
223
43.6M
        unsigned char ch=*buffer++;
224
43.6M
        switch (state) {
225
12.5k
          case Header:
226
12.5k
            hold[count++]=ch;
227
12.5k
            if (count==6) {
228
                // Header
229
2.08k
                gif89=(hold[3]!='8' || hold[4]!='7');
230
2.08k
                state=LogicalScreenDescriptor;
231
2.08k
                count=0;
232
2.08k
            }
233
12.5k
            break;
234
14.5k
          case LogicalScreenDescriptor:
235
14.5k
            hold[count++]=ch;
236
14.5k
            if (count==7) {
237
                // Logical Screen Descriptor
238
2.06k
                swidth=LM(hold[0], hold[1]);
239
2.06k
                sheight=LM(hold[2], hold[3]);
240
2.06k
                gcmap=!!(hold[4]&0x80);
241
                //UNUSED: bpchan=(((hold[4]&0x70)>>3)+1);
242
                //UNUSED: gcmsortflag=!!(hold[4]&0x08);
243
2.06k
                gncols=2<<(hold[4]&0x7);
244
2.06k
                bgcol=(gcmap) ? hold[5] : -1;
245
                //aspect=hold[6] ? double(hold[6]+15)/64.0 : 1.0;
246
247
2.06k
                trans_index = -1;
248
2.06k
                count=0;
249
2.06k
                ncols=gncols;
250
2.06k
                if (gcmap) {
251
376
                    ccount=0;
252
376
                    state=GlobalColorMap;
253
376
                    globalcmap = new QRgb[gncols+1]; // +1 for trans_index
254
376
                    globalcmap[gncols] = Q_TRANSPARENT;
255
1.69k
                } else {
256
1.69k
                    state=Introducer;
257
1.69k
                }
258
2.06k
            }
259
14.5k
            break;
260
164k
          case GlobalColorMap: case LocalColorMap:
261
164k
            hold[count++]=ch;
262
164k
            if (count==3) {
263
54.9k
                QRgb rgb = qRgb(hold[0], hold[1], hold[2]);
264
54.9k
                if (state == LocalColorMap) {
265
14.1k
                    if (ccount < lncols)
266
14.1k
                        localcmap[ccount] =  rgb;
267
40.7k
                } else {
268
40.7k
                    globalcmap[ccount] = rgb;
269
40.7k
                }
270
54.9k
                if (++ccount >= ncols) {
271
435
                    if (state == LocalColorMap)
272
149
                        state=TableImageLZWSize;
273
286
                    else
274
286
                        state=Introducer;
275
435
                }
276
54.9k
                count=0;
277
54.9k
            }
278
164k
            break;
279
4.53k
          case Introducer:
280
4.53k
            hold[count++]=ch;
281
4.53k
            switch (ch) {
282
1.33k
              case ',':
283
1.33k
                state=ImageDescriptor;
284
1.33k
                break;
285
2.83k
              case '!':
286
2.83k
                state=ExtensionLabel;
287
2.83k
                break;
288
247
              case ';':
289
                  // ### Changed: QRect(0, 0, swidth, sheight)
290
247
                state=Done;
291
247
                break;
292
122
              default:
293
122
                digress=true;
294
                // Unexpected Introducer - ignore block
295
122
                state=Error;
296
4.53k
            }
297
4.53k
            break;
298
11.8k
          case ImageDescriptor:
299
11.8k
            hold[count++]=ch;
300
11.8k
            if (count==10) {
301
1.31k
                int newleft=LM(hold[1], hold[2]);
302
1.31k
                int newtop=LM(hold[3], hold[4]);
303
1.31k
                int newwidth=LM(hold[5], hold[6]);
304
1.31k
                int newheight=LM(hold[7], hold[8]);
305
306
                // disbelieve ridiculous logical screen sizes,
307
                // unless the image frames are also large.
308
1.31k
                if (swidth/10 > qMax(newwidth,16384))
309
0
                    swidth = -1;
310
1.31k
                if (sheight/10 > qMax(newheight,16384))
311
0
                    sheight = -1;
312
313
1.31k
                if (swidth <= 0)
314
133
                    swidth = newleft + newwidth;
315
1.31k
                if (sheight <= 0)
316
164
                    sheight = newtop + newheight;
317
318
1.31k
                QImage::Format format = trans_index >= 0 ? QImage::Format_ARGB32 : QImage::Format_RGB32;
319
1.31k
                if (image->isNull()) {
320
1.31k
                    if (!withinSizeLimit(swidth, sheight)) {
321
18
                        state = Error;
322
18
                        return -1;
323
18
                    }
324
1.29k
                    if (!QImageIOHandler::allocateImage(QSize(swidth, sheight), format, image)) {
325
33
                        state = Error;
326
33
                        return -1;
327
33
                    }
328
1.26k
                    bpl = image->bytesPerLine();
329
1.26k
                    bits = image->bits();
330
1.26k
                    if (bits)
331
1.26k
                        memset(bits, 0, image->sizeInBytes());
332
1.26k
                }
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
1.26k
                if (image->isNull()) {
337
0
                    state = Error;
338
0
                    return -1;
339
0
                }
340
341
1.26k
                disposePrevious(image);
342
1.26k
                disposed = false;
343
344
1.26k
                left = newleft;
345
1.26k
                top = newtop;
346
1.26k
                width = newwidth;
347
1.26k
                height = newheight;
348
349
1.26k
                right=qMax(0, qMin(left+width, swidth)-1);
350
1.26k
                bottom=qMax(0, qMin(top+height, sheight)-1);
351
1.26k
                lcmap=!!(hold[9]&0x80);
352
1.26k
                interlace=!!(hold[9]&0x40);
353
                //bool lcmsortflag=!!(hold[9]&0x20);
354
1.26k
                lncols=lcmap ? (2<<(hold[9]&0x7)) : 0;
355
1.26k
                if (lncols) {
356
302
                    if (localcmap)
357
0
                        delete [] localcmap;
358
302
                    localcmap = new QRgb[lncols+1];
359
302
                    localcmap[lncols] = Q_TRANSPARENT;
360
302
                    ncols = lncols;
361
958
                } else {
362
958
                    ncols = gncols;
363
958
                }
364
1.26k
                frame++;
365
1.26k
                if (frame == 0) {
366
1.26k
                    if (left || top || width<swidth || height<sheight) {
367
                        // Not full-size image - erase with bg or transparent
368
1.21k
                        if (trans_index >= 0) {
369
118
                            fillRect(image, 0, 0, swidth, sheight, color(trans_index));
370
                            // ### Changed: QRect(0, 0, swidth, sheight)
371
1.09k
                        } else if (bgcol>=0) {
372
79
                            fillRect(image, 0, 0, swidth, sheight, color(bgcol));
373
                            // ### Changed: QRect(0, 0, swidth, sheight)
374
79
                        }
375
1.21k
                    }
376
1.26k
                }
377
378
1.26k
                if (disposal == RestoreImage) {
379
106
                    int l = qMin(swidth-1,left);
380
106
                    int r = qMin(swidth-1,right);
381
106
                    int t = qMin(sheight-1,top);
382
106
                    int b = qMin(sheight-1,bottom);
383
106
                    int w = r-l+1;
384
106
                    int h = b-t+1;
385
386
106
                    if (backingstore.width() < w
387
100
                        || backingstore.height() < h) {
388
389
100
                        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
100
                        QSize bsSize(qMax(backingstore.width(), w), qMax(backingstore.height(), h));
395
100
                        if (!QImageIOHandler::allocateImage(bsSize, QImage::Format_RGB32,
396
100
                                                            &backingstore)) {
397
19
                            state = Error;
398
19
                            return -1;
399
19
                        }
400
81
                        memset(backingstore.bits(), 0, backingstore.sizeInBytes());
401
81
                    }
402
87
                    const qsizetype dest_bpl = backingstore.bytesPerLine();
403
87
                    unsigned char *dest_data = backingstore.bits();
404
1.04M
                    for (int ln=0; ln<h; ln++) {
405
1.04M
                        memcpy(FAST_SCAN_LINE(dest_data, dest_bpl, ln),
406
1.04M
                               FAST_SCAN_LINE(bits, bpl, t+ln) + l*sizeof(QRgb), w*sizeof(QRgb));
407
1.04M
                    }
408
87
                }
409
410
1.24k
                count=0;
411
1.24k
                if (lcmap) {
412
300
                    ccount=0;
413
300
                    state=LocalColorMap;
414
941
                } else {
415
941
                    state=TableImageLZWSize;
416
941
                }
417
1.24k
                x = left;
418
1.24k
                y = top;
419
1.24k
                accum = 0;
420
1.24k
                bitcount = 0;
421
1.24k
                sp = stack;
422
1.24k
                firstcode = oldcode = 0;
423
1.24k
                needfirst = true;
424
1.24k
                out_of_bounds = left>=swidth || y>=sheight;
425
1.24k
            }
426
11.7k
            break;
427
11.7k
          case TableImageLZWSize: {
428
1.05k
            lzwsize=ch;
429
1.05k
            if (lzwsize > max_lzw_bits) {
430
119
                state=Error;
431
940
            } else {
432
940
                code_size=lzwsize+1;
433
940
                clear_code=1<<lzwsize;
434
940
                end_code=clear_code+1;
435
940
                max_code_size=2*clear_code;
436
940
                max_code=clear_code+2;
437
940
                int i;
438
360k
                for (i=0; i<clear_code; i++) {
439
359k
                    table[0][i]=0;
440
359k
                    table[1][i]=i;
441
359k
                }
442
940
                state=ImageDataBlockSize;
443
940
            }
444
1.05k
            count=0;
445
1.05k
            break;
446
97.6k
          } case ImageDataBlockSize:
447
97.6k
            expectcount=ch;
448
97.6k
            if (expectcount) {
449
97.5k
                state=ImageDataBlock;
450
97.5k
            } else {
451
91
                state=Introducer;
452
91
                digress = true;
453
91
                newFrame = true;
454
91
            }
455
97.6k
            break;
456
3.70M
          case ImageDataBlock:
457
3.70M
            count++;
458
3.70M
            if (bitcount != -32768) {
459
636k
                if (bitcount < 0 || bitcount > 31) {
460
0
                    state = Error;
461
0
                    return -1;
462
0
                }
463
636k
                accum |= (ch << bitcount);
464
636k
                bitcount += 8;
465
636k
            }
466
4.16M
            while (bitcount>=code_size && state==ImageDataBlock) {
467
462k
                int code=accum&((1<<code_size)-1);
468
462k
                bitcount-=code_size;
469
462k
                accum>>=code_size;
470
471
462k
                if (code==clear_code) {
472
18.3k
                    if (!needfirst) {
473
7.77k
                        code_size=lzwsize+1;
474
7.77k
                        max_code_size=2*clear_code;
475
7.77k
                        max_code=clear_code+2;
476
7.77k
                    }
477
18.3k
                    needfirst=true;
478
443k
                } else if (code==end_code) {
479
266
                    bitcount = -32768;
480
                    // Left the block end arrive
481
443k
                } else {
482
443k
                    if (needfirst) {
483
8.62k
                        firstcode=oldcode=code;
484
8.62k
                        if (!out_of_bounds && image->height() > y && ((frame == 0) || (firstcode != trans_index)))
485
5.48k
                            ((QRgb*)FAST_SCAN_LINE(bits, bpl, y))[x] = color(firstcode);
486
8.62k
                        x++;
487
8.62k
                        if (x>=swidth) out_of_bounds = true;
488
8.62k
                        needfirst=false;
489
8.62k
                        if (x>=left+width) {
490
3.86k
                            x=left;
491
3.86k
                            out_of_bounds = left>=swidth || y>=sheight;
492
3.86k
                            nextY(bits, bpl);
493
3.86k
                        }
494
434k
                    } else {
495
434k
                        incode=code;
496
434k
                        if (code>=max_code) {
497
6.08k
                            *sp++=firstcode;
498
6.08k
                            code=oldcode;
499
6.08k
                        }
500
695k
                        while (code>=clear_code+2) {
501
261k
                            if (code >= max_code) {
502
256
                                state = Error;
503
256
                                return -1;
504
256
                            }
505
260k
                            *sp++=table[1][code];
506
260k
                            if (code==table[0][code]) {
507
31
                                state=Error;
508
31
                                return -1;
509
31
                            }
510
260k
                            if (sp-stack>=(1<<(max_lzw_bits))*2) {
511
0
                                state=Error;
512
0
                                return -1;
513
0
                            }
514
260k
                            code=table[0][code];
515
260k
                        }
516
434k
                        if (code < 0) {
517
0
                            state = Error;
518
0
                            return -1;
519
0
                        }
520
521
434k
                        *sp++=firstcode=table[1][code];
522
434k
                        code=max_code;
523
434k
                        if (code<(1<<max_lzw_bits)) {
524
112k
                            table[0][code]=oldcode;
525
112k
                            table[1][code]=firstcode;
526
112k
                            max_code++;
527
112k
                            if ((max_code>=max_code_size)
528
6.17k
                             && (max_code_size<(1<<max_lzw_bits)))
529
6.14k
                            {
530
6.14k
                                max_code_size*=2;
531
6.14k
                                code_size++;
532
6.14k
                            }
533
112k
                        }
534
434k
                        oldcode=incode;
535
434k
                        const int h = image->height();
536
434k
                        QRgb *line = nullptr;
537
434k
                        if (!out_of_bounds && h > y)
538
52.6k
                            line = (QRgb*)FAST_SCAN_LINE(bits, bpl, y);
539
1.13M
                        while (sp>stack) {
540
701k
                            const uchar index = *(--sp);
541
701k
                            if (!out_of_bounds && h > y && ((frame == 0) || (index != trans_index))) {
542
90.7k
                                line[x] = color(index);
543
90.7k
                            }
544
701k
                            x++;
545
701k
                            if (x>=swidth) out_of_bounds = true;
546
701k
                            if (x>=left+width) {
547
670k
                                x=left;
548
670k
                                out_of_bounds = left>=swidth || y>=sheight;
549
670k
                                nextY(bits, bpl);
550
670k
                                if (!out_of_bounds && h > y)
551
80.8k
                                    line = (QRgb*)FAST_SCAN_LINE(bits, bpl, y);
552
670k
                            }
553
701k
                        }
554
434k
                    }
555
443k
                }
556
462k
            }
557
3.70M
            partialNewFrame = true;
558
3.70M
            if (count==expectcount) {
559
96.7k
                count=0;
560
96.7k
                state=ImageDataBlockSize;
561
96.7k
            }
562
3.70M
            break;
563
2.83k
          case ExtensionLabel:
564
2.83k
            switch (ch) {
565
1.48k
            case 0xf9:
566
1.48k
                state=GraphicControlExtension;
567
1.48k
                break;
568
916
            case 0xff:
569
916
                state=ApplicationExtension;
570
916
                break;
571
#if 0
572
            case 0xfe:
573
                state=CommentExtension;
574
                break;
575
            case 0x01:
576
                break;
577
#endif
578
430
            default:
579
430
                state=SkipBlockSize;
580
2.83k
            }
581
2.83k
            count=0;
582
2.83k
            break;
583
15.0k
          case ApplicationExtension:
584
15.0k
            if (count<11) hold[count]=ch;
585
15.0k
            count++;
586
15.0k
            if (count==hold[0]+1) {
587
871
                if (qstrncmp((char*)(hold+1), "NETSCAPE", 8)==0) {
588
                    // Looping extension
589
487
                    state=NetscapeExtensionBlockSize;
590
487
                } else {
591
384
                    state=SkipBlockSize;
592
384
                }
593
871
                count=0;
594
871
            }
595
15.0k
            break;
596
486
          case NetscapeExtensionBlockSize:
597
486
            expectcount=ch;
598
486
            count=0;
599
486
            if (expectcount) state=NetscapeExtensionBlock;
600
7
            else state=Introducer;
601
486
            break;
602
2.75k
          case NetscapeExtensionBlock:
603
2.75k
            if (count<3) hold[count]=ch;
604
2.75k
            count++;
605
2.75k
            if (count==expectcount) {
606
466
                *loopCount = hold[1]+hold[2]*256;
607
466
                state=SkipBlockSize; // Ignore further blocks
608
466
            }
609
2.75k
            break;
610
8.11k
          case GraphicControlExtension:
611
8.11k
            if (count<5) hold[count]=ch;
612
8.11k
            count++;
613
8.11k
            if (count==hold[0]+1) {
614
1.45k
                disposePrevious(image);
615
1.45k
                uint dBits = (hold[1] >> 2) & 0x7;
616
1.45k
                disposal = (dBits <= RestoreImage) ? Disposal(dBits) : NoDisposal;
617
                //UNUSED: waitforuser=!!((hold[1]>>1)&0x1);
618
1.45k
                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
1.45k
                *nextFrameDelay = (delay < 2 ? 10 : delay) * 10;
622
623
1.45k
                bool havetrans=hold[1]&0x1;
624
1.45k
                trans_index = havetrans ? hold[4] : -1;
625
626
1.45k
                count=0;
627
1.45k
                state=SkipBlockSize;
628
1.45k
            }
629
8.11k
            break;
630
36.4k
          case SkipBlockSize:
631
36.4k
            expectcount=ch;
632
36.4k
            count=0;
633
36.4k
            if (expectcount) state=SkipBlock;
634
2.56k
            else state=Introducer;
635
36.4k
            break;
636
3.48M
          case SkipBlock:
637
3.48M
            count++;
638
3.48M
            if (count==expectcount) state=SkipBlockSize;
639
3.48M
            break;
640
36.1M
          case Done:
641
36.1M
            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
36.1M
            break;
647
219
          case Error:
648
219
            return -1; // Called again after done.
649
43.6M
        }
650
43.6M
    }
651
36.1M
    return initial-length;
652
36.1M
}
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
197
{
923
197
    if (w>0) {
924
4.43M
        for (int j=0; j<h; j++) {
925
4.43M
            QRgb *line = (QRgb*)image->scanLine(j+row);
926
4.98G
            for (int i=0; i<w; i++)
927
4.97G
                *(line+col+i) = color;
928
4.43M
        }
929
197
    }
930
197
}
931
932
void QGIFFormat::nextY(unsigned char *bits, int bpl)
933
674k
{
934
674k
    if (out_of_bounds)
935
591k
        return;
936
83.3k
    int my;
937
83.3k
    switch (interlace) {
938
71.3k
    case 0: // Non-interlaced
939
        // if (!out_of_bounds) {
940
        //     ### Changed: QRect(left, y, right - left + 1, 1);
941
        // }
942
71.3k
        y++;
943
71.3k
        break;
944
5.38k
    case 1: {
945
5.38k
        int i;
946
5.38k
        my = qMin(7, bottom-y);
947
        // Don't dup with transparency
948
5.38k
        if (trans_index < 0) {
949
39.9k
            for (i=1; i<=my; i++) {
950
34.8k
                memcpy(FAST_SCAN_LINE(bits, bpl, y+i)+left*sizeof(QRgb), FAST_SCAN_LINE(bits, bpl, y)+left*sizeof(QRgb),
951
34.8k
                       (right-left+1)*sizeof(QRgb));
952
34.8k
            }
953
5.07k
        }
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
5.38k
        y+=8;
961
5.38k
        if (y>bottom) {
962
173
            interlace++; y=top+4;
963
173
            if (y > bottom) { // for really broken GIFs with bottom < 5
964
52
                interlace=2;
965
52
                y = top + 2;
966
52
                if (y > bottom) { // for really broken GIF with bottom < 3
967
40
                    interlace = 0;
968
40
                    y = top + 1;
969
40
                }
970
52
            }
971
173
        }
972
5.38k
    } break;
973
2.53k
    case 2: {
974
2.53k
        int i;
975
2.53k
        my = qMin(3, bottom-y);
976
        // Don't dup with transparency
977
2.53k
        if (trans_index < 0) {
978
9.99k
            for (i=1; i<=my; i++) {
979
7.46k
                memcpy(FAST_SCAN_LINE(bits, bpl, y+i)+left*sizeof(QRgb), FAST_SCAN_LINE(bits, bpl, y)+left*sizeof(QRgb),
980
7.46k
                       (right-left+1)*sizeof(QRgb));
981
7.46k
            }
982
2.53k
        }
983
984
        // if (!out_of_bounds) {
985
        //     ### Changed: QRect(left, y, right - left + 1, my + 1);
986
        // }
987
2.53k
        y+=8;
988
2.53k
        if (y>bottom) {
989
109
            interlace++; y=top+2;
990
            // handle broken GIF with bottom < 3
991
109
            if (y > bottom) {
992
0
                interlace = 3;
993
0
                y = top + 1;
994
0
            }
995
109
        }
996
2.53k
    } break;
997
2.41k
    case 3: {
998
2.41k
        int i;
999
2.41k
        my = qMin(1, bottom-y);
1000
        // Don't dup with transparency
1001
2.41k
        if (trans_index < 0) {
1002
4.79k
            for (i=1; i<=my; i++) {
1003
2.38k
                memcpy(FAST_SCAN_LINE(bits, bpl, y+i)+left*sizeof(QRgb), FAST_SCAN_LINE(bits, bpl, y)+left*sizeof(QRgb),
1004
2.38k
                       (right-left+1)*sizeof(QRgb));
1005
2.38k
            }
1006
2.41k
        }
1007
        // if (!out_of_bounds) {
1008
        //     ### Changed: QRect(left, y, right - left + 1, my + 1);
1009
        // }
1010
2.41k
        y+=4;
1011
2.41k
        if (y>bottom) { interlace++; y=top+1; }
1012
2.41k
    } break;
1013
1.73k
    case 4:
1014
        // if (!out_of_bounds) {
1015
        //     ### Changed: QRect(left, y, right - left + 1, 1);
1016
        // }
1017
1.73k
        y+=2;
1018
83.3k
    }
1019
1020
    // Consume bogus extra lines
1021
83.3k
    if (y >= sheight) out_of_bounds=true; //y=bottom;
1022
83.3k
}
1023
1024
inline QRgb QGIFFormat::color(uchar index) const
1025
96.4k
{
1026
96.4k
    if (index > ncols)
1027
8.82k
        return Q_TRANSPARENT;
1028
1029
87.6k
    QRgb *map = lcmap ? localcmap : globalcmap;
1030
87.6k
    QRgb col = map ? map[index] : 0;
1031
87.6k
    return index == trans_index ? col & Q_TRANSPARENT : col;
1032
96.4k
}
1033
1034
//-------------------------------------------------------------------------
1035
//-------------------------------------------------------------------------
1036
//-------------------------------------------------------------------------
1037
1038
QGifHandler::QGifHandler()
1039
2.08k
{
1040
2.08k
    gifFormat = new QGIFFormat;
1041
2.08k
    nextDelay = 100;
1042
2.08k
    loopCnt = -1;
1043
2.08k
    frameNumber = -1;
1044
2.08k
    scanIsCached = false;
1045
2.08k
}
1046
1047
QGifHandler::~QGifHandler()
1048
2.08k
{
1049
2.08k
    delete gifFormat;
1050
2.08k
}
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
1.26k
{
1076
1.26k
    if (canRead(device()) || imageIsComing()) {
1077
1.26k
        setFormat("gif");
1078
1.26k
        return true;
1079
1.26k
    }
1080
1081
0
    return false;
1082
1.26k
}
1083
1084
bool QGifHandler::canRead(QIODevice *device)
1085
101k
{
1086
101k
    if (!device) {
1087
0
        qCWarning(lcGif, "QGifHandler::canRead() called with no device");
1088
0
        return false;
1089
0
    }
1090
1091
101k
    char head[6];
1092
101k
    if (device->peek(head, sizeof(head)) == sizeof(head))
1093
81.5k
        return qstrncmp(head, "GIF87a", 6) == 0
1094
80.0k
            || qstrncmp(head, "GIF89a", 6) == 0;
1095
19.7k
    return false;
1096
101k
}
1097
1098
bool QGifHandler::read(QImage *image)
1099
2.08k
{
1100
2.08k
    const int GifChunkSize = 4096;
1101
1102
36.1M
    while (!gifFormat->newFrame) {
1103
36.1M
        if (buffer.isEmpty()) {
1104
13.9k
            buffer += device()->read(GifChunkSize);
1105
13.9k
            if (buffer.isEmpty())
1106
1.41k
                break;
1107
13.9k
        }
1108
1109
36.1M
        int decoded = gifFormat->decode(&lastImage, (const uchar *)buffer.constData(), buffer.size(),
1110
36.1M
                                        &nextDelay, &loopCnt);
1111
36.1M
        if (decoded == -1)
1112
576
            break;
1113
36.1M
        buffer.remove(0, decoded);
1114
36.1M
    }
1115
2.08k
    if (gifFormat->newFrame || (gifFormat->partialNewFrame && device()->atEnd())) {
1116
901
        *image = lastImage;
1117
901
        ++frameNumber;
1118
901
        gifFormat->newFrame = false;
1119
901
        gifFormat->partialNewFrame = false;
1120
901
        return true;
1121
901
    }
1122
1123
1.18k
    return false;
1124
2.08k
}
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
8.83k
{
1134
8.83k
    if (!device() || device()->isSequential())
1135
0
        return option == Animation;
1136
8.83k
    else
1137
8.83k
        return option == Size
1138
8.83k
            || option == Animation;
1139
8.83k
}
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