~ubuntu-branches/ubuntu/wily/qtbase-opensource-src/wily

« back to all changes in this revision

Viewing changes to src/gui/image/qgifhandler.cpp

  • Committer: Package Import Robot
  • Author(s): Timo Jyrinki
  • Date: 2013-02-05 12:46:17 UTC
  • Revision ID: package-import@ubuntu.com-20130205124617-c8jouts182j002fx
Tags: upstream-5.0.1+dfsg
ImportĀ upstreamĀ versionĀ 5.0.1+dfsg

Show diffs side-by-side

added added

removed removed

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