From 6d1d425e219b0a5e03603f7d708cd740b7d3a3f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Trond=20Kjern=C3=A5sen?= Date: Thu, 4 Feb 2010 11:44:44 +0100 Subject: Cache the sizes of the images in an animated GIF. Rework the previous commit a bit and include caching of image sizes. Task-number: QTBUG-6696 Reviewed-by: Kim --- src/plugins/imageformats/gif/qgifhandler.cpp | 85 ++++++++++++++++++---------- src/plugins/imageformats/gif/qgifhandler.h | 4 +- tests/auto/qimagereader/tst_qimagereader.cpp | 1 + 3 files changed, 59 insertions(+), 31 deletions(-) diff --git a/src/plugins/imageformats/gif/qgifhandler.cpp b/src/plugins/imageformats/gif/qgifhandler.cpp index 6cd7841..6d473e3 100644 --- a/src/plugins/imageformats/gif/qgifhandler.cpp +++ b/src/plugins/imageformats/gif/qgifhandler.cpp @@ -71,8 +71,8 @@ public: ~QGIFFormat(); int decode(QImage *image, const uchar* buffer, int length, - int *nextFrameDelay, int *loopCount, QSize *nextSize); - static int imageCount(QIODevice *device); + int *nextFrameDelay, int *loopCount); + static void scan(QIODevice *device, QVector *imageSizes); bool newFrame; bool partialNewFrame; @@ -230,7 +230,7 @@ void QGIFFormat::disposePrevious(QImage *image) Returns the number of bytes consumed. */ int QGIFFormat::decode(QImage *image, const uchar *buffer, int length, - int *nextFrameDelay, int *loopCount, QSize *nextSize) + int *nextFrameDelay, int *loopCount) { // We are required to state that // "The Graphics Interchange Format(c) is the Copyright property of @@ -347,10 +347,6 @@ int QGIFFormat::decode(QImage *image, const uchar *buffer, int length, bpl = image->bytesPerLine(); bits = image->bits(); memset(bits, 0, image->byteCount()); - - // ### size of the upcoming frame, should rather - // be known before decoding it. - *nextSize = QSize(swidth, sheight); } disposePrevious(image); @@ -647,17 +643,17 @@ int QGIFFormat::decode(QImage *image, const uchar *buffer, int length, } /*! - Returns the number of images that can be read from \a device. + Scans through the data stream defined by \a device and returns the image + sizes found in the stream in the \a imageSizes vector. */ - -int QGIFFormat::imageCount(QIODevice *device) +void QGIFFormat::scan(QIODevice *device, QVector *imageSizes) { if (!device) - return 0; + return; qint64 oldPos = device->pos(); if (!device->seek(0)) - return 0; + return; int colorCount = 0; int localColorCount = 0; @@ -667,18 +663,21 @@ int QGIFFormat::imageCount(QIODevice *device) bool globalColormap = false; int count = 0; int blockSize = 0; + int imageWidth = 0; + int imageHeight = 0; bool done = false; uchar hold[16]; - int imageCount = 0; State state = Header; const int readBufferSize = 40960; // 40k read buffer QByteArray readBuffer(device->read(readBufferSize)); - if (readBuffer.isEmpty()) - return 0; + if (readBuffer.isEmpty()) { + device->seek(oldPos); + return; + } - // this is a specialized version of the state machine from decode(), + // This is a specialized version of the state machine from decode(), // which doesn't do any image decoding or mallocing, and has an // optimized way of skipping SkipBlocks, ImageDataBlocks and // Global/LocalColorMaps. @@ -700,6 +699,8 @@ int QGIFFormat::imageCount(QIODevice *device) case LogicalScreenDescriptor: hold[count++] = ch; if (count == 7) { + imageWidth = LM(hold[0], hold[1]); + imageHeight = LM(hold[2], hold[3]); globalColormap = !!(hold[4] & 0x80); globalColorCount = 2 << (hold[4] & 0x7); count = 0; @@ -753,13 +754,29 @@ int QGIFFormat::imageCount(QIODevice *device) case ImageDescriptor: hold[count++] = ch; if (count == 10) { + int newLeft = LM(hold[1], hold[2]); + int newTop = LM(hold[3], hold[4]); + int newWidth = LM(hold[5], hold[6]); + int newHeight = LM(hold[7], hold[8]); + + if (imageWidth/10 > qMax(newWidth,200)) + imageWidth = -1; + if (imageHeight/10 > qMax(newHeight,200)) + imageHeight = -1; + + if (imageWidth <= 0) + imageWidth = newLeft + newWidth; + if (imageHeight <= 0) + imageHeight = newTop + newHeight; + + *imageSizes << QSize(imageWidth, imageHeight); + localColormap = !!(hold[9] & 0x80); localColorCount = localColormap ? (2 << (hold[9] & 0x7)) : 0; if (localColorCount) colorCount = localColorCount; else colorCount = globalColorCount; - imageCount++; count = 0; if (localColormap) { @@ -865,13 +882,13 @@ int QGIFFormat::imageCount(QIODevice *device) break; case Error: device->seek(oldPos); - return 0; + return; } } readBuffer = device->read(readBufferSize); } device->seek(oldPos); - return imageCount; + return; } void QGIFFormat::fillRect(QImage *image, int col, int row, int w, int h, QRgb color) @@ -994,8 +1011,7 @@ QGifHandler::QGifHandler() nextDelay = 0; loopCnt = 0; frameNumber = -1; - nextSize = QSize(); - imageCnt = -1; + scanIsCached = false; } QGifHandler::~QGifHandler() @@ -1017,7 +1033,7 @@ bool QGifHandler::imageIsComing() const } int decoded = gifFormat->decode(&lastImage, (const uchar *)buffer.constData(), buffer.size(), - &nextDelay, &loopCnt, &nextSize); + &nextDelay, &loopCnt); if (decoded == -1) break; buffer.remove(0, decoded); @@ -1061,7 +1077,7 @@ bool QGifHandler::read(QImage *image) } int decoded = gifFormat->decode(&lastImage, (const uchar *)buffer.constData(), buffer.size(), - &nextDelay, &loopCnt, &nextSize); + &nextDelay, &loopCnt); if (decoded == -1) break; buffer.remove(0, decoded); @@ -1092,8 +1108,18 @@ bool QGifHandler::supportsOption(ImageOption option) const QVariant QGifHandler::option(ImageOption option) const { if (option == Size) { - if (imageIsComing()) - return nextSize; + if (!scanIsCached) { + QGIFFormat::scan(device(), &imageSizes); + scanIsCached = true; + } + // before the first frame is read, or we have an empty data stream + if (frameNumber == -1) + return (imageSizes.count() > 0) ? QVariant(imageSizes.at(0)) : QVariant(); + // after the last frame has been read, the next size is undefined + if (frameNumber >= imageSizes.count() - 1) + return QVariant(); + // and the last case: the size of the next frame + return imageSizes.at(frameNumber + 1); } else if (option == Animation) { return true; } @@ -1113,10 +1139,11 @@ int QGifHandler::nextImageDelay() const int QGifHandler::imageCount() const { - if (imageCnt != -1) - return imageCnt; - imageCnt = QGIFFormat::imageCount(device()); - return imageCnt; + if (!scanIsCached) { + QGIFFormat::scan(device(), &imageSizes); + scanIsCached = true; + } + return imageSizes.count(); } int QGifHandler::loopCount() const diff --git a/src/plugins/imageformats/gif/qgifhandler.h b/src/plugins/imageformats/gif/qgifhandler.h index 830cd38..8e07aff 100644 --- a/src/plugins/imageformats/gif/qgifhandler.h +++ b/src/plugins/imageformats/gif/qgifhandler.h @@ -87,8 +87,8 @@ private: mutable int nextDelay; mutable int loopCnt; int frameNumber; - mutable QSize nextSize; - mutable int imageCnt; + mutable QVector imageSizes; + mutable bool scanIsCached; }; QT_END_NAMESPACE diff --git a/tests/auto/qimagereader/tst_qimagereader.cpp b/tests/auto/qimagereader/tst_qimagereader.cpp index debc090..3e40527 100644 --- a/tests/auto/qimagereader/tst_qimagereader.cpp +++ b/tests/auto/qimagereader/tst_qimagereader.cpp @@ -881,6 +881,7 @@ void tst_QImageReader::gifImageCount() { QImageReader io(":images/trolltech.gif"); QVERIFY(io.imageCount() == 34); + QVERIFY(io.size() == QSize(128,64)); } } #endif -- cgit v0.12