summaryrefslogtreecommitdiffstats
path: root/src/plugins/imageformats
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@nokia.com>2009-03-23 10:18:55 +0100
committerSimon Hausmann <simon.hausmann@nokia.com>2009-03-23 10:18:55 +0100
commite5fcad302d86d316390c6b0f62759a067313e8a9 (patch)
treec2afbf6f1066b6ce261f14341cf6d310e5595bc1 /src/plugins/imageformats
Long live Qt 4.5!
Diffstat (limited to 'src/plugins/imageformats')
-rw-r--r--src/plugins/imageformats/gif/gif.pro10
-rw-r--r--src/plugins/imageformats/gif/main.cpp98
-rw-r--r--src/plugins/imageformats/gif/qgifhandler.cpp892
-rw-r--r--src/plugins/imageformats/gif/qgifhandler.h95
-rw-r--r--src/plugins/imageformats/ico/ico.pro12
-rw-r--r--src/plugins/imageformats/ico/main.cpp96
-rw-r--r--src/plugins/imageformats/ico/qicohandler.cpp862
-rw-r--r--src/plugins/imageformats/ico/qicohandler.h72
-rw-r--r--src/plugins/imageformats/imageformats.pro8
-rw-r--r--src/plugins/imageformats/jpeg/jpeg.pro73
-rw-r--r--src/plugins/imageformats/jpeg/main.cpp97
-rw-r--r--src/plugins/imageformats/jpeg/qjpeghandler.cpp1264
-rw-r--r--src/plugins/imageformats/jpeg/qjpeghandler.h75
-rw-r--r--src/plugins/imageformats/mng/main.cpp98
-rw-r--r--src/plugins/imageformats/mng/mng.pro49
-rw-r--r--src/plugins/imageformats/mng/qmnghandler.cpp500
-rw-r--r--src/plugins/imageformats/mng/qmnghandler.h82
-rw-r--r--src/plugins/imageformats/svg/main.cpp102
-rw-r--r--src/plugins/imageformats/svg/qsvgiohandler.cpp191
-rw-r--r--src/plugins/imageformats/svg/qsvgiohandler.h77
-rw-r--r--src/plugins/imageformats/svg/svg.pro11
-rw-r--r--src/plugins/imageformats/tiff/main.cpp97
-rw-r--r--src/plugins/imageformats/tiff/qtiffhandler.cpp328
-rw-r--r--src/plugins/imageformats/tiff/qtiffhandler.h78
-rw-r--r--src/plugins/imageformats/tiff/tiff.pro70
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 0000000000..74586b2c20
--- /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 0000000000..075757b854
--- /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 0000000000..495794eb47
--- /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 0000000000..c56c3abd15
--- /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 0000000000..73665cd729
--- /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 0000000000..5685c4ed56
--- /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 0000000000..299190b186
--- /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 0000000000..0bba208723
--- /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 0000000000..2e4036b90d
--- /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 0000000000..f3109024cb
--- /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 0000000000..e4e2892bad
--- /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 0000000000..6d0bc1f15a
--- /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 &parameters, 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))
+ longjmp(jerr.setjmp_buffer, 1);
+ } 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())
+ longjmp(jerr.setjmp_buffer, 1);
+
+ 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))
+ longjmp(jerr.setjmp_buffer, 1);
+
+ 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 0000000000..2a8debcbda
--- /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 0000000000..60936a7ad7
--- /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 0000000000..f0943f5580
--- /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 0000000000..19cc16d3e5
--- /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 0000000000..909be2daa7
--- /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 0000000000..978ae40942
--- /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 0000000000..41b247bb32
--- /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 0000000000..a7a55283f4
--- /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 0000000000..747d556216
--- /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 0000000000..f6590d47a2
--- /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 0000000000..518e6d19b9
--- /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 0000000000..0c0d9ef1dd
--- /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 0000000000..8930cf3c25
--- /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