1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the plugins of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file. Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights. These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
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
45 ****************************************************************************/
47 #include "qgifhandler_p.h"
50 #include <qiodevice.h>
55 #define Q_TRANSPARENT 0x00ffffff
57 // avoid going through QImage::scanLine() which calls detach
58 #define FAST_SCAN_LINE(bits, bpl, y) (bits + (y) * bpl)
62 Incremental image decoder for GIF image format.
64 This subclass of QImageFormat decodes GIF format images,
65 including animated GIFs. Internally in
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);
81 void fillRect(QImage *image, int x, int y, int w, int h, QRgb col);
82 inline QRgb color(uchar index) const;
88 unsigned char hold[16];
95 LogicalScreenDescriptor,
104 GraphicControlExtension,
105 ApplicationExtension,
106 NetscapeExtensionBlockSize,
107 NetscapeExtensionBlock,
120 int left, top, right, bottom;
121 enum Disposal { NoDisposal, DoNotChange, RestoreBackground, RestoreImage };
131 enum { max_lzw_bits=12 }; // (poor-compiler's static const int)
133 int code_size, clear_code, end_code, max_code_size, max_code;
134 int firstcode, oldcode, incode;
143 void nextY(unsigned char *bits, int bpl);
144 void disposePrevious(QImage *image);
148 Constructs a QGIFFormat.
150 QGIFFormat::QGIFFormat()
156 disposal = NoDisposal;
157 out_of_bounds = false;
164 partialNewFrame = false;
171 Destroys a QGIFFormat.
173 QGIFFormat::~QGIFFormat()
175 if (globalcmap) delete[] globalcmap;
176 if (localcmap) delete[] localcmap;
180 void QGIFFormat::disposePrevious(QImage *image)
183 // flush anything that survived
184 // ### Changed: QRect(0, 0, swidth, sheight)
187 // Handle disposal of previous image before processing next one
189 if (disposed) return;
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);
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));
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]);
213 // ### Changed: QRect(l, t, r-l+1, b-t+1)
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));
222 // ### Changed: QRect(l, t, r-l+1, b-t+1)
226 disposal = NoDisposal; // Until an extension says otherwise.
232 This function decodes some data into image changes.
234 Returns the number of bytes consumed.
236 int QGIFFormat::decode(QImage *image, const uchar *buffer, int length,
237 int *nextFrameDelay, int *loopCount)
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."
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];
251 int bpl = image->bytesPerLine();
252 unsigned char *bits = image->bits();
254 #define LM(l, m) (((m)<<8)|l)
256 const int initial = length;
257 while (!digress && length) {
259 unsigned char ch=*buffer++;
265 gif89=(hold[3]!='8' || hold[4]!='7');
266 state=LogicalScreenDescriptor;
270 case LogicalScreenDescriptor:
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;
288 state=GlobalColorMap;
289 globalcmap = new QRgb[gncols+1]; // +1 for trans_index
290 globalcmap[gncols] = Q_TRANSPARENT;
296 case GlobalColorMap: case LocalColorMap:
299 QRgb rgb = qRgb(hold[0], hold[1], hold[2]);
300 if (state == LocalColorMap) {
302 localcmap[ccount] = rgb;
304 globalcmap[ccount] = rgb;
306 if (++ccount >= ncols) {
307 if (state == LocalColorMap)
308 state=TableImageLZWSize;
319 state=ImageDescriptor;
322 state=ExtensionLabel;
325 // ### Changed: QRect(0, 0, swidth, sheight)
330 // Unexpected Introducer - ignore block
334 case ImageDescriptor:
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]);
342 // disbelieve ridiculous logical screen sizes,
343 // unless the image frames are also large.
344 if (swidth/10 > qMax(newwidth,200))
346 if (sheight/10 > qMax(newheight,200))
350 swidth = newleft + newwidth;
352 sheight = newtop + newheight;
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());
362 disposePrevious(image);
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;
379 localcmap = new QRgb[lncols+1];
380 localcmap[lncols] = Q_TRANSPARENT;
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)
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);
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());
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));
428 state=TableImageLZWSize;
435 firstcode = oldcode = 0;
437 out_of_bounds = left>=swidth || y>=sheight;
440 case TableImageLZWSize: {
442 if (lzwsize > max_lzw_bits) {
446 clear_code=1<<lzwsize;
447 end_code=clear_code+1;
448 max_code_size=2*clear_code;
449 max_code=clear_code+2;
451 for (i=0; i<clear_code; i++) {
455 state=ImageDataBlockSize;
459 } case ImageDataBlockSize:
462 state=ImageDataBlock;
471 accum|=(ch<<bitcount);
473 while (bitcount>=code_size && state==ImageDataBlock) {
474 int code=accum&((1<<code_size)-1);
478 if (code==clear_code) {
481 max_code_size=2*clear_code;
482 max_code=clear_code+2;
485 } else if (code==end_code) {
487 // Left the block end arrive
490 firstcode=oldcode=code;
491 if (!out_of_bounds && image->height() > y && firstcode!=trans_index)
492 ((QRgb*)FAST_SCAN_LINE(bits, bpl, y))[x] = color(firstcode);
494 if (x>=swidth) out_of_bounds = true;
498 out_of_bounds = left>=swidth || y>=sheight;
503 if (code>=max_code) {
507 while (code>=clear_code+2) {
508 if (code >= max_code) {
512 *sp++=table[1][code];
513 if (code==table[0][code]) {
517 if (sp-stack>=(1<<(max_lzw_bits))*2) {
528 *sp++=firstcode=table[1][code];
530 if (code<(1<<max_lzw_bits)) {
531 table[0][code]=oldcode;
532 table[1][code]=firstcode;
534 if ((max_code>=max_code_size)
535 && (max_code_size<(1<<max_lzw_bits)))
542 const int h = image->height();
543 const QRgb *map = lcmap ? localcmap : globalcmap;
545 if (!out_of_bounds && h > y)
546 line = (QRgb*)FAST_SCAN_LINE(bits, bpl, y);
548 const uchar index = *(--sp);
549 if (!out_of_bounds && h > y && index!=trans_index) {
551 line[x] = Q_TRANSPARENT;
553 line[x] = map ? map[index] : 0;
556 if (x>=swidth) out_of_bounds = true;
559 out_of_bounds = left>=swidth || y>=sheight;
561 if (!out_of_bounds && h > y)
562 line = (QRgb*)FAST_SCAN_LINE(bits, bpl, y);
568 partialNewFrame = true;
569 if (count==expectcount) {
571 state=ImageDataBlockSize;
577 state=GraphicControlExtension;
580 state=ApplicationExtension;
584 state=CommentExtension;
594 case ApplicationExtension:
595 if (count<11) hold[count]=ch;
597 if (count==hold[0]+1) {
598 if (qstrncmp((char*)(hold+1), "NETSCAPE", 8)==0) {
600 state=NetscapeExtensionBlockSize;
607 case NetscapeExtensionBlockSize:
610 if (expectcount) state=NetscapeExtensionBlock;
611 else state=Introducer;
613 case NetscapeExtensionBlock:
614 if (count<3) hold[count]=ch;
616 if (count==expectcount) {
617 *loopCount = hold[1]+hold[2]*256;
618 state=SkipBlockSize; // Ignore further blocks
621 case GraphicControlExtension:
622 if (count<5) hold[count]=ch;
624 if (count==hold[0]+1) {
625 disposePrevious(image);
626 disposal=Disposal((hold[1]>>2)&0x7);
627 //UNUSED: waitforuser=!!((hold[1]>>1)&0x1);
628 int delay=count>3 ? LM(hold[2], hold[3]) : 1;
629 // IE and mozilla use a minimum delay of 10. With the minimum delay of 10
630 // we are compatible to them and avoid huge loads on the app and xserver.
631 *nextFrameDelay = (delay < 2 ? 10 : delay) * 10;
633 bool havetrans=hold[1]&0x1;
634 trans_index = havetrans ? hold[4] : -1;
643 if (expectcount) state=SkipBlock;
644 else state=Introducer;
648 if (count==expectcount) state=SkipBlockSize;
652 /* Netscape ignores the junk, so we do too.
654 state=Error; // More calls to this is an error
658 return -1; // Called again after done.
661 return initial-length;
665 Scans through the data stream defined by \a device and returns the image
666 sizes found in the stream in the \a imageSizes vector.
668 void QGIFFormat::scan(QIODevice *device, QVector<QSize> *imageSizes, int *loopCount)
673 qint64 oldPos = device->pos();
674 if (!device->seek(0))
678 int localColorCount = 0;
679 int globalColorCount = 0;
680 int colorReadCount = 0;
681 bool localColormap = false;
682 bool globalColormap = false;
689 State state = Header;
691 const int readBufferSize = 40960; // 40k read buffer
692 QByteArray readBuffer(device->read(readBufferSize));
694 if (readBuffer.isEmpty()) {
695 device->seek(oldPos);
699 // This is a specialized version of the state machine from decode(),
700 // which doesn't do any image decoding or mallocing, and has an
701 // optimized way of skipping SkipBlocks, ImageDataBlocks and
702 // Global/LocalColorMaps.
704 while (!readBuffer.isEmpty()) {
705 int length = readBuffer.size();
706 const uchar *buffer = (const uchar *) readBuffer.constData();
707 while (!done && length) {
709 uchar ch = *buffer++;
714 state = LogicalScreenDescriptor;
718 case LogicalScreenDescriptor:
721 imageWidth = LM(hold[0], hold[1]);
722 imageHeight = LM(hold[2], hold[3]);
723 globalColormap = !!(hold[4] & 0x80);
724 globalColorCount = 2 << (hold[4] & 0x7);
726 colorCount = globalColorCount;
727 if (globalColormap) {
728 int colorTableSize = 3 * globalColorCount;
729 if (length >= colorTableSize) {
730 // skip the global color table in one go
731 length -= colorTableSize;
732 buffer += colorTableSize;
736 state = GlobalColorMap;
747 if (++colorReadCount >= colorCount) {
748 if (state == LocalColorMap)
749 state = TableImageLZWSize;
760 state = ImageDescriptor;
763 state = ExtensionLabel;
773 case ImageDescriptor:
776 int newLeft = LM(hold[1], hold[2]);
777 int newTop = LM(hold[3], hold[4]);
778 int newWidth = LM(hold[5], hold[6]);
779 int newHeight = LM(hold[7], hold[8]);
781 if (imageWidth/10 > qMax(newWidth,200))
783 if (imageHeight/10 > qMax(newHeight,200))
787 imageWidth = newLeft + newWidth;
788 if (imageHeight <= 0)
789 imageHeight = newTop + newHeight;
791 *imageSizes << QSize(imageWidth, imageHeight);
793 localColormap = !!(hold[9] & 0x80);
794 localColorCount = localColormap ? (2 << (hold[9] & 0x7)) : 0;
796 colorCount = localColorCount;
798 colorCount = globalColorCount;
802 int colorTableSize = 3 * localColorCount;
803 if (length >= colorTableSize) {
804 // skip the local color table in one go
805 length -= colorTableSize;
806 buffer += colorTableSize;
807 state = TableImageLZWSize;
810 state = LocalColorMap;
813 state = TableImageLZWSize;
817 case TableImageLZWSize:
818 if (ch > max_lzw_bits)
821 state = ImageDataBlockSize;
824 case ImageDataBlockSize:
827 if (length >= blockSize) {
828 // we can skip the block in one go
833 state = ImageDataBlock;
841 if (count == blockSize) {
843 state = ImageDataBlockSize;
849 state = GraphicControlExtension;
852 state = ApplicationExtension;
855 state = SkipBlockSize;
859 case ApplicationExtension:
863 if (count == hold[0] + 1) {
864 if (qstrncmp((char*)(hold+1), "NETSCAPE", 8) == 0)
865 state=NetscapeExtensionBlockSize;
871 case GraphicControlExtension:
875 if (count == hold[0] + 1) {
877 state = SkipBlockSize;
880 case NetscapeExtensionBlockSize:
884 state = NetscapeExtensionBlock;
888 case NetscapeExtensionBlock:
892 if (count == blockSize) {
893 *loopCount = LM(hold[1], hold[2]);
894 state = SkipBlockSize;
901 if (length >= blockSize) {
902 // we can skip the block in one go
914 if (count == blockSize)
915 state = SkipBlockSize;
921 device->seek(oldPos);
925 readBuffer = device->read(readBufferSize);
927 device->seek(oldPos);
931 void QGIFFormat::fillRect(QImage *image, int col, int row, int w, int h, QRgb color)
934 for (int j=0; j<h; j++) {
935 QRgb *line = (QRgb*)image->scanLine(j+row);
936 for (int i=0; i<w; i++)
937 *(line+col+i) = color;
942 void QGIFFormat::nextY(unsigned char *bits, int bpl)
946 case 0: // Non-interlaced
947 // if (!out_of_bounds) {
948 // ### Changed: QRect(left, y, right - left + 1, 1);
954 my = qMin(7, bottom-y);
955 // Don't dup with transparency
956 if (trans_index < 0) {
957 for (i=1; i<=my; i++) {
958 memcpy(FAST_SCAN_LINE(bits, bpl, y+i)+left*sizeof(QRgb), FAST_SCAN_LINE(bits, bpl, y)+left*sizeof(QRgb),
959 (right-left+1)*sizeof(QRgb));
963 // if (!out_of_bounds) {
964 // ### Changed: QRect(left, y, right - left + 1, my + 1);
966 // if (!out_of_bounds)
967 // qDebug("consumer->changed(QRect(%d, %d, %d, %d))", left, y, right-left+1, my+1);
970 interlace++; y=top+4;
971 if (y > bottom) { // for really broken GIFs with bottom < 5
974 if (y > bottom) { // for really broken GIF with bottom < 3
983 my = qMin(3, bottom-y);
984 // Don't dup with transparency
985 if (trans_index < 0) {
986 for (i=1; i<=my; i++) {
987 memcpy(FAST_SCAN_LINE(bits, bpl, y+i)+left*sizeof(QRgb), FAST_SCAN_LINE(bits, bpl, y)+left*sizeof(QRgb),
988 (right-left+1)*sizeof(QRgb));
992 // if (!out_of_bounds) {
993 // ### Changed: QRect(left, y, right - left + 1, my + 1);
997 interlace++; y=top+2;
998 // handle broken GIF with bottom < 3
1007 my = qMin(1, bottom-y);
1008 // Don't dup with transparency
1009 if (trans_index < 0) {
1010 for (i=1; i<=my; i++) {
1011 memcpy(FAST_SCAN_LINE(bits, bpl, y+i)+left*sizeof(QRgb), FAST_SCAN_LINE(bits, bpl, y)+left*sizeof(QRgb),
1012 (right-left+1)*sizeof(QRgb));
1015 // if (!out_of_bounds) {
1016 // ### Changed: QRect(left, y, right - left + 1, my + 1);
1019 if (y>bottom) { interlace++; y=top+1; }
1022 // if (!out_of_bounds) {
1023 // ### Changed: QRect(left, y, right - left + 1, 1);
1028 // Consume bogus extra lines
1029 if (y >= sheight) out_of_bounds=true; //y=bottom;
1032 inline QRgb QGIFFormat::color(uchar index) const
1034 if (index == trans_index || index > ncols)
1035 return Q_TRANSPARENT;
1037 QRgb *map = lcmap ? localcmap : globalcmap;
1038 return map ? map[index] : 0;
1041 //-------------------------------------------------------------------------
1042 //-------------------------------------------------------------------------
1043 //-------------------------------------------------------------------------
1045 QGifHandler::QGifHandler()
1047 gifFormat = new QGIFFormat;
1051 scanIsCached = false;
1054 QGifHandler::~QGifHandler()
1059 // Does partial decode if necessary, just to see if an image is coming
1061 bool QGifHandler::imageIsComing() const
1063 const int GifChunkSize = 4096;
1065 while (!gifFormat->partialNewFrame) {
1066 if (buffer.isEmpty()) {
1067 buffer += device()->read(GifChunkSize);
1068 if (buffer.isEmpty())
1072 int decoded = gifFormat->decode(&lastImage, (const uchar *)buffer.constData(), buffer.size(),
1073 &nextDelay, &loopCnt);
1076 buffer.remove(0, decoded);
1078 return gifFormat->partialNewFrame;
1081 bool QGifHandler::canRead() const
1083 if (canRead(device()) || imageIsComing()) {
1091 bool QGifHandler::canRead(QIODevice *device)
1094 qWarning("QGifHandler::canRead() called with no device");
1099 if (device->peek(head, sizeof(head)) == sizeof(head))
1100 return qstrncmp(head, "GIF87a", 6) == 0
1101 || qstrncmp(head, "GIF89a", 6) == 0;
1105 bool QGifHandler::read(QImage *image)
1107 const int GifChunkSize = 4096;
1109 while (!gifFormat->newFrame) {
1110 if (buffer.isEmpty()) {
1111 buffer += device()->read(GifChunkSize);
1112 if (buffer.isEmpty())
1116 int decoded = gifFormat->decode(&lastImage, (const uchar *)buffer.constData(), buffer.size(),
1117 &nextDelay, &loopCnt);
1120 buffer.remove(0, decoded);
1122 if (gifFormat->newFrame || (gifFormat->partialNewFrame && device()->atEnd())) {
1125 gifFormat->newFrame = false;
1126 gifFormat->partialNewFrame = false;
1133 bool QGifHandler::write(const QImage &image)
1139 bool QGifHandler::supportsOption(ImageOption option) const
1141 if (!device() || device()->isSequential())
1142 return option == Animation;
1144 return option == Size
1145 || option == Animation;
1148 QVariant QGifHandler::option(ImageOption option) const
1150 if (option == Size) {
1151 if (!scanIsCached) {
1152 QGIFFormat::scan(device(), &imageSizes, &loopCnt);
1153 scanIsCached = true;
1155 // before the first frame is read, or we have an empty data stream
1156 if (frameNumber == -1)
1157 return (imageSizes.count() > 0) ? QVariant(imageSizes.at(0)) : QVariant();
1158 // after the last frame has been read, the next size is undefined
1159 if (frameNumber >= imageSizes.count() - 1)
1161 // and the last case: the size of the next frame
1162 return imageSizes.at(frameNumber + 1);
1163 } else if (option == Animation) {
1169 void QGifHandler::setOption(ImageOption option, const QVariant &value)
1175 int QGifHandler::nextImageDelay() const
1180 int QGifHandler::imageCount() const
1182 if (!scanIsCached) {
1183 QGIFFormat::scan(device(), &imageSizes, &loopCnt);
1184 scanIsCached = true;
1186 return imageSizes.count();
1189 int QGifHandler::loopCount() const
1191 if (!scanIsCached) {
1192 QGIFFormat::scan(device(), &imageSizes, &loopCnt);
1193 scanIsCached = true;
1198 else if (loopCnt == -1)
1204 int QGifHandler::currentImageNumber() const
1209 QByteArray QGifHandler::name() const