summaryrefslogtreecommitdiffstats
path: root/src/plugins/imageformats/gif/qgifhandler.cpp
diff options
context:
space:
mode:
authorEskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@nokia.com>2010-02-08 08:32:48 (GMT)
committerEskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@nokia.com>2010-02-08 08:32:48 (GMT)
commitaa674169ca31b6e296021b3b8f5a448aeecf0973 (patch)
tree493f0b7922ac116894a3e7f99833e97984035ccc /src/plugins/imageformats/gif/qgifhandler.cpp
parent50a668e62739c715dcd4f001b659ff25f71cc9c4 (diff)
parent941b13d52d975069d10093a873cd3a55bb2fd7dd (diff)
downloadQt-aa674169ca31b6e296021b3b8f5a448aeecf0973.zip
Qt-aa674169ca31b6e296021b3b8f5a448aeecf0973.tar.gz
Qt-aa674169ca31b6e296021b3b8f5a448aeecf0973.tar.bz2
Merge branch '4.6' into qstatictext-4.6
Diffstat (limited to 'src/plugins/imageformats/gif/qgifhandler.cpp')
-rw-r--r--src/plugins/imageformats/gif/qgifhandler.cpp115
1 files changed, 82 insertions, 33 deletions
diff --git a/src/plugins/imageformats/gif/qgifhandler.cpp b/src/plugins/imageformats/gif/qgifhandler.cpp
index 6cd7841..25d3dfa 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<QSize> *imageSizes, int *loopCount);
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<QSize> *imageSizes, int *loopCount)
{
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) {
@@ -825,7 +842,10 @@ int QGIFFormat::imageCount(QIODevice *device)
hold[count] = ch;
++count;
if (count == hold[0] + 1) {
- state = SkipBlockSize;
+ if (qstrncmp((char*)(hold+1), "NETSCAPE", 8) == 0)
+ state=NetscapeExtensionBlockSize;
+ else
+ state=SkipBlockSize;
count = 0;
}
break;
@@ -838,7 +858,23 @@ int QGIFFormat::imageCount(QIODevice *device)
state = SkipBlockSize;
}
break;
- case NetscapeExtensionBlockSize: // fallthrough
+ case NetscapeExtensionBlockSize:
+ blockSize = ch;
+ count = 0;
+ if (blockSize)
+ state = NetscapeExtensionBlock;
+ else
+ state = Introducer;
+ break;
+ case NetscapeExtensionBlock:
+ if (count < 3)
+ hold[count] = ch;
+ count++;
+ if (count == blockSize) {
+ *loopCount = LM(hold[1], hold[2]);
+ state = SkipBlockSize;
+ }
+ break;
case SkipBlockSize:
blockSize = ch;
count = 0;
@@ -854,7 +890,6 @@ int QGIFFormat::imageCount(QIODevice *device)
state = Introducer;
}
break;
- case NetscapeExtensionBlock: // fallthrough
case SkipBlock:
++count;
if (count == blockSize)
@@ -865,13 +900,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)
@@ -992,10 +1027,9 @@ QGifHandler::QGifHandler()
{
gifFormat = new QGIFFormat;
nextDelay = 0;
- loopCnt = 0;
+ loopCnt = 1;
frameNumber = -1;
- nextSize = QSize();
- imageCnt = -1;
+ scanIsCached = false;
}
QGifHandler::~QGifHandler()
@@ -1017,7 +1051,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 +1095,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 +1126,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, &loopCnt);
+ 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,14 +1157,19 @@ 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, &loopCnt);
+ scanIsCached = true;
+ }
+ return imageSizes.count();
}
int QGifHandler::loopCount() const
{
+ if (!scanIsCached) {
+ QGIFFormat::scan(device(), &imageSizes, &loopCnt);
+ scanIsCached = true;
+ }
return loopCnt-1; // In GIF, loop count is iteration count, so subtract one
}