diff options
Diffstat (limited to 'src/plugins/imageformats')
25 files changed, 5337 insertions, 0 deletions
diff --git a/src/plugins/imageformats/gif/gif.pro b/src/plugins/imageformats/gif/gif.pro new file mode 100644 index 0000000..74586b2 --- /dev/null +++ b/src/plugins/imageformats/gif/gif.pro @@ -0,0 +1,10 @@ +TARGET = qgif +include(../../qpluginbase.pri) + +HEADERS += qgifhandler.h +SOURCES += main.cpp \ + qgifhandler.cpp + +QTDIR_build:DESTDIR = $$QT_BUILD_TREE/plugins/imageformats +target.path += $$[QT_INSTALL_PLUGINS]/imageformats +INSTALLS += target diff --git a/src/plugins/imageformats/gif/main.cpp b/src/plugins/imageformats/gif/main.cpp new file mode 100644 index 0000000..075757b --- /dev/null +++ b/src/plugins/imageformats/gif/main.cpp @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qimageiohandler.h> +#include <qstringlist.h> + +#ifndef QT_NO_IMAGEFORMATPLUGIN + +#ifdef QT_NO_IMAGEFORMAT_GIF +#undef QT_NO_IMAGEFORMAT_GIF +#endif +#include "qgifhandler.h" + +QT_BEGIN_NAMESPACE + +class QGifPlugin : public QImageIOPlugin +{ +public: + QGifPlugin(); + ~QGifPlugin(); + + QStringList keys() const; + Capabilities capabilities(QIODevice *device, const QByteArray &format) const; + QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const; +}; + +QGifPlugin::QGifPlugin() +{ +} + +QGifPlugin::~QGifPlugin() +{ +} + +QStringList QGifPlugin::keys() const +{ + return QStringList() << QLatin1String("gif"); +} + +QImageIOPlugin::Capabilities QGifPlugin::capabilities(QIODevice *device, const QByteArray &format) const +{ + if (format == "gif" || (device && device->isReadable() && QGifHandler::canRead(device))) + return Capabilities(CanRead); + return 0; +} + +QImageIOHandler *QGifPlugin::create(QIODevice *device, const QByteArray &format) const +{ + QImageIOHandler *handler = new QGifHandler; + handler->setDevice(device); + handler->setFormat(format); + return handler; +} + +Q_EXPORT_STATIC_PLUGIN(QGifPlugin) +Q_EXPORT_PLUGIN2(qgif, QGifPlugin) + +#endif // QT_NO_IMAGEFORMATPLUGIN + +QT_END_NAMESPACE diff --git a/src/plugins/imageformats/gif/qgifhandler.cpp b/src/plugins/imageformats/gif/qgifhandler.cpp new file mode 100644 index 0000000..495794e --- /dev/null +++ b/src/plugins/imageformats/gif/qgifhandler.cpp @@ -0,0 +1,892 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +** WARNING: +** A separate license from Unisys may be required to use the gif +** reader. See http://www.unisys.com/about__unisys/lzw/ +** for information from Unisys +** +****************************************************************************/ + +#include "qgifhandler.h" + +#include <qimage.h> +#include <qiodevice.h> +#include <qvariant.h> + +QT_BEGIN_NAMESPACE + +#define Q_TRANSPARENT 0x00ffffff + +/* + Incremental image decoder for GIF image format. + + This subclass of QImageFormat decodes GIF format images, + including animated GIFs. Internally in +*/ + +class QGIFFormat { +public: + QGIFFormat(); + ~QGIFFormat(); + + int decode(QImage *image, const uchar* buffer, int length, + int *nextFrameDelay, int *loopCount, QSize *nextSize); + + bool newFrame; + bool partialNewFrame; + +private: + void fillRect(QImage *image, int x, int y, int w, int h, QRgb col); + inline 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 *image); + void disposePrevious(QImage *image); +}; + +/*! + 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; + newFrame = false; + partialNewFrame = false; +} + +/*! + Destroys a QGIFFormat. +*/ +QGIFFormat::~QGIFFormat() +{ + if (globalcmap) delete[] globalcmap; + if (localcmap) delete[] localcmap; +} + +void QGIFFormat::disposePrevious(QImage *image) +{ + if (out_of_bounds) { + // flush anything that survived + // ### 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(image, l, t, r-l+1, b-t+1, Q_TRANSPARENT); + } else if (bgcol>=0) { + // Easy: we use the bgcol given + fillRect(image, l, t, r-l+1, b-t+1, color(bgcol)); + } else { + // Impossible: We don't know of a bgcol - use pixel 0 + QRgb *bits = (QRgb*)image->bits(); + fillRect(image, l, t, r-l+1, b-t+1, bits[0]); + } + // ### Changed: QRect(l, t, r-l+1, b-t+1) + break; + case RestoreImage: { + if (frame >= 0) { + for (int ln=t; ln<=b; ln++) { + memcpy(image->scanLine(ln)+l, + backingstore.scanLine(ln-t), + (r-l+1)*sizeof(QRgb)); + } + // ### 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 *image, const uchar *buffer, int length, + int *nextFrameDelay, int *loopCount, QSize *nextSize) +{ + // 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; + 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 ';': + // ### Changed: QRect(0, 0, swidth, sheight) + 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; + + QImage::Format format = trans_index >= 0 ? QImage::Format_ARGB32 : QImage::Format_RGB32; + if (image->isNull() || (image->size() != QSize(swidth, sheight)) || image->format() != format) { + (*image) = QImage(swidth, sheight, format); + memset(image->bits(), 0, image->numBytes()); + + // ### size of the upcoming frame, should rather + // be known before decoding it. + *nextSize = QSize(swidth, sheight); + } + + disposePrevious(image); + 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(image, 0, 0, swidth, sheight, color(trans_index)); + // ### Changed: QRect(0, 0, swidth, sheight) + } else if (bgcol>=0) { + fillRect(image, 0, 0, swidth, sheight, color(bgcol)); + // ### 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 = QImage(qMax(backingstore.width(), w), + qMax(backingstore.height(), h), + QImage::Format_RGB32); + memset(image->bits(), 0, image->numBytes()); + } + for (int ln=0; ln<h; ln++) { + memcpy(backingstore.scanLine(ln), + image->scanLine(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 = true; + 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++) { + table[0][i]=0; + table[1][i]=i; + } + state=ImageDataBlockSize; + } + count=0; + break; + } case ImageDataBlockSize: + expectcount=ch; + if (expectcount) { + state=ImageDataBlock; + } else { + state=Introducer; + digress = true; + newFrame = true; + } + 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) { + code_size=lzwsize+1; + max_code_size=2*clear_code; + max_code=clear_code+2; + } + needfirst=true; + } else if (code==end_code) { + bitcount = -32768; + // Left the block end arrive + } else { + if (needfirst) { + firstcode=oldcode=code; + if (!out_of_bounds && image->height() > y && firstcode!=trans_index) + ((QRgb*)image->scanLine(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(image); + } + } else { + incode=code; + if (code>=max_code) { + *sp++=firstcode; + code=oldcode; + } + while (code>=clear_code+2) { + *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; + const int h = image->height(); + const QRgb *map = lcmap ? localcmap : globalcmap; + QRgb *line = 0; + if (!out_of_bounds && h > y) + line = (QRgb*)image->scanLine(y); + while (sp>stack) { + const uchar index = *(--sp); + if (!out_of_bounds && h > y && index!=trans_index) { + if (index > ncols) + line[x] = Q_TRANSPARENT; + else + line[x] = map ? map[index] : 0; + } + x++; + if (x>=swidth) out_of_bounds = true; + if (x>=left+width) { + x=left; + out_of_bounds = left>=swidth || y>=sheight; + nextY(image); + if (!out_of_bounds && h > y) + line = (QRgb*)image->scanLine(y); + } + } + } + } + } + partialNewFrame = true; + 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) { + *loopCount = hold[1]+hold[2]*256; + state=SkipBlockSize; // Ignore further blocks + } + break; + case GraphicControlExtension: + if (count<5) hold[count]=ch; + count++; + if (count==hold[0]+1) { + disposePrevious(image); + 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 minimum delay of 10 + // we are compatible to them and avoid huge loads on the app and xserver. + *nextFrameDelay = (delay < 2 ? 10 : delay) * 10; + + bool havetrans=hold[1]&0x1; + trans_index = havetrans ? hold[4] : -1; + + 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 *image, int col, int row, int w, int h, QRgb color) +{ + if (w>0) { + for (int j=0; j<h; j++) { + QRgb *line = (QRgb*)image->scanLine(j+row); + for (int i=0; i<w; i++) + *(line+col+i) = color; + } + } +} + +void QGIFFormat::nextY(QImage *image) +{ + int my; + switch (interlace) { + case 0: // Non-interlaced + // if (!out_of_bounds) { + // ### Changed: QRect(left, y, right - left + 1, 1); + // } + y++; + break; + case 1: { + int i; + my = qMin(7, bottom-y); + // Don't dup with transparency + if (trans_index < 0) { + for (i=1; i<=my; i++) { + memcpy(image->scanLine(y+i)+left*sizeof(QRgb), image->scanLine(y)+left*sizeof(QRgb), + (right-left+1)*sizeof(QRgb)); + } + } + + // if (!out_of_bounds) { + // ### Changed: QRect(left, y, right - left + 1, my + 1); + // } +// if (!out_of_bounds) +// qDebug("consumer->changed(QRect(%d, %d, %d, %d))", 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); + // Don't dup with transparency + if (trans_index < 0) { + for (i=1; i<=my; i++) { + memcpy(image->scanLine(y+i)+left*sizeof(QRgb), image->scanLine(y)+left*sizeof(QRgb), + (right-left+1)*sizeof(QRgb)); + } + } + + // if (!out_of_bounds) { + // ### Changed: QRect(left, y, right - left + 1, my + 1); + // } + y+=8; + if (y>bottom) { + interlace++; y=top+2; + // handle broken GIF with bottom < 3 + if (y > bottom) { + interlace = 3; + y = top + 1; + } + } + } break; + case 3: { + int i; + my = qMin(1, bottom-y); + // Don't dup with transparency + if (trans_index < 0) { + for (i=1; i<=my; i++) { + memcpy(image->scanLine(y+i)+left*sizeof(QRgb), image->scanLine(y)+left*sizeof(QRgb), + (right-left+1)*sizeof(QRgb)); + } + } + // if (!out_of_bounds) { + // ### Changed: QRect(left, y, right - left + 1, my + 1); + // } + y+=4; + if (y>bottom) { interlace++; y=top+1; } + } break; + case 4: + // if (!out_of_bounds) { + // ### Changed: QRect(left, y, right - left + 1, 1); + // } + y+=2; + } + + // Consume bogus extra lines + if (y >= sheight) out_of_bounds=true; //y=bottom; +} + +inline 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; +} + +//------------------------------------------------------------------------- +//------------------------------------------------------------------------- +//------------------------------------------------------------------------- + +QGifHandler::QGifHandler() +{ + gifFormat = new QGIFFormat; + nextDelay = 0; + loopCnt = 0; + frameNumber = -1; + nextSize = QSize(); +} + +QGifHandler::~QGifHandler() +{ + delete gifFormat; +} + +// Does partial decode if necessary, just to see if an image is coming + +bool QGifHandler::imageIsComing() const +{ + const int GifChunkSize = 4096; + + while (!gifFormat->partialNewFrame) { + if (buffer.isEmpty()) { + buffer += device()->read(GifChunkSize); + if (buffer.isEmpty()) + break; + } + + int decoded = gifFormat->decode(&lastImage, (const uchar *)buffer.constData(), buffer.size(), + &nextDelay, &loopCnt, &nextSize); + if (decoded == -1) + break; + buffer.remove(0, decoded); + } + return gifFormat->partialNewFrame; +} + +bool QGifHandler::canRead() const +{ + if (!nextDelay && canRead(device())) { + setFormat("gif"); + return true; + } + + return imageIsComing(); +} + +bool QGifHandler::canRead(QIODevice *device) +{ + if (!device) { + qWarning("QGifHandler::canRead() called with no device"); + return false; + } + + char head[6]; + if (device->peek(head, sizeof(head)) == sizeof(head)) + return qstrncmp(head, "GIF87a", 6) == 0 + || qstrncmp(head, "GIF89a", 6) == 0; + return false; +} + +bool QGifHandler::read(QImage *image) +{ + const int GifChunkSize = 4096; + + while (!gifFormat->newFrame) { + if (buffer.isEmpty()) { + buffer += device()->read(GifChunkSize); + if (buffer.isEmpty()) + break; + } + + int decoded = gifFormat->decode(&lastImage, (const uchar *)buffer.constData(), buffer.size(), + &nextDelay, &loopCnt, &nextSize); + if (decoded == -1) + break; + buffer.remove(0, decoded); + } + if (gifFormat->newFrame || (gifFormat->partialNewFrame && device()->atEnd())) { + *image = lastImage; + ++frameNumber; + gifFormat->newFrame = false; + gifFormat->partialNewFrame = false; + return true; + } + + return false; +} + +bool QGifHandler::write(const QImage &image) +{ + Q_UNUSED(image); + return false; +} + +bool QGifHandler::supportsOption(ImageOption option) const +{ + return option == Size + || option == Animation; +} + +QVariant QGifHandler::option(ImageOption option) const +{ + if (option == Size) { + if (imageIsComing()) + return nextSize; + } else if (option == Animation) { + return true; + } + return QVariant(); +} + +void QGifHandler::setOption(ImageOption option, const QVariant &value) +{ + Q_UNUSED(option); + Q_UNUSED(value); +} + +int QGifHandler::nextImageDelay() const +{ + return nextDelay; +} + +int QGifHandler::imageCount() const +{ + return 0; // Don't know +} + +int QGifHandler::loopCount() const +{ + return loopCnt-1; // In GIF, loop count is iteration count, so subtract one +} + +int QGifHandler::currentImageNumber() const +{ + return frameNumber; +} + +QByteArray QGifHandler::name() const +{ + return "gif"; +} + +QT_END_NAMESPACE diff --git a/src/plugins/imageformats/gif/qgifhandler.h b/src/plugins/imageformats/gif/qgifhandler.h new file mode 100644 index 0000000..c56c3ab --- /dev/null +++ b/src/plugins/imageformats/gif/qgifhandler.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +** WARNING: +** A separate license from Unisys may be required to use the gif +** reader. See http://www.unisys.com/about__unisys/lzw/ +** for information from Unisys +** +****************************************************************************/ + +#ifndef QGIFHANDLER_H +#define QGIFHANDLER_H + +#include <QtGui/qimageiohandler.h> +#include <QtGui/qimage.h> +#include <QtCore/qbytearray.h> + +QT_BEGIN_NAMESPACE + +class QGIFFormat; +class QGifHandler : public QImageIOHandler +{ +public: + QGifHandler(); + ~QGifHandler(); + + bool canRead() const; + bool read(QImage *image); + bool write(const QImage &image); + + QByteArray name() const; + + static bool canRead(QIODevice *device); + + QVariant option(ImageOption option) const; + void setOption(ImageOption option, const QVariant &value); + bool supportsOption(ImageOption option) const; + + int imageCount() const; + int loopCount() const; + int nextImageDelay() const; + int currentImageNumber() const; + +private: + bool imageIsComing() const; + QGIFFormat *gifFormat; + QString fileName; + mutable QByteArray buffer; + mutable QImage lastImage; + + mutable int nextDelay; + mutable int loopCnt; + int frameNumber; + mutable QSize nextSize; +}; + +QT_END_NAMESPACE + +#endif // QGIFHANDLER_H diff --git a/src/plugins/imageformats/ico/ico.pro b/src/plugins/imageformats/ico/ico.pro new file mode 100644 index 0000000..73665cd --- /dev/null +++ b/src/plugins/imageformats/ico/ico.pro @@ -0,0 +1,12 @@ +TARGET = qico +include(../../qpluginbase.pri) + +QTDIR_build:REQUIRES = "!contains(QT_CONFIG, no-ico)" + +HEADERS += qicohandler.h +SOURCES += main.cpp \ + qicohandler.cpp + +QTDIR_build:DESTDIR = $$QT_BUILD_TREE/plugins/imageformats +target.path += $$[QT_INSTALL_PLUGINS]/imageformats +INSTALLS += target diff --git a/src/plugins/imageformats/ico/main.cpp b/src/plugins/imageformats/ico/main.cpp new file mode 100644 index 0000000..5685c4e --- /dev/null +++ b/src/plugins/imageformats/ico/main.cpp @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qimageiohandler.h> +#include <qdebug.h> + +#ifndef QT_NO_IMAGEFORMATPLUGIN + +#ifdef QT_NO_IMAGEFORMAT_ICO +#undef QT_NO_IMAGEFORMAT_ICO +#endif +#include "qicohandler.h" + +QT_BEGIN_NAMESPACE + +class QICOPlugin : public QImageIOPlugin +{ +public: + QStringList keys() const; + Capabilities capabilities(QIODevice *device, const QByteArray &format) const; + QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const; +}; + +QStringList QICOPlugin::keys() const +{ + return QStringList() << QLatin1String("ico"); +} + +QImageIOPlugin::Capabilities QICOPlugin::capabilities(QIODevice *device, const QByteArray &format) const +{ + if (format == "ico") + return Capabilities(CanRead | CanWrite); + if (!format.isEmpty()) + return 0; + if (!device->isOpen()) + return 0; + + Capabilities cap; + if (device->isReadable() && QtIcoHandler::canRead(device)) + cap |= CanRead; + if (device->isWritable()) + cap |= CanWrite; + return cap; +} + +QImageIOHandler *QICOPlugin::create(QIODevice *device, const QByteArray &format) const +{ + QImageIOHandler *handler = new QtIcoHandler(device); + + handler->setFormat(format); + return handler; +} + +Q_EXPORT_PLUGIN2(qico, QICOPlugin) + +QT_END_NAMESPACE + +#endif /* QT_NO_IMAGEFORMATPLUGIN */ diff --git a/src/plugins/imageformats/ico/qicohandler.cpp b/src/plugins/imageformats/ico/qicohandler.cpp new file mode 100644 index 0000000..299190b --- /dev/null +++ b/src/plugins/imageformats/ico/qicohandler.cpp @@ -0,0 +1,862 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \class QtIcoHandler + \since 4.4 + \brief The QtIcoHandler class provides support for the ICO image format. + \internal +*/ + + + +#include "qicohandler.h" +#include <QtCore/qendian.h> +#include <QtGui/QImage> +#include <QtCore/QFile> +#include <QtCore/QBuffer> +// These next two structs represent how the icon information is stored +// in an ICO file. +typedef struct +{ + quint8 bWidth; // Width of the image + quint8 bHeight; // Height of the image (times 2) + quint8 bColorCount; // Number of colors in image (0 if >=8bpp) [ not ture ] + quint8 bReserved; // Reserved + quint16 wPlanes; // Color Planes + quint16 wBitCount; // Bits per pixel + quint32 dwBytesInRes; // how many bytes in this resource? + quint32 dwImageOffset; // where in the file is this image +} ICONDIRENTRY, *LPICONDIRENTRY; +#define ICONDIRENTRY_SIZE 16 + +typedef struct +{ + quint16 idReserved; // Reserved + quint16 idType; // resource type (1 for icons) + quint16 idCount; // how many images? + ICONDIRENTRY idEntries[1]; // the entries for each image +} ICONDIR, *LPICONDIR; +#define ICONDIR_SIZE 6 // Exclude the idEntries field + +typedef struct { // BMP information header + quint32 biSize; // size of this struct + quint32 biWidth; // pixmap width + quint32 biHeight; // pixmap height + quint16 biPlanes; // should be 1 + quint16 biBitCount; // number of bits per pixel + quint32 biCompression; // compression method + quint32 biSizeImage; // size of image + quint32 biXPelsPerMeter; // horizontal resolution + quint32 biYPelsPerMeter; // vertical resolution + quint32 biClrUsed; // number of colors used + quint32 biClrImportant; // number of important colors +} BMP_INFOHDR ,*LPBMP_INFOHDR; +#define BMP_INFOHDR_SIZE 40 + +class ICOReader +{ +public: + ICOReader(QIODevice * iodevice); + int count(); + QImage iconAt(int index); + static bool canRead(QIODevice *iodev); + + static QList<QImage> read(QIODevice * device); + + static bool write(QIODevice * device, const QList<QImage> & images); + +private: + bool readHeader(); + bool readIconEntry(int index, ICONDIRENTRY * iconEntry); + + bool readBMPHeader(ICONDIRENTRY & iconEntry, BMP_INFOHDR * header); + void findColorInfo(QImage & image); + void readColorTable(QImage & image); + + void readBMP(QImage & image); + void read1BitBMP(QImage & image); + void read4BitBMP(QImage & image); + void read8BitBMP(QImage & image); + void read16_24_32BMP(QImage & image); + + struct IcoAttrib + { + int nbits; + int ncolors; + int h; + int w; + int depth; + } icoAttrib; + + QIODevice * iod; + qint64 startpos; + bool headerRead; + ICONDIR iconDir; + +}; + +// Data readers and writers that takes care of alignment and endian stuff. +static bool readIconDirEntry(QIODevice *iodev, ICONDIRENTRY *iconDirEntry) +{ + if (iodev) { + uchar tmp[ICONDIRENTRY_SIZE]; + if (iodev->read((char*)tmp, ICONDIRENTRY_SIZE) == ICONDIRENTRY_SIZE) { + iconDirEntry->bWidth = tmp[0]; + iconDirEntry->bHeight = tmp[1]; + iconDirEntry->bColorCount = tmp[2]; + iconDirEntry->bReserved = tmp[3]; + + iconDirEntry->wPlanes = qFromLittleEndian<quint16>(&tmp[4]); + iconDirEntry->wBitCount = qFromLittleEndian<quint16>(&tmp[6]); + iconDirEntry->dwBytesInRes = qFromLittleEndian<quint32>(&tmp[8]); + iconDirEntry->dwImageOffset = qFromLittleEndian<quint32>(&tmp[12]); + return true; + } + } + return false; +} + +static bool writeIconDirEntry(QIODevice *iodev, const ICONDIRENTRY &iconEntry) +{ + if (iodev) { + uchar tmp[ICONDIRENTRY_SIZE]; + tmp[0] = iconEntry.bWidth; + tmp[1] = iconEntry.bHeight; + tmp[2] = iconEntry.bColorCount; + tmp[3] = iconEntry.bReserved; + qToLittleEndian<quint16>(iconEntry.wPlanes, &tmp[4]); + qToLittleEndian<quint16>(iconEntry.wBitCount, &tmp[6]); + qToLittleEndian<quint32>(iconEntry.dwBytesInRes, &tmp[8]); + qToLittleEndian<quint32>(iconEntry.dwImageOffset, &tmp[12]); + return (iodev->write((char*)tmp, ICONDIRENTRY_SIZE) == ICONDIRENTRY_SIZE) ? true : false; + } + + return false; +} + +static bool readIconDir(QIODevice *iodev, ICONDIR *iconDir) +{ + if (iodev) { + uchar tmp[ICONDIR_SIZE]; + if (iodev->read((char*)tmp, ICONDIR_SIZE) == ICONDIR_SIZE) { + iconDir->idReserved = qFromLittleEndian<quint16>(&tmp[0]); + iconDir->idType = qFromLittleEndian<quint16>(&tmp[2]); + iconDir->idCount = qFromLittleEndian<quint16>(&tmp[4]); + return true; + } + } + return false; +} + +static bool writeIconDir(QIODevice *iodev, const ICONDIR &iconDir) +{ + if (iodev) { + uchar tmp[6]; + qToLittleEndian(iconDir.idReserved, tmp); + qToLittleEndian(iconDir.idType, &tmp[2]); + qToLittleEndian(iconDir.idCount, &tmp[4]); + return (iodev->write((char*)tmp, 6) == 6) ? true : false; + } + return false; +} + +static bool readBMPInfoHeader(QIODevice *iodev, BMP_INFOHDR *pHeader) +{ + if (iodev) { + uchar header[BMP_INFOHDR_SIZE]; + if (iodev->read((char*)header, BMP_INFOHDR_SIZE) == BMP_INFOHDR_SIZE) { + pHeader->biSize = qFromLittleEndian<quint32>(&header[0]); + pHeader->biWidth = qFromLittleEndian<quint32>(&header[4]); + pHeader->biHeight = qFromLittleEndian<quint32>(&header[8]); + pHeader->biPlanes = qFromLittleEndian<quint16>(&header[12]); + pHeader->biBitCount = qFromLittleEndian<quint16>(&header[14]); + pHeader->biCompression = qFromLittleEndian<quint32>(&header[16]); + pHeader->biSizeImage = qFromLittleEndian<quint32>(&header[20]); + pHeader->biXPelsPerMeter = qFromLittleEndian<quint32>(&header[24]); + pHeader->biYPelsPerMeter = qFromLittleEndian<quint32>(&header[28]); + pHeader->biClrUsed = qFromLittleEndian<quint32>(&header[32]); + pHeader->biClrImportant = qFromLittleEndian<quint32>(&header[36]); + return true; + } + } + return false; +} + +static bool writeBMPInfoHeader(QIODevice *iodev, const BMP_INFOHDR &header) +{ + if (iodev) { + uchar tmp[BMP_INFOHDR_SIZE]; + qToLittleEndian<quint32>(header.biSize, &tmp[0]); + qToLittleEndian<quint32>(header.biWidth, &tmp[4]); + qToLittleEndian<quint32>(header.biHeight, &tmp[8]); + qToLittleEndian<quint16>(header.biPlanes, &tmp[12]); + qToLittleEndian<quint16>(header.biBitCount, &tmp[14]); + qToLittleEndian<quint32>(header.biCompression, &tmp[16]); + qToLittleEndian<quint32>(header.biSizeImage, &tmp[20]); + qToLittleEndian<quint32>(header.biXPelsPerMeter, &tmp[24]); + qToLittleEndian<quint32>(header.biYPelsPerMeter, &tmp[28]); + qToLittleEndian<quint32>(header.biClrUsed, &tmp[32]); + qToLittleEndian<quint32>(header.biClrImportant, &tmp[36]); + + return (iodev->write((char*)tmp, BMP_INFOHDR_SIZE) == BMP_INFOHDR_SIZE) ? true : false; + } + return false; +} + + +ICOReader::ICOReader(QIODevice * iodevice) +: iod(iodevice) +, startpos(0) +, headerRead(false) +{ +} + + +int ICOReader::count() +{ + if (readHeader()) + return iconDir.idCount; + return 0; +} + +bool ICOReader::canRead(QIODevice *iodev) +{ + bool isProbablyICO = false; + if (iodev) { + qint64 oldPos = iodev->pos(); + + ICONDIR ikonDir; + if (readIconDir(iodev, &ikonDir)) { + qint64 readBytes = ICONDIR_SIZE; + if (readIconDirEntry(iodev, &ikonDir.idEntries[0])) { + readBytes += ICONDIRENTRY_SIZE; + // ICO format does not have a magic identifier, so we read 6 different values, which will hopefully be enough to identify the file. + if ( ikonDir.idReserved == 0 + && ikonDir.idType == 1 + && ikonDir.idEntries[0].bReserved == 0 + && ikonDir.idEntries[0].wPlanes <= 1 + && ikonDir.idEntries[0].wBitCount <= 32 // Bits per pixel + && ikonDir.idEntries[0].dwBytesInRes >= 40 // Must be over 40, since sizeof (infoheader) == 40 + ) { + isProbablyICO = true; + } + + if (iodev->isSequential()) { + // Our structs might be padded due to alignment, so we need to fetch each member before we ungetChar() ! + quint32 tmp = ikonDir.idEntries[0].dwImageOffset; + iodev->ungetChar((tmp >> 24) & 0xff); + iodev->ungetChar((tmp >> 16) & 0xff); + iodev->ungetChar((tmp >> 8) & 0xff); + iodev->ungetChar(tmp & 0xff); + + tmp = ikonDir.idEntries[0].dwBytesInRes; + iodev->ungetChar((tmp >> 24) & 0xff); + iodev->ungetChar((tmp >> 16) & 0xff); + iodev->ungetChar((tmp >> 8) & 0xff); + iodev->ungetChar(tmp & 0xff); + + tmp = ikonDir.idEntries[0].wBitCount; + iodev->ungetChar((tmp >> 8) & 0xff); + iodev->ungetChar(tmp & 0xff); + + tmp = ikonDir.idEntries[0].wPlanes; + iodev->ungetChar((tmp >> 8) & 0xff); + iodev->ungetChar(tmp & 0xff); + + iodev->ungetChar(ikonDir.idEntries[0].bReserved); + iodev->ungetChar(ikonDir.idEntries[0].bColorCount); + iodev->ungetChar(ikonDir.idEntries[0].bHeight); + iodev->ungetChar(ikonDir.idEntries[0].bWidth); + } + } + + if (iodev->isSequential()) { + // Our structs might be padded due to alignment, so we need to fetch each member before we ungetChar() ! + quint32 tmp = ikonDir.idCount; + iodev->ungetChar((tmp >> 8) & 0xff); + iodev->ungetChar(tmp & 0xff); + + tmp = ikonDir.idType; + iodev->ungetChar((tmp >> 8) & 0xff); + iodev->ungetChar(tmp & 0xff); + + tmp = ikonDir.idReserved; + iodev->ungetChar((tmp >> 8) & 0xff); + iodev->ungetChar(tmp & 0xff); + } + + } + if (!iodev->isSequential()) iodev->seek(oldPos); + } + + return isProbablyICO; +} + +bool ICOReader::readHeader() +{ + if (iod && !headerRead) { + startpos = iod->pos(); + if (readIconDir(iod, &iconDir)) { + if (iconDir.idReserved == 0 || iconDir.idType == 1) + headerRead = true; + } + } + + return headerRead; +} + +bool ICOReader::readIconEntry(int index, ICONDIRENTRY * iconEntry) +{ + if (iod) { + if (iod->seek(startpos + ICONDIR_SIZE + (index * ICONDIRENTRY_SIZE))) { + return readIconDirEntry(iod, iconEntry); + } + } + return false; +} + + + +bool ICOReader::readBMPHeader(ICONDIRENTRY & iconEntry, BMP_INFOHDR * header) +{ + memset(&icoAttrib, 0, sizeof(IcoAttrib)); + if (iod) { + if (iod->seek(startpos + iconEntry.dwImageOffset)) { + if (readBMPInfoHeader(iod, header)) { + + icoAttrib.nbits = header->biBitCount ? header->biBitCount : iconEntry.wBitCount; + icoAttrib.h = header->biHeight / 2; // this height is always double the iconEntry height (for the mask) + icoAttrib.w = header->biWidth; + + switch (icoAttrib.nbits) { + case 32: + case 24: + case 16: + icoAttrib.depth = 32; + break; + case 8: + case 4: + icoAttrib.depth = 8; + break; + default: + icoAttrib.depth = 1; + } + + if ( icoAttrib.depth == 32 ) // there's no colormap + icoAttrib.ncolors = 0; + else // # colors used + icoAttrib.ncolors = header->biClrUsed ? header->biClrUsed : 1 << icoAttrib.nbits; + //qDebug() << "Bits:" << icoAttrib.nbits << "Depth:" << icoAttrib.depth << "Ncols:" << icoAttrib.ncolors; + return TRUE; + } + } + } + return FALSE; +} + +void ICOReader::findColorInfo(QImage & image) +{ + if (icoAttrib.ncolors > 0) { // set color table + readColorTable(image); + } else if (icoAttrib.nbits == 16) { // don't support RGB values for 15/16 bpp + image = QImage(); + } +} + +void ICOReader::readColorTable(QImage & image) +{ + if (iod) { + image.setNumColors(icoAttrib.ncolors); + uchar rgb[4]; + for (int i=0; i<icoAttrib.ncolors; i++) { + if (iod->read((char*)rgb, 4) != 4) { + image = QImage(); + break; + } + image.setColor(i, qRgb(rgb[2],rgb[1],rgb[0])); + } + } else { + image = QImage(); + } +} + +void ICOReader::readBMP(QImage & image) +{ + if (icoAttrib.nbits == 1) { // 1 bit BMP image + read1BitBMP(image); + } else if (icoAttrib.nbits == 4) { // 4 bit BMP image + read4BitBMP(image); + } else if (icoAttrib.nbits == 8) { + read8BitBMP(image); + } else if (icoAttrib.nbits == 16 || icoAttrib.nbits == 24 || icoAttrib.nbits == 32 ) { // 16,24,32 bit BMP image + read16_24_32BMP(image); + } +} + + +/** + * NOTE: A 1 bit BMP is only flipped vertically, and not horizontally like all other color depths! + * (This is the same with the bitmask) + * + */ +void ICOReader::read1BitBMP(QImage & image) +{ + if (iod) { + + int h = image.height(); + int bpl = image.bytesPerLine(); + + while (--h >= 0) { + if (iod->read((char*)image.scanLine(h),bpl) != bpl) { + image = QImage(); + break; + } + } + } else { + image = QImage(); + } +} + +void ICOReader::read4BitBMP(QImage & image) +{ + if (iod) { + + int h = icoAttrib.h; + int buflen = ((icoAttrib.w+7)/8)*4; + uchar *buf = new uchar[buflen]; + Q_CHECK_PTR(buf); + + while (--h >= 0) { + if (iod->read((char*)buf,buflen) != buflen) { + image = QImage(); + break; + } + register uchar *p = image.scanLine(h); + uchar *b = buf; + for (int i=0; i<icoAttrib.w/2; i++) { // convert nibbles to bytes + *p++ = *b >> 4; + *p++ = *b++ & 0x0f; + } + if (icoAttrib.w & 1) // the last nibble + *p = *b >> 4; + } + + delete [] buf; + + } else { + image = QImage(); + } +} + +void ICOReader::read8BitBMP(QImage & image) +{ + if (iod) { + + int h = icoAttrib.h; + int bpl = image.bytesPerLine(); + + while (--h >= 0) { + if (iod->read((char *)image.scanLine(h), bpl) != bpl) { + image = QImage(); + break; + } + } + } else { + image = QImage(); + } +} + +void ICOReader::read16_24_32BMP(QImage & image) +{ + if (iod) { + + int h = icoAttrib.h; + register QRgb *p; + QRgb *end; + uchar *buf = new uchar[image.bytesPerLine()]; + int bpl = ((icoAttrib.w*icoAttrib.nbits+31)/32)*4; + uchar *b; + + while (--h >= 0) { + p = (QRgb *)image.scanLine(h); + end = p + icoAttrib.w; + if (iod->read((char *)buf, bpl) != bpl) { + image = QImage(); + break; + } + b = buf; + while (p < end) { + if (icoAttrib.nbits == 24) + *p++ = qRgb(*(b+2), *(b+1), *b); + else if (icoAttrib.nbits == 32) + *p++ = qRgba(*(b+2), *(b+1), *b, *(b+3)); + b += icoAttrib.nbits/8; + } + } + + delete[] buf; + + } else { + image = QImage(); + } +} + +QImage ICOReader::iconAt(int index) +{ + QImage img; + + if (count() > index) { // forces header to be read + + ICONDIRENTRY iconEntry; + if (readIconEntry(index, &iconEntry)) { + + BMP_INFOHDR header; + if (readBMPHeader(iconEntry, &header)) { + + QImage::Format format = QImage::Format_ARGB32; + if (icoAttrib.nbits == 24) + format = QImage::Format_RGB32; + else if (icoAttrib.ncolors == 2) + format = QImage::Format_Mono; + else if (icoAttrib.ncolors > 0) + format = QImage::Format_Indexed8; + + QImage image(icoAttrib.w, icoAttrib.h, format); + if (!image.isNull()) { + findColorInfo(image); + if (!image.isNull()) { + readBMP(image); + if (!image.isNull()) { + QImage mask(image.width(), image.height(), QImage::Format_Mono); + if (!mask.isNull()) { + mask.setNumColors(2); + mask.setColor(0, qRgba(255,255,255,0xff)); + mask.setColor(1, qRgba(0 ,0 ,0 ,0xff)); + read1BitBMP(mask); + if (!mask.isNull()) { + img = QImage(image.width(), image.height(), QImage::Format_ARGB32 ); + img = image; + img.setAlphaChannel(mask); + // (Luckily, it seems that setAlphaChannel() does not ruin the alpha values + // of partially transparent pixels in those icons that have that) + } + } + } + } + } + } + } + } + + return img; +} + + +/*! + Reads all the icons from the given \a device, and returns them as + a list of QImage objects. + + Each image has an alpha channel that represents the mask from the + corresponding icon. + + \sa write() +*/ +QList<QImage> ICOReader::read(QIODevice * device) +{ + QList<QImage> images; + + ICOReader reader(device); + for (int i=0; i<reader.count(); i++) + images += reader.iconAt(i); + + return images; +} + + +/*! + Writes all the QImages in the \a images list to the given \a + device. Returns true if the images are written successfully; + otherwise returns false. + + The first image in the list is stored as the first icon in the + device, and is therefore used as the default icon by applications. + The alpha channel of each image is converted to a mask for each + corresponding icon. + + \sa read() +*/ +bool ICOReader::write(QIODevice * device, const QList<QImage> & images) +{ + bool retValue = false; + + if (images.count()) { + + qint64 origOffset = device->pos(); + + ICONDIR id; + id.idReserved = 0; + id.idType = 1; + id.idCount = images.count(); + + ICONDIRENTRY * entries = new ICONDIRENTRY[id.idCount]; + BMP_INFOHDR * bmpHeaders = new BMP_INFOHDR[id.idCount]; + QByteArray * imageData = new QByteArray[id.idCount]; + + for (int i=0; i<id.idCount; i++) { + + QImage image = images[i]; + // Scale down the image if it is larger than 128 pixels in either width or height + if (image.width() > 128 || image.height() > 128) + { + image = image.scaled(128, 128, Qt::KeepAspectRatio, Qt::SmoothTransformation); + } + QImage maskImage(image.width(), image.height(), QImage::Format_Mono); + image = image.convertToFormat(QImage::Format_ARGB32); + + if (image.hasAlphaChannel()) { + maskImage = image.createAlphaMask(); + } else { + maskImage.fill(0xff); + } + maskImage = maskImage.convertToFormat(QImage::Format_Mono); + + int nbits = 32; + int bpl_bmp = ((image.width()*nbits+31)/32)*4; + + entries[i].bColorCount = 0; + entries[i].bReserved = 0; + entries[i].wBitCount = nbits; + entries[i].bHeight = image.height(); + entries[i].bWidth = image.width(); + entries[i].dwBytesInRes = BMP_INFOHDR_SIZE + (bpl_bmp * image.height()) + + (maskImage.bytesPerLine() * maskImage.height()); + entries[i].wPlanes = 1; + if (i==0) + entries[i].dwImageOffset = origOffset + ICONDIR_SIZE + + (id.idCount * ICONDIRENTRY_SIZE); + else + entries[i].dwImageOffset = entries[i-1].dwImageOffset + entries[i-1].dwBytesInRes; + + bmpHeaders[i].biBitCount = entries[i].wBitCount; + bmpHeaders[i].biClrImportant = 0; + bmpHeaders[i].biClrUsed = entries[i].bColorCount; + bmpHeaders[i].biCompression = 0; + bmpHeaders[i].biHeight = entries[i].bHeight * 2; // 2 is for the mask + bmpHeaders[i].biPlanes = entries[i].wPlanes; + bmpHeaders[i].biSize = BMP_INFOHDR_SIZE; + bmpHeaders[i].biSizeImage = entries[i].dwBytesInRes - BMP_INFOHDR_SIZE; + bmpHeaders[i].biWidth = entries[i].bWidth; + bmpHeaders[i].biXPelsPerMeter = 0; + bmpHeaders[i].biYPelsPerMeter = 0; + + QBuffer buffer(&imageData[i]); + buffer.open(QIODevice::WriteOnly); + + uchar *buf = new uchar[bpl_bmp]; + uchar *b; + memset( buf, 0, bpl_bmp ); + int y; + for (y=image.height()-1; y>=0; y--) { // write the image bits + // 32 bits + QRgb *p = (QRgb *)image.scanLine(y); + QRgb *end = p + image.width(); + b = buf; + int x = 0; + while (p < end) { + *b++ = qBlue(*p); + *b++ = qGreen(*p); + *b++ = qRed(*p); + *b++ = qAlpha(*p); + if (qAlpha(*p) > 0) // Even mostly transparent pixels must not be masked away + maskImage.setPixel(x, y, Qt::color1); // (i.e. createAlphaMask() takes away too much) + p++; + x++; + } + buffer.write((char*)buf, bpl_bmp); + } + delete[] buf; + + maskImage.invertPixels(); // seems as though it needs this + // NOTE! !! The mask is only flipped vertically - not horizontally !! + for (y=maskImage.height()-1; y>=0; y--) + buffer.write((char*)maskImage.scanLine(y), maskImage.bytesPerLine()); + + } + + + + if (writeIconDir(device, id)) { + int i; + bool bOK = true; + for (i = 0; i < id.idCount && bOK; i++) { + bOK = writeIconDirEntry(device, entries[i]); + } + if (bOK) { + for (i=0; i<id.idCount && bOK; i++) { + bOK = writeBMPInfoHeader(device, bmpHeaders[i]); + bOK &= (device->write(imageData[i]) == (int) imageData[i].size()); + } + retValue = bOK; + } + } + + delete [] entries; + delete [] bmpHeaders; + delete [] imageData; + + } + return retValue; +} + +/*! + Constructs an instance of QtIcoHandler initialized to use \a device. +*/ +QtIcoHandler::QtIcoHandler(QIODevice *device) +{ + m_currentIconIndex = 0; + setDevice(device); + m_pICOReader = new ICOReader(device); +} + +/*! + Destructor for QtIcoHandler. +*/ +QtIcoHandler::~QtIcoHandler() +{ + delete m_pICOReader; +} + +/*! + * Verifies if some values (magic bytes) are set as expected in the header of the file. + * If the magic bytes were found, it is assumed that the QtIcoHandler can read the file. + * + */ +bool QtIcoHandler::canRead() const +{ + bool bCanRead = false; + QIODevice *device = QImageIOHandler::device(); + if (device) { + bCanRead = ICOReader::canRead(device); + if (bCanRead) + setFormat("ico"); + } else { + qWarning("QtIcoHandler::canRead() called with no device"); + } + return bCanRead; +} + +/*! This static function is used by the plugin code, and is provided for convenience only. + \a device must be an opened device with pointing to the start of the header data of the ICO file. +*/ +bool QtIcoHandler::canRead(QIODevice *device) +{ + Q_ASSERT(device); + return ICOReader::canRead(device); +} + +/*! \reimp + +*/ +bool QtIcoHandler::read(QImage *image) +{ + bool bSuccess = false; + QImage img = m_pICOReader->iconAt(m_currentIconIndex); + + // Make sure we only write to \a image when we succeed. + if (!img.isNull()) { + bSuccess = true; + *image = img; + } + + return bSuccess; +} + + +/*! \reimp + +*/ +bool QtIcoHandler::write(const QImage &image) +{ + QIODevice *device = QImageIOHandler::device(); + QList<QImage> imgs; + imgs.append(image); + return ICOReader::write(device, imgs); +} + +/*! + * Return the common identifier of the format. + * For ICO format this will return "ico". + */ +QByteArray QtIcoHandler::name() const +{ + return "ico"; +} + + +/*! \reimp + +*/ +int QtIcoHandler::imageCount() const +{ + return m_pICOReader->count(); +} + +/*! \reimp + +*/ +bool QtIcoHandler::jumpToImage(int imageNumber) +{ + if (imageNumber < imageCount()) { + m_currentIconIndex = imageNumber; + } + + return (imageNumber < imageCount()) ? true : false; +} + +/*! \reimp + +*/ +bool QtIcoHandler::jumpToNextImage() +{ + return jumpToImage(m_currentIconIndex + 1); +} + diff --git a/src/plugins/imageformats/ico/qicohandler.h b/src/plugins/imageformats/ico/qicohandler.h new file mode 100644 index 0000000..0bba208 --- /dev/null +++ b/src/plugins/imageformats/ico/qicohandler.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QTICOHANDLER_H +#define QTICOHANDLER_H + +#include <QtGui/QImageIOHandler> + +class ICOReader; +class QtIcoHandler: public QImageIOHandler +{ +public: + QtIcoHandler(QIODevice *device); + virtual ~QtIcoHandler(); + + bool canRead() const; + bool read(QImage *image); + bool write(const QImage &image); + + QByteArray name() const; + + int imageCount() const; + bool jumpToImage(int imageNumber); + bool jumpToNextImage(); + + static bool canRead(QIODevice *device); + +private: + int m_currentIconIndex; + ICOReader *m_pICOReader; + +}; + +#endif /* QTICOHANDLER_H */ + diff --git a/src/plugins/imageformats/imageformats.pro b/src/plugins/imageformats/imageformats.pro new file mode 100644 index 0000000..2e4036b --- /dev/null +++ b/src/plugins/imageformats/imageformats.pro @@ -0,0 +1,8 @@ +TEMPLATE = subdirs + +!contains(QT_CONFIG, no-jpeg):SUBDIRS += jpeg +!contains(QT_CONFIG, no-gif):SUBDIRS += gif +!contains(QT_CONFIG, no-mng):SUBDIRS += mng +contains(QT_CONFIG, svg):SUBDIRS += svg +!contains(QT_CONFIG, no-tiff):SUBDIRS += tiff +!contains(QT_CONFIG, no-ico):SUBDIRS += ico diff --git a/src/plugins/imageformats/jpeg/jpeg.pro b/src/plugins/imageformats/jpeg/jpeg.pro new file mode 100644 index 0000000..f310902 --- /dev/null +++ b/src/plugins/imageformats/jpeg/jpeg.pro @@ -0,0 +1,73 @@ +TARGET = qjpeg +include(../../qpluginbase.pri) + +QTDIR_build:REQUIRES = "!contains(QT_CONFIG, no-jpeg)" + +HEADERS += qjpeghandler.h +SOURCES += main.cpp \ + qjpeghandler.cpp + +wince*: { + DEFINES += NO_GETENV + contains(CE_ARCH,x86):CONFIG -= stl exceptions + contains(CE_ARCH,x86):CONFIG += exceptions_off +} + +contains(QT_CONFIG, system-jpeg) { + unix:LIBS += -ljpeg + win32:LIBS += libjpeg.lib +} +!contains(QT_CONFIG, system-jpeg) { + INCLUDEPATH += ../../../3rdparty/libjpeg + SOURCES += \ + ../../../3rdparty/libjpeg/jcapimin.c \ + ../../../3rdparty/libjpeg/jcapistd.c \ + ../../../3rdparty/libjpeg/jccoefct.c \ + ../../../3rdparty/libjpeg/jccolor.c \ + ../../../3rdparty/libjpeg/jcdctmgr.c \ + ../../../3rdparty/libjpeg/jchuff.c \ + ../../../3rdparty/libjpeg/jcinit.c \ + ../../../3rdparty/libjpeg/jcmainct.c \ + ../../../3rdparty/libjpeg/jcmarker.c \ + ../../../3rdparty/libjpeg/jcmaster.c \ + ../../../3rdparty/libjpeg/jcomapi.c \ + ../../../3rdparty/libjpeg/jcparam.c \ + ../../../3rdparty/libjpeg/jcphuff.c \ + ../../../3rdparty/libjpeg/jcprepct.c \ + ../../../3rdparty/libjpeg/jcsample.c \ + ../../../3rdparty/libjpeg/jctrans.c \ + ../../../3rdparty/libjpeg/jdapimin.c \ + ../../../3rdparty/libjpeg/jdapistd.c \ + ../../../3rdparty/libjpeg/jdatadst.c \ + ../../../3rdparty/libjpeg/jdatasrc.c \ + ../../../3rdparty/libjpeg/jdcoefct.c \ + ../../../3rdparty/libjpeg/jdcolor.c \ + ../../../3rdparty/libjpeg/jddctmgr.c \ + ../../../3rdparty/libjpeg/jdhuff.c \ + ../../../3rdparty/libjpeg/jdinput.c \ + ../../../3rdparty/libjpeg/jdmainct.c \ + ../../../3rdparty/libjpeg/jdmarker.c \ + ../../../3rdparty/libjpeg/jdmaster.c \ + ../../../3rdparty/libjpeg/jdmerge.c \ + ../../../3rdparty/libjpeg/jdphuff.c \ + ../../../3rdparty/libjpeg/jdpostct.c \ + ../../../3rdparty/libjpeg/jdsample.c \ + ../../../3rdparty/libjpeg/jdtrans.c \ + ../../../3rdparty/libjpeg/jerror.c \ + ../../../3rdparty/libjpeg/jfdctflt.c \ + ../../../3rdparty/libjpeg/jfdctfst.c \ + ../../../3rdparty/libjpeg/jfdctint.c \ + ../../../3rdparty/libjpeg/jidctflt.c \ + ../../../3rdparty/libjpeg/jidctfst.c \ + ../../../3rdparty/libjpeg/jidctint.c \ + ../../../3rdparty/libjpeg/jidctred.c \ + ../../../3rdparty/libjpeg/jmemmgr.c \ + ../../../3rdparty/libjpeg/jquant1.c \ + ../../../3rdparty/libjpeg/jquant2.c \ + ../../../3rdparty/libjpeg/jutils.c \ + ../../../3rdparty/libjpeg/jmemnobs.c +} + +QTDIR_build:DESTDIR = $$QT_BUILD_TREE/plugins/imageformats +target.path += $$[QT_INSTALL_PLUGINS]/imageformats +INSTALLS += target diff --git a/src/plugins/imageformats/jpeg/main.cpp b/src/plugins/imageformats/jpeg/main.cpp new file mode 100644 index 0000000..e4e2892 --- /dev/null +++ b/src/plugins/imageformats/jpeg/main.cpp @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qimageiohandler.h> +#include <qstringlist.h> + +#ifndef QT_NO_IMAGEFORMATPLUGIN + +#ifdef QT_NO_IMAGEFORMAT_JPEG +#undef QT_NO_IMAGEFORMAT_JPEG +#endif +#include "qjpeghandler.h" + +QT_BEGIN_NAMESPACE + +class QJpegPlugin : public QImageIOPlugin +{ +public: + QStringList keys() const; + Capabilities capabilities(QIODevice *device, const QByteArray &format) const; + QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const; +}; + +QStringList QJpegPlugin::keys() const +{ + return QStringList() << QLatin1String("jpeg") << QLatin1String("jpg"); +} + +QImageIOPlugin::Capabilities QJpegPlugin::capabilities(QIODevice *device, const QByteArray &format) const +{ + if (format == "jpeg" || format == "jpg") + return Capabilities(CanRead | CanWrite); + if (!format.isEmpty()) + return 0; + if (!device->isOpen()) + return 0; + + Capabilities cap; + if (device->isReadable() && QJpegHandler::canRead(device)) + cap |= CanRead; + if (device->isWritable()) + cap |= CanWrite; + return cap; +} + +QImageIOHandler *QJpegPlugin::create(QIODevice *device, const QByteArray &format) const +{ + QImageIOHandler *handler = new QJpegHandler; + handler->setDevice(device); + handler->setFormat(format); + return handler; +} + +Q_EXPORT_STATIC_PLUGIN(QJpegPlugin) +Q_EXPORT_PLUGIN2(qjpeg, QJpegPlugin) + +QT_END_NAMESPACE + +#endif // QT_NO_IMAGEFORMATPLUGIN diff --git a/src/plugins/imageformats/jpeg/qjpeghandler.cpp b/src/plugins/imageformats/jpeg/qjpeghandler.cpp new file mode 100644 index 0000000..31e5c66 --- /dev/null +++ b/src/plugins/imageformats/jpeg/qjpeghandler.cpp @@ -0,0 +1,1264 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qjpeghandler.h" + +#include <qimage.h> +#include <qvariant.h> +#include <qvector.h> + +#include <stdio.h> // jpeglib needs this to be pre-included +#include <setjmp.h> + +#ifdef FAR +#undef FAR +#endif + +// hw: optimize smoothscaler for returning 24-bit images + +// including jpeglib.h seems to be a little messy +extern "C" { +// mingw includes rpcndr.h but does not define boolean +#if defined(Q_OS_WIN) && defined(Q_CC_GNU) +# if defined(__RPCNDR_H__) && !defined(boolean) + typedef unsigned char boolean; +# define HAVE_BOOLEAN +# endif +#endif + +#define XMD_H // shut JPEGlib up +#if defined(Q_OS_UNIXWARE) +# define HAVE_BOOLEAN // libjpeg under Unixware seems to need this +#endif +#include <jpeglib.h> +#ifdef const +# undef const // remove crazy C hackery in jconfig.h +#endif +} + +QT_BEGIN_NAMESPACE + +//#define QT_NO_IMAGE_SMOOTHSCALE +#ifndef QT_NO_IMAGE_SMOOTHSCALE +class QImageSmoothScalerPrivate; +class QImageSmoothScaler +{ +public: + QImageSmoothScaler(const int w, const int h, const QImage &src); + QImageSmoothScaler(const int srcWidth, const int srcHeight, + const char *parameters); + + virtual ~QImageSmoothScaler(void); + + QImage scale(); + +protected: + int scaledWidth(void) const; + +private: + QImageSmoothScalerPrivate *d; + virtual QRgb *scanLine(const int line = 0, const QImage *src = 0); +}; + +class QImageSmoothScalerPrivate +{ +public: + int cols; + int newcols; + int rows; + int newrows; + bool hasAlpha; + + const QImage *src; + + void setup(const int srcWidth, const int srcHeight, const int dstWidth, + const int dstHeight, bool hasAlphaChannel); +}; + +QImageSmoothScaler::QImageSmoothScaler(const int w, const int h, + const QImage &src) +{ + d = new QImageSmoothScalerPrivate; + + d->setup(src.width(), src.height(), w, h, src.hasAlphaChannel() ); + this->d->src = &src; +} + +QImageSmoothScaler::QImageSmoothScaler(const int srcWidth, const int srcHeight, + const char *parameters) +{ + char sModeStr[1024]; + int t1; + int t2; + int dstWidth; + int dstHeight; + + sModeStr[0] = '\0'; + + d = new QImageSmoothScalerPrivate; +#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && defined(_MSC_VER) && _MSC_VER >= 1400 + sscanf_s(parameters, "Scale( %i, %i, %1023s )", &dstWidth, &dstHeight, sModeStr, sizeof(sModeStr)); +#else + sscanf(parameters, "Scale( %i, %i, %s )", &dstWidth, &dstHeight, sModeStr); +#endif + QString sModeQStr = QString::fromLatin1(sModeStr); + + t1 = srcWidth * dstHeight; + t2 = srcHeight * dstWidth; + + if (((sModeQStr == QLatin1String("ScaleMin")) && (t1 > t2)) || ((sModeQStr == QLatin1String("ScaleMax")) && (t2 < t2))) { + dstHeight = t2 / srcWidth; + } else if (sModeQStr != QLatin1String("ScaleFree")) { + dstWidth = t1 / srcHeight; + } + + d->setup(srcWidth, srcHeight, dstWidth, dstHeight, 0); +} + +void QImageSmoothScalerPrivate::setup(const int srcWidth, const int srcHeight, + const int dstWidth, const int dstHeight, + bool hasAlphaChannel) +{ + cols = srcWidth; + rows = srcHeight; + newcols = dstWidth; + newrows = dstHeight; + hasAlpha = hasAlphaChannel; +} + +int QImageSmoothScaler::scaledWidth() const +{ + return d->cols; +} + +QImageSmoothScaler::~QImageSmoothScaler() +{ + delete d; +} + +inline QRgb *QImageSmoothScaler::scanLine(const int line, const QImage *src) +{ + return (QRgb*)src->scanLine(line); +} + +/* + This function uses code based on pnmscale.c by Jef Poskanzer. + + pnmscale.c - read a portable anymap and scale it + + Copyright (C) 1989, 1991 by Jef Poskanzer. + + Permission to use, copy, modify, and distribute this software and its + documentation for any purpose and without fee is hereby granted, provided + that the above copyright notice appear in all copies and that both that + copyright notice and this permission notice appear in supporting + documentation. This software is provided "as is" without express or + implied warranty. +*/ + +QImage QImageSmoothScaler::scale() +{ + long SCALE; + long HALFSCALE; + QRgb *xelrow = 0; + QRgb *tempxelrow = 0; + QRgb *xP; + QRgb *nxP; + int row, rowsread; + int col, needtoreadrow; + uchar maxval = 255; + qreal xscale, yscale; + long sxscale, syscale; + long fracrowtofill, fracrowleft; + long *as; + long *rs; + long *gs; + long *bs; + int rowswritten = 0; + QImage dst; + + if (d->cols > 4096) { + SCALE = 4096; + HALFSCALE = 2048; + } else { + int fac = 4096; + while (d->cols * fac > 4096) { + fac /= 2; + } + + SCALE = fac * d->cols; + HALFSCALE = fac * d->cols / 2; + } + + xscale = (qreal) d->newcols / (qreal) d->cols; + yscale = (qreal) d->newrows / (qreal) d->rows; + sxscale = (long)(xscale * SCALE); + syscale = (long)(yscale * SCALE); + + if ( d->newrows != d->rows ) /* shortcut Y scaling if possible */ + tempxelrow = new QRgb[d->cols]; + + if ( d->hasAlpha ) { + as = new long[d->cols]; + for ( col = 0; col < d->cols; ++col ) + as[col] = HALFSCALE; + } else { + as = 0; + } + rs = new long[d->cols]; + gs = new long[d->cols]; + bs = new long[d->cols]; + rowsread = 0; + fracrowleft = syscale; + needtoreadrow = 1; + for ( col = 0; col < d->cols; ++col ) + rs[col] = gs[col] = bs[col] = HALFSCALE; + fracrowtofill = SCALE; + + dst = QImage( d->newcols, d->newrows, d->hasAlpha ? QImage::Format_ARGB32 : QImage::Format_RGB32 ); + + for ( row = 0; row < d->newrows; ++row ) { + /* First scale Y from xelrow into tempxelrow. */ + if ( d->newrows == d->rows ) { + /* shortcut Y scaling if possible */ + tempxelrow = xelrow = scanLine(rowsread++, d->src); + } else { + while ( fracrowleft < fracrowtofill ) { + if ( needtoreadrow && rowsread < d->rows ) { + xelrow = scanLine(rowsread++, d->src); + } + for ( col = 0, xP = xelrow; col < d->cols; ++col, ++xP ) { + if (as) { + as[col] += fracrowleft * qAlpha( *xP ); + rs[col] += fracrowleft * qRed( *xP ) * qAlpha( *xP ) / 255; + gs[col] += fracrowleft * qGreen( *xP ) * qAlpha( *xP ) / 255; + bs[col] += fracrowleft * qBlue( *xP ) * qAlpha( *xP ) / 255; + } else { + rs[col] += fracrowleft * qRed( *xP ); + gs[col] += fracrowleft * qGreen( *xP ); + bs[col] += fracrowleft * qBlue( *xP ); + } + } + fracrowtofill -= fracrowleft; + fracrowleft = syscale; + needtoreadrow = 1; + } + /* Now fracrowleft is >= fracrowtofill, so we can produce a row. */ + if ( needtoreadrow && rowsread < d->rows) { + xelrow = scanLine(rowsread++, d->src); + needtoreadrow = 0; + } + for ( col = 0, xP = xelrow, nxP = tempxelrow; + col < d->cols; ++col, ++xP, ++nxP ) + { + register long a, r, g, b; + + if ( as ) { + r = rs[col] + fracrowtofill * qRed( *xP ) * qAlpha( *xP ) / 255; + g = gs[col] + fracrowtofill * qGreen( *xP ) * qAlpha( *xP ) / 255; + b = bs[col] + fracrowtofill * qBlue( *xP ) * qAlpha( *xP ) / 255; + a = as[col] + fracrowtofill * qAlpha( *xP ); + if ( a ) { + r = r * 255 / a * SCALE; + g = g * 255 / a * SCALE; + b = b * 255 / a * SCALE; + } + } else { + r = rs[col] + fracrowtofill * qRed( *xP ); + g = gs[col] + fracrowtofill * qGreen( *xP ); + b = bs[col] + fracrowtofill * qBlue( *xP ); + a = 0; // unwarn + } + r /= SCALE; + if ( r > maxval ) r = maxval; + g /= SCALE; + if ( g > maxval ) g = maxval; + b /= SCALE; + if ( b > maxval ) b = maxval; + if ( as ) { + a /= SCALE; + if ( a > maxval ) a = maxval; + *nxP = qRgba( (int)r, (int)g, (int)b, (int)a ); + as[col] = HALFSCALE; + } else { + *nxP = qRgb( (int)r, (int)g, (int)b ); + } + rs[col] = gs[col] = bs[col] = HALFSCALE; + } + fracrowleft -= fracrowtofill; + if ( fracrowleft == 0 ) { + fracrowleft = syscale; + needtoreadrow = 1; + } + fracrowtofill = SCALE; + } + + /* Now scale X from tempxelrow into dst and write it out. */ + if ( d->newcols == d->cols ) { + /* shortcut X scaling if possible */ + memcpy(dst.scanLine(rowswritten++), tempxelrow, d->newcols*4); + } else { + register long a, r, g, b; + register long fraccoltofill, fraccolleft = 0; + register int needcol; + + nxP = (QRgb*)dst.scanLine(rowswritten++); + fraccoltofill = SCALE; + a = r = g = b = HALFSCALE; + needcol = 0; + for ( col = 0, xP = tempxelrow; col < d->cols; ++col, ++xP ) { + fraccolleft = sxscale; + while ( fraccolleft >= fraccoltofill ) { + if ( needcol ) { + ++nxP; + a = r = g = b = HALFSCALE; + } + if ( as ) { + r += fraccoltofill * qRed( *xP ) * qAlpha( *xP ) / 255; + g += fraccoltofill * qGreen( *xP ) * qAlpha( *xP ) / 255; + b += fraccoltofill * qBlue( *xP ) * qAlpha( *xP ) / 255; + a += fraccoltofill * qAlpha( *xP ); + if ( a ) { + r = r * 255 / a * SCALE; + g = g * 255 / a * SCALE; + b = b * 255 / a * SCALE; + } + } else { + r += fraccoltofill * qRed( *xP ); + g += fraccoltofill * qGreen( *xP ); + b += fraccoltofill * qBlue( *xP ); + } + r /= SCALE; + if ( r > maxval ) r = maxval; + g /= SCALE; + if ( g > maxval ) g = maxval; + b /= SCALE; + if ( b > maxval ) b = maxval; + if (as) { + a /= SCALE; + if ( a > maxval ) a = maxval; + *nxP = qRgba( (int)r, (int)g, (int)b, (int)a ); + } else { + *nxP = qRgb( (int)r, (int)g, (int)b ); + } + fraccolleft -= fraccoltofill; + fraccoltofill = SCALE; + needcol = 1; + } + if ( fraccolleft > 0 ) { + if ( needcol ) { + ++nxP; + a = r = g = b = HALFSCALE; + needcol = 0; + } + if (as) { + a += fraccolleft * qAlpha( *xP ); + r += fraccolleft * qRed( *xP ) * qAlpha( *xP ) / 255; + g += fraccolleft * qGreen( *xP ) * qAlpha( *xP ) / 255; + b += fraccolleft * qBlue( *xP ) * qAlpha( *xP ) / 255; + } else { + r += fraccolleft * qRed( *xP ); + g += fraccolleft * qGreen( *xP ); + b += fraccolleft * qBlue( *xP ); + } + fraccoltofill -= fraccolleft; + } + } + if ( fraccoltofill > 0 ) { + --xP; + if (as) { + a += fraccolleft * qAlpha( *xP ); + r += fraccoltofill * qRed( *xP ) * qAlpha( *xP ) / 255; + g += fraccoltofill * qGreen( *xP ) * qAlpha( *xP ) / 255; + b += fraccoltofill * qBlue( *xP ) * qAlpha( *xP ) / 255; + if ( a ) { + r = r * 255 / a * SCALE; + g = g * 255 / a * SCALE; + b = b * 255 / a * SCALE; + } + } else { + r += fraccoltofill * qRed( *xP ); + g += fraccoltofill * qGreen( *xP ); + b += fraccoltofill * qBlue( *xP ); + } + } + if ( ! needcol ) { + r /= SCALE; + if ( r > maxval ) r = maxval; + g /= SCALE; + if ( g > maxval ) g = maxval; + b /= SCALE; + if ( b > maxval ) b = maxval; + if (as) { + a /= SCALE; + if ( a > maxval ) a = maxval; + *nxP = qRgba( (int)r, (int)g, (int)b, (int)a ); + } else { + *nxP = qRgb( (int)r, (int)g, (int)b ); + } + } + } + } + + if ( d->newrows != d->rows && tempxelrow )// Robust, tempxelrow might be 0 1 day + delete [] tempxelrow; + if ( as ) // Avoid purify complaint + delete [] as; + if ( rs ) // Robust, rs might be 0 one day + delete [] rs; + if ( gs ) // Robust, gs might be 0 one day + delete [] gs; + if ( bs ) // Robust, bs might be 0 one day + delete [] bs; + + return dst; +} + +class jpegSmoothScaler : public QImageSmoothScaler +{ +public: + jpegSmoothScaler(struct jpeg_decompress_struct *info, const char *params): + QImageSmoothScaler(info->output_width, info->output_height, params) + { + cinfo = info; + cols24Bit = scaledWidth() * 3; + + cacheHeight = 1; + imageCache = QImage( info->output_width, cacheHeight, QImage::Format_RGB32 ); + } + +private: + int cols24Bit; + QImage imageCache; + int cacheHeight; + struct jpeg_decompress_struct *cinfo; + + QRgb *scanLine(const int line = 0, const QImage *src = 0) + { + QRgb *out; + uchar *in; + + Q_UNUSED(line); + Q_UNUSED(src); + + uchar* data = imageCache.bits(); + jpeg_read_scanlines(cinfo, &data, 1); + out = (QRgb*)imageCache.scanLine(0); + + // + // The smooth scale algorithm only works on 32-bit images; + // convert from (8|24) bits to 32. + // + if (cinfo->output_components == 1) { + in = (uchar*)out + scaledWidth(); + for (uint i = scaledWidth(); i--; ) { + in--; + out[i] = qRgb(*in, *in, *in); + } + } else { + in = (uchar*)out + cols24Bit; + for (uint i = scaledWidth(); i--; ) { + in -= 3; + out[i] = qRgb(in[0], in[1], in[2]); + } + } + + return out; + } + +}; +#endif + +struct my_error_mgr : public jpeg_error_mgr { + jmp_buf setjmp_buffer; +}; + +#if defined(Q_C_CALLBACKS) +extern "C" { +#endif + +static void my_error_exit (j_common_ptr cinfo) +{ + my_error_mgr* myerr = (my_error_mgr*) cinfo->err; + char buffer[JMSG_LENGTH_MAX]; + (*cinfo->err->format_message)(cinfo, buffer); + qWarning("%s", buffer); + longjmp(myerr->setjmp_buffer, 1); +} + +#if defined(Q_C_CALLBACKS) +} +#endif + + +static const int max_buf = 4096; + +struct my_jpeg_source_mgr : public jpeg_source_mgr { + // Nothing dynamic - cannot rely on destruction over longjump + QIODevice *device; + JOCTET buffer[max_buf]; + +public: + my_jpeg_source_mgr(QIODevice *device); +}; + +#if defined(Q_C_CALLBACKS) +extern "C" { +#endif + +static void qt_init_source(j_decompress_ptr) +{ +} + +static boolean qt_fill_input_buffer(j_decompress_ptr cinfo) +{ + int num_read; + my_jpeg_source_mgr* src = (my_jpeg_source_mgr*)cinfo->src; + src->next_input_byte = src->buffer; + num_read = src->device->read((char*)src->buffer, max_buf); + if (num_read <= 0) { + // Insert a fake EOI marker - as per jpeglib recommendation + src->buffer[0] = (JOCTET) 0xFF; + src->buffer[1] = (JOCTET) JPEG_EOI; + src->bytes_in_buffer = 2; + } else { + src->bytes_in_buffer = num_read; + } +#if defined(Q_OS_UNIXWARE) + return B_TRUE; +#else + return true; +#endif +} + +static void qt_skip_input_data(j_decompress_ptr cinfo, long num_bytes) +{ + my_jpeg_source_mgr* src = (my_jpeg_source_mgr*)cinfo->src; + + // `dumb' implementation from jpeglib + + /* Just a dumb implementation for now. Could use fseek() except + * it doesn't work on pipes. Not clear that being smart is worth + * any trouble anyway --- large skips are infrequent. + */ + if (num_bytes > 0) { + while (num_bytes > (long) src->bytes_in_buffer) { + num_bytes -= (long) src->bytes_in_buffer; + (void) qt_fill_input_buffer(cinfo); + /* note we assume that qt_fill_input_buffer will never return false, + * so suspension need not be handled. + */ + } + src->next_input_byte += (size_t) num_bytes; + src->bytes_in_buffer -= (size_t) num_bytes; + } +} + +static void qt_term_source(j_decompress_ptr cinfo) +{ + my_jpeg_source_mgr* src = (my_jpeg_source_mgr*)cinfo->src; + if (!src->device->isSequential()) + src->device->seek(src->device->pos() - src->bytes_in_buffer); +} + +#if defined(Q_C_CALLBACKS) +} +#endif + +inline my_jpeg_source_mgr::my_jpeg_source_mgr(QIODevice *device) +{ + jpeg_source_mgr::init_source = qt_init_source; + jpeg_source_mgr::fill_input_buffer = qt_fill_input_buffer; + jpeg_source_mgr::skip_input_data = qt_skip_input_data; + jpeg_source_mgr::resync_to_restart = jpeg_resync_to_restart; + jpeg_source_mgr::term_source = qt_term_source; + this->device = device; + bytes_in_buffer = 0; + next_input_byte = buffer; +} + + +static void scaleSize(int &reqW, int &reqH, int imgW, int imgH, Qt::AspectRatioMode mode) +{ + if (mode == Qt::IgnoreAspectRatio) + return; + int t1 = imgW * reqH; + int t2 = reqW * imgH; + if ((mode == Qt::KeepAspectRatio && (t1 > t2)) || (mode == Qt::KeepAspectRatioByExpanding && (t1 < t2))) + reqH = t2 / imgW; + else + reqW = t1 / imgH; +} + +static bool read_jpeg_size(QIODevice *device, int &w, int &h) +{ + bool rt = false; + struct jpeg_decompress_struct cinfo; + + struct my_jpeg_source_mgr *iod_src = new my_jpeg_source_mgr(device); + struct my_error_mgr jerr; + + jpeg_create_decompress(&cinfo); + + cinfo.src = iod_src; + + cinfo.err = jpeg_std_error(&jerr); + jerr.error_exit = my_error_exit; + + if (!setjmp(jerr.setjmp_buffer)) { +#if defined(Q_OS_UNIXWARE) + (void) jpeg_read_header(&cinfo, B_TRUE); +#else + (void) jpeg_read_header(&cinfo, true); +#endif + (void) jpeg_calc_output_dimensions(&cinfo); + + w = cinfo.output_width; + h = cinfo.output_height; + rt = true; + } + jpeg_destroy_decompress(&cinfo); + delete iod_src; + return rt; +} + +#define HIGH_QUALITY_THRESHOLD 50 + +static bool read_jpeg_format(QIODevice *device, QImage::Format &format) +{ + bool result = false; + struct jpeg_decompress_struct cinfo; + + struct my_jpeg_source_mgr *iod_src = new my_jpeg_source_mgr(device); + struct my_error_mgr jerr; + + jpeg_create_decompress(&cinfo); + + cinfo.src = iod_src; + + cinfo.err = jpeg_std_error(&jerr); + jerr.error_exit = my_error_exit; + + if (!setjmp(jerr.setjmp_buffer)) { +#if defined(Q_OS_UNIXWARE) + (void) jpeg_read_header(&cinfo, B_TRUE); +#else + (void) jpeg_read_header(&cinfo, true); +#endif + // This does not allocate memory for the whole image + // or such, so we are safe. + (void) jpeg_start_decompress(&cinfo); + result = true; + switch (cinfo.output_components) { + case 1: + format = QImage::Format_Indexed8; + break; + case 3: + case 4: + format = QImage::Format_RGB32; + break; + default: + result = false; + break; + } + cinfo.output_scanline = cinfo.output_height; + (void) jpeg_finish_decompress(&cinfo); + } + jpeg_destroy_decompress(&cinfo); + delete iod_src; + return result; +} + +static bool ensureValidImage(QImage *dest, struct jpeg_decompress_struct *info, + bool dummy = false) +{ + QImage::Format format; + switch (info->output_components) { + case 1: + format = QImage::Format_Indexed8; + break; + case 3: + case 4: + format = QImage::Format_RGB32; + break; + default: + return false; // unsupported format + } + + const QSize size(info->output_width, info->output_height); + if (dest->size() != size || dest->format() != format) { + static uchar dummyImage[1]; + if (dummy) // Create QImage but don't read the pixels + *dest = QImage(dummyImage, size.width(), size.height(), format); + else + *dest = QImage(size, format); + + if (format == QImage::Format_Indexed8) { + dest->setNumColors(256); + for (int i = 0; i < 256; i++) + dest->setColor(i, qRgb(i,i,i)); + } + } + + return !dest->isNull(); +} + +static bool read_jpeg_image(QIODevice *device, QImage *outImage, + const QByteArray ¶meters, QSize scaledSize, + int inQuality ) +{ +#ifdef QT_NO_IMAGE_SMOOTHSCALE + Q_UNUSED( scaledSize ); +#endif + + struct jpeg_decompress_struct cinfo; + + struct my_jpeg_source_mgr *iod_src = new my_jpeg_source_mgr(device); + struct my_error_mgr jerr; + + jpeg_create_decompress(&cinfo); + + cinfo.src = iod_src; + + cinfo.err = jpeg_std_error(&jerr); + jerr.error_exit = my_error_exit; + + if (!setjmp(jerr.setjmp_buffer)) { +#if defined(Q_OS_UNIXWARE) + (void) jpeg_read_header(&cinfo, B_TRUE); +#else + (void) jpeg_read_header(&cinfo, true); +#endif + + // -1 means default quality. + int quality = inQuality; + if (quality < 0) + quality = 75; + + QString params = QString::fromLatin1(parameters); + params.simplified(); + int sWidth = 0, sHeight = 0; + char sModeStr[1024] = ""; + Qt::AspectRatioMode sMode; + +#ifndef QT_NO_IMAGE_SMOOTHSCALE + // If high quality not required, shrink image during decompression + if (scaledSize.isValid() && quality < HIGH_QUALITY_THRESHOLD && !params.contains(QLatin1String("GetHeaderInformation")) ) { + cinfo.scale_denom = qMin(cinfo.image_width / scaledSize.width(), + cinfo.image_width / scaledSize.height()); + if (cinfo.scale_denom < 2) { + cinfo.scale_denom = 1; + } else if (cinfo.scale_denom < 4) { + cinfo.scale_denom = 2; + } else if (cinfo.scale_denom < 8) { + cinfo.scale_denom = 4; + } else { + cinfo.scale_denom = 8; + } + cinfo.scale_num = 1; + } +#endif + + + // If high quality not required, use fast decompression + if( quality < HIGH_QUALITY_THRESHOLD ) { + cinfo.dct_method = JDCT_IFAST; + cinfo.do_fancy_upsampling = FALSE; + } + + + (void) jpeg_start_decompress(&cinfo); + + if (params.contains(QLatin1String("GetHeaderInformation"))) { + if (!ensureValidImage(outImage, &cinfo, true)) + return false; + } else if (params.contains(QLatin1String("Scale"))) { +#if defined(_MSC_VER) && _MSC_VER >= 1400 && !defined(Q_OS_WINCE) + sscanf_s(params.toLatin1().data(), "Scale(%i, %i, %1023s)", + &sWidth, &sHeight, sModeStr, sizeof(sModeStr)); +#else + sscanf(params.toLatin1().data(), "Scale(%i, %i, %1023s)", + &sWidth, &sHeight, sModeStr); +#endif + + QString sModeQStr(QString::fromLatin1(sModeStr)); + if (sModeQStr == QLatin1String("IgnoreAspectRatio")) { + sMode = Qt::IgnoreAspectRatio; + } else if (sModeQStr == QLatin1String("KeepAspectRatio")) { + sMode = Qt::KeepAspectRatio; + } else if (sModeQStr == QLatin1String("KeepAspectRatioByExpanding")) { + sMode = Qt::KeepAspectRatioByExpanding; + } else { + qDebug("read_jpeg_image: invalid aspect ratio mode \"%s\", see QImage::AspectRatioMode documentation", sModeStr); + sMode = Qt::KeepAspectRatio; + } + +// qDebug("Parameters ask to scale the image to %i x %i AspectRatioMode: %s", sWidth, sHeight, sModeStr); + scaleSize(sWidth, sHeight, cinfo.output_width, cinfo.output_height, sMode); +// qDebug("Scaling the jpeg to %i x %i", sWidth, sHeight, sModeStr); + + if (cinfo.output_components == 3 || cinfo.output_components == 4) { + if (outImage->size() != QSize(sWidth, sHeight) || outImage->format() != QImage::Format_RGB32) + *outImage = QImage(sWidth, sHeight, QImage::Format_RGB32); + } else if (cinfo.output_components == 1) { + if (outImage->size() != QSize(sWidth, sHeight) || outImage->format() != QImage::Format_Indexed8) + *outImage = QImage(sWidth, sHeight, QImage::Format_Indexed8); + outImage->setNumColors(256); + for (int i = 0; i < 256; ++i) + outImage->setColor(i, qRgb(i,i,i)); + } else { + // Unsupported format + } + if (outImage->isNull()) + return false; + + if (!outImage->isNull()) { + QImage tmpImage(cinfo.output_width, 1, QImage::Format_RGB32); + uchar* inData = tmpImage.bits(); + uchar* outData = outImage->bits(); + int out_bpl = outImage->bytesPerLine(); + while (cinfo.output_scanline < cinfo.output_height) { + int outputLine = sHeight * cinfo.output_scanline / cinfo.output_height; + (void) jpeg_read_scanlines(&cinfo, &inData, 1); + if (cinfo.output_components == 3) { + uchar *in = inData; + QRgb *out = (QRgb*)outData + outputLine * out_bpl; + for (uint i=0; i<cinfo.output_width; i++) { +// ### Only scaling down an image works, I don't think scaling up will work at the moment +// ### An idea I have to make this a smooth scale is to progressively add the pixel values up +// When scaling down, multiple values are being over drawn in to the output buffer. +// Instead, a weighting based on the distance the line or pixel is from the output pixel determines +// the weight of it when added to the output buffer. At present it is a non-smooth scale which is +// inefficently implemented, it still uncompresses all the jpeg, an optimization for progressive +// jpegs could be made if scaling by say 50% or some other special cases + out[sWidth * i / cinfo.output_width] = qRgb(in[0], in[1], in[2]); + in += 3; + } + } else { +// ### Need to test the case where the jpeg is grayscale, need some black and white jpegs to test +// this code. (also only scales down and probably won't scale to a larger size) + uchar *in = inData; + uchar *out = outData + outputLine*out_bpl; + for (uint i=0; i<cinfo.output_width; i++) { + out[sWidth * i / cinfo.output_width] = in[i]; + } + } + } + (void) jpeg_finish_decompress(&cinfo); + } +#ifndef QT_NO_IMAGE_SMOOTHSCALE + } else if (scaledSize.isValid()) { + + jpegSmoothScaler scaler(&cinfo, QString().sprintf("Scale( %d, %d, ScaleFree )", + scaledSize.width(), + scaledSize.height()).toLatin1().data()); + *outImage = scaler.scale(); +#endif + } else { + if (!ensureValidImage(outImage, &cinfo)) + return false; + + uchar* data = outImage->bits(); + int bpl = outImage->bytesPerLine(); + while (cinfo.output_scanline < cinfo.output_height) { + uchar *d = data + cinfo.output_scanline * bpl; + (void) jpeg_read_scanlines(&cinfo, + &d, + 1); + } + (void) jpeg_finish_decompress(&cinfo); + + if (cinfo.output_components == 3) { + // Expand 24->32 bpp. + for (uint j=0; j<cinfo.output_height; j++) { + uchar *in = outImage->scanLine(j) + cinfo.output_width * 3; + QRgb *out = (QRgb*)outImage->scanLine(j); + + for (uint i=cinfo.output_width; i--;) { + in-=3; + out[i] = qRgb(in[0], in[1], in[2]); + } + } + } else if (cinfo.out_color_space == JCS_CMYK) { + for (uint j = 0; j < cinfo.output_height; ++j) { + uchar *in = outImage->scanLine(j) + cinfo.output_width * 4; + QRgb *out = (QRgb*)outImage->scanLine(j); + + for (uint i = cinfo.output_width; i--; ) { + in-=4; + int k = in[3]; + out[i] = qRgb(k * in[0] / 255, k * in[1] / 255, k * in[2] / 255); + } + } + } + if (cinfo.density_unit == 1) { + outImage->setDotsPerMeterX(int(100. * cinfo.X_density / 2.54)); + outImage->setDotsPerMeterY(int(100. * cinfo.Y_density / 2.54)); + } else if (cinfo.density_unit == 2) { + outImage->setDotsPerMeterX(int(100. * cinfo.X_density)); + outImage->setDotsPerMeterY(int(100. * cinfo.Y_density)); + } + } + } + + jpeg_destroy_decompress(&cinfo); + delete iod_src; + return !outImage->isNull(); +} + + +struct my_jpeg_destination_mgr : public jpeg_destination_mgr { + // Nothing dynamic - cannot rely on destruction over longjump + QIODevice *device; + JOCTET buffer[max_buf]; + +public: + my_jpeg_destination_mgr(QIODevice *); +}; + + +#if defined(Q_C_CALLBACKS) +extern "C" { +#endif + +static void qt_init_destination(j_compress_ptr) +{ +} + +static boolean qt_empty_output_buffer(j_compress_ptr cinfo) +{ + my_jpeg_destination_mgr* dest = (my_jpeg_destination_mgr*)cinfo->dest; + + int written = dest->device->write((char*)dest->buffer, max_buf); + if (written == -1) + (*cinfo->err->error_exit)((j_common_ptr)cinfo); + + dest->next_output_byte = dest->buffer; + dest->free_in_buffer = max_buf; + +#if defined(Q_OS_UNIXWARE) + return B_TRUE; +#else + return true; +#endif +} + +static void qt_term_destination(j_compress_ptr cinfo) +{ + my_jpeg_destination_mgr* dest = (my_jpeg_destination_mgr*)cinfo->dest; + qint64 n = max_buf - dest->free_in_buffer; + + qint64 written = dest->device->write((char*)dest->buffer, n); + if (written == -1) + (*cinfo->err->error_exit)((j_common_ptr)cinfo); +} + +#if defined(Q_C_CALLBACKS) +} +#endif + +inline my_jpeg_destination_mgr::my_jpeg_destination_mgr(QIODevice *device) +{ + jpeg_destination_mgr::init_destination = qt_init_destination; + jpeg_destination_mgr::empty_output_buffer = qt_empty_output_buffer; + jpeg_destination_mgr::term_destination = qt_term_destination; + this->device = device; + next_output_byte = buffer; + free_in_buffer = max_buf; +} + + +static bool write_jpeg_image(const QImage &sourceImage, QIODevice *device, int sourceQuality) +{ + bool success = false; + const QImage image = sourceImage; + const QVector<QRgb> cmap = image.colorTable(); + + struct jpeg_compress_struct cinfo; + JSAMPROW row_pointer[1]; + row_pointer[0] = 0; + + struct my_jpeg_destination_mgr *iod_dest = new my_jpeg_destination_mgr(device); + struct my_error_mgr jerr; + + cinfo.err = jpeg_std_error(&jerr); + jerr.error_exit = my_error_exit; + + if (!setjmp(jerr.setjmp_buffer)) { + // WARNING: + // this if loop is inside a setjmp/longjmp branch + // do not create C++ temporaries here because the destructor may never be called + // if you allocate memory, make sure that you can free it (row_pointer[0]) + jpeg_create_compress(&cinfo); + + cinfo.dest = iod_dest; + + cinfo.image_width = image.width(); + cinfo.image_height = image.height(); + + bool gray=false; + switch (image.format()) { + case QImage::Format_Mono: + case QImage::Format_MonoLSB: + case QImage::Format_Indexed8: + gray = true; + for (int i = image.numColors(); gray && i--;) { + gray = gray & (qRed(cmap[i]) == qGreen(cmap[i]) && + qRed(cmap[i]) == qBlue(cmap[i])); + } + cinfo.input_components = gray ? 1 : 3; + cinfo.in_color_space = gray ? JCS_GRAYSCALE : JCS_RGB; + break; + default: + cinfo.input_components = 3; + cinfo.in_color_space = JCS_RGB; + } + + jpeg_set_defaults(&cinfo); + + qreal diffInch = qAbs(image.dotsPerMeterX()*2.54/100. - qRound(image.dotsPerMeterX()*2.54/100.)) + + qAbs(image.dotsPerMeterY()*2.54/100. - qRound(image.dotsPerMeterY()*2.54/100.)); + qreal diffCm = (qAbs(image.dotsPerMeterX()/100. - qRound(image.dotsPerMeterX()/100.)) + + qAbs(image.dotsPerMeterY()/100. - qRound(image.dotsPerMeterY()/100.)))*2.54; + if (diffInch < diffCm) { + cinfo.density_unit = 1; // dots/inch + cinfo.X_density = qRound(image.dotsPerMeterX()*2.54/100.); + cinfo.Y_density = qRound(image.dotsPerMeterY()*2.54/100.); + } else { + cinfo.density_unit = 2; // dots/cm + cinfo.X_density = (image.dotsPerMeterX()+50) / 100; + cinfo.Y_density = (image.dotsPerMeterY()+50) / 100; + } + + + int quality = sourceQuality >= 0 ? qMin(sourceQuality,100) : 75; +#if defined(Q_OS_UNIXWARE) + jpeg_set_quality(&cinfo, quality, B_TRUE /* limit to baseline-JPEG values */); + jpeg_start_compress(&cinfo, B_TRUE); +#else + jpeg_set_quality(&cinfo, quality, true /* limit to baseline-JPEG values */); + jpeg_start_compress(&cinfo, true); +#endif + + row_pointer[0] = new uchar[cinfo.image_width*cinfo.input_components]; + int w = cinfo.image_width; + while (cinfo.next_scanline < cinfo.image_height) { + uchar *row = row_pointer[0]; + switch (image.format()) { + case QImage::Format_Mono: + case QImage::Format_MonoLSB: + if (gray) { + const uchar* data = image.scanLine(cinfo.next_scanline); + if (image.format() == QImage::Format_MonoLSB) { + for (int i=0; i<w; i++) { + bool bit = !!(*(data + (i >> 3)) & (1 << (i & 7))); + row[i] = qRed(cmap[bit]); + } + } else { + for (int i=0; i<w; i++) { + bool bit = !!(*(data + (i >> 3)) & (1 << (7 -(i & 7)))); + row[i] = qRed(cmap[bit]); + } + } + } else { + const uchar* data = image.scanLine(cinfo.next_scanline); + if (image.format() == QImage::Format_MonoLSB) { + for (int i=0; i<w; i++) { + bool bit = !!(*(data + (i >> 3)) & (1 << (i & 7))); + *row++ = qRed(cmap[bit]); + *row++ = qGreen(cmap[bit]); + *row++ = qBlue(cmap[bit]); + } + } else { + for (int i=0; i<w; i++) { + bool bit = !!(*(data + (i >> 3)) & (1 << (7 -(i & 7)))); + *row++ = qRed(cmap[bit]); + *row++ = qGreen(cmap[bit]); + *row++ = qBlue(cmap[bit]); + } + } + } + break; + case QImage::Format_Indexed8: + if (gray) { + const uchar* pix = image.scanLine(cinfo.next_scanline); + for (int i=0; i<w; i++) { + *row = qRed(cmap[*pix]); + ++row; ++pix; + } + } else { + const uchar* pix = image.scanLine(cinfo.next_scanline); + for (int i=0; i<w; i++) { + *row++ = qRed(cmap[*pix]); + *row++ = qGreen(cmap[*pix]); + *row++ = qBlue(cmap[*pix]); + ++pix; + } + } + break; + case QImage::Format_RGB888: + memcpy(row, image.scanLine(cinfo.next_scanline), w * 3); + break; + case QImage::Format_RGB32: + case QImage::Format_ARGB32: + case QImage::Format_ARGB32_Premultiplied: { + QRgb* rgb = (QRgb*)image.scanLine(cinfo.next_scanline); + for (int i=0; i<w; i++) { + *row++ = qRed(*rgb); + *row++ = qGreen(*rgb); + *row++ = qBlue(*rgb); + ++rgb; + } + break; + } + default: + qWarning("QJpegHandler: unable to write image of format %i", + image.format()); + break; + } + jpeg_write_scanlines(&cinfo, row_pointer, 1); + } + + jpeg_finish_compress(&cinfo); + jpeg_destroy_compress(&cinfo); + success = true; + } else { + jpeg_destroy_compress(&cinfo); + success = false; + } + + delete iod_dest; + delete [] row_pointer[0]; + return success; +} + +QJpegHandler::QJpegHandler() +{ + quality = 75; +} + +bool QJpegHandler::canRead() const +{ + if (canRead(device())) { + setFormat("jpeg"); + return true; + } + return false; +} + +bool QJpegHandler::canRead(QIODevice *device) +{ + if (!device) { + qWarning("QJpegHandler::canRead() called with no device"); + return false; + } + + return device->peek(2) == "\xFF\xD8"; +} + +bool QJpegHandler::read(QImage *image) +{ + if (!canRead()) + return false; + return read_jpeg_image(device(), image, parameters, scaledSize, quality); +} + +bool QJpegHandler::write(const QImage &image) +{ + return write_jpeg_image(image, device(), quality); +} + +bool QJpegHandler::supportsOption(ImageOption option) const +{ + return option == Quality +#ifndef QT_NO_IMAGE_SMOOTHSCALE + || option == ScaledSize +#endif + || option == Size + || option == ImageFormat; +} + +QVariant QJpegHandler::option(ImageOption option) const +{ + if (option == Quality) { + return quality; +#ifndef QT_NO_IMAGE_SMOOTHSCALE + } else if (option == ScaledSize) { + return scaledSize; +#endif + } else if (option == Size) { + if (canRead() && !device()->isSequential()) { + qint64 pos = device()->pos(); + int width = 0; + int height = 0; + read_jpeg_size(device(), width, height); + device()->seek(pos); + return QSize(width, height); + } + } else if (option == ImageFormat) { + if (canRead() && !device()->isSequential()) { + qint64 pos = device()->pos(); + QImage::Format format = QImage::Format_Invalid; + read_jpeg_format(device(), format); + device()->seek(pos); + return format; + } + return QImage::Format_Invalid; + } + return QVariant(); +} + +void QJpegHandler::setOption(ImageOption option, const QVariant &value) +{ + if (option == Quality) + quality = value.toInt(); +#ifndef QT_NO_IMAGE_SMOOTHSCALE + else if ( option == ScaledSize ) + scaledSize = value.toSize(); +#endif +} + +QByteArray QJpegHandler::name() const +{ + return "jpeg"; +} + +QT_END_NAMESPACE diff --git a/src/plugins/imageformats/jpeg/qjpeghandler.h b/src/plugins/imageformats/jpeg/qjpeghandler.h new file mode 100644 index 0000000..2a8debc --- /dev/null +++ b/src/plugins/imageformats/jpeg/qjpeghandler.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QJPEGHANDLER_H +#define QJPEGHANDLER_H + +#include <QtGui/qimageiohandler.h> +#include <QtCore/QSize> + +QT_BEGIN_NAMESPACE + +class QJpegHandler : public QImageIOHandler +{ +public: + QJpegHandler(); + + bool canRead() const; + bool read(QImage *image); + bool write(const QImage &image); + + QByteArray name() const; + + static bool canRead(QIODevice *device); + + QVariant option(ImageOption option) const; + void setOption(ImageOption option, const QVariant &value); + bool supportsOption(ImageOption option) const; + +private: + int quality; + QByteArray parameters; + QSize scaledSize; +}; + +QT_END_NAMESPACE + +#endif // QJPEGHANDLER_H diff --git a/src/plugins/imageformats/mng/main.cpp b/src/plugins/imageformats/mng/main.cpp new file mode 100644 index 0000000..60936a7 --- /dev/null +++ b/src/plugins/imageformats/mng/main.cpp @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qimageiohandler.h> +#include <qstringlist.h> + +#ifndef QT_NO_IMAGEFORMATPLUGIN + +#ifdef QT_NO_IMAGEFORMAT_MNG +#undef QT_NO_IMAGEFORMAT_MNG +#endif +#include "qmnghandler.h" + +#include <qiodevice.h> +#include <qbytearray.h> + +QT_BEGIN_NAMESPACE + +class QMngPlugin : public QImageIOPlugin +{ + public: + QStringList keys() const; + Capabilities capabilities(QIODevice *device, const QByteArray &format) const; + QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const; +}; + +QStringList QMngPlugin::keys() const +{ + return QStringList() << QLatin1String("mng"); +} + +QImageIOPlugin::Capabilities QMngPlugin::capabilities(QIODevice *device, const QByteArray &format) const +{ + if (format == "mng") + return Capabilities(CanRead); + if (!format.isEmpty()) + return 0; + if (!device->isOpen()) + return 0; + + Capabilities cap; + if (device->isReadable() && QMngHandler::canRead(device)) + cap |= CanRead; + return cap; +} + +QImageIOHandler *QMngPlugin::create(QIODevice *device, const QByteArray &format) const +{ + QMngHandler *hand = new QMngHandler(); + hand->setDevice(device); + hand->setFormat(format); + return hand; +} + +Q_EXPORT_STATIC_PLUGIN(QMngPlugin) +Q_EXPORT_PLUGIN2(qmng, QMngPlugin) + +QT_END_NAMESPACE + +#endif // !QT_NO_IMAGEFORMATPLUGIN diff --git a/src/plugins/imageformats/mng/mng.pro b/src/plugins/imageformats/mng/mng.pro new file mode 100644 index 0000000..f0943f5 --- /dev/null +++ b/src/plugins/imageformats/mng/mng.pro @@ -0,0 +1,49 @@ +TARGET = qmng +include(../../qpluginbase.pri) + +QTDIR_build:REQUIRES = "!contains(QT_CONFIG, no-mng)" + +HEADERS += qmnghandler.h +SOURCES += main.cpp \ + qmnghandler.cpp + +contains(QT_CONFIG, system-mng) { + unix:LIBS += -lmng + win32:LIBS += libmng.lib +} +!contains(QT_CONFIG, system-mng) { + DEFINES += MNG_BUILD_SO + DEFINES += MNG_NO_INCLUDE_JNG + INCLUDEPATH += ../../../3rdparty/libmng + SOURCES += \ + ../../../3rdparty/libmng/libmng_callback_xs.c \ + ../../../3rdparty/libmng/libmng_chunk_io.c \ + ../../../3rdparty/libmng/libmng_chunk_descr.c \ + ../../../3rdparty/libmng/libmng_chunk_prc.c \ + ../../../3rdparty/libmng/libmng_chunk_xs.c \ + ../../../3rdparty/libmng/libmng_cms.c \ + ../../../3rdparty/libmng/libmng_display.c \ + ../../../3rdparty/libmng/libmng_dither.c \ + ../../../3rdparty/libmng/libmng_error.c \ + ../../../3rdparty/libmng/libmng_filter.c \ + ../../../3rdparty/libmng/libmng_hlapi.c \ + ../../../3rdparty/libmng/libmng_jpeg.c \ + ../../../3rdparty/libmng/libmng_object_prc.c \ + ../../../3rdparty/libmng/libmng_pixels.c \ + ../../../3rdparty/libmng/libmng_prop_xs.c \ + ../../../3rdparty/libmng/libmng_read.c \ + ../../../3rdparty/libmng/libmng_trace.c \ + ../../../3rdparty/libmng/libmng_write.c \ + ../../../3rdparty/libmng/libmng_zlib.c +} + +contains(QT_CONFIG, system-zlib) { + LIBS += -lz +} +!contains(QT_CONFIG, system-zlib) { + INCLUDEPATH += ../../../3rdparty/zlib +} + +QTDIR_build:DESTDIR = $$QT_BUILD_TREE/plugins/imageformats +target.path += $$[QT_INSTALL_PLUGINS]/imageformats +INSTALLS += target diff --git a/src/plugins/imageformats/mng/qmnghandler.cpp b/src/plugins/imageformats/mng/qmnghandler.cpp new file mode 100644 index 0000000..19cc16d --- /dev/null +++ b/src/plugins/imageformats/mng/qmnghandler.cpp @@ -0,0 +1,500 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmnghandler.h" + +#include "qimage.h" +#include "qvariant.h" +#include "qcolor.h" + +#define MNG_USE_SO +#include <libmng.h> + +QT_BEGIN_NAMESPACE + +class QMngHandlerPrivate +{ + Q_DECLARE_PUBLIC(QMngHandler) + public: + bool haveReadNone; + bool haveReadAll; + mng_handle hMNG; + QImage image; + int elapsed; + int nextDelay; + int iterCount; + int frameIndex; + int nextIndex; + int frameCount; + mng_uint32 iStyle; + mng_bool readData(mng_ptr pBuf, mng_uint32 iSize, mng_uint32p pRead); + mng_bool writeData(mng_ptr pBuf, mng_uint32 iSize, mng_uint32p pWritten); + mng_bool processHeader(mng_uint32 iWidth, mng_uint32 iHeight); + QMngHandlerPrivate(QMngHandler *q_ptr); + ~QMngHandlerPrivate(); + bool getNextImage(QImage *result); + bool writeImage(const QImage &image); + int currentImageNumber() const; + int imageCount() const; + bool jumpToImage(int imageNumber); + bool jumpToNextImage(); + int nextImageDelay() const; + bool setBackgroundColor(const QColor &color); + QColor backgroundColor() const; + QMngHandler *q_ptr; +}; + +static mng_bool myerror(mng_handle /*hMNG*/, + mng_int32 iErrorcode, + mng_int8 /*iSeverity*/, + mng_chunkid iChunkname, + mng_uint32 /*iChunkseq*/, + mng_int32 iExtra1, + mng_int32 iExtra2, + mng_pchar zErrortext) +{ + qWarning("MNG error %d: %s; chunk %c%c%c%c; subcode %d:%d", + iErrorcode,zErrortext, + (iChunkname>>24)&0xff, + (iChunkname>>16)&0xff, + (iChunkname>>8)&0xff, + (iChunkname>>0)&0xff, + iExtra1,iExtra2); + return TRUE; +} + +static mng_ptr myalloc(mng_size_t iSize) +{ +#if defined(Q_OS_WINCE) + mng_ptr ptr = malloc(iSize); + memset(ptr, 0, iSize); + return ptr; +#else + return (mng_ptr)calloc(1, iSize); +#endif +} + +static void myfree(mng_ptr pPtr, mng_size_t /*iSize*/) +{ + free(pPtr); +} + +static mng_bool myopenstream(mng_handle) +{ + return MNG_TRUE; +} + +static mng_bool myclosestream(mng_handle hMNG) +{ + QMngHandlerPrivate *pMydata = reinterpret_cast<QMngHandlerPrivate *>(mng_get_userdata(hMNG)); + pMydata->haveReadAll = true; + return MNG_TRUE; +} + +static mng_bool myreaddata(mng_handle hMNG, + mng_ptr pBuf, + mng_uint32 iSize, + mng_uint32p pRead) +{ + QMngHandlerPrivate *pMydata = reinterpret_cast<QMngHandlerPrivate *>(mng_get_userdata(hMNG)); + return pMydata->readData(pBuf, iSize, pRead); +} + +static mng_bool mywritedata(mng_handle hMNG, + mng_ptr pBuf, + mng_uint32 iSize, + mng_uint32p pWritten) +{ + QMngHandlerPrivate *pMydata = reinterpret_cast<QMngHandlerPrivate *>(mng_get_userdata(hMNG)); + return pMydata->writeData(pBuf, iSize, pWritten); +} + +static mng_bool myprocessheader(mng_handle hMNG, + mng_uint32 iWidth, + mng_uint32 iHeight) +{ + QMngHandlerPrivate *pMydata = reinterpret_cast<QMngHandlerPrivate *>(mng_get_userdata(hMNG)); + return pMydata->processHeader(iWidth, iHeight); +} + +static mng_ptr mygetcanvasline(mng_handle hMNG, + mng_uint32 iLinenr) +{ + QMngHandlerPrivate *pMydata = reinterpret_cast<QMngHandlerPrivate *>(mng_get_userdata(hMNG)); + return (mng_ptr)pMydata->image.scanLine(iLinenr); +} + +static mng_bool myrefresh(mng_handle /*hMNG*/, + mng_uint32 /*iX*/, + mng_uint32 /*iY*/, + mng_uint32 /*iWidth*/, + mng_uint32 /*iHeight*/) +{ + return MNG_TRUE; +} + +static mng_uint32 mygettickcount(mng_handle hMNG) +{ + QMngHandlerPrivate *pMydata = reinterpret_cast<QMngHandlerPrivate *>(mng_get_userdata(hMNG)); + return pMydata->elapsed++; +} + +static mng_bool mysettimer(mng_handle hMNG, + mng_uint32 iMsecs) +{ + QMngHandlerPrivate *pMydata = reinterpret_cast<QMngHandlerPrivate *>(mng_get_userdata(hMNG)); + pMydata->elapsed += iMsecs; + pMydata->nextDelay = iMsecs; + return MNG_TRUE; +} + +static mng_bool myprocessterm(mng_handle hMNG, + mng_uint8 iTermaction, + mng_uint8 /*iIteraction*/, + mng_uint32 /*iDelay*/, + mng_uint32 iItermax) +{ + QMngHandlerPrivate *pMydata = reinterpret_cast<QMngHandlerPrivate *>(mng_get_userdata(hMNG)); + if (iTermaction == 3) + pMydata->iterCount = iItermax; + return MNG_TRUE; +} + +static mng_bool mytrace(mng_handle, + mng_int32 iFuncnr, + mng_int32 iFuncseq, + mng_pchar zFuncname) +{ + qDebug("mng trace: iFuncnr: %d iFuncseq: %d zFuncname: %s", iFuncnr, iFuncseq, zFuncname); + return MNG_TRUE; +} + +QMngHandlerPrivate::QMngHandlerPrivate(QMngHandler *q_ptr) + : haveReadNone(true), haveReadAll(false), elapsed(0), nextDelay(0), iterCount(1), + frameIndex(-1), nextIndex(0), frameCount(0), q_ptr(q_ptr) +{ + iStyle = (QSysInfo::ByteOrder == QSysInfo::LittleEndian) ? MNG_CANVAS_BGRA8 : MNG_CANVAS_ARGB8; + // Initialize libmng + hMNG = mng_initialize((mng_ptr)this, myalloc, myfree, mytrace); + if (hMNG) { + // Set callback functions + mng_setcb_errorproc(hMNG, myerror); + mng_setcb_openstream(hMNG, myopenstream); + mng_setcb_closestream(hMNG, myclosestream); + mng_setcb_readdata(hMNG, myreaddata); + mng_setcb_writedata(hMNG, mywritedata); + mng_setcb_processheader(hMNG, myprocessheader); + mng_setcb_getcanvasline(hMNG, mygetcanvasline); + mng_setcb_refresh(hMNG, myrefresh); + mng_setcb_gettickcount(hMNG, mygettickcount); + mng_setcb_settimer(hMNG, mysettimer); + mng_setcb_processterm(hMNG, myprocessterm); + mng_set_doprogressive(hMNG, MNG_FALSE); + mng_set_suspensionmode(hMNG, MNG_TRUE); + } +} + +QMngHandlerPrivate::~QMngHandlerPrivate() +{ + mng_cleanup(&hMNG); +} + +mng_bool QMngHandlerPrivate::readData(mng_ptr pBuf, mng_uint32 iSize, mng_uint32p pRead) +{ + Q_Q(QMngHandler); + *pRead = q->device()->read((char *)pBuf, iSize); + return (*pRead > 0) ? MNG_TRUE : MNG_FALSE; +} + +mng_bool QMngHandlerPrivate::writeData(mng_ptr pBuf, mng_uint32 iSize, mng_uint32p pWritten) +{ + Q_Q(QMngHandler); + *pWritten = q->device()->write((char *)pBuf, iSize); + return MNG_TRUE; +} + +mng_bool QMngHandlerPrivate::processHeader(mng_uint32 iWidth, mng_uint32 iHeight) +{ + if (mng_set_canvasstyle(hMNG, iStyle) != MNG_NOERROR) + return MNG_FALSE; + image = QImage(iWidth, iHeight, QImage::Format_ARGB32); + image.fill(0); + return MNG_TRUE; +} + +bool QMngHandlerPrivate::getNextImage(QImage *result) +{ + mng_retcode ret; + if (haveReadNone) { + haveReadNone = false; + ret = mng_readdisplay(hMNG); + } else { + ret = mng_display_resume(hMNG); + } + if ((MNG_NOERROR == ret) || (MNG_NEEDTIMERWAIT == ret)) { + *result = image; + image.fill(0); + frameIndex = nextIndex++; + if (haveReadAll && (frameCount == 0)) + frameCount = nextIndex; + return true; + } + return false; +} + +bool QMngHandlerPrivate::writeImage(const QImage &image) +{ + mng_reset(hMNG); + if (mng_create(hMNG) != MNG_NOERROR) + return false; + + this->image = image.convertToFormat(QImage::Format_ARGB32); + int w = image.width(); + int h = image.height(); + + if ( + // width, height, ticks, layercount, framecount, playtime, simplicity + (mng_putchunk_mhdr(hMNG, w, h, 1000, 0, 0, 0, 7) == MNG_NOERROR) && + // termination_action, action_after_iterations, delay, iteration_max + (mng_putchunk_term(hMNG, 3, 0, 1, 0x7FFFFFFF) == MNG_NOERROR) && + // width, height, bitdepth, colortype, compression, filter, interlace + (mng_putchunk_ihdr(hMNG, w, h, 8, 6, 0, 0, 0) == MNG_NOERROR) && + // width, height, colortype, bitdepth, compression, filter, interlace, canvasstyle, getcanvasline + (mng_putimgdata_ihdr(hMNG, w, h, 6, 8, 0, 0, 0, iStyle, mygetcanvasline) == MNG_NOERROR) && + (mng_putchunk_iend(hMNG) == MNG_NOERROR) && + (mng_putchunk_mend(hMNG) == MNG_NOERROR) && + (mng_write(hMNG) == MNG_NOERROR) + ) + return true; + return false; +} + +int QMngHandlerPrivate::currentImageNumber() const +{ +// return mng_get_currentframe(hMNG) % imageCount(); not implemented, apparently + return frameIndex; +} + +int QMngHandlerPrivate::imageCount() const +{ +// return mng_get_totalframes(hMNG); not implemented, apparently + if (haveReadAll) + return frameCount; + return 0; // Don't know +} + +bool QMngHandlerPrivate::jumpToImage(int imageNumber) +{ + if (imageNumber == nextIndex) + return true; + + if ((imageNumber == 0) && haveReadAll && (nextIndex == frameCount)) { + // Loop! + nextIndex = 0; + return true; + } + if (mng_display_freeze(hMNG) == MNG_NOERROR) { + if (mng_display_goframe(hMNG, imageNumber) == MNG_NOERROR) { + nextIndex = imageNumber; + return true; + } + } + return false; +} + +bool QMngHandlerPrivate::jumpToNextImage() +{ + return jumpToImage((currentImageNumber()+1) % imageCount()); +} + +int QMngHandlerPrivate::nextImageDelay() const +{ + return nextDelay; +} + +bool QMngHandlerPrivate::setBackgroundColor(const QColor &color) +{ + mng_uint16 iRed = (mng_uint16)(color.red() << 8); + mng_uint16 iBlue = (mng_uint16)(color.blue() << 8); + mng_uint16 iGreen = (mng_uint16)(color.green() << 8); + return (mng_set_bgcolor(hMNG, iRed, iBlue, iGreen) == MNG_NOERROR); +} + +QColor QMngHandlerPrivate::backgroundColor() const +{ + mng_uint16 iRed; + mng_uint16 iBlue; + mng_uint16 iGreen; + if (mng_get_bgcolor(hMNG, &iRed, &iBlue, &iGreen) == MNG_NOERROR) + return QColor((iRed >> 8) & 0xFF, (iGreen >> 8) & 0xFF, (iBlue >> 8) & 0xFF); + return QColor(); +} + +QMngHandler::QMngHandler() + : d_ptr(new QMngHandlerPrivate(this)) +{ +} + +QMngHandler::~QMngHandler() +{ + Q_D(QMngHandler); + delete d; +} + +/*! \reimp */ +bool QMngHandler::canRead() const +{ + Q_D(const QMngHandler); + if (!d->haveReadNone) + return (!d->haveReadAll || (d->haveReadAll && (d->nextIndex < d->frameCount))); + + if (canRead(device())) { + setFormat("mng"); + return true; + } + return false; +} + +/*! \internal */ +bool QMngHandler::canRead(QIODevice *device) +{ + if (!device) { + qWarning("QMngHandler::canRead() called with no device"); + return false; + } + + return device->peek(8) == "\x8A\x4D\x4E\x47\x0D\x0A\x1A\x0A"; +} + +/*! \reimp */ +QByteArray QMngHandler::name() const +{ + return "mng"; +} + +/*! \reimp */ +bool QMngHandler::read(QImage *image) +{ + Q_D(QMngHandler); + return canRead() ? d->getNextImage(image) : false; +} + +/*! \reimp */ +bool QMngHandler::write(const QImage &image) +{ + Q_D(QMngHandler); + return d->writeImage(image); +} + +/*! \reimp */ +int QMngHandler::currentImageNumber() const +{ + Q_D(const QMngHandler); + return d->currentImageNumber(); +} + +/*! \reimp */ +int QMngHandler::imageCount() const +{ + Q_D(const QMngHandler); + return d->imageCount(); +} + +/*! \reimp */ +bool QMngHandler::jumpToImage(int imageNumber) +{ + Q_D(QMngHandler); + return d->jumpToImage(imageNumber); +} + +/*! \reimp */ +bool QMngHandler::jumpToNextImage() +{ + Q_D(QMngHandler); + return d->jumpToNextImage(); +} + +/*! \reimp */ +int QMngHandler::loopCount() const +{ + Q_D(const QMngHandler); + if (d->iterCount == 0x7FFFFFFF) + return -1; // infinite loop + return d->iterCount-1; +} + +/*! \reimp */ +int QMngHandler::nextImageDelay() const +{ + Q_D(const QMngHandler); + return d->nextImageDelay(); +} + +/*! \reimp */ +QVariant QMngHandler::option(ImageOption option) const +{ + Q_D(const QMngHandler); + if (option == QImageIOHandler::Animation) + return true; + else if (option == QImageIOHandler::BackgroundColor) + return d->backgroundColor(); + return QVariant(); +} + +/*! \reimp */ +void QMngHandler::setOption(ImageOption option, const QVariant & value) +{ + Q_D(QMngHandler); + if (option == QImageIOHandler::BackgroundColor) + d->setBackgroundColor(qVariantValue<QColor>(value)); +} + +/*! \reimp */ +bool QMngHandler::supportsOption(ImageOption option) const +{ + if (option == QImageIOHandler::Animation) + return true; + else if (option == QImageIOHandler::BackgroundColor) + return true; + return false; +} + +QT_END_NAMESPACE diff --git a/src/plugins/imageformats/mng/qmnghandler.h b/src/plugins/imageformats/mng/qmnghandler.h new file mode 100644 index 0000000..909be2d --- /dev/null +++ b/src/plugins/imageformats/mng/qmnghandler.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMNGHANDLER_H +#define QMNGHANDLER_H + +#include <QtGui/qimageiohandler.h> + +QT_BEGIN_NAMESPACE + +class QImage; +class QByteArray; +class QIODevice; +class QVariant; +class QMngHandlerPrivate; + +class QMngHandler : public QImageIOHandler +{ + public: + QMngHandler(); + ~QMngHandler(); + virtual bool canRead() const; + virtual QByteArray name() const; + virtual bool read(QImage *image); + virtual bool write(const QImage &image); + virtual int currentImageNumber() const; + virtual int imageCount() const; + virtual bool jumpToImage(int imageNumber); + virtual bool jumpToNextImage(); + virtual int loopCount() const; + virtual int nextImageDelay() const; + static bool canRead(QIODevice *device); + virtual QVariant option(ImageOption option) const; + virtual void setOption(ImageOption option, const QVariant & value); + virtual bool supportsOption(ImageOption option) const; + + private: + Q_DECLARE_PRIVATE(QMngHandler) + QMngHandlerPrivate *d_ptr; +}; + +QT_END_NAMESPACE + +#endif // QMNGHANDLER_H diff --git a/src/plugins/imageformats/svg/main.cpp b/src/plugins/imageformats/svg/main.cpp new file mode 100644 index 0000000..978ae40 --- /dev/null +++ b/src/plugins/imageformats/svg/main.cpp @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qimageiohandler.h> +#include <qstringlist.h> + +#if !defined(QT_NO_IMAGEFORMATPLUGIN) && !defined(QT_NO_SVGRENDERER) + +#include "qsvgiohandler.h" + +#include <qiodevice.h> +#include <qbytearray.h> +#include <qdebug.h> + +QT_BEGIN_NAMESPACE + +class QSvgPlugin : public QImageIOPlugin +{ +public: + QStringList keys() const; + Capabilities capabilities(QIODevice *device, const QByteArray &format) const; + QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const; +}; + +QStringList QSvgPlugin::keys() const +{ + return QStringList() << QLatin1String("svg"); +} + +QImageIOPlugin::Capabilities QSvgPlugin::capabilities(QIODevice *device, const QByteArray &format) const +{ + //### canRead disabled for now because it's hard to detect + // whether the file is actually svg without parsing it + //if (device->isReadable() && QSvgIOHandler::canRead(device)) + + if (format == "svg") + return Capabilities(CanRead); + else + return 0; + + + if (!device->isOpen()) + return 0; + + Capabilities cap; + if (device->isReadable()) + cap |= CanRead; + return cap; +} + +QImageIOHandler *QSvgPlugin::create(QIODevice *device, const QByteArray &format) const +{ + QSvgIOHandler *hand = new QSvgIOHandler(); + hand->setDevice(device); + hand->setFormat(format); + return hand; +} + +Q_EXPORT_STATIC_PLUGIN(QSvgPlugin) +Q_EXPORT_PLUGIN2(qsvg, QSvgPlugin) + +QT_END_NAMESPACE + +#endif // !QT_NO_IMAGEFORMATPLUGIN diff --git a/src/plugins/imageformats/svg/qsvgiohandler.cpp b/src/plugins/imageformats/svg/qsvgiohandler.cpp new file mode 100644 index 0000000..41b247b --- /dev/null +++ b/src/plugins/imageformats/svg/qsvgiohandler.cpp @@ -0,0 +1,191 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsvgiohandler.h" + +#ifndef QT_NO_SVGRENDERER + +#include "qsvgrenderer.h" +#include "qimage.h" +#include "qpixmap.h" +#include "qpainter.h" +#include "qvariant.h" +#include "qdebug.h" + +QT_BEGIN_NAMESPACE + +class QSvgIOHandlerPrivate +{ +public: + QSvgIOHandlerPrivate() + : r(new QSvgRenderer()), loaded(false) + {} + ~QSvgIOHandlerPrivate() + { + delete r; + } + + bool load(QIODevice *device); + + QSvgRenderer *r; + QSize defaultSize; + QSize currentSize; + bool loaded; +}; + +bool QSvgIOHandlerPrivate::load(QIODevice *device) +{ + if (loaded) + return true; + + if (r->load(device->readAll())) { + defaultSize = QSize(r->viewBox().width(), r->viewBox().height()); + if (currentSize.isEmpty()) + currentSize = defaultSize; + } + loaded = r->isValid(); + + return loaded; +} + +QSvgIOHandler::QSvgIOHandler() + : d(new QSvgIOHandlerPrivate()) +{ + +} + + +QSvgIOHandler::~QSvgIOHandler() +{ + delete d; +} + + +bool QSvgIOHandler::canRead() const +{ + QByteArray contents = device()->peek(80); + + return contents.contains("<svg"); +} + + +QByteArray QSvgIOHandler::name() const +{ + return "svg"; +} + + +bool QSvgIOHandler::read(QImage *image) +{ + if (d->load(device())) { + *image = QImage(d->currentSize, QImage::Format_ARGB32_Premultiplied); + if (!d->currentSize.isEmpty()) { + image->fill(0x00000000); + QPainter p(image); + d->r->render(&p); + p.end(); + } + return true; + } + + return false; +} + + +QVariant QSvgIOHandler::option(ImageOption option) const +{ + switch(option) { + case ImageFormat: + return QImage::Format_ARGB32_Premultiplied; + break; + case Size: + d->load(device()); + return d->defaultSize; + break; + case ScaledSize: + return d->currentSize; + break; + default: + break; + } + return QVariant(); +} + + +void QSvgIOHandler::setOption(ImageOption option, const QVariant & value) +{ + switch(option) { + case Size: + d->defaultSize = value.toSize(); + d->currentSize = value.toSize(); + break; + case ScaledSize: + d->currentSize = value.toSize(); + break; + default: + break; + } +} + + +bool QSvgIOHandler::supportsOption(ImageOption option) const +{ + switch(option) + { + case ImageFormat: + case Size: + case ScaledSize: + return true; + default: + break; + } + return false; +} + +bool QSvgIOHandler::canRead(QIODevice *device) +{ + QByteArray contents = device->peek(80); + return contents.contains("<svg"); +} + +QT_END_NAMESPACE + +#endif // QT_NO_SVGRENDERER diff --git a/src/plugins/imageformats/svg/qsvgiohandler.h b/src/plugins/imageformats/svg/qsvgiohandler.h new file mode 100644 index 0000000..a7a5528 --- /dev/null +++ b/src/plugins/imageformats/svg/qsvgiohandler.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSVGIOHANDLER_H +#define QSVGIOHANDLER_H + +#include <QtGui/qimageiohandler.h> + +#ifndef QT_NO_SVGRENDERER + +QT_BEGIN_NAMESPACE + +class QImage; +class QByteArray; +class QIODevice; +class QVariant; +class QSvgIOHandlerPrivate; + +class QSvgIOHandler : public QImageIOHandler +{ +public: + QSvgIOHandler(); + ~QSvgIOHandler(); + virtual bool canRead() const; + virtual QByteArray name() const; + virtual bool read(QImage *image); + static bool canRead(QIODevice *device); + virtual QVariant option(ImageOption option) const; + virtual void setOption(ImageOption option, const QVariant & value); + virtual bool supportsOption(ImageOption option) const; + +private: + QSvgIOHandlerPrivate *d; +}; + +QT_END_NAMESPACE + +#endif // QT_NO_SVGRENDERER +#endif // QSVGIOHANDLER_H diff --git a/src/plugins/imageformats/svg/svg.pro b/src/plugins/imageformats/svg/svg.pro new file mode 100644 index 0000000..747d556 --- /dev/null +++ b/src/plugins/imageformats/svg/svg.pro @@ -0,0 +1,11 @@ +TARGET = qsvg +include(../../qpluginbase.pri) + +HEADERS += qsvgiohandler.h +SOURCES += main.cpp \ + qsvgiohandler.cpp +QT += xml svg + +QTDIR_build:DESTDIR = $$QT_BUILD_TREE/plugins/imageformats +target.path += $$[QT_INSTALL_PLUGINS]/imageformats +INSTALLS += target diff --git a/src/plugins/imageformats/tiff/main.cpp b/src/plugins/imageformats/tiff/main.cpp new file mode 100644 index 0000000..f6590d4 --- /dev/null +++ b/src/plugins/imageformats/tiff/main.cpp @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qimageiohandler.h> +#include <qdebug.h> + +#ifndef QT_NO_IMAGEFORMATPLUGIN + +#ifdef QT_NO_IMAGEFORMAT_TIFF +#undef QT_NO_IMAGEFORMAT_TIFF +#endif +#include "qtiffhandler.h" + +QT_BEGIN_NAMESPACE + +class QTiffPlugin : public QImageIOPlugin +{ +public: + Capabilities capabilities(QIODevice * device, const QByteArray & format) const; + QImageIOHandler * create(QIODevice * device, const QByteArray & format = QByteArray()) const; + QStringList keys() const; +}; + +QImageIOPlugin::Capabilities QTiffPlugin::capabilities(QIODevice *device, const QByteArray &format) const +{ + if (format == "tiff" || format == "tif") + return Capabilities(CanRead | CanWrite); + if (!format.isEmpty()) + return 0; + if (!device->isOpen()) + return 0; + + Capabilities cap; + if (device->isReadable() && QTiffHandler::canRead(device)) + cap |= CanRead; + if (device->isWritable()) + cap |= CanWrite; + return cap; +} + +QImageIOHandler* QTiffPlugin::create(QIODevice *device, const QByteArray &format) const +{ + QImageIOHandler *tiffHandler = new QTiffHandler(); + tiffHandler->setDevice(device); + tiffHandler->setFormat(format); + return tiffHandler; +} + +QStringList QTiffPlugin::keys() const +{ + return QStringList() << QLatin1String("tiff") << QLatin1String("tif"); +} + +Q_EXPORT_STATIC_PLUGIN(QTiffPlugin) +Q_EXPORT_PLUGIN2(qtiff, QTiffPlugin) + +QT_END_NAMESPACE + +#endif /* QT_NO_IMAGEFORMATPLUGIN */ diff --git a/src/plugins/imageformats/tiff/qtiffhandler.cpp b/src/plugins/imageformats/tiff/qtiffhandler.cpp new file mode 100644 index 0000000..518e6d1 --- /dev/null +++ b/src/plugins/imageformats/tiff/qtiffhandler.cpp @@ -0,0 +1,328 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtiffhandler.h" +#include <qvariant.h> +#include <qdebug.h> +#include <qimage.h> +#include <qglobal.h> +extern "C" { +#include "tiffio.h" +} + +QT_BEGIN_NAMESPACE + +tsize_t qtiffReadProc(thandle_t fd, tdata_t buf, tsize_t size) +{ + QIODevice* device = static_cast<QTiffHandler*>(fd)->device(); + return device->isReadable() ? device->read(static_cast<char *>(buf), size) : -1; +} + +tsize_t qtiffWriteProc(thandle_t fd, tdata_t buf, tsize_t size) +{ + return static_cast<QTiffHandler*>(fd)->device()->write(static_cast<char *>(buf), size); +} + +toff_t qtiffSeekProc(thandle_t fd, toff_t off, int whence) +{ + QIODevice *device = static_cast<QTiffHandler*>(fd)->device(); + switch (whence) { + case SEEK_SET: + device->seek(off); + break; + case SEEK_CUR: + device->seek(device->pos() + off); + break; + case SEEK_END: + device->seek(device->size() + off); + break; + } + + return device->pos(); +} + +int qtiffCloseProc(thandle_t /*fd*/) +{ + return 0; +} + +toff_t qtiffSizeProc(thandle_t fd) +{ + return static_cast<QTiffHandler*>(fd)->device()->size(); +} + +int qtiffMapProc(thandle_t /*fd*/, tdata_t* /*pbase*/, toff_t* /*psize*/) +{ + return 0; +} + +void qtiffUnmapProc(thandle_t /*fd*/, tdata_t /*base*/, toff_t /*size*/) +{ +} + +QTiffHandler::QTiffHandler() : QImageIOHandler() +{ + compression = NoCompression; +} + +bool QTiffHandler::canRead() const +{ + if (canRead(device())) { + setFormat("tiff"); + return true; + } + return false; +} + +bool QTiffHandler::canRead(QIODevice *device) +{ + if (!device) { + qWarning("QTiffHandler::canRead() called with no device"); + return false; + } + + // current implementation uses TIFFClientOpen which needs to be + // able to seek, so sequential devices are not supported + QByteArray header = device->peek(4); + return header == QByteArray::fromRawData("\x49\x49\x2A\x00", 4) + || header == QByteArray::fromRawData("\x4D\x4D\x00\x2A", 4); +} + +bool QTiffHandler::read(QImage *image) +{ + if (!canRead()) + return false; + + TIFF *tiff = TIFFClientOpen("foo", + "r", + this, + qtiffReadProc, + qtiffWriteProc, + qtiffSeekProc, + qtiffCloseProc, + qtiffSizeProc, + qtiffMapProc, + qtiffUnmapProc); + + if (tiff) { + uint32 width = 0; + uint32 height = 0; + TIFFGetField(tiff, TIFFTAG_IMAGEWIDTH, &width); + TIFFGetField(tiff, TIFFTAG_IMAGELENGTH, &height); + if (image->size() != QSize(width, height) || image->format() != QImage::Format_ARGB32) + *image = QImage(width, height, QImage::Format_ARGB32); + if (!image->isNull()) { + if (TIFFReadRGBAImageOriented(tiff, width, height, reinterpret_cast<uint32 *>(image->bits()), ORIENTATION_TOPLEFT, 0)) { + uint16 resUnit = RESUNIT_NONE; + float resX = 0; + float resY = 0; + TIFFGetField(tiff, TIFFTAG_RESOLUTIONUNIT, &resUnit); + TIFFGetField(tiff, TIFFTAG_XRESOLUTION, &resX); + TIFFGetField(tiff, TIFFTAG_YRESOLUTION, &resY); + switch(resUnit) { + case RESUNIT_CENTIMETER: + image->setDotsPerMeterX(qRound(resX * 100)); + image->setDotsPerMeterY(qRound(resY * 100)); + break; + case RESUNIT_INCH: + image->setDotsPerMeterX(qRound(resX * (100 / 2.54))); + image->setDotsPerMeterY(qRound(resY * (100 / 2.54))); + break; + default: + // do nothing as defaults have already + // been set within the QImage class + break; + } + for (uint32 y=0; y<height; ++y) + convert32BitOrder(image->scanLine(y), width); + } else { + *image = QImage(); + } + } + TIFFClose(tiff); + } + + if (image->isNull()) + return false; + + return true; +} + +bool QTiffHandler::write(const QImage &image) +{ + if (!device()->isWritable()) + return false; + + TIFF *tiff = TIFFClientOpen("foo", + "w", + this, + qtiffReadProc, + qtiffWriteProc, + qtiffSeekProc, + qtiffCloseProc, + qtiffSizeProc, + qtiffMapProc, + qtiffUnmapProc); + + if (tiff) { + int width = image.width(); + int height = image.height(); + int depth = 32; + + if (!TIFFSetField(tiff, TIFFTAG_IMAGEWIDTH, width) + || !TIFFSetField(tiff, TIFFTAG_IMAGELENGTH, height) + || !TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB) + || !TIFFSetField(tiff, TIFFTAG_COMPRESSION, compression == NoCompression ? COMPRESSION_NONE : COMPRESSION_LZW) + || !TIFFSetField(tiff, TIFFTAG_SAMPLESPERPIXEL, depth/8) + || !TIFFSetField(tiff, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG) + || !TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, 8)) { + TIFFClose(tiff); + return false; + } + + // try to do the ARGB32 conversion in chunks no greater than 16 MB + int chunks = (width * height * 4 / (1024 * 1024 * 16)) + 1; + int chunkHeight = qMax(height / chunks, 1); + + int y = 0; + while (y < height) { + QImage chunk = image.copy(0, y, width, qMin(chunkHeight, height - y)).convertToFormat(QImage::Format_ARGB32); + + int chunkStart = y; + int chunkEnd = y + chunk.height(); + while (y < chunkEnd) { + if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) + convert32BitOrder(chunk.scanLine(y - chunkStart), width); + else + convert32BitOrderBigEndian(chunk.scanLine(y - chunkStart), width); + + if (TIFFWriteScanline(tiff, reinterpret_cast<uint32 *>(chunk.scanLine(y - chunkStart)), y) != 1) { + TIFFClose(tiff); + return false; + } + ++y; + } + } + TIFFClose(tiff); + } else { + return false; + } + return true; +} + +QByteArray QTiffHandler::name() const +{ + return "tiff"; +} + +QVariant QTiffHandler::option(ImageOption option) const +{ + if (option == Size && canRead()) { + QSize imageSize; + qint64 pos = device()->pos(); + TIFF *tiff = TIFFClientOpen("foo", + "r", + const_cast<QTiffHandler*>(this), + qtiffReadProc, + qtiffWriteProc, + qtiffSeekProc, + qtiffCloseProc, + qtiffSizeProc, + qtiffMapProc, + qtiffUnmapProc); + + if (tiff) { + uint32 width = 0; + uint32 height = 0; + TIFFGetField(tiff, TIFFTAG_IMAGEWIDTH, &width); + TIFFGetField(tiff, TIFFTAG_IMAGELENGTH, &height); + imageSize = QSize(width, height); + } + device()->seek(pos); + if (imageSize.isValid()) + return imageSize; + } else if (option == CompressionRatio) { + return compression; + } else if (option == ImageFormat) { + return QImage::Format_ARGB32; + } + return QVariant(); +} + +void QTiffHandler::setOption(ImageOption option, const QVariant &value) +{ + if (option == CompressionRatio && value.type() == QVariant::Int) + compression = value.toInt(); +} + +bool QTiffHandler::supportsOption(ImageOption option) const +{ + return option == CompressionRatio + || option == Size + || option == ImageFormat; +} + +void QTiffHandler::convert32BitOrder(void *buffer, int width) +{ + uint32 *target = reinterpret_cast<uint32 *>(buffer); + for (int32 x=0; x<width; ++x) { + uint32 p = target[x]; + // convert between ARGB and ABGR + target[x] = (p & 0xff000000) + | ((p & 0x00ff0000) >> 16) + | (p & 0x0000ff00) + | ((p & 0x000000ff) << 16); + } +} + +void QTiffHandler::convert32BitOrderBigEndian(void *buffer, int width) +{ + uint32 *target = reinterpret_cast<uint32 *>(buffer); + for (int32 x=0; x<width; ++x) { + uint32 p = target[x]; + target[x] = (p & 0xff000000) >> 24 + | (p & 0x00ff0000) << 8 + | (p & 0x0000ff00) << 8 + | (p & 0x000000ff) << 8; + } +} + +QT_END_NAMESPACE diff --git a/src/plugins/imageformats/tiff/qtiffhandler.h b/src/plugins/imageformats/tiff/qtiffhandler.h new file mode 100644 index 0000000..0c0d9ef --- /dev/null +++ b/src/plugins/imageformats/tiff/qtiffhandler.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTIFFHANDLER_H +#define QTIFFHANDLER_H + +#include <QtGui/qimageiohandler.h> + +QT_BEGIN_NAMESPACE + +class QTiffHandler : public QImageIOHandler +{ +public: + QTiffHandler(); + + bool canRead() const; + bool read(QImage *image); + bool write(const QImage &image); + + QByteArray name() const; + + static bool canRead(QIODevice *device); + + QVariant option(ImageOption option) const; + void setOption(ImageOption option, const QVariant &value); + bool supportsOption(ImageOption option) const; + + enum Compression { + NoCompression = 0, + LzwCompression = 1 + }; +private: + void convert32BitOrder(void *buffer, int width); + void convert32BitOrderBigEndian(void *buffer, int width); + int compression; +}; + +QT_END_NAMESPACE + +#endif // QTIFFHANDLER_H diff --git a/src/plugins/imageformats/tiff/tiff.pro b/src/plugins/imageformats/tiff/tiff.pro new file mode 100644 index 0000000..8930cf3 --- /dev/null +++ b/src/plugins/imageformats/tiff/tiff.pro @@ -0,0 +1,70 @@ +TARGET = qtiff +include(../../qpluginbase.pri) + +QTDIR_build:REQUIRES = "!contains(QT_CONFIG, no-tiff)" + +HEADERS += qtiffhandler.h +SOURCES += main.cpp \ + qtiffhandler.cpp + +contains(QT_CONFIG, system-tiff) { + unix:LIBS += -ltiff + win32:LIBS += libtiff.lib +} +!contains(QT_CONFIG, system-tiff) { + INCLUDEPATH += ../../../3rdparty/libtiff/libtiff + SOURCES += \ + ../../../3rdparty/libtiff/libtiff/tif_aux.c \ + ../../../3rdparty/libtiff/libtiff/tif_close.c \ + ../../../3rdparty/libtiff/libtiff/tif_codec.c \ + ../../../3rdparty/libtiff/libtiff/tif_color.c \ + ../../../3rdparty/libtiff/libtiff/tif_compress.c \ + ../../../3rdparty/libtiff/libtiff/tif_dir.c \ + ../../../3rdparty/libtiff/libtiff/tif_dirinfo.c \ + ../../../3rdparty/libtiff/libtiff/tif_dirread.c \ + ../../../3rdparty/libtiff/libtiff/tif_dirwrite.c \ + ../../../3rdparty/libtiff/libtiff/tif_dumpmode.c \ + ../../../3rdparty/libtiff/libtiff/tif_error.c \ + ../../../3rdparty/libtiff/libtiff/tif_extension.c \ + ../../../3rdparty/libtiff/libtiff/tif_fax3.c \ + ../../../3rdparty/libtiff/libtiff/tif_fax3sm.c \ + ../../../3rdparty/libtiff/libtiff/tif_flush.c \ + ../../../3rdparty/libtiff/libtiff/tif_getimage.c \ + ../../../3rdparty/libtiff/libtiff/tif_luv.c \ + ../../../3rdparty/libtiff/libtiff/tif_lzw.c \ + ../../../3rdparty/libtiff/libtiff/tif_next.c \ + ../../../3rdparty/libtiff/libtiff/tif_open.c \ + ../../../3rdparty/libtiff/libtiff/tif_packbits.c \ + ../../../3rdparty/libtiff/libtiff/tif_pixarlog.c \ + ../../../3rdparty/libtiff/libtiff/tif_predict.c \ + ../../../3rdparty/libtiff/libtiff/tif_print.c \ + ../../../3rdparty/libtiff/libtiff/tif_read.c \ + ../../../3rdparty/libtiff/libtiff/tif_strip.c \ + ../../../3rdparty/libtiff/libtiff/tif_swab.c \ + ../../../3rdparty/libtiff/libtiff/tif_thunder.c \ + ../../../3rdparty/libtiff/libtiff/tif_tile.c \ + ../../../3rdparty/libtiff/libtiff/tif_version.c \ + ../../../3rdparty/libtiff/libtiff/tif_warning.c \ + ../../../3rdparty/libtiff/libtiff/tif_write.c \ + ../../../3rdparty/libtiff/libtiff/tif_zip.c + win32 { + SOURCES += ../../../3rdparty/libtiff/libtiff/tif_win32.c + } + unix: { + SOURCES += ../../../3rdparty/libtiff/libtiff/tif_unix.c + } + wince*: { + SOURCES += ../../../corelib/kernel/qfunctions_wince.cpp + } +} + +contains(QT_CONFIG, system-zlib) { + LIBS += -lz +} +!contains(QT_CONFIG, system-zlib) { + INCLUDEPATH += ../../../3rdparty/zlib +} + +QTDIR_build:DESTDIR = $$QT_BUILD_TREE/plugins/imageformats +target.path += $$[QT_INSTALL_PLUGINS]/imageformats +INSTALLS += target |