diff options
Diffstat (limited to 'src/kernel/qasyncimageio.cpp')
-rw-r--r-- | src/kernel/qasyncimageio.cpp | 1309 |
1 files changed, 1309 insertions, 0 deletions
diff --git a/src/kernel/qasyncimageio.cpp b/src/kernel/qasyncimageio.cpp new file mode 100644 index 0000000..8ecd1eb --- /dev/null +++ b/src/kernel/qasyncimageio.cpp @@ -0,0 +1,1309 @@ +/**************************************************************************** +** +** Implementation of asynchronous image/movie loading classes +** +** Created : 970617 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the kernel module of the Qt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free Qt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing requirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.QPL +** included in the packaging of this file. Licensees holding valid Qt +** Commercial licenses may use this file in accordance with the Qt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "qasyncimageio.h" + +#ifndef QT_NO_ASYNC_IMAGE_IO + +#include "qptrlist.h" +#include "qgif.h" +#include <stdlib.h> + +extern void qt_init_image_handlers(); +extern void qt_init_image_plugins(); + +#define Q_TRANSPARENT 0x00ffffff + +/*! + \class QImageConsumer qasyncimageio.h + \brief The QImageConsumer class is an abstraction used by QImageDecoder. + + \ingroup images + \ingroup graphics + \ingroup multimedia + + The QMovie class, or QLabel::setMovie(), are easy to use and for + most situations do what you want with regards animated images. + + A QImageConsumer consumes information about changes to the QImage + maintained by a QImageDecoder. Think of the QImage as the model or + source of the image data, with the QImageConsumer as a view of + that data and the QImageDecoder being the controller that + orchestrates the relationship between the model and the view. + + You'd use the QImageConsumer class, for example, if you were + implementing a web browser with your own image loaders. + + \sa QImageDecoder +*/ + +/*! + \fn void QImageConsumer::changed(const QRect&) + + Called when the given area of the image has changed. +*/ + +/*! + \fn void QImageConsumer::end() + + Called when all the data from all the frames has been decoded and + revealed as changed(). +*/ + +/*! + \fn void QImageConsumer::frameDone() + + One of the two frameDone() functions will be called when a frame + of an animated image has ended and been revealed as changed(). + + When this function is called, the current image should be + displayed. + + The decoder will not make any further changes to the image until + the next call to QImageFormat::decode(). +*/ + +/*! + \overload void QImageConsumer::frameDone( const QPoint& offset, const QRect& rect ) + + One of the two frameDone() functions will be called when a frame + of an animated image has ended and been revealed as changed(). + + When this function is called, the area \a rect in the current + image should be moved by \a offset and displayed. + + The decoder will not make any further changes to the image until + the next call to QImageFormat::decode(). +*/ + +/*! + \fn void QImageConsumer::setLooping(int n) + + Called to indicate that the sequence of frames in the image + should be repeated \a n times, including the sequence during + decoding. + + \list + \i 0 = Forever + \i 1 = Only display frames the first time through + \i 2 = Repeat once after first pass through images + \i etc. + \endlist + + To make the QImageDecoder do this, just delete it and pass the + information to it again for decoding (setLooping() will be called + again, of course, but that can be ignored), or keep copies of the + changed areas at the ends of frames. +*/ + +/*! + \fn void QImageConsumer::setFramePeriod(int milliseconds) + + Notes that the frame about to be decoded should not be displayed + until the given number of \a milliseconds after the time that this + function is called. Of course, the image may not have been + decoded by then, in which case the frame should not be displayed + until it is complete. A value of -1 (the assumed default) + indicates that the image should be displayed even while it is only + partially loaded. +*/ + +/*! + \fn void QImageConsumer::setSize(int, int) + + This function is called as soon as the size of the image has been + determined. +*/ + + +/*! + \class QImageDecoder qasyncimageio.h + \brief The QImageDecoder class is an incremental image decoder for all supported image formats. + + \ingroup images + \ingroup graphics + \ingroup multimedia + + New formats are installed by creating objects of class + QImageFormatType; the QMovie class can be used for all installed + incremental image formats. QImageDecoder is only useful for + creating new ways of feeding data to an QImageConsumer. + + A QImageDecoder is a machine that decodes images. It takes encoded + image data via its decode() method and expresses its decoding by + supplying information to a QImageConsumer. It implements its + decoding by using a QImageFormat created by one of the + currently-existing QImageFormatType factory objects. + + QImageFormatType and QImageFormat are the classes that you might + need to implement support for additional image formats. + + \legalese + + Qt supports GIF reading if it is configured that way during + installation (see qgif.h). If it is, we are required to state that + "The Graphics Interchange Format(c) is the Copyright property of + CompuServe Incorporated. GIF(sm) is a Service Mark property of + CompuServe Incorporated." + + \warning If you are in a country that recognizes software patents + and in which Unisys holds a patent on LZW compression and/or + decompression and you want to use GIF, Unisys may require you to + license that technology. Such countries include Canada, Japan, + the USA, France, Germany, Italy and the UK. + + GIF support may be removed completely in a future version of Qt. + We recommend using the MNG or PNG format. +*/ + +static const int max_header = 32; + + + + + +// See qgif.h for important information regarding this option +#if defined(QT_BUILTIN_GIF_READER) && QT_BUILTIN_GIF_READER == 1 +class QGIFFormat : public QImageFormat { +public: + QGIFFormat(); + virtual ~QGIFFormat(); + + int decode(QImage& img, QImageConsumer* consumer, + const uchar* buffer, int length); + +private: + void fillRect(QImage&, int x, int y, int w, int h, QRgb col); + QRgb color( uchar index ) const; + + // GIF specific stuff + QRgb* globalcmap; + QRgb* localcmap; + QImage backingstore; + unsigned char hold[16]; + bool gif89; + int count; + int ccount; + int expectcount; + enum State { + Header, + LogicalScreenDescriptor, + GlobalColorMap, + LocalColorMap, + Introducer, + ImageDescriptor, + TableImageLZWSize, + ImageDataBlockSize, + ImageDataBlock, + ExtensionLabel, + GraphicControlExtension, + ApplicationExtension, + NetscapeExtensionBlockSize, + NetscapeExtensionBlock, + SkipBlockSize, + SkipBlock, + Done, + Error + } state; + int gncols; + int lncols; + int ncols; + int lzwsize; + bool lcmap; + int swidth, sheight; + int width, height; + int left, top, right, bottom; + enum Disposal { NoDisposal, DoNotChange, RestoreBackground, RestoreImage }; + Disposal disposal; + bool disposed; + int trans_index; + bool gcmap; + int bgcol; + int interlace; + int accum; + int bitcount; + + enum { max_lzw_bits=12 }; // (poor-compiler's static const int) + + int code_size, clear_code, end_code, max_code_size, max_code; + int firstcode, oldcode, incode; + short table[2][1<< max_lzw_bits]; + short stack[(1<<(max_lzw_bits))*2]; + short *sp; + bool needfirst; + int x, y; + int frame; + bool out_of_bounds; + bool digress; + void nextY(QImage& img, QImageConsumer* consumer); + void disposePrevious( QImage& img, QImageConsumer* consumer ); +}; + +class QGIFFormatType : public QImageFormatType +{ + QImageFormat* decoderFor(const uchar* buffer, int length); + const char* formatName() const; +}; + +#endif + + +class QImageDecoderPrivate +{ +public: + QImageDecoderPrivate() + { + count = 0; + } + + static void cleanup(); + + static void ensureFactories() + { + if ( !factories ) { + factories = new QPtrList<QImageFormatType>; +// See qgif.h for important information regarding this option +#if defined(QT_BUILTIN_GIF_READER) && QT_BUILTIN_GIF_READER == 1 + gif_decoder_factory = new QGIFFormatType; +#endif + qt_init_image_handlers(); + qAddPostRoutine( cleanup ); + } + } + + static QPtrList<QImageFormatType> * factories; + +// See qgif.h for important information regarding this option +#if defined(QT_BUILTIN_GIF_READER) && QT_BUILTIN_GIF_READER == 1 + static QGIFFormatType * gif_decoder_factory; +#endif + + uchar header[max_header]; + int count; +}; + +QPtrList<QImageFormatType> * QImageDecoderPrivate::factories = 0; +// See qgif.h for important information regarding this option +#if defined(QT_BUILTIN_GIF_READER) && QT_BUILTIN_GIF_READER == 1 +QGIFFormatType * QImageDecoderPrivate::gif_decoder_factory = 0; +#endif + + +void QImageDecoderPrivate::cleanup() +{ + delete factories; + factories = 0; +// See qgif.h for important information regarding this option +#if defined(QT_BUILTIN_GIF_READER) && QT_BUILTIN_GIF_READER == 1 + delete gif_decoder_factory; + gif_decoder_factory = 0; +#endif +} + + +/*! + Constructs a QImageDecoder that will send change information to + the QImageConsumer \a c. +*/ +QImageDecoder::QImageDecoder(QImageConsumer* c) +{ + qt_init_image_handlers(); + d = new QImageDecoderPrivate; + Q_CHECK_PTR(d); + consumer = c; + actual_decoder = 0; +} + +/*! + Destroys a QImageDecoder. The image it built is destroyed. The + decoder built by the factory for the file format is destroyed. The + consumer for which it decoded the image is \e not destroyed. +*/ +QImageDecoder::~QImageDecoder() +{ + delete d; + delete actual_decoder; +} + +/*! + \fn const QImage& QImageDecoder::image() + + Returns the image currently being decoded. +*/ + +static bool plugins_loaded = FALSE; + +/*! + Call this function to decode some data into image changes. The + data in \a buffer will be decoded, sending change information to + the QImageConsumer of this QImageDecoder until one of the change + functions of the consumer returns FALSE. The length of the data is + given in \a length. + + Returns the number of bytes consumed: 0 if consumption is + complete, and -1 if decoding fails due to invalid data. +*/ +int QImageDecoder::decode(const uchar* buffer, int length) +{ + if (!actual_decoder) { + int i=0; + + while (i < length && d->count < max_header) + d->header[d->count++] = buffer[i++]; + + QImageDecoderPrivate::ensureFactories(); + + for (QImageFormatType* f = QImageDecoderPrivate::factories->first(); + f && !actual_decoder; + f = QImageDecoderPrivate::factories->next()) + { + actual_decoder = f->decoderFor(d->header, d->count); + } + if ( !actual_decoder && !plugins_loaded) { + qt_init_image_plugins(); + plugins_loaded = TRUE; + + for (QImageFormatType* f = QImageDecoderPrivate::factories->first(); + f && !actual_decoder; + f = QImageDecoderPrivate::factories->next()) + { + actual_decoder = f->decoderFor(d->header, d->count); + } + } + + if (!actual_decoder) { + if ( d->count < max_header ) { + // not enough info yet + return i; + } else { + // failure - nothing matches max_header bytes + return -1; + } + } + } + return actual_decoder->decode(img, consumer, buffer, length); +} + +/*! + Returns a QImageFormatType by name. This might be used when the + user needs to force data to be interpreted as being in a certain + format. \a name is one of the formats listed by + QImageDecoder::inputFormats(). Note that you will still need to + supply decodable data to result->decoderFor() before you can begin + decoding the data. +*/ +QImageFormatType* QImageDecoder::format( const char* name ) +{ + QImageDecoderPrivate::ensureFactories(); + qt_init_image_plugins(); + + for (QImageFormatType* f = QImageDecoderPrivate::factories->first(); + f; + f = QImageDecoderPrivate::factories->next()) + { + if ( qstricmp(name,f->formatName())==0 ) + return f; + } + return 0; +} + +/*! + Call this function to find the name of the format of the given + header. The returned string is statically allocated. The function + will look at the first \a length characters in the \a buffer. + + Returns 0 if the format is not recognized. +*/ +const char* QImageDecoder::formatName(const uchar* buffer, int length) +{ + QImageDecoderPrivate::ensureFactories(); + + const char* name = 0; + for (QImageFormatType* f = QImageDecoderPrivate::factories->first(); + f && !name; + f = QImageDecoderPrivate::factories->next()) + { + QImageFormat *decoder = f->decoderFor(buffer, length); + if (decoder) { + name = f->formatName(); + delete decoder; + } + } + if ( !name && !plugins_loaded) { + qt_init_image_plugins(); + plugins_loaded = TRUE; + for (QImageFormatType* f = QImageDecoderPrivate::factories->first(); + f && !name; + f = QImageDecoderPrivate::factories->next()) + { + QImageFormat *decoder = f->decoderFor(buffer, length); + if (decoder) { + name = f->formatName(); + delete decoder; + } + } + } + + return name; +} + +/*! + Returns a sorted list of formats for which asynchronous loading is + supported. +*/ +QStrList QImageDecoder::inputFormats() +{ + QImageDecoderPrivate::ensureFactories(); + qt_init_image_plugins(); + + QStrList result; + + for (QImageFormatType* f = QImageDecoderPrivate::factories->first(); + f; + f = QImageDecoderPrivate::factories->next()) + { + if ( !result.contains( f->formatName() ) ) { + result.inSort( f->formatName() ); + } + } + + return result; +} + +/*! + Registers the new QImageFormatType \a f. This is not needed in + application code because factories call this themselves. +*/ +void QImageDecoder::registerDecoderFactory(QImageFormatType* f) +{ + QImageDecoderPrivate::ensureFactories(); + + QImageDecoderPrivate::factories->insert(0,f); +} + +/*! + Unregisters the QImageFormatType \a f. This is not needed in + application code because factories call this themselves. +*/ +void QImageDecoder::unregisterDecoderFactory(QImageFormatType* f) +{ + if ( !QImageDecoderPrivate::factories ) + return; + + QImageDecoderPrivate::factories->remove(f); +} + +/*! + \class QImageFormat qasyncimageio.h + \brief The QImageFormat class is an incremental image decoder for a specific image format. + + \ingroup images + \ingroup graphics + \ingroup multimedia + + By making a derived class of QImageFormatType, which in turn + creates objects that are a subclass of QImageFormat, you can add + support for more incremental image formats, allowing such formats + to be sources for a QMovie or for the first frame of the image + stream to be loaded as a QImage or QPixmap. + + Your new subclass must reimplement the decode() function in order + to process your new format. + + New QImageFormat objects are generated by new QImageFormatType + factories. +*/ + +/*! + Destroys the object. + + \internal + More importantly, destroys derived classes. +*/ +QImageFormat::~QImageFormat() +{ +} + +/*! + \fn int QImageFormat::decode(QImage& img, QImageConsumer* consumer, const uchar* buffer, int length) + + New subclasses must reimplement this method. + + It should decode some or all of the bytes from \a buffer into \a + img, calling the methods of \a consumer as the decoding proceeds + to inform that consumer of changes to the image. The length of the + data is given in \a length. The consumer may be 0, in which case + the function should just process the data into \a img without + telling any consumer about the changes. Note that the decoder must + store enough state to be able to continue in subsequent calls to + this method - this is the essence of the incremental image + loading. + + The function should return without processing all the data if it + reaches the end of a frame in the input. + + The function must return the number of bytes it has processed. +*/ + +/*! + \class QImageFormatType qasyncimageio.h + \brief The QImageFormatType class is a factory that makes QImageFormat objects. + + \ingroup images + \ingroup graphics + \ingroup multimedia + + Whereas the QImageIO class allows for \e complete loading of + images, QImageFormatType allows for \e incremental loading of + images. + + New image file formats are installed by creating objects of + derived classes of QImageFormatType. They must implement + decoderFor() and formatName(). + + QImageFormatType is a very simple class. Its only task is to + recognize image data in some format and make a new object, + subclassed from QImageFormat, which can decode that format. + + The factories for formats built into Qt are automatically defined + before any other factory is initialized. If two factories would + recognize an image format, the factory created last will override + the earlier one; you can thus override current and future built-in + formats. +*/ + +/*! + \fn virtual QImageFormat* QImageFormatType::decoderFor(const uchar* buffer, int length) + + Returns a decoder for decoding an image that starts with the bytes + in \a buffer. The length of the data is given in \a length. This + function should only return a decoder if it is certain that the + decoder applies to data with the given header. Returns 0 if there + is insufficient data in the header to make a positive + identification or if the data is not recognized. +*/ + +/*! + \fn virtual const char* QImageFormatType::formatName() const + + Returns the name of the format supported by decoders from this + factory. The string is statically allocated. +*/ + +/*! + Constructs a factory. It automatically registers itself with + QImageDecoder. +*/ +QImageFormatType::QImageFormatType() +{ + QImageDecoder::registerDecoderFactory(this); +} + +/*! + Destroys a factory. It automatically unregisters itself from + QImageDecoder. +*/ +QImageFormatType::~QImageFormatType() +{ + QImageDecoder::unregisterDecoderFactory(this); +} + + +/*! + Returns TRUE if Qt was compiled with built-in GIF reading support; + otherwise returns FALSE. +*/ +bool qt_builtin_gif_reader() +{ +#if defined(QT_BUILTIN_GIF_READER) + return QT_BUILTIN_GIF_READER == 1; +#else + return 0; +#endif +} + +// See qgif.h for important information regarding this option +#if defined(QT_BUILTIN_GIF_READER) && QT_BUILTIN_GIF_READER == 1 + +/* -- NOTDOC + \class QGIFFormat qasyncimageio.h + \brief Incremental image decoder for GIF image format. + + \ingroup images + \ingroup graphics + + This subclass of QImageFormat decodes GIF format images, + including animated GIFs. Internally in +*/ + +/*! + Constructs a QGIFFormat. +*/ +QGIFFormat::QGIFFormat() +{ + globalcmap = 0; + localcmap = 0; + lncols = 0; + gncols = 0; + disposal = NoDisposal; + out_of_bounds = FALSE; + disposed = TRUE; + frame = -1; + state = Header; + count = 0; + lcmap = FALSE; +} + +/*! + Destroys a QGIFFormat. +*/ +QGIFFormat::~QGIFFormat() +{ + if (globalcmap) delete[] globalcmap; + if ( localcmap ) delete[] localcmap; +} + + +/* -- NOTDOC + \class QGIFFormatType qasyncimageio.h + \brief Incremental image decoder for GIF image format. + + \ingroup images + \ingroup graphics + + This subclass of QImageFormatType recognizes GIF + format images, creating a QGIFFormat when required. An instance + of this class is created automatically before any other factories, + so you should have no need for such objects. +*/ + +QImageFormat* QGIFFormatType::decoderFor( + const uchar* buffer, int length) +{ + if (length < 6) return 0; + if (buffer[0]=='G' + && buffer[1]=='I' + && buffer[2]=='F' + && buffer[3]=='8' + && (buffer[4]=='9' || buffer[4]=='7') + && buffer[5]=='a') + return new QGIFFormat; + return 0; +} + +const char* QGIFFormatType::formatName() const +{ + return "GIF"; +} + + +void QGIFFormat::disposePrevious( QImage& img, QImageConsumer* consumer ) +{ + if ( out_of_bounds ) // flush anything that survived + consumer->changed(QRect(0,0,swidth,sheight)); + + // Handle disposal of previous image before processing next one + + if ( disposed ) return; + + int l = QMIN(swidth-1,left); + int r = QMIN(swidth-1,right); + int t = QMIN(sheight-1,top); + int b = QMIN(sheight-1,bottom); + + switch (disposal) { + case NoDisposal: + break; + case DoNotChange: + break; + case RestoreBackground: + if (trans_index>=0) { + // Easy: we use the transparent color + fillRect(img, l, t, r-l+1, b-t+1, Q_TRANSPARENT); + } else if (bgcol>=0) { + // Easy: we use the bgcol given + fillRect(img, l, t, r-l+1, b-t+1, color(bgcol)); + } else { + // Impossible: We don't know of a bgcol - use pixel 0 + QRgb** line = (QRgb **)img.jumpTable(); + fillRect(img, l, t, r-l+1, b-t+1, line[0][0]); + } + if (consumer) + consumer->changed(QRect(l, t, r-l+1, b-t+1)); + break; + case RestoreImage: { + if ( frame >= 0 ) { + QRgb** line = (QRgb **)img.jumpTable(); + for (int ln=t; ln<=b; ln++) { + memcpy(line[ln]+l, + backingstore.scanLine(ln-t), + (r-l+1)*sizeof(QRgb) ); + } + consumer->changed(QRect(l, t, r-l+1, b-t+1)); + } + } + } + disposal = NoDisposal; // Until an extension says otherwise. + + disposed = TRUE; +} + +/*! + This function decodes some data into image changes. + + Returns the number of bytes consumed. +*/ +int QGIFFormat::decode(QImage& img, QImageConsumer* consumer, + const uchar* buffer, int length) +{ + // We are required to state that + // "The Graphics Interchange Format(c) is the Copyright property of + // CompuServe Incorporated. GIF(sm) is a Service Mark property of + // CompuServe Incorporated." + +#define LM(l, m) (((m)<<8)|l) + digress = FALSE; + int initial = length; + QRgb** line = (QRgb **)img.jumpTable(); + while (!digress && length) { + length--; + unsigned char ch=*buffer++; + switch (state) { + case Header: + hold[count++]=ch; + if (count==6) { + // Header + gif89=(hold[3]!='8' || hold[4]!='7'); + state=LogicalScreenDescriptor; + count=0; + } + break; + case LogicalScreenDescriptor: + hold[count++]=ch; + if (count==7) { + // Logical Screen Descriptor + swidth=LM(hold[0], hold[1]); + sheight=LM(hold[2], hold[3]); + gcmap=!!(hold[4]&0x80); + //UNUSED: bpchan=(((hold[4]&0x70)>>3)+1); + //UNUSED: gcmsortflag=!!(hold[4]&0x08); + gncols=2<<(hold[4]&0x7); + bgcol=(gcmap) ? hold[5] : -1; + //aspect=hold[6] ? double(hold[6]+15)/64.0 : 1.0; + + trans_index = -1; + count=0; + ncols=gncols; + if (gcmap) { + ccount=0; + state=GlobalColorMap; + globalcmap = new QRgb[gncols+1]; // +1 for trans_index + globalcmap[gncols] = Q_TRANSPARENT; + } else { + state=Introducer; + } + } + break; + case GlobalColorMap: case LocalColorMap: + hold[count++]=ch; + if (count==3) { + QRgb rgb = qRgb(hold[0], hold[1], hold[2]); + if ( state == LocalColorMap ) { + if ( ccount < lncols ) + localcmap[ccount] = rgb; + } else { + globalcmap[ccount] = rgb; + } + if (++ccount >= ncols) { + if ( state == LocalColorMap ) + state=TableImageLZWSize; + else + state=Introducer; + } + count=0; + } + break; + case Introducer: + hold[count++]=ch; + switch (ch) { + case ',': + state=ImageDescriptor; + break; + case '!': + state=ExtensionLabel; + break; + case ';': + if (consumer) { + if ( out_of_bounds ) // flush anything that survived + consumer->changed(QRect(0,0,swidth,sheight)); + consumer->end(); + } + state=Done; + break; + default: + digress=TRUE; + // Unexpected Introducer - ignore block + state=Error; + } + break; + 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]); + + // disbelieve ridiculous logical screen sizes, + // unless the image frames are also large. + if ( swidth/10 > QMAX(newwidth,200) ) + swidth = -1; + if ( sheight/10 > QMAX(newheight,200) ) + sheight = -1; + + if ( swidth <= 0 ) + swidth = newleft + newwidth; + if ( sheight <= 0 ) + sheight = newtop + newheight; + + if (img.isNull()) { + img.create(swidth, sheight, 32); + memset( img.bits(), 0, img.numBytes() ); + if (consumer) consumer->setSize(swidth, sheight); + } + img.setAlphaBuffer(trans_index >= 0); + line = (QRgb **)img.jumpTable(); + + disposePrevious( img, consumer ); + disposed = FALSE; + + left = newleft; + top = newtop; + width = newwidth; + height = newheight; + + right=QMAX( 0, QMIN(left+width, swidth)-1); + bottom=QMAX(0, QMIN(top+height, sheight)-1); + lcmap=!!(hold[9]&0x80); + interlace=!!(hold[9]&0x40); + //bool lcmsortflag=!!(hold[9]&0x20); + lncols=lcmap ? (2<<(hold[9]&0x7)) : 0; + if (lncols) { + if ( localcmap ) + delete [] localcmap; + localcmap = new QRgb[lncols+1]; + localcmap[lncols] = Q_TRANSPARENT; + ncols = lncols; + } else { + ncols = gncols; + } + frame++; + if ( frame == 0 ) { + if ( left || top || width<swidth || height<sheight ) { + // Not full-size image - erase with bg or transparent + if ( trans_index >= 0 ) { + fillRect(img, 0, 0, swidth, sheight, color(trans_index)); + if (consumer) consumer->changed(QRect(0,0,swidth,sheight)); + } else if ( bgcol>=0 ) { + fillRect(img, 0, 0, swidth, sheight, color(bgcol)); + if (consumer) consumer->changed(QRect(0,0,swidth,sheight)); + } + } + } + + if ( disposal == RestoreImage ) { + int l = QMIN(swidth-1,left); + int r = QMIN(swidth-1,right); + int t = QMIN(sheight-1,top); + int b = QMIN(sheight-1,bottom); + int w = r-l+1; + int h = b-t+1; + + if (backingstore.width() < w + || backingstore.height() < h) { + // We just use the backing store as a byte array + backingstore.create( QMAX(backingstore.width(), w), + QMAX(backingstore.height(), h), + 32); + memset( img.bits(), 0, img.numBytes() ); + } + for (int ln=0; ln<h; ln++) { + memcpy(backingstore.scanLine(ln), + line[t+ln]+l, w*sizeof(QRgb)); + } + } + + count=0; + if (lcmap) { + ccount=0; + state=LocalColorMap; + } else { + state=TableImageLZWSize; + } + x = left; + y = top; + accum = 0; + bitcount = 0; + sp = stack; + firstcode = oldcode = 0; + needfirst = FALSE; + out_of_bounds = left>=swidth || y>=sheight; + } + break; + case TableImageLZWSize: { + lzwsize=ch; + if ( lzwsize > max_lzw_bits ) { + state=Error; + } else { + code_size=lzwsize+1; + clear_code=1<<lzwsize; + end_code=clear_code+1; + max_code_size=2*clear_code; + max_code=clear_code+2; + int i; + for (i=0; i<clear_code && i<(1<<max_lzw_bits); i++) { + table[0][i]=0; + table[1][i]=i; + } + for (i=clear_code; i<(1<<max_lzw_bits); i++) { + table[0][i]=table[1][i]=0; + } + state=ImageDataBlockSize; + } + count=0; + break; + } case ImageDataBlockSize: + expectcount=ch; + if (expectcount) { + state=ImageDataBlock; + } else { + if (consumer) { + consumer->frameDone(); + digress = TRUE; + } + + state=Introducer; + } + break; + case ImageDataBlock: + count++; + accum|=(ch<<bitcount); + bitcount+=8; + while (bitcount>=code_size && state==ImageDataBlock) { + int code=accum&((1<<code_size)-1); + bitcount-=code_size; + accum>>=code_size; + + if (code==clear_code) { + if (!needfirst) { + int i; + code_size=lzwsize+1; + max_code_size=2*clear_code; + max_code=clear_code+2; + for (i=0; i<clear_code; i++) { + table[0][i]=0; + table[1][i]=i; + } + for (i=clear_code; i<(1<<max_lzw_bits); i++) { + table[0][i]=table[1][i]=0; + } + } + needfirst=TRUE; + } else if (code==end_code) { + bitcount = -32768; + // Left the block end arrive + } else { + if (needfirst) { + firstcode=oldcode=code; + if (!out_of_bounds && line && firstcode!=trans_index) + line[y][x] = color(firstcode); + x++; + if (x>=swidth) out_of_bounds = TRUE; + needfirst=FALSE; + if (x>=left+width) { + x=left; + out_of_bounds = left>=swidth || y>=sheight; + nextY(img,consumer); + } + } else { + incode=code; + if (code>=max_code) { + *sp++=firstcode; + code=oldcode; + } + while (code>=clear_code) { + *sp++=table[1][code]; + if (code==table[0][code]) { + state=Error; + break; + } + if (sp-stack>=(1<<(max_lzw_bits))*2) { + state=Error; + break; + } + code=table[0][code]; + } + *sp++=firstcode=table[1][code]; + code=max_code; + if (code<(1<<max_lzw_bits)) { + table[0][code]=oldcode; + table[1][code]=firstcode; + max_code++; + if ((max_code>=max_code_size) + && (max_code_size<(1<<max_lzw_bits))) + { + max_code_size*=2; + code_size++; + } + } + oldcode=incode; + while (sp>stack) { + --sp; + if (!out_of_bounds && line && *sp!=trans_index) + line[y][x] = color(*sp); + x++; + if (x>=swidth) out_of_bounds = TRUE; + if (x>=left+width) { + x=left; + out_of_bounds = left>=swidth || y>=sheight; + nextY(img,consumer); + } + } + } + } + } + if (count==expectcount) { + count=0; + state=ImageDataBlockSize; + } + break; + case ExtensionLabel: + switch (ch) { + case 0xf9: + state=GraphicControlExtension; + break; + case 0xff: + state=ApplicationExtension; + break; +#if 0 + case 0xfe: + state=CommentExtension; + break; + case 0x01: + break; +#endif + default: + state=SkipBlockSize; + } + count=0; + break; + case ApplicationExtension: + if (count<11) hold[count]=ch; + count++; + if (count==hold[0]+1) { + if (qstrncmp((char*)(hold+1), "NETSCAPE", 8)==0) { + // Looping extension + state=NetscapeExtensionBlockSize; + } else { + state=SkipBlockSize; + } + count=0; + } + break; + case NetscapeExtensionBlockSize: + expectcount=ch; + count=0; + if (expectcount) state=NetscapeExtensionBlock; + else state=Introducer; + break; + case NetscapeExtensionBlock: + if (count<3) hold[count]=ch; + count++; + if (count==expectcount) { + int loop = hold[0]+hold[1]*256; + if (consumer) consumer->setLooping(loop); + state=SkipBlockSize; // Ignore further blocks + } + break; + case GraphicControlExtension: + if (count<5) hold[count]=ch; + count++; + if (count==hold[0]+1) { + disposePrevious( img, consumer ); + disposal=Disposal((hold[1]>>2)&0x7); + //UNUSED: waitforuser=!!((hold[1]>>1)&0x1); + int delay=count>3 ? LM(hold[2], hold[3]) : 1; + // IE and mozilla use a minimum delay of 10. With the minumum delay of 10 + // we are compatible to them and avoid huge loads on the app and xserver. + if ( delay < 10 ) + delay = 10; + + bool havetrans=hold[1]&0x1; + trans_index = havetrans ? hold[4] : -1; + + if (consumer) consumer->setFramePeriod(delay*10); + count=0; + state=SkipBlockSize; + } + break; + case SkipBlockSize: + expectcount=ch; + count=0; + if (expectcount) state=SkipBlock; + else state=Introducer; + break; + case SkipBlock: + count++; + if (count==expectcount) state=SkipBlockSize; + break; + case Done: + digress=TRUE; + /* Netscape ignores the junk, so we do too. + length++; // Unget + state=Error; // More calls to this is an error + */ + break; + case Error: + return -1; // Called again after done. + } + } + return initial-length; +} + +void QGIFFormat::fillRect(QImage& img, int col, int row, int w, int h, QRgb color) +{ + if (w>0) { + QRgb** line = (QRgb **)img.jumpTable() + row; + for (int j=0; j<h; j++) { + for ( int i=0; i<w; i++ ) { + *(line[j]+col+i) = color; + } + } + } +} + +void QGIFFormat::nextY(QImage& img, QImageConsumer* consumer) +{ + int my; + switch (interlace) { + case 0: + // Non-interlaced + if (consumer && !out_of_bounds) + consumer->changed(QRect(left, y, right-left+1, 1)); + y++; + break; + case 1: + { + int i; + my = QMIN(7, bottom-y); + if ( trans_index < 0 ) // Don't dup with transparency + for (i=1; i<=my; i++) + memcpy(img.scanLine(y+i)+left, img.scanLine(y)+left, + (right-left+1)*sizeof(QRgb)); + if (consumer && !out_of_bounds) + consumer->changed(QRect(left, y, right-left+1, my+1)); + y+=8; + if (y>bottom) { + interlace++; y=top+4; + if (y > bottom) { // for really broken GIFs with bottom < 5 + interlace=2; + y = top + 2; + if (y > bottom) { // for really broken GIF with bottom < 3 + interlace = 0; + y = top + 1; + } + } + } + } break; + case 2: + { + int i; + my = QMIN(3, bottom-y); + if ( trans_index < 0 ) // Don't dup with transparency + for (i=1; i<=my; i++) + memcpy(img.scanLine(y+i)+left, img.scanLine(y)+left, + (right-left+1)*sizeof(QRgb)); + if (consumer && !out_of_bounds) + consumer->changed(QRect(left, y, right-left+1, my+1)); + y+=8; + if (y>bottom) { + interlace++; y=top+2; + if (y > bottom) { // for really broken GIF with bottom < 3 + interlace = 3; + y = top + 1; + } + } + } break; + case 3: + { + int i; + my = QMIN(1, bottom-y); + if ( trans_index < 0 ) // Don't dup with transparency + for (i=1; i<=my; i++) + memcpy(img.scanLine(y+i)+left, img.scanLine(y)+left, + (right-left+1)*sizeof(QRgb)); + if (consumer && !out_of_bounds) + consumer->changed(QRect(left, y, right-left+1, my+1)); + y+=4; + if (y>bottom) { interlace++; y=top+1; } + } break; + case 4: + if (consumer && !out_of_bounds) + consumer->changed(QRect(left, y, right-left+1, 1)); + y+=2; + } + + // Consume bogus extra lines + if (y >= sheight) out_of_bounds=TRUE; //y=bottom; +} + +QRgb QGIFFormat::color( uchar index ) const +{ + if ( index == trans_index || index > ncols ) + return Q_TRANSPARENT; + QRgb *map = lcmap ? localcmap : globalcmap; + return map ? map[index] : 0; +} + + + +#endif // QT_BUILTIN_GIF_READER + +#endif // QT_NO_ASYNC_IMAGE_IO |