src/plugins/imageformats/gif/qgifhandler.cpp
changeset 18 2f34d5167611
parent 3 41300fa6a67c
child 30 5dc02b23752f
equal deleted inserted replaced
3:41300fa6a67c 18:2f34d5167611
     1 /****************************************************************************
     1 /****************************************************************************
     2 **
     2 **
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
     3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
     4 ** All rights reserved.
     4 ** All rights reserved.
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
     6 **
     6 **
     7 ** This file is part of the plugins of the Qt Toolkit.
     7 ** This file is part of the plugins of the Qt Toolkit.
     8 **
     8 **
    52 
    52 
    53 QT_BEGIN_NAMESPACE
    53 QT_BEGIN_NAMESPACE
    54 
    54 
    55 #define Q_TRANSPARENT 0x00ffffff
    55 #define Q_TRANSPARENT 0x00ffffff
    56 
    56 
       
    57 // avoid going through QImage::scanLine() which calls detach
       
    58 #define FAST_SCAN_LINE(bits, bpl, y) (bits + (y) * bpl)
       
    59 
       
    60 
    57 /*
    61 /*
    58   Incremental image decoder for GIF image format.
    62   Incremental image decoder for GIF image format.
    59 
    63 
    60   This subclass of QImageFormat decodes GIF format images,
    64   This subclass of QImageFormat decodes GIF format images,
    61   including animated GIFs. Internally in
    65   including animated GIFs. Internally in
    65 public:
    69 public:
    66     QGIFFormat();
    70     QGIFFormat();
    67     ~QGIFFormat();
    71     ~QGIFFormat();
    68 
    72 
    69     int decode(QImage *image, const uchar* buffer, int length,
    73     int decode(QImage *image, const uchar* buffer, int length,
    70                int *nextFrameDelay, int *loopCount, QSize *nextSize);
    74                int *nextFrameDelay, int *loopCount);
       
    75     static void scan(QIODevice *device, QVector<QSize> *imageSizes, int *loopCount);
    71 
    76 
    72     bool newFrame;
    77     bool newFrame;
    73     bool partialNewFrame;
    78     bool partialNewFrame;
    74 
    79 
    75 private:
    80 private:
   133     bool needfirst;
   138     bool needfirst;
   134     int x, y;
   139     int x, y;
   135     int frame;
   140     int frame;
   136     bool out_of_bounds;
   141     bool out_of_bounds;
   137     bool digress;
   142     bool digress;
   138     void nextY(QImage *image);
   143     void nextY(unsigned char *bits, int bpl);
   139     void disposePrevious(QImage *image);
   144     void disposePrevious(QImage *image);
   140 };
   145 };
   141 
   146 
   142 /*!
   147 /*!
   143     Constructs a QGIFFormat.
   148     Constructs a QGIFFormat.
   223     This function decodes some data into image changes.
   228     This function decodes some data into image changes.
   224 
   229 
   225     Returns the number of bytes consumed.
   230     Returns the number of bytes consumed.
   226 */
   231 */
   227 int QGIFFormat::decode(QImage *image, const uchar *buffer, int length,
   232 int QGIFFormat::decode(QImage *image, const uchar *buffer, int length,
   228                        int *nextFrameDelay, int *loopCount, QSize *nextSize)
   233                        int *nextFrameDelay, int *loopCount)
   229 {
   234 {
   230     // We are required to state that
   235     // We are required to state that
   231     //    "The Graphics Interchange Format(c) is the Copyright property of
   236     //    "The Graphics Interchange Format(c) is the Copyright property of
   232     //    CompuServe Incorporated. GIF(sm) is a Service Mark property of
   237     //    CompuServe Incorporated. GIF(sm) is a Service Mark property of
   233     //    CompuServe Incorporated."
   238     //    CompuServe Incorporated."
       
   239 
       
   240     image->detach();
       
   241     int bpl = image->bytesPerLine();
       
   242     unsigned char *bits = image->bits();
   234 
   243 
   235 #define LM(l, m) (((m)<<8)|l)
   244 #define LM(l, m) (((m)<<8)|l)
   236     digress = false;
   245     digress = false;
   237     const int initial = length;
   246     const int initial = length;
   238     while (!digress && length) {
   247     while (!digress && length) {
   333                     sheight = newtop + newheight;
   342                     sheight = newtop + newheight;
   334 
   343 
   335                 QImage::Format format = trans_index >= 0 ? QImage::Format_ARGB32 : QImage::Format_RGB32;
   344                 QImage::Format format = trans_index >= 0 ? QImage::Format_ARGB32 : QImage::Format_RGB32;
   336                 if (image->isNull()) {
   345                 if (image->isNull()) {
   337                     (*image) = QImage(swidth, sheight, format);
   346                     (*image) = QImage(swidth, sheight, format);
   338                     memset(image->bits(), 0, image->byteCount());
   347                     bpl = image->bytesPerLine();
   339 
   348                     bits = image->bits();
   340                     // ### size of the upcoming frame, should rather
   349                     memset(bits, 0, image->byteCount());
   341                     // be known before decoding it.
       
   342                     *nextSize = QSize(swidth, sheight);
       
   343                 }
   350                 }
   344 
   351 
   345                 disposePrevious(image);
   352                 disposePrevious(image);
   346                 disposed = false;
   353                 disposed = false;
   347 
   354 
   391                         || backingstore.height() < h) {
   398                         || backingstore.height() < h) {
   392                         // We just use the backing store as a byte array
   399                         // We just use the backing store as a byte array
   393                         backingstore = QImage(qMax(backingstore.width(), w),
   400                         backingstore = QImage(qMax(backingstore.width(), w),
   394                                               qMax(backingstore.height(), h),
   401                                               qMax(backingstore.height(), h),
   395                                               QImage::Format_RGB32);
   402                                               QImage::Format_RGB32);
   396                         memset(image->bits(), 0, image->byteCount());
   403                         memset(bits, 0, image->byteCount());
   397                     }
   404                     }
       
   405                     const int dest_bpl = backingstore.bytesPerLine();
       
   406                     unsigned char *dest_data = backingstore.bits();
   398                     for (int ln=0; ln<h; ln++) {
   407                     for (int ln=0; ln<h; ln++) {
   399                         memcpy(backingstore.scanLine(ln),
   408                         memcpy(FAST_SCAN_LINE(dest_data, dest_bpl, ln),
   400                                image->scanLine(t+ln)+l, w*sizeof(QRgb));
   409                                FAST_SCAN_LINE(bits, bpl, t+ln) + l, w*sizeof(QRgb));
   401                     }
   410                     }
   402                 }
   411                 }
   403 
   412 
   404                 count=0;
   413                 count=0;
   405                 if (lcmap) {
   414                 if (lcmap) {
   468                     // Left the block end arrive
   477                     // Left the block end arrive
   469                 } else {
   478                 } else {
   470                     if (needfirst) {
   479                     if (needfirst) {
   471                         firstcode=oldcode=code;
   480                         firstcode=oldcode=code;
   472                         if (!out_of_bounds && image->height() > y && firstcode!=trans_index)
   481                         if (!out_of_bounds && image->height() > y && firstcode!=trans_index)
   473                             ((QRgb*)image->scanLine(y))[x] = color(firstcode);
   482                             ((QRgb*)FAST_SCAN_LINE(bits, bpl, y))[x] = color(firstcode);
   474                         x++;
   483                         x++;
   475                         if (x>=swidth) out_of_bounds = true;
   484                         if (x>=swidth) out_of_bounds = true;
   476                         needfirst=false;
   485                         needfirst=false;
   477                         if (x>=left+width) {
   486                         if (x>=left+width) {
   478                             x=left;
   487                             x=left;
   479                             out_of_bounds = left>=swidth || y>=sheight;
   488                             out_of_bounds = left>=swidth || y>=sheight;
   480                             nextY(image);
   489                             nextY(bits, bpl);
   481                         }
   490                         }
   482                     } else {
   491                     } else {
   483                         incode=code;
   492                         incode=code;
   484                         if (code>=max_code) {
   493                         if (code>=max_code) {
   485                             *sp++=firstcode;
   494                             *sp++=firstcode;
   513                         oldcode=incode;
   522                         oldcode=incode;
   514                         const int h = image->height();
   523                         const int h = image->height();
   515                         const QRgb *map = lcmap ? localcmap : globalcmap;
   524                         const QRgb *map = lcmap ? localcmap : globalcmap;
   516                         QRgb *line = 0;
   525                         QRgb *line = 0;
   517                         if (!out_of_bounds && h > y)
   526                         if (!out_of_bounds && h > y)
   518                             line = (QRgb*)image->scanLine(y);
   527                             line = (QRgb*)FAST_SCAN_LINE(bits, bpl, y);
   519                         while (sp>stack) {
   528                         while (sp>stack) {
   520                             const uchar index = *(--sp);
   529                             const uchar index = *(--sp);
   521                             if (!out_of_bounds && h > y && index!=trans_index) {
   530                             if (!out_of_bounds && h > y && index!=trans_index) {
   522                                 if (index > ncols)
   531                                 if (index > ncols)
   523                                     line[x] = Q_TRANSPARENT;
   532                                     line[x] = Q_TRANSPARENT;
   527                             x++;
   536                             x++;
   528                             if (x>=swidth) out_of_bounds = true;
   537                             if (x>=swidth) out_of_bounds = true;
   529                             if (x>=left+width) {
   538                             if (x>=left+width) {
   530                                 x=left;
   539                                 x=left;
   531                                 out_of_bounds = left>=swidth || y>=sheight;
   540                                 out_of_bounds = left>=swidth || y>=sheight;
   532                                 nextY(image);
   541                                 nextY(bits, bpl);
   533                                 if (!out_of_bounds && h > y)
   542                                 if (!out_of_bounds && h > y)
   534                                     line = (QRgb*)image->scanLine(y);
   543                                     line = (QRgb*)FAST_SCAN_LINE(bits, bpl, y);
   535                             }
   544                             }
   536                         }
   545                         }
   537                     }
   546                     }
   538                 }
   547                 }
   539             }
   548             }
   631         }
   640         }
   632     }
   641     }
   633     return initial-length;
   642     return initial-length;
   634 }
   643 }
   635 
   644 
       
   645 /*!
       
   646    Scans through the data stream defined by \a device and returns the image
       
   647    sizes found in the stream in the \a imageSizes vector.
       
   648 */
       
   649 void QGIFFormat::scan(QIODevice *device, QVector<QSize> *imageSizes, int *loopCount)
       
   650 {
       
   651     if (!device)
       
   652         return;
       
   653 
       
   654     qint64 oldPos = device->pos();
       
   655     if (!device->seek(0))
       
   656         return;
       
   657 
       
   658     int colorCount = 0;
       
   659     int localColorCount = 0;
       
   660     int globalColorCount = 0;
       
   661     int colorReadCount = 0;
       
   662     bool localColormap = false;
       
   663     bool globalColormap = false;
       
   664     int count = 0;
       
   665     int blockSize = 0;
       
   666     int imageWidth = 0;
       
   667     int imageHeight = 0;
       
   668     bool done = false;
       
   669     uchar hold[16];
       
   670     State state = Header;
       
   671 
       
   672     const int readBufferSize = 40960; // 40k read buffer
       
   673     QByteArray readBuffer(device->read(readBufferSize));
       
   674 
       
   675     if (readBuffer.isEmpty()) {
       
   676         device->seek(oldPos);
       
   677         return;
       
   678     }
       
   679 
       
   680     // This is a specialized version of the state machine from decode(),
       
   681     // which doesn't do any image decoding or mallocing, and has an
       
   682     // optimized way of skipping SkipBlocks, ImageDataBlocks and
       
   683     // Global/LocalColorMaps.
       
   684 
       
   685     while (!readBuffer.isEmpty()) {
       
   686         int length = readBuffer.size();
       
   687         const uchar *buffer = (const uchar *) readBuffer.constData();
       
   688         while (!done && length) {
       
   689             length--;
       
   690             uchar ch = *buffer++;
       
   691             switch (state) {
       
   692             case Header:
       
   693                 hold[count++] = ch;
       
   694                 if (count == 6) {
       
   695                     state = LogicalScreenDescriptor;
       
   696                     count = 0;
       
   697                 }
       
   698                 break;
       
   699             case LogicalScreenDescriptor:
       
   700                 hold[count++] = ch;
       
   701                 if (count == 7) {
       
   702                     imageWidth = LM(hold[0], hold[1]);
       
   703                     imageHeight = LM(hold[2], hold[3]);
       
   704                     globalColormap = !!(hold[4] & 0x80);
       
   705                     globalColorCount = 2 << (hold[4] & 0x7);
       
   706                     count = 0;
       
   707                     colorCount = globalColorCount;
       
   708                     if (globalColormap) {
       
   709                         int colorTableSize = 3 * globalColorCount;
       
   710                         if (length >= colorTableSize) {
       
   711                             // skip the global color table in one go
       
   712                             length -= colorTableSize;
       
   713                             buffer += colorTableSize;
       
   714                             state = Introducer;
       
   715                         } else {
       
   716                             colorReadCount = 0;
       
   717                             state = GlobalColorMap;
       
   718                         }
       
   719                     } else {
       
   720                         state=Introducer;
       
   721                     }
       
   722                 }
       
   723                 break;
       
   724             case GlobalColorMap:
       
   725             case LocalColorMap:
       
   726                 hold[count++] = ch;
       
   727                 if (count == 3) {
       
   728                     if (++colorReadCount >= colorCount) {
       
   729                         if (state == LocalColorMap)
       
   730                             state = TableImageLZWSize;
       
   731                         else
       
   732                             state = Introducer;
       
   733                     }
       
   734                     count = 0;
       
   735                 }
       
   736                 break;
       
   737             case Introducer:
       
   738                 hold[count++] = ch;
       
   739                 switch (ch) {
       
   740                 case 0x2c:
       
   741                     state = ImageDescriptor;
       
   742                     break;
       
   743                 case 0x21:
       
   744                     state = ExtensionLabel;
       
   745                     break;
       
   746                 case 0x3b:
       
   747                     state = Done;
       
   748                     break;
       
   749                 default:
       
   750                     done = true;
       
   751                     state = Error;
       
   752                 }
       
   753                 break;
       
   754             case ImageDescriptor:
       
   755                 hold[count++] = ch;
       
   756                 if (count == 10) {
       
   757                     int newLeft = LM(hold[1], hold[2]);
       
   758                     int newTop = LM(hold[3], hold[4]);
       
   759                     int newWidth = LM(hold[5], hold[6]);
       
   760                     int newHeight = LM(hold[7], hold[8]);
       
   761 
       
   762                     if (imageWidth/10 > qMax(newWidth,200))
       
   763                         imageWidth = -1;
       
   764                     if (imageHeight/10 > qMax(newHeight,200))
       
   765                         imageHeight = -1;
       
   766 
       
   767                     if (imageWidth <= 0)
       
   768                         imageWidth = newLeft + newWidth;
       
   769                     if (imageHeight <= 0)
       
   770                         imageHeight = newTop + newHeight;
       
   771 
       
   772                     *imageSizes << QSize(imageWidth, imageHeight);
       
   773 
       
   774                     localColormap = !!(hold[9] & 0x80);
       
   775                     localColorCount = localColormap ? (2 << (hold[9] & 0x7)) : 0;
       
   776                     if (localColorCount)
       
   777                         colorCount = localColorCount;
       
   778                     else
       
   779                         colorCount = globalColorCount;
       
   780 
       
   781                     count = 0;
       
   782                     if (localColormap) {
       
   783                         int colorTableSize = 3 * localColorCount;
       
   784                         if (length >= colorTableSize) {
       
   785                             // skip the local color table in one go
       
   786                             length -= colorTableSize;
       
   787                             buffer += colorTableSize;
       
   788                             state = TableImageLZWSize;
       
   789                         } else {
       
   790                             colorReadCount = 0;
       
   791                             state = LocalColorMap;
       
   792                         }
       
   793                     } else {
       
   794                         state = TableImageLZWSize;
       
   795                     }
       
   796                 }
       
   797                 break;
       
   798             case TableImageLZWSize:
       
   799                 if (ch > max_lzw_bits)
       
   800                     state = Error;
       
   801                 else
       
   802                     state = ImageDataBlockSize;
       
   803                 count = 0;
       
   804                 break;
       
   805             case ImageDataBlockSize:
       
   806                 blockSize = ch;
       
   807                 if (blockSize) {
       
   808                     if (length >= blockSize) {
       
   809                         // we can skip the block in one go
       
   810                         length -= blockSize;
       
   811                         buffer += blockSize;
       
   812                         count = 0;
       
   813                     } else {
       
   814                         state = ImageDataBlock;
       
   815                     }
       
   816                 } else {
       
   817                     state = Introducer;
       
   818                 }
       
   819                 break;
       
   820             case ImageDataBlock:
       
   821                 ++count;
       
   822                 if (count == blockSize) {
       
   823                     count = 0;
       
   824                     state = ImageDataBlockSize;
       
   825                 }
       
   826                 break;
       
   827             case ExtensionLabel:
       
   828                 switch (ch) {
       
   829                 case 0xf9:
       
   830                     state = GraphicControlExtension;
       
   831                     break;
       
   832                 case 0xff:
       
   833                     state = ApplicationExtension;
       
   834                     break;
       
   835                 default:
       
   836                     state = SkipBlockSize;
       
   837                 }
       
   838                 count = 0;
       
   839                 break;
       
   840             case ApplicationExtension:
       
   841                 if (count < 11)
       
   842                     hold[count] = ch;
       
   843                 ++count;
       
   844                 if (count == hold[0] + 1) {
       
   845                     if (qstrncmp((char*)(hold+1), "NETSCAPE", 8) == 0)
       
   846                         state=NetscapeExtensionBlockSize;
       
   847                     else
       
   848                         state=SkipBlockSize;
       
   849                     count = 0;
       
   850                 }
       
   851                 break;
       
   852             case GraphicControlExtension:
       
   853                 if (count < 5)
       
   854                     hold[count] = ch;
       
   855                 ++count;
       
   856                 if (count == hold[0] + 1) {
       
   857                     count = 0;
       
   858                     state = SkipBlockSize;
       
   859                 }
       
   860                 break;
       
   861             case NetscapeExtensionBlockSize:
       
   862                 blockSize = ch;
       
   863                 count = 0;
       
   864                 if (blockSize)
       
   865                     state = NetscapeExtensionBlock;
       
   866                 else
       
   867                     state = Introducer;
       
   868                 break;
       
   869             case NetscapeExtensionBlock:
       
   870                 if (count < 3)
       
   871                     hold[count] = ch;
       
   872                 count++;
       
   873                 if (count == blockSize) {
       
   874                     *loopCount = LM(hold[1], hold[2]);
       
   875                     state = SkipBlockSize;
       
   876                 }
       
   877                 break;
       
   878             case SkipBlockSize:
       
   879                 blockSize = ch;
       
   880                 count = 0;
       
   881                 if (blockSize) {
       
   882                     if (length >= blockSize) {
       
   883                         // we can skip the block in one go
       
   884                         length -= blockSize;
       
   885                         buffer += blockSize;
       
   886                     } else {
       
   887                         state = SkipBlock;
       
   888                     }
       
   889                 } else {
       
   890                     state = Introducer;
       
   891                 }
       
   892                 break;
       
   893             case SkipBlock:
       
   894                 ++count;
       
   895                 if (count == blockSize)
       
   896                     state = SkipBlockSize;
       
   897                 break;
       
   898             case Done:
       
   899                 done = true;
       
   900                 break;
       
   901             case Error:
       
   902                 device->seek(oldPos);
       
   903                 return;
       
   904             }
       
   905         }
       
   906         readBuffer = device->read(readBufferSize);
       
   907     }
       
   908     device->seek(oldPos);
       
   909     return;
       
   910 }
       
   911 
   636 void QGIFFormat::fillRect(QImage *image, int col, int row, int w, int h, QRgb color)
   912 void QGIFFormat::fillRect(QImage *image, int col, int row, int w, int h, QRgb color)
   637 {
   913 {
   638     if (w>0) {
   914     if (w>0) {
   639         for (int j=0; j<h; j++) {
   915         for (int j=0; j<h; j++) {
   640             QRgb *line = (QRgb*)image->scanLine(j+row);
   916             QRgb *line = (QRgb*)image->scanLine(j+row);
   642                 *(line+col+i) = color;
   918                 *(line+col+i) = color;
   643         }
   919         }
   644     }
   920     }
   645 }
   921 }
   646 
   922 
   647 void QGIFFormat::nextY(QImage *image)
   923 void QGIFFormat::nextY(unsigned char *bits, int bpl)
   648 {
   924 {
   649     int my;
   925     int my;
   650     switch (interlace) {
   926     switch (interlace) {
   651     case 0: // Non-interlaced
   927     case 0: // Non-interlaced
   652         // if (!out_of_bounds) {
   928         // if (!out_of_bounds) {
   658         int i;
   934         int i;
   659         my = qMin(7, bottom-y);
   935         my = qMin(7, bottom-y);
   660         // Don't dup with transparency
   936         // Don't dup with transparency
   661         if (trans_index < 0) {
   937         if (trans_index < 0) {
   662             for (i=1; i<=my; i++) {
   938             for (i=1; i<=my; i++) {
   663                 memcpy(image->scanLine(y+i)+left*sizeof(QRgb), image->scanLine(y)+left*sizeof(QRgb),
   939                 memcpy(FAST_SCAN_LINE(bits, bpl, y+i)+left*sizeof(QRgb), FAST_SCAN_LINE(bits, bpl, y)+left*sizeof(QRgb),
   664                        (right-left+1)*sizeof(QRgb));
   940                        (right-left+1)*sizeof(QRgb));
   665             }
   941             }
   666         }
   942         }
   667 
   943 
   668         // if (!out_of_bounds) {
   944         // if (!out_of_bounds) {
   687         int i;
   963         int i;
   688         my = qMin(3, bottom-y);
   964         my = qMin(3, bottom-y);
   689         // Don't dup with transparency
   965         // Don't dup with transparency
   690         if (trans_index < 0) {
   966         if (trans_index < 0) {
   691             for (i=1; i<=my; i++) {
   967             for (i=1; i<=my; i++) {
   692                 memcpy(image->scanLine(y+i)+left*sizeof(QRgb), image->scanLine(y)+left*sizeof(QRgb),
   968                 memcpy(FAST_SCAN_LINE(bits, bpl, y+i)+left*sizeof(QRgb), FAST_SCAN_LINE(bits, bpl, y)+left*sizeof(QRgb),
   693                        (right-left+1)*sizeof(QRgb));
   969                        (right-left+1)*sizeof(QRgb));
   694             }
   970             }
   695         }
   971         }
   696 
   972 
   697         // if (!out_of_bounds) {
   973         // if (!out_of_bounds) {
   711         int i;
   987         int i;
   712         my = qMin(1, bottom-y);
   988         my = qMin(1, bottom-y);
   713         // Don't dup with transparency
   989         // Don't dup with transparency
   714         if (trans_index < 0) {
   990         if (trans_index < 0) {
   715             for (i=1; i<=my; i++) {
   991             for (i=1; i<=my; i++) {
   716                 memcpy(image->scanLine(y+i)+left*sizeof(QRgb), image->scanLine(y)+left*sizeof(QRgb),
   992                 memcpy(FAST_SCAN_LINE(bits, bpl, y+i)+left*sizeof(QRgb), FAST_SCAN_LINE(bits, bpl, y)+left*sizeof(QRgb),
   717                        (right-left+1)*sizeof(QRgb));
   993                        (right-left+1)*sizeof(QRgb));
   718             }
   994             }
   719         }
   995         }
   720         // if (!out_of_bounds) {
   996         // if (!out_of_bounds) {
   721         //     ### Changed: QRect(left, y, right - left + 1, my + 1);
   997         //     ### Changed: QRect(left, y, right - left + 1, my + 1);
   749 
  1025 
   750 QGifHandler::QGifHandler()
  1026 QGifHandler::QGifHandler()
   751 {
  1027 {
   752     gifFormat = new QGIFFormat;
  1028     gifFormat = new QGIFFormat;
   753     nextDelay = 0;
  1029     nextDelay = 0;
   754     loopCnt = 0;
  1030     loopCnt = 1;
   755     frameNumber = -1;
  1031     frameNumber = -1;
   756     nextSize = QSize();
  1032     scanIsCached = false;
   757 }
  1033 }
   758 
  1034 
   759 QGifHandler::~QGifHandler()
  1035 QGifHandler::~QGifHandler()
   760 {
  1036 {
   761     delete gifFormat;
  1037     delete gifFormat;
   773             if (buffer.isEmpty())
  1049             if (buffer.isEmpty())
   774                 break;
  1050                 break;
   775         }
  1051         }
   776 
  1052 
   777         int decoded = gifFormat->decode(&lastImage, (const uchar *)buffer.constData(), buffer.size(),
  1053         int decoded = gifFormat->decode(&lastImage, (const uchar *)buffer.constData(), buffer.size(),
   778                                         &nextDelay, &loopCnt, &nextSize);
  1054                                         &nextDelay, &loopCnt);
   779         if (decoded == -1)
  1055         if (decoded == -1)
   780             break;
  1056             break;
   781         buffer.remove(0, decoded);
  1057         buffer.remove(0, decoded);
   782     }
  1058     }
   783     return gifFormat->partialNewFrame;
  1059     return gifFormat->partialNewFrame;
   817             if (buffer.isEmpty())
  1093             if (buffer.isEmpty())
   818                 break;
  1094                 break;
   819         }
  1095         }
   820 
  1096 
   821         int decoded = gifFormat->decode(&lastImage, (const uchar *)buffer.constData(), buffer.size(),
  1097         int decoded = gifFormat->decode(&lastImage, (const uchar *)buffer.constData(), buffer.size(),
   822                                         &nextDelay, &loopCnt, &nextSize);
  1098                                         &nextDelay, &loopCnt);
   823         if (decoded == -1)
  1099         if (decoded == -1)
   824             break;
  1100             break;
   825         buffer.remove(0, decoded);
  1101         buffer.remove(0, decoded);
   826     }
  1102     }
   827     if (gifFormat->newFrame || (gifFormat->partialNewFrame && device()->atEnd())) {
  1103     if (gifFormat->newFrame || (gifFormat->partialNewFrame && device()->atEnd())) {
   848 }
  1124 }
   849 
  1125 
   850 QVariant QGifHandler::option(ImageOption option) const
  1126 QVariant QGifHandler::option(ImageOption option) const
   851 {
  1127 {
   852     if (option == Size) {
  1128     if (option == Size) {
   853         if (imageIsComing())
  1129         if (!scanIsCached) {
   854             return nextSize;
  1130             QGIFFormat::scan(device(), &imageSizes, &loopCnt);
       
  1131             scanIsCached = true;
       
  1132         }
       
  1133         // before the first frame is read, or we have an empty data stream
       
  1134         if (frameNumber == -1)
       
  1135             return (imageSizes.count() > 0) ? QVariant(imageSizes.at(0)) : QVariant();
       
  1136         // after the last frame has been read, the next size is undefined
       
  1137         if (frameNumber >= imageSizes.count() - 1)
       
  1138             return QVariant();
       
  1139         // and the last case: the size of the next frame
       
  1140         return imageSizes.at(frameNumber + 1);
   855     } else if (option == Animation) {
  1141     } else if (option == Animation) {
   856         return true;
  1142         return true;
   857     }
  1143     }
   858     return QVariant();
  1144     return QVariant();
   859 }
  1145 }
   869     return nextDelay;
  1155     return nextDelay;
   870 }
  1156 }
   871 
  1157 
   872 int QGifHandler::imageCount() const
  1158 int QGifHandler::imageCount() const
   873 {
  1159 {
   874     return 0; // Don't know
  1160     if (!scanIsCached) {
       
  1161         QGIFFormat::scan(device(), &imageSizes, &loopCnt);
       
  1162         scanIsCached = true;
       
  1163     }
       
  1164     return imageSizes.count();
   875 }
  1165 }
   876 
  1166 
   877 int QGifHandler::loopCount() const
  1167 int QGifHandler::loopCount() const
   878 {
  1168 {
       
  1169     if (!scanIsCached) {
       
  1170         QGIFFormat::scan(device(), &imageSizes, &loopCnt);
       
  1171         scanIsCached = true;
       
  1172     }
   879     return loopCnt-1; // In GIF, loop count is iteration count, so subtract one
  1173     return loopCnt-1; // In GIF, loop count is iteration count, so subtract one
   880 }
  1174 }
   881 
  1175 
   882 int QGifHandler::currentImageNumber() const
  1176 int QGifHandler::currentImageNumber() const
   883 {
  1177 {