summaryrefslogtreecommitdiffstats
path: root/src/gui/image
diff options
context:
space:
mode:
authorQt by Nokia <qt-info@nokia.com>2011-04-27 12:05:43 +0200
committeraxis <qt-info@nokia.com>2011-04-27 12:05:43 +0200
commit38be0d13830efd2d98281c645c3a60afe05ffece (patch)
tree6ea73f3ec77f7d153333779883e8120f82820abe /src/gui/image
Initial import from the monolithic Qt.
This is the beginning of revision history for this module. If you want to look at revision history older than this, please refer to the Qt Git wiki for how to use Git history grafting. At the time of writing, this wiki is located here: http://qt.gitorious.org/qt/pages/GitIntroductionWithQt If you have already performed the grafting and you don't see any history beyond this commit, try running "git log" with the "--follow" argument. Branched from the monolithic repo, Qt master branch, at commit 896db169ea224deb96c59ce8af800d019de63f12
Diffstat (limited to 'src/gui/image')
-rw-r--r--src/gui/image/image.pri115
-rw-r--r--src/gui/image/qbitmap.cpp411
-rw-r--r--src/gui/image/qbitmap.h109
-rw-r--r--src/gui/image/qbmphandler.cpp837
-rw-r--r--src/gui/image/qbmphandler_p.h117
-rw-r--r--src/gui/image/qgifhandler.cpp1214
-rw-r--r--src/gui/image/qgifhandler.pri4
-rw-r--r--src/gui/image/qgifhandler_p.h96
-rw-r--r--src/gui/image/qicon.cpp1258
-rw-r--r--src/gui/image/qicon.h162
-rw-r--r--src/gui/image/qicon_p.h139
-rw-r--r--src/gui/image/qiconengine.cpp324
-rw-r--r--src/gui/image/qiconengine.h104
-rw-r--r--src/gui/image/qiconengineplugin.cpp171
-rw-r--r--src/gui/image/qiconengineplugin.h104
-rw-r--r--src/gui/image/qiconloader.cpp570
-rw-r--r--src/gui/image/qiconloader_p.h192
-rw-r--r--src/gui/image/qimage.cpp6717
-rw-r--r--src/gui/image/qimage.h374
-rw-r--r--src/gui/image/qimage_neon.cpp114
-rw-r--r--src/gui/image/qimage_p.h154
-rw-r--r--src/gui/image/qimage_sse2.cpp109
-rw-r--r--src/gui/image/qimage_ssse3.cpp149
-rw-r--r--src/gui/image/qimageiohandler.cpp570
-rw-r--r--src/gui/image/qimageiohandler.h152
-rw-r--r--src/gui/image/qimagepixmapcleanuphooks.cpp163
-rw-r--r--src/gui/image/qimagepixmapcleanuphooks_p.h103
-rw-r--r--src/gui/image/qimagereader.cpp1515
-rw-r--r--src/gui/image/qimagereader.h147
-rw-r--r--src/gui/image/qimagewriter.cpp734
-rw-r--r--src/gui/image/qimagewriter.h116
-rw-r--r--src/gui/image/qjpeghandler.cpp915
-rw-r--r--src/gui/image/qjpeghandler.pri10
-rw-r--r--src/gui/image/qjpeghandler_p.h76
-rw-r--r--src/gui/image/qmnghandler.cpp497
-rw-r--r--src/gui/image/qmnghandler.pri10
-rw-r--r--src/gui/image/qmnghandler_p.h83
-rw-r--r--src/gui/image/qmovie.cpp1089
-rw-r--r--src/gui/image/qmovie.h177
-rw-r--r--src/gui/image/qnativeimage.cpp315
-rw-r--r--src/gui/image/qnativeimage_p.h109
-rw-r--r--src/gui/image/qnativeimagehandleprovider_p.h69
-rw-r--r--src/gui/image/qpaintengine_pic.cpp539
-rw-r--r--src/gui/image/qpaintengine_pic_p.h122
-rw-r--r--src/gui/image/qpicture.cpp1999
-rw-r--r--src/gui/image/qpicture.h204
-rw-r--r--src/gui/image/qpicture_p.h169
-rw-r--r--src/gui/image/qpictureformatplugin.cpp139
-rw-r--r--src/gui/image/qpictureformatplugin.h94
-rw-r--r--src/gui/image/qpixmap.cpp2297
-rw-r--r--src/gui/image/qpixmap.h335
-rw-r--r--src/gui/image/qpixmap_blitter.cpp310
-rw-r--r--src/gui/image/qpixmap_blitter_p.h166
-rw-r--r--src/gui/image/qpixmap_mac.cpp1195
-rw-r--r--src/gui/image/qpixmap_mac_p.h134
-rw-r--r--src/gui/image/qpixmap_qpa.cpp49
-rw-r--r--src/gui/image/qpixmap_qws.cpp160
-rw-r--r--src/gui/image/qpixmap_raster.cpp492
-rw-r--r--src/gui/image/qpixmap_raster_p.h107
-rw-r--r--src/gui/image/qpixmap_s60.cpp1040
-rw-r--r--src/gui/image/qpixmap_s60_p.h141
-rw-r--r--src/gui/image/qpixmap_win.cpp477
-rw-r--r--src/gui/image/qpixmap_x11.cpp2419
-rw-r--r--src/gui/image/qpixmap_x11_p.h156
-rw-r--r--src/gui/image/qpixmapcache.cpp680
-rw-r--r--src/gui/image/qpixmapcache.h102
-rw-r--r--src/gui/image/qpixmapcache_p.h103
-rw-r--r--src/gui/image/qpixmapdata.cpp290
-rw-r--r--src/gui/image/qpixmapdata_p.h180
-rw-r--r--src/gui/image/qpixmapdatafactory.cpp115
-rw-r--r--src/gui/image/qpixmapdatafactory_p.h81
-rw-r--r--src/gui/image/qpixmapfilter.cpp1382
-rw-r--r--src/gui/image/qpixmapfilter_p.h196
-rw-r--r--src/gui/image/qpnghandler.cpp991
-rw-r--r--src/gui/image/qpnghandler.pri10
-rw-r--r--src/gui/image/qpnghandler_p.h88
-rw-r--r--src/gui/image/qppmhandler.cpp533
-rw-r--r--src/gui/image/qppmhandler_p.h98
-rw-r--r--src/gui/image/qtiffhandler.cpp665
-rw-r--r--src/gui/image/qtiffhandler.pri10
-rw-r--r--src/gui/image/qtiffhandler_p.h78
-rw-r--r--src/gui/image/qvolatileimage.cpp318
-rw-r--r--src/gui/image/qvolatileimage_p.h101
-rw-r--r--src/gui/image/qvolatileimagedata.cpp114
-rw-r--r--src/gui/image/qvolatileimagedata_p.h97
-rw-r--r--src/gui/image/qvolatileimagedata_symbian.cpp474
-rw-r--r--src/gui/image/qxbmhandler.cpp361
-rw-r--r--src/gui/image/qxbmhandler_p.h95
-rw-r--r--src/gui/image/qxpmhandler.cpp1295
-rw-r--r--src/gui/image/qxpmhandler_p.h100
90 files changed, 41695 insertions, 0 deletions
diff --git a/src/gui/image/image.pri b/src/gui/image/image.pri
new file mode 100644
index 0000000000..72738c9fa8
--- /dev/null
+++ b/src/gui/image/image.pri
@@ -0,0 +1,115 @@
+# -*-mode:sh-*-
+# Qt image handling
+
+# Qt kernel module
+
+HEADERS += \
+ image/qbitmap.h \
+ image/qicon.h \
+ image/qicon_p.h \
+ image/qiconloader_p.h \
+ image/qiconengine.h \
+ image/qiconengineplugin.h \
+ image/qimage.h \
+ image/qimage_p.h \
+ image/qimageiohandler.h \
+ image/qimagereader.h \
+ image/qimagewriter.h \
+ image/qmovie.h \
+ image/qnativeimage_p.h \
+ image/qpaintengine_pic_p.h \
+ image/qpicture.h \
+ image/qpicture_p.h \
+ image/qpictureformatplugin.h \
+ image/qpixmap.h \
+ image/qpixmap_raster_p.h \
+ image/qpixmap_blitter_p.h \
+ image/qpixmapcache.h \
+ image/qpixmapcache_p.h \
+ image/qpixmapdata_p.h \
+ image/qpixmapdatafactory_p.h \
+ image/qpixmapfilter_p.h \
+ image/qimagepixmapcleanuphooks_p.h \
+ image/qvolatileimage_p.h \
+ image/qvolatileimagedata_p.h \
+ image/qnativeimagehandleprovider_p.h
+
+SOURCES += \
+ image/qbitmap.cpp \
+ image/qicon.cpp \
+ image/qiconloader.cpp \
+ image/qimage.cpp \
+ image/qimageiohandler.cpp \
+ image/qimagereader.cpp \
+ image/qimagewriter.cpp \
+ image/qpaintengine_pic.cpp \
+ image/qpicture.cpp \
+ image/qpictureformatplugin.cpp \
+ image/qpixmap.cpp \
+ image/qpixmapcache.cpp \
+ image/qpixmapdata.cpp \
+ image/qpixmapdatafactory.cpp \
+ image/qpixmapfilter.cpp \
+ image/qiconengine.cpp \
+ image/qiconengineplugin.cpp \
+ image/qmovie.cpp \
+ image/qpixmap_raster.cpp \
+ image/qpixmap_blitter.cpp \
+ image/qnativeimage.cpp \
+ image/qimagepixmapcleanuphooks.cpp \
+ image/qvolatileimage.cpp
+
+win32 {
+ SOURCES += image/qpixmap_win.cpp
+}
+else:embedded {
+ SOURCES += image/qpixmap_qws.cpp
+}
+else:qpa {
+ SOURCES += image/qpixmap_qpa.cpp
+}
+else:x11 {
+ HEADERS += image/qpixmap_x11_p.h
+ SOURCES += image/qpixmap_x11.cpp
+}
+else:mac {
+ HEADERS += image/qpixmap_mac_p.h
+ SOURCES += image/qpixmap_mac.cpp
+}
+else:symbian {
+ HEADERS += image/qpixmap_s60_p.h
+ SOURCES += image/qpixmap_s60.cpp
+}
+
+!symbian|contains(S60_VERSION, 3.1)|contains(S60_VERSION, 3.2) {
+ SOURCES += image/qvolatileimagedata.cpp
+}
+else {
+ SOURCES += image/qvolatileimagedata_symbian.cpp
+}
+
+# Built-in image format support
+HEADERS += \
+ image/qbmphandler_p.h \
+ image/qppmhandler_p.h \
+ image/qxbmhandler_p.h \
+ image/qxpmhandler_p.h
+
+SOURCES += \
+ image/qbmphandler.cpp \
+ image/qppmhandler.cpp \
+ image/qxbmhandler.cpp \
+ image/qxpmhandler.cpp
+
+!contains(QT_CONFIG, no-png):include($$PWD/qpnghandler.pri)
+else:DEFINES *= QT_NO_IMAGEFORMAT_PNG
+
+contains(QT_CONFIG, jpeg):include($$PWD/qjpeghandler.pri)
+contains(QT_CONFIG, mng):include($$PWD/qmnghandler.pri)
+contains(QT_CONFIG, tiff):include($$PWD/qtiffhandler.pri)
+contains(QT_CONFIG, gif):include($$PWD/qgifhandler.pri)
+
+# SIMD
+NEON_SOURCES += image/qimage_neon.cpp
+SSE2_SOURCES += image/qimage_sse2.cpp
+SSSE3_SOURCES += image/qimage_ssse3.cpp
diff --git a/src/gui/image/qbitmap.cpp b/src/gui/image/qbitmap.cpp
new file mode 100644
index 0000000000..260b397140
--- /dev/null
+++ b/src/gui/image/qbitmap.cpp
@@ -0,0 +1,411 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qbitmap.h"
+#include "qpixmapdata_p.h"
+#include "qimage.h"
+#include "qvariant.h"
+#include <qpainter.h>
+#include <private/qgraphicssystem_p.h>
+#include <private/qapplication_p.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QBitmap
+ \brief The QBitmap class provides monochrome (1-bit depth) pixmaps.
+
+ \ingroup painting
+ \ingroup shared
+
+ The QBitmap class is a monochrome off-screen paint device used
+ mainly for creating custom QCursor and QBrush objects,
+ constructing QRegion objects, and for setting masks for pixmaps
+ and widgets.
+
+ QBitmap is a QPixmap subclass ensuring a depth of 1, except for
+ null objects which have a depth of 0. If a pixmap with a depth
+ greater than 1 is assigned to a bitmap, the bitmap will be
+ dithered automatically.
+
+ Use the QColor objects Qt::color0 and Qt::color1 when drawing on a
+ QBitmap object (or a QPixmap object with depth 1).
+
+ Painting with Qt::color0 sets the bitmap bits to 0, and painting
+ with Qt::color1 sets the bits to 1. For a bitmap, 0-bits indicate
+ background (or transparent pixels) and 1-bits indicate foreground
+ (or opaque pixels). Use the clear() function to set all the bits
+ to Qt::color0. Note that using the Qt::black and Qt::white colors
+ make no sense because the QColor::pixel() value is not necessarily
+ 0 for black and 1 for white.
+
+ The QBitmap class provides the transformed() function returning a
+ transformed copy of the bitmap; use the QTransform argument to
+ translate, scale, shear, and rotate the bitmap. In addition,
+ QBitmap provides the static fromData() function which returns a
+ bitmap constructed from the given \c uchar data, and the static
+ fromImage() function returning a converted copy of a QImage
+ object.
+
+ Just like the QPixmap class, QBitmap is optimized by the use of
+ implicit data sharing. For more information, see the \l {Implicit
+ Data Sharing} documentation.
+
+ \sa QPixmap, QImage, QImageReader, QImageWriter
+*/
+
+/*! \typedef QBitmap::DataPtr
+ \internal
+ */
+
+/*!
+ Constructs a null bitmap.
+
+ \sa QPixmap::isNull()
+*/
+QBitmap::QBitmap()
+ : QPixmap(QSize(0, 0), QPixmapData::BitmapType)
+{
+}
+
+/*!
+ \fn QBitmap::QBitmap(int width, int height)
+
+ Constructs a bitmap with the given \a width and \a height. The pixels
+ inside are uninitialized.
+
+ \sa clear()
+*/
+
+QBitmap::QBitmap(int w, int h)
+ : QPixmap(QSize(w, h), QPixmapData::BitmapType)
+{
+}
+
+/*!
+ Constructs a bitmap with the given \a size. The pixels in the
+ bitmap are uninitialized.
+
+ \sa clear()
+*/
+
+QBitmap::QBitmap(const QSize &size)
+ : QPixmap(size, QPixmapData::BitmapType)
+{
+}
+
+/*!
+ \fn QBitmap::clear()
+
+ Clears the bitmap, setting all its bits to Qt::color0.
+*/
+
+/*!
+ Constructs a bitmap that is a copy of the given \a pixmap.
+
+ If the pixmap has a depth greater than 1, the resulting bitmap
+ will be dithered automatically.
+
+ \sa QPixmap::depth(), fromImage(), fromData()
+*/
+
+QBitmap::QBitmap(const QPixmap &pixmap)
+{
+ QBitmap::operator=(pixmap);
+}
+
+/*!
+ \fn QBitmap::QBitmap(const QImage &image)
+
+ Constructs a bitmap that is a copy of the given \a image.
+
+ Use the static fromImage() function instead.
+*/
+
+/*!
+ Constructs a bitmap from the file specified by the given \a
+ fileName. If the file does not exist, or has an unknown format,
+ the bitmap becomes a null bitmap.
+
+ The \a fileName and \a format parameters are passed on to the
+ QPixmap::load() function. If the file format uses more than 1 bit
+ per pixel, the resulting bitmap will be dithered automatically.
+
+ \sa QPixmap::isNull(), QImageReader::imageFormat()
+*/
+
+QBitmap::QBitmap(const QString& fileName, const char *format)
+ : QPixmap(QSize(0, 0), QPixmapData::BitmapType)
+{
+ load(fileName, format, Qt::MonoOnly);
+}
+
+/*!
+ \overload
+
+ Assigns the given \a pixmap to this bitmap and returns a reference
+ to this bitmap.
+
+ If the pixmap has a depth greater than 1, the resulting bitmap
+ will be dithered automatically.
+
+ \sa QPixmap::depth()
+ */
+
+QBitmap &QBitmap::operator=(const QPixmap &pixmap)
+{
+ if (pixmap.isNull()) { // a null pixmap
+ QBitmap bm(0, 0);
+ QBitmap::operator=(bm);
+ } else if (pixmap.depth() == 1) { // 1-bit pixmap
+ QPixmap::operator=(pixmap); // shallow assignment
+ } else { // n-bit depth pixmap
+ QImage image;
+ image = pixmap.toImage(); // convert pixmap to image
+ *this = fromImage(image); // will dither image
+ }
+ return *this;
+}
+
+
+#ifdef QT3_SUPPORT
+QBitmap::QBitmap(int w, int h, const uchar *bits, bool isXbitmap)
+{
+ *this = fromData(QSize(w, h), bits, isXbitmap ? QImage::Format_MonoLSB : QImage::Format_Mono);
+}
+
+
+QBitmap::QBitmap(const QSize &size, const uchar *bits, bool isXbitmap)
+{
+ *this = fromData(size, bits, isXbitmap ? QImage::Format_MonoLSB : QImage::Format_Mono);
+}
+#endif
+
+/*!
+ Destroys the bitmap.
+*/
+QBitmap::~QBitmap()
+{
+}
+
+/*!
+ \fn void QBitmap::swap(QBitmap &other)
+ \since 4.8
+
+ Swaps bitmap \a other with this bitmap. This operation is very
+ fast and never fails.
+*/
+
+/*!
+ Returns the bitmap as a QVariant.
+*/
+QBitmap::operator QVariant() const
+{
+ return QVariant(QVariant::Bitmap, this);
+}
+
+/*!
+ \fn QBitmap &QBitmap::operator=(const QImage &image)
+ \overload
+
+ Converts the given \a image to a bitmap, and assigns the result to
+ this bitmap. Returns a reference to the bitmap.
+
+ Use the static fromImage() function instead.
+*/
+
+/*!
+ Returns a copy of the given \a image converted to a bitmap using
+ the specified image conversion \a flags.
+
+ \sa fromData()
+*/
+QBitmap QBitmap::fromImage(const QImage &image, Qt::ImageConversionFlags flags)
+{
+ if (image.isNull())
+ return QBitmap();
+
+ QImage img = image.convertToFormat(QImage::Format_MonoLSB, flags);
+
+ // make sure image.color(0) == Qt::color0 (white)
+ // and image.color(1) == Qt::color1 (black)
+ const QRgb c0 = QColor(Qt::black).rgb();
+ const QRgb c1 = QColor(Qt::white).rgb();
+ if (img.color(0) == c0 && img.color(1) == c1) {
+ img.invertPixels();
+ img.setColor(0, c1);
+ img.setColor(1, c0);
+ }
+
+ QGraphicsSystem* gs = QApplicationPrivate::graphicsSystem();
+ QScopedPointer<QPixmapData> data(gs ? gs->createPixmapData(QPixmapData::BitmapType)
+ : QGraphicsSystem::createDefaultPixmapData(QPixmapData::BitmapType));
+
+ data->fromImage(img, flags | Qt::MonoOnly);
+ return QPixmap(data.take());
+}
+
+/*!
+ Constructs a bitmap with the given \a size, and sets the contents to
+ the \a bits supplied.
+
+ The bitmap data has to be byte aligned and provided in in the bit
+ order specified by \a monoFormat. The mono format must be either
+ QImage::Format_Mono or QImage::Format_MonoLSB. Use
+ QImage::Format_Mono to specify data on the XBM format.
+
+ \sa fromImage()
+
+*/
+QBitmap QBitmap::fromData(const QSize &size, const uchar *bits, QImage::Format monoFormat)
+{
+ Q_ASSERT(monoFormat == QImage::Format_Mono || monoFormat == QImage::Format_MonoLSB);
+
+ QImage image(size, monoFormat);
+ image.setColor(0, QColor(Qt::color0).rgb());
+ image.setColor(1, QColor(Qt::color1).rgb());
+
+ // Need to memcpy each line separatly since QImage is 32bit aligned and
+ // this data is only byte aligned...
+ int bytesPerLine = (size.width() + 7) / 8;
+ for (int y = 0; y < size.height(); ++y)
+ memcpy(image.scanLine(y), bits + bytesPerLine * y, bytesPerLine);
+ return QBitmap::fromImage(image);
+}
+
+/*!
+ Returns a copy of this bitmap, transformed according to the given
+ \a matrix.
+
+ \sa QPixmap::transformed()
+ */
+QBitmap QBitmap::transformed(const QTransform &matrix) const
+{
+ QBitmap bm = QPixmap::transformed(matrix);
+ return bm;
+}
+
+/*!
+ \overload
+ \obsolete
+
+ This convenience function converts the \a matrix to a QTransform
+ and calls the overloaded function.
+*/
+QBitmap QBitmap::transformed(const QMatrix &matrix) const
+{
+ return transformed(QTransform(matrix));
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ \fn QBitmap QBitmap::xForm(const QMatrix &matrix) const
+
+ Returns a copy of this bitmap, transformed according to the given
+ \a matrix.
+
+ Use transformed() instead.
+*/
+
+/*!
+ \fn QBitmap::QBitmap(const QSize &size, bool clear)
+
+ Constructs a bitmap with the given \a size. If \a clear is true,
+ the bits are initialized to Qt::color0.
+
+ Use the corresponding QBitmap() constructor instead, and then call
+ the clear() function if the \a clear parameter is true.
+*/
+
+/*!
+ \fn QBitmap::QBitmap(int width, int height, bool clear)
+
+ Constructs a bitmap with the given \a width and \a height. If \a
+ clear is true, the bits are initialized to Qt::color0.
+
+ Use the corresponding QBitmap() constructor instead, and then call
+ the clear() function if the \a clear parameter is true.
+*/
+
+/*!
+ \fn QBitmap::QBitmap(int width, int height, const uchar *bits, bool isXbitmap)
+
+ Constructs a bitmap with the given \a width and \a height, and
+ sets the contents to the \a bits supplied. The \a isXbitmap flag
+ should be true if \a bits was generated by the X11 bitmap
+ program.
+
+ Use the static fromData() function instead. If \a isXbitmap is
+ true, use the default bit order(QImage_FormatMonoLSB) otherwise
+ use QImage::Format_Mono.
+
+ \omit
+ The X bitmap bit order is little endian. The QImage
+ documentation discusses bit order of monochrome images. Opposed to
+ QImage, the data has to be byte aligned.
+
+ Example (creates an arrow bitmap):
+ \snippet doc/src/snippets/code/src_gui_image_qbitmap.cpp 0
+ \endomit
+*/
+
+
+/*!
+ \fn QBitmap::QBitmap(const QSize &size, const uchar *bits, bool isXbitmap)
+
+ \overload
+
+ Constructs a bitmap with the given \a size, and sets the contents
+ to the \a bits supplied. The \a isXbitmap flag should be true if
+ \a bits was generated by the X11 bitmap program.
+
+ \omit
+ The X bitmap bit order is little endian. The QImage documentation
+ discusses bit order of monochrome images.
+ \endomit
+
+ Use the static fromData() function instead. If \a isXbitmap is
+ true, use the default bit order(QImage_FormatMonoLSB) otherwise
+ use QImage::Format_Mono.
+*/
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/gui/image/qbitmap.h b/src/gui/image/qbitmap.h
new file mode 100644
index 0000000000..f1771e3e43
--- /dev/null
+++ b/src/gui/image/qbitmap.h
@@ -0,0 +1,109 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QBITMAP_H
+#define QBITMAP_H
+
+#include <QtGui/qpixmap.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QVariant;
+
+class Q_GUI_EXPORT QBitmap : public QPixmap
+{
+public:
+ QBitmap();
+ QBitmap(const QPixmap &);
+ QBitmap(int w, int h);
+ explicit QBitmap(const QSize &);
+ explicit QBitmap(const QString &fileName, const char *format=0);
+ ~QBitmap();
+
+ QBitmap &operator=(const QPixmap &);
+ inline void swap(QBitmap &other) { QPixmap::swap(other); } // prevent QBitmap<->QPixmap swaps
+ operator QVariant() const;
+
+ inline void clear() { fill(Qt::color0); }
+
+ static QBitmap fromImage(const QImage &image, Qt::ImageConversionFlags flags = Qt::AutoColor);
+ static QBitmap fromData(const QSize &size, const uchar *bits,
+ QImage::Format monoFormat = QImage::Format_MonoLSB);
+
+ QBitmap transformed(const QMatrix &) const;
+ QBitmap transformed(const QTransform &matrix) const;
+#ifdef QT3_SUPPORT
+ inline QT3_SUPPORT_CONSTRUCTOR QBitmap(int w, int h, bool clear);
+ inline QT3_SUPPORT_CONSTRUCTOR QBitmap(const QSize &, bool clear);
+ QT3_SUPPORT_CONSTRUCTOR QBitmap(int w, int h, const uchar *bits, bool isXbitmap=false);
+ QT3_SUPPORT_CONSTRUCTOR QBitmap(const QSize &, const uchar *bits, bool isXbitmap=false);
+ inline QT3_SUPPORT QBitmap xForm(const QMatrix &matrix) const { return transformed(QTransform(matrix)); }
+ QT3_SUPPORT_CONSTRUCTOR QBitmap(const QImage &image) { *this = fromImage(image); }
+ QT3_SUPPORT QBitmap &operator=(const QImage &image) { *this = fromImage(image); return *this; }
+#endif
+
+ typedef QExplicitlySharedDataPointer<QPixmapData> DataPtr;
+};
+Q_DECLARE_SHARED(QBitmap)
+
+#ifdef QT3_SUPPORT
+inline QBitmap::QBitmap(int w, int h, bool clear)
+ : QPixmap(QSize(w, h), 1)
+{
+ if (clear) this->clear();
+}
+
+inline QBitmap::QBitmap(const QSize &size, bool clear)
+ : QPixmap(size, 1)
+{
+ if (clear) this->clear();
+}
+#endif
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QBITMAP_H
diff --git a/src/gui/image/qbmphandler.cpp b/src/gui/image/qbmphandler.cpp
new file mode 100644
index 0000000000..6dea9d9917
--- /dev/null
+++ b/src/gui/image/qbmphandler.cpp
@@ -0,0 +1,837 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "private/qbmphandler_p.h"
+
+#ifndef QT_NO_IMAGEFORMAT_BMP
+
+#include <qimage.h>
+#include <qvariant.h>
+#include <qvector.h>
+
+QT_BEGIN_NAMESPACE
+
+static void swapPixel01(QImage *image) // 1-bpp: swap 0 and 1 pixels
+{
+ int i;
+ if (image->depth() == 1 && image->colorCount() == 2) {
+ register uint *p = (uint *)image->bits();
+ int nbytes = image->byteCount();
+ for (i=0; i<nbytes/4; i++) {
+ *p = ~*p;
+ p++;
+ }
+ uchar *p2 = (uchar *)p;
+ for (i=0; i<(nbytes&3); i++) {
+ *p2 = ~*p2;
+ p2++;
+ }
+ QRgb t = image->color(0); // swap color 0 and 1
+ image->setColor(0, image->color(1));
+ image->setColor(1, t);
+ }
+}
+
+/*
+ QImageIO::defineIOHandler("BMP", "^BM", 0,
+ read_bmp_image, write_bmp_image);
+*/
+
+/*****************************************************************************
+ BMP (DIB) image read/write functions
+ *****************************************************************************/
+
+const int BMP_FILEHDR_SIZE = 14; // size of BMP_FILEHDR data
+
+static QDataStream &operator>>(QDataStream &s, BMP_FILEHDR &bf)
+{ // read file header
+ s.readRawData(bf.bfType, 2);
+ s >> bf.bfSize >> bf.bfReserved1 >> bf.bfReserved2 >> bf.bfOffBits;
+ return s;
+}
+
+static QDataStream &operator<<(QDataStream &s, const BMP_FILEHDR &bf)
+{ // write file header
+ s.writeRawData(bf.bfType, 2);
+ s << bf.bfSize << bf.bfReserved1 << bf.bfReserved2 << bf.bfOffBits;
+ return s;
+}
+
+
+const int BMP_OLD = 12; // old Windows/OS2 BMP size
+const int BMP_WIN = 40; // new Windows BMP size
+const int BMP_OS2 = 64; // new OS/2 BMP size
+
+const int BMP_RGB = 0; // no compression
+const int BMP_RLE8 = 1; // run-length encoded, 8 bits
+const int BMP_RLE4 = 2; // run-length encoded, 4 bits
+const int BMP_BITFIELDS = 3; // RGB values encoded in data as bit-fields
+
+
+static QDataStream &operator>>(QDataStream &s, BMP_INFOHDR &bi)
+{
+ s >> bi.biSize;
+ if (bi.biSize == BMP_WIN || bi.biSize == BMP_OS2) {
+ s >> bi.biWidth >> bi.biHeight >> bi.biPlanes >> bi.biBitCount;
+ s >> bi.biCompression >> bi.biSizeImage;
+ s >> bi.biXPelsPerMeter >> bi.biYPelsPerMeter;
+ s >> bi.biClrUsed >> bi.biClrImportant;
+ }
+ else { // probably old Windows format
+ qint16 w, h;
+ s >> w >> h >> bi.biPlanes >> bi.biBitCount;
+ bi.biWidth = w;
+ bi.biHeight = h;
+ bi.biCompression = BMP_RGB; // no compression
+ bi.biSizeImage = 0;
+ bi.biXPelsPerMeter = bi.biYPelsPerMeter = 0;
+ bi.biClrUsed = bi.biClrImportant = 0;
+ }
+ return s;
+}
+
+static QDataStream &operator<<(QDataStream &s, const BMP_INFOHDR &bi)
+{
+ s << bi.biSize;
+ s << bi.biWidth << bi.biHeight;
+ s << bi.biPlanes;
+ s << bi.biBitCount;
+ s << bi.biCompression;
+ s << bi.biSizeImage;
+ s << bi.biXPelsPerMeter << bi.biYPelsPerMeter;
+ s << bi.biClrUsed << bi.biClrImportant;
+ return s;
+}
+
+static int calc_shift(int mask)
+{
+ int result = 0;
+ while (mask && !(mask & 1)) {
+ result++;
+ mask >>= 1;
+ }
+ return result;
+}
+
+static bool read_dib_fileheader(QDataStream &s, BMP_FILEHDR &bf)
+{
+ // read BMP file header
+ s >> bf;
+ if (s.status() != QDataStream::Ok)
+ return false;
+
+ // check header
+ if (qstrncmp(bf.bfType,"BM",2) != 0)
+ return false;
+
+ return true;
+}
+
+static bool read_dib_infoheader(QDataStream &s, BMP_INFOHDR &bi)
+{
+ s >> bi; // read BMP info header
+ if (s.status() != QDataStream::Ok)
+ return false;
+
+ int nbits = bi.biBitCount;
+ int comp = bi.biCompression;
+ if (!(nbits == 1 || nbits == 4 || nbits == 8 || nbits == 16 || nbits == 24 || nbits == 32) ||
+ bi.biPlanes != 1 || comp > BMP_BITFIELDS)
+ return false; // weird BMP image
+ if (!(comp == BMP_RGB || (nbits == 4 && comp == BMP_RLE4) ||
+ (nbits == 8 && comp == BMP_RLE8) || ((nbits == 16 || nbits == 32) && comp == BMP_BITFIELDS)))
+ return false; // weird compression type
+
+ return true;
+}
+
+static bool read_dib_body(QDataStream &s, const BMP_INFOHDR &bi, int offset, int startpos, QImage &image)
+{
+ QIODevice* d = s.device();
+ if (d->atEnd()) // end of stream/file
+ return false;
+#if 0
+ qDebug("offset...........%d", offset);
+ qDebug("startpos.........%d", startpos);
+ qDebug("biSize...........%d", bi.biSize);
+ qDebug("biWidth..........%d", bi.biWidth);
+ qDebug("biHeight.........%d", bi.biHeight);
+ qDebug("biPlanes.........%d", bi.biPlanes);
+ qDebug("biBitCount.......%d", bi.biBitCount);
+ qDebug("biCompression....%d", bi.biCompression);
+ qDebug("biSizeImage......%d", bi.biSizeImage);
+ qDebug("biXPelsPerMeter..%d", bi.biXPelsPerMeter);
+ qDebug("biYPelsPerMeter..%d", bi.biYPelsPerMeter);
+ qDebug("biClrUsed........%d", bi.biClrUsed);
+ qDebug("biClrImportant...%d", bi.biClrImportant);
+#endif
+ int w = bi.biWidth, h = bi.biHeight, nbits = bi.biBitCount;
+ int t = bi.biSize, comp = bi.biCompression;
+ int red_mask = 0;
+ int green_mask = 0;
+ int blue_mask = 0;
+ int red_shift = 0;
+ int green_shift = 0;
+ int blue_shift = 0;
+ int red_scale = 0;
+ int green_scale = 0;
+ int blue_scale = 0;
+
+ int ncols = 0;
+ int depth = 0;
+ QImage::Format format;
+ switch (nbits) {
+ case 32:
+ case 24:
+ case 16:
+ depth = 32;
+ format = QImage::Format_RGB32;
+ break;
+ case 8:
+ case 4:
+ depth = 8;
+ format = QImage::Format_Indexed8;
+ break;
+ default:
+ depth = 1;
+ format = QImage::Format_Mono;
+ }
+
+ if (bi.biHeight < 0)
+ h = -h; // support images with negative height
+
+ if (image.size() != QSize(w, h) || image.format() != format) {
+ image = QImage(w, h, format);
+ if (image.isNull()) // could not create image
+ return false;
+ }
+
+ if (depth != 32) {
+ ncols = bi.biClrUsed ? bi.biClrUsed : 1 << nbits;
+ if (ncols > 256) // sanity check - don't run out of mem if color table is broken
+ return false;
+ image.setColorCount(ncols);
+ }
+
+ image.setDotsPerMeterX(bi.biXPelsPerMeter);
+ image.setDotsPerMeterY(bi.biYPelsPerMeter);
+
+ if (!d->isSequential())
+ d->seek(startpos + BMP_FILEHDR_SIZE + bi.biSize); // goto start of colormap
+
+ if (ncols > 0) { // read color table
+ uchar rgb[4];
+ int rgb_len = t == BMP_OLD ? 3 : 4;
+ for (int i=0; i<ncols; i++) {
+ if (d->read((char *)rgb, rgb_len) != rgb_len)
+ return false;
+ image.setColor(i, qRgb(rgb[2],rgb[1],rgb[0]));
+ if (d->atEnd()) // truncated file
+ return false;
+ }
+ } else if (comp == BMP_BITFIELDS && (nbits == 16 || nbits == 32)) {
+ if (d->read((char *)&red_mask, sizeof(red_mask)) != sizeof(red_mask))
+ return false;
+ if (d->read((char *)&green_mask, sizeof(green_mask)) != sizeof(green_mask))
+ return false;
+ if (d->read((char *)&blue_mask, sizeof(blue_mask)) != sizeof(blue_mask))
+ return false;
+ red_shift = calc_shift(red_mask);
+ red_scale = 256 / ((red_mask >> red_shift) + 1);
+ green_shift = calc_shift(green_mask);
+ green_scale = 256 / ((green_mask >> green_shift) + 1);
+ blue_shift = calc_shift(blue_mask);
+ blue_scale = 256 / ((blue_mask >> blue_shift) + 1);
+ } else if (comp == BMP_RGB && (nbits == 24 || nbits == 32)) {
+ blue_mask = 0x000000ff;
+ green_mask = 0x0000ff00;
+ red_mask = 0x00ff0000;
+ blue_shift = 0;
+ green_shift = 8;
+ red_shift = 16;
+ blue_scale = green_scale = red_scale = 1;
+ } else if (comp == BMP_RGB && nbits == 16) {
+ blue_mask = 0x001f;
+ green_mask = 0x03e0;
+ red_mask = 0x7c00;
+ blue_shift = 0;
+ green_shift = 2;
+ red_shift = 7;
+ red_scale = 1;
+ green_scale = 1;
+ blue_scale = 8;
+ }
+
+ // offset can be bogus, be careful
+ if (offset>=0 && startpos + offset > d->pos()) {
+ if (!d->isSequential())
+ d->seek(startpos + offset); // start of image data
+ }
+
+ int bpl = image.bytesPerLine();
+ uchar *data = image.bits();
+
+ if (nbits == 1) { // 1 bit BMP image
+ while (--h >= 0) {
+ if (d->read((char*)(data + h*bpl), bpl) != bpl)
+ break;
+ }
+ if (ncols == 2 && qGray(image.color(0)) < qGray(image.color(1)))
+ swapPixel01(&image); // pixel 0 is white!
+ }
+
+ else if (nbits == 4) { // 4 bit BMP image
+ int buflen = ((w+7)/8)*4;
+ uchar *buf = new uchar[buflen];
+ if (comp == BMP_RLE4) { // run length compression
+ int x=0, y=0, c, i;
+ quint8 b;
+ register uchar *p = data + (h-1)*bpl;
+ const uchar *endp = p + w;
+ while (y < h) {
+ if (!d->getChar((char *)&b))
+ break;
+ if (b == 0) { // escape code
+ if (!d->getChar((char *)&b) || b == 1) {
+ y = h; // exit loop
+ } else switch (b) {
+ case 0: // end of line
+ x = 0;
+ y++;
+ p = data + (h-y-1)*bpl;
+ break;
+ case 2: // delta (jump)
+ {
+ quint8 tmp;
+ d->getChar((char *)&tmp);
+ x += tmp;
+ d->getChar((char *)&tmp);
+ y += tmp;
+ }
+
+ // Protection
+ if ((uint)x >= (uint)w)
+ x = w-1;
+ if ((uint)y >= (uint)h)
+ y = h-1;
+
+ p = data + (h-y-1)*bpl + x;
+ break;
+ default: // absolute mode
+ // Protection
+ if (p + b > endp)
+ b = endp-p;
+
+ i = (c = b)/2;
+ while (i--) {
+ d->getChar((char *)&b);
+ *p++ = b >> 4;
+ *p++ = b & 0x0f;
+ }
+ if (c & 1) {
+ unsigned char tmp;
+ d->getChar((char *)&tmp);
+ *p++ = tmp >> 4;
+ }
+ if ((((c & 3) + 1) & 2) == 2)
+ d->getChar(0); // align on word boundary
+ x += c;
+ }
+ } else { // encoded mode
+ // Protection
+ if (p + b > endp)
+ b = endp-p;
+
+ i = (c = b)/2;
+ d->getChar((char *)&b); // 2 pixels to be repeated
+ while (i--) {
+ *p++ = b >> 4;
+ *p++ = b & 0x0f;
+ }
+ if (c & 1)
+ *p++ = b >> 4;
+ x += c;
+ }
+ }
+ } else if (comp == BMP_RGB) { // no compression
+ memset(data, 0, h*bpl);
+ while (--h >= 0) {
+ if (d->read((char*)buf,buflen) != buflen)
+ break;
+ register uchar *p = data + h*bpl;
+ uchar *b = buf;
+ for (int i=0; i<w/2; i++) { // convert nibbles to bytes
+ *p++ = *b >> 4;
+ *p++ = *b++ & 0x0f;
+ }
+ if (w & 1) // the last nibble
+ *p = *b >> 4;
+ }
+ }
+ delete [] buf;
+ }
+
+ else if (nbits == 8) { // 8 bit BMP image
+ if (comp == BMP_RLE8) { // run length compression
+ int x=0, y=0;
+ quint8 b;
+ register uchar *p = data + (h-1)*bpl;
+ const uchar *endp = p + w;
+ while (y < h) {
+ if (!d->getChar((char *)&b))
+ break;
+ if (b == 0) { // escape code
+ if (!d->getChar((char *)&b) || b == 1) {
+ y = h; // exit loop
+ } else switch (b) {
+ case 0: // end of line
+ x = 0;
+ y++;
+ p = data + (h-y-1)*bpl;
+ break;
+ case 2: // delta (jump)
+ // Protection
+ if ((uint)x >= (uint)w)
+ x = w-1;
+ if ((uint)y >= (uint)h)
+ y = h-1;
+
+ {
+ quint8 tmp;
+ d->getChar((char *)&tmp);
+ x += tmp;
+ d->getChar((char *)&tmp);
+ y += tmp;
+ }
+ p = data + (h-y-1)*bpl + x;
+ break;
+ default: // absolute mode
+ // Protection
+ if (p + b > endp)
+ b = endp-p;
+
+ if (d->read((char *)p, b) != b)
+ return false;
+ if ((b & 1) == 1)
+ d->getChar(0); // align on word boundary
+ x += b;
+ p += b;
+ }
+ } else { // encoded mode
+ // Protection
+ if (p + b > endp)
+ b = endp-p;
+
+ char tmp;
+ d->getChar(&tmp);
+ memset(p, tmp, b); // repeat pixel
+ x += b;
+ p += b;
+ }
+ }
+ } else if (comp == BMP_RGB) { // uncompressed
+ while (--h >= 0) {
+ if (d->read((char *)data + h*bpl, bpl) != bpl)
+ break;
+ }
+ }
+ }
+
+ else if (nbits == 16 || nbits == 24 || nbits == 32) { // 16,24,32 bit BMP image
+ register QRgb *p;
+ QRgb *end;
+ uchar *buf24 = new uchar[bpl];
+ int bpl24 = ((w*nbits+31)/32)*4;
+ uchar *b;
+ int c;
+
+ while (--h >= 0) {
+ p = (QRgb *)(data + h*bpl);
+ end = p + w;
+ if (d->read((char *)buf24,bpl24) != bpl24)
+ break;
+ b = buf24;
+ while (p < end) {
+ c = *(uchar*)b | (*(uchar*)(b+1)<<8);
+ if (nbits != 16)
+ c |= *(uchar*)(b+2)<<16;
+ *p++ = qRgb(((c & red_mask) >> red_shift) * red_scale,
+ ((c & green_mask) >> green_shift) * green_scale,
+ ((c & blue_mask) >> blue_shift) * blue_scale);
+ b += nbits/8;
+ }
+ }
+ delete[] buf24;
+ }
+
+ if (bi.biHeight < 0) {
+ // Flip the image
+ uchar *buf = new uchar[bpl];
+ h = -bi.biHeight;
+ for (int y = 0; y < h/2; ++y) {
+ memcpy(buf, data + y*bpl, bpl);
+ memcpy(data + y*bpl, data + (h-y-1)*bpl, bpl);
+ memcpy(data + (h-y-1)*bpl, buf, bpl);
+ }
+ delete [] buf;
+ }
+
+ return true;
+}
+
+// this is also used in qmime_win.cpp
+bool qt_write_dib(QDataStream &s, QImage image)
+{
+ int nbits;
+ int bpl_bmp;
+ int bpl = image.bytesPerLine();
+
+ QIODevice* d = s.device();
+ if (!d->isWritable())
+ return false;
+
+ if (image.depth() == 8 && image.colorCount() <= 16) {
+ bpl_bmp = (((bpl+1)/2+3)/4)*4;
+ nbits = 4;
+ } else if (image.depth() == 32) {
+ bpl_bmp = ((image.width()*24+31)/32)*4;
+ nbits = 24;
+#ifdef Q_WS_QWS
+ } else if (image.depth() == 1 || image.depth() == 8) {
+ // Qt for Embedded Linux doesn't word align.
+ bpl_bmp = ((image.width()*image.depth()+31)/32)*4;
+ nbits = image.depth();
+#endif
+ } else {
+ bpl_bmp = bpl;
+ nbits = image.depth();
+ }
+
+ BMP_INFOHDR bi;
+ bi.biSize = BMP_WIN; // build info header
+ bi.biWidth = image.width();
+ bi.biHeight = image.height();
+ bi.biPlanes = 1;
+ bi.biBitCount = nbits;
+ bi.biCompression = BMP_RGB;
+ bi.biSizeImage = bpl_bmp*image.height();
+ bi.biXPelsPerMeter = image.dotsPerMeterX() ? image.dotsPerMeterX()
+ : 2834; // 72 dpi default
+ bi.biYPelsPerMeter = image.dotsPerMeterY() ? image.dotsPerMeterY() : 2834;
+ bi.biClrUsed = image.colorCount();
+ bi.biClrImportant = image.colorCount();
+ s << bi; // write info header
+ if (s.status() != QDataStream::Ok)
+ return false;
+
+ if (image.depth() != 32) { // write color table
+ uchar *color_table = new uchar[4*image.colorCount()];
+ uchar *rgb = color_table;
+ QVector<QRgb> c = image.colorTable();
+ for (int i=0; i<image.colorCount(); i++) {
+ *rgb++ = qBlue (c[i]);
+ *rgb++ = qGreen(c[i]);
+ *rgb++ = qRed (c[i]);
+ *rgb++ = 0;
+ }
+ if (d->write((char *)color_table, 4*image.colorCount()) == -1) {
+ delete [] color_table;
+ return false;
+ }
+ delete [] color_table;
+ }
+
+ if (image.format() == QImage::Format_MonoLSB)
+ image = image.convertToFormat(QImage::Format_Mono);
+
+ int y;
+
+ if (nbits == 1 || nbits == 8) { // direct output
+#ifdef Q_WS_QWS
+ // Qt for Embedded Linux doesn't word align.
+ int pad = bpl_bmp - bpl;
+ char padding[4];
+#endif
+ for (y=image.height()-1; y>=0; y--) {
+ if (d->write((char*)image.scanLine(y), bpl) == -1)
+ return false;
+#ifdef Q_WS_QWS
+ if (d->write(padding, pad) == -1)
+ return false;
+#endif
+ }
+ return true;
+ }
+
+ uchar *buf = new uchar[bpl_bmp];
+ uchar *b, *end;
+ register uchar *p;
+
+ memset(buf, 0, bpl_bmp);
+ for (y=image.height()-1; y>=0; y--) { // write the image bits
+ if (nbits == 4) { // convert 8 -> 4 bits
+ p = image.scanLine(y);
+ b = buf;
+ end = b + image.width()/2;
+ while (b < end) {
+ *b++ = (*p << 4) | (*(p+1) & 0x0f);
+ p += 2;
+ }
+ if (image.width() & 1)
+ *b = *p << 4;
+ } else { // 32 bits
+ QRgb *p = (QRgb *)image.scanLine(y);
+ QRgb *end = p + image.width();
+ b = buf;
+ while (p < end) {
+ *b++ = qBlue(*p);
+ *b++ = qGreen(*p);
+ *b++ = qRed(*p);
+ p++;
+ }
+ }
+ if (bpl_bmp != d->write((char*)buf, bpl_bmp)) {
+ delete[] buf;
+ return false;
+ }
+ }
+ delete[] buf;
+ return true;
+}
+
+// this is also used in qmime_win.cpp
+bool qt_read_dib(QDataStream &s, QImage &image)
+{
+ BMP_INFOHDR bi;
+ if (!read_dib_infoheader(s, bi))
+ return false;
+ return read_dib_body(s, bi, -1, -BMP_FILEHDR_SIZE, image);
+}
+
+QBmpHandler::QBmpHandler()
+ : state(Ready)
+{
+}
+
+bool QBmpHandler::readHeader()
+{
+ state = Error;
+
+ QIODevice *d = device();
+ QDataStream s(d);
+ startpos = d->pos();
+
+ // Intel byte order
+ s.setByteOrder(QDataStream::LittleEndian);
+
+ // read BMP file header
+ if (!read_dib_fileheader(s, fileHeader))
+ return false;
+
+ // read BMP info header
+ if (!read_dib_infoheader(s, infoHeader))
+ return false;
+
+ state = ReadHeader;
+ return true;
+}
+
+bool QBmpHandler::canRead() const
+{
+ if (state == Ready && !canRead(device()))
+ return false;
+
+ if (state != Error) {
+ setFormat("bmp");
+ return true;
+ }
+
+ return false;
+}
+
+bool QBmpHandler::canRead(QIODevice *device)
+{
+ if (!device) {
+ qWarning("QBmpHandler::canRead() called with 0 pointer");
+ return false;
+ }
+
+ char head[2];
+ if (device->peek(head, sizeof(head)) != sizeof(head))
+ return false;
+
+ return (qstrncmp(head, "BM", 2) == 0);
+}
+
+bool QBmpHandler::read(QImage *image)
+{
+ if (state == Error)
+ return false;
+
+ if (!image) {
+ qWarning("QBmpHandler::read: cannot read into null pointer");
+ return false;
+ }
+
+ if (state == Ready && !readHeader()) {
+ state = Error;
+ return false;
+ }
+
+ QIODevice *d = device();
+ QDataStream s(d);
+
+ // Intel byte order
+ s.setByteOrder(QDataStream::LittleEndian);
+
+ // read image
+ if (!read_dib_body(s, infoHeader, fileHeader.bfOffBits, startpos, *image))
+ return false;
+
+ state = Ready;
+ return true;
+}
+
+bool QBmpHandler::write(const QImage &img)
+{
+ QImage image;
+ switch (img.format()) {
+ case QImage::Format_ARGB8565_Premultiplied:
+ case QImage::Format_ARGB8555_Premultiplied:
+ case QImage::Format_ARGB6666_Premultiplied:
+ case QImage::Format_ARGB4444_Premultiplied:
+ image = img.convertToFormat(QImage::Format_ARGB32);
+ break;
+ case QImage::Format_RGB16:
+ case QImage::Format_RGB888:
+ case QImage::Format_RGB666:
+ case QImage::Format_RGB555:
+ case QImage::Format_RGB444:
+ image = img.convertToFormat(QImage::Format_RGB32);
+ break;
+ default:
+ image = img;
+ }
+
+ QIODevice *d = device();
+ QDataStream s(d);
+ BMP_FILEHDR bf;
+ int bpl_bmp;
+ int bpl = image.bytesPerLine();
+
+ // Code partially repeated in qt_write_dib
+ if (image.depth() == 8 && image.colorCount() <= 16) {
+ bpl_bmp = (((bpl+1)/2+3)/4)*4;
+ } else if (image.depth() == 32) {
+ bpl_bmp = ((image.width()*24+31)/32)*4;
+ } else {
+ bpl_bmp = bpl;
+ }
+
+ // Intel byte order
+ s.setByteOrder(QDataStream::LittleEndian);
+
+ // build file header
+ memcpy(bf.bfType, "BM", 2);
+
+ // write file header
+ bf.bfReserved1 = 0;
+ bf.bfReserved2 = 0;
+ bf.bfOffBits = BMP_FILEHDR_SIZE + BMP_WIN + image.colorCount() * 4;
+ bf.bfSize = bf.bfOffBits + bpl_bmp*image.height();
+ s << bf;
+
+ // write image
+ return qt_write_dib(s, image);
+}
+
+bool QBmpHandler::supportsOption(ImageOption option) const
+{
+ return option == Size
+ || option == ImageFormat;
+}
+
+QVariant QBmpHandler::option(ImageOption option) const
+{
+ if (option == Size) {
+ if (state == Error)
+ return QVariant();
+ if (state == Ready && !const_cast<QBmpHandler*>(this)->readHeader())
+ return QVariant();
+ return QSize(infoHeader.biWidth, infoHeader.biHeight);
+ } else if (option == ImageFormat) {
+ if (state == Error)
+ return QVariant();
+ if (state == Ready && !const_cast<QBmpHandler*>(this)->readHeader())
+ return QVariant();
+ QImage::Format format;
+ switch (infoHeader.biBitCount) {
+ case 32:
+ case 24:
+ case 16:
+ format = QImage::Format_RGB32;
+ break;
+ case 8:
+ case 4:
+ format = QImage::Format_Indexed8;
+ break;
+ default:
+ format = QImage::Format_Mono;
+ }
+ return format;
+ }
+ return QVariant();
+}
+
+void QBmpHandler::setOption(ImageOption option, const QVariant &value)
+{
+ Q_UNUSED(option);
+ Q_UNUSED(value);
+}
+
+QByteArray QBmpHandler::name() const
+{
+ return "bmp";
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_IMAGEFORMAT_BMP
diff --git a/src/gui/image/qbmphandler_p.h b/src/gui/image/qbmphandler_p.h
new file mode 100644
index 0000000000..3bf08e8395
--- /dev/null
+++ b/src/gui/image/qbmphandler_p.h
@@ -0,0 +1,117 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QBMPHANDLER_P_H
+#define QBMPHANDLER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtGui/qimageiohandler.h"
+
+#ifndef QT_NO_IMAGEFORMAT_BMP
+
+QT_BEGIN_NAMESPACE
+
+struct BMP_FILEHDR { // BMP file header
+ char bfType[2]; // "BM"
+ qint32 bfSize; // size of file
+ qint16 bfReserved1;
+ qint16 bfReserved2;
+ qint32 bfOffBits; // pointer to the pixmap bits
+};
+
+struct BMP_INFOHDR { // BMP information header
+ qint32 biSize; // size of this struct
+ qint32 biWidth; // pixmap width
+ qint32 biHeight; // pixmap height
+ qint16 biPlanes; // should be 1
+ qint16 biBitCount; // number of bits per pixel
+ qint32 biCompression; // compression method
+ qint32 biSizeImage; // size of image
+ qint32 biXPelsPerMeter; // horizontal resolution
+ qint32 biYPelsPerMeter; // vertical resolution
+ qint32 biClrUsed; // number of colors used
+ qint32 biClrImportant; // number of important colors
+};
+
+class QBmpHandler : public QImageIOHandler
+{
+public:
+ QBmpHandler();
+ 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:
+ bool readHeader();
+ enum State {
+ Ready,
+ ReadHeader,
+ Error
+ };
+ State state;
+ BMP_FILEHDR fileHeader;
+ BMP_INFOHDR infoHeader;
+ int startpos;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_IMAGEFORMAT_BMP
+
+#endif // QBMPHANDLER_P_H
diff --git a/src/gui/image/qgifhandler.cpp b/src/gui/image/qgifhandler.cpp
new file mode 100644
index 0000000000..7cb7373b44
--- /dev/null
+++ b/src/gui/image/qgifhandler.cpp
@@ -0,0 +1,1214 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@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_p.h"
+
+#include <qimage.h>
+#include <qiodevice.h>
+#include <qvariant.h>
+
+QT_BEGIN_NAMESPACE
+
+#define Q_TRANSPARENT 0x00ffffff
+
+// avoid going through QImage::scanLine() which calls detach
+#define FAST_SCAN_LINE(bits, bpl, y) (bits + (y) * bpl)
+
+
+/*
+ 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);
+ static void scan(QIODevice *device, QVector<QSize> *imageSizes, int *loopCount);
+
+ 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];
+ short* stack;
+ short *sp;
+ bool needfirst;
+ int x, y;
+ int frame;
+ bool out_of_bounds;
+ bool digress;
+ void nextY(unsigned char *bits, int bpl);
+ 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;
+ table[0] = 0;
+ table[1] = 0;
+ stack = 0;
+}
+
+/*!
+ Destroys a QGIFFormat.
+*/
+QGIFFormat::~QGIFFormat()
+{
+ if (globalcmap) delete[] globalcmap;
+ if (localcmap) delete[] localcmap;
+ delete [] stack;
+}
+
+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)
+{
+ // 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."
+
+ if (!stack) {
+ stack = new short[(1 << max_lzw_bits) * 4];
+ table[0] = &stack[(1 << max_lzw_bits) * 2];
+ table[1] = &stack[(1 << max_lzw_bits) * 3];
+ }
+
+ image->detach();
+ int bpl = image->bytesPerLine();
+ unsigned char *bits = image->bits();
+
+#define LM(l, m) (((m)<<8)|l)
+ digress = false;
+ const 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) = QImage(swidth, sheight, format);
+ bpl = image->bytesPerLine();
+ bits = image->bits();
+ memset(bits, 0, image->byteCount());
+ }
+
+ 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(bits, 0, image->byteCount());
+ }
+ const int dest_bpl = backingstore.bytesPerLine();
+ unsigned char *dest_data = backingstore.bits();
+ for (int ln=0; ln<h; ln++) {
+ memcpy(FAST_SCAN_LINE(dest_data, dest_bpl, ln),
+ FAST_SCAN_LINE(bits, bpl, 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*)FAST_SCAN_LINE(bits, bpl, 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(bits, bpl);
+ }
+ } else {
+ incode=code;
+ if (code>=max_code) {
+ *sp++=firstcode;
+ code=oldcode;
+ }
+ while (code>=clear_code+2) {
+ if (code >= max_code) {
+ state = Error;
+ return -1;
+ }
+ *sp++=table[1][code];
+ if (code==table[0][code]) {
+ state=Error;
+ return -1;
+ }
+ if (sp-stack>=(1<<(max_lzw_bits))*2) {
+ state=Error;
+ return -1;
+ }
+ code=table[0][code];
+ }
+ if (code < 0) {
+ state = Error;
+ return -1;
+ }
+
+ *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*)FAST_SCAN_LINE(bits, bpl, 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(bits, bpl);
+ if (!out_of_bounds && h > y)
+ line = (QRgb*)FAST_SCAN_LINE(bits, bpl, 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;
+}
+
+/*!
+ Scans through the data stream defined by \a device and returns the image
+ sizes found in the stream in the \a imageSizes vector.
+*/
+void QGIFFormat::scan(QIODevice *device, QVector<QSize> *imageSizes, int *loopCount)
+{
+ if (!device)
+ return;
+
+ qint64 oldPos = device->pos();
+ if (!device->seek(0))
+ return;
+
+ int colorCount = 0;
+ int localColorCount = 0;
+ int globalColorCount = 0;
+ int colorReadCount = 0;
+ bool localColormap = false;
+ bool globalColormap = false;
+ int count = 0;
+ int blockSize = 0;
+ int imageWidth = 0;
+ int imageHeight = 0;
+ bool done = false;
+ uchar hold[16];
+ State state = Header;
+
+ const int readBufferSize = 40960; // 40k read buffer
+ QByteArray readBuffer(device->read(readBufferSize));
+
+ if (readBuffer.isEmpty()) {
+ device->seek(oldPos);
+ return;
+ }
+
+ // This is a specialized version of the state machine from decode(),
+ // which doesn't do any image decoding or mallocing, and has an
+ // optimized way of skipping SkipBlocks, ImageDataBlocks and
+ // Global/LocalColorMaps.
+
+ while (!readBuffer.isEmpty()) {
+ int length = readBuffer.size();
+ const uchar *buffer = (const uchar *) readBuffer.constData();
+ while (!done && length) {
+ length--;
+ uchar ch = *buffer++;
+ switch (state) {
+ case Header:
+ hold[count++] = ch;
+ if (count == 6) {
+ state = LogicalScreenDescriptor;
+ count = 0;
+ }
+ break;
+ case LogicalScreenDescriptor:
+ hold[count++] = ch;
+ if (count == 7) {
+ imageWidth = LM(hold[0], hold[1]);
+ imageHeight = LM(hold[2], hold[3]);
+ globalColormap = !!(hold[4] & 0x80);
+ globalColorCount = 2 << (hold[4] & 0x7);
+ count = 0;
+ colorCount = globalColorCount;
+ if (globalColormap) {
+ int colorTableSize = 3 * globalColorCount;
+ if (length >= colorTableSize) {
+ // skip the global color table in one go
+ length -= colorTableSize;
+ buffer += colorTableSize;
+ state = Introducer;
+ } else {
+ colorReadCount = 0;
+ state = GlobalColorMap;
+ }
+ } else {
+ state=Introducer;
+ }
+ }
+ break;
+ case GlobalColorMap:
+ case LocalColorMap:
+ hold[count++] = ch;
+ if (count == 3) {
+ if (++colorReadCount >= colorCount) {
+ if (state == LocalColorMap)
+ state = TableImageLZWSize;
+ else
+ state = Introducer;
+ }
+ count = 0;
+ }
+ break;
+ case Introducer:
+ hold[count++] = ch;
+ switch (ch) {
+ case 0x2c:
+ state = ImageDescriptor;
+ break;
+ case 0x21:
+ state = ExtensionLabel;
+ break;
+ case 0x3b:
+ state = Done;
+ break;
+ default:
+ done = true;
+ 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]);
+
+ if (imageWidth/10 > qMax(newWidth,200))
+ imageWidth = -1;
+ if (imageHeight/10 > qMax(newHeight,200))
+ imageHeight = -1;
+
+ if (imageWidth <= 0)
+ imageWidth = newLeft + newWidth;
+ if (imageHeight <= 0)
+ imageHeight = newTop + newHeight;
+
+ *imageSizes << QSize(imageWidth, imageHeight);
+
+ localColormap = !!(hold[9] & 0x80);
+ localColorCount = localColormap ? (2 << (hold[9] & 0x7)) : 0;
+ if (localColorCount)
+ colorCount = localColorCount;
+ else
+ colorCount = globalColorCount;
+
+ count = 0;
+ if (localColormap) {
+ int colorTableSize = 3 * localColorCount;
+ if (length >= colorTableSize) {
+ // skip the local color table in one go
+ length -= colorTableSize;
+ buffer += colorTableSize;
+ state = TableImageLZWSize;
+ } else {
+ colorReadCount = 0;
+ state = LocalColorMap;
+ }
+ } else {
+ state = TableImageLZWSize;
+ }
+ }
+ break;
+ case TableImageLZWSize:
+ if (ch > max_lzw_bits)
+ state = Error;
+ else
+ state = ImageDataBlockSize;
+ count = 0;
+ break;
+ case ImageDataBlockSize:
+ blockSize = ch;
+ if (blockSize) {
+ if (length >= blockSize) {
+ // we can skip the block in one go
+ length -= blockSize;
+ buffer += blockSize;
+ count = 0;
+ } else {
+ state = ImageDataBlock;
+ }
+ } else {
+ state = Introducer;
+ }
+ break;
+ case ImageDataBlock:
+ ++count;
+ if (count == blockSize) {
+ count = 0;
+ state = ImageDataBlockSize;
+ }
+ break;
+ case ExtensionLabel:
+ switch (ch) {
+ case 0xf9:
+ state = GraphicControlExtension;
+ break;
+ case 0xff:
+ state = ApplicationExtension;
+ break;
+ 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)
+ state=NetscapeExtensionBlockSize;
+ else
+ state=SkipBlockSize;
+ count = 0;
+ }
+ break;
+ case GraphicControlExtension:
+ if (count < 5)
+ hold[count] = ch;
+ ++count;
+ if (count == hold[0] + 1) {
+ count = 0;
+ state = SkipBlockSize;
+ }
+ break;
+ case NetscapeExtensionBlockSize:
+ blockSize = ch;
+ count = 0;
+ if (blockSize)
+ state = NetscapeExtensionBlock;
+ else
+ state = Introducer;
+ break;
+ case NetscapeExtensionBlock:
+ if (count < 3)
+ hold[count] = ch;
+ count++;
+ if (count == blockSize) {
+ *loopCount = LM(hold[1], hold[2]);
+ state = SkipBlockSize;
+ }
+ break;
+ case SkipBlockSize:
+ blockSize = ch;
+ count = 0;
+ if (blockSize) {
+ if (length >= blockSize) {
+ // we can skip the block in one go
+ length -= blockSize;
+ buffer += blockSize;
+ } else {
+ state = SkipBlock;
+ }
+ } else {
+ state = Introducer;
+ }
+ break;
+ case SkipBlock:
+ ++count;
+ if (count == blockSize)
+ state = SkipBlockSize;
+ break;
+ case Done:
+ done = true;
+ break;
+ case Error:
+ device->seek(oldPos);
+ return;
+ }
+ }
+ readBuffer = device->read(readBufferSize);
+ }
+ device->seek(oldPos);
+ return;
+}
+
+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(unsigned char *bits, int bpl)
+{
+ 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(FAST_SCAN_LINE(bits, bpl, y+i)+left*sizeof(QRgb), FAST_SCAN_LINE(bits, bpl, 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(FAST_SCAN_LINE(bits, bpl, y+i)+left*sizeof(QRgb), FAST_SCAN_LINE(bits, bpl, 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(FAST_SCAN_LINE(bits, bpl, y+i)+left*sizeof(QRgb), FAST_SCAN_LINE(bits, bpl, 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 = 100;
+ loopCnt = -1;
+ frameNumber = -1;
+ scanIsCached = false;
+}
+
+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);
+ if (decoded == -1)
+ break;
+ buffer.remove(0, decoded);
+ }
+ return gifFormat->partialNewFrame;
+}
+
+bool QGifHandler::canRead() const
+{
+ if (canRead(device()) || imageIsComing()) {
+ setFormat("gif");
+ return true;
+ }
+
+ return false;
+}
+
+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);
+ 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
+{
+ if (!device() || device()->isSequential())
+ return option == Animation;
+ else
+ return option == Size
+ || option == Animation;
+}
+
+QVariant QGifHandler::option(ImageOption option) const
+{
+ if (option == Size) {
+ if (!scanIsCached) {
+ QGIFFormat::scan(device(), &imageSizes, &loopCnt);
+ scanIsCached = true;
+ }
+ // before the first frame is read, or we have an empty data stream
+ if (frameNumber == -1)
+ return (imageSizes.count() > 0) ? QVariant(imageSizes.at(0)) : QVariant();
+ // after the last frame has been read, the next size is undefined
+ if (frameNumber >= imageSizes.count() - 1)
+ return QVariant();
+ // and the last case: the size of the next frame
+ return imageSizes.at(frameNumber + 1);
+ } else if (option == Animation) {
+ return true;
+ }
+ 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
+{
+ if (!scanIsCached) {
+ QGIFFormat::scan(device(), &imageSizes, &loopCnt);
+ scanIsCached = true;
+ }
+ return imageSizes.count();
+}
+
+int QGifHandler::loopCount() const
+{
+ if (!scanIsCached) {
+ QGIFFormat::scan(device(), &imageSizes, &loopCnt);
+ scanIsCached = true;
+ }
+
+ if (loopCnt == 0)
+ return -1;
+ else if (loopCnt == -1)
+ return 0;
+ else
+ return loopCnt;
+}
+
+int QGifHandler::currentImageNumber() const
+{
+ return frameNumber;
+}
+
+QByteArray QGifHandler::name() const
+{
+ return "gif";
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/image/qgifhandler.pri b/src/gui/image/qgifhandler.pri
new file mode 100644
index 0000000000..6eb0751372
--- /dev/null
+++ b/src/gui/image/qgifhandler.pri
@@ -0,0 +1,4 @@
+# common to plugin and built-in forms
+INCLUDEPATH *= $$PWD
+HEADERS += $$PWD/qgifhandler_p.h
+SOURCES += $$PWD/qgifhandler.cpp
diff --git a/src/gui/image/qgifhandler_p.h b/src/gui/image/qgifhandler_p.h
new file mode 100644
index 0000000000..0ea4ccfc38
--- /dev/null
+++ b/src/gui/image/qgifhandler_p.h
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@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_P_H
+#define QGIFHANDLER_P_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 QVector<QSize> imageSizes;
+ mutable bool scanIsCached;
+};
+
+QT_END_NAMESPACE
+
+#endif // QGIFHANDLER_P_H
diff --git a/src/gui/image/qicon.cpp b/src/gui/image/qicon.cpp
new file mode 100644
index 0000000000..7182062e59
--- /dev/null
+++ b/src/gui/image/qicon.cpp
@@ -0,0 +1,1258 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qicon.h"
+#include "qicon_p.h"
+#include "qiconengine.h"
+#include "qiconengineplugin.h"
+#include "private/qfactoryloader_p.h"
+#include "private/qiconloader_p.h"
+#include "qapplication.h"
+#include "qstyleoption.h"
+#include "qpainter.h"
+#include "qfileinfo.h"
+#include "qstyle.h"
+#include "qpixmapcache.h"
+#include "qvariant.h"
+#include "qcache.h"
+#include "qdebug.h"
+#include "private/qguiplatformplugin_p.h"
+
+#ifdef Q_WS_MAC
+#include <private/qt_mac_p.h>
+#include <private/qt_cocoa_helpers_mac_p.h>
+#endif
+
+#ifdef Q_WS_X11
+#include "private/qt_x11_p.h"
+#include "private/qkde_p.h"
+#endif
+
+#include "private/qstylehelper_p.h"
+
+#ifndef QT_NO_ICON
+QT_BEGIN_NAMESPACE
+
+/*!
+ \enum QIcon::Mode
+
+ This enum type describes the mode for which a pixmap is intended
+ to be used. The currently defined modes are:
+
+ \value Normal
+ Display the pixmap when the user is
+ not interacting with the icon, but the
+ functionality represented by the icon is available.
+ \value Disabled
+ Display the pixmap when the
+ functionality represented by the icon is not available.
+ \value Active
+ Display the pixmap when the
+ functionality represented by the icon is available and
+ the user is interacting with the icon, for example, moving the
+ mouse over it or clicking it.
+ \value Selected
+ Display the pixmap when the item represented by the icon is
+ selected.
+*/
+
+/*!
+ \enum QIcon::State
+
+ This enum describes the state for which a pixmap is intended to be
+ used. The \e state can be:
+
+ \value Off Display the pixmap when the widget is in an "off" state
+ \value On Display the pixmap when the widget is in an "on" state
+*/
+
+static QBasicAtomicInt serialNumCounter = Q_BASIC_ATOMIC_INITIALIZER(1);
+
+static void qt_cleanup_icon_cache();
+typedef QCache<QString, QIcon> IconCache;
+Q_GLOBAL_STATIC_WITH_INITIALIZER(IconCache, qtIconCache, qAddPostRoutine(qt_cleanup_icon_cache))
+
+static void qt_cleanup_icon_cache()
+{
+ qtIconCache()->clear();
+}
+
+QIconPrivate::QIconPrivate()
+ : engine(0), ref(1),
+ serialNum(serialNumCounter.fetchAndAddRelaxed(1)),
+ detach_no(0),
+ engine_version(2),
+ v1RefCount(0)
+{
+}
+
+QPixmapIconEngine::QPixmapIconEngine()
+{
+}
+
+QPixmapIconEngine::QPixmapIconEngine(const QPixmapIconEngine &other)
+ : QIconEngineV2(other), pixmaps(other.pixmaps)
+{
+}
+
+QPixmapIconEngine::~QPixmapIconEngine()
+{
+}
+
+void QPixmapIconEngine::paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state)
+{
+ QSize pixmapSize = rect.size();
+#if defined(Q_WS_MAC)
+ pixmapSize *= qt_mac_get_scalefactor();
+#endif
+ painter->drawPixmap(rect, pixmap(pixmapSize, mode, state));
+}
+
+static inline int area(const QSize &s) { return s.width() * s.height(); }
+
+// returns the smallest of the two that is still larger than or equal to size.
+static QPixmapIconEngineEntry *bestSizeMatch( const QSize &size, QPixmapIconEngineEntry *pa, QPixmapIconEngineEntry *pb)
+{
+ int s = area(size);
+ if (pa->size == QSize() && pa->pixmap.isNull()) {
+ pa->pixmap = QPixmap(pa->fileName);
+ pa->size = pa->pixmap.size();
+ }
+ int a = area(pa->size);
+ if (pb->size == QSize() && pb->pixmap.isNull()) {
+ pb->pixmap = QPixmap(pb->fileName);
+ pb->size = pb->pixmap.size();
+ }
+ int b = area(pb->size);
+ int res = a;
+ if (qMin(a,b) >= s)
+ res = qMin(a,b);
+ else
+ res = qMax(a,b);
+ if (res == a)
+ return pa;
+ return pb;
+}
+
+QPixmapIconEngineEntry *QPixmapIconEngine::tryMatch(const QSize &size, QIcon::Mode mode, QIcon::State state)
+{
+ QPixmapIconEngineEntry *pe = 0;
+ for (int i = 0; i < pixmaps.count(); ++i)
+ if (pixmaps.at(i).mode == mode && pixmaps.at(i).state == state) {
+ if (pe)
+ pe = bestSizeMatch(size, &pixmaps[i], pe);
+ else
+ pe = &pixmaps[i];
+ }
+ return pe;
+}
+
+
+QPixmapIconEngineEntry *QPixmapIconEngine::bestMatch(const QSize &size, QIcon::Mode mode, QIcon::State state, bool sizeOnly)
+{
+ QPixmapIconEngineEntry *pe = tryMatch(size, mode, state);
+ while (!pe){
+ QIcon::State oppositeState = (state == QIcon::On) ? QIcon::Off : QIcon::On;
+ if (mode == QIcon::Disabled || mode == QIcon::Selected) {
+ QIcon::Mode oppositeMode = (mode == QIcon::Disabled) ? QIcon::Selected : QIcon::Disabled;
+ if ((pe = tryMatch(size, QIcon::Normal, state)))
+ break;
+ if ((pe = tryMatch(size, QIcon::Active, state)))
+ break;
+ if ((pe = tryMatch(size, mode, oppositeState)))
+ break;
+ if ((pe = tryMatch(size, QIcon::Normal, oppositeState)))
+ break;
+ if ((pe = tryMatch(size, QIcon::Active, oppositeState)))
+ break;
+ if ((pe = tryMatch(size, oppositeMode, state)))
+ break;
+ if ((pe = tryMatch(size, oppositeMode, oppositeState)))
+ break;
+ } else {
+ QIcon::Mode oppositeMode = (mode == QIcon::Normal) ? QIcon::Active : QIcon::Normal;
+ if ((pe = tryMatch(size, oppositeMode, state)))
+ break;
+ if ((pe = tryMatch(size, mode, oppositeState)))
+ break;
+ if ((pe = tryMatch(size, oppositeMode, oppositeState)))
+ break;
+ if ((pe = tryMatch(size, QIcon::Disabled, state)))
+ break;
+ if ((pe = tryMatch(size, QIcon::Selected, state)))
+ break;
+ if ((pe = tryMatch(size, QIcon::Disabled, oppositeState)))
+ break;
+ if ((pe = tryMatch(size, QIcon::Selected, oppositeState)))
+ break;
+ }
+
+ if (!pe)
+ return pe;
+ }
+
+ if (sizeOnly ? (pe->size.isNull() || !pe->size.isValid()) : pe->pixmap.isNull()) {
+ pe->pixmap = QPixmap(pe->fileName);
+ if (!pe->pixmap.isNull())
+ pe->size = pe->pixmap.size();
+ }
+
+ return pe;
+}
+
+QPixmap QPixmapIconEngine::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state)
+{
+ QPixmap pm;
+ QPixmapIconEngineEntry *pe = bestMatch(size, mode, state, false);
+ if (pe)
+ pm = pe->pixmap;
+
+ if (pm.isNull()) {
+ int idx = pixmaps.count();
+ while (--idx >= 0) {
+ if (pe == &pixmaps[idx]) {
+ pixmaps.remove(idx);
+ break;
+ }
+ }
+ if (pixmaps.isEmpty())
+ return pm;
+ else
+ return pixmap(size, mode, state);
+ }
+
+ QSize actualSize = pm.size();
+ if (!actualSize.isNull() && (actualSize.width() > size.width() || actualSize.height() > size.height()))
+ actualSize.scale(size, Qt::KeepAspectRatio);
+
+ QString key = QLatin1Literal("qt_")
+ % HexString<quint64>(pm.cacheKey())
+ % HexString<uint>(pe->mode)
+ % HexString<quint64>(QApplication::palette().cacheKey())
+ % HexString<uint>(actualSize.width())
+ % HexString<uint>(actualSize.height());
+
+ if (mode == QIcon::Active) {
+ if (QPixmapCache::find(key % HexString<uint>(mode), pm))
+ return pm; // horray
+ if (QPixmapCache::find(key % HexString<uint>(QIcon::Normal), pm)) {
+ QStyleOption opt(0);
+ opt.palette = QApplication::palette();
+ QPixmap active = QApplication::style()->generatedIconPixmap(QIcon::Active, pm, &opt);
+ if (pm.cacheKey() == active.cacheKey())
+ return pm;
+ }
+ }
+
+ if (!QPixmapCache::find(key % HexString<uint>(mode), pm)) {
+ if (pm.size() != actualSize)
+ pm = pm.scaled(actualSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+ if (pe->mode != mode && mode != QIcon::Normal) {
+ QStyleOption opt(0);
+ opt.palette = QApplication::palette();
+ QPixmap generated = QApplication::style()->generatedIconPixmap(mode, pm, &opt);
+ if (!generated.isNull())
+ pm = generated;
+ }
+ QPixmapCache::insert(key % HexString<uint>(mode), pm);
+ }
+ return pm;
+}
+
+QSize QPixmapIconEngine::actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state)
+{
+ QSize actualSize;
+ if (QPixmapIconEngineEntry *pe = bestMatch(size, mode, state, true))
+ actualSize = pe->size;
+
+ if (actualSize.isNull())
+ return actualSize;
+
+ if (!actualSize.isNull() && (actualSize.width() > size.width() || actualSize.height() > size.height()))
+ actualSize.scale(size, Qt::KeepAspectRatio);
+ return actualSize;
+}
+
+void QPixmapIconEngine::addPixmap(const QPixmap &pixmap, QIcon::Mode mode, QIcon::State state)
+{
+ if (!pixmap.isNull()) {
+ QPixmapIconEngineEntry *pe = tryMatch(pixmap.size(), mode, state);
+ if(pe && pe->size == pixmap.size()) {
+ pe->pixmap = pixmap;
+ pe->fileName.clear();
+ } else {
+ pixmaps += QPixmapIconEngineEntry(pixmap, mode, state);
+ }
+ }
+}
+
+void QPixmapIconEngine::addFile(const QString &fileName, const QSize &_size, QIcon::Mode mode, QIcon::State state)
+{
+ if (!fileName.isEmpty()) {
+ QSize size = _size;
+ QPixmap pixmap;
+
+ QString abs = fileName;
+ if (fileName.at(0) != QLatin1Char(':'))
+ abs = QFileInfo(fileName).absoluteFilePath();
+
+ for (int i = 0; i < pixmaps.count(); ++i) {
+ if (pixmaps.at(i).mode == mode && pixmaps.at(i).state == state) {
+ QPixmapIconEngineEntry *pe = &pixmaps[i];
+ if(size == QSize()) {
+ pixmap = QPixmap(abs);
+ size = pixmap.size();
+ }
+ if (pe->size == QSize() && pe->pixmap.isNull()) {
+ pe->pixmap = QPixmap(pe->fileName);
+ pe->size = pe->pixmap.size();
+ }
+ if(pe->size == size) {
+ pe->pixmap = pixmap;
+ pe->fileName = abs;
+ return;
+ }
+ }
+ }
+ QPixmapIconEngineEntry e(abs, size, mode, state);
+ e.pixmap = pixmap;
+ pixmaps += e;
+ }
+}
+
+QString QPixmapIconEngine::key() const
+{
+ return QLatin1String("QPixmapIconEngine");
+}
+
+QIconEngineV2 *QPixmapIconEngine::clone() const
+{
+ return new QPixmapIconEngine(*this);
+}
+
+bool QPixmapIconEngine::read(QDataStream &in)
+{
+ int num_entries;
+ QPixmap pm;
+ QString fileName;
+ QSize sz;
+ uint mode;
+ uint state;
+
+ in >> num_entries;
+ for (int i=0; i < num_entries; ++i) {
+ if (in.atEnd()) {
+ pixmaps.clear();
+ return false;
+ }
+ in >> pm;
+ in >> fileName;
+ in >> sz;
+ in >> mode;
+ in >> state;
+ if (pm.isNull()) {
+ addFile(fileName, sz, QIcon::Mode(mode), QIcon::State(state));
+ } else {
+ QPixmapIconEngineEntry pe(fileName, sz, QIcon::Mode(mode), QIcon::State(state));
+ pe.pixmap = pm;
+ pixmaps += pe;
+ }
+ }
+ return true;
+}
+
+bool QPixmapIconEngine::write(QDataStream &out) const
+{
+ int num_entries = pixmaps.size();
+ out << num_entries;
+ for (int i=0; i < num_entries; ++i) {
+ if (pixmaps.at(i).pixmap.isNull())
+ out << QPixmap(pixmaps.at(i).fileName);
+ else
+ out << pixmaps.at(i).pixmap;
+ out << pixmaps.at(i).fileName;
+ out << pixmaps.at(i).size;
+ out << (uint) pixmaps.at(i).mode;
+ out << (uint) pixmaps.at(i).state;
+ }
+ return true;
+}
+
+void QPixmapIconEngine::virtual_hook(int id, void *data)
+{
+ switch (id) {
+ case QIconEngineV2::AvailableSizesHook: {
+ QIconEngineV2::AvailableSizesArgument &arg =
+ *reinterpret_cast<QIconEngineV2::AvailableSizesArgument*>(data);
+ arg.sizes.clear();
+ for (int i = 0; i < pixmaps.size(); ++i) {
+ QPixmapIconEngineEntry &pe = pixmaps[i];
+ if (pe.size == QSize() && pe.pixmap.isNull()) {
+ pe.pixmap = QPixmap(pe.fileName);
+ pe.size = pe.pixmap.size();
+ }
+ if (pe.mode == arg.mode && pe.state == arg.state && !pe.size.isEmpty())
+ arg.sizes.push_back(pe.size);
+ }
+ break;
+ }
+ default:
+ QIconEngineV2::virtual_hook(id, data);
+ }
+}
+
+#ifndef QT_NO_LIBRARY
+Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
+ (QIconEngineFactoryInterface_iid, QLatin1String("/iconengines"), Qt::CaseInsensitive))
+Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loaderV2,
+ (QIconEngineFactoryInterfaceV2_iid, QLatin1String("/iconengines"), Qt::CaseInsensitive))
+#endif
+
+
+
+/*!
+ \class QIcon
+
+ \brief The QIcon class provides scalable icons in different modes
+ and states.
+
+ \ingroup painting
+ \ingroup shared
+
+
+ A QIcon can generate smaller, larger, active, and disabled pixmaps
+ from the set of pixmaps it is given. Such pixmaps are used by Qt
+ widgets to show an icon representing a particular action.
+
+ The simplest use of QIcon is to create one from a QPixmap file or
+ resource, and then use it, allowing Qt to work out all the required
+ icon styles and sizes. For example:
+
+ \snippet doc/src/snippets/code/src_gui_image_qicon.cpp 0
+
+ To undo a QIcon, simply set a null icon in its place:
+
+ \snippet doc/src/snippets/code/src_gui_image_qicon.cpp 1
+
+ Use the QImageReader::supportedImageFormats() and
+ QImageWriter::supportedImageFormats() functions to retrieve a
+ complete list of the supported file formats.
+
+ When you retrieve a pixmap using pixmap(QSize, Mode, State), and no
+ pixmap for this given size, mode and state has been added with
+ addFile() or addPixmap(), then QIcon will generate one on the
+ fly. This pixmap generation happens in a QIconEngineV2. The default
+ engine scales pixmaps down if required, but never up, and it uses
+ the current style to calculate a disabled appearance. By using
+ custom icon engines, you can customize every aspect of generated
+ icons. With QIconEnginePluginV2 it is possible to register different
+ icon engines for different file suffixes, making it possible for
+ third parties to provide additional icon engines to those included
+ with Qt.
+
+ \note Since Qt 4.2, an icon engine that supports SVG is included.
+
+ \section1 Making Classes that Use QIcon
+
+ If you write your own widgets that have an option to set a small
+ pixmap, consider allowing a QIcon to be set for that pixmap. The
+ Qt class QToolButton is an example of such a widget.
+
+ Provide a method to set a QIcon, and when you draw the icon, choose
+ whichever pixmap is appropriate for the current state of your widget.
+ For example:
+ \snippet doc/src/snippets/code/src_gui_image_qicon.cpp 2
+
+ You might also make use of the \c Active mode, perhaps making your
+ widget \c Active when the mouse is over the widget (see \l
+ QWidget::enterEvent()), while the mouse is pressed pending the
+ release that will activate the function, or when it is the currently
+ selected item. If the widget can be toggled, the "On" mode might be
+ used to draw a different icon.
+
+ \img icon.png QIcon
+
+ \sa {fowler}{GUI Design Handbook: Iconic Label}, {Icons Example}
+*/
+
+
+/*!
+ Constructs a null icon.
+*/
+QIcon::QIcon()
+ : d(0)
+{
+}
+
+/*!
+ Constructs an icon from a \a pixmap.
+ */
+QIcon::QIcon(const QPixmap &pixmap)
+ :d(0)
+{
+ addPixmap(pixmap);
+}
+
+/*!
+ Constructs a copy of \a other. This is very fast.
+*/
+QIcon::QIcon(const QIcon &other)
+ :d(other.d)
+{
+ if (d)
+ d->ref.ref();
+}
+
+/*!
+ Constructs an icon from the file with the given \a fileName. The
+ file will be loaded on demand.
+
+ If \a fileName contains a relative path (e.g. the filename only)
+ the relevant file must be found relative to the runtime working
+ directory.
+
+ The file name can be either refer to an actual file on disk or to
+ one of the application's embedded resources. See the
+ \l{resources.html}{Resource System} overview for details on how to
+ embed images and other resource files in the application's
+ executable.
+
+ Use the QImageReader::supportedImageFormats() and
+ QImageWriter::supportedImageFormats() functions to retrieve a
+ complete list of the supported file formats.
+*/
+QIcon::QIcon(const QString &fileName)
+ : d(0)
+{
+ addFile(fileName);
+}
+
+
+/*!
+ Creates an icon with a specific icon \a engine. The icon takes
+ ownership of the engine.
+*/
+QIcon::QIcon(QIconEngine *engine)
+ :d(new QIconPrivate)
+{
+ d->engine_version = 1;
+ d->engine = engine;
+ d->v1RefCount = new QAtomicInt(1);
+}
+
+/*!
+ Creates an icon with a specific icon \a engine. The icon takes
+ ownership of the engine.
+*/
+QIcon::QIcon(QIconEngineV2 *engine)
+ :d(new QIconPrivate)
+{
+ d->engine_version = 2;
+ d->engine = engine;
+}
+
+/*!
+ Destroys the icon.
+*/
+QIcon::~QIcon()
+{
+ if (d && !d->ref.deref())
+ delete d;
+}
+
+/*!
+ Assigns the \a other icon to this icon and returns a reference to
+ this icon.
+*/
+QIcon &QIcon::operator=(const QIcon &other)
+{
+ if (other.d)
+ other.d->ref.ref();
+ if (d && !d->ref.deref())
+ delete d;
+ d = other.d;
+ return *this;
+}
+
+/*!
+ \fn void QIcon::swap(QIcon &other)
+ \since 4.8
+
+ Swaps icon \a other with this icon. This operation is very
+ fast and never fails.
+*/
+
+/*!
+ Returns the icon as a QVariant.
+*/
+QIcon::operator QVariant() const
+{
+ return QVariant(QVariant::Icon, this);
+}
+
+/*! \obsolete
+
+ Returns a number that identifies the contents of this
+ QIcon object. Distinct QIcon objects can have
+ the same serial number if they refer to the same contents
+ (but they don't have to). Also, the serial number of
+ a QIcon object may change during its lifetime.
+
+ Use cacheKey() instead.
+
+ A null icon always has a serial number of 0.
+
+ Serial numbers are mostly useful in conjunction with caching.
+
+ \sa QPixmap::serialNumber()
+*/
+
+int QIcon::serialNumber() const
+{
+ return d ? d->serialNum : 0;
+}
+
+/*!
+ Returns a number that identifies the contents of this QIcon
+ object. Distinct QIcon objects can have the same key if
+ they refer to the same contents.
+ \since 4.3
+
+ The cacheKey() will change when the icon is altered via
+ addPixmap() or addFile().
+
+ Cache keys are mostly useful in conjunction with caching.
+
+ \sa QPixmap::cacheKey()
+*/
+qint64 QIcon::cacheKey() const
+{
+ if (!d)
+ return 0;
+ return (((qint64) d->serialNum) << 32) | ((qint64) (d->detach_no));
+}
+
+/*!
+ Returns a pixmap with the requested \a size, \a mode, and \a
+ state, generating one if necessary. The pixmap might be smaller than
+ requested, but never larger.
+
+ \sa actualSize(), paint()
+*/
+QPixmap QIcon::pixmap(const QSize &size, Mode mode, State state) const
+{
+ if (!d)
+ return QPixmap();
+ return d->engine->pixmap(size, mode, state);
+}
+
+/*!
+ \fn QPixmap QIcon::pixmap(int w, int h, Mode mode = Normal, State state = Off) const
+
+ \overload
+
+ Returns a pixmap of size QSize(\a w, \a h). The pixmap might be smaller than
+ requested, but never larger.
+*/
+
+/*!
+ \fn QPixmap QIcon::pixmap(int extent, Mode mode = Normal, State state = Off) const
+
+ \overload
+
+ Returns a pixmap of size QSize(\a extent, \a extent). The pixmap might be smaller
+ than requested, but never larger.
+*/
+
+/*! Returns the actual size of the icon for the requested \a size, \a
+ mode, and \a state. The result might be smaller than requested, but
+ never larger.
+
+ \sa pixmap(), paint()
+*/
+QSize QIcon::actualSize(const QSize &size, Mode mode, State state) const
+{
+ if (!d)
+ return QSize();
+ return d->engine->actualSize(size, mode, state);
+}
+
+
+/*!
+ Uses the \a painter to paint the icon with specified \a alignment,
+ required \a mode, and \a state into the rectangle \a rect.
+
+ \sa actualSize(), pixmap()
+*/
+void QIcon::paint(QPainter *painter, const QRect &rect, Qt::Alignment alignment, Mode mode, State state) const
+{
+ if (!d || !painter)
+ return;
+ QRect alignedRect = QStyle::alignedRect(painter->layoutDirection(), alignment, d->engine->actualSize(rect.size(), mode, state), rect);
+ d->engine->paint(painter, alignedRect, mode, state);
+}
+
+/*!
+ \fn void QIcon::paint(QPainter *painter, int x, int y, int w, int h, Qt::Alignment alignment,
+ Mode mode, State state) const
+
+ \overload
+
+ Paints the icon into the rectangle QRect(\a x, \a y, \a w, \a h).
+*/
+
+/*!
+ Returns true if the icon is empty; otherwise returns false.
+
+ An icon is empty if it has neither a pixmap nor a filename.
+
+ Note: Even a non-null icon might not be able to create valid
+ pixmaps, eg. if the file does not exist or cannot be read.
+*/
+bool QIcon::isNull() const
+{
+ return !d;
+}
+
+/*!\internal
+ */
+bool QIcon::isDetached() const
+{
+ return !d || d->ref == 1;
+}
+
+/*! \internal
+ */
+void QIcon::detach()
+{
+ if (d) {
+ if (d->ref != 1) {
+ QIconPrivate *x = new QIconPrivate;
+ if (d->engine_version > 1) {
+ QIconEngineV2 *engine = static_cast<QIconEngineV2 *>(d->engine);
+ x->engine = engine->clone();
+ } else {
+ x->engine = d->engine;
+ x->v1RefCount = d->v1RefCount;
+ x->v1RefCount->ref();
+ }
+ x->engine_version = d->engine_version;
+ if (!d->ref.deref())
+ delete d;
+ d = x;
+ }
+ ++d->detach_no;
+ }
+}
+
+/*!
+ Adds \a pixmap to the icon, as a specialization for \a mode and
+ \a state.
+
+ Custom icon engines are free to ignore additionally added
+ pixmaps.
+
+ \sa addFile()
+*/
+void QIcon::addPixmap(const QPixmap &pixmap, Mode mode, State state)
+{
+ if (pixmap.isNull())
+ return;
+ if (!d) {
+ d = new QIconPrivate;
+ d->engine = new QPixmapIconEngine;
+ } else {
+ detach();
+ }
+ d->engine->addPixmap(pixmap, mode, state);
+}
+
+
+/*! Adds an image from the file with the given \a fileName to the
+ icon, as a specialization for \a size, \a mode and \a state. The
+ file will be loaded on demand. Note: custom icon engines are free
+ to ignore additionally added pixmaps.
+
+ If \a fileName contains a relative path (e.g. the filename only)
+ the relevant file must be found relative to the runtime working
+ directory.
+
+ The file name can be either refer to an actual file on disk or to
+ one of the application's embedded resources. See the
+ \l{resources.html}{Resource System} overview for details on how to
+ embed images and other resource files in the application's
+ executable.
+
+ Use the QImageReader::supportedImageFormats() and
+ QImageWriter::supportedImageFormats() functions to retrieve a
+ complete list of the supported file formats.
+
+ Note: When you add a non-empty filename to a QIcon, the icon becomes
+ non-null, even if the file doesn't exist or points to a corrupt file.
+
+ \sa addPixmap()
+ */
+void QIcon::addFile(const QString &fileName, const QSize &size, Mode mode, State state)
+{
+ if (fileName.isEmpty())
+ return;
+ if (!d) {
+#if !defined (QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
+ QFileInfo info(fileName);
+ QString suffix = info.suffix();
+ if (!suffix.isEmpty()) {
+ // first try version 2 engines..
+ if (QIconEngineFactoryInterfaceV2 *factory = qobject_cast<QIconEngineFactoryInterfaceV2*>(loaderV2()->instance(suffix))) {
+ if (QIconEngine *engine = factory->create(fileName)) {
+ d = new QIconPrivate;
+ d->engine = engine;
+ }
+ }
+ // ..then fall back and try to load version 1 engines
+ if (!d) {
+ if (QIconEngineFactoryInterface *factory = qobject_cast<QIconEngineFactoryInterface*>(loader()->instance(suffix))) {
+ if (QIconEngine *engine = factory->create(fileName)) {
+ d = new QIconPrivate;
+ d->engine = engine;
+ d->engine_version = 1;
+ d->v1RefCount = new QAtomicInt(1);
+ }
+ }
+ }
+ }
+#endif
+ // ...then fall back to the default engine
+ if (!d) {
+ d = new QIconPrivate;
+ d->engine = new QPixmapIconEngine;
+ }
+ } else {
+ detach();
+ }
+ d->engine->addFile(fileName, size, mode, state);
+}
+
+/*!
+ \since 4.5
+
+ Returns a list of available icon sizes for the specified \a mode and
+ \a state.
+*/
+QList<QSize> QIcon::availableSizes(Mode mode, State state) const
+{
+ if (!d || !d->engine || d->engine_version < 2)
+ return QList<QSize>();
+ QIconEngineV2 *engine = static_cast<QIconEngineV2*>(d->engine);
+ return engine->availableSizes(mode, state);
+}
+
+/*!
+ \since 4.7
+
+ Returns the name used to create the icon, if available.
+
+ Depending on the way the icon was created, it may have an associated
+ name. This is the case for icons created with fromTheme() or icons
+ using a QIconEngine which supports the QIconEngineV2::IconNameHook.
+
+ \sa fromTheme(), QIconEngine
+*/
+QString QIcon::name() const
+{
+ if (!d || !d->engine || d->engine_version < 2)
+ return QString();
+ QIconEngineV2 *engine = static_cast<QIconEngineV2*>(d->engine);
+ return engine->iconName();
+}
+
+/*!
+ \since 4.6
+
+ Sets the search paths for icon themes to \a paths.
+ \sa themeSearchPaths(), fromTheme(), setThemeName()
+*/
+void QIcon::setThemeSearchPaths(const QStringList &paths)
+{
+ QIconLoader::instance()->setThemeSearchPath(paths);
+}
+
+/*!
+ \since 4.6
+
+ Returns the search paths for icon themes.
+
+ The default value will depend on the platform:
+
+ On X11, the search path will use the XDG_DATA_DIRS environment
+ variable if available.
+
+ By default all platforms will have the resource directory
+ \c{:\icons} as a fallback. You can use "rcc -project" to generate a
+ resource file from your icon theme.
+
+ \sa setThemeSearchPaths(), fromTheme(), setThemeName()
+*/
+QStringList QIcon::themeSearchPaths()
+{
+ return QIconLoader::instance()->themeSearchPaths();
+}
+
+/*!
+ \since 4.6
+
+ Sets the current icon theme to \a name.
+
+ The \a name should correspond to a directory name in the
+ themeSearchPath() containing an index.theme
+ file describing it's contents.
+
+ \sa themeSearchPaths(), themeName()
+*/
+void QIcon::setThemeName(const QString &name)
+{
+ QIconLoader::instance()->setThemeName(name);
+}
+
+/*!
+ \since 4.6
+
+ Returns the name of the current icon theme.
+
+ On X11, the current icon theme depends on your desktop
+ settings. On other platforms it is not set by default.
+
+ \sa setThemeName(), themeSearchPaths(), fromTheme(),
+ hasThemeIcon()
+*/
+QString QIcon::themeName()
+{
+ return QIconLoader::instance()->themeName();
+}
+
+/*!
+ \since 4.6
+
+ Returns the QIcon corresponding to \a name in the current
+ icon theme. If no such icon is found in the current theme
+ \a fallback is returned instead.
+
+ The latest version of the freedesktop icon specification and naming
+ specification can be obtained here:
+
+ \list
+ \o \l{http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html}
+ \o \l{http://standards.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html}
+ \endlist
+
+ To fetch an icon from the current icon theme:
+
+ \snippet doc/src/snippets/code/src_gui_image_qicon.cpp 3
+
+ Or if you want to provide a guaranteed fallback for platforms that
+ do not support theme icons, you can use the second argument:
+
+ \snippet doc/src/snippets/code/src_gui_image_qicon.cpp 4
+
+ \note By default, only X11 will support themed icons. In order to
+ use themed icons on Mac and Windows, you will have to bundle a
+ compliant theme in one of your themeSearchPaths() and set the
+ appropriate themeName().
+
+ \sa themeName(), setThemeName(), themeSearchPaths()
+*/
+QIcon QIcon::fromTheme(const QString &name, const QIcon &fallback)
+{
+ QIcon icon;
+
+ if (qtIconCache()->contains(name)) {
+ icon = *qtIconCache()->object(name);
+ } else {
+ QIcon *cachedIcon = new QIcon(new QIconLoaderEngine(name));
+ qtIconCache()->insert(name, cachedIcon);
+ icon = *cachedIcon;
+ }
+
+ // Note the qapp check is to allow lazy loading of static icons
+ // Supporting fallbacks will not work for this case.
+ if (qApp && icon.availableSizes().isEmpty())
+ return fallback;
+
+ return icon;
+}
+
+/*!
+ \since 4.6
+
+ Returns true if there is an icon available for \a name in the
+ current icon theme, otherwise returns false.
+
+ \sa themeSearchPaths(), fromTheme(), setThemeName()
+*/
+bool QIcon::hasThemeIcon(const QString &name)
+{
+ QIcon icon = fromTheme(name);
+
+ return !icon.isNull();
+}
+
+
+/*****************************************************************************
+ QIcon stream functions
+ *****************************************************************************/
+#if !defined(QT_NO_DATASTREAM)
+/*!
+ \fn QDataStream &operator<<(QDataStream &stream, const QIcon &icon)
+ \relates QIcon
+ \since 4.2
+
+ Writes the given \a icon to the given \a stream as a PNG
+ image. If the icon contains more than one image, all images will
+ be written to the stream. Note that writing the stream to a file
+ will not produce a valid image file.
+*/
+
+QDataStream &operator<<(QDataStream &s, const QIcon &icon)
+{
+ if (s.version() >= QDataStream::Qt_4_3) {
+ if (icon.isNull()) {
+ s << QString();
+ } else {
+ if (icon.d->engine_version > 1) {
+ QIconEngineV2 *engine = static_cast<QIconEngineV2 *>(icon.d->engine);
+ s << engine->key();
+ engine->write(s);
+ } else {
+ // not really supported
+ qWarning("QIcon: Cannot stream QIconEngine. Use QIconEngineV2 instead.");
+ }
+ }
+ } else if (s.version() == QDataStream::Qt_4_2) {
+ if (icon.isNull()) {
+ s << 0;
+ } else {
+ QPixmapIconEngine *engine = static_cast<QPixmapIconEngine *>(icon.d->engine);
+ int num_entries = engine->pixmaps.size();
+ s << num_entries;
+ for (int i=0; i < num_entries; ++i) {
+ s << engine->pixmaps.at(i).pixmap;
+ s << engine->pixmaps.at(i).fileName;
+ s << engine->pixmaps.at(i).size;
+ s << (uint) engine->pixmaps.at(i).mode;
+ s << (uint) engine->pixmaps.at(i).state;
+ }
+ }
+ } else {
+ s << QPixmap(icon.pixmap(22,22));
+ }
+ return s;
+}
+
+/*!
+ \fn QDataStream &operator>>(QDataStream &stream, QIcon &icon)
+ \relates QIcon
+ \since 4.2
+
+ Reads an image, or a set of images, from the given \a stream into
+ the given \a icon.
+*/
+
+QDataStream &operator>>(QDataStream &s, QIcon &icon)
+{
+ if (s.version() >= QDataStream::Qt_4_3) {
+ icon = QIcon();
+ QString key;
+ s >> key;
+ if (key == QLatin1String("QPixmapIconEngine")) {
+ icon.d = new QIconPrivate;
+ QIconEngineV2 *engine = new QPixmapIconEngine;
+ icon.d->engine = engine;
+ engine->read(s);
+ } else if (key == QLatin1String("QIconLoaderEngine")) {
+ icon.d = new QIconPrivate;
+ QIconEngineV2 *engine = new QIconLoaderEngine();
+ icon.d->engine = engine;
+ engine->read(s);
+#if !defined (QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
+ } else if (QIconEngineFactoryInterfaceV2 *factory = qobject_cast<QIconEngineFactoryInterfaceV2*>(loaderV2()->instance(key))) {
+ if (QIconEngineV2 *engine= factory->create()) {
+ icon.d = new QIconPrivate;
+ icon.d->engine = engine;
+ engine->read(s);
+ }
+#endif
+ }
+ } else if (s.version() == QDataStream::Qt_4_2) {
+ icon = QIcon();
+ int num_entries;
+ QPixmap pm;
+ QString fileName;
+ QSize sz;
+ uint mode;
+ uint state;
+
+ s >> num_entries;
+ for (int i=0; i < num_entries; ++i) {
+ s >> pm;
+ s >> fileName;
+ s >> sz;
+ s >> mode;
+ s >> state;
+ if (pm.isNull())
+ icon.addFile(fileName, sz, QIcon::Mode(mode), QIcon::State(state));
+ else
+ icon.addPixmap(pm, QIcon::Mode(mode), QIcon::State(state));
+ }
+ } else {
+ QPixmap pm;
+ s >> pm;
+ icon.addPixmap(pm);
+ }
+ return s;
+}
+
+#endif //QT_NO_DATASTREAM
+
+
+#ifdef QT3_SUPPORT
+
+static int widths[2] = { 22, 32 };
+static int heights[2] = { 22, 32 };
+
+static QSize pixmapSizeHelper(QIcon::Size which)
+{
+ int i = 0;
+ if (which == QIcon::Large)
+ i = 1;
+ return QSize(widths[i], heights[i]);
+}
+
+/*!
+ \enum QIcon::Size
+ \compat
+
+ \value Small Use QStyle::pixelMetric(QStyle::PM_SmallIconSize) instead.
+ \value Large Use QStyle::pixelMetric(QStyle::PM_LargeIconSize) instead.
+ \value Automatic N/A.
+*/
+
+/*!
+ Use pixmap(QSize(...), \a mode, \a state), where the first
+ argument is an appropriate QSize instead of a \l Size value.
+
+ \sa pixmapSize()
+*/
+QPixmap QIcon::pixmap(Size size, Mode mode, State state) const
+{ return pixmap(pixmapSizeHelper(size), mode, state); }
+
+/*!
+ Use pixmap(QSize(...), mode, \a state), where the first argument
+ is an appropriate QSize instead of a \l Size value, and the
+ second argument is QIcon::Normal or QIcon::Disabled, depending on
+ the value of \a enabled.
+
+ \sa pixmapSize()
+*/
+QPixmap QIcon::pixmap(Size size, bool enabled, State state) const
+{ return pixmap(pixmapSizeHelper(size), enabled ? Normal : Disabled, state); }
+
+/*!
+ Use one of the other pixmap() overloads.
+*/
+QPixmap QIcon::pixmap() const
+{ return pixmap(pixmapSizeHelper(Small), Normal, Off); }
+
+/*!
+ The pixmap() function now takes a QSize instead of a QIcon::Size,
+ so there is no need for this function in new code.
+*/
+void QIcon::setPixmapSize(Size which, const QSize &size)
+{
+ int i = 0;
+ if (which == Large)
+ i = 1;
+ widths[i] = size.width();
+ heights[i] = size.height();
+}
+
+/*!
+ Use QStyle::pixelMetric() with QStyle::PM_SmallIconSize or
+ QStyle::PM_LargeIconSize as the first argument, depending on \a
+ which.
+*/
+QSize QIcon::pixmapSize(Size which)
+{
+ return pixmapSizeHelper(which);
+}
+
+/*!
+ \fn void QIcon::reset(const QPixmap &pixmap, Size size)
+
+ Use the constructor that takes a QPixmap and operator=().
+*/
+
+/*!
+ \fn void QIcon::setPixmap(const QPixmap &pixmap, Size size, Mode mode, State state)
+
+ Use addPixmap(\a pixmap, \a mode, \a state) instead. The \a size
+ parameter is ignored.
+*/
+
+/*!
+ \fn void QIcon::setPixmap(const QString &fileName, Size size, Mode mode, State state)
+
+ Use addFile(\a fileName, \a mode, \a state) instead. The \a size
+ parameter is ignored.
+*/
+
+#endif // QT3_SUPPORT
+
+/*!
+ \fn DataPtr &QIcon::data_ptr()
+ \internal
+*/
+
+/*!
+ \typedef QIcon::DataPtr
+ \internal
+*/
+
+QT_END_NAMESPACE
+#endif //QT_NO_ICON
diff --git a/src/gui/image/qicon.h b/src/gui/image/qicon.h
new file mode 100644
index 0000000000..170559182b
--- /dev/null
+++ b/src/gui/image/qicon.h
@@ -0,0 +1,162 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QICON_H
+#define QICON_H
+
+#include <QtCore/qglobal.h>
+#include <QtCore/qsize.h>
+#include <QtCore/qlist.h>
+#include <QtGui/qpixmap.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QIconPrivate;
+class QIconEngine;
+class QIconEngineV2;
+
+class Q_GUI_EXPORT QIcon
+{
+public:
+ enum Mode { Normal, Disabled, Active, Selected };
+ enum State { On, Off };
+
+ QIcon();
+ QIcon(const QPixmap &pixmap);
+ QIcon(const QIcon &other);
+ explicit QIcon(const QString &fileName); // file or resource name
+ explicit QIcon(QIconEngine *engine);
+ explicit QIcon(QIconEngineV2 *engine);
+ ~QIcon();
+ QIcon &operator=(const QIcon &other);
+#ifdef Q_COMPILER_RVALUE_REFS
+ inline QIcon &operator=(QIcon &&other)
+ { qSwap(d, other.d); return *this; }
+#endif
+ inline void swap(QIcon &other) { qSwap(d, other.d); }
+
+ operator QVariant() const;
+
+ QPixmap pixmap(const QSize &size, Mode mode = Normal, State state = Off) const;
+ inline QPixmap pixmap(int w, int h, Mode mode = Normal, State state = Off) const
+ { return pixmap(QSize(w, h), mode, state); }
+ inline QPixmap pixmap(int extent, Mode mode = Normal, State state = Off) const
+ { return pixmap(QSize(extent, extent), mode, state); }
+
+ QSize actualSize(const QSize &size, Mode mode = Normal, State state = Off) const;
+
+ QString name() const;
+
+ void paint(QPainter *painter, const QRect &rect, Qt::Alignment alignment = Qt::AlignCenter, Mode mode = Normal, State state = Off) const;
+ inline void paint(QPainter *painter, int x, int y, int w, int h, Qt::Alignment alignment = Qt::AlignCenter, Mode mode = Normal, State state = Off) const
+ { paint(painter, QRect(x, y, w, h), alignment, mode, state); }
+
+ bool isNull() const;
+ bool isDetached() const;
+ void detach();
+
+ int serialNumber() const;
+ qint64 cacheKey() const;
+
+ void addPixmap(const QPixmap &pixmap, Mode mode = Normal, State state = Off);
+ void addFile(const QString &fileName, const QSize &size = QSize(), Mode mode = Normal, State state = Off);
+
+ QList<QSize> availableSizes(Mode mode = Normal, State state = Off) const;
+
+ static QIcon fromTheme(const QString &name, const QIcon &fallback = QIcon());
+ static bool hasThemeIcon(const QString &name);
+
+ static QStringList themeSearchPaths();
+ static void setThemeSearchPaths(const QStringList &searchpath);
+
+ static QString themeName();
+ static void setThemeName(const QString &path);
+
+
+#ifdef QT3_SUPPORT
+ enum Size { Small, Large, Automatic = Small };
+ static QT3_SUPPORT void setPixmapSize(Size which, const QSize &size);
+ static QT3_SUPPORT QSize pixmapSize(Size which);
+ inline QT3_SUPPORT void reset(const QPixmap &pixmap, Size /*size*/) { *this = QIcon(pixmap); }
+ inline QT3_SUPPORT void setPixmap(const QPixmap &pixmap, Size, Mode mode = Normal, State state = Off)
+ { addPixmap(pixmap, mode, state); }
+ inline QT3_SUPPORT void setPixmap(const QString &fileName, Size, Mode mode = Normal, State state = Off)
+ { addPixmap(QPixmap(fileName), mode, state); }
+ QT3_SUPPORT QPixmap pixmap(Size size, Mode mode, State state = Off) const;
+ QT3_SUPPORT QPixmap pixmap(Size size, bool enabled, State state = Off) const;
+ QT3_SUPPORT QPixmap pixmap() const;
+#endif
+
+ Q_DUMMY_COMPARISON_OPERATOR(QIcon)
+
+private:
+ QIconPrivate *d;
+#if !defined(QT_NO_DATASTREAM)
+ friend Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QIcon &);
+ friend Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QIcon &);
+#endif
+
+public:
+ typedef QIconPrivate * DataPtr;
+ inline DataPtr &data_ptr() { return d; }
+};
+
+Q_DECLARE_SHARED(QIcon)
+Q_DECLARE_TYPEINFO(QIcon, Q_MOVABLE_TYPE);
+
+#if !defined(QT_NO_DATASTREAM)
+Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QIcon &);
+Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QIcon &);
+#endif
+
+#ifdef QT3_SUPPORT
+typedef QIcon QIconSet;
+#endif
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QICON_H
diff --git a/src/gui/image/qicon_p.h b/src/gui/image/qicon_p.h
new file mode 100644
index 0000000000..0bf7e65cc1
--- /dev/null
+++ b/src/gui/image/qicon_p.h
@@ -0,0 +1,139 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QICON_P_H
+#define QICON_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qglobal.h>
+#include <QtCore/qsize.h>
+#include <QtCore/qlist.h>
+#include <QtGui/qpixmap.h>
+#include <QtGui/qicon.h>
+#include <QtGui/qiconengine.h>
+
+#ifndef QT_NO_ICON
+QT_BEGIN_NAMESPACE
+
+class QIconPrivate
+{
+public:
+ QIconPrivate();
+
+ ~QIconPrivate() {
+ if (engine_version == 1) {
+ if (!v1RefCount->deref()) {
+ delete engine;
+ delete v1RefCount;
+ }
+ } else if (engine_version == 2) {
+ delete engine;
+ }
+ }
+
+ QIconEngine *engine;
+
+ QAtomicInt ref;
+ int serialNum;
+ int detach_no;
+ int engine_version;
+
+ QAtomicInt *v1RefCount;
+};
+
+
+struct QPixmapIconEngineEntry
+{
+ QPixmapIconEngineEntry():mode(QIcon::Normal), state(QIcon::Off){}
+ QPixmapIconEngineEntry(const QPixmap &pm, QIcon::Mode m = QIcon::Normal, QIcon::State s = QIcon::Off)
+ :pixmap(pm), size(pm.size()), mode(m), state(s){}
+ QPixmapIconEngineEntry(const QString &file, const QSize &sz = QSize(), QIcon::Mode m = QIcon::Normal, QIcon::State s = QIcon::Off)
+ :fileName(file), size(sz), mode(m), state(s){}
+ QPixmap pixmap;
+ QString fileName;
+ QSize size;
+ QIcon::Mode mode;
+ QIcon::State state;
+ bool isNull() const {return (fileName.isEmpty() && pixmap.isNull()); }
+};
+
+
+
+class QPixmapIconEngine : public QIconEngineV2 {
+public:
+ QPixmapIconEngine();
+ QPixmapIconEngine(const QPixmapIconEngine &);
+ ~QPixmapIconEngine();
+ void paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state);
+ QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state);
+ QPixmapIconEngineEntry *bestMatch(const QSize &size, QIcon::Mode mode, QIcon::State state, bool sizeOnly);
+ QSize actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state);
+ void addPixmap(const QPixmap &pixmap, QIcon::Mode mode, QIcon::State state);
+ void addFile(const QString &fileName, const QSize &size, QIcon::Mode mode, QIcon::State state);
+
+ // v2 functions
+ QString key() const;
+ QIconEngineV2 *clone() const;
+ bool read(QDataStream &in);
+ bool write(QDataStream &out) const;
+ void virtual_hook(int id, void *data);
+
+private:
+ QPixmapIconEngineEntry *tryMatch(const QSize &size, QIcon::Mode mode, QIcon::State state);
+ QVector<QPixmapIconEngineEntry> pixmaps;
+
+ friend QDataStream &operator<<(QDataStream &s, const QIcon &icon);
+ friend class QIconThemeEngine;
+};
+
+QT_END_NAMESPACE
+#endif //QT_NO_ICON
+#endif // QICON_P_H
diff --git a/src/gui/image/qiconengine.cpp b/src/gui/image/qiconengine.cpp
new file mode 100644
index 0000000000..6168a83940
--- /dev/null
+++ b/src/gui/image/qiconengine.cpp
@@ -0,0 +1,324 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qiconengine.h"
+#include "qpainter.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QIconEngine
+
+ \brief The QIconEngine class provides an abstract base class for QIcon renderers.
+
+ \ingroup painting
+
+ \bold {Use QIconEngineV2 instead.}
+
+ An icon engine provides the rendering functions for a QIcon. Each icon has a
+ corresponding icon engine that is responsible for drawing the icon with a
+ requested size, mode and state.
+
+ The icon is rendered by the paint() function, and the icon can additionally be
+ obtained as a pixmap with the pixmap() function (the default implementation
+ simply uses paint() to achieve this). The addPixmap() function can be used to
+ add new pixmaps to the icon engine, and is used by QIcon to add specialized
+ custom pixmaps.
+
+ The paint(), pixmap(), and addPixmap() functions are all virtual, and can
+ therefore be reimplemented in subclasses of QIconEngine.
+
+ \sa QIconEngineV2, QIconEnginePlugin
+
+*/
+
+/*!
+ \fn virtual void QIconEngine::paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state) = 0;
+
+ Uses the given \a painter to paint the icon with the required \a mode and
+ \a state into the rectangle \a rect.
+*/
+
+/*! Returns the actual size of the icon the engine provides for the
+ requested \a size, \a mode and \a state. The default implementation
+ returns the given \a size.
+ */
+QSize QIconEngine::actualSize(const QSize &size, QIcon::Mode /*mode*/, QIcon::State /*state*/)
+{
+ return size;
+}
+
+
+/*!
+ Destroys the icon engine.
+ */
+QIconEngine::~QIconEngine()
+{
+}
+
+
+/*!
+ Returns the icon as a pixmap with the required \a size, \a mode,
+ and \a state. The default implementation creates a new pixmap and
+ calls paint() to fill it.
+*/
+QPixmap QIconEngine::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state)
+{
+ QPixmap pm(size);
+ {
+ QPainter p(&pm);
+ paint(&p, QRect(QPoint(0,0),size), mode, state);
+ }
+ return pm;
+}
+
+/*!
+ Called by QIcon::addPixmap(). Adds a specialized \a pixmap for the given
+ \a mode and \a state. The default pixmap-based engine stores any supplied
+ pixmaps, and it uses them instead of scaled pixmaps if the size of a pixmap
+ matches the size of icon requested. Custom icon engines that implement
+ scalable vector formats are free to ignores any extra pixmaps.
+ */
+void QIconEngine::addPixmap(const QPixmap &/*pixmap*/, QIcon::Mode /*mode*/, QIcon::State /*state*/)
+{
+}
+
+
+/*! Called by QIcon::addFile(). Adds a specialized pixmap from the
+ file with the given \a fileName, \a size, \a mode and \a state. The
+ default pixmap-based engine stores any supplied file names, and it
+ loads the pixmaps on demand instead of using scaled pixmaps if the
+ size of a pixmap matches the size of icon requested. Custom icon
+ engines that implement scalable vector formats are free to ignores
+ any extra files.
+ */
+void QIconEngine::addFile(const QString &/*fileName*/, const QSize &/*size*/, QIcon::Mode /*mode*/, QIcon::State /*state*/)
+{
+}
+
+
+
+// version 2 functions
+
+
+/*!
+ \class QIconEngineV2
+
+ \brief The QIconEngineV2 class provides an abstract base class for QIcon renderers.
+
+ \ingroup painting
+ \since 4.3
+
+ An icon engine renders \l{QIcon}s. With icon engines, you can
+ customize icons. Qt provides a default engine that makes icons
+ adhere to the current style by scaling the icons and providing a
+ disabled appearance.
+
+ An engine is installed on an icon either through a QIcon
+ constructor or through a QIconEnginePluginV2. The plugins are used
+ by Qt if a specific engine is not given when the icon is created.
+ See the QIconEngineV2 class description to learn how to create
+ icon engine plugins.
+
+ An icon engine provides the rendering functions for a QIcon. Each
+ icon has a corresponding icon engine that is responsible for drawing
+ the icon with a requested size, mode and state.
+
+ QIconEngineV2 extends the API of QIconEngine to allow streaming of
+ the icon engine contents, and should be used instead of QIconEngine
+ for implementing new icon engines.
+
+ \sa QIconEnginePluginV2
+
+*/
+
+/*!
+ \enum QIconEngineV2::IconEngineHook
+ \since 4.5
+
+ These enum values are used for virtual_hook() to allow additional
+ queries to icon engine without breaking binary compatibility.
+
+ \value AvailableSizesHook Allows to query the sizes of the
+ contained pixmaps for pixmap-based engines. The \a data argument
+ of the virtual_hook() function is a AvailableSizesArgument pointer
+ that should be filled with icon sizes. Engines that work in terms
+ of a scalable, vectorial format normally return an empty list.
+
+ \value IconNameHook Allows to query the name used to create the
+ icon, for example when instantiating an icon using
+ QIcon::fromTheme().
+
+ \sa virtual_hook()
+ */
+
+/*!
+ \class QIconEngineV2::AvailableSizesArgument
+ \since 4.5
+
+ This struct represents arguments to virtual_hook() function when
+ \a id parameter is QIconEngineV2::AvailableSizesHook.
+
+ \sa virtual_hook(), QIconEngineV2::IconEngineHook
+ */
+
+/*!
+ \variable QIconEngineV2::AvailableSizesArgument::mode
+ \brief the requested mode of an image.
+
+ \sa QIcon::Mode
+*/
+
+/*!
+ \variable QIconEngineV2::AvailableSizesArgument::state
+ \brief the requested state of an image.
+
+ \sa QIcon::State
+*/
+
+/*!
+ \variable QIconEngineV2::AvailableSizesArgument::sizes
+
+ \brief image sizes that are available with specified \a mode and
+ \a state. This is an output parameter and is filled after call to
+ virtual_hook(). Engines that work in terms of a scalable,
+ vectorial format normally return an empty list.
+*/
+
+
+/*!
+ Returns a key that identifies this icon engine.
+ */
+QString QIconEngineV2::key() const
+{
+ return QString();
+}
+
+/*!
+ Returns a clone of this icon engine.
+ */
+QIconEngineV2 *QIconEngineV2::clone() const
+{
+ return 0;
+}
+
+/*!
+ Reads icon engine contents from the QDataStream \a in. Returns
+ true if the contents were read; otherwise returns false.
+
+ QIconEngineV2's default implementation always return false.
+ */
+bool QIconEngineV2::read(QDataStream &)
+{
+ return false;
+}
+
+/*!
+ Writes the contents of this engine to the QDataStream \a out.
+ Returns true if the contents were written; otherwise returns false.
+
+ QIconEngineV2's default implementation always return false.
+ */
+bool QIconEngineV2::write(QDataStream &) const
+{
+ return false;
+}
+
+/*!
+ \since 4.5
+
+ Additional method to allow extending QIconEngineV2 without
+ adding new virtual methods (and without breaking binary compatibility).
+ The actual action and format of \a data depends on \a id argument
+ which is in fact a constant from IconEngineHook enum.
+
+ \sa IconEngineHook
+*/
+void QIconEngineV2::virtual_hook(int id, void *data)
+{
+ switch (id) {
+ case QIconEngineV2::AvailableSizesHook: {
+ QIconEngineV2::AvailableSizesArgument &arg =
+ *reinterpret_cast<QIconEngineV2::AvailableSizesArgument*>(data);
+ arg.sizes.clear();
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+/*!
+ \since 4.5
+
+ Returns sizes of all images that are contained in the engine for the
+ specific \a mode and \a state.
+
+ \note This is a helper method and the actual work is done by
+ virtual_hook() method, hence this method depends on icon engine support
+ and may not work with all icon engines.
+ */
+QList<QSize> QIconEngineV2::availableSizes(QIcon::Mode mode, QIcon::State state)
+{
+ AvailableSizesArgument arg;
+ arg.mode = mode;
+ arg.state = state;
+ virtual_hook(QIconEngineV2::AvailableSizesHook, reinterpret_cast<void*>(&arg));
+ return arg.sizes;
+}
+
+/*!
+ \since 4.7
+
+ Returns the name used to create the engine, if available.
+
+ \note This is a helper method and the actual work is done by
+ virtual_hook() method, hence this method depends on icon engine support
+ and may not work with all icon engines.
+ */
+QString QIconEngineV2::iconName()
+{
+ QString name;
+ virtual_hook(QIconEngineV2::IconNameHook, reinterpret_cast<void*>(&name));
+ return name;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/image/qiconengine.h b/src/gui/image/qiconengine.h
new file mode 100644
index 0000000000..12caea8a20
--- /dev/null
+++ b/src/gui/image/qiconengine.h
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QICONENGINE_H
+#define QICONENGINE_H
+
+#include <QtCore/qglobal.h>
+#include <QtCore/qlist.h>
+#include <QtGui/qicon.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class Q_GUI_EXPORT QIconEngine
+{
+public:
+ virtual ~QIconEngine();
+ virtual void paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state) = 0;
+ virtual QSize actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state);
+ virtual QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state);
+
+ virtual void addPixmap(const QPixmap &pixmap, QIcon::Mode mode, QIcon::State state);
+ virtual void addFile(const QString &fileName, const QSize &size, QIcon::Mode mode, QIcon::State state);
+
+#if 0
+ virtual int frameCount(QIcon::Mode fromMode, QIcon::State fromState, QIcon::Mode toMode, QIcon::State toState);
+ virtual void paintFrame(QPainter *painter, const QRect &rect, int frameNumber, QIcon::Mode fromMode, QIcon::State fromState, QIcon::Mode toMode, QIcon::State toState);
+#endif
+};
+
+// ### Qt 5: move the below into QIconEngine
+class Q_GUI_EXPORT QIconEngineV2 : public QIconEngine
+{
+public:
+ virtual QString key() const;
+ virtual QIconEngineV2 *clone() const;
+ virtual bool read(QDataStream &in);
+ virtual bool write(QDataStream &out) const;
+ virtual void virtual_hook(int id, void *data);
+
+public:
+ enum IconEngineHook { AvailableSizesHook = 1, IconNameHook };
+
+ struct AvailableSizesArgument
+ {
+ QIcon::Mode mode;
+ QIcon::State state;
+ QList<QSize> sizes;
+ };
+
+ // ### Qt 5: make this function const and virtual.
+ QList<QSize> availableSizes(QIcon::Mode mode = QIcon::Normal,
+ QIcon::State state = QIcon::Off);
+
+ // ### Qt 5: make this function const and virtual.
+ QString iconName();
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QICONENGINE_H
diff --git a/src/gui/image/qiconengineplugin.cpp b/src/gui/image/qiconengineplugin.cpp
new file mode 100644
index 0000000000..7c8c3a3c1a
--- /dev/null
+++ b/src/gui/image/qiconengineplugin.cpp
@@ -0,0 +1,171 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qiconengineplugin.h"
+#include "qiconengine.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QIconEnginePlugin
+ \brief The QIconEnginePlugin class provides an abstract base for custom QIconEngine plugins.
+
+ \ingroup plugins
+
+ \bold {Use QIconEnginePluginV2 instead.}
+
+ The icon engine plugin is a simple plugin interface that makes it easy to
+ create custom icon engines that can be loaded dynamically into applications
+ through QIcon. QIcon uses the file or resource name's suffix to determine
+ what icon engine to use.
+
+ Writing a icon engine plugin is achieved by subclassing this base class,
+ reimplementing the pure virtual functions keys() and create(), and
+ exporting the class with the Q_EXPORT_PLUGIN2() macro.
+
+ \sa {How to Create Qt Plugins}
+*/
+
+/*!
+ \fn QStringList QIconEnginePlugin::keys() const
+
+ Returns a list of icon engine keys that this plugin supports. The keys correspond
+ to the suffix of the file or resource name used when the plugin was created.
+ Keys are case insensitive.
+
+ \sa create()
+*/
+
+/*!
+ \fn QIconEngine* QIconEnginePlugin::create(const QString& filename)
+
+ Creates and returns a QIconEngine object for the icon with the given
+ \a filename.
+
+ \sa keys()
+*/
+
+/*!
+ Constructs a icon engine plugin with the given \a parent. This is invoked
+ automatically by the Q_EXPORT_PLUGIN2() macro.
+*/
+QIconEnginePlugin::QIconEnginePlugin(QObject *parent)
+ : QObject(parent)
+{
+}
+
+/*!
+ Destroys the icon engine plugin.
+
+ You never have to call this explicitly. Qt destroys a plugin
+ automatically when it is no longer used.
+*/
+QIconEnginePlugin::~QIconEnginePlugin()
+{
+}
+
+// version 2
+
+/*!
+ \class QIconEnginePluginV2
+ \brief The QIconEnginePluginV2 class provides an abstract base for custom QIconEngineV2 plugins.
+
+ \ingroup plugins
+ \since 4.3
+
+ Icon engine plugins produces \l{QIconEngine}s for \l{QIcon}s; an
+ icon engine is used to render the icon. The keys that identifies
+ the engines the plugin can create are suffixes of
+ icon filenames; they are returned by keys(). The create() function
+ receives the icon filename to return an engine for; it should
+ return 0 if it cannot produce an engine for the file.
+
+ Writing an icon engine plugin is achieved by inheriting
+ QIconEnginePluginV2, reimplementing keys() and create(), and
+ adding the Q_EXPORT_PLUGIN2() macro.
+
+ You should ensure that you do not duplicate keys. Qt will query
+ the plugins for icon engines in the order in which the plugins are
+ found during plugin search (see the plugins \l{How to Create Qt
+ Plugins}{overview document}).
+
+ \sa {How to Create Qt Plugins}
+*/
+
+/*!
+ \fn QStringList QIconEnginePluginV2::keys() const
+
+ Returns a list of icon engine keys that this plugin supports. The keys correspond
+ to the suffix of the file or resource name used when the plugin was created.
+ Keys are case insensitive.
+
+ \sa create()
+*/
+
+/*!
+ \fn QIconEngineV2* QIconEnginePluginV2::create(const QString& filename = QString())
+
+ Creates and returns a QIconEngine object for the icon with the given
+ \a filename.
+
+ \sa keys()
+*/
+
+/*!
+ Constructs a icon engine plugin with the given \a parent. This is invoked
+ automatically by the Q_EXPORT_PLUGIN2() macro.
+*/
+QIconEnginePluginV2::QIconEnginePluginV2(QObject *parent)
+ : QObject(parent)
+{
+}
+
+/*!
+ Destroys the icon engine plugin.
+
+ You never have to call this explicitly. Qt destroys a plugin
+ automatically when it is no longer used.
+*/
+QIconEnginePluginV2::~QIconEnginePluginV2()
+{
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/image/qiconengineplugin.h b/src/gui/image/qiconengineplugin.h
new file mode 100644
index 0000000000..e892a38f7b
--- /dev/null
+++ b/src/gui/image/qiconengineplugin.h
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QICONENGINEPLUGIN_H
+#define QICONENGINEPLUGIN_H
+
+#include <QtCore/qplugin.h>
+#include <QtCore/qfactoryinterface.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QIconEngine;
+class QIconEngineV2;
+
+struct Q_GUI_EXPORT QIconEngineFactoryInterface : public QFactoryInterface
+{
+ virtual QIconEngine *create(const QString &filename) = 0;
+};
+
+#define QIconEngineFactoryInterface_iid \
+ "com.trolltech.Qt.QIconEngineFactoryInterface"
+Q_DECLARE_INTERFACE(QIconEngineFactoryInterface, QIconEngineFactoryInterface_iid)
+
+class Q_GUI_EXPORT QIconEnginePlugin : public QObject, public QIconEngineFactoryInterface
+{
+ Q_OBJECT
+ Q_INTERFACES(QIconEngineFactoryInterface:QFactoryInterface)
+public:
+ QIconEnginePlugin(QObject *parent = 0);
+ ~QIconEnginePlugin();
+
+ virtual QStringList keys() const = 0;
+ virtual QIconEngine *create(const QString &filename) = 0;
+};
+
+// ### Qt 5: remove version 2
+struct Q_GUI_EXPORT QIconEngineFactoryInterfaceV2 : public QFactoryInterface
+{
+ virtual QIconEngineV2 *create(const QString &filename = QString()) = 0;
+};
+
+#define QIconEngineFactoryInterfaceV2_iid \
+ "com.trolltech.Qt.QIconEngineFactoryInterfaceV2"
+Q_DECLARE_INTERFACE(QIconEngineFactoryInterfaceV2, QIconEngineFactoryInterfaceV2_iid)
+
+class Q_GUI_EXPORT QIconEnginePluginV2 : public QObject, public QIconEngineFactoryInterfaceV2
+{
+ Q_OBJECT
+ Q_INTERFACES(QIconEngineFactoryInterfaceV2:QFactoryInterface)
+public:
+ QIconEnginePluginV2(QObject *parent = 0);
+ ~QIconEnginePluginV2();
+
+ virtual QStringList keys() const = 0;
+ virtual QIconEngineV2 *create(const QString &filename = QString()) = 0;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QICONENGINEPLUGIN_H
diff --git a/src/gui/image/qiconloader.cpp b/src/gui/image/qiconloader.cpp
new file mode 100644
index 0000000000..0a42f0af78
--- /dev/null
+++ b/src/gui/image/qiconloader.cpp
@@ -0,0 +1,570 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QT_NO_ICON
+#include <private/qiconloader_p.h>
+
+#include <private/qapplication_p.h>
+#include <private/qicon_p.h>
+#include <private/qguiplatformplugin_p.h>
+
+#include <QtGui/QIconEnginePlugin>
+#include <QtGui/QPixmapCache>
+#include <QtGui/QIconEngine>
+#include <QtGui/QStyleOption>
+#include <QtCore/QList>
+#include <QtCore/QHash>
+#include <QtCore/QDir>
+#include <QtCore/QSettings>
+#include <QtGui/QPainter>
+
+#ifdef Q_WS_MAC
+#include <private/qt_cocoa_helpers_mac_p.h>
+#endif
+
+#ifdef Q_WS_X11
+#include <private/qt_x11_p.h>
+#endif
+
+#include <private/qstylehelper_p.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_GLOBAL_STATIC(QIconLoader, iconLoaderInstance)
+
+/* Theme to use in last resort, if the theme does not have the icon, neither the parents */
+static QString fallbackTheme()
+{
+#ifdef Q_WS_X11
+ if (X11->desktopEnvironment == DE_GNOME) {
+ return QLatin1String("gnome");
+ } else if (X11->desktopEnvironment == DE_KDE) {
+ return X11->desktopVersion >= 4
+ ? QString::fromLatin1("oxygen")
+ : QString::fromLatin1("crystalsvg");
+ } else {
+ return QLatin1String("hicolor");
+ }
+#endif
+ return QString();
+}
+
+QIconLoader::QIconLoader() :
+ m_themeKey(1), m_supportsSvg(false), m_initialized(false)
+{
+}
+
+// We lazily initialize the loader to make static icons
+// work. Though we do not officially support this.
+void QIconLoader::ensureInitialized()
+{
+ if (!m_initialized) {
+ m_initialized = true;
+
+ Q_ASSERT(qApp);
+
+ m_systemTheme = qt_guiPlatformPlugin()->systemIconThemeName();
+ if (m_systemTheme.isEmpty())
+ m_systemTheme = fallbackTheme();
+#ifndef QT_NO_LIBRARY
+ QFactoryLoader iconFactoryLoader(QIconEngineFactoryInterfaceV2_iid,
+ QLatin1String("/iconengines"),
+ Qt::CaseInsensitive);
+ if (iconFactoryLoader.keys().contains(QLatin1String("svg")))
+ m_supportsSvg = true;
+#endif //QT_NO_LIBRARY
+ }
+}
+
+QIconLoader *QIconLoader::instance()
+{
+ return iconLoaderInstance();
+}
+
+// Queries the system theme and invalidates existing
+// icons if the theme has changed.
+void QIconLoader::updateSystemTheme()
+{
+ // Only change if this is not explicitly set by the user
+ if (m_userTheme.isEmpty()) {
+ QString theme = qt_guiPlatformPlugin()->systemIconThemeName();
+ if (theme.isEmpty())
+ theme = fallbackTheme();
+ if (theme != m_systemTheme) {
+ m_systemTheme = theme;
+ invalidateKey();
+ }
+ }
+}
+
+void QIconLoader::setThemeName(const QString &themeName)
+{
+ m_userTheme = themeName;
+ invalidateKey();
+}
+
+void QIconLoader::setThemeSearchPath(const QStringList &searchPaths)
+{
+ m_iconDirs = searchPaths;
+ themeList.clear();
+ invalidateKey();
+}
+
+QStringList QIconLoader::themeSearchPaths() const
+{
+ if (m_iconDirs.isEmpty()) {
+ m_iconDirs = qt_guiPlatformPlugin()->iconThemeSearchPaths();
+ // Always add resource directory as search path
+ m_iconDirs.append(QLatin1String(":/icons"));
+ }
+ return m_iconDirs;
+}
+
+QIconTheme::QIconTheme(const QString &themeName)
+ : m_valid(false)
+{
+ QFile themeIndex;
+
+ QList <QIconDirInfo> keyList;
+ QStringList iconDirs = QIcon::themeSearchPaths();
+ for ( int i = 0 ; i < iconDirs.size() ; ++i) {
+ QDir iconDir(iconDirs[i]);
+ QString themeDir = iconDir.path() + QLatin1Char('/') + themeName;
+ themeIndex.setFileName(themeDir + QLatin1String("/index.theme"));
+ if (themeIndex.exists()) {
+ m_contentDir = themeDir;
+ m_valid = true;
+ break;
+ }
+ }
+#ifndef QT_NO_SETTINGS
+ if (themeIndex.exists()) {
+ const QSettings indexReader(themeIndex.fileName(), QSettings::IniFormat);
+ QStringListIterator keyIterator(indexReader.allKeys());
+ while (keyIterator.hasNext()) {
+
+ const QString key = keyIterator.next();
+ if (key.endsWith(QLatin1String("/Size"))) {
+ // Note the QSettings ini-format does not accept
+ // slashes in key names, hence we have to cheat
+ if (int size = indexReader.value(key).toInt()) {
+ QString directoryKey = key.left(key.size() - 5);
+ QIconDirInfo dirInfo(directoryKey);
+ dirInfo.size = size;
+ QString type = indexReader.value(directoryKey +
+ QLatin1String("/Type")
+ ).toString();
+
+ if (type == QLatin1String("Fixed"))
+ dirInfo.type = QIconDirInfo::Fixed;
+ else if (type == QLatin1String("Scalable"))
+ dirInfo.type = QIconDirInfo::Scalable;
+ else
+ dirInfo.type = QIconDirInfo::Threshold;
+
+ dirInfo.threshold = indexReader.value(directoryKey +
+ QLatin1String("/Threshold"),
+ 2).toInt();
+
+ dirInfo.minSize = indexReader.value(directoryKey +
+ QLatin1String("/MinSize"),
+ size).toInt();
+
+ dirInfo.maxSize = indexReader.value(directoryKey +
+ QLatin1String("/MaxSize"),
+ size).toInt();
+ m_keyList.append(dirInfo);
+ }
+ }
+ }
+
+ // Parent themes provide fallbacks for missing icons
+ m_parents = indexReader.value(
+ QLatin1String("Icon Theme/Inherits")).toStringList();
+
+ // Ensure a default platform fallback for all themes
+ if (m_parents.isEmpty())
+ m_parents.append(fallbackTheme());
+
+ // Ensure that all themes fall back to hicolor
+ if (!m_parents.contains(QLatin1String("hicolor")))
+ m_parents.append(QLatin1String("hicolor"));
+ }
+#endif //QT_NO_SETTINGS
+}
+
+QThemeIconEntries QIconLoader::findIconHelper(const QString &themeName,
+ const QString &iconName,
+ QStringList &visited) const
+{
+ QThemeIconEntries entries;
+ Q_ASSERT(!themeName.isEmpty());
+
+ QPixmap pixmap;
+
+ // Used to protect against potential recursions
+ visited << themeName;
+
+ QIconTheme theme = themeList.value(themeName);
+ if (!theme.isValid()) {
+ theme = QIconTheme(themeName);
+ if (!theme.isValid())
+ theme = QIconTheme(fallbackTheme());
+
+ themeList.insert(themeName, theme);
+ }
+
+ QString contentDir = theme.contentDir() + QLatin1Char('/');
+ QList<QIconDirInfo> subDirs = theme.keyList();
+
+ const QString svgext(QLatin1String(".svg"));
+ const QString pngext(QLatin1String(".png"));
+
+ // Add all relevant files
+ for (int i = 0; i < subDirs.size() ; ++i) {
+ const QIconDirInfo &dirInfo = subDirs.at(i);
+ QString subdir = dirInfo.path;
+ QDir currentDir(contentDir + subdir);
+ if (currentDir.exists(iconName + pngext)) {
+ PixmapEntry *iconEntry = new PixmapEntry;
+ iconEntry->dir = dirInfo;
+ iconEntry->filename = currentDir.filePath(iconName + pngext);
+ // Notice we ensure that pixmap entries always come before
+ // scalable to preserve search order afterwards
+ entries.prepend(iconEntry);
+ } else if (m_supportsSvg &&
+ currentDir.exists(iconName + svgext)) {
+ ScalableEntry *iconEntry = new ScalableEntry;
+ iconEntry->dir = dirInfo;
+ iconEntry->filename = currentDir.filePath(iconName + svgext);
+ entries.append(iconEntry);
+ }
+ }
+
+ if (entries.isEmpty()) {
+ const QStringList parents = theme.parents();
+ // Search recursively through inherited themes
+ for (int i = 0 ; i < parents.size() ; ++i) {
+
+ const QString parentTheme = parents.at(i).trimmed();
+
+ if (!visited.contains(parentTheme)) // guard against recursion
+ entries = findIconHelper(parentTheme, iconName, visited);
+
+ if (!entries.isEmpty()) // success
+ break;
+ }
+ }
+ return entries;
+}
+
+QThemeIconEntries QIconLoader::loadIcon(const QString &name) const
+{
+ if (!themeName().isEmpty()) {
+ QStringList visited;
+ return findIconHelper(themeName(), name, visited);
+ }
+
+ return QThemeIconEntries();
+}
+
+
+// -------- Icon Loader Engine -------- //
+
+
+QIconLoaderEngine::QIconLoaderEngine(const QString& iconName)
+ : m_iconName(iconName), m_key(0)
+{
+}
+
+QIconLoaderEngine::~QIconLoaderEngine()
+{
+ while (!m_entries.isEmpty())
+ delete m_entries.takeLast();
+ Q_ASSERT(m_entries.size() == 0);
+}
+
+QIconLoaderEngine::QIconLoaderEngine(const QIconLoaderEngine &other)
+ : QIconEngineV2(other),
+ m_iconName(other.m_iconName),
+ m_key(0)
+{
+}
+
+QIconEngineV2 *QIconLoaderEngine::clone() const
+{
+ return new QIconLoaderEngine(*this);
+}
+
+bool QIconLoaderEngine::read(QDataStream &in) {
+ in >> m_iconName;
+ return true;
+}
+
+bool QIconLoaderEngine::write(QDataStream &out) const
+{
+ out << m_iconName;
+ return true;
+}
+
+bool QIconLoaderEngine::hasIcon() const
+{
+ return !(m_entries.isEmpty());
+}
+
+// Lazily load the icon
+void QIconLoaderEngine::ensureLoaded()
+{
+
+ iconLoaderInstance()->ensureInitialized();
+
+ if (!(iconLoaderInstance()->themeKey() == m_key)) {
+
+ while (!m_entries.isEmpty())
+ delete m_entries.takeLast();
+
+ Q_ASSERT(m_entries.size() == 0);
+ m_entries = iconLoaderInstance()->loadIcon(m_iconName);
+ m_key = iconLoaderInstance()->themeKey();
+ }
+}
+
+void QIconLoaderEngine::paint(QPainter *painter, const QRect &rect,
+ QIcon::Mode mode, QIcon::State state)
+{
+ QSize pixmapSize = rect.size();
+#if defined(Q_WS_MAC)
+ pixmapSize *= qt_mac_get_scalefactor();
+#endif
+ painter->drawPixmap(rect, pixmap(pixmapSize, mode, state));
+}
+
+/*
+ * This algorithm is defined by the freedesktop spec:
+ * http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html
+ */
+static bool directoryMatchesSize(const QIconDirInfo &dir, int iconsize)
+{
+ if (dir.type == QIconDirInfo::Fixed) {
+ return dir.size == iconsize;
+
+ } else if (dir.type == QIconDirInfo::Scalable) {
+ return dir.size <= dir.maxSize &&
+ iconsize >= dir.minSize;
+
+ } else if (dir.type == QIconDirInfo::Threshold) {
+ return iconsize >= dir.size - dir.threshold &&
+ iconsize <= dir.size + dir.threshold;
+ }
+
+ Q_ASSERT(1); // Not a valid value
+ return false;
+}
+
+/*
+ * This algorithm is defined by the freedesktop spec:
+ * http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html
+ */
+static int directorySizeDistance(const QIconDirInfo &dir, int iconsize)
+{
+ if (dir.type == QIconDirInfo::Fixed) {
+ return qAbs(dir.size - iconsize);
+
+ } else if (dir.type == QIconDirInfo::Scalable) {
+ if (iconsize < dir.minSize)
+ return dir.minSize - iconsize;
+ else if (iconsize > dir.maxSize)
+ return iconsize - dir.maxSize;
+ else
+ return 0;
+
+ } else if (dir.type == QIconDirInfo::Threshold) {
+ if (iconsize < dir.size - dir.threshold)
+ return dir.minSize - iconsize;
+ else if (iconsize > dir.size + dir.threshold)
+ return iconsize - dir.maxSize;
+ else return 0;
+ }
+
+ Q_ASSERT(1); // Not a valid value
+ return INT_MAX;
+}
+
+QIconLoaderEngineEntry *QIconLoaderEngine::entryForSize(const QSize &size)
+{
+ int iconsize = qMin(size.width(), size.height());
+
+ // Note that m_entries are sorted so that png-files
+ // come first
+
+ // Search for exact matches first
+ for (int i = 0; i < m_entries.count(); ++i) {
+ QIconLoaderEngineEntry *entry = m_entries.at(i);
+ if (directoryMatchesSize(entry->dir, iconsize)) {
+ return entry;
+ }
+ }
+
+ // Find the minimum distance icon
+ int minimalSize = INT_MAX;
+ QIconLoaderEngineEntry *closestMatch = 0;
+ for (int i = 0; i < m_entries.count(); ++i) {
+ QIconLoaderEngineEntry *entry = m_entries.at(i);
+ int distance = directorySizeDistance(entry->dir, iconsize);
+ if (distance < minimalSize) {
+ minimalSize = distance;
+ closestMatch = entry;
+ }
+ }
+ return closestMatch;
+}
+
+/*
+ * Returns the actual icon size. For scalable svg's this is equivalent
+ * to the requested size. Otherwise the closest match is returned but
+ * we can never return a bigger size than the requested size.
+ *
+ */
+QSize QIconLoaderEngine::actualSize(const QSize &size, QIcon::Mode mode,
+ QIcon::State state)
+{
+ ensureLoaded();
+
+ QIconLoaderEngineEntry *entry = entryForSize(size);
+ if (entry) {
+ const QIconDirInfo &dir = entry->dir;
+ if (dir.type == QIconDirInfo::Scalable)
+ return size;
+ else {
+ int result = qMin<int>(dir.size, qMin(size.width(), size.height()));
+ return QSize(result, result);
+ }
+ }
+ return QIconEngineV2::actualSize(size, mode, state);
+}
+
+QPixmap PixmapEntry::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state)
+{
+ Q_UNUSED(state);
+
+ // Ensure that basePixmap is lazily initialized before generating the
+ // key, otherwise the cache key is not unique
+ if (basePixmap.isNull())
+ basePixmap.load(filename);
+
+ int actualSize = qMin(size.width(), size.height());
+
+ QString key = QLatin1Literal("$qt_theme_")
+ % HexString<qint64>(basePixmap.cacheKey())
+ % HexString<int>(mode)
+ % HexString<qint64>(qApp->palette().cacheKey())
+ % HexString<int>(actualSize);
+
+ QPixmap cachedPixmap;
+ if (QPixmapCache::find(key, &cachedPixmap)) {
+ return cachedPixmap;
+ } else {
+ QStyleOption opt(0);
+ opt.palette = qApp->palette();
+ cachedPixmap = qApp->style()->generatedIconPixmap(mode, basePixmap, &opt);
+ QPixmapCache::insert(key, cachedPixmap);
+ }
+ return cachedPixmap;
+}
+
+QPixmap ScalableEntry::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state)
+{
+ if (svgIcon.isNull())
+ svgIcon = QIcon(filename);
+
+ // Simply reuse svg icon engine
+ return svgIcon.pixmap(size, mode, state);
+}
+
+QPixmap QIconLoaderEngine::pixmap(const QSize &size, QIcon::Mode mode,
+ QIcon::State state)
+{
+ ensureLoaded();
+
+ QIconLoaderEngineEntry *entry = entryForSize(size);
+ if (entry)
+ return entry->pixmap(size, mode, state);
+
+ return QPixmap();
+}
+
+QString QIconLoaderEngine::key() const
+{
+ return QLatin1String("QIconLoaderEngine");
+}
+
+void QIconLoaderEngine::virtual_hook(int id, void *data)
+{
+ ensureLoaded();
+
+ switch (id) {
+ case QIconEngineV2::AvailableSizesHook:
+ {
+ QIconEngineV2::AvailableSizesArgument &arg
+ = *reinterpret_cast<QIconEngineV2::AvailableSizesArgument*>(data);
+ const QList<QIconDirInfo> directoryKey = iconLoaderInstance()->theme().keyList();
+ arg.sizes.clear();
+
+ // Gets all sizes from the DirectoryInfo entries
+ for (int i = 0 ; i < m_entries.size() ; ++i) {
+ int size = m_entries.at(i)->dir.size;
+ arg.sizes.append(QSize(size, size));
+ }
+ }
+ break;
+ case QIconEngineV2::IconNameHook:
+ {
+ QString &name = *reinterpret_cast<QString*>(data);
+ name = m_iconName;
+ }
+ break;
+ default:
+ QIconEngineV2::virtual_hook(id, data);
+ }
+}
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_ICON
diff --git a/src/gui/image/qiconloader_p.h b/src/gui/image/qiconloader_p.h
new file mode 100644
index 0000000000..00a3976b40
--- /dev/null
+++ b/src/gui/image/qiconloader_p.h
@@ -0,0 +1,192 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDESKTOPICON_P_H
+#define QDESKTOPICON_P_H
+
+#ifndef QT_NO_ICON
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/QIcon>
+#include <QtGui/QIconEngine>
+#include <QtGui/QPixmapCache>
+#include <private/qicon_p.h>
+#include <private/qfactoryloader_p.h>
+#include <QtCore/QHash>
+
+QT_BEGIN_NAMESPACE
+
+class QIconLoader;
+
+struct QIconDirInfo
+{
+ enum Type { Fixed, Scalable, Threshold };
+ QIconDirInfo(const QString &_path = QString()) :
+ path(_path),
+ size(0),
+ maxSize(0),
+ minSize(0),
+ threshold(0),
+ type(Threshold) {}
+ QString path;
+ short size;
+ short maxSize;
+ short minSize;
+ short threshold;
+ Type type : 4;
+};
+
+class QIconLoaderEngineEntry
+ {
+public:
+ virtual ~QIconLoaderEngineEntry() {}
+ virtual QPixmap pixmap(const QSize &size,
+ QIcon::Mode mode,
+ QIcon::State state) = 0;
+ QString filename;
+ QIconDirInfo dir;
+ static int count;
+};
+
+struct ScalableEntry : public QIconLoaderEngineEntry
+{
+ QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state);
+ QIcon svgIcon;
+};
+
+struct PixmapEntry : public QIconLoaderEngineEntry
+{
+ QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state);
+ QPixmap basePixmap;
+};
+
+typedef QList<QIconLoaderEngineEntry*> QThemeIconEntries;
+
+class QIconLoaderEngine : public QIconEngineV2
+{
+public:
+ QIconLoaderEngine(const QString& iconName = QString());
+ ~QIconLoaderEngine();
+
+ void paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state);
+ QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state);
+ QSize actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state);
+ QIconEngineV2 *clone() const;
+ bool read(QDataStream &in);
+ bool write(QDataStream &out) const;
+
+private:
+ QString key() const;
+ bool hasIcon() const;
+ void ensureLoaded();
+ void virtual_hook(int id, void *data);
+ QIconLoaderEngineEntry *entryForSize(const QSize &size);
+ QIconLoaderEngine(const QIconLoaderEngine &other);
+ QThemeIconEntries m_entries;
+ QString m_iconName;
+ uint m_key;
+
+ friend class QIconLoader;
+};
+
+class QIconTheme
+{
+public:
+ QIconTheme(const QString &name);
+ QIconTheme() : m_valid(false) {}
+ QStringList parents() { return m_parents; }
+ QList <QIconDirInfo> keyList() { return m_keyList; }
+ QString contentDir() { return m_contentDir; }
+ bool isValid() { return m_valid; }
+
+private:
+ QString m_contentDir;
+ QList <QIconDirInfo> m_keyList;
+ QStringList m_parents;
+ bool m_valid;
+};
+
+class QIconLoader : public QObject
+{
+public:
+ QIconLoader();
+ QThemeIconEntries loadIcon(const QString &iconName) const;
+ uint themeKey() const { return m_themeKey; }
+
+ QString themeName() const { return m_userTheme.isEmpty() ? m_systemTheme : m_userTheme; }
+ void setThemeName(const QString &themeName);
+ QIconTheme theme() { return themeList.value(themeName()); }
+ void setThemeSearchPath(const QStringList &searchPaths);
+ QStringList themeSearchPaths() const;
+ QIconDirInfo dirInfo(int dirindex);
+ static QIconLoader *instance();
+ void updateSystemTheme();
+ void invalidateKey() { m_themeKey++; }
+ void ensureInitialized();
+
+private:
+ QThemeIconEntries findIconHelper(const QString &themeName,
+ const QString &iconName,
+ QStringList &visited) const;
+ uint m_themeKey;
+ bool m_supportsSvg;
+ bool m_initialized;
+
+ mutable QString m_userTheme;
+ mutable QString m_systemTheme;
+ mutable QStringList m_iconDirs;
+ mutable QHash <QString, QIconTheme> themeList;
+};
+
+QT_END_NAMESPACE
+
+#endif // QDESKTOPICON_P_H
+
+#endif //QT_NO_ICON
diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp
new file mode 100644
index 0000000000..50b372ea63
--- /dev/null
+++ b/src/gui/image/qimage.cpp
@@ -0,0 +1,6717 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qimage.h"
+#include "qdatastream.h"
+#include "qbuffer.h"
+#include "qmap.h"
+#include "qmatrix.h"
+#include "qtransform.h"
+#include "qimagereader.h"
+#include "qimagewriter.h"
+#include "qstringlist.h"
+#include "qvariant.h"
+#include "qimagepixmapcleanuphooks_p.h"
+#include <ctype.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <math.h>
+#include <private/qdrawhelper_p.h>
+#include <private/qmemrotate_p.h>
+#include <private/qpixmapdata_p.h>
+#include <private/qimagescale_p.h>
+#include <private/qsimd_p.h>
+
+#include <qhash.h>
+
+#include <private/qpaintengine_raster_p.h>
+
+#include <private/qimage_p.h>
+#include <private/qfont_p.h>
+
+QT_BEGIN_NAMESPACE
+
+static inline bool checkPixelSize(const QImage::Format format)
+{
+ switch (format) {
+ case QImage::Format_ARGB8565_Premultiplied:
+ return (sizeof(qargb8565) == 3);
+ case QImage::Format_RGB666:
+ return (sizeof(qrgb666) == 3);
+ case QImage::Format_ARGB6666_Premultiplied:
+ return (sizeof(qargb6666) == 3);
+ case QImage::Format_RGB555:
+ return (sizeof(qrgb555) == 2);
+ case QImage::Format_ARGB8555_Premultiplied:
+ return (sizeof(qargb8555) == 3);
+ case QImage::Format_RGB888:
+ return (sizeof(qrgb888) == 3);
+ case QImage::Format_RGB444:
+ return (sizeof(qrgb444) == 2);
+ case QImage::Format_ARGB4444_Premultiplied:
+ return (sizeof(qargb4444) == 2);
+ default:
+ return true;
+ }
+}
+
+#if defined(Q_CC_DEC) && defined(__alpha) && (__DECCXX_VER-0 >= 50190001)
+#pragma message disable narrowptr
+#endif
+
+
+#define QIMAGE_SANITYCHECK_MEMORY(image) \
+ if ((image).isNull()) { \
+ qWarning("QImage: out of memory, returning null image"); \
+ return QImage(); \
+ }
+
+
+static QImage rotated90(const QImage &src);
+static QImage rotated180(const QImage &src);
+static QImage rotated270(const QImage &src);
+
+// ### Qt 5: remove
+Q_GUI_EXPORT qint64 qt_image_id(const QImage &image)
+{
+ return image.cacheKey();
+}
+
+const QVector<QRgb> *qt_image_colortable(const QImage &image)
+{
+ return &image.d->colortable;
+}
+
+QBasicAtomicInt qimage_serial_number = Q_BASIC_ATOMIC_INITIALIZER(1);
+
+QImageData::QImageData()
+ : ref(0), width(0), height(0), depth(0), nbytes(0), data(0),
+#ifdef QT3_SUPPORT
+ jumptable(0),
+#endif
+ format(QImage::Format_ARGB32), bytes_per_line(0),
+ ser_no(qimage_serial_number.fetchAndAddRelaxed(1)),
+ detach_no(0),
+ dpmx(qt_defaultDpiX() * 100 / qreal(2.54)),
+ dpmy(qt_defaultDpiY() * 100 / qreal(2.54)),
+ offset(0, 0), own_data(true), ro_data(false), has_alpha_clut(false),
+ is_cached(false), paintEngine(0)
+{
+}
+
+/*! \fn QImageData * QImageData::create(const QSize &size, QImage::Format format, int numColors)
+
+ \internal
+
+ Creates a new image data.
+ Returns 0 if invalid parameters are give or anything else failed.
+*/
+QImageData * QImageData::create(const QSize &size, QImage::Format format, int numColors)
+{
+ if (!size.isValid() || numColors < 0 || format == QImage::Format_Invalid)
+ return 0; // invalid parameter(s)
+
+ if (!checkPixelSize(format)) {
+ qWarning("QImageData::create(): Invalid pixel size for format %i",
+ format);
+ return 0;
+ }
+
+ uint width = size.width();
+ uint height = size.height();
+ uint depth = qt_depthForFormat(format);
+
+ switch (format) {
+ case QImage::Format_Mono:
+ case QImage::Format_MonoLSB:
+ numColors = 2;
+ break;
+ case QImage::Format_Indexed8:
+ numColors = qBound(0, numColors, 256);
+ break;
+ default:
+ numColors = 0;
+ break;
+ }
+
+ const int bytes_per_line = ((width * depth + 31) >> 5) << 2; // bytes per scanline (must be multiple of 4)
+
+ // sanity check for potential overflows
+ if (INT_MAX/depth < width
+ || bytes_per_line <= 0
+ || height <= 0
+ || INT_MAX/uint(bytes_per_line) < height
+ || INT_MAX/sizeof(uchar *) < uint(height))
+ return 0;
+
+ QScopedPointer<QImageData> d(new QImageData);
+ d->colortable.resize(numColors);
+ if (depth == 1) {
+ d->colortable[0] = QColor(Qt::black).rgba();
+ d->colortable[1] = QColor(Qt::white).rgba();
+ } else {
+ for (int i = 0; i < numColors; ++i)
+ d->colortable[i] = 0;
+ }
+
+ d->width = width;
+ d->height = height;
+ d->depth = depth;
+ d->format = format;
+ d->has_alpha_clut = false;
+ d->is_cached = false;
+
+ d->bytes_per_line = bytes_per_line;
+
+ d->nbytes = d->bytes_per_line*height;
+ d->data = (uchar *)malloc(d->nbytes);
+
+ if (!d->data) {
+ return 0;
+ }
+
+ d->ref.ref();
+ return d.take();
+
+}
+
+QImageData::~QImageData()
+{
+ if (is_cached)
+ QImagePixmapCleanupHooks::executeImageHooks((((qint64) ser_no) << 32) | ((qint64) detach_no));
+ delete paintEngine;
+ if (data && own_data)
+ free(data);
+#ifdef QT3_SUPPORT
+ if (jumptable)
+ free(jumptable);
+ jumptable = 0;
+#endif
+ data = 0;
+}
+
+
+bool QImageData::checkForAlphaPixels() const
+{
+ bool has_alpha_pixels = false;
+
+ switch (format) {
+
+ case QImage::Format_Mono:
+ case QImage::Format_MonoLSB:
+ case QImage::Format_Indexed8:
+ has_alpha_pixels = has_alpha_clut;
+ break;
+
+ case QImage::Format_ARGB32:
+ case QImage::Format_ARGB32_Premultiplied: {
+ uchar *bits = data;
+ for (int y=0; y<height && !has_alpha_pixels; ++y) {
+ for (int x=0; x<width; ++x)
+ has_alpha_pixels |= (((uint *)bits)[x] & 0xff000000) != 0xff000000;
+ bits += bytes_per_line;
+ }
+ } break;
+
+ case QImage::Format_ARGB8555_Premultiplied:
+ case QImage::Format_ARGB8565_Premultiplied: {
+ uchar *bits = data;
+ uchar *end_bits = data + bytes_per_line;
+
+ for (int y=0; y<height && !has_alpha_pixels; ++y) {
+ while (bits < end_bits) {
+ has_alpha_pixels |= bits[0] != 0;
+ bits += 3;
+ }
+ bits = end_bits;
+ end_bits += bytes_per_line;
+ }
+ } break;
+
+ case QImage::Format_ARGB6666_Premultiplied: {
+ uchar *bits = data;
+ uchar *end_bits = data + bytes_per_line;
+
+ for (int y=0; y<height && !has_alpha_pixels; ++y) {
+ while (bits < end_bits) {
+ has_alpha_pixels |= (bits[0] & 0xfc) != 0;
+ bits += 3;
+ }
+ bits = end_bits;
+ end_bits += bytes_per_line;
+ }
+ } break;
+
+ case QImage::Format_ARGB4444_Premultiplied: {
+ uchar *bits = data;
+ uchar *end_bits = data + bytes_per_line;
+
+ for (int y=0; y<height && !has_alpha_pixels; ++y) {
+ while (bits < end_bits) {
+ has_alpha_pixels |= (bits[0] & 0xf0) != 0;
+ bits += 2;
+ }
+ bits = end_bits;
+ end_bits += bytes_per_line;
+ }
+ } break;
+
+ default:
+ break;
+ }
+
+ return has_alpha_pixels;
+}
+
+/*!
+ \class QImage
+
+ \ingroup painting
+ \ingroup shared
+
+ \reentrant
+
+ \brief The QImage class provides a hardware-independent image
+ representation that allows direct access to the pixel data, and
+ can be used as a paint device.
+
+ Qt provides four classes for handling image data: QImage, QPixmap,
+ QBitmap and QPicture. QImage is designed and optimized for I/O,
+ and for direct pixel access and manipulation, while QPixmap is
+ designed and optimized for showing images on screen. QBitmap is
+ only a convenience class that inherits QPixmap, ensuring a
+ depth of 1. Finally, the QPicture class is a paint device that
+ records and replays QPainter commands.
+
+ Because QImage is a QPaintDevice subclass, QPainter can be used to
+ draw directly onto images. When using QPainter on a QImage, the
+ painting can be performed in another thread than the current GUI
+ thread.
+
+ The QImage class supports several image formats described by the
+ \l Format enum. These include monochrome, 8-bit, 32-bit and
+ alpha-blended images which are available in all versions of Qt
+ 4.x.
+
+ QImage provides a collection of functions that can be used to
+ obtain a variety of information about the image. There are also
+ several functions that enables transformation of the image.
+
+ QImage objects can be passed around by value since the QImage
+ class uses \l{Implicit Data Sharing}{implicit data
+ sharing}. QImage objects can also be streamed and compared.
+
+ \note If you would like to load QImage objects in a static build of Qt,
+ refer to the \l{How To Create Qt Plugins#Static Plugins}{Plugin HowTo}.
+
+ \warning Painting on a QImage with the format
+ QImage::Format_Indexed8 is not supported.
+
+ \tableofcontents
+
+ \section1 Reading and Writing Image Files
+
+ QImage provides several ways of loading an image file: The file
+ can be loaded when constructing the QImage object, or by using the
+ load() or loadFromData() functions later on. QImage also provides
+ the static fromData() function, constructing a QImage from the
+ given data. When loading an image, the file name can either refer
+ to an actual file on disk or to one of the application's embedded
+ resources. See \l{The Qt Resource System} overview for details
+ on how to embed images and other resource files in the
+ application's executable.
+
+ Simply call the save() function to save a QImage object.
+
+ The complete list of supported file formats are available through
+ the QImageReader::supportedImageFormats() and
+ QImageWriter::supportedImageFormats() functions. New file formats
+ can be added as plugins. By default, Qt supports the following
+ formats:
+
+ \table
+ \header \o Format \o Description \o Qt's support
+ \row \o BMP \o Windows Bitmap \o Read/write
+ \row \o GIF \o Graphic Interchange Format (optional) \o Read
+ \row \o JPG \o Joint Photographic Experts Group \o Read/write
+ \row \o JPEG \o Joint Photographic Experts Group \o Read/write
+ \row \o PNG \o Portable Network Graphics \o Read/write
+ \row \o PBM \o Portable Bitmap \o Read
+ \row \o PGM \o Portable Graymap \o Read
+ \row \o PPM \o Portable Pixmap \o Read/write
+ \row \o TIFF \o Tagged Image File Format \o Read/write
+ \row \o XBM \o X11 Bitmap \o Read/write
+ \row \o XPM \o X11 Pixmap \o Read/write
+ \endtable
+
+ \section1 Image Information
+
+ QImage provides a collection of functions that can be used to
+ obtain a variety of information about the image:
+
+ \table
+ \header
+ \o \o Available Functions
+
+ \row
+ \o Geometry
+ \o
+
+ The size(), width(), height(), dotsPerMeterX(), and
+ dotsPerMeterY() functions provide information about the image size
+ and aspect ratio.
+
+ The rect() function returns the image's enclosing rectangle. The
+ valid() function tells if a given pair of coordinates is within
+ this rectangle. The offset() function returns the number of pixels
+ by which the image is intended to be offset by when positioned
+ relative to other images, which also can be manipulated using the
+ setOffset() function.
+
+ \row
+ \o Colors
+ \o
+
+ The color of a pixel can be retrieved by passing its coordinates
+ to the pixel() function. The pixel() function returns the color
+ as a QRgb value indepedent of the image's format.
+
+ In case of monochrome and 8-bit images, the colorCount() and
+ colorTable() functions provide information about the color
+ components used to store the image data: The colorTable() function
+ returns the image's entire color table. To obtain a single entry,
+ use the pixelIndex() function to retrieve the pixel index for a
+ given pair of coordinates, then use the color() function to
+ retrieve the color. Note that if you create an 8-bit image
+ manually, you have to set a valid color table on the image as
+ well.
+
+ The hasAlphaChannel() function tells if the image's format
+ respects the alpha channel, or not. The allGray() and
+ isGrayscale() functions tell whether an image's colors are all
+ shades of gray.
+
+ See also the \l {QImage#Pixel Manipulation}{Pixel Manipulation}
+ and \l {QImage#Image Transformations}{Image Transformations}
+ sections.
+
+ \row
+ \o Text
+ \o
+
+ The text() function returns the image text associated with the
+ given text key. An image's text keys can be retrieved using the
+ textKeys() function. Use the setText() function to alter an
+ image's text.
+
+ \row
+ \o Low-level information
+ \o
+
+ The depth() function returns the depth of the image. The supported
+ depths are 1 (monochrome), 8, 16, 24 and 32 bits. The
+ bitPlaneCount() function tells how many of those bits that are
+ used. For more information see the
+ \l {QImage#Image Formats}{Image Formats} section.
+
+ The format(), bytesPerLine(), and byteCount() functions provide
+ low-level information about the data stored in the image.
+
+ The cacheKey() function returns a number that uniquely
+ identifies the contents of this QImage object.
+ \endtable
+
+ \section1 Pixel Manipulation
+
+ The functions used to manipulate an image's pixels depend on the
+ image format. The reason is that monochrome and 8-bit images are
+ index-based and use a color lookup table, while 32-bit images
+ store ARGB values directly. For more information on image formats,
+ see the \l {Image Formats} section.
+
+ In case of a 32-bit image, the setPixel() function can be used to
+ alter the color of the pixel at the given coordinates to any other
+ color specified as an ARGB quadruplet. To make a suitable QRgb
+ value, use the qRgb() (adding a default alpha component to the
+ given RGB values, i.e. creating an opaque color) or qRgba()
+ function. For example:
+
+ \table
+ \header
+ \o {2,1}32-bit
+ \row
+ \o \inlineimage qimage-32bit_scaled.png
+ \o
+ \snippet doc/src/snippets/code/src_gui_image_qimage.cpp 0
+ \endtable
+
+ In case of a 8-bit and monchrome images, the pixel value is only
+ an index from the image's color table. So the setPixel() function
+ can only be used to alter the color of the pixel at the given
+ coordinates to a predefined color from the image's color table,
+ i.e. it can only change the pixel's index value. To alter or add a
+ color to an image's color table, use the setColor() function.
+
+ An entry in the color table is an ARGB quadruplet encoded as an
+ QRgb value. Use the qRgb() and qRgba() functions to make a
+ suitable QRgb value for use with the setColor() function. For
+ example:
+
+ \table
+ \header
+ \o {2,1} 8-bit
+ \row
+ \o \inlineimage qimage-8bit_scaled.png
+ \o
+ \snippet doc/src/snippets/code/src_gui_image_qimage.cpp 1
+ \endtable
+
+ QImage also provide the scanLine() function which returns a
+ pointer to the pixel data at the scanline with the given index,
+ and the bits() function which returns a pointer to the first pixel
+ data (this is equivalent to \c scanLine(0)).
+
+ \section1 Image Formats
+
+ Each pixel stored in a QImage is represented by an integer. The
+ size of the integer varies depending on the format. QImage
+ supports several image formats described by the \l Format
+ enum.
+
+ Monochrome images are stored using 1-bit indexes into a color table
+ with at most two colors. There are two different types of
+ monochrome images: big endian (MSB first) or little endian (LSB
+ first) bit order.
+
+ 8-bit images are stored using 8-bit indexes into a color table,
+ i.e. they have a single byte per pixel. The color table is a
+ QVector<QRgb>, and the QRgb typedef is equivalent to an unsigned
+ int containing an ARGB quadruplet on the format 0xAARRGGBB.
+
+ 32-bit images have no color table; instead, each pixel contains an
+ QRgb value. There are three different types of 32-bit images
+ storing RGB (i.e. 0xffRRGGBB), ARGB and premultiplied ARGB
+ values respectively. In the premultiplied format the red, green,
+ and blue channels are multiplied by the alpha component divided by
+ 255.
+
+ An image's format can be retrieved using the format()
+ function. Use the convertToFormat() functions to convert an image
+ into another format. The allGray() and isGrayscale() functions
+ tell whether a color image can safely be converted to a grayscale
+ image.
+
+ \section1 Image Transformations
+
+ QImage supports a number of functions for creating a new image
+ that is a transformed version of the original: The
+ createAlphaMask() function builds and returns a 1-bpp mask from
+ the alpha buffer in this image, and the createHeuristicMask()
+ function creates and returns a 1-bpp heuristic mask for this
+ image. The latter function works by selecting a color from one of
+ the corners, then chipping away pixels of that color starting at
+ all the edges.
+
+ The mirrored() function returns a mirror of the image in the
+ desired direction, the scaled() returns a copy of the image scaled
+ to a rectangle of the desired measures, and the rgbSwapped() function
+ constructs a BGR image from a RGB image.
+
+ The scaledToWidth() and scaledToHeight() functions return scaled
+ copies of the image.
+
+ The transformed() function returns a copy of the image that is
+ transformed with the given transformation matrix and
+ transformation mode: Internally, the transformation matrix is
+ adjusted to compensate for unwanted translation,
+ i.e. transformed() returns the smallest image containing all
+ transformed points of the original image. The static trueMatrix()
+ function returns the actual matrix used for transforming the
+ image.
+
+ There are also functions for changing attributes of an image
+ in-place:
+
+ \table
+ \header \o Function \o Description
+ \row
+ \o setDotsPerMeterX()
+ \o Defines the aspect ratio by setting the number of pixels that fit
+ horizontally in a physical meter.
+ \row
+ \o setDotsPerMeterY()
+ \o Defines the aspect ratio by setting the number of pixels that fit
+ vertically in a physical meter.
+ \row
+ \o fill()
+ \o Fills the entire image with the given pixel value.
+ \row
+ \o invertPixels()
+ \o Inverts all pixel values in the image using the given InvertMode value.
+ \row
+ \o setColorTable()
+ \o Sets the color table used to translate color indexes. Only
+ monochrome and 8-bit formats.
+ \row
+ \o setColorCount()
+ \o Resizes the color table. Only monochrome and 8-bit formats.
+
+ \endtable
+
+ \section1 Legal Information
+
+ For smooth scaling, the transformed() functions use code based on
+ smooth scaling algorithm by Daniel M. Duley.
+
+ \legalese
+ Copyright (C) 2004, 2005 Daniel M. Duley
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ \endlegalese
+
+ \sa QImageReader, QImageWriter, QPixmap, QSvgRenderer, {Image Composition Example},
+ {Image Viewer Example}, {Scribble Example}, {Pixelator Example}
+*/
+
+/*!
+ \enum QImage::Endian
+ \compat
+
+ This enum type is used to describe the endianness of the CPU and
+ graphics hardware. It is provided here for compatibility with earlier versions of Qt.
+
+ Use the \l Format enum instead. The \l Format enum specify the
+ endianess for monchrome formats, but for other formats the
+ endianess is not relevant.
+
+ \value IgnoreEndian Endianness does not matter. Useful for some
+ operations that are independent of endianness.
+ \value BigEndian Most significant bit first or network byte order, as on SPARC, PowerPC, and Motorola CPUs.
+ \value LittleEndian Least significant bit first or little endian byte order, as on Intel x86.
+*/
+
+/*!
+ \enum QImage::InvertMode
+
+ This enum type is used to describe how pixel values should be
+ inverted in the invertPixels() function.
+
+ \value InvertRgb Invert only the RGB values and leave the alpha
+ channel unchanged.
+
+ \value InvertRgba Invert all channels, including the alpha channel.
+
+ \sa invertPixels()
+*/
+
+/*!
+ \enum QImage::Format
+
+ The following image formats are available in Qt. Values greater
+ than QImage::Format_RGB16 were added in Qt 4.4. See the notes
+ after the table.
+
+ \value Format_Invalid The image is invalid.
+ \value Format_Mono The image is stored using 1-bit per pixel. Bytes are
+ packed with the most significant bit (MSB) first.
+ \value Format_MonoLSB The image is stored using 1-bit per pixel. Bytes are
+ packed with the less significant bit (LSB) first.
+
+ \value Format_Indexed8 The image is stored using 8-bit indexes
+ into a colormap.
+
+ \value Format_RGB32 The image is stored using a 32-bit RGB format (0xffRRGGBB).
+
+ \value Format_ARGB32 The image is stored using a 32-bit ARGB
+ format (0xAARRGGBB).
+
+ \value Format_ARGB32_Premultiplied The image is stored using a premultiplied 32-bit
+ ARGB format (0xAARRGGBB), i.e. the red,
+ green, and blue channels are multiplied
+ by the alpha component divided by 255. (If RR, GG, or BB
+ has a higher value than the alpha channel, the results are
+ undefined.) Certain operations (such as image composition
+ using alpha blending) are faster using premultiplied ARGB32
+ than with plain ARGB32.
+
+ \value Format_RGB16 The image is stored using a 16-bit RGB format (5-6-5).
+
+ \value Format_ARGB8565_Premultiplied The image is stored using a
+ premultiplied 24-bit ARGB format (8-5-6-5).
+ \value Format_RGB666 The image is stored using a 24-bit RGB format (6-6-6).
+ The unused most significant bits is always zero.
+ \value Format_ARGB6666_Premultiplied The image is stored using a
+ premultiplied 24-bit ARGB format (6-6-6-6).
+ \value Format_RGB555 The image is stored using a 16-bit RGB format (5-5-5).
+ The unused most significant bit is always zero.
+ \value Format_ARGB8555_Premultiplied The image is stored using a
+ premultiplied 24-bit ARGB format (8-5-5-5).
+ \value Format_RGB888 The image is stored using a 24-bit RGB format (8-8-8).
+ \value Format_RGB444 The image is stored using a 16-bit RGB format (4-4-4).
+ The unused bits are always zero.
+ \value Format_ARGB4444_Premultiplied The image is stored using a
+ premultiplied 16-bit ARGB format (4-4-4-4).
+
+ \note Drawing into a QImage with QImage::Format_Indexed8 is not
+ supported.
+
+ \note Do not render into ARGB32 images using QPainter. Using
+ QImage::Format_ARGB32_Premultiplied is significantly faster.
+
+ \sa format(), convertToFormat()
+*/
+
+/*****************************************************************************
+ QImage member functions
+ *****************************************************************************/
+
+// table to flip bits
+static const uchar bitflip[256] = {
+ /*
+ open OUT, "| fmt";
+ for $i (0..255) {
+ print OUT (($i >> 7) & 0x01) | (($i >> 5) & 0x02) |
+ (($i >> 3) & 0x04) | (($i >> 1) & 0x08) |
+ (($i << 7) & 0x80) | (($i << 5) & 0x40) |
+ (($i << 3) & 0x20) | (($i << 1) & 0x10), ", ";
+ }
+ close OUT;
+ */
+ 0, 128, 64, 192, 32, 160, 96, 224, 16, 144, 80, 208, 48, 176, 112, 240,
+ 8, 136, 72, 200, 40, 168, 104, 232, 24, 152, 88, 216, 56, 184, 120, 248,
+ 4, 132, 68, 196, 36, 164, 100, 228, 20, 148, 84, 212, 52, 180, 116, 244,
+ 12, 140, 76, 204, 44, 172, 108, 236, 28, 156, 92, 220, 60, 188, 124, 252,
+ 2, 130, 66, 194, 34, 162, 98, 226, 18, 146, 82, 210, 50, 178, 114, 242,
+ 10, 138, 74, 202, 42, 170, 106, 234, 26, 154, 90, 218, 58, 186, 122, 250,
+ 6, 134, 70, 198, 38, 166, 102, 230, 22, 150, 86, 214, 54, 182, 118, 246,
+ 14, 142, 78, 206, 46, 174, 110, 238, 30, 158, 94, 222, 62, 190, 126, 254,
+ 1, 129, 65, 193, 33, 161, 97, 225, 17, 145, 81, 209, 49, 177, 113, 241,
+ 9, 137, 73, 201, 41, 169, 105, 233, 25, 153, 89, 217, 57, 185, 121, 249,
+ 5, 133, 69, 197, 37, 165, 101, 229, 21, 149, 85, 213, 53, 181, 117, 245,
+ 13, 141, 77, 205, 45, 173, 109, 237, 29, 157, 93, 221, 61, 189, 125, 253,
+ 3, 131, 67, 195, 35, 163, 99, 227, 19, 147, 83, 211, 51, 179, 115, 243,
+ 11, 139, 75, 203, 43, 171, 107, 235, 27, 155, 91, 219, 59, 187, 123, 251,
+ 7, 135, 71, 199, 39, 167, 103, 231, 23, 151, 87, 215, 55, 183, 119, 247,
+ 15, 143, 79, 207, 47, 175, 111, 239, 31, 159, 95, 223, 63, 191, 127, 255
+};
+
+const uchar *qt_get_bitflip_array() // called from QPixmap code
+{
+ return bitflip;
+}
+
+#if defined(QT3_SUPPORT)
+static QImage::Format formatFor(int depth, QImage::Endian bitOrder)
+{
+ QImage::Format format;
+ if (depth == 1) {
+ format = bitOrder == QImage::BigEndian ? QImage::Format_Mono : QImage::Format_MonoLSB;
+ } else if (depth == 8) {
+ format = QImage::Format_Indexed8;
+ } else if (depth == 32) {
+ format = QImage::Format_RGB32;
+ } else if (depth == 24) {
+ format = QImage::Format_RGB888;
+ } else if (depth == 16) {
+ format = QImage::Format_RGB16;
+ } else {
+ qWarning("QImage: Depth %d not supported", depth);
+ format = QImage::Format_Invalid;
+ }
+ return format;
+}
+#endif
+
+/*!
+ Constructs a null image.
+
+ \sa isNull()
+*/
+
+QImage::QImage()
+ : QPaintDevice()
+{
+ d = 0;
+}
+
+/*!
+ Constructs an image with the given \a width, \a height and \a
+ format.
+
+ A \l{isNull()}{null} image will be returned if memory cannot be allocated.
+
+ \warning This will create a QImage with uninitialized data. Call
+ fill() to fill the image with an appropriate pixel value before
+ drawing onto it with QPainter.
+*/
+QImage::QImage(int width, int height, Format format)
+ : QPaintDevice()
+{
+ d = QImageData::create(QSize(width, height), format, 0);
+}
+
+/*!
+ Constructs an image with the given \a size and \a format.
+
+ A \l{isNull()}{null} image is returned if memory cannot be allocated.
+
+ \warning This will create a QImage with uninitialized data. Call
+ fill() to fill the image with an appropriate pixel value before
+ drawing onto it with QPainter.
+*/
+QImage::QImage(const QSize &size, Format format)
+ : QPaintDevice()
+{
+ d = QImageData::create(size, format, 0);
+}
+
+
+
+QImageData *QImageData::create(uchar *data, int width, int height, int bpl, QImage::Format format, bool readOnly)
+{
+ QImageData *d = 0;
+
+ if (format == QImage::Format_Invalid)
+ return d;
+
+ if (!checkPixelSize(format)) {
+ qWarning("QImageData::create(): Invalid pixel size for format %i",
+ format);
+ return 0;
+ }
+
+ const int depth = qt_depthForFormat(format);
+ const int calc_bytes_per_line = ((width * depth + 31)/32) * 4;
+ const int min_bytes_per_line = (width * depth + 7)/8;
+
+ if (bpl <= 0)
+ bpl = calc_bytes_per_line;
+
+ if (width <= 0 || height <= 0 || !data
+ || INT_MAX/sizeof(uchar *) < uint(height)
+ || INT_MAX/uint(depth) < uint(width)
+ || bpl <= 0
+ || height <= 0
+ || bpl < min_bytes_per_line
+ || INT_MAX/uint(bpl) < uint(height))
+ return d; // invalid parameter(s)
+
+ d = new QImageData;
+ d->ref.ref();
+
+ d->own_data = false;
+ d->ro_data = readOnly;
+ d->data = data;
+ d->width = width;
+ d->height = height;
+ d->depth = depth;
+ d->format = format;
+
+ d->bytes_per_line = bpl;
+ d->nbytes = d->bytes_per_line * height;
+
+ return d;
+}
+
+/*!
+ Constructs an image with the given \a width, \a height and \a
+ format, that uses an existing memory buffer, \a data. The \a width
+ and \a height must be specified in pixels, \a data must be 32-bit aligned,
+ and each scanline of data in the image must also be 32-bit aligned.
+
+ The buffer must remain valid throughout the life of the
+ QImage. The image does not delete the buffer at destruction.
+
+ If \a format is an indexed color format, the image color table is
+ initially empty and must be sufficiently expanded with
+ setColorCount() or setColorTable() before the image is used.
+*/
+QImage::QImage(uchar* data, int width, int height, Format format)
+ : QPaintDevice()
+{
+ d = QImageData::create(data, width, height, 0, format, false);
+}
+
+/*!
+ Constructs an image with the given \a width, \a height and \a
+ format, that uses an existing read-only memory buffer, \a
+ data. The \a width and \a height must be specified in pixels, \a
+ data must be 32-bit aligned, and each scanline of data in the
+ image must also be 32-bit aligned.
+
+ The buffer must remain valid throughout the life of the QImage and
+ all copies that have not been modified or otherwise detached from
+ the original buffer. The image does not delete the buffer at
+ destruction.
+
+ If \a format is an indexed color format, the image color table is
+ initially empty and must be sufficiently expanded with
+ setColorCount() or setColorTable() before the image is used.
+
+ Unlike the similar QImage constructor that takes a non-const data buffer,
+ this version will never alter the contents of the buffer. For example,
+ calling QImage::bits() will return a deep copy of the image, rather than
+ the buffer passed to the constructor. This allows for the efficiency of
+ constructing a QImage from raw data, without the possibility of the raw
+ data being changed.
+*/
+QImage::QImage(const uchar* data, int width, int height, Format format)
+ : QPaintDevice()
+{
+ d = QImageData::create(const_cast<uchar*>(data), width, height, 0, format, true);
+}
+
+/*!
+ Constructs an image with the given \a width, \a height and \a
+ format, that uses an existing memory buffer, \a data. The \a width
+ and \a height must be specified in pixels. \a bytesPerLine
+ specifies the number of bytes per line (stride).
+
+ The buffer must remain valid throughout the life of the
+ QImage. The image does not delete the buffer at destruction.
+
+ If \a format is an indexed color format, the image color table is
+ initially empty and must be sufficiently expanded with
+ setColorCount() or setColorTable() before the image is used.
+*/
+QImage::QImage(uchar *data, int width, int height, int bytesPerLine, Format format)
+ :QPaintDevice()
+{
+ d = QImageData::create(data, width, height, bytesPerLine, format, false);
+}
+
+
+/*!
+ Constructs an image with the given \a width, \a height and \a
+ format, that uses an existing memory buffer, \a data. The \a width
+ and \a height must be specified in pixels. \a bytesPerLine
+ specifies the number of bytes per line (stride).
+
+ The buffer must remain valid throughout the life of the
+ QImage. The image does not delete the buffer at destruction.
+
+ If \a format is an indexed color format, the image color table is
+ initially empty and must be sufficiently expanded with
+ setColorCount() or setColorTable() before the image is used.
+
+ Unlike the similar QImage constructor that takes a non-const data buffer,
+ this version will never alter the contents of the buffer. For example,
+ calling QImage::bits() will return a deep copy of the image, rather than
+ the buffer passed to the constructor. This allows for the efficiency of
+ constructing a QImage from raw data, without the possibility of the raw
+ data being changed.
+*/
+
+QImage::QImage(const uchar *data, int width, int height, int bytesPerLine, Format format)
+ :QPaintDevice()
+{
+ d = QImageData::create(const_cast<uchar*>(data), width, height, bytesPerLine, format, true);
+}
+
+/*!
+ Constructs an image and tries to load the image from the file with
+ the given \a fileName.
+
+ The loader attempts to read the image using the specified \a
+ format. If the \a format is not specified (which is the default),
+ the loader probes the file for a header to guess the file format.
+
+ If the loading of the image failed, this object is a null image.
+
+ The file name can either refer to an actual file on disk or to one
+ of the application's embedded resources. See the
+ \l{resources.html}{Resource System} overview for details on how to
+ embed images and other resource files in the application's
+ executable.
+
+ \sa isNull(), {QImage#Reading and Writing Image Files}{Reading and Writing Image Files}
+*/
+
+QImage::QImage(const QString &fileName, const char *format)
+ : QPaintDevice()
+{
+ d = 0;
+ load(fileName, format);
+}
+
+/*!
+ Constructs an image and tries to load the image from the file with
+ the given \a fileName.
+
+ The loader attempts to read the image using the specified \a
+ format. If the \a format is not specified (which is the default),
+ the loader probes the file for a header to guess the file format.
+
+ If the loading of the image failed, this object is a null image.
+
+ The file name can either refer to an actual file on disk or to one
+ of the application's embedded resources. See the
+ \l{resources.html}{Resource System} overview for details on how to
+ embed images and other resource files in the application's
+ executable.
+
+ You can disable this constructor by defining \c
+ QT_NO_CAST_FROM_ASCII when you compile your applications. This can
+ be useful, for example, if you want to ensure that all
+ user-visible strings go through QObject::tr().
+
+ \sa QString::fromAscii(), isNull(), {QImage#Reading and Writing
+ Image Files}{Reading and Writing Image Files}
+*/
+#ifndef QT_NO_CAST_FROM_ASCII
+QImage::QImage(const char *fileName, const char *format)
+ : QPaintDevice()
+{
+ // ### Qt 5: if you remove the QImage(const QByteArray &) QT3_SUPPORT
+ // constructor, remove this constructor as well. The constructor here
+ // exists so that QImage("foo.png") compiles without ambiguity.
+ d = 0;
+ load(QString::fromAscii(fileName), format);
+}
+#endif
+
+#ifndef QT_NO_IMAGEFORMAT_XPM
+extern bool qt_read_xpm_image_or_array(QIODevice *device, const char * const *source, QImage &image);
+
+/*!
+ Constructs an image from the given \a xpm image.
+
+ Make sure that the image is a valid XPM image. Errors are silently
+ ignored.
+
+ Note that it's possible to squeeze the XPM variable a little bit
+ by using an unusual declaration:
+
+ \snippet doc/src/snippets/code/src_gui_image_qimage.cpp 2
+
+ The extra \c const makes the entire definition read-only, which is
+ slightly more efficient (e.g., when the code is in a shared
+ library) and able to be stored in ROM with the application.
+*/
+
+QImage::QImage(const char * const xpm[])
+ : QPaintDevice()
+{
+ d = 0;
+ if (!xpm)
+ return;
+ if (!qt_read_xpm_image_or_array(0, xpm, *this))
+ // Issue: Warning because the constructor may be ambigious
+ qWarning("QImage::QImage(), XPM is not supported");
+}
+#endif // QT_NO_IMAGEFORMAT_XPM
+
+/*!
+ \fn QImage::QImage(const QByteArray &data)
+
+ Use the static fromData() function instead.
+
+ \oldcode
+ QByteArray data;
+ ...
+ QImage image(data);
+ \newcode
+ QByteArray data;
+ ...
+ QImage image = QImage::fromData(data);
+ \endcode
+*/
+
+
+/*!
+ Constructs a shallow copy of the given \a image.
+
+ For more information about shallow copies, see the \l {Implicit
+ Data Sharing} documentation.
+
+ \sa copy()
+*/
+
+QImage::QImage(const QImage &image)
+ : QPaintDevice()
+{
+ if (image.paintingActive()) {
+ d = 0;
+ operator=(image.copy());
+ } else {
+ d = image.d;
+ if (d)
+ d->ref.ref();
+ }
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ \fn QImage::QImage(int width, int height, int depth, int numColors, Endian bitOrder)
+
+ Constructs an image with the given \a width, \a height, \a depth,
+ \a numColors colors and \a bitOrder.
+
+ Use the constructor that accepts a width, a height and a format
+ (i.e. specifying the depth and bit order), in combination with the
+ setColorCount() function, instead.
+
+ \oldcode
+ QImage image(width, height, depth, numColors);
+ \newcode
+ QImage image(width, height, format);
+
+ // For 8 bit images the default number of colors is 256. If
+ // another number of colors is required it can be specified
+ // using the setColorCount() function.
+ image.setColorCount(numColors);
+ \endcode
+*/
+
+QImage::QImage(int w, int h, int depth, int colorCount, Endian bitOrder)
+ : QPaintDevice()
+{
+ d = QImageData::create(QSize(w, h), formatFor(depth, bitOrder), colorCount);
+}
+
+/*!
+ Constructs an image with the given \a size, \a depth, \a numColors
+ and \a bitOrder.
+
+ Use the constructor that accepts a size and a format
+ (i.e. specifying the depth and bit order), in combination with the
+ setColorCount() function, instead.
+
+ \oldcode
+ QSize mySize(width, height);
+ QImage image(mySize, depth, numColors);
+ \newcode
+ QSize mySize(width, height);
+ QImage image(mySize, format);
+
+ // For 8 bit images the default number of colors is 256. If
+ // another number of colors is required it can be specified
+ // using the setColorCount() function.
+ image.setColorCount(numColors);
+ \endcode
+*/
+QImage::QImage(const QSize& size, int depth, int numColors, Endian bitOrder)
+ : QPaintDevice()
+{
+ d = QImageData::create(size, formatFor(depth, bitOrder), numColors);
+}
+
+/*!
+ \fn QImage::QImage(uchar* data, int width, int height, int depth, const QRgb* colortable, int numColors, Endian bitOrder)
+
+ Constructs an image with the given \a width, \a height, depth, \a
+ colortable, \a numColors and \a bitOrder, that uses an existing
+ memory buffer, \a data.
+
+ Use the constructor that accepts a uchar pointer, a width, a
+ height and a format (i.e. specifying the depth and bit order), in
+ combination with the setColorTable() function, instead.
+
+ \oldcode
+ uchar *myData;
+ QRgb *myColorTable;
+
+ QImage image(myData, width, height, depth,
+ myColorTable, numColors, IgnoreEndian);
+ \newcode
+ uchar *myData;
+ QVector<QRgb> myColorTable;
+
+ QImage image(myData, width, height, format);
+ image.setColorTable(myColorTable);
+ \endcode
+*/
+QImage::QImage(uchar* data, int w, int h, int depth, const QRgb* colortable, int numColors, Endian bitOrder)
+ : QPaintDevice()
+{
+ d = 0;
+ Format f = formatFor(depth, bitOrder);
+ if (f == Format_Invalid)
+ return;
+
+ const int bytes_per_line = ((w*depth+31)/32)*4; // bytes per scanline
+ if (w <= 0 || h <= 0 || numColors < 0 || !data
+ || INT_MAX/sizeof(uchar *) < uint(h)
+ || INT_MAX/uint(depth) < uint(w)
+ || bytes_per_line <= 0
+ || INT_MAX/uint(bytes_per_line) < uint(h))
+ return; // invalid parameter(s)
+ d = new QImageData;
+ d->ref.ref();
+
+ d->own_data = false;
+ d->data = data;
+ d->width = w;
+ d->height = h;
+ d->depth = depth;
+ d->format = f;
+ if (depth == 32)
+ numColors = 0;
+
+ d->bytes_per_line = bytes_per_line;
+ d->nbytes = d->bytes_per_line * h;
+ if (colortable) {
+ d->colortable.resize(numColors);
+ for (int i = 0; i < numColors; ++i)
+ d->colortable[i] = colortable[i];
+ } else if (numColors) {
+ setColorCount(numColors);
+ }
+}
+
+#ifdef Q_WS_QWS
+
+/*!
+ \fn QImage::QImage(uchar* data, int width, int height, int depth, int bytesPerLine, const QRgb* colortable, int numColors, Endian bitOrder)
+
+ Constructs an image with the given \a width, \a height, \a depth,
+ \a bytesPerLine, \a colortable, \a numColors and \a bitOrder, that
+ uses an existing memory buffer, \a data. The image does not delete
+ the buffer at destruction.
+
+ \warning This constructor is only available in Qt for Embedded Linux.
+
+ The data has to be 32-bit aligned, and each scanline of data in the image
+ must also be 32-bit aligned, so it's no longer possible to specify a custom
+ \a bytesPerLine value.
+*/
+QImage::QImage(uchar* data, int w, int h, int depth, int bpl, const QRgb* colortable, int numColors, Endian bitOrder)
+ : QPaintDevice()
+{
+ d = 0;
+ Format f = formatFor(depth, bitOrder);
+ if (f == Format_Invalid)
+ return;
+ if (!data || w <= 0 || h <= 0 || depth <= 0 || numColors < 0
+ || INT_MAX/sizeof(uchar *) < uint(h)
+ || INT_MAX/uint(depth) < uint(w)
+ || bpl <= 0
+ || INT_MAX/uint(bpl) < uint(h))
+ return; // invalid parameter(s)
+
+ d = new QImageData;
+ d->ref.ref();
+ d->own_data = false;
+ d->data = data;
+ d->width = w;
+ d->height = h;
+ d->depth = depth;
+ d->format = f;
+ if (depth == 32)
+ numColors = 0;
+ d->bytes_per_line = bpl;
+ d->nbytes = d->bytes_per_line * h;
+ if (colortable) {
+ d->colortable.resize(numColors);
+ for (int i = 0; i < numColors; ++i)
+ d->colortable[i] = colortable[i];
+ } else if (numColors) {
+ setColorCount(numColors);
+ }
+}
+#endif // Q_WS_QWS
+#endif // QT3_SUPPORT
+
+/*!
+ Destroys the image and cleans up.
+*/
+
+QImage::~QImage()
+{
+ if (d && !d->ref.deref())
+ delete d;
+}
+
+/*!
+ Assigns a shallow copy of the given \a image to this image and
+ returns a reference to this image.
+
+ For more information about shallow copies, see the \l {Implicit
+ Data Sharing} documentation.
+
+ \sa copy(), QImage()
+*/
+
+QImage &QImage::operator=(const QImage &image)
+{
+ if (image.paintingActive()) {
+ operator=(image.copy());
+ } else {
+ if (image.d)
+ image.d->ref.ref();
+ if (d && !d->ref.deref())
+ delete d;
+ d = image.d;
+ }
+ return *this;
+}
+
+/*!
+ \fn void QImage::swap(QImage &other)
+ \since 4.8
+
+ Swaps image \a other with this image. This operation is very
+ fast and never fails.
+*/
+
+/*!
+ \internal
+*/
+int QImage::devType() const
+{
+ return QInternal::Image;
+}
+
+/*!
+ Returns the image as a QVariant.
+*/
+QImage::operator QVariant() const
+{
+ return QVariant(QVariant::Image, this);
+}
+
+/*!
+ \internal
+
+ If multiple images share common data, this image makes a copy of
+ the data and detaches itself from the sharing mechanism, making
+ sure that this image is the only one referring to the data.
+
+ Nothing is done if there is just a single reference.
+
+ \sa copy(), isDetached(), {Implicit Data Sharing}
+*/
+void QImage::detach()
+{
+ if (d) {
+ if (d->is_cached && d->ref == 1)
+ QImagePixmapCleanupHooks::executeImageHooks(cacheKey());
+
+ if (d->ref != 1 || d->ro_data)
+ *this = copy();
+
+ if (d)
+ ++d->detach_no;
+ }
+}
+
+
+/*!
+ \fn QImage QImage::copy(int x, int y, int width, int height) const
+ \overload
+
+ The returned image is copied from the position (\a x, \a y) in
+ this image, and will always have the given \a width and \a height.
+ In areas beyond this image, pixels are set to 0.
+
+*/
+
+/*!
+ \fn QImage QImage::copy(const QRect& rectangle) const
+
+ Returns a sub-area of the image as a new image.
+
+ The returned image is copied from the position (\a
+ {rectangle}.x(), \a{rectangle}.y()) in this image, and will always
+ have the size of the given \a rectangle.
+
+ In areas beyond this image, pixels are set to 0. For 32-bit RGB
+ images, this means black; for 32-bit ARGB images, this means
+ transparent black; for 8-bit images, this means the color with
+ index 0 in the color table which can be anything; for 1-bit
+ images, this means Qt::color0.
+
+ If the given \a rectangle is a null rectangle the entire image is
+ copied.
+
+ \sa QImage()
+*/
+QImage QImage::copy(const QRect& r) const
+{
+ if (!d)
+ return QImage();
+
+ if (r.isNull()) {
+ QImage image(d->width, d->height, d->format);
+ if (image.isNull())
+ return image;
+
+ // Qt for Embedded Linux can create images with non-default bpl
+ // make sure we don't crash.
+ if (image.d->nbytes != d->nbytes) {
+ int bpl = image.bytesPerLine();
+ for (int i = 0; i < height(); i++)
+ memcpy(image.scanLine(i), scanLine(i), bpl);
+ } else
+ memcpy(image.bits(), bits(), d->nbytes);
+ image.d->colortable = d->colortable;
+ image.d->dpmx = d->dpmx;
+ image.d->dpmy = d->dpmy;
+ image.d->offset = d->offset;
+ image.d->has_alpha_clut = d->has_alpha_clut;
+#ifndef QT_NO_IMAGE_TEXT
+ image.d->text = d->text;
+#endif
+ return image;
+ }
+
+ int x = r.x();
+ int y = r.y();
+ int w = r.width();
+ int h = r.height();
+
+ int dx = 0;
+ int dy = 0;
+ if (w <= 0 || h <= 0)
+ return QImage();
+
+ QImage image(w, h, d->format);
+ if (image.isNull())
+ return image;
+
+ if (x < 0 || y < 0 || x + w > d->width || y + h > d->height) {
+ // bitBlt will not cover entire image - clear it.
+ image.fill(0);
+ if (x < 0) {
+ dx = -x;
+ x = 0;
+ }
+ if (y < 0) {
+ dy = -y;
+ y = 0;
+ }
+ }
+
+ image.d->colortable = d->colortable;
+
+ int pixels_to_copy = qMax(w - dx, 0);
+ if (x > d->width)
+ pixels_to_copy = 0;
+ else if (pixels_to_copy > d->width - x)
+ pixels_to_copy = d->width - x;
+ int lines_to_copy = qMax(h - dy, 0);
+ if (y > d->height)
+ lines_to_copy = 0;
+ else if (lines_to_copy > d->height - y)
+ lines_to_copy = d->height - y;
+
+ bool byteAligned = true;
+ if (d->format == Format_Mono || d->format == Format_MonoLSB)
+ byteAligned = !(dx & 7) && !(x & 7) && !(pixels_to_copy & 7);
+
+ if (byteAligned) {
+ const uchar *src = d->data + ((x * d->depth) >> 3) + y * d->bytes_per_line;
+ uchar *dest = image.d->data + ((dx * d->depth) >> 3) + dy * image.d->bytes_per_line;
+ const int bytes_to_copy = (pixels_to_copy * d->depth) >> 3;
+ for (int i = 0; i < lines_to_copy; ++i) {
+ memcpy(dest, src, bytes_to_copy);
+ src += d->bytes_per_line;
+ dest += image.d->bytes_per_line;
+ }
+ } else if (d->format == Format_Mono) {
+ const uchar *src = d->data + y * d->bytes_per_line;
+ uchar *dest = image.d->data + dy * image.d->bytes_per_line;
+ for (int i = 0; i < lines_to_copy; ++i) {
+ for (int j = 0; j < pixels_to_copy; ++j) {
+ if (src[(x + j) >> 3] & (0x80 >> ((x + j) & 7)))
+ dest[(dx + j) >> 3] |= (0x80 >> ((dx + j) & 7));
+ else
+ dest[(dx + j) >> 3] &= ~(0x80 >> ((dx + j) & 7));
+ }
+ src += d->bytes_per_line;
+ dest += image.d->bytes_per_line;
+ }
+ } else { // Format_MonoLSB
+ Q_ASSERT(d->format == Format_MonoLSB);
+ const uchar *src = d->data + y * d->bytes_per_line;
+ uchar *dest = image.d->data + dy * image.d->bytes_per_line;
+ for (int i = 0; i < lines_to_copy; ++i) {
+ for (int j = 0; j < pixels_to_copy; ++j) {
+ if (src[(x + j) >> 3] & (0x1 << ((x + j) & 7)))
+ dest[(dx + j) >> 3] |= (0x1 << ((dx + j) & 7));
+ else
+ dest[(dx + j) >> 3] &= ~(0x1 << ((dx + j) & 7));
+ }
+ src += d->bytes_per_line;
+ dest += image.d->bytes_per_line;
+ }
+ }
+
+ image.d->dpmx = dotsPerMeterX();
+ image.d->dpmy = dotsPerMeterY();
+ image.d->offset = offset();
+ image.d->has_alpha_clut = d->has_alpha_clut;
+#ifndef QT_NO_IMAGE_TEXT
+ image.d->text = d->text;
+#endif
+ return image;
+}
+
+
+/*!
+ \fn bool QImage::isNull() const
+
+ Returns true if it is a null image, otherwise returns false.
+
+ A null image has all parameters set to zero and no allocated data.
+*/
+bool QImage::isNull() const
+{
+ return !d;
+}
+
+/*!
+ \fn int QImage::width() const
+
+ Returns the width of the image.
+
+ \sa {QImage#Image Information}{Image Information}
+*/
+int QImage::width() const
+{
+ return d ? d->width : 0;
+}
+
+/*!
+ \fn int QImage::height() const
+
+ Returns the height of the image.
+
+ \sa {QImage#Image Information}{Image Information}
+*/
+int QImage::height() const
+{
+ return d ? d->height : 0;
+}
+
+/*!
+ \fn QSize QImage::size() const
+
+ Returns the size of the image, i.e. its width() and height().
+
+ \sa {QImage#Image Information}{Image Information}
+*/
+QSize QImage::size() const
+{
+ return d ? QSize(d->width, d->height) : QSize(0, 0);
+}
+
+/*!
+ \fn QRect QImage::rect() const
+
+ Returns the enclosing rectangle (0, 0, width(), height()) of the
+ image.
+
+ \sa {QImage#Image Information}{Image Information}
+*/
+QRect QImage::rect() const
+{
+ return d ? QRect(0, 0, d->width, d->height) : QRect();
+}
+
+/*!
+ Returns the depth of the image.
+
+ The image depth is the number of bits used to store a single
+ pixel, also called bits per pixel (bpp).
+
+ The supported depths are 1, 8, 16, 24 and 32.
+
+ \sa bitPlaneCount(), convertToFormat(), {QImage#Image Formats}{Image Formats},
+ {QImage#Image Information}{Image Information}
+
+*/
+int QImage::depth() const
+{
+ return d ? d->depth : 0;
+}
+
+/*!
+ \obsolete
+ \fn int QImage::numColors() const
+
+ Returns the size of the color table for the image.
+
+ \sa setColorCount()
+*/
+int QImage::numColors() const
+{
+ return d ? d->colortable.size() : 0;
+}
+
+/*!
+ \since 4.6
+ \fn int QImage::colorCount() const
+
+ Returns the size of the color table for the image.
+
+ Notice that colorCount() returns 0 for 32-bpp images because these
+ images do not use color tables, but instead encode pixel values as
+ ARGB quadruplets.
+
+ \sa setColorCount(), {QImage#Image Information}{Image Information}
+*/
+int QImage::colorCount() const
+{
+ return d ? d->colortable.size() : 0;
+}
+
+
+#ifdef QT3_SUPPORT
+/*!
+ \fn QImage::Endian QImage::bitOrder() const
+
+ Returns the bit order for the image. If it is a 1-bpp image, this
+ function returns either QImage::BigEndian or
+ QImage::LittleEndian. Otherwise, this function returns
+ QImage::IgnoreEndian.
+
+ Use the format() function instead for the monochrome formats. For
+ non-monochrome formats the bit order is irrelevant.
+*/
+
+/*!
+ Returns a pointer to the scanline pointer table. This is the
+ beginning of the data block for the image.
+ Returns 0 in case of an error.
+
+ Use the bits() or scanLine() function instead.
+*/
+uchar **QImage::jumpTable()
+{
+ if (!d)
+ return 0;
+ detach();
+
+ // in case detach() ran out of memory..
+ if (!d)
+ return 0;
+
+ if (!d->jumptable) {
+ d->jumptable = (uchar **)malloc(d->height*sizeof(uchar *));
+ if (!d->jumptable)
+ return 0;
+ uchar *data = d->data;
+ int height = d->height;
+ uchar **p = d->jumptable;
+ while (height--) {
+ *p++ = data;
+ data += d->bytes_per_line;
+ }
+ }
+ return d->jumptable;
+}
+
+/*!
+ \overload
+*/
+const uchar * const *QImage::jumpTable() const
+{
+ if (!d)
+ return 0;
+ if (!d->jumptable) {
+ d->jumptable = (uchar **)malloc(d->height*sizeof(uchar *));
+ if (!d->jumptable)
+ return 0;
+ uchar *data = d->data;
+ int height = d->height;
+ uchar **p = d->jumptable;
+ while (height--) {
+ *p++ = data;
+ data += d->bytes_per_line;
+ }
+ }
+ return d->jumptable;
+}
+#endif
+
+/*!
+ Sets the color table used to translate color indexes to QRgb
+ values, to the specified \a colors.
+
+ When the image is used, the color table must be large enough to
+ have entries for all the pixel/index values present in the image,
+ otherwise the results are undefined.
+
+ \sa colorTable(), setColor(), {QImage#Image Transformations}{Image
+ Transformations}
+*/
+void QImage::setColorTable(const QVector<QRgb> colors)
+{
+ if (!d)
+ return;
+ detach();
+
+ // In case detach() ran out of memory
+ if (!d)
+ return;
+
+ d->colortable = colors;
+ d->has_alpha_clut = false;
+ for (int i = 0; i < d->colortable.size(); ++i) {
+ if (qAlpha(d->colortable.at(i)) != 255) {
+ d->has_alpha_clut = true;
+ break;
+ }
+ }
+}
+
+/*!
+ Returns a list of the colors contained in the image's color table,
+ or an empty list if the image does not have a color table
+
+ \sa setColorTable(), colorCount(), color()
+*/
+QVector<QRgb> QImage::colorTable() const
+{
+ return d ? d->colortable : QVector<QRgb>();
+}
+
+
+/*!
+ \obsolete
+ Returns the number of bytes occupied by the image data.
+
+ \sa byteCount()
+*/
+int QImage::numBytes() const
+{
+ return d ? d->nbytes : 0;
+}
+
+/*!
+ \since 4.6
+ Returns the number of bytes occupied by the image data.
+
+ \sa bytesPerLine(), bits(), {QImage#Image Information}{Image
+ Information}
+*/
+int QImage::byteCount() const
+{
+ return d ? d->nbytes : 0;
+}
+
+/*!
+ Returns the number of bytes per image scanline.
+
+ This is equivalent to byteCount() / height().
+
+ \sa scanLine()
+*/
+int QImage::bytesPerLine() const
+{
+ return (d && d->height) ? d->nbytes / d->height : 0;
+}
+
+
+/*!
+ Returns the color in the color table at index \a i. The first
+ color is at index 0.
+
+ The colors in an image's color table are specified as ARGB
+ quadruplets (QRgb). Use the qAlpha(), qRed(), qGreen(), and
+ qBlue() functions to get the color value components.
+
+ \sa setColor(), pixelIndex(), {QImage#Pixel Manipulation}{Pixel
+ Manipulation}
+*/
+QRgb QImage::color(int i) const
+{
+ Q_ASSERT(i < colorCount());
+ return d ? d->colortable.at(i) : QRgb(uint(-1));
+}
+
+/*!
+ \fn void QImage::setColor(int index, QRgb colorValue)
+
+ Sets the color at the given \a index in the color table, to the
+ given to \a colorValue. The color value is an ARGB quadruplet.
+
+ If \a index is outside the current size of the color table, it is
+ expanded with setColorCount().
+
+ \sa color(), colorCount(), setColorTable(), {QImage#Pixel Manipulation}{Pixel
+ Manipulation}
+*/
+void QImage::setColor(int i, QRgb c)
+{
+ if (!d)
+ return;
+ if (i < 0 || d->depth > 8 || i >= 1<<d->depth) {
+ qWarning("QImage::setColor: Index out of bound %d", i);
+ return;
+ }
+ detach();
+
+ // In case detach() run out of memory
+ if (!d)
+ return;
+
+ if (i >= d->colortable.size())
+ setColorCount(i+1);
+ d->colortable[i] = c;
+ d->has_alpha_clut |= (qAlpha(c) != 255);
+}
+
+/*!
+ Returns a pointer to the pixel data at the scanline with index \a
+ i. The first scanline is at index 0.
+
+ The scanline data is aligned on a 32-bit boundary.
+
+ \warning If you are accessing 32-bpp image data, cast the returned
+ pointer to \c{QRgb*} (QRgb has a 32-bit size) and use it to
+ read/write the pixel value. You cannot use the \c{uchar*} pointer
+ directly, because the pixel format depends on the byte order on
+ the underlying platform. Use qRed(), qGreen(), qBlue(), and
+ qAlpha() to access the pixels.
+
+ \sa bytesPerLine(), bits(), {QImage#Pixel Manipulation}{Pixel
+ Manipulation}, constScanLine()
+*/
+uchar *QImage::scanLine(int i)
+{
+ if (!d)
+ return 0;
+
+ detach();
+
+ // In case detach() ran out of memory
+ if (!d)
+ return 0;
+
+ return d->data + i * d->bytes_per_line;
+}
+
+/*!
+ \overload
+*/
+const uchar *QImage::scanLine(int i) const
+{
+ if (!d)
+ return 0;
+
+ Q_ASSERT(i >= 0 && i < height());
+ return d->data + i * d->bytes_per_line;
+}
+
+
+/*!
+ Returns a pointer to the pixel data at the scanline with index \a
+ i. The first scanline is at index 0.
+
+ The scanline data is aligned on a 32-bit boundary.
+
+ Note that QImage uses \l{Implicit Data Sharing} {implicit data
+ sharing}, but this function does \e not perform a deep copy of the
+ shared pixel data, because the returned data is const.
+
+ \sa scanLine(), constBits()
+ \since 4.7
+*/
+const uchar *QImage::constScanLine(int i) const
+{
+ if (!d)
+ return 0;
+
+ Q_ASSERT(i >= 0 && i < height());
+ return d->data + i * d->bytes_per_line;
+}
+
+/*!
+ Returns a pointer to the first pixel data. This is equivalent to
+ scanLine(0).
+
+ Note that QImage uses \l{Implicit Data Sharing} {implicit data
+ sharing}. This function performs a deep copy of the shared pixel
+ data, thus ensuring that this QImage is the only one using the
+ current return value.
+
+ \sa scanLine(), byteCount(), constBits()
+*/
+uchar *QImage::bits()
+{
+ if (!d)
+ return 0;
+ detach();
+
+ // In case detach ran out of memory...
+ if (!d)
+ return 0;
+
+ return d->data;
+}
+
+/*!
+ \overload
+
+ Note that QImage uses \l{Implicit Data Sharing} {implicit data
+ sharing}, but this function does \e not perform a deep copy of the
+ shared pixel data, because the returned data is const.
+*/
+const uchar *QImage::bits() const
+{
+ return d ? d->data : 0;
+}
+
+
+/*!
+ Returns a pointer to the first pixel data.
+
+ Note that QImage uses \l{Implicit Data Sharing} {implicit data
+ sharing}, but this function does \e not perform a deep copy of the
+ shared pixel data, because the returned data is const.
+
+ \sa bits(), constScanLine()
+ \since 4.7
+*/
+const uchar *QImage::constBits() const
+{
+ return d ? d->data : 0;
+}
+
+/*!
+ \fn void QImage::reset()
+
+ Resets all image parameters and deallocates the image data.
+
+ Assign a null image instead.
+
+ \oldcode
+ QImage image;
+ image.reset();
+ \newcode
+ QImage image;
+ image = QImage();
+ \endcode
+*/
+
+/*!
+ \fn void QImage::fill(uint pixelValue)
+
+ Fills the entire image with the given \a pixelValue.
+
+ If the depth of this image is 1, only the lowest bit is used. If
+ you say fill(0), fill(2), etc., the image is filled with 0s. If
+ you say fill(1), fill(3), etc., the image is filled with 1s. If
+ the depth is 8, the lowest 8 bits are used and if the depth is 16
+ the lowest 16 bits are used.
+
+ Note: QImage::pixel() returns the color of the pixel at the given
+ coordinates while QColor::pixel() returns the pixel value of the
+ underlying window system (essentially an index value), so normally
+ you will want to use QImage::pixel() to use a color from an
+ existing image or QColor::rgb() to use a specific color.
+
+ \sa depth(), {QImage#Image Transformations}{Image Transformations}
+*/
+
+void QImage::fill(uint pixel)
+{
+ if (!d)
+ return;
+
+ detach();
+
+ // In case detach() ran out of memory
+ if (!d)
+ return;
+
+ if (d->depth == 1 || d->depth == 8) {
+ int w = d->width;
+ if (d->depth == 1) {
+ if (pixel & 1)
+ pixel = 0xffffffff;
+ else
+ pixel = 0;
+ w = (w + 7) / 8;
+ } else {
+ pixel &= 0xff;
+ }
+ qt_rectfill<quint8>(d->data, pixel, 0, 0,
+ w, d->height, d->bytes_per_line);
+ return;
+ } else if (d->depth == 16) {
+ qt_rectfill<quint16>(reinterpret_cast<quint16*>(d->data), pixel,
+ 0, 0, d->width, d->height, d->bytes_per_line);
+ return;
+ } else if (d->depth == 24) {
+ qt_rectfill<quint24>(reinterpret_cast<quint24*>(d->data), pixel,
+ 0, 0, d->width, d->height, d->bytes_per_line);
+ return;
+ }
+
+ if (d->format == Format_RGB32)
+ pixel |= 0xff000000;
+
+ qt_rectfill<uint>(reinterpret_cast<uint*>(d->data), pixel,
+ 0, 0, d->width, d->height, d->bytes_per_line);
+}
+
+
+/*!
+ \fn void QImage::fill(Qt::GlobalColor color)
+
+ \overload
+
+ \since 4.8
+ */
+
+void QImage::fill(Qt::GlobalColor color)
+{
+ fill(QColor(color));
+}
+
+
+
+/*!
+ \fn void QImage::fill(Qt::GlobalColor color)
+
+ \overload
+
+ Fills the entire image with the given \a color.
+
+ If the depth of the image is 1, the image will be filled with 1 if
+ \a color equals Qt::color1; it will otherwise be filled with 0.
+
+ If the depth of the image is 8, the image will be filled with the
+ index corresponding the \a color in the color table if present; it
+ will otherwise be filled with 0.
+
+ \since 4.8
+*/
+
+void QImage::fill(const QColor &color)
+{
+ if (!d)
+ return;
+ detach();
+
+ // In case we run out of memory
+ if (!d)
+ return;
+
+ if (d->depth == 32) {
+ uint pixel = color.rgba();
+ if (d->format == QImage::Format_ARGB32_Premultiplied)
+ pixel = PREMUL(pixel);
+ fill((uint) pixel);
+
+ } else if (d->depth == 16 && d->format == QImage::Format_RGB16) {
+ qrgb565 p(color.rgba());
+ fill((uint) p.rawValue());
+
+ } else if (d->depth == 1) {
+ if (color == Qt::color1)
+ fill((uint) 1);
+ else
+ fill((uint) 0);
+
+ } else if (d->depth == 8) {
+ uint pixel = 0;
+ for (int i=0; i<d->colortable.size(); ++i) {
+ if (color.rgba() == d->colortable.at(i)) {
+ pixel = i;
+ break;
+ }
+ }
+ fill(pixel);
+
+ } else {
+ QPainter p(this);
+ p.setCompositionMode(QPainter::CompositionMode_Source);
+ p.fillRect(rect(), color);
+ }
+
+}
+
+
+
+
+
+
+/*!
+ Inverts all pixel values in the image.
+
+ The given invert \a mode only have a meaning when the image's
+ depth is 32. The default \a mode is InvertRgb, which leaves the
+ alpha channel unchanged. If the \a mode is InvertRgba, the alpha
+ bits are also inverted.
+
+ Inverting an 8-bit image means to replace all pixels using color
+ index \e i with a pixel using color index 255 minus \e i. The same
+ is the case for a 1-bit image. Note that the color table is \e not
+ changed.
+
+ \sa {QImage#Image Transformations}{Image Transformations}
+*/
+
+void QImage::invertPixels(InvertMode mode)
+{
+ if (!d)
+ return;
+
+ detach();
+
+ // In case detach() ran out of memory
+ if (!d)
+ return;
+
+ if (depth() != 32) {
+ // number of used bytes pr line
+ int bpl = (d->width * d->depth + 7) / 8;
+ int pad = d->bytes_per_line - bpl;
+ uchar *sl = d->data;
+ for (int y=0; y<d->height; ++y) {
+ for (int x=0; x<bpl; ++x)
+ *sl++ ^= 0xff;
+ sl += pad;
+ }
+ } else {
+ quint32 *p = (quint32*)d->data;
+ quint32 *end = (quint32*)(d->data + d->nbytes);
+ uint xorbits = (mode == InvertRgba) ? 0xffffffff : 0x00ffffff;
+ while (p < end)
+ *p++ ^= xorbits;
+ }
+}
+
+/*!
+ \fn void QImage::invertPixels(bool invertAlpha)
+
+ Use the invertPixels() function that takes a QImage::InvertMode
+ parameter instead.
+*/
+
+/*! \fn QImage::Endian QImage::systemByteOrder()
+
+ Determines the host computer byte order. Returns
+ QImage::LittleEndian (LSB first) or QImage::BigEndian (MSB first).
+
+ This function is no longer relevant for QImage. Use QSysInfo
+ instead.
+*/
+
+// Windows defines these
+#if defined(write)
+# undef write
+#endif
+#if defined(close)
+# undef close
+#endif
+#if defined(read)
+# undef read
+#endif
+
+/*!
+ \obsolete
+ Resizes the color table to contain \a numColors entries.
+
+ \sa setColorCount()
+*/
+
+void QImage::setNumColors(int numColors)
+{
+ setColorCount(numColors);
+}
+
+/*!
+ \since 4.6
+ Resizes the color table to contain \a colorCount entries.
+
+ If the color table is expanded, all the extra colors will be set to
+ transparent (i.e qRgba(0, 0, 0, 0)).
+
+ When the image is used, the color table must be large enough to
+ have entries for all the pixel/index values present in the image,
+ otherwise the results are undefined.
+
+ \sa colorCount(), colorTable(), setColor(), {QImage#Image
+ Transformations}{Image Transformations}
+*/
+
+void QImage::setColorCount(int colorCount)
+{
+ if (!d) {
+ qWarning("QImage::setColorCount: null image");
+ return;
+ }
+
+ detach();
+
+ // In case detach() ran out of memory
+ if (!d)
+ return;
+
+ if (colorCount == d->colortable.size())
+ return;
+ if (colorCount <= 0) { // use no color table
+ d->colortable = QVector<QRgb>();
+ return;
+ }
+ int nc = d->colortable.size();
+ d->colortable.resize(colorCount);
+ for (int i = nc; i < colorCount; ++i)
+ d->colortable[i] = 0;
+}
+
+/*!
+ Returns the format of the image.
+
+ \sa {QImage#Image Formats}{Image Formats}
+*/
+QImage::Format QImage::format() const
+{
+ return d ? d->format : Format_Invalid;
+}
+
+
+#ifdef QT3_SUPPORT
+/*!
+ Returns true if alpha buffer mode is enabled; otherwise returns
+ false.
+
+ Use the hasAlphaChannel() function instead.
+
+*/
+bool QImage::hasAlphaBuffer() const
+{
+ if (!d)
+ return false;
+
+ switch (d->format) {
+ case Format_ARGB32:
+ case Format_ARGB32_Premultiplied:
+ case Format_ARGB8565_Premultiplied:
+ case Format_ARGB8555_Premultiplied:
+ case Format_ARGB6666_Premultiplied:
+ case Format_ARGB4444_Premultiplied:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/*!
+ Enables alpha buffer mode if \a enable is true, otherwise disables
+ it. The alpha buffer is used to set a mask when a QImage is
+ translated to a QPixmap.
+
+ If a monochrome or indexed 8-bit image has alpha channels in their
+ color tables they will automatically detect that they have an
+ alpha channel, so this function is not required. To force alpha
+ channels on 32-bit images, use the convertToFormat() function.
+*/
+
+void QImage::setAlphaBuffer(bool enable)
+{
+ if (!d
+ || d->format == Format_Mono
+ || d->format == Format_MonoLSB
+ || d->format == Format_Indexed8)
+ return;
+ if (enable && (d->format == Format_ARGB32 ||
+ d->format == Format_ARGB32_Premultiplied ||
+ d->format == Format_ARGB8565_Premultiplied ||
+ d->format == Format_ARGB6666_Premultiplied ||
+ d->format == Format_ARGB8555_Premultiplied ||
+ d->format == Format_ARGB4444_Premultiplied))
+ {
+ return;
+ }
+ if (!enable && (d->format == Format_RGB32 ||
+ d->format == Format_RGB555 ||
+ d->format == Format_RGB666 ||
+ d->format == Format_RGB888 ||
+ d->format == Format_RGB444))
+ {
+ return;
+ }
+ detach();
+ d->format = (enable ? Format_ARGB32 : Format_RGB32);
+}
+
+
+/*!
+ \fn bool QImage::create(int width, int height, int depth, int numColors, Endian bitOrder)
+
+ Sets the image \a width, \a height, \a depth, its number of colors
+ (in \a numColors), and bit order. Returns true if successful, or
+ false if the parameters are incorrect or if memory cannot be
+ allocated.
+
+ The \a width and \a height is limited to 32767. \a depth must be
+ 1, 8, or 32. If \a depth is 1, \a bitOrder must be set to
+ either QImage::LittleEndian or QImage::BigEndian. For other depths
+ \a bitOrder must be QImage::IgnoreEndian.
+
+ This function allocates a color table and a buffer for the image
+ data. The image data is not initialized. The image buffer is
+ allocated as a single block that consists of a table of scanLine()
+ pointers (jumpTable()) and the image data (bits()).
+
+ Use a QImage constructor instead.
+*/
+bool QImage::create(int width, int height, int depth, int numColors, Endian bitOrder)
+{
+ if (d && !d->ref.deref())
+ delete d;
+ d = QImageData::create(QSize(width, height), formatFor(depth, bitOrder), numColors);
+ return true;
+}
+
+/*!
+ \fn bool QImage::create(const QSize& size, int depth, int numColors, Endian bitOrder)
+ \overload
+
+ The width and height are specified in the \a size argument.
+
+ Use a QImage constructor instead.
+*/
+bool QImage::create(const QSize& size, int depth, int numColors, QImage::Endian bitOrder)
+{
+ if (d && !d->ref.deref())
+ delete d;
+ d = QImageData::create(size, formatFor(depth, bitOrder), numColors);
+ return true;
+}
+#endif // QT3_SUPPORT
+
+/*****************************************************************************
+ Internal routines for converting image depth.
+ *****************************************************************************/
+
+typedef void (*Image_Converter)(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags);
+
+typedef bool (*InPlace_Image_Converter)(QImageData *data, Qt::ImageConversionFlags);
+
+static void convert_ARGB_to_ARGB_PM(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
+{
+ Q_ASSERT(src->format == QImage::Format_ARGB32);
+ Q_ASSERT(dest->format == QImage::Format_ARGB32_Premultiplied);
+ Q_ASSERT(src->width == dest->width);
+ Q_ASSERT(src->height == dest->height);
+
+ const int src_pad = (src->bytes_per_line >> 2) - src->width;
+ const int dest_pad = (dest->bytes_per_line >> 2) - dest->width;
+ const QRgb *src_data = (QRgb *) src->data;
+ QRgb *dest_data = (QRgb *) dest->data;
+
+ for (int i = 0; i < src->height; ++i) {
+ const QRgb *end = src_data + src->width;
+ while (src_data < end) {
+ *dest_data = PREMUL(*src_data);
+ ++src_data;
+ ++dest_data;
+ }
+ src_data += src_pad;
+ dest_data += dest_pad;
+ }
+}
+
+static bool convert_ARGB_to_ARGB_PM_inplace(QImageData *data, Qt::ImageConversionFlags)
+{
+ Q_ASSERT(data->format == QImage::Format_ARGB32);
+
+ const int pad = (data->bytes_per_line >> 2) - data->width;
+ QRgb *rgb_data = (QRgb *) data->data;
+
+ for (int i = 0; i < data->height; ++i) {
+ const QRgb *end = rgb_data + data->width;
+ while (rgb_data < end) {
+ *rgb_data = PREMUL(*rgb_data);
+ ++rgb_data;
+ }
+ rgb_data += pad;
+ }
+ data->format = QImage::Format_ARGB32_Premultiplied;
+ return true;
+}
+
+static bool convert_indexed8_to_ARGB_PM_inplace(QImageData *data, Qt::ImageConversionFlags)
+{
+ Q_ASSERT(data->format == QImage::Format_Indexed8);
+ const int depth = 32;
+
+ const int dst_bytes_per_line = ((data->width * depth + 31) >> 5) << 2;
+ const int nbytes = dst_bytes_per_line * data->height;
+ uchar *const newData = (uchar *)realloc(data->data, nbytes);
+ if (!newData)
+ return false;
+
+ data->data = newData;
+
+ // start converting from the end because the end image is bigger than the source
+ uchar *src_data = newData + data->nbytes; // end of src
+ quint32 *dest_data = (quint32 *) (newData + nbytes); // end of dest > end of src
+ const int width = data->width;
+ const int src_pad = data->bytes_per_line - width;
+ const int dest_pad = (dst_bytes_per_line >> 2) - width;
+ if (data->colortable.size() == 0) {
+ data->colortable.resize(256);
+ for (int i = 0; i < 256; ++i)
+ data->colortable[i] = qRgb(i, i, i);
+ } else {
+ for (int i = 0; i < data->colortable.size(); ++i)
+ data->colortable[i] = PREMUL(data->colortable.at(i));
+
+ // Fill the rest of the table in case src_data > colortable.size()
+ const int oldSize = data->colortable.size();
+ const QRgb lastColor = data->colortable.at(oldSize - 1);
+ data->colortable.insert(oldSize, 256 - oldSize, lastColor);
+ }
+
+ for (int i = 0; i < data->height; ++i) {
+ src_data -= src_pad;
+ dest_data -= dest_pad;
+ for (int pixI = 0; pixI < width; ++pixI) {
+ --src_data;
+ --dest_data;
+ *dest_data = data->colortable.at(*src_data);
+ }
+ }
+
+ data->colortable = QVector<QRgb>();
+ data->format = QImage::Format_ARGB32_Premultiplied;
+ data->bytes_per_line = dst_bytes_per_line;
+ data->depth = depth;
+ data->nbytes = nbytes;
+
+ return true;
+}
+
+static bool convert_indexed8_to_RGB_inplace(QImageData *data, Qt::ImageConversionFlags)
+{
+ Q_ASSERT(data->format == QImage::Format_Indexed8);
+ const int depth = 32;
+
+ const int dst_bytes_per_line = ((data->width * depth + 31) >> 5) << 2;
+ const int nbytes = dst_bytes_per_line * data->height;
+ uchar *const newData = (uchar *)realloc(data->data, nbytes);
+ if (!newData)
+ return false;
+
+ data->data = newData;
+
+ // start converting from the end because the end image is bigger than the source
+ uchar *src_data = newData + data->nbytes;
+ quint32 *dest_data = (quint32 *) (newData + nbytes);
+ const int width = data->width;
+ const int src_pad = data->bytes_per_line - width;
+ const int dest_pad = (dst_bytes_per_line >> 2) - width;
+ if (data->colortable.size() == 0) {
+ data->colortable.resize(256);
+ for (int i = 0; i < 256; ++i)
+ data->colortable[i] = qRgb(i, i, i);
+ } else {
+ // Fill the rest of the table in case src_data > colortable.size()
+ const int oldSize = data->colortable.size();
+ const QRgb lastColor = data->colortable.at(oldSize - 1);
+ data->colortable.insert(oldSize, 256 - oldSize, lastColor);
+ }
+
+ for (int i = 0; i < data->height; ++i) {
+ src_data -= src_pad;
+ dest_data -= dest_pad;
+ for (int pixI = 0; pixI < width; ++pixI) {
+ --src_data;
+ --dest_data;
+ *dest_data = (quint32) data->colortable.at(*src_data);
+ }
+ }
+
+ data->colortable = QVector<QRgb>();
+ data->format = QImage::Format_RGB32;
+ data->bytes_per_line = dst_bytes_per_line;
+ data->depth = depth;
+ data->nbytes = nbytes;
+
+ return true;
+}
+
+static bool convert_indexed8_to_RGB16_inplace(QImageData *data, Qt::ImageConversionFlags)
+{
+ Q_ASSERT(data->format == QImage::Format_Indexed8);
+ const int depth = 16;
+
+ const int dst_bytes_per_line = ((data->width * depth + 31) >> 5) << 2;
+ const int nbytes = dst_bytes_per_line * data->height;
+ uchar *const newData = (uchar *)realloc(data->data, nbytes);
+ if (!newData)
+ return false;
+
+ data->data = newData;
+
+ // start converting from the end because the end image is bigger than the source
+ uchar *src_data = newData + data->nbytes;
+ quint16 *dest_data = (quint16 *) (newData + nbytes);
+ const int width = data->width;
+ const int src_pad = data->bytes_per_line - width;
+ const int dest_pad = (dst_bytes_per_line >> 1) - width;
+
+ quint16 colorTableRGB16[256];
+ if (data->colortable.isEmpty()) {
+ for (int i = 0; i < 256; ++i)
+ colorTableRGB16[i] = qt_colorConvert<quint16, quint32>(qRgb(i, i, i), 0);
+ } else {
+ // 1) convert the existing colors to RGB16
+ const int tableSize = data->colortable.size();
+ for (int i = 0; i < tableSize; ++i)
+ colorTableRGB16[i] = qt_colorConvert<quint16, quint32>(data->colortable.at(i), 0);
+ data->colortable = QVector<QRgb>();
+
+ // 2) fill the rest of the table in case src_data > colortable.size()
+ const quint16 lastColor = colorTableRGB16[tableSize - 1];
+ for (int i = tableSize; i < 256; ++i)
+ colorTableRGB16[i] = lastColor;
+ }
+
+ for (int i = 0; i < data->height; ++i) {
+ src_data -= src_pad;
+ dest_data -= dest_pad;
+ for (int pixI = 0; pixI < width; ++pixI) {
+ --src_data;
+ --dest_data;
+ *dest_data = colorTableRGB16[*src_data];
+ }
+ }
+
+ data->format = QImage::Format_RGB16;
+ data->bytes_per_line = dst_bytes_per_line;
+ data->depth = depth;
+ data->nbytes = nbytes;
+
+ return true;
+}
+
+static bool convert_RGB_to_RGB16_inplace(QImageData *data, Qt::ImageConversionFlags)
+{
+ Q_ASSERT(data->format == QImage::Format_RGB32);
+ const int depth = 16;
+
+ const int dst_bytes_per_line = ((data->width * depth + 31) >> 5) << 2;
+ const int src_bytes_per_line = data->bytes_per_line;
+ quint32 *src_data = (quint32 *) data->data;
+ quint16 *dst_data = (quint16 *) data->data;
+
+ for (int i = 0; i < data->height; ++i) {
+ qt_memconvert(dst_data, src_data, data->width);
+ src_data = (quint32 *) (((char*)src_data) + src_bytes_per_line);
+ dst_data = (quint16 *) (((char*)dst_data) + dst_bytes_per_line);
+ }
+ data->format = QImage::Format_RGB16;
+ data->bytes_per_line = dst_bytes_per_line;
+ data->depth = depth;
+ data->nbytes = dst_bytes_per_line * data->height;
+ uchar *const newData = (uchar *)realloc(data->data, data->nbytes);
+ if (newData) {
+ data->data = newData;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+static void convert_ARGB_PM_to_ARGB(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
+{
+ Q_ASSERT(src->format == QImage::Format_ARGB32_Premultiplied);
+ Q_ASSERT(dest->format == QImage::Format_ARGB32);
+ Q_ASSERT(src->width == dest->width);
+ Q_ASSERT(src->height == dest->height);
+
+ const int src_pad = (src->bytes_per_line >> 2) - src->width;
+ const int dest_pad = (dest->bytes_per_line >> 2) - dest->width;
+ const QRgb *src_data = (QRgb *) src->data;
+ QRgb *dest_data = (QRgb *) dest->data;
+
+ for (int i = 0; i < src->height; ++i) {
+ const QRgb *end = src_data + src->width;
+ while (src_data < end) {
+ *dest_data = INV_PREMUL(*src_data);
+ ++src_data;
+ ++dest_data;
+ }
+ src_data += src_pad;
+ dest_data += dest_pad;
+ }
+}
+
+static void convert_ARGB_PM_to_RGB(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
+{
+ Q_ASSERT(src->format == QImage::Format_ARGB32_Premultiplied);
+ Q_ASSERT(dest->format == QImage::Format_RGB32);
+ Q_ASSERT(src->width == dest->width);
+ Q_ASSERT(src->height == dest->height);
+
+ const int src_pad = (src->bytes_per_line >> 2) - src->width;
+ const int dest_pad = (dest->bytes_per_line >> 2) - dest->width;
+ const QRgb *src_data = (QRgb *) src->data;
+ QRgb *dest_data = (QRgb *) dest->data;
+
+ for (int i = 0; i < src->height; ++i) {
+ const QRgb *end = src_data + src->width;
+ while (src_data < end) {
+ *dest_data = 0xff000000 | INV_PREMUL(*src_data);
+ ++src_data;
+ ++dest_data;
+ }
+ src_data += src_pad;
+ dest_data += dest_pad;
+ }
+}
+
+static void swap_bit_order(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
+{
+ Q_ASSERT(src->format == QImage::Format_Mono || src->format == QImage::Format_MonoLSB);
+ Q_ASSERT(dest->format == QImage::Format_Mono || dest->format == QImage::Format_MonoLSB);
+ Q_ASSERT(src->width == dest->width);
+ Q_ASSERT(src->height == dest->height);
+ Q_ASSERT(src->nbytes == dest->nbytes);
+ Q_ASSERT(src->bytes_per_line == dest->bytes_per_line);
+
+ dest->colortable = src->colortable;
+
+ const uchar *src_data = src->data;
+ const uchar *end = src->data + src->nbytes;
+ uchar *dest_data = dest->data;
+ while (src_data < end) {
+ *dest_data = bitflip[*src_data];
+ ++src_data;
+ ++dest_data;
+ }
+}
+
+static void mask_alpha_converter(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
+{
+ Q_ASSERT(src->width == dest->width);
+ Q_ASSERT(src->height == dest->height);
+
+ const int src_pad = (src->bytes_per_line >> 2) - src->width;
+ const int dest_pad = (dest->bytes_per_line >> 2) - dest->width;
+ const uint *src_data = (const uint *)src->data;
+ uint *dest_data = (uint *)dest->data;
+
+ for (int i = 0; i < src->height; ++i) {
+ const uint *end = src_data + src->width;
+ while (src_data < end) {
+ *dest_data = *src_data | 0xff000000;
+ ++src_data;
+ ++dest_data;
+ }
+ src_data += src_pad;
+ dest_data += dest_pad;
+ }
+}
+
+static QVector<QRgb> fix_color_table(const QVector<QRgb> &ctbl, QImage::Format format)
+{
+ QVector<QRgb> colorTable = ctbl;
+ if (format == QImage::Format_RGB32) {
+ // check if the color table has alpha
+ for (int i = 0; i < colorTable.size(); ++i)
+ if (qAlpha(colorTable.at(i) != 0xff))
+ colorTable[i] = colorTable.at(i) | 0xff000000;
+ } else if (format == QImage::Format_ARGB32_Premultiplied) {
+ // check if the color table has alpha
+ for (int i = 0; i < colorTable.size(); ++i)
+ colorTable[i] = PREMUL(colorTable.at(i));
+ }
+ return colorTable;
+}
+
+//
+// dither_to_1: Uses selected dithering algorithm.
+//
+
+static void dither_to_Mono(QImageData *dst, const QImageData *src,
+ Qt::ImageConversionFlags flags, bool fromalpha)
+{
+ Q_ASSERT(src->width == dst->width);
+ Q_ASSERT(src->height == dst->height);
+ Q_ASSERT(dst->format == QImage::Format_Mono || dst->format == QImage::Format_MonoLSB);
+
+ dst->colortable.clear();
+ dst->colortable.append(0xffffffff);
+ dst->colortable.append(0xff000000);
+
+ enum { Threshold, Ordered, Diffuse } dithermode;
+
+ if (fromalpha) {
+ if ((flags & Qt::AlphaDither_Mask) == Qt::DiffuseAlphaDither)
+ dithermode = Diffuse;
+ else if ((flags & Qt::AlphaDither_Mask) == Qt::OrderedAlphaDither)
+ dithermode = Ordered;
+ else
+ dithermode = Threshold;
+ } else {
+ if ((flags & Qt::Dither_Mask) == Qt::ThresholdDither)
+ dithermode = Threshold;
+ else if ((flags & Qt::Dither_Mask) == Qt::OrderedDither)
+ dithermode = Ordered;
+ else
+ dithermode = Diffuse;
+ }
+
+ int w = src->width;
+ int h = src->height;
+ int d = src->depth;
+ uchar gray[256]; // gray map for 8 bit images
+ bool use_gray = (d == 8);
+ if (use_gray) { // make gray map
+ if (fromalpha) {
+ // Alpha 0x00 -> 0 pixels (white)
+ // Alpha 0xFF -> 1 pixels (black)
+ for (int i = 0; i < src->colortable.size(); i++)
+ gray[i] = (255 - (src->colortable.at(i) >> 24));
+ } else {
+ // Pixel 0x00 -> 1 pixels (black)
+ // Pixel 0xFF -> 0 pixels (white)
+ for (int i = 0; i < src->colortable.size(); i++)
+ gray[i] = qGray(src->colortable.at(i));
+ }
+ }
+
+ uchar *dst_data = dst->data;
+ int dst_bpl = dst->bytes_per_line;
+ const uchar *src_data = src->data;
+ int src_bpl = src->bytes_per_line;
+
+ switch (dithermode) {
+ case Diffuse: {
+ QScopedArrayPointer<int> lineBuffer(new int[w * 2]);
+ int *line1 = lineBuffer.data();
+ int *line2 = lineBuffer.data() + w;
+ int bmwidth = (w+7)/8;
+
+ int *b1, *b2;
+ int wbytes = w * (d/8);
+ register const uchar *p = src->data;
+ const uchar *end = p + wbytes;
+ b2 = line2;
+ if (use_gray) { // 8 bit image
+ while (p < end)
+ *b2++ = gray[*p++];
+ } else { // 32 bit image
+ if (fromalpha) {
+ while (p < end) {
+ *b2++ = 255 - (*(uint*)p >> 24);
+ p += 4;
+ }
+ } else {
+ while (p < end) {
+ *b2++ = qGray(*(uint*)p);
+ p += 4;
+ }
+ }
+ }
+ for (int y=0; y<h; y++) { // for each scan line...
+ int *tmp = line1; line1 = line2; line2 = tmp;
+ bool not_last_line = y < h - 1;
+ if (not_last_line) { // calc. grayvals for next line
+ p = src->data + (y+1)*src->bytes_per_line;
+ end = p + wbytes;
+ b2 = line2;
+ if (use_gray) { // 8 bit image
+ while (p < end)
+ *b2++ = gray[*p++];
+ } else { // 24 bit image
+ if (fromalpha) {
+ while (p < end) {
+ *b2++ = 255 - (*(uint*)p >> 24);
+ p += 4;
+ }
+ } else {
+ while (p < end) {
+ *b2++ = qGray(*(uint*)p);
+ p += 4;
+ }
+ }
+ }
+ }
+
+ int err;
+ uchar *p = dst->data + y*dst->bytes_per_line;
+ memset(p, 0, bmwidth);
+ b1 = line1;
+ b2 = line2;
+ int bit = 7;
+ for (int x=1; x<=w; x++) {
+ if (*b1 < 128) { // black pixel
+ err = *b1++;
+ *p |= 1 << bit;
+ } else { // white pixel
+ err = *b1++ - 255;
+ }
+ if (bit == 0) {
+ p++;
+ bit = 7;
+ } else {
+ bit--;
+ }
+ if (x < w)
+ *b1 += (err*7)>>4; // spread error to right pixel
+ if (not_last_line) {
+ b2[0] += (err*5)>>4; // pixel below
+ if (x > 1)
+ b2[-1] += (err*3)>>4; // pixel below left
+ if (x < w)
+ b2[1] += err>>4; // pixel below right
+ }
+ b2++;
+ }
+ }
+ } break;
+ case Ordered: {
+
+ memset(dst->data, 0, dst->nbytes);
+ if (d == 32) {
+ for (int i=0; i<h; i++) {
+ const uint *p = (const uint *)src_data;
+ const uint *end = p + w;
+ uchar *m = dst_data;
+ int bit = 7;
+ int j = 0;
+ if (fromalpha) {
+ while (p < end) {
+ if ((*p++ >> 24) >= qt_bayer_matrix[j++&15][i&15])
+ *m |= 1 << bit;
+ if (bit == 0) {
+ m++;
+ bit = 7;
+ } else {
+ bit--;
+ }
+ }
+ } else {
+ while (p < end) {
+ if ((uint)qGray(*p++) < qt_bayer_matrix[j++&15][i&15])
+ *m |= 1 << bit;
+ if (bit == 0) {
+ m++;
+ bit = 7;
+ } else {
+ bit--;
+ }
+ }
+ }
+ dst_data += dst_bpl;
+ src_data += src_bpl;
+ }
+ } else
+ /* (d == 8) */ {
+ for (int i=0; i<h; i++) {
+ const uchar *p = src_data;
+ const uchar *end = p + w;
+ uchar *m = dst_data;
+ int bit = 7;
+ int j = 0;
+ while (p < end) {
+ if ((uint)gray[*p++] < qt_bayer_matrix[j++&15][i&15])
+ *m |= 1 << bit;
+ if (bit == 0) {
+ m++;
+ bit = 7;
+ } else {
+ bit--;
+ }
+ }
+ dst_data += dst_bpl;
+ src_data += src_bpl;
+ }
+ }
+ } break;
+ default: { // Threshold:
+ memset(dst->data, 0, dst->nbytes);
+ if (d == 32) {
+ for (int i=0; i<h; i++) {
+ const uint *p = (const uint *)src_data;
+ const uint *end = p + w;
+ uchar *m = dst_data;
+ int bit = 7;
+ if (fromalpha) {
+ while (p < end) {
+ if ((*p++ >> 24) >= 128)
+ *m |= 1 << bit; // Set mask "on"
+ if (bit == 0) {
+ m++;
+ bit = 7;
+ } else {
+ bit--;
+ }
+ }
+ } else {
+ while (p < end) {
+ if (qGray(*p++) < 128)
+ *m |= 1 << bit; // Set pixel "black"
+ if (bit == 0) {
+ m++;
+ bit = 7;
+ } else {
+ bit--;
+ }
+ }
+ }
+ dst_data += dst_bpl;
+ src_data += src_bpl;
+ }
+ } else
+ if (d == 8) {
+ for (int i=0; i<h; i++) {
+ const uchar *p = src_data;
+ const uchar *end = p + w;
+ uchar *m = dst_data;
+ int bit = 7;
+ while (p < end) {
+ if (gray[*p++] < 128)
+ *m |= 1 << bit; // Set mask "on"/ pixel "black"
+ if (bit == 0) {
+ m++;
+ bit = 7;
+ } else {
+ bit--;
+ }
+ }
+ dst_data += dst_bpl;
+ src_data += src_bpl;
+ }
+ }
+ }
+ }
+
+ if (dst->format == QImage::Format_MonoLSB) {
+ // need to swap bit order
+ uchar *sl = dst->data;
+ int bpl = (dst->width + 7) * dst->depth / 8;
+ int pad = dst->bytes_per_line - bpl;
+ for (int y=0; y<dst->height; ++y) {
+ for (int x=0; x<bpl; ++x) {
+ *sl = bitflip[*sl];
+ ++sl;
+ }
+ sl += pad;
+ }
+ }
+}
+
+static void convert_X_to_Mono(QImageData *dst, const QImageData *src, Qt::ImageConversionFlags flags)
+{
+ dither_to_Mono(dst, src, flags, false);
+}
+
+static void convert_ARGB_PM_to_Mono(QImageData *dst, const QImageData *src, Qt::ImageConversionFlags flags)
+{
+ QScopedPointer<QImageData> tmp(QImageData::create(QSize(src->width, src->height), QImage::Format_ARGB32));
+ convert_ARGB_PM_to_ARGB(tmp.data(), src, flags);
+ dither_to_Mono(dst, tmp.data(), flags, false);
+}
+
+//
+// convert_32_to_8: Converts a 32 bits depth (true color) to an 8 bit
+// image with a colormap. If the 32 bit image has more than 256 colors,
+// we convert the red,green and blue bytes into a single byte encoded
+// as 6 shades of each of red, green and blue.
+//
+// if dithering is needed, only 1 color at most is available for alpha.
+//
+struct QRgbMap {
+ inline QRgbMap() : used(0) { }
+ uchar pix;
+ uchar used;
+ QRgb rgb;
+};
+
+static void convert_RGB_to_Indexed8(QImageData *dst, const QImageData *src, Qt::ImageConversionFlags flags)
+{
+ Q_ASSERT(src->format == QImage::Format_RGB32 || src->format == QImage::Format_ARGB32);
+ Q_ASSERT(dst->format == QImage::Format_Indexed8);
+ Q_ASSERT(src->width == dst->width);
+ Q_ASSERT(src->height == dst->height);
+
+ bool do_quant = (flags & Qt::DitherMode_Mask) == Qt::PreferDither
+ || src->format == QImage::Format_ARGB32;
+ uint alpha_mask = src->format == QImage::Format_RGB32 ? 0xff000000 : 0;
+
+ const int tablesize = 997; // prime
+ QRgbMap table[tablesize];
+ int pix=0;
+
+ if (!dst->colortable.isEmpty()) {
+ QVector<QRgb> ctbl = dst->colortable;
+ dst->colortable.resize(256);
+ // Preload palette into table.
+ // Almost same code as pixel insertion below
+ for (int i = 0; i < dst->colortable.size(); ++i) {
+ // Find in table...
+ QRgb p = ctbl.at(i) | alpha_mask;
+ int hash = p % tablesize;
+ for (;;) {
+ if (table[hash].used) {
+ if (table[hash].rgb == p) {
+ // Found previous insertion - use it
+ break;
+ } else {
+ // Keep searching...
+ if (++hash == tablesize) hash = 0;
+ }
+ } else {
+ // Cannot be in table
+ Q_ASSERT (pix != 256); // too many colors
+ // Insert into table at this unused position
+ dst->colortable[pix] = p;
+ table[hash].pix = pix++;
+ table[hash].rgb = p;
+ table[hash].used = 1;
+ break;
+ }
+ }
+ }
+ }
+
+ if ((flags & Qt::DitherMode_Mask) != Qt::PreferDither) {
+ dst->colortable.resize(256);
+ const uchar *src_data = src->data;
+ uchar *dest_data = dst->data;
+ for (int y = 0; y < src->height; y++) { // check if <= 256 colors
+ const QRgb *s = (const QRgb *)src_data;
+ uchar *b = dest_data;
+ for (int x = 0; x < src->width; ++x) {
+ QRgb p = s[x] | alpha_mask;
+ int hash = p % tablesize;
+ for (;;) {
+ if (table[hash].used) {
+ if (table[hash].rgb == (p)) {
+ // Found previous insertion - use it
+ break;
+ } else {
+ // Keep searching...
+ if (++hash == tablesize) hash = 0;
+ }
+ } else {
+ // Cannot be in table
+ if (pix == 256) { // too many colors
+ do_quant = true;
+ // Break right out
+ x = src->width;
+ y = src->height;
+ } else {
+ // Insert into table at this unused position
+ dst->colortable[pix] = p;
+ table[hash].pix = pix++;
+ table[hash].rgb = p;
+ table[hash].used = 1;
+ }
+ break;
+ }
+ }
+ *b++ = table[hash].pix; // May occur once incorrectly
+ }
+ src_data += src->bytes_per_line;
+ dest_data += dst->bytes_per_line;
+ }
+ }
+ int numColors = do_quant ? 256 : pix;
+
+ dst->colortable.resize(numColors);
+
+ if (do_quant) { // quantization needed
+
+#define MAX_R 5
+#define MAX_G 5
+#define MAX_B 5
+#define INDEXOF(r,g,b) (((r)*(MAX_G+1)+(g))*(MAX_B+1)+(b))
+
+ for (int rc=0; rc<=MAX_R; rc++) // build 6x6x6 color cube
+ for (int gc=0; gc<=MAX_G; gc++)
+ for (int bc=0; bc<=MAX_B; bc++)
+ dst->colortable[INDEXOF(rc,gc,bc)] = 0xff000000 | qRgb(rc*255/MAX_R, gc*255/MAX_G, bc*255/MAX_B);
+
+ const uchar *src_data = src->data;
+ uchar *dest_data = dst->data;
+ if ((flags & Qt::Dither_Mask) == Qt::ThresholdDither) {
+ for (int y = 0; y < src->height; y++) {
+ const QRgb *p = (const QRgb *)src_data;
+ const QRgb *end = p + src->width;
+ uchar *b = dest_data;
+
+ while (p < end) {
+#define DITHER(p,m) ((uchar) ((p * (m) + 127) / 255))
+ *b++ =
+ INDEXOF(
+ DITHER(qRed(*p), MAX_R),
+ DITHER(qGreen(*p), MAX_G),
+ DITHER(qBlue(*p), MAX_B)
+ );
+#undef DITHER
+ p++;
+ }
+ src_data += src->bytes_per_line;
+ dest_data += dst->bytes_per_line;
+ }
+ } else if ((flags & Qt::Dither_Mask) == Qt::DiffuseDither) {
+ int* line1[3];
+ int* line2[3];
+ int* pv[3];
+ QScopedArrayPointer<int> lineBuffer(new int[src->width * 9]);
+ line1[0] = lineBuffer.data();
+ line2[0] = lineBuffer.data() + src->width;
+ line1[1] = lineBuffer.data() + src->width * 2;
+ line2[1] = lineBuffer.data() + src->width * 3;
+ line1[2] = lineBuffer.data() + src->width * 4;
+ line2[2] = lineBuffer.data() + src->width * 5;
+ pv[0] = lineBuffer.data() + src->width * 6;
+ pv[1] = lineBuffer.data() + src->width * 7;
+ pv[2] = lineBuffer.data() + src->width * 8;
+
+ int endian = (QSysInfo::ByteOrder == QSysInfo::BigEndian);
+ for (int y = 0; y < src->height; y++) {
+ const uchar* q = src_data;
+ const uchar* q2 = y < src->height - 1 ? q + src->bytes_per_line : src->data;
+ uchar *b = dest_data;
+ for (int chan = 0; chan < 3; chan++) {
+ int *l1 = (y&1) ? line2[chan] : line1[chan];
+ int *l2 = (y&1) ? line1[chan] : line2[chan];
+ if (y == 0) {
+ for (int i = 0; i < src->width; i++)
+ l1[i] = q[i*4+chan+endian];
+ }
+ if (y+1 < src->height) {
+ for (int i = 0; i < src->width; i++)
+ l2[i] = q2[i*4+chan+endian];
+ }
+ // Bi-directional error diffusion
+ if (y&1) {
+ for (int x = 0; x < src->width; x++) {
+ int pix = qMax(qMin(5, (l1[x] * 5 + 128)/ 255), 0);
+ int err = l1[x] - pix * 255 / 5;
+ pv[chan][x] = pix;
+
+ // Spread the error around...
+ if (x + 1< src->width) {
+ l1[x+1] += (err*7)>>4;
+ l2[x+1] += err>>4;
+ }
+ l2[x]+=(err*5)>>4;
+ if (x>1)
+ l2[x-1]+=(err*3)>>4;
+ }
+ } else {
+ for (int x = src->width; x-- > 0;) {
+ int pix = qMax(qMin(5, (l1[x] * 5 + 128)/ 255), 0);
+ int err = l1[x] - pix * 255 / 5;
+ pv[chan][x] = pix;
+
+ // Spread the error around...
+ if (x > 0) {
+ l1[x-1] += (err*7)>>4;
+ l2[x-1] += err>>4;
+ }
+ l2[x]+=(err*5)>>4;
+ if (x + 1 < src->width)
+ l2[x+1]+=(err*3)>>4;
+ }
+ }
+ }
+ if (endian) {
+ for (int x = 0; x < src->width; x++) {
+ *b++ = INDEXOF(pv[0][x],pv[1][x],pv[2][x]);
+ }
+ } else {
+ for (int x = 0; x < src->width; x++) {
+ *b++ = INDEXOF(pv[2][x],pv[1][x],pv[0][x]);
+ }
+ }
+ src_data += src->bytes_per_line;
+ dest_data += dst->bytes_per_line;
+ }
+ } else { // OrderedDither
+ for (int y = 0; y < src->height; y++) {
+ const QRgb *p = (const QRgb *)src_data;
+ const QRgb *end = p + src->width;
+ uchar *b = dest_data;
+
+ int x = 0;
+ while (p < end) {
+ uint d = qt_bayer_matrix[y & 15][x & 15] << 8;
+
+#define DITHER(p, d, m) ((uchar) ((((256 * (m) + (m) + 1)) * (p) + (d)) >> 16))
+ *b++ =
+ INDEXOF(
+ DITHER(qRed(*p), d, MAX_R),
+ DITHER(qGreen(*p), d, MAX_G),
+ DITHER(qBlue(*p), d, MAX_B)
+ );
+#undef DITHER
+
+ p++;
+ x++;
+ }
+ src_data += src->bytes_per_line;
+ dest_data += dst->bytes_per_line;
+ }
+ }
+
+ if (src->format != QImage::Format_RGB32
+ && src->format != QImage::Format_RGB16) {
+ const int trans = 216;
+ Q_ASSERT(dst->colortable.size() > trans);
+ dst->colortable[trans] = 0;
+ QScopedPointer<QImageData> mask(QImageData::create(QSize(src->width, src->height), QImage::Format_Mono));
+ dither_to_Mono(mask.data(), src, flags, true);
+ uchar *dst_data = dst->data;
+ const uchar *mask_data = mask->data;
+ for (int y = 0; y < src->height; y++) {
+ for (int x = 0; x < src->width ; x++) {
+ if (!(mask_data[x>>3] & (0x80 >> (x & 7))))
+ dst_data[x] = trans;
+ }
+ mask_data += mask->bytes_per_line;
+ dst_data += dst->bytes_per_line;
+ }
+ dst->has_alpha_clut = true;
+ }
+
+#undef MAX_R
+#undef MAX_G
+#undef MAX_B
+#undef INDEXOF
+
+ }
+}
+
+static void convert_ARGB_PM_to_Indexed8(QImageData *dst, const QImageData *src, Qt::ImageConversionFlags flags)
+{
+ QScopedPointer<QImageData> tmp(QImageData::create(QSize(src->width, src->height), QImage::Format_ARGB32));
+ convert_ARGB_PM_to_ARGB(tmp.data(), src, flags);
+ convert_RGB_to_Indexed8(dst, tmp.data(), flags);
+}
+
+static void convert_ARGB_to_Indexed8(QImageData *dst, const QImageData *src, Qt::ImageConversionFlags flags)
+{
+ convert_RGB_to_Indexed8(dst, src, flags);
+}
+
+static void convert_Indexed8_to_X32(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
+{
+ Q_ASSERT(src->format == QImage::Format_Indexed8);
+ Q_ASSERT(dest->format == QImage::Format_RGB32
+ || dest->format == QImage::Format_ARGB32
+ || dest->format == QImage::Format_ARGB32_Premultiplied);
+ Q_ASSERT(src->width == dest->width);
+ Q_ASSERT(src->height == dest->height);
+
+ QVector<QRgb> colorTable = fix_color_table(src->colortable, dest->format);
+ if (colorTable.size() == 0) {
+ colorTable.resize(256);
+ for (int i=0; i<256; ++i)
+ colorTable[i] = qRgb(i, i, i);
+ }
+
+ int w = src->width;
+ const uchar *src_data = src->data;
+ uchar *dest_data = dest->data;
+ int tableSize = colorTable.size() - 1;
+ for (int y = 0; y < src->height; y++) {
+ uint *p = (uint *)dest_data;
+ const uchar *b = src_data;
+ uint *end = p + w;
+
+ while (p < end)
+ *p++ = colorTable.at(qMin<int>(tableSize, *b++));
+
+ src_data += src->bytes_per_line;
+ dest_data += dest->bytes_per_line;
+ }
+}
+
+static void convert_Mono_to_X32(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
+{
+ Q_ASSERT(src->format == QImage::Format_Mono || src->format == QImage::Format_MonoLSB);
+ Q_ASSERT(dest->format == QImage::Format_RGB32
+ || dest->format == QImage::Format_ARGB32
+ || dest->format == QImage::Format_ARGB32_Premultiplied);
+ Q_ASSERT(src->width == dest->width);
+ Q_ASSERT(src->height == dest->height);
+
+ QVector<QRgb> colorTable = fix_color_table(src->colortable, dest->format);
+
+ // Default to black / white colors
+ if (colorTable.size() < 2) {
+ if (colorTable.size() == 0)
+ colorTable << 0xff000000;
+ colorTable << 0xffffffff;
+ }
+
+ const uchar *src_data = src->data;
+ uchar *dest_data = dest->data;
+ if (src->format == QImage::Format_Mono) {
+ for (int y = 0; y < dest->height; y++) {
+ register uint *p = (uint *)dest_data;
+ for (int x = 0; x < dest->width; x++)
+ *p++ = colorTable.at((src_data[x>>3] >> (7 - (x & 7))) & 1);
+
+ src_data += src->bytes_per_line;
+ dest_data += dest->bytes_per_line;
+ }
+ } else {
+ for (int y = 0; y < dest->height; y++) {
+ register uint *p = (uint *)dest_data;
+ for (int x = 0; x < dest->width; x++)
+ *p++ = colorTable.at((src_data[x>>3] >> (x & 7)) & 1);
+
+ src_data += src->bytes_per_line;
+ dest_data += dest->bytes_per_line;
+ }
+ }
+}
+
+
+static void convert_Mono_to_Indexed8(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
+{
+ Q_ASSERT(src->format == QImage::Format_Mono || src->format == QImage::Format_MonoLSB);
+ Q_ASSERT(dest->format == QImage::Format_Indexed8);
+ Q_ASSERT(src->width == dest->width);
+ Q_ASSERT(src->height == dest->height);
+
+ QVector<QRgb> ctbl = src->colortable;
+ if (ctbl.size() > 2) {
+ ctbl.resize(2);
+ } else if (ctbl.size() < 2) {
+ if (ctbl.size() == 0)
+ ctbl << 0xff000000;
+ ctbl << 0xffffffff;
+ }
+ dest->colortable = ctbl;
+ dest->has_alpha_clut = src->has_alpha_clut;
+
+
+ const uchar *src_data = src->data;
+ uchar *dest_data = dest->data;
+ if (src->format == QImage::Format_Mono) {
+ for (int y = 0; y < dest->height; y++) {
+ register uchar *p = dest_data;
+ for (int x = 0; x < dest->width; x++)
+ *p++ = (src_data[x>>3] >> (7 - (x & 7))) & 1;
+ src_data += src->bytes_per_line;
+ dest_data += dest->bytes_per_line;
+ }
+ } else {
+ for (int y = 0; y < dest->height; y++) {
+ register uchar *p = dest_data;
+ for (int x = 0; x < dest->width; x++)
+ *p++ = (src_data[x>>3] >> (x & 7)) & 1;
+ src_data += src->bytes_per_line;
+ dest_data += dest->bytes_per_line;
+ }
+ }
+}
+
+#define CONVERT_DECL(DST, SRC) \
+ static void convert_##SRC##_to_##DST(QImageData *dest, \
+ const QImageData *src, \
+ Qt::ImageConversionFlags) \
+ { \
+ qt_rectconvert<DST, SRC>(reinterpret_cast<DST*>(dest->data), \
+ reinterpret_cast<const SRC*>(src->data), \
+ 0, 0, src->width, src->height, \
+ dest->bytes_per_line, src->bytes_per_line); \
+ }
+
+CONVERT_DECL(quint32, quint16)
+CONVERT_DECL(quint16, quint32)
+CONVERT_DECL(quint32, qargb8565)
+CONVERT_DECL(qargb8565, quint32)
+CONVERT_DECL(quint32, qrgb555)
+CONVERT_DECL(qrgb666, quint32)
+CONVERT_DECL(quint32, qrgb666)
+CONVERT_DECL(qargb6666, quint32)
+CONVERT_DECL(quint32, qargb6666)
+CONVERT_DECL(qrgb555, quint32)
+#if !defined(Q_WS_QWS) || (defined(QT_QWS_DEPTH_15) && defined(QT_QWS_DEPTH_16))
+CONVERT_DECL(quint16, qrgb555)
+CONVERT_DECL(qrgb555, quint16)
+#endif
+CONVERT_DECL(quint32, qrgb888)
+CONVERT_DECL(qrgb888, quint32)
+CONVERT_DECL(quint32, qargb8555)
+CONVERT_DECL(qargb8555, quint32)
+CONVERT_DECL(quint32, qrgb444)
+CONVERT_DECL(qrgb444, quint32)
+CONVERT_DECL(quint32, qargb4444)
+CONVERT_DECL(qargb4444, quint32)
+#undef CONVERT_DECL
+#define CONVERT_PTR(DST, SRC) convert_##SRC##_to_##DST
+
+/*
+ Format_Invalid,
+ Format_Mono,
+ Format_MonoLSB,
+ Format_Indexed8,
+ Format_RGB32,
+ Format_ARGB32,
+ Format_ARGB32_Premultiplied,
+ Format_RGB16,
+ Format_ARGB8565_Premultiplied,
+ Format_RGB666,
+ Format_ARGB6666_Premultiplied,
+ Format_RGB555,
+ Format_ARGB8555_Premultiplied,
+ Format_RGB888
+ Format_RGB444
+ Format_ARGB4444_Premultiplied
+*/
+
+
+// first index source, second dest
+static Image_Converter converter_map[QImage::NImageFormats][QImage::NImageFormats] =
+{
+ {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ },
+ {
+ 0,
+ 0,
+ swap_bit_order,
+ convert_Mono_to_Indexed8,
+ convert_Mono_to_X32,
+ convert_Mono_to_X32,
+ convert_Mono_to_X32,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ }, // Format_Mono
+
+ {
+ 0,
+ swap_bit_order,
+ 0,
+ convert_Mono_to_Indexed8,
+ convert_Mono_to_X32,
+ convert_Mono_to_X32,
+ convert_Mono_to_X32,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ }, // Format_MonoLSB
+
+ {
+ 0,
+ convert_X_to_Mono,
+ convert_X_to_Mono,
+ 0,
+ convert_Indexed8_to_X32,
+ convert_Indexed8_to_X32,
+ convert_Indexed8_to_X32,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ }, // Format_Indexed8
+
+ {
+ 0,
+ convert_X_to_Mono,
+ convert_X_to_Mono,
+ convert_RGB_to_Indexed8,
+ 0,
+ mask_alpha_converter,
+ mask_alpha_converter,
+ CONVERT_PTR(quint16, quint32),
+ CONVERT_PTR(qargb8565, quint32),
+ CONVERT_PTR(qrgb666, quint32),
+ CONVERT_PTR(qargb6666, quint32),
+ CONVERT_PTR(qrgb555, quint32),
+ CONVERT_PTR(qargb8555, quint32),
+ CONVERT_PTR(qrgb888, quint32),
+ CONVERT_PTR(qrgb444, quint32),
+ CONVERT_PTR(qargb4444, quint32)
+ }, // Format_RGB32
+
+ {
+ 0,
+ convert_X_to_Mono,
+ convert_X_to_Mono,
+ convert_ARGB_to_Indexed8,
+ mask_alpha_converter,
+ 0,
+ convert_ARGB_to_ARGB_PM,
+ CONVERT_PTR(quint16, quint32),
+ CONVERT_PTR(qargb8565, quint32),
+ CONVERT_PTR(qrgb666, quint32),
+ CONVERT_PTR(qargb6666, quint32),
+ CONVERT_PTR(qrgb555, quint32),
+ CONVERT_PTR(qargb8555, quint32),
+ CONVERT_PTR(qrgb888, quint32),
+ CONVERT_PTR(qrgb444, quint32),
+ CONVERT_PTR(qargb4444, quint32)
+ }, // Format_ARGB32
+
+ {
+ 0,
+ convert_ARGB_PM_to_Mono,
+ convert_ARGB_PM_to_Mono,
+ convert_ARGB_PM_to_Indexed8,
+ convert_ARGB_PM_to_RGB,
+ convert_ARGB_PM_to_ARGB,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ }, // Format_ARGB32_Premultiplied
+
+ {
+ 0,
+ 0,
+ 0,
+ 0,
+ CONVERT_PTR(quint32, quint16),
+ CONVERT_PTR(quint32, quint16),
+ CONVERT_PTR(quint32, quint16),
+ 0,
+ 0,
+ 0,
+ 0,
+#if !defined(Q_WS_QWS) || (defined(QT_QWS_DEPTH_15) && defined(QT_QWS_DEPTH_16))
+ CONVERT_PTR(qrgb555, quint16),
+#else
+ 0,
+#endif
+ 0,
+ 0,
+ 0,
+ 0
+ }, // Format_RGB16
+
+ {
+ 0,
+ 0,
+ 0,
+ 0,
+ CONVERT_PTR(quint32, qargb8565),
+ CONVERT_PTR(quint32, qargb8565),
+ CONVERT_PTR(quint32, qargb8565),
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ }, // Format_ARGB8565_Premultiplied
+
+ {
+ 0,
+ 0,
+ 0,
+ 0,
+ CONVERT_PTR(quint32, qrgb666),
+ CONVERT_PTR(quint32, qrgb666),
+ CONVERT_PTR(quint32, qrgb666),
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ }, // Format_RGB666
+
+ {
+ 0,
+ 0,
+ 0,
+ 0,
+ CONVERT_PTR(quint32, qargb6666),
+ CONVERT_PTR(quint32, qargb6666),
+ CONVERT_PTR(quint32, qargb6666),
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ }, // Format_ARGB6666_Premultiplied
+
+ {
+ 0,
+ 0,
+ 0,
+ 0,
+ CONVERT_PTR(quint32, qrgb555),
+ CONVERT_PTR(quint32, qrgb555),
+ CONVERT_PTR(quint32, qrgb555),
+#if !defined(Q_WS_QWS) || (defined(QT_QWS_DEPTH_15) && defined(QT_QWS_DEPTH_16))
+ CONVERT_PTR(quint16, qrgb555),
+#else
+ 0,
+#endif
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ }, // Format_RGB555
+
+ {
+ 0,
+ 0,
+ 0,
+ 0,
+ CONVERT_PTR(quint32, qargb8555),
+ CONVERT_PTR(quint32, qargb8555),
+ CONVERT_PTR(quint32, qargb8555),
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ }, // Format_ARGB8555_Premultiplied
+
+ {
+ 0,
+ 0,
+ 0,
+ 0,
+ CONVERT_PTR(quint32, qrgb888),
+ CONVERT_PTR(quint32, qrgb888),
+ CONVERT_PTR(quint32, qrgb888),
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ }, // Format_RGB888
+
+ {
+ 0,
+ 0,
+ 0,
+ 0,
+ CONVERT_PTR(quint32, qrgb444),
+ CONVERT_PTR(quint32, qrgb444),
+ CONVERT_PTR(quint32, qrgb444),
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ }, // Format_RGB444
+
+ {
+ 0,
+ 0,
+ 0,
+ 0,
+ CONVERT_PTR(quint32, qargb4444),
+ CONVERT_PTR(quint32, qargb4444),
+ CONVERT_PTR(quint32, qargb4444),
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ } // Format_ARGB4444_Premultiplied
+};
+
+static InPlace_Image_Converter inplace_converter_map[QImage::NImageFormats][QImage::NImageFormats] =
+{
+ {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ },
+ {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ }, // Format_Mono
+ {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ }, // Format_MonoLSB
+ {
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ convert_indexed8_to_RGB_inplace,
+ convert_indexed8_to_ARGB_PM_inplace,
+ convert_indexed8_to_RGB16_inplace,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ }, // Format_Indexed8
+ {
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ convert_RGB_to_RGB16_inplace,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ }, // Format_ARGB32
+ {
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ convert_ARGB_to_ARGB_PM_inplace,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ }, // Format_ARGB32
+ {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ }, // Format_ARGB32_Premultiplied
+ {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ }, // Format_RGB16
+ {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ }, // Format_ARGB8565_Premultiplied
+ {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ }, // Format_RGB666
+ {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ }, // Format_ARGB6666_Premultiplied
+ {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ }, // Format_RGB555
+ {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ }, // Format_ARGB8555_Premultiplied
+ {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ }, // Format_RGB888
+ {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ }, // Format_RGB444
+ {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ } // Format_ARGB4444_Premultiplied
+};
+
+void qInitImageConversions()
+{
+ const uint features = qDetectCPUFeatures();
+ Q_UNUSED(features);
+
+#ifdef QT_HAVE_SSE2
+ if (features & SSE2) {
+ extern bool convert_ARGB_to_ARGB_PM_inplace_sse2(QImageData *data, Qt::ImageConversionFlags);
+ inplace_converter_map[QImage::Format_ARGB32][QImage::Format_ARGB32_Premultiplied] = convert_ARGB_to_ARGB_PM_inplace_sse2;
+ }
+#endif
+#ifdef QT_HAVE_SSSE3
+ if (features & SSSE3) {
+ extern void convert_RGB888_to_RGB32_ssse3(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags);
+ converter_map[QImage::Format_RGB888][QImage::Format_RGB32] = convert_RGB888_to_RGB32_ssse3;
+ converter_map[QImage::Format_RGB888][QImage::Format_ARGB32] = convert_RGB888_to_RGB32_ssse3;
+ converter_map[QImage::Format_RGB888][QImage::Format_ARGB32_Premultiplied] = convert_RGB888_to_RGB32_ssse3;
+ }
+#endif
+#ifdef QT_HAVE_NEON
+ if (features & NEON) {
+ extern void convert_RGB888_to_RGB32_neon(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags);
+ converter_map[QImage::Format_RGB888][QImage::Format_RGB32] = convert_RGB888_to_RGB32_neon;
+ converter_map[QImage::Format_RGB888][QImage::Format_ARGB32] = convert_RGB888_to_RGB32_neon;
+ converter_map[QImage::Format_RGB888][QImage::Format_ARGB32_Premultiplied] = convert_RGB888_to_RGB32_neon;
+ }
+#endif
+}
+
+void qGamma_correct_back_to_linear_cs(QImage *image)
+{
+ extern uchar qt_pow_rgb_gamma[256];
+
+ // gamma correct the pixels back to linear color space...
+ int h = image->height();
+ int w = image->width();
+
+ for (int y=0; y<h; ++y) {
+ uint *pixels = (uint *) image->scanLine(y);
+ for (int x=0; x<w; ++x) {
+ uint p = pixels[x];
+ uint r = qt_pow_rgb_gamma[qRed(p)];
+ uint g = qt_pow_rgb_gamma[qGreen(p)];
+ uint b = qt_pow_rgb_gamma[qBlue(p)];
+ pixels[x] = (r << 16) | (g << 8) | b | 0xff000000;
+ }
+ }
+}
+
+/*!
+ Returns a copy of the image in the given \a format.
+
+ The specified image conversion \a flags control how the image data
+ is handled during the conversion process.
+
+ \sa {QImage#Image Format}{Image Format}
+*/
+QImage QImage::convertToFormat(Format format, Qt::ImageConversionFlags flags) const
+{
+ if (!d || d->format == format)
+ return *this;
+
+ if (format == Format_Invalid || d->format == Format_Invalid)
+ return QImage();
+
+ const Image_Converter *converterPtr = &converter_map[d->format][format];
+ Image_Converter converter = *converterPtr;
+ if (converter) {
+ QImage image(d->width, d->height, format);
+
+ QIMAGE_SANITYCHECK_MEMORY(image);
+
+ image.setDotsPerMeterY(dotsPerMeterY());
+ image.setDotsPerMeterX(dotsPerMeterX());
+
+#if !defined(QT_NO_IMAGE_TEXT)
+ image.d->text = d->text;
+#endif // !QT_NO_IMAGE_TEXT
+
+ converter(image.d, d, flags);
+ return image;
+ }
+
+ Q_ASSERT(format != QImage::Format_ARGB32);
+ Q_ASSERT(d->format != QImage::Format_ARGB32);
+
+ QImage image = convertToFormat(Format_ARGB32, flags);
+ return image.convertToFormat(format, flags);
+}
+
+
+
+static inline int pixel_distance(QRgb p1, QRgb p2) {
+ int r1 = qRed(p1);
+ int g1 = qGreen(p1);
+ int b1 = qBlue(p1);
+ int a1 = qAlpha(p1);
+
+ int r2 = qRed(p2);
+ int g2 = qGreen(p2);
+ int b2 = qBlue(p2);
+ int a2 = qAlpha(p2);
+
+ return abs(r1 - r2) + abs(g1 - g2) + abs(b1 - b2) + abs(a1 - a2);
+}
+
+static inline int closestMatch(QRgb pixel, const QVector<QRgb> &clut) {
+ int idx = 0;
+ int current_distance = INT_MAX;
+ for (int i=0; i<clut.size(); ++i) {
+ int dist = pixel_distance(pixel, clut.at(i));
+ if (dist < current_distance) {
+ current_distance = dist;
+ idx = i;
+ }
+ }
+ return idx;
+}
+
+static QImage convertWithPalette(const QImage &src, QImage::Format format,
+ const QVector<QRgb> &clut) {
+ QImage dest(src.size(), format);
+ dest.setColorTable(clut);
+
+#if !defined(QT_NO_IMAGE_TEXT)
+ QString textsKeys = src.text();
+ QStringList textKeyList = textsKeys.split(QLatin1Char('\n'), QString::SkipEmptyParts);
+ foreach (const QString &textKey, textKeyList) {
+ QStringList textKeySplitted = textKey.split(QLatin1String(": "));
+ dest.setText(textKeySplitted[0], textKeySplitted[1]);
+ }
+#endif // !QT_NO_IMAGE_TEXT
+
+ int h = src.height();
+ int w = src.width();
+
+ QHash<QRgb, int> cache;
+
+ if (format == QImage::Format_Indexed8) {
+ for (int y=0; y<h; ++y) {
+ QRgb *src_pixels = (QRgb *) src.scanLine(y);
+ uchar *dest_pixels = (uchar *) dest.scanLine(y);
+ for (int x=0; x<w; ++x) {
+ int src_pixel = src_pixels[x];
+ int value = cache.value(src_pixel, -1);
+ if (value == -1) {
+ value = closestMatch(src_pixel, clut);
+ cache.insert(src_pixel, value);
+ }
+ dest_pixels[x] = (uchar) value;
+ }
+ }
+ } else {
+ QVector<QRgb> table = clut;
+ table.resize(2);
+ for (int y=0; y<h; ++y) {
+ QRgb *src_pixels = (QRgb *) src.scanLine(y);
+ for (int x=0; x<w; ++x) {
+ int src_pixel = src_pixels[x];
+ int value = cache.value(src_pixel, -1);
+ if (value == -1) {
+ value = closestMatch(src_pixel, table);
+ cache.insert(src_pixel, value);
+ }
+ dest.setPixel(x, y, value);
+ }
+ }
+ }
+
+ return dest;
+}
+
+/*!
+ \overload
+
+ Returns a copy of the image converted to the given \a format,
+ using the specified \a colorTable.
+
+ Conversion from 32 bit to 8 bit indexed is a slow operation and
+ will use a straightforward nearest color approach, with no
+ dithering.
+*/
+QImage QImage::convertToFormat(Format format, const QVector<QRgb> &colorTable, Qt::ImageConversionFlags flags) const
+{
+ if (d->format == format)
+ return *this;
+
+ if (format <= QImage::Format_Indexed8 && depth() == 32) {
+ return convertWithPalette(*this, format, colorTable);
+ }
+
+ const Image_Converter *converterPtr = &converter_map[d->format][format];
+ Image_Converter converter = *converterPtr;
+ if (!converter)
+ return QImage();
+
+ QImage image(d->width, d->height, format);
+ QIMAGE_SANITYCHECK_MEMORY(image);
+
+#if !defined(QT_NO_IMAGE_TEXT)
+ image.d->text = d->text;
+#endif // !QT_NO_IMAGE_TEXT
+
+ converter(image.d, d, flags);
+ return image;
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ Converts the depth (bpp) of the image to the given \a depth and
+ returns the converted image. The original image is not changed.
+ Returns this image if \a depth is equal to the image depth, or a
+ null image if this image cannot be converted. The \a depth
+ argument must be 1, 8 or 32. If the image needs to be modified to
+ fit in a lower-resolution result (e.g. converting from 32-bit to
+ 8-bit), use the \a flags to specify how you'd prefer this to
+ happen.
+
+ Use the convertToFormat() function instead.
+*/
+
+QImage QImage::convertDepth(int depth, Qt::ImageConversionFlags flags) const
+{
+ if (!d || d->depth == depth)
+ return *this;
+
+ Format format = formatFor (depth, QImage::LittleEndian);
+ return convertToFormat(format, flags);
+}
+#endif
+
+/*!
+ \fn bool QImage::valid(const QPoint &pos) const
+
+ Returns true if \a pos is a valid coordinate pair within the
+ image; otherwise returns false.
+
+ \sa rect(), QRect::contains()
+*/
+
+/*!
+ \overload
+
+ Returns true if QPoint(\a x, \a y) is a valid coordinate pair
+ within the image; otherwise returns false.
+*/
+bool QImage::valid(int x, int y) const
+{
+ return d
+ && x >= 0 && x < d->width
+ && y >= 0 && y < d->height;
+}
+
+/*!
+ \fn int QImage::pixelIndex(const QPoint &position) const
+
+ Returns the pixel index at the given \a position.
+
+ If \a position is not valid, or if the image is not a paletted
+ image (depth() > 8), the results are undefined.
+
+ \sa valid(), depth(), {QImage#Pixel Manipulation}{Pixel Manipulation}
+*/
+
+/*!
+ \overload
+
+ Returns the pixel index at (\a x, \a y).
+*/
+int QImage::pixelIndex(int x, int y) const
+{
+ if (!d || x < 0 || x >= d->width || y < 0 || y >= height()) {
+ qWarning("QImage::pixelIndex: coordinate (%d,%d) out of range", x, y);
+ return -12345;
+ }
+ const uchar * s = scanLine(y);
+ switch(d->format) {
+ case Format_Mono:
+ return (*(s + (x >> 3)) >> (7- (x & 7))) & 1;
+ case Format_MonoLSB:
+ return (*(s + (x >> 3)) >> (x & 7)) & 1;
+ case Format_Indexed8:
+ return (int)s[x];
+ default:
+ qWarning("QImage::pixelIndex: Not applicable for %d-bpp images (no palette)", d->depth);
+ }
+ return 0;
+}
+
+
+/*!
+ \fn QRgb QImage::pixel(const QPoint &position) const
+
+ Returns the color of the pixel at the given \a position.
+
+ If the \a position is not valid, the results are undefined.
+
+ \warning This function is expensive when used for massive pixel
+ manipulations.
+
+ \sa setPixel(), valid(), {QImage#Pixel Manipulation}{Pixel
+ Manipulation}
+*/
+
+/*!
+ \overload
+
+ Returns the color of the pixel at coordinates (\a x, \a y).
+*/
+QRgb QImage::pixel(int x, int y) const
+{
+ if (!d || x < 0 || x >= d->width || y < 0 || y >= height()) {
+ qWarning("QImage::pixel: coordinate (%d,%d) out of range", x, y);
+ return 12345;
+ }
+ const uchar * s = scanLine(y);
+ switch(d->format) {
+ case Format_Mono:
+ return d->colortable.at((*(s + (x >> 3)) >> (7- (x & 7))) & 1);
+ case Format_MonoLSB:
+ return d->colortable.at((*(s + (x >> 3)) >> (x & 7)) & 1);
+ case Format_Indexed8:
+ return d->colortable.at((int)s[x]);
+ case Format_ARGB8565_Premultiplied:
+ return qt_colorConvert<quint32, qargb8565>(reinterpret_cast<const qargb8565*>(s)[x], 0);
+ case Format_RGB666:
+ return qt_colorConvert<quint32, qrgb666>(reinterpret_cast<const qrgb666*>(s)[x], 0);
+ case Format_ARGB6666_Premultiplied:
+ return qt_colorConvert<quint32, qargb6666>(reinterpret_cast<const qargb6666*>(s)[x], 0);
+ case Format_RGB555:
+ return qt_colorConvert<quint32, qrgb555>(reinterpret_cast<const qrgb555*>(s)[x], 0);
+ case Format_ARGB8555_Premultiplied:
+ return qt_colorConvert<quint32, qargb8555>(reinterpret_cast<const qargb8555*>(s)[x], 0);
+ case Format_RGB888:
+ return qt_colorConvert<quint32, qrgb888>(reinterpret_cast<const qrgb888*>(s)[x], 0);
+ case Format_RGB444:
+ return qt_colorConvert<quint32, qrgb444>(reinterpret_cast<const qrgb444*>(s)[x], 0);
+ case Format_ARGB4444_Premultiplied:
+ return qt_colorConvert<quint32, qargb4444>(reinterpret_cast<const qargb4444*>(s)[x], 0);
+ case Format_RGB16:
+ return qt_colorConvert<quint32, quint16>(reinterpret_cast<const quint16*>(s)[x], 0);
+ default:
+ return ((QRgb*)s)[x];
+ }
+}
+
+
+/*!
+ \fn void QImage::setPixel(const QPoint &position, uint index_or_rgb)
+
+ Sets the pixel index or color at the given \a position to \a
+ index_or_rgb.
+
+ If the image's format is either monochrome or 8-bit, the given \a
+ index_or_rgb value must be an index in the image's color table,
+ otherwise the parameter must be a QRgb value.
+
+ If \a position is not a valid coordinate pair in the image, or if
+ \a index_or_rgb >= colorCount() in the case of monochrome and
+ 8-bit images, the result is undefined.
+
+ \warning This function is expensive due to the call of the internal
+ \c{detach()} function called within; if performance is a concern, we
+ recommend the use of \l{QImage::}{scanLine()} to access pixel data
+ directly.
+
+ \sa pixel(), {QImage#Pixel Manipulation}{Pixel Manipulation}
+*/
+
+/*!
+ \overload
+
+ Sets the pixel index or color at (\a x, \a y) to \a index_or_rgb.
+*/
+void QImage::setPixel(int x, int y, uint index_or_rgb)
+{
+ if (!d || x < 0 || x >= width() || y < 0 || y >= height()) {
+ qWarning("QImage::setPixel: coordinate (%d,%d) out of range", x, y);
+ return;
+ }
+ // detach is called from within scanLine
+ uchar * s = scanLine(y);
+ const quint32p p = quint32p::fromRawData(index_or_rgb);
+ switch(d->format) {
+ case Format_Mono:
+ case Format_MonoLSB:
+ if (index_or_rgb > 1) {
+ qWarning("QImage::setPixel: Index %d out of range", index_or_rgb);
+ } else if (format() == Format_MonoLSB) {
+ if (index_or_rgb==0)
+ *(s + (x >> 3)) &= ~(1 << (x & 7));
+ else
+ *(s + (x >> 3)) |= (1 << (x & 7));
+ } else {
+ if (index_or_rgb==0)
+ *(s + (x >> 3)) &= ~(1 << (7-(x & 7)));
+ else
+ *(s + (x >> 3)) |= (1 << (7-(x & 7)));
+ }
+ break;
+ case Format_Indexed8:
+ if (index_or_rgb >= (uint)d->colortable.size()) {
+ qWarning("QImage::setPixel: Index %d out of range", index_or_rgb);
+ return;
+ }
+ s[x] = index_or_rgb;
+ break;
+ case Format_RGB32:
+ //make sure alpha is 255, we depend on it in qdrawhelper for cases
+ // when image is set as a texture pattern on a qbrush
+ ((uint *)s)[x] = uint(255 << 24) | index_or_rgb;
+ break;
+ case Format_ARGB32:
+ case Format_ARGB32_Premultiplied:
+ ((uint *)s)[x] = index_or_rgb;
+ break;
+ case Format_RGB16:
+ ((quint16 *)s)[x] = qt_colorConvert<quint16, quint32p>(p, 0);
+ break;
+ case Format_ARGB8565_Premultiplied:
+ ((qargb8565*)s)[x] = qt_colorConvert<qargb8565, quint32p>(p, 0);
+ break;
+ case Format_RGB666:
+ ((qrgb666*)s)[x] = qt_colorConvert<qrgb666, quint32p>(p, 0);
+ break;
+ case Format_ARGB6666_Premultiplied:
+ ((qargb6666*)s)[x] = qt_colorConvert<qargb6666, quint32p>(p, 0);
+ break;
+ case Format_RGB555:
+ ((qrgb555*)s)[x] = qt_colorConvert<qrgb555, quint32p>(p, 0);
+ break;
+ case Format_ARGB8555_Premultiplied:
+ ((qargb8555*)s)[x] = qt_colorConvert<qargb8555, quint32p>(p, 0);
+ break;
+ case Format_RGB888:
+ ((qrgb888*)s)[x] = qt_colorConvert<qrgb888, quint32p>(p, 0);
+ break;
+ case Format_RGB444:
+ ((qrgb444*)s)[x] = qt_colorConvert<qrgb444, quint32p>(p, 0);
+ break;
+ case Format_ARGB4444_Premultiplied:
+ ((qargb4444*)s)[x] = qt_colorConvert<qargb4444, quint32p>(p, 0);
+ break;
+ case Format_Invalid:
+ case NImageFormats:
+ Q_ASSERT(false);
+ }
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ Converts the bit order of the image to the given \a bitOrder and
+ returns the converted image. The original image is not changed.
+ Returns this image if the given \a bitOrder is equal to the image
+ current bit order, or a null image if this image cannot be
+ converted.
+
+ Use convertToFormat() instead.
+*/
+
+QImage QImage::convertBitOrder(Endian bitOrder) const
+{
+ if (!d || isNull() || d->depth != 1 || !(bitOrder == BigEndian || bitOrder == LittleEndian))
+ return QImage();
+
+ if ((d->format == Format_Mono && bitOrder == BigEndian)
+ || (d->format == Format_MonoLSB && bitOrder == LittleEndian))
+ return *this;
+
+ QImage image(d->width, d->height, d->format == Format_Mono ? Format_MonoLSB : Format_Mono);
+
+ const uchar *data = d->data;
+ const uchar *end = data + d->nbytes;
+ uchar *ndata = image.d->data;
+ while (data < end)
+ *ndata++ = bitflip[*data++];
+
+ image.setDotsPerMeterX(dotsPerMeterX());
+ image.setDotsPerMeterY(dotsPerMeterY());
+
+ image.d->colortable = d->colortable;
+ return image;
+}
+#endif
+/*!
+ Returns true if all the colors in the image are shades of gray
+ (i.e. their red, green and blue components are equal); otherwise
+ false.
+
+ Note that this function is slow for images without color table.
+
+ \sa isGrayscale()
+*/
+bool QImage::allGray() const
+{
+ if (!d)
+ return true;
+
+ if (d->depth == 32) {
+ int p = width()*height();
+ const QRgb* b = (const QRgb*)bits();
+ while (p--)
+ if (!qIsGray(*b++))
+ return false;
+ } else if (d->depth == 16) {
+ int p = width()*height();
+ const ushort* b = (const ushort *)bits();
+ while (p--)
+ if (!qIsGray(qt_colorConvert<quint32, quint16>(*b++, 0)))
+ return false;
+ } else if (d->format == QImage::Format_RGB888) {
+ int p = width()*height();
+ const qrgb888* b = (const qrgb888 *)bits();
+ while (p--)
+ if (!qIsGray(qt_colorConvert<quint32, qrgb888>(*b++, 0)))
+ return false;
+ } else {
+ if (d->colortable.isEmpty())
+ return true;
+ for (int i = 0; i < colorCount(); i++)
+ if (!qIsGray(d->colortable.at(i)))
+ return false;
+ }
+ return true;
+}
+
+/*!
+ For 32-bit images, this function is equivalent to allGray().
+
+ For 8-bpp images, this function returns true if color(i) is
+ QRgb(i, i, i) for all indexes of the color table; otherwise
+ returns false.
+
+ \sa allGray(), {QImage#Image Formats}{Image Formats}
+*/
+bool QImage::isGrayscale() const
+{
+ if (!d)
+ return false;
+
+ switch (depth()) {
+ case 32:
+ case 24:
+ case 16:
+ return allGray();
+ case 8: {
+ for (int i = 0; i < colorCount(); i++)
+ if (d->colortable.at(i) != qRgb(i,i,i))
+ return false;
+ return true;
+ }
+ }
+ return false;
+}
+
+
+/*!
+ \fn QImage QImage::smoothScale(int width, int height, Qt::AspectRatioMode mode) const
+
+ Use scaled() instead.
+
+ \oldcode
+ QImage image;
+ image.smoothScale(width, height, mode);
+ \newcode
+ QImage image;
+ image.scaled(width, height, mode, Qt::SmoothTransformation);
+ \endcode
+*/
+
+/*!
+ \fn QImage QImage::smoothScale(const QSize &size, Qt::AspectRatioMode mode) const
+ \overload
+
+ Use scaled() instead.
+
+ \oldcode
+ QImage image;
+ image.smoothScale(size, mode);
+ \newcode
+ QImage image;
+ image.scaled(size, mode, Qt::SmoothTransformation);
+ \endcode
+*/
+
+/*!
+ \fn QImage QImage::scaled(int width, int height, Qt::AspectRatioMode aspectRatioMode,
+ Qt::TransformationMode transformMode) const
+ \overload
+
+ Returns a copy of the image scaled to a rectangle with the given
+ \a width and \a height according to the given \a aspectRatioMode
+ and \a transformMode.
+
+ If either the \a width or the \a height is zero or negative, this
+ function returns a null image.
+*/
+
+/*!
+ \fn QImage QImage::scaled(const QSize &size, Qt::AspectRatioMode aspectRatioMode,
+ Qt::TransformationMode transformMode) const
+
+ Returns a copy of the image scaled to a rectangle defined by the
+ given \a size according to the given \a aspectRatioMode and \a
+ transformMode.
+
+ \image qimage-scaling.png
+
+ \list
+ \i If \a aspectRatioMode is Qt::IgnoreAspectRatio, the image
+ is scaled to \a size.
+ \i If \a aspectRatioMode is Qt::KeepAspectRatio, the image is
+ scaled to a rectangle as large as possible inside \a size, preserving the aspect ratio.
+ \i If \a aspectRatioMode is Qt::KeepAspectRatioByExpanding,
+ the image is scaled to a rectangle as small as possible
+ outside \a size, preserving the aspect ratio.
+ \endlist
+
+ If the given \a size is empty, this function returns a null image.
+
+ \sa isNull(), {QImage#Image Transformations}{Image
+ Transformations}
+*/
+QImage QImage::scaled(const QSize& s, Qt::AspectRatioMode aspectMode, Qt::TransformationMode mode) const
+{
+ if (!d) {
+ qWarning("QImage::scaled: Image is a null image");
+ return QImage();
+ }
+ if (s.isEmpty())
+ return QImage();
+
+ QSize newSize = size();
+ newSize.scale(s, aspectMode);
+ if (newSize == size())
+ return *this;
+
+ QTransform wm = QTransform::fromScale((qreal)newSize.width() / width(), (qreal)newSize.height() / height());
+ QImage img = transformed(wm, mode);
+ return img;
+}
+
+/*!
+ \fn QImage QImage::scaledToWidth(int width, Qt::TransformationMode mode) const
+
+ Returns a scaled copy of the image. The returned image is scaled
+ to the given \a width using the specified transformation \a
+ mode.
+
+ This function automatically calculates the height of the image so
+ that its aspect ratio is preserved.
+
+ If the given \a width is 0 or negative, a null image is returned.
+
+ \sa {QImage#Image Transformations}{Image Transformations}
+*/
+QImage QImage::scaledToWidth(int w, Qt::TransformationMode mode) const
+{
+ if (!d) {
+ qWarning("QImage::scaleWidth: Image is a null image");
+ return QImage();
+ }
+ if (w <= 0)
+ return QImage();
+
+ qreal factor = (qreal) w / width();
+ QTransform wm = QTransform::fromScale(factor, factor);
+ return transformed(wm, mode);
+}
+
+/*!
+ \fn QImage QImage::scaledToHeight(int height, Qt::TransformationMode mode) const
+
+ Returns a scaled copy of the image. The returned image is scaled
+ to the given \a height using the specified transformation \a
+ mode.
+
+ This function automatically calculates the width of the image so that
+ the ratio of the image is preserved.
+
+ If the given \a height is 0 or negative, a null image is returned.
+
+ \sa {QImage#Image Transformations}{Image Transformations}
+*/
+QImage QImage::scaledToHeight(int h, Qt::TransformationMode mode) const
+{
+ if (!d) {
+ qWarning("QImage::scaleHeight: Image is a null image");
+ return QImage();
+ }
+ if (h <= 0)
+ return QImage();
+
+ qreal factor = (qreal) h / height();
+ QTransform wm = QTransform::fromScale(factor, factor);
+ return transformed(wm, mode);
+}
+
+
+/*!
+ \fn QMatrix QImage::trueMatrix(const QMatrix &matrix, int width, int height)
+
+ Returns the actual matrix used for transforming an image with the
+ given \a width, \a height and \a matrix.
+
+ When transforming an image using the transformed() function, the
+ transformation matrix is internally adjusted to compensate for
+ unwanted translation, i.e. transformed() returns the smallest
+ image containing all transformed points of the original image.
+ This function returns the modified matrix, which maps points
+ correctly from the original image into the new image.
+
+ \sa transformed(), {QImage#Image Transformations}{Image
+ Transformations}
+*/
+QMatrix QImage::trueMatrix(const QMatrix &matrix, int w, int h)
+{
+ return trueMatrix(QTransform(matrix), w, h).toAffine();
+}
+
+/*!
+ Returns a copy of the image that is transformed using the given
+ transformation \a matrix and transformation \a mode.
+
+ The transformation \a matrix is internally adjusted to compensate
+ for unwanted translation; i.e. the image produced is the smallest
+ image that contains all the transformed points of the original
+ image. Use the trueMatrix() function to retrieve the actual matrix
+ used for transforming an image.
+
+ \sa trueMatrix(), {QImage#Image Transformations}{Image
+ Transformations}
+*/
+QImage QImage::transformed(const QMatrix &matrix, Qt::TransformationMode mode) const
+{
+ return transformed(QTransform(matrix), mode);
+}
+
+/*!
+ Builds and returns a 1-bpp mask from the alpha buffer in this
+ image. Returns a null image if the image's format is
+ QImage::Format_RGB32.
+
+ The \a flags argument is a bitwise-OR of the
+ Qt::ImageConversionFlags, and controls the conversion
+ process. Passing 0 for flags sets all the default options.
+
+ The returned image has little-endian bit order (i.e. the image's
+ format is QImage::Format_MonoLSB), which you can convert to
+ big-endian (QImage::Format_Mono) using the convertToFormat()
+ function.
+
+ \sa createHeuristicMask(), {QImage#Image Transformations}{Image
+ Transformations}
+*/
+QImage QImage::createAlphaMask(Qt::ImageConversionFlags flags) const
+{
+ if (!d || d->format == QImage::Format_RGB32)
+ return QImage();
+
+ if (d->depth == 1) {
+ // A monochrome pixmap, with alpha channels on those two colors.
+ // Pretty unlikely, so use less efficient solution.
+ return convertToFormat(Format_Indexed8, flags).createAlphaMask(flags);
+ }
+
+ QImage mask(d->width, d->height, Format_MonoLSB);
+ if (!mask.isNull())
+ dither_to_Mono(mask.d, d, flags, true);
+ return mask;
+}
+
+#ifndef QT_NO_IMAGE_HEURISTIC_MASK
+/*!
+ Creates and returns a 1-bpp heuristic mask for this image.
+
+ The function works by selecting a color from one of the corners,
+ then chipping away pixels of that color starting at all the edges.
+ The four corners vote for which color is to be masked away. In
+ case of a draw (this generally means that this function is not
+ applicable to the image), the result is arbitrary.
+
+ The returned image has little-endian bit order (i.e. the image's
+ format is QImage::Format_MonoLSB), which you can convert to
+ big-endian (QImage::Format_Mono) using the convertToFormat()
+ function.
+
+ If \a clipTight is true (the default) the mask is just large
+ enough to cover the pixels; otherwise, the mask is larger than the
+ data pixels.
+
+ Note that this function disregards the alpha buffer.
+
+ \sa createAlphaMask(), {QImage#Image Transformations}{Image
+ Transformations}
+*/
+
+QImage QImage::createHeuristicMask(bool clipTight) const
+{
+ if (!d)
+ return QImage();
+
+ if (d->depth != 32) {
+ QImage img32 = convertToFormat(Format_RGB32);
+ return img32.createHeuristicMask(clipTight);
+ }
+
+#define PIX(x,y) (*((QRgb*)scanLine(y)+x) & 0x00ffffff)
+
+ int w = width();
+ int h = height();
+ QImage m(w, h, Format_MonoLSB);
+ QIMAGE_SANITYCHECK_MEMORY(m);
+ m.setColorCount(2);
+ m.setColor(0, QColor(Qt::color0).rgba());
+ m.setColor(1, QColor(Qt::color1).rgba());
+ m.fill(0xff);
+
+ QRgb background = PIX(0,0);
+ if (background != PIX(w-1,0) &&
+ background != PIX(0,h-1) &&
+ background != PIX(w-1,h-1)) {
+ background = PIX(w-1,0);
+ if (background != PIX(w-1,h-1) &&
+ background != PIX(0,h-1) &&
+ PIX(0,h-1) == PIX(w-1,h-1)) {
+ background = PIX(w-1,h-1);
+ }
+ }
+
+ int x,y;
+ bool done = false;
+ uchar *ypp, *ypc, *ypn;
+ while(!done) {
+ done = true;
+ ypn = m.scanLine(0);
+ ypc = 0;
+ for (y = 0; y < h; y++) {
+ ypp = ypc;
+ ypc = ypn;
+ ypn = (y == h-1) ? 0 : m.scanLine(y+1);
+ QRgb *p = (QRgb *)scanLine(y);
+ for (x = 0; x < w; x++) {
+ // slowness here - it's possible to do six of these tests
+ // together in one go. oh well.
+ if ((x == 0 || y == 0 || x == w-1 || y == h-1 ||
+ !(*(ypc + ((x-1) >> 3)) & (1 << ((x-1) & 7))) ||
+ !(*(ypc + ((x+1) >> 3)) & (1 << ((x+1) & 7))) ||
+ !(*(ypp + (x >> 3)) & (1 << (x & 7))) ||
+ !(*(ypn + (x >> 3)) & (1 << (x & 7)))) &&
+ ( (*(ypc + (x >> 3)) & (1 << (x & 7)))) &&
+ ((*p & 0x00ffffff) == background)) {
+ done = false;
+ *(ypc + (x >> 3)) &= ~(1 << (x & 7));
+ }
+ p++;
+ }
+ }
+ }
+
+ if (!clipTight) {
+ ypn = m.scanLine(0);
+ ypc = 0;
+ for (y = 0; y < h; y++) {
+ ypp = ypc;
+ ypc = ypn;
+ ypn = (y == h-1) ? 0 : m.scanLine(y+1);
+ QRgb *p = (QRgb *)scanLine(y);
+ for (x = 0; x < w; x++) {
+ if ((*p & 0x00ffffff) != background) {
+ if (x > 0)
+ *(ypc + ((x-1) >> 3)) |= (1 << ((x-1) & 7));
+ if (x < w-1)
+ *(ypc + ((x+1) >> 3)) |= (1 << ((x+1) & 7));
+ if (y > 0)
+ *(ypp + (x >> 3)) |= (1 << (x & 7));
+ if (y < h-1)
+ *(ypn + (x >> 3)) |= (1 << (x & 7));
+ }
+ p++;
+ }
+ }
+ }
+
+#undef PIX
+
+ return m;
+}
+#endif //QT_NO_IMAGE_HEURISTIC_MASK
+
+/*!
+ Creates and returns a mask for this image based on the given \a
+ color value. If the \a mode is MaskInColor (the default value),
+ all pixels matching \a color will be opaque pixels in the mask. If
+ \a mode is MaskOutColor, all pixels matching the given color will
+ be transparent.
+
+ \sa createAlphaMask(), createHeuristicMask()
+*/
+
+QImage QImage::createMaskFromColor(QRgb color, Qt::MaskMode mode) const
+{
+ if (!d)
+ return QImage();
+ QImage maskImage(size(), QImage::Format_MonoLSB);
+ QIMAGE_SANITYCHECK_MEMORY(maskImage);
+ maskImage.fill(0);
+ uchar *s = maskImage.bits();
+
+ if (depth() == 32) {
+ for (int h = 0; h < d->height; h++) {
+ const uint *sl = (uint *) scanLine(h);
+ for (int w = 0; w < d->width; w++) {
+ if (sl[w] == color)
+ *(s + (w >> 3)) |= (1 << (w & 7));
+ }
+ s += maskImage.bytesPerLine();
+ }
+ } else {
+ for (int h = 0; h < d->height; h++) {
+ for (int w = 0; w < d->width; w++) {
+ if ((uint) pixel(w, h) == color)
+ *(s + (w >> 3)) |= (1 << (w & 7));
+ }
+ s += maskImage.bytesPerLine();
+ }
+ }
+ if (mode == Qt::MaskOutColor)
+ maskImage.invertPixels();
+ return maskImage;
+}
+
+
+/*
+ This code is contributed by Philipp Lang,
+ GeneriCom Software Germany (www.generi.com)
+ under the terms of the QPL, Version 1.0
+*/
+
+/*!
+ \fn QImage QImage::mirror(bool horizontal, bool vertical) const
+
+ Use mirrored() instead.
+*/
+
+/*!
+ Returns a mirror of the image, mirrored in the horizontal and/or
+ the vertical direction depending on whether \a horizontal and \a
+ vertical are set to true or false.
+
+ Note that the original image is not changed.
+
+ \sa {QImage#Image Transformations}{Image Transformations}
+*/
+QImage QImage::mirrored(bool horizontal, bool vertical) const
+{
+ if (!d)
+ return QImage();
+
+ if ((d->width <= 1 && d->height <= 1) || (!horizontal && !vertical))
+ return *this;
+
+ int w = d->width;
+ int h = d->height;
+ // Create result image, copy colormap
+ QImage result(d->width, d->height, d->format);
+ QIMAGE_SANITYCHECK_MEMORY(result);
+
+ // check if we ran out of of memory..
+ if (!result.d)
+ return QImage();
+
+ result.d->colortable = d->colortable;
+ result.d->has_alpha_clut = d->has_alpha_clut;
+
+ if (depth() == 1)
+ w = (w+7)/8;
+ int dxi = horizontal ? -1 : 1;
+ int dxs = horizontal ? w-1 : 0;
+ int dyi = vertical ? -1 : 1;
+ int dy = vertical ? h-1: 0;
+
+ // 1 bit, 8 bit
+ if (d->depth == 1 || d->depth == 8) {
+ for (int sy = 0; sy < h; sy++, dy += dyi) {
+ quint8* ssl = (quint8*)(d->data + sy*d->bytes_per_line);
+ quint8* dsl = (quint8*)(result.d->data + dy*result.d->bytes_per_line);
+ int dx = dxs;
+ for (int sx = 0; sx < w; sx++, dx += dxi)
+ dsl[dx] = ssl[sx];
+ }
+ }
+ // 16 bit
+ else if (d->depth == 16) {
+ for (int sy = 0; sy < h; sy++, dy += dyi) {
+ quint16* ssl = (quint16*)(d->data + sy*d->bytes_per_line);
+ quint16* dsl = (quint16*)(result.d->data + dy*result.d->bytes_per_line);
+ int dx = dxs;
+ for (int sx = 0; sx < w; sx++, dx += dxi)
+ dsl[dx] = ssl[sx];
+ }
+ }
+ // 24 bit
+ else if (d->depth == 24) {
+ for (int sy = 0; sy < h; sy++, dy += dyi) {
+ quint24* ssl = (quint24*)(d->data + sy*d->bytes_per_line);
+ quint24* dsl = (quint24*)(result.d->data + dy*result.d->bytes_per_line);
+ int dx = dxs;
+ for (int sx = 0; sx < w; sx++, dx += dxi)
+ dsl[dx] = ssl[sx];
+ }
+ }
+ // 32 bit
+ else if (d->depth == 32) {
+ for (int sy = 0; sy < h; sy++, dy += dyi) {
+ quint32* ssl = (quint32*)(d->data + sy*d->bytes_per_line);
+ quint32* dsl = (quint32*)(result.d->data + dy*result.d->bytes_per_line);
+ int dx = dxs;
+ for (int sx = 0; sx < w; sx++, dx += dxi)
+ dsl[dx] = ssl[sx];
+ }
+ }
+
+ // special handling of 1 bit images for horizontal mirroring
+ if (horizontal && d->depth == 1) {
+ int shift = width() % 8;
+ for (int y = h-1; y >= 0; y--) {
+ quint8* a0 = (quint8*)(result.d->data + y*d->bytes_per_line);
+ // Swap bytes
+ quint8* a = a0+dxs;
+ while (a >= a0) {
+ *a = bitflip[*a];
+ a--;
+ }
+ // Shift bits if unaligned
+ if (shift != 0) {
+ a = a0+dxs;
+ quint8 c = 0;
+ if (format() == Format_MonoLSB) {
+ while (a >= a0) {
+ quint8 nc = *a << shift;
+ *a = (*a >> (8-shift)) | c;
+ --a;
+ c = nc;
+ }
+ } else {
+ while (a >= a0) {
+ quint8 nc = *a >> shift;
+ *a = (*a << (8-shift)) | c;
+ --a;
+ c = nc;
+ }
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+/*!
+ \fn QImage QImage::swapRGB() const
+
+ Use rgbSwapped() instead.
+
+ \omit
+ Returns a QImage in which the values of the red and blue
+ components of all pixels have been swapped, effectively converting
+ an RGB image to an BGR image. The original QImage is not changed.
+ \endomit
+*/
+
+/*!
+ Returns a QImage in which the values of the red and blue
+ components of all pixels have been swapped, effectively converting
+ an RGB image to an BGR image.
+
+ The original QImage is not changed.
+
+ \sa {QImage#Image Transformations}{Image Transformations}
+*/
+QImage QImage::rgbSwapped() const
+{
+ if (isNull())
+ return *this;
+ QImage res;
+ switch (d->format) {
+ case Format_Invalid:
+ case NImageFormats:
+ Q_ASSERT(false);
+ break;
+ case Format_Mono:
+ case Format_MonoLSB:
+ case Format_Indexed8:
+ res = copy();
+ for (int i = 0; i < res.d->colortable.size(); i++) {
+ QRgb c = res.d->colortable.at(i);
+ res.d->colortable[i] = QRgb(((c << 16) & 0xff0000) | ((c >> 16) & 0xff) | (c & 0xff00ff00));
+ }
+ break;
+ case Format_RGB32:
+ case Format_ARGB32:
+ case Format_ARGB32_Premultiplied:
+ res = QImage(d->width, d->height, d->format);
+ QIMAGE_SANITYCHECK_MEMORY(res);
+ for (int i = 0; i < d->height; i++) {
+ uint *q = (uint*)res.scanLine(i);
+ uint *p = (uint*)constScanLine(i);
+ uint *end = p + d->width;
+ while (p < end) {
+ *q = ((*p << 16) & 0xff0000) | ((*p >> 16) & 0xff) | (*p & 0xff00ff00);
+ p++;
+ q++;
+ }
+ }
+ break;
+ case Format_RGB16:
+ res = QImage(d->width, d->height, d->format);
+ QIMAGE_SANITYCHECK_MEMORY(res);
+ for (int i = 0; i < d->height; i++) {
+ ushort *q = (ushort*)res.scanLine(i);
+ const ushort *p = (const ushort*)constScanLine(i);
+ const ushort *end = p + d->width;
+ while (p < end) {
+ *q = ((*p << 11) & 0xf800) | ((*p >> 11) & 0x1f) | (*p & 0x07e0);
+ p++;
+ q++;
+ }
+ }
+ break;
+ case Format_ARGB8565_Premultiplied:
+ res = QImage(d->width, d->height, d->format);
+ QIMAGE_SANITYCHECK_MEMORY(res);
+ for (int i = 0; i < d->height; i++) {
+ const quint8 *p = constScanLine(i);
+ quint8 *q = res.scanLine(i);
+ const quint8 *end = p + d->width * sizeof(qargb8565);
+ while (p < end) {
+ q[0] = p[0];
+ q[1] = (p[1] & 0xe0) | (p[2] >> 3);
+ q[2] = (p[2] & 0x07) | (p[1] << 3);
+ p += sizeof(qargb8565);
+ q += sizeof(qargb8565);
+ }
+ }
+ break;
+ case Format_RGB666:
+ res = QImage(d->width, d->height, d->format);
+ QIMAGE_SANITYCHECK_MEMORY(res);
+ for (int i = 0; i < d->height; i++) {
+ qrgb666 *q = reinterpret_cast<qrgb666*>(res.scanLine(i));
+ const qrgb666 *p = reinterpret_cast<const qrgb666*>(constScanLine(i));
+ const qrgb666 *end = p + d->width;
+ while (p < end) {
+ const QRgb rgb = quint32(*p++);
+ *q++ = qRgb(qBlue(rgb), qGreen(rgb), qRed(rgb));
+ }
+ }
+ break;
+ case Format_ARGB6666_Premultiplied:
+ res = QImage(d->width, d->height, d->format);
+ QIMAGE_SANITYCHECK_MEMORY(res);
+ for (int i = 0; i < d->height; i++) {
+ const quint8 *p = constScanLine(i);
+ const quint8 *end = p + d->width * sizeof(qargb6666);
+ quint8 *q = res.scanLine(i);
+ while (p < end) {
+ q[0] = (p[1] >> 4) | ((p[2] & 0x3) << 4) | (p[0] & 0xc0);
+ q[1] = (p[1] & 0xf) | (p[0] << 4);
+ q[2] = (p[2] & 0xfc) | ((p[0] >> 4) & 0x3);
+ p += sizeof(qargb6666);
+ q += sizeof(qargb6666);
+ }
+ }
+ break;
+ case Format_RGB555:
+ res = QImage(d->width, d->height, d->format);
+ QIMAGE_SANITYCHECK_MEMORY(res);
+ for (int i = 0; i < d->height; i++) {
+ quint16 *q = (quint16*)res.scanLine(i);
+ const quint16 *p = (const quint16*)constScanLine(i);
+ const quint16 *end = p + d->width;
+ while (p < end) {
+ *q = ((*p << 10) & 0x7c00) | ((*p >> 10) & 0x1f) | (*p & 0x3e0);
+ p++;
+ q++;
+ }
+ }
+ break;
+ case Format_ARGB8555_Premultiplied:
+ res = QImage(d->width, d->height, d->format);
+ QIMAGE_SANITYCHECK_MEMORY(res);
+ for (int i = 0; i < d->height; i++) {
+ const quint8 *p = constScanLine(i);
+ quint8 *q = res.scanLine(i);
+ const quint8 *end = p + d->width * sizeof(qargb8555);
+ while (p < end) {
+ q[0] = p[0];
+ q[1] = (p[1] & 0xe0) | (p[2] >> 2);
+ q[2] = (p[2] & 0x03) | ((p[1] << 2) & 0x7f);
+ p += sizeof(qargb8555);
+ q += sizeof(qargb8555);
+ }
+ }
+ break;
+ case Format_RGB888:
+ res = QImage(d->width, d->height, d->format);
+ QIMAGE_SANITYCHECK_MEMORY(res);
+ for (int i = 0; i < d->height; i++) {
+ quint8 *q = res.scanLine(i);
+ const quint8 *p = constScanLine(i);
+ const quint8 *end = p + d->width * sizeof(qrgb888);
+ while (p < end) {
+ q[0] = p[2];
+ q[1] = p[1];
+ q[2] = p[0];
+ q += sizeof(qrgb888);
+ p += sizeof(qrgb888);
+ }
+ }
+ break;
+ case Format_RGB444:
+ case Format_ARGB4444_Premultiplied:
+ res = QImage(d->width, d->height, d->format);
+ QIMAGE_SANITYCHECK_MEMORY(res);
+ for (int i = 0; i < d->height; i++) {
+ quint16 *q = reinterpret_cast<quint16*>(res.scanLine(i));
+ const quint16 *p = reinterpret_cast<const quint16*>(constScanLine(i));
+ const quint16 *end = p + d->width;
+ while (p < end) {
+ *q = (*p & 0xf0f0) | ((*p & 0x0f) << 8) | ((*p & 0xf00) >> 8);
+ p++;
+ q++;
+ }
+ }
+ break;
+ }
+ return res;
+}
+
+/*!
+ Loads an image from the file with the given \a fileName. Returns true if
+ the image was successfully loaded; otherwise returns false.
+
+ The loader attempts to read the image using the specified \a format, e.g.,
+ PNG or JPG. If \a format is not specified (which is the default), the
+ loader probes the file for a header to guess the file format.
+
+ The file name can either refer to an actual file on disk or to one
+ of the application's embedded resources. See the
+ \l{resources.html}{Resource System} overview for details on how to
+ embed images and other resource files in the application's
+ executable.
+
+ \sa {QImage#Reading and Writing Image Files}{Reading and Writing Image Files}
+*/
+
+bool QImage::load(const QString &fileName, const char* format)
+{
+ if (fileName.isEmpty())
+ return false;
+
+ QImage image = QImageReader(fileName, format).read();
+ if (!image.isNull()) {
+ operator=(image);
+ return true;
+ }
+ return false;
+}
+
+/*!
+ \overload
+
+ This function reads a QImage from the given \a device. This can,
+ for example, be used to load an image directly into a QByteArray.
+*/
+
+bool QImage::load(QIODevice* device, const char* format)
+{
+ QImage image = QImageReader(device, format).read();
+ if(!image.isNull()) {
+ operator=(image);
+ return true;
+ }
+ return false;
+}
+
+/*!
+ \fn bool QImage::loadFromData(const uchar *data, int len, const char *format)
+
+ Loads an image from the first \a len bytes of the given binary \a
+ data. Returns true if the image was successfully loaded; otherwise
+ returns false.
+
+ The loader attempts to read the image using the specified \a format, e.g.,
+ PNG or JPG. If \a format is not specified (which is the default), the
+ loader probes the file for a header to guess the file format.
+
+ \sa {QImage#Reading and Writing Image Files}{Reading and Writing Image Files}
+*/
+
+bool QImage::loadFromData(const uchar *data, int len, const char *format)
+{
+ QImage image = fromData(data, len, format);
+ if (!image.isNull()) {
+ operator=(image);
+ return true;
+ }
+ return false;
+}
+
+/*!
+ \fn bool QImage::loadFromData(const QByteArray &data, const char *format)
+
+ \overload
+
+ Loads an image from the given QByteArray \a data.
+*/
+
+/*!
+ \fn QImage QImage::fromData(const uchar *data, int size, const char *format)
+
+ Constructs a QImage from the first \a size bytes of the given
+ binary \a data. The loader attempts to read the image using the
+ specified \a format. If \a format is not specified (which is the default),
+ the loader probes the file for a header to guess the file format.
+ binary \a data. The loader attempts to read the image, either using the
+ optional image \a format specified or by determining the image format from
+ the data.
+
+ If \a format is not specified (which is the default), the loader probes the
+ file for a header to determine the file format. If \a format is specified,
+ it must be one of the values returned by QImageReader::supportedImageFormats().
+
+ If the loading of the image fails, the image returned will be a null image.
+
+ \sa load(), save(), {QImage#Reading and Writing Image Files}{Reading and Writing Image Files}
+ */
+
+QImage QImage::fromData(const uchar *data, int size, const char *format)
+{
+ QByteArray a = QByteArray::fromRawData(reinterpret_cast<const char *>(data), size);
+ QBuffer b;
+ b.setData(a);
+ b.open(QIODevice::ReadOnly);
+ return QImageReader(&b, format).read();
+}
+
+/*!
+ \fn QImage QImage::fromData(const QByteArray &data, const char *format)
+
+ \overload
+
+ Loads an image from the given QByteArray \a data.
+*/
+
+/*!
+ Saves the image to the file with the given \a fileName, using the
+ given image file \a format and \a quality factor. If \a format is
+ 0, QImage will attempt to guess the format by looking at \a fileName's
+ suffix.
+
+ The \a quality factor must be in the range 0 to 100 or -1. Specify
+ 0 to obtain small compressed files, 100 for large uncompressed
+ files, and -1 (the default) to use the default settings.
+
+ Returns true if the image was successfully saved; otherwise
+ returns false.
+
+ \sa {QImage#Reading and Writing Image Files}{Reading and Writing
+ Image Files}
+*/
+bool QImage::save(const QString &fileName, const char *format, int quality) const
+{
+ if (isNull())
+ return false;
+ QImageWriter writer(fileName, format);
+ return d->doImageIO(this, &writer, quality);
+}
+
+/*!
+ \overload
+
+ This function writes a QImage to the given \a device.
+
+ This can, for example, be used to save an image directly into a
+ QByteArray:
+
+ \snippet doc/src/snippets/image/image.cpp 0
+*/
+
+bool QImage::save(QIODevice* device, const char* format, int quality) const
+{
+ if (isNull())
+ return false; // nothing to save
+ QImageWriter writer(device, format);
+ return d->doImageIO(this, &writer, quality);
+}
+
+/* \internal
+*/
+
+bool QImageData::doImageIO(const QImage *image, QImageWriter *writer, int quality) const
+{
+ if (quality > 100 || quality < -1)
+ qWarning("QPixmap::save: Quality out of range [-1, 100]");
+ if (quality >= 0)
+ writer->setQuality(qMin(quality,100));
+ return writer->write(*image);
+}
+
+/*****************************************************************************
+ QImage stream functions
+ *****************************************************************************/
+#if !defined(QT_NO_DATASTREAM)
+/*!
+ \fn QDataStream &operator<<(QDataStream &stream, const QImage &image)
+ \relates QImage
+
+ Writes the given \a image to the given \a stream as a PNG image,
+ or as a BMP image if the stream's version is 1. Note that writing
+ the stream to a file will not produce a valid image file.
+
+ \sa QImage::save(), {Serializing Qt Data Types}
+*/
+
+QDataStream &operator<<(QDataStream &s, const QImage &image)
+{
+ if (s.version() >= 5) {
+ if (image.isNull()) {
+ s << (qint32) 0; // null image marker
+ return s;
+ } else {
+ s << (qint32) 1;
+ // continue ...
+ }
+ }
+ QImageWriter writer(s.device(), s.version() == 1 ? "bmp" : "png");
+ writer.write(image);
+ return s;
+}
+
+/*!
+ \fn QDataStream &operator>>(QDataStream &stream, QImage &image)
+ \relates QImage
+
+ Reads an image from the given \a stream and stores it in the given
+ \a image.
+
+ \sa QImage::load(), {Serializing Qt Data Types}
+*/
+
+QDataStream &operator>>(QDataStream &s, QImage &image)
+{
+ if (s.version() >= 5) {
+ qint32 nullMarker;
+ s >> nullMarker;
+ if (!nullMarker) {
+ image = QImage(); // null image
+ return s;
+ }
+ }
+ image = QImageReader(s.device(), 0).read();
+ return s;
+}
+#endif // QT_NO_DATASTREAM
+
+
+#ifdef QT3_SUPPORT
+/*!
+ \fn QImage QImage::convertDepthWithPalette(int depth, QRgb* palette, int palette_count, Qt::ImageConversionFlags flags) const
+
+ Returns an image with the given \a depth, using the \a
+ palette_count colors pointed to by \a palette. If \a depth is 1 or
+ 8, the returned image will have its color table ordered in the
+ same way as \a palette.
+
+ If the image needs to be modified to fit in a lower-resolution
+ result (e.g. converting from 32-bit to 8-bit), use the \a flags to
+ specify how you'd prefer this to happen.
+
+ Note: currently no closest-color search is made. If colors are
+ found that are not in the palette, the palette may not be used at
+ all. This result should not be considered valid because it may
+ change in future implementations.
+
+ Currently inefficient for non-32-bit images.
+
+ Use the convertToFormat() function in combination with the
+ setColorTable() function instead.
+*/
+QImage QImage::convertDepthWithPalette(int d, QRgb* palette, int palette_count, Qt::ImageConversionFlags flags) const
+{
+ Format f = formatFor(d, QImage::LittleEndian);
+ QVector<QRgb> colortable;
+ for (int i = 0; i < palette_count; ++i)
+ colortable.append(palette[i]);
+ return convertToFormat(f, colortable, flags);
+}
+
+/*!
+ \relates QImage
+
+ Copies a block of pixels from \a src to \a dst. The pixels
+ copied from source (src) are converted according to
+ \a flags if it is incompatible with the destination
+ (\a dst).
+
+ \a sx, \a sy is the top-left pixel in \a src, \a dx, \a dy is the
+ top-left position in \a dst and \a sw, \a sh is the size of the
+ copied block. The copying is clipped if areas outside \a src or \a
+ dst are specified. If \a sw is -1, it is adjusted to
+ src->width(). Similarly, if \a sh is -1, it is adjusted to
+ src->height().
+
+ Currently inefficient for non 32-bit images.
+
+ Use copy() or QPainter::drawImage() instead.
+*/
+void bitBlt(QImage *dst, int dx, int dy, const QImage *src, int sx, int sy, int sw, int sh,
+ Qt::ImageConversionFlags flags)
+{
+ if (dst->isNull() || src->isNull())
+ return;
+ QPainter p(dst);
+ p.drawImage(QPoint(dx, dy), *src, QRect(sx, sy, sw, sh), flags);
+}
+#endif
+
+/*!
+ \fn bool QImage::operator==(const QImage & image) const
+
+ Returns true if this image and the given \a image have the same
+ contents; otherwise returns false.
+
+ The comparison can be slow, unless there is some obvious
+ difference (e.g. different size or format), in which case the
+ function will return quickly.
+
+ \sa operator=()
+*/
+
+bool QImage::operator==(const QImage & i) const
+{
+ // same object, or shared?
+ if (i.d == d)
+ return true;
+ if (!i.d || !d)
+ return false;
+
+ // obviously different stuff?
+ if (i.d->height != d->height || i.d->width != d->width || i.d->format != d->format)
+ return false;
+
+ if (d->format != Format_RGB32) {
+ if (d->format >= Format_ARGB32) { // all bits defined
+ const int n = d->width * d->depth / 8;
+ if (n == d->bytes_per_line && n == i.d->bytes_per_line) {
+ if (memcmp(bits(), i.bits(), d->nbytes))
+ return false;
+ } else {
+ for (int y = 0; y < d->height; ++y) {
+ if (memcmp(scanLine(y), i.scanLine(y), n))
+ return false;
+ }
+ }
+ } else {
+ const int w = width();
+ const int h = height();
+ const QVector<QRgb> &colortable = d->colortable;
+ const QVector<QRgb> &icolortable = i.d->colortable;
+ for (int y=0; y<h; ++y) {
+ for (int x=0; x<w; ++x) {
+ if (colortable[pixelIndex(x, y)] != icolortable[i.pixelIndex(x, y)])
+ return false;
+ }
+ }
+ }
+ } else {
+ //alpha channel undefined, so we must mask it out
+ for(int l = 0; l < d->height; l++) {
+ int w = d->width;
+ const uint *p1 = reinterpret_cast<const uint*>(scanLine(l));
+ const uint *p2 = reinterpret_cast<const uint*>(i.scanLine(l));
+ while (w--) {
+ if ((*p1++ & 0x00ffffff) != (*p2++ & 0x00ffffff))
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+
+/*!
+ \fn bool QImage::operator!=(const QImage & image) const
+
+ Returns true if this image and the given \a image have different
+ contents; otherwise returns false.
+
+ The comparison can be slow, unless there is some obvious
+ difference, such as different widths, in which case the function
+ will return quickly.
+
+ \sa operator=()
+*/
+
+bool QImage::operator!=(const QImage & i) const
+{
+ return !(*this == i);
+}
+
+
+
+
+/*!
+ Returns the number of pixels that fit horizontally in a physical
+ meter. Together with dotsPerMeterY(), this number defines the
+ intended scale and aspect ratio of the image.
+
+ \sa setDotsPerMeterX(), {QImage#Image Information}{Image
+ Information}
+*/
+int QImage::dotsPerMeterX() const
+{
+ return d ? qRound(d->dpmx) : 0;
+}
+
+/*!
+ Returns the number of pixels that fit vertically in a physical
+ meter. Together with dotsPerMeterX(), this number defines the
+ intended scale and aspect ratio of the image.
+
+ \sa setDotsPerMeterY(), {QImage#Image Information}{Image
+ Information}
+*/
+int QImage::dotsPerMeterY() const
+{
+ return d ? qRound(d->dpmy) : 0;
+}
+
+/*!
+ Sets the number of pixels that fit horizontally in a physical
+ meter, to \a x.
+
+ Together with dotsPerMeterY(), this number defines the intended
+ scale and aspect ratio of the image, and determines the scale
+ at which QPainter will draw graphics on the image. It does not
+ change the scale or aspect ratio of the image when it is rendered
+ on other paint devices.
+
+ \sa dotsPerMeterX(), {QImage#Image Information}{Image Information}
+*/
+void QImage::setDotsPerMeterX(int x)
+{
+ if (!d || !x)
+ return;
+ detach();
+
+ if (d)
+ d->dpmx = x;
+}
+
+/*!
+ Sets the number of pixels that fit vertically in a physical meter,
+ to \a y.
+
+ Together with dotsPerMeterX(), this number defines the intended
+ scale and aspect ratio of the image, and determines the scale
+ at which QPainter will draw graphics on the image. It does not
+ change the scale or aspect ratio of the image when it is rendered
+ on other paint devices.
+
+ \sa dotsPerMeterY(), {QImage#Image Information}{Image Information}
+*/
+void QImage::setDotsPerMeterY(int y)
+{
+ if (!d || !y)
+ return;
+ detach();
+
+ if (d)
+ d->dpmy = y;
+}
+
+/*!
+ \fn QPoint QImage::offset() const
+
+ Returns the number of pixels by which the image is intended to be
+ offset by when positioning relative to other images.
+
+ \sa setOffset(), {QImage#Image Information}{Image Information}
+*/
+QPoint QImage::offset() const
+{
+ return d ? d->offset : QPoint();
+}
+
+
+/*!
+ \fn void QImage::setOffset(const QPoint& offset)
+
+ Sets the number of pixels by which the image is intended to be
+ offset by when positioning relative to other images, to \a offset.
+
+ \sa offset(), {QImage#Image Information}{Image Information}
+*/
+void QImage::setOffset(const QPoint& p)
+{
+ if (!d)
+ return;
+ detach();
+
+ if (d)
+ d->offset = p;
+}
+#ifndef QT_NO_IMAGE_TEXT
+
+/*!
+ Returns the text keys for this image.
+
+ You can use these keys with text() to list the image text for a
+ certain key.
+
+ \sa text()
+*/
+QStringList QImage::textKeys() const
+{
+ return d ? QStringList(d->text.keys()) : QStringList();
+}
+
+/*!
+ Returns the image text associated with the given \a key. If the
+ specified \a key is an empty string, the whole image text is
+ returned, with each key-text pair separated by a newline.
+
+ \sa setText(), textKeys()
+*/
+QString QImage::text(const QString &key) const
+{
+ if (!d)
+ return QString();
+
+ if (!key.isEmpty())
+ return d->text.value(key);
+
+ QString tmp;
+ foreach (const QString &key, d->text.keys()) {
+ if (!tmp.isEmpty())
+ tmp += QLatin1String("\n\n");
+ tmp += key + QLatin1String(": ") + d->text.value(key).simplified();
+ }
+ return tmp;
+}
+
+/*!
+ \fn void QImage::setText(const QString &key, const QString &text)
+
+ Sets the image text to the given \a text and associate it with the
+ given \a key.
+
+ If you just want to store a single text block (i.e., a "comment"
+ or just a description), you can either pass an empty key, or use a
+ generic key like "Description".
+
+ The image text is embedded into the image data when you
+ call save() or QImageWriter::write().
+
+ Not all image formats support embedded text. You can find out
+ if a specific image or format supports embedding text
+ by using QImageWriter::supportsOption(). We give an example:
+
+ \snippet doc/src/snippets/image/supportedformat.cpp 0
+
+ You can use QImageWriter::supportedImageFormats() to find out
+ which image formats are available to you.
+
+ \sa text(), textKeys()
+*/
+void QImage::setText(const QString &key, const QString &value)
+{
+ if (!d)
+ return;
+ detach();
+
+ if (d)
+ d->text.insert(key, value);
+}
+
+/*!
+ \fn QString QImage::text(const char* key, const char* language) const
+ \obsolete
+
+ Returns the text recorded for the given \a key in the given \a
+ language, or in a default language if \a language is 0.
+
+ Use text() instead.
+
+ The language the text is recorded in is no longer relevant since
+ the text is always set using QString and UTF-8 representation.
+*/
+QString QImage::text(const char* key, const char* lang) const
+{
+ if (!d)
+ return QString();
+ QString k = QString::fromAscii(key);
+ if (lang && *lang)
+ k += QLatin1Char('/') + QString::fromAscii(lang);
+ return d->text.value(k);
+}
+
+/*!
+ \fn QString QImage::text(const QImageTextKeyLang& keywordAndLanguage) const
+ \overload
+ \obsolete
+
+ Returns the text recorded for the given \a keywordAndLanguage.
+
+ Use text() instead.
+
+ The language the text is recorded in is no longer relevant since
+ the text is always set using QString and UTF-8 representation.
+*/
+QString QImage::text(const QImageTextKeyLang& kl) const
+{
+ if (!d)
+ return QString();
+ QString k = QString::fromAscii(kl.key);
+ if (!kl.lang.isEmpty())
+ k += QLatin1Char('/') + QString::fromAscii(kl.lang);
+ return d->text.value(k);
+}
+
+/*!
+ \obsolete
+
+ Returns the language identifiers for which some texts are
+ recorded. Note that if you want to iterate over the list, you
+ should iterate over a copy.
+
+ The language the text is recorded in is no longer relevant since
+ the text is always set using QString and UTF-8 representation.
+*/
+QStringList QImage::textLanguages() const
+{
+ if (!d)
+ return QStringList();
+ QStringList keys = textKeys();
+ QStringList languages;
+ for (int i = 0; i < keys.size(); ++i) {
+ int index = keys.at(i).indexOf(QLatin1Char('/'));
+ if (index > 0)
+ languages += keys.at(i).mid(index+1);
+ }
+
+ return languages;
+}
+
+/*!
+ \obsolete
+
+ Returns a list of QImageTextKeyLang objects that enumerate all the
+ texts key/language pairs set for this image.
+
+ Use textKeys() instead.
+
+ The language the text is recorded in is no longer relevant since
+ the text is always set using QString and UTF-8 representation.
+*/
+QList<QImageTextKeyLang> QImage::textList() const
+{
+ QList<QImageTextKeyLang> imageTextKeys;
+ if (!d)
+ return imageTextKeys;
+ QStringList keys = textKeys();
+ for (int i = 0; i < keys.size(); ++i) {
+ int index = keys.at(i).indexOf(QLatin1Char('/'));
+ if (index > 0) {
+ QImageTextKeyLang tkl;
+ tkl.key = keys.at(i).left(index).toAscii();
+ tkl.lang = keys.at(i).mid(index+1).toAscii();
+ imageTextKeys += tkl;
+ }
+ }
+
+ return imageTextKeys;
+}
+
+/*!
+ \fn void QImage::setText(const char* key, const char* language, const QString& text)
+ \obsolete
+
+ Sets the image text to the given \a text and associate it with the
+ given \a key. The text is recorded in the specified \a language,
+ or in a default language if \a language is 0.
+
+ Use setText() instead.
+
+ The language the text is recorded in is no longer relevant since
+ the text is always set using QString and UTF-8 representation.
+
+ \omit
+ Records string \a for the keyword \a key. The \a key should be
+ a portable keyword recognizable by other software - some suggested
+ values can be found in
+ \l{http://www.libpng.org/pub/png/spec/1.2/png-1.2-pdg.html#C.Anc-text}
+ {the PNG specification}. \a s can be any text. \a lang should
+ specify the language code (see
+ \l{http://www.rfc-editor.org/rfc/rfc1766.txt}{RFC 1766}) or 0.
+ \endomit
+*/
+void QImage::setText(const char* key, const char* lang, const QString& s)
+{
+ if (!d)
+ return;
+ detach();
+
+ // In case detach() ran out of memory
+ if (!d)
+ return;
+
+ QString k = QString::fromAscii(key);
+ if (lang && *lang)
+ k += QLatin1Char('/') + QString::fromAscii(lang);
+ d->text.insert(k, s);
+}
+
+#endif // QT_NO_IMAGE_TEXT
+
+/*
+ Sets the image bits to the \a pixmap contents and returns a
+ reference to the image.
+
+ If the image shares data with other images, it will first
+ dereference the shared data.
+
+ Makes a call to QPixmap::convertToImage().
+*/
+
+/*! \fn QImage::Endian QImage::systemBitOrder()
+
+ Determines the bit order of the display hardware. Returns
+ QImage::LittleEndian (LSB first) or QImage::BigEndian (MSB first).
+
+ This function is no longer relevant for QImage. Use QSysInfo
+ instead.
+*/
+
+
+/*!
+ \internal
+
+ Used by QPainter to retrieve a paint engine for the image.
+*/
+
+QPaintEngine *QImage::paintEngine() const
+{
+ if (!d)
+ return 0;
+
+ if (!d->paintEngine) {
+ d->paintEngine = new QRasterPaintEngine(const_cast<QImage *>(this));
+ }
+
+ return d->paintEngine;
+}
+
+
+/*!
+ \internal
+
+ Returns the size for the specified \a metric on the device.
+*/
+int QImage::metric(PaintDeviceMetric metric) const
+{
+ if (!d)
+ return 0;
+
+ switch (metric) {
+ case PdmWidth:
+ return d->width;
+ break;
+
+ case PdmHeight:
+ return d->height;
+ break;
+
+ case PdmWidthMM:
+ return qRound(d->width * 1000 / d->dpmx);
+ break;
+
+ case PdmHeightMM:
+ return qRound(d->height * 1000 / d->dpmy);
+ break;
+
+ case PdmNumColors:
+ return d->colortable.size();
+ break;
+
+ case PdmDepth:
+ return d->depth;
+ break;
+
+ case PdmDpiX:
+ return qRound(d->dpmx * 0.0254);
+ break;
+
+ case PdmDpiY:
+ return qRound(d->dpmy * 0.0254);
+ break;
+
+ case PdmPhysicalDpiX:
+ return qRound(d->dpmx * 0.0254);
+ break;
+
+ case PdmPhysicalDpiY:
+ return qRound(d->dpmy * 0.0254);
+ break;
+
+ default:
+ qWarning("QImage::metric(): Unhandled metric type %d", metric);
+ break;
+ }
+ return 0;
+}
+
+
+
+/*****************************************************************************
+ QPixmap (and QImage) helper functions
+ *****************************************************************************/
+/*
+ This internal function contains the common (i.e. platform independent) code
+ to do a transformation of pixel data. It is used by QPixmap::transform() and by
+ QImage::transform().
+
+ \a trueMat is the true transformation matrix (see QPixmap::trueMatrix()) and
+ \a xoffset is an offset to the matrix.
+
+ \a msbfirst specifies for 1bpp images, if the MSB or LSB comes first and \a
+ depth specifies the colordepth of the data.
+
+ \a dptr is a pointer to the destination data, \a dbpl specifies the bits per
+ line for the destination data, \a p_inc is the offset that we advance for
+ every scanline and \a dHeight is the height of the destination image.
+
+ \a sprt is the pointer to the source data, \a sbpl specifies the bits per
+ line of the source data, \a sWidth and \a sHeight are the width and height of
+ the source data.
+*/
+
+#undef IWX_MSB
+#define IWX_MSB(b) if (trigx < maxws && trigy < maxhs) { \
+ if (*(sptr+sbpl*(trigy>>12)+(trigx>>15)) & \
+ (1 << (7-((trigx>>12)&7)))) \
+ *dptr |= b; \
+ } \
+ trigx += m11; \
+ trigy += m12;
+ // END OF MACRO
+#undef IWX_LSB
+#define IWX_LSB(b) if (trigx < maxws && trigy < maxhs) { \
+ if (*(sptr+sbpl*(trigy>>12)+(trigx>>15)) & \
+ (1 << ((trigx>>12)&7))) \
+ *dptr |= b; \
+ } \
+ trigx += m11; \
+ trigy += m12;
+ // END OF MACRO
+#undef IWX_PIX
+#define IWX_PIX(b) if (trigx < maxws && trigy < maxhs) { \
+ if ((*(sptr+sbpl*(trigy>>12)+(trigx>>15)) & \
+ (1 << (7-((trigx>>12)&7)))) == 0) \
+ *dptr &= ~b; \
+ } \
+ trigx += m11; \
+ trigy += m12;
+ // END OF MACRO
+bool qt_xForm_helper(const QTransform &trueMat, int xoffset, int type, int depth,
+ uchar *dptr, int dbpl, int p_inc, int dHeight,
+ const uchar *sptr, int sbpl, int sWidth, int sHeight)
+{
+ int m11 = int(trueMat.m11()*4096.0);
+ int m12 = int(trueMat.m12()*4096.0);
+ int m21 = int(trueMat.m21()*4096.0);
+ int m22 = int(trueMat.m22()*4096.0);
+ int dx = qRound(trueMat.dx()*4096.0);
+ int dy = qRound(trueMat.dy()*4096.0);
+
+ int m21ydx = dx + (xoffset<<16) + (m11 + m21) / 2;
+ int m22ydy = dy + (m12 + m22) / 2;
+ uint trigx;
+ uint trigy;
+ uint maxws = sWidth<<12;
+ uint maxhs = sHeight<<12;
+
+ for (int y=0; y<dHeight; y++) { // for each target scanline
+ trigx = m21ydx;
+ trigy = m22ydy;
+ uchar *maxp = dptr + dbpl;
+ if (depth != 1) {
+ switch (depth) {
+ case 8: // 8 bpp transform
+ while (dptr < maxp) {
+ if (trigx < maxws && trigy < maxhs)
+ *dptr = *(sptr+sbpl*(trigy>>12)+(trigx>>12));
+ trigx += m11;
+ trigy += m12;
+ dptr++;
+ }
+ break;
+
+ case 16: // 16 bpp transform
+ while (dptr < maxp) {
+ if (trigx < maxws && trigy < maxhs)
+ *((ushort*)dptr) = *((ushort *)(sptr+sbpl*(trigy>>12) +
+ ((trigx>>12)<<1)));
+ trigx += m11;
+ trigy += m12;
+ dptr++;
+ dptr++;
+ }
+ break;
+
+ case 24: // 24 bpp transform
+ while (dptr < maxp) {
+ if (trigx < maxws && trigy < maxhs) {
+ const uchar *p2 = sptr+sbpl*(trigy>>12) + ((trigx>>12)*3);
+ dptr[0] = p2[0];
+ dptr[1] = p2[1];
+ dptr[2] = p2[2];
+ }
+ trigx += m11;
+ trigy += m12;
+ dptr += 3;
+ }
+ break;
+
+ case 32: // 32 bpp transform
+ while (dptr < maxp) {
+ if (trigx < maxws && trigy < maxhs)
+ *((uint*)dptr) = *((uint *)(sptr+sbpl*(trigy>>12) +
+ ((trigx>>12)<<2)));
+ trigx += m11;
+ trigy += m12;
+ dptr += 4;
+ }
+ break;
+
+ default: {
+ return false;
+ }
+ }
+ } else {
+ switch (type) {
+ case QT_XFORM_TYPE_MSBFIRST:
+ while (dptr < maxp) {
+ IWX_MSB(128);
+ IWX_MSB(64);
+ IWX_MSB(32);
+ IWX_MSB(16);
+ IWX_MSB(8);
+ IWX_MSB(4);
+ IWX_MSB(2);
+ IWX_MSB(1);
+ dptr++;
+ }
+ break;
+ case QT_XFORM_TYPE_LSBFIRST:
+ while (dptr < maxp) {
+ IWX_LSB(1);
+ IWX_LSB(2);
+ IWX_LSB(4);
+ IWX_LSB(8);
+ IWX_LSB(16);
+ IWX_LSB(32);
+ IWX_LSB(64);
+ IWX_LSB(128);
+ dptr++;
+ }
+ break;
+# if defined(Q_WS_WIN)
+ case QT_XFORM_TYPE_WINDOWSPIXMAP:
+ while (dptr < maxp) {
+ IWX_PIX(128);
+ IWX_PIX(64);
+ IWX_PIX(32);
+ IWX_PIX(16);
+ IWX_PIX(8);
+ IWX_PIX(4);
+ IWX_PIX(2);
+ IWX_PIX(1);
+ dptr++;
+ }
+ break;
+# endif
+ }
+ }
+ m21ydx += m21;
+ m22ydy += m22;
+ dptr += p_inc;
+ }
+ return true;
+}
+#undef IWX_MSB
+#undef IWX_LSB
+#undef IWX_PIX
+
+/*!
+ \fn QImage QImage::xForm(const QMatrix &matrix) const
+
+ Use transformed() instead.
+
+ \oldcode
+ QImage image;
+ ...
+ image.xForm(matrix);
+ \newcode
+ QImage image;
+ ...
+ image.transformed(matrix);
+ \endcode
+*/
+
+/*! \obsolete
+ Returns a number that identifies the contents of this
+ QImage object. Distinct QImage objects can only have the same
+ serial number if they refer to the same contents (but they don't
+ have to).
+
+ Use cacheKey() instead.
+
+ \warning The serial number doesn't necessarily change when the
+ image is altered. This means that it may be dangerous to use
+ it as a cache key.
+
+ \sa operator==()
+*/
+
+int QImage::serialNumber() const
+{
+ if (!d)
+ return 0;
+ else
+ return d->ser_no;
+}
+
+/*!
+ Returns a number that identifies the contents of this QImage
+ object. Distinct QImage objects can only have the same key if they
+ refer to the same contents.
+
+ The key will change when the image is altered.
+*/
+qint64 QImage::cacheKey() const
+{
+ if (!d)
+ return 0;
+ else
+ return (((qint64) d->ser_no) << 32) | ((qint64) d->detach_no);
+}
+
+/*!
+ \internal
+
+ Returns true if the image is detached; otherwise returns false.
+
+ \sa detach(), {Implicit Data Sharing}
+*/
+
+bool QImage::isDetached() const
+{
+ return d && d->ref == 1;
+}
+
+
+/*!
+ \obsolete
+ Sets the alpha channel of this image to the given \a alphaChannel.
+
+ If \a alphaChannel is an 8 bit grayscale image, the intensity values are
+ written into this buffer directly. Otherwise, \a alphaChannel is converted
+ to 32 bit and the intensity of the RGB pixel values is used.
+
+ Note that the image will be converted to the Format_ARGB32_Premultiplied
+ format if the function succeeds.
+
+ Use one of the composition modes in QPainter::CompositionMode instead.
+
+ \warning This function is expensive.
+
+ \sa alphaChannel(), {QImage#Image Transformations}{Image
+ Transformations}, {QImage#Image Formats}{Image Formats}
+*/
+
+void QImage::setAlphaChannel(const QImage &alphaChannel)
+{
+ if (!d)
+ return;
+
+ int w = d->width;
+ int h = d->height;
+
+ if (w != alphaChannel.d->width || h != alphaChannel.d->height) {
+ qWarning("QImage::setAlphaChannel: "
+ "Alpha channel must have same dimensions as the target image");
+ return;
+ }
+
+ if (d->paintEngine && d->paintEngine->isActive()) {
+ qWarning("QImage::setAlphaChannel: "
+ "Unable to set alpha channel while image is being painted on");
+ return;
+ }
+
+ if (d->format == QImage::Format_ARGB32_Premultiplied)
+ detach();
+ else
+ *this = convertToFormat(QImage::Format_ARGB32_Premultiplied);
+
+ if (isNull())
+ return;
+
+ // Slight optimization since alphachannels are returned as 8-bit grays.
+ if (alphaChannel.d->depth == 8 && alphaChannel.isGrayscale()) {
+ const uchar *src_data = alphaChannel.d->data;
+ const uchar *dest_data = d->data;
+ for (int y=0; y<h; ++y) {
+ const uchar *src = src_data;
+ QRgb *dest = (QRgb *)dest_data;
+ for (int x=0; x<w; ++x) {
+ int alpha = *src;
+ int destAlpha = qt_div_255(alpha * qAlpha(*dest));
+ *dest = ((destAlpha << 24)
+ | (qt_div_255(qRed(*dest) * alpha) << 16)
+ | (qt_div_255(qGreen(*dest) * alpha) << 8)
+ | (qt_div_255(qBlue(*dest) * alpha)));
+ ++dest;
+ ++src;
+ }
+ src_data += alphaChannel.d->bytes_per_line;
+ dest_data += d->bytes_per_line;
+ }
+
+ } else {
+ const QImage sourceImage = alphaChannel.convertToFormat(QImage::Format_RGB32);
+ const uchar *src_data = sourceImage.d->data;
+ const uchar *dest_data = d->data;
+ for (int y=0; y<h; ++y) {
+ const QRgb *src = (const QRgb *) src_data;
+ QRgb *dest = (QRgb *) dest_data;
+ for (int x=0; x<w; ++x) {
+ int alpha = qGray(*src);
+ int destAlpha = qt_div_255(alpha * qAlpha(*dest));
+ *dest = ((destAlpha << 24)
+ | (qt_div_255(qRed(*dest) * alpha) << 16)
+ | (qt_div_255(qGreen(*dest) * alpha) << 8)
+ | (qt_div_255(qBlue(*dest) * alpha)));
+ ++dest;
+ ++src;
+ }
+ src_data += sourceImage.d->bytes_per_line;
+ dest_data += d->bytes_per_line;
+ }
+ }
+}
+
+
+/*!
+ \obsolete
+
+ Returns the alpha channel of the image as a new grayscale QImage in which
+ each pixel's red, green, and blue values are given the alpha value of the
+ original image. The color depth of the returned image is 8-bit.
+
+ You can see an example of use of this function in QPixmap's
+ \l{QPixmap::}{alphaChannel()}, which works in the same way as
+ this function on QPixmaps.
+
+ Most usecases for this function can be replaced with QPainter and
+ using composition modes.
+
+ \warning This is an expensive function.
+
+ \sa setAlphaChannel(), hasAlphaChannel(),
+ {QPixmap#Pixmap Information}{Pixmap},
+ {QImage#Image Transformations}{Image Transformations}
+*/
+
+QImage QImage::alphaChannel() const
+{
+ if (!d)
+ return QImage();
+
+ int w = d->width;
+ int h = d->height;
+
+ QImage image(w, h, Format_Indexed8);
+ image.setColorCount(256);
+
+ // set up gray scale table.
+ for (int i=0; i<256; ++i)
+ image.setColor(i, qRgb(i, i, i));
+
+ if (!hasAlphaChannel()) {
+ image.fill(255);
+ return image;
+ }
+
+ if (d->format == Format_Indexed8) {
+ const uchar *src_data = d->data;
+ uchar *dest_data = image.d->data;
+ for (int y=0; y<h; ++y) {
+ const uchar *src = src_data;
+ uchar *dest = dest_data;
+ for (int x=0; x<w; ++x) {
+ *dest = qAlpha(d->colortable.at(*src));
+ ++dest;
+ ++src;
+ }
+ src_data += d->bytes_per_line;
+ dest_data += image.d->bytes_per_line;
+ }
+ } else {
+ QImage alpha32 = *this;
+ if (d->format != Format_ARGB32 && d->format != Format_ARGB32_Premultiplied)
+ alpha32 = convertToFormat(Format_ARGB32);
+
+ const uchar *src_data = alpha32.d->data;
+ uchar *dest_data = image.d->data;
+ for (int y=0; y<h; ++y) {
+ const QRgb *src = (const QRgb *) src_data;
+ uchar *dest = dest_data;
+ for (int x=0; x<w; ++x) {
+ *dest = qAlpha(*src);
+ ++dest;
+ ++src;
+ }
+ src_data += alpha32.d->bytes_per_line;
+ dest_data += image.d->bytes_per_line;
+ }
+ }
+
+ return image;
+}
+
+/*!
+ Returns true if the image has a format that respects the alpha
+ channel, otherwise returns false.
+
+ \sa {QImage#Image Information}{Image Information}
+*/
+bool QImage::hasAlphaChannel() const
+{
+ return d && (d->format == Format_ARGB32_Premultiplied
+ || d->format == Format_ARGB32
+ || d->format == Format_ARGB8565_Premultiplied
+ || d->format == Format_ARGB8555_Premultiplied
+ || d->format == Format_ARGB6666_Premultiplied
+ || d->format == Format_ARGB4444_Premultiplied
+ || (d->has_alpha_clut && (d->format == Format_Indexed8
+ || d->format == Format_Mono
+ || d->format == Format_MonoLSB)));
+}
+
+
+/*!
+ \since 4.7
+ Returns the number of bit planes in the image.
+
+ The number of bit planes is the number of bits of color and
+ transparency information for each pixel. This is different from
+ (i.e. smaller than) the depth when the image format contains
+ unused bits.
+
+ \sa depth(), format(), {QImage#Image Formats}{Image Formats}
+*/
+int QImage::bitPlaneCount() const
+{
+ if (!d)
+ return 0;
+ int bpc = 0;
+ switch (d->format) {
+ case QImage::Format_Invalid:
+ break;
+ case QImage::Format_RGB32:
+ bpc = 24;
+ break;
+ case QImage::Format_RGB666:
+ bpc = 18;
+ break;
+ case QImage::Format_RGB555:
+ bpc = 15;
+ break;
+ case QImage::Format_ARGB8555_Premultiplied:
+ bpc = 23;
+ break;
+ case QImage::Format_RGB444:
+ bpc = 12;
+ break;
+ default:
+ bpc = qt_depthForFormat(d->format);
+ break;
+ }
+ return bpc;
+}
+
+
+#ifdef QT3_SUPPORT
+#if defined(Q_WS_X11)
+QT_BEGIN_INCLUDE_NAMESPACE
+#include <private/qt_x11_p.h>
+QT_END_INCLUDE_NAMESPACE
+#endif
+
+QImage::Endian QImage::systemBitOrder()
+{
+#if defined(Q_WS_X11)
+ return BitmapBitOrder(X11->display) == MSBFirst ? BigEndian : LittleEndian;
+#else
+ return BigEndian;
+#endif
+}
+#endif
+
+/*!
+ \fn QImage QImage::copy(const QRect &rect, Qt::ImageConversionFlags flags) const
+ \compat
+
+ Use copy() instead.
+*/
+
+/*!
+ \fn QImage QImage::copy(int x, int y, int w, int h, Qt::ImageConversionFlags flags) const
+ \compat
+
+ Use copy() instead.
+*/
+
+/*!
+ \fn QImage QImage::scaleWidth(int w) const
+ \compat
+
+ Use scaledToWidth() instead.
+*/
+
+/*!
+ \fn QImage QImage::scaleHeight(int h) const
+ \compat
+
+ Use scaledToHeight() instead.
+*/
+
+static QImage smoothScaled(const QImage &source, int w, int h) {
+ QImage src = source;
+ if (src.format() == QImage::Format_ARGB32)
+ src = src.convertToFormat(QImage::Format_ARGB32_Premultiplied);
+ else if (src.depth() < 32) {
+ if (src.hasAlphaChannel())
+ src = src.convertToFormat(QImage::Format_ARGB32_Premultiplied);
+ else
+ src = src.convertToFormat(QImage::Format_RGB32);
+ }
+
+ return qSmoothScaleImage(src, w, h);
+}
+
+
+static QImage rotated90(const QImage &image) {
+ QImage out(image.height(), image.width(), image.format());
+ if (image.colorCount() > 0)
+ out.setColorTable(image.colorTable());
+ int w = image.width();
+ int h = image.height();
+ switch (image.format()) {
+ case QImage::Format_RGB32:
+ case QImage::Format_ARGB32:
+ case QImage::Format_ARGB32_Premultiplied:
+ qt_memrotate270(reinterpret_cast<const quint32*>(image.bits()),
+ w, h, image.bytesPerLine(),
+ reinterpret_cast<quint32*>(out.bits()),
+ out.bytesPerLine());
+ break;
+ case QImage::Format_RGB666:
+ case QImage::Format_ARGB6666_Premultiplied:
+ case QImage::Format_ARGB8565_Premultiplied:
+ case QImage::Format_ARGB8555_Premultiplied:
+ case QImage::Format_RGB888:
+ qt_memrotate270(reinterpret_cast<const quint24*>(image.bits()),
+ w, h, image.bytesPerLine(),
+ reinterpret_cast<quint24*>(out.bits()),
+ out.bytesPerLine());
+ break;
+ case QImage::Format_RGB555:
+ case QImage::Format_RGB16:
+ case QImage::Format_ARGB4444_Premultiplied:
+ qt_memrotate270(reinterpret_cast<const quint16*>(image.bits()),
+ w, h, image.bytesPerLine(),
+ reinterpret_cast<quint16*>(out.bits()),
+ out.bytesPerLine());
+ break;
+ case QImage::Format_Indexed8:
+ qt_memrotate270(reinterpret_cast<const quint8*>(image.bits()),
+ w, h, image.bytesPerLine(),
+ reinterpret_cast<quint8*>(out.bits()),
+ out.bytesPerLine());
+ break;
+ default:
+ for (int y=0; y<h; ++y) {
+ if (image.colorCount())
+ for (int x=0; x<w; ++x)
+ out.setPixel(h-y-1, x, image.pixelIndex(x, y));
+ else
+ for (int x=0; x<w; ++x)
+ out.setPixel(h-y-1, x, image.pixel(x, y));
+ }
+ break;
+ }
+ return out;
+}
+
+
+static QImage rotated180(const QImage &image) {
+ return image.mirrored(true, true);
+}
+
+
+static QImage rotated270(const QImage &image) {
+ QImage out(image.height(), image.width(), image.format());
+ if (image.colorCount() > 0)
+ out.setColorTable(image.colorTable());
+ int w = image.width();
+ int h = image.height();
+ switch (image.format()) {
+ case QImage::Format_RGB32:
+ case QImage::Format_ARGB32:
+ case QImage::Format_ARGB32_Premultiplied:
+ qt_memrotate90(reinterpret_cast<const quint32*>(image.bits()),
+ w, h, image.bytesPerLine(),
+ reinterpret_cast<quint32*>(out.bits()),
+ out.bytesPerLine());
+ break;
+ case QImage::Format_RGB666:
+ case QImage::Format_ARGB6666_Premultiplied:
+ case QImage::Format_ARGB8565_Premultiplied:
+ case QImage::Format_ARGB8555_Premultiplied:
+ case QImage::Format_RGB888:
+ qt_memrotate90(reinterpret_cast<const quint24*>(image.bits()),
+ w, h, image.bytesPerLine(),
+ reinterpret_cast<quint24*>(out.bits()),
+ out.bytesPerLine());
+ break;
+ case QImage::Format_RGB555:
+ case QImage::Format_RGB16:
+ case QImage::Format_ARGB4444_Premultiplied:
+ qt_memrotate90(reinterpret_cast<const quint16*>(image.bits()),
+ w, h, image.bytesPerLine(),
+ reinterpret_cast<quint16*>(out.bits()),
+ out.bytesPerLine());
+ break;
+ case QImage::Format_Indexed8:
+ qt_memrotate90(reinterpret_cast<const quint8*>(image.bits()),
+ w, h, image.bytesPerLine(),
+ reinterpret_cast<quint8*>(out.bits()),
+ out.bytesPerLine());
+ break;
+ default:
+ for (int y=0; y<h; ++y) {
+ if (image.colorCount())
+ for (int x=0; x<w; ++x)
+ out.setPixel(y, w-x-1, image.pixelIndex(x, y));
+ else
+ for (int x=0; x<w; ++x)
+ out.setPixel(y, w-x-1, image.pixel(x, y));
+ }
+ break;
+ }
+ return out;
+}
+
+/*!
+ Returns a copy of the image that is transformed using the given
+ transformation \a matrix and transformation \a mode.
+
+ The transformation \a matrix is internally adjusted to compensate
+ for unwanted translation; i.e. the image produced is the smallest
+ image that contains all the transformed points of the original
+ image. Use the trueMatrix() function to retrieve the actual matrix
+ used for transforming an image.
+
+ Unlike the other overload, this function can be used to perform perspective
+ transformations on images.
+
+ \sa trueMatrix(), {QImage#Image Transformations}{Image
+ Transformations}
+*/
+
+QImage QImage::transformed(const QTransform &matrix, Qt::TransformationMode mode ) const
+{
+ if (!d)
+ return QImage();
+
+ // source image data
+ int ws = width();
+ int hs = height();
+
+ // target image data
+ int wd;
+ int hd;
+
+ // compute size of target image
+ QTransform mat = trueMatrix(matrix, ws, hs);
+ bool complex_xform = false;
+ bool scale_xform = false;
+ if (mat.type() <= QTransform::TxScale) {
+ if (mat.type() == QTransform::TxNone) // identity matrix
+ return *this;
+ else if (mat.m11() == -1. && mat.m22() == -1.)
+ return rotated180(*this);
+
+ if (mode == Qt::FastTransformation) {
+ hd = qRound(qAbs(mat.m22()) * hs);
+ wd = qRound(qAbs(mat.m11()) * ws);
+ } else {
+ hd = int(qAbs(mat.m22()) * hs + 0.9999);
+ wd = int(qAbs(mat.m11()) * ws + 0.9999);
+ }
+ scale_xform = true;
+ } else {
+ if (mat.type() <= QTransform::TxRotate && mat.m11() == 0 && mat.m22() == 0) {
+ if (mat.m12() == 1. && mat.m21() == -1.)
+ return rotated90(*this);
+ else if (mat.m12() == -1. && mat.m21() == 1.)
+ return rotated270(*this);
+ }
+
+ QPolygonF a(QRectF(0, 0, ws, hs));
+ a = mat.map(a);
+ QRect r = a.boundingRect().toAlignedRect();
+ wd = r.width();
+ hd = r.height();
+ complex_xform = true;
+ }
+
+ if (wd == 0 || hd == 0)
+ return QImage();
+
+ // Make use of the optimized algorithm when we're scaling
+ if (scale_xform && mode == Qt::SmoothTransformation) {
+ if (mat.m11() < 0.0F && mat.m22() < 0.0F) { // horizontal/vertical flip
+ return smoothScaled(mirrored(true, true), wd, hd);
+ } else if (mat.m11() < 0.0F) { // horizontal flip
+ return smoothScaled(mirrored(true, false), wd, hd);
+ } else if (mat.m22() < 0.0F) { // vertical flip
+ return smoothScaled(mirrored(false, true), wd, hd);
+ } else { // no flipping
+ return smoothScaled(*this, wd, hd);
+ }
+ }
+
+ int bpp = depth();
+
+ int sbpl = bytesPerLine();
+ const uchar *sptr = bits();
+
+ QImage::Format target_format = d->format;
+
+ if (complex_xform || mode == Qt::SmoothTransformation) {
+ if (d->format < QImage::Format_RGB32 || !hasAlphaChannel()) {
+ switch(d->format) {
+ case QImage::Format_RGB16:
+ target_format = Format_ARGB8565_Premultiplied;
+ break;
+ case QImage::Format_RGB555:
+ target_format = Format_ARGB8555_Premultiplied;
+ break;
+ case QImage::Format_RGB666:
+ target_format = Format_ARGB6666_Premultiplied;
+ break;
+ case QImage::Format_RGB444:
+ target_format = Format_ARGB4444_Premultiplied;
+ break;
+ default:
+ target_format = Format_ARGB32_Premultiplied;
+ break;
+ }
+ }
+ }
+
+ QImage dImage(wd, hd, target_format);
+ QIMAGE_SANITYCHECK_MEMORY(dImage);
+
+ if (target_format == QImage::Format_MonoLSB
+ || target_format == QImage::Format_Mono
+ || target_format == QImage::Format_Indexed8) {
+ dImage.d->colortable = d->colortable;
+ dImage.d->has_alpha_clut = d->has_alpha_clut | complex_xform;
+ }
+
+ dImage.d->dpmx = dotsPerMeterX();
+ dImage.d->dpmy = dotsPerMeterY();
+
+ switch (bpp) {
+ // initizialize the data
+ case 8:
+ if (dImage.d->colortable.size() < 256) {
+ // colors are left in the color table, so pick that one as transparent
+ dImage.d->colortable.append(0x0);
+ memset(dImage.bits(), dImage.d->colortable.size() - 1, dImage.byteCount());
+ } else {
+ memset(dImage.bits(), 0, dImage.byteCount());
+ }
+ break;
+ case 1:
+ case 16:
+ case 24:
+ case 32:
+ memset(dImage.bits(), 0x00, dImage.byteCount());
+ break;
+ }
+
+ if (target_format >= QImage::Format_RGB32) {
+ QPainter p(&dImage);
+ if (mode == Qt::SmoothTransformation) {
+ p.setRenderHint(QPainter::Antialiasing);
+ p.setRenderHint(QPainter::SmoothPixmapTransform);
+ }
+ p.setTransform(mat);
+ p.drawImage(QPoint(0, 0), *this);
+ } else {
+ bool invertible;
+ mat = mat.inverted(&invertible); // invert matrix
+ if (!invertible) // error, return null image
+ return QImage();
+
+ // create target image (some of the code is from QImage::copy())
+ int type = format() == Format_Mono ? QT_XFORM_TYPE_MSBFIRST : QT_XFORM_TYPE_LSBFIRST;
+ int dbpl = dImage.bytesPerLine();
+ qt_xForm_helper(mat, 0, type, bpp, dImage.bits(), dbpl, 0, hd, sptr, sbpl, ws, hs);
+ }
+ return dImage;
+}
+
+/*!
+ \fn QTransform QImage::trueMatrix(const QTransform &matrix, int width, int height)
+
+ Returns the actual matrix used for transforming an image with the
+ given \a width, \a height and \a matrix.
+
+ When transforming an image using the transformed() function, the
+ transformation matrix is internally adjusted to compensate for
+ unwanted translation, i.e. transformed() returns the smallest
+ image containing all transformed points of the original image.
+ This function returns the modified matrix, which maps points
+ correctly from the original image into the new image.
+
+ Unlike the other overload, this function creates transformation
+ matrices that can be used to perform perspective
+ transformations on images.
+
+ \sa transformed(), {QImage#Image Transformations}{Image
+ Transformations}
+*/
+
+QTransform QImage::trueMatrix(const QTransform &matrix, int w, int h)
+{
+ const QRectF rect(0, 0, w, h);
+ const QRect mapped = matrix.mapRect(rect).toAlignedRect();
+ const QPoint delta = mapped.topLeft();
+ return matrix * QTransform().translate(-delta.x(), -delta.y());
+}
+
+bool QImageData::convertInPlace(QImage::Format newFormat, Qt::ImageConversionFlags flags)
+{
+ if (format == newFormat)
+ return true;
+
+ // No in-place conversion if we have to detach
+ if (ref > 1)
+ return false;
+
+ const InPlace_Image_Converter *const converterPtr = &inplace_converter_map[format][newFormat];
+ InPlace_Image_Converter converter = *converterPtr;
+ if (converter)
+ return converter(this, flags);
+ else
+ return false;
+}
+
+/*!
+ \typedef QImage::DataPtr
+ \internal
+*/
+
+/*!
+ \fn DataPtr & QImage::data_ptr()
+ \internal
+*/
+
+QT_END_NAMESPACE
diff --git a/src/gui/image/qimage.h b/src/gui/image/qimage.h
new file mode 100644
index 0000000000..496fe93c54
--- /dev/null
+++ b/src/gui/image/qimage.h
@@ -0,0 +1,374 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QIMAGE_H
+#define QIMAGE_H
+
+#include <QtGui/qtransform.h>
+#include <QtGui/qpaintdevice.h>
+#include <QtGui/qrgb.h>
+#include <QtCore/qbytearray.h>
+#include <QtCore/qrect.h>
+#include <QtCore/qstring.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QIODevice;
+class QStringList;
+class QMatrix;
+class QTransform;
+class QVariant;
+template <class T> class QList;
+template <class T> class QVector;
+
+struct QImageData;
+class QImageDataMisc; // internal
+#ifndef QT_NO_IMAGE_TEXT
+class Q_GUI_EXPORT QImageTextKeyLang {
+public:
+ QImageTextKeyLang(const char* k, const char* l) : key(k), lang(l) { }
+ QImageTextKeyLang() { }
+
+ QByteArray key;
+ QByteArray lang;
+
+ bool operator< (const QImageTextKeyLang& other) const
+ { return key < other.key || (key==other.key && lang < other.lang); }
+ bool operator== (const QImageTextKeyLang& other) const
+ { return key==other.key && lang==other.lang; }
+ inline bool operator!= (const QImageTextKeyLang &other) const
+ { return !operator==(other); }
+};
+#endif //QT_NO_IMAGE_TEXT
+
+
+class Q_GUI_EXPORT QImage : public QPaintDevice
+{
+public:
+ enum InvertMode { InvertRgb, InvertRgba };
+ enum Format {
+ Format_Invalid,
+ Format_Mono,
+ Format_MonoLSB,
+ Format_Indexed8,
+ Format_RGB32,
+ Format_ARGB32,
+ Format_ARGB32_Premultiplied,
+ Format_RGB16,
+ Format_ARGB8565_Premultiplied,
+ Format_RGB666,
+ Format_ARGB6666_Premultiplied,
+ Format_RGB555,
+ Format_ARGB8555_Premultiplied,
+ Format_RGB888,
+ Format_RGB444,
+ Format_ARGB4444_Premultiplied,
+#if 0
+ // reserved for future use
+ Format_RGB15,
+ Format_Grayscale16,
+ Format_Grayscale8,
+ Format_Grayscale4,
+ Format_Grayscale4LSB,
+ Format_Grayscale2,
+ Format_Grayscale2LSB
+#endif
+#ifndef qdoc
+ NImageFormats
+#endif
+ };
+
+ QImage();
+ QImage(const QSize &size, Format format);
+ QImage(int width, int height, Format format);
+ QImage(uchar *data, int width, int height, Format format);
+ QImage(const uchar *data, int width, int height, Format format);
+ QImage(uchar *data, int width, int height, int bytesPerLine, Format format);
+ QImage(const uchar *data, int width, int height, int bytesPerLine, Format format);
+
+#ifndef QT_NO_IMAGEFORMAT_XPM
+ explicit QImage(const char * const xpm[]);
+#endif
+ explicit QImage(const QString &fileName, const char *format = 0);
+#ifndef QT_NO_CAST_FROM_ASCII
+ explicit QImage(const char *fileName, const char *format = 0);
+#endif
+
+ QImage(const QImage &);
+ ~QImage();
+
+ QImage &operator=(const QImage &);
+#ifdef Q_COMPILER_RVALUE_REFS
+ inline QImage &operator=(QImage &&other)
+ { qSwap(d, other.d); return *this; }
+#endif
+ inline void swap(QImage &other) { qSwap(d, other.d); }
+
+ bool isNull() const;
+
+ int devType() const;
+
+ bool operator==(const QImage &) const;
+ bool operator!=(const QImage &) const;
+ operator QVariant() const;
+ void detach();
+ bool isDetached() const;
+
+ QImage copy(const QRect &rect = QRect()) const;
+ inline QImage copy(int x, int y, int w, int h) const
+ { return copy(QRect(x, y, w, h)); }
+
+ Format format() const;
+
+ QImage convertToFormat(Format f, Qt::ImageConversionFlags flags = Qt::AutoColor) const Q_REQUIRED_RESULT;
+ QImage convertToFormat(Format f, const QVector<QRgb> &colorTable, Qt::ImageConversionFlags flags = Qt::AutoColor) const Q_REQUIRED_RESULT;
+
+ int width() const;
+ int height() const;
+ QSize size() const;
+ QRect rect() const;
+
+ int depth() const;
+#ifdef QT_DEPRECATED
+ QT_DEPRECATED int numColors() const;
+#endif
+ int colorCount() const;
+ int bitPlaneCount() const;
+
+ QRgb color(int i) const;
+ void setColor(int i, QRgb c);
+#ifdef QT_DEPRECATED
+ QT_DEPRECATED void setNumColors(int);
+#endif
+ void setColorCount(int);
+
+ bool allGray() const;
+ bool isGrayscale() const;
+
+ uchar *bits();
+ const uchar *bits() const;
+ const uchar *constBits() const;
+#ifdef QT_DEPRECATED
+ QT_DEPRECATED int numBytes() const;
+#endif
+ int byteCount() const;
+
+ uchar *scanLine(int);
+ const uchar *scanLine(int) const;
+ const uchar *constScanLine(int) const;
+ int bytesPerLine() const;
+
+ bool valid(int x, int y) const;
+ bool valid(const QPoint &pt) const;
+
+ int pixelIndex(int x, int y) const;
+ int pixelIndex(const QPoint &pt) const;
+
+ QRgb pixel(int x, int y) const;
+ QRgb pixel(const QPoint &pt) const;
+
+ void setPixel(int x, int y, uint index_or_rgb);
+ void setPixel(const QPoint &pt, uint index_or_rgb);
+
+ QVector<QRgb> colorTable() const;
+ void setColorTable(const QVector<QRgb> colors);
+
+ void fill(uint pixel);
+ void fill(const QColor &color);
+ void fill(Qt::GlobalColor color);
+
+
+ bool hasAlphaChannel() const;
+ void setAlphaChannel(const QImage &alphaChannel);
+ QImage alphaChannel() const;
+ QImage createAlphaMask(Qt::ImageConversionFlags flags = Qt::AutoColor) const;
+#ifndef QT_NO_IMAGE_HEURISTIC_MASK
+ QImage createHeuristicMask(bool clipTight = true) const;
+#endif
+ QImage createMaskFromColor(QRgb color, Qt::MaskMode mode = Qt::MaskInColor) const;
+
+ inline QImage scaled(int w, int h, Qt::AspectRatioMode aspectMode = Qt::IgnoreAspectRatio,
+ Qt::TransformationMode mode = Qt::FastTransformation) const
+ { return scaled(QSize(w, h), aspectMode, mode); }
+ QImage scaled(const QSize &s, Qt::AspectRatioMode aspectMode = Qt::IgnoreAspectRatio,
+ Qt::TransformationMode mode = Qt::FastTransformation) const;
+ QImage scaledToWidth(int w, Qt::TransformationMode mode = Qt::FastTransformation) const;
+ QImage scaledToHeight(int h, Qt::TransformationMode mode = Qt::FastTransformation) const;
+ QImage transformed(const QMatrix &matrix, Qt::TransformationMode mode = Qt::FastTransformation) const;
+ static QMatrix trueMatrix(const QMatrix &, int w, int h);
+ QImage transformed(const QTransform &matrix, Qt::TransformationMode mode = Qt::FastTransformation) const;
+ static QTransform trueMatrix(const QTransform &, int w, int h);
+ QImage mirrored(bool horizontally = false, bool vertically = true) const;
+ QImage rgbSwapped() const;
+ void invertPixels(InvertMode = InvertRgb);
+
+
+ bool load(QIODevice *device, const char* format);
+ bool load(const QString &fileName, const char* format=0);
+ bool loadFromData(const uchar *buf, int len, const char *format = 0);
+ inline bool loadFromData(const QByteArray &data, const char* aformat=0)
+ { return loadFromData(reinterpret_cast<const uchar *>(data.constData()), data.size(), aformat); }
+
+ bool save(const QString &fileName, const char* format=0, int quality=-1) const;
+ bool save(QIODevice *device, const char* format=0, int quality=-1) const;
+
+ static QImage fromData(const uchar *data, int size, const char *format = 0);
+ inline static QImage fromData(const QByteArray &data, const char *format = 0)
+ { return fromData(reinterpret_cast<const uchar *>(data.constData()), data.size(), format); }
+
+ int serialNumber() const;
+ qint64 cacheKey() const;
+
+ QPaintEngine *paintEngine() const;
+
+ // Auxiliary data
+ int dotsPerMeterX() const;
+ int dotsPerMeterY() const;
+ void setDotsPerMeterX(int);
+ void setDotsPerMeterY(int);
+ QPoint offset() const;
+ void setOffset(const QPoint&);
+#ifndef QT_NO_IMAGE_TEXT
+ QStringList textKeys() const;
+ QString text(const QString &key = QString()) const;
+ void setText(const QString &key, const QString &value);
+
+ // The following functions are obsolete as of 4.1
+ QString text(const char* key, const char* lang=0) const;
+ QList<QImageTextKeyLang> textList() const;
+ QStringList textLanguages() const;
+ QString text(const QImageTextKeyLang&) const;
+ void setText(const char* key, const char* lang, const QString&);
+#endif
+
+#ifdef QT3_SUPPORT
+ enum Endian { BigEndian, LittleEndian, IgnoreEndian };
+ QT3_SUPPORT_CONSTRUCTOR QImage(int width, int height, int depth, int numColors=0, Endian bitOrder=IgnoreEndian);
+ QT3_SUPPORT_CONSTRUCTOR QImage(const QSize&, int depth, int numColors=0, Endian bitOrder=IgnoreEndian);
+ QT3_SUPPORT_CONSTRUCTOR QImage(uchar *data, int w, int h, int depth, const QRgb *colortable, int numColors, Endian bitOrder);
+#ifdef Q_WS_QWS
+ QT3_SUPPORT_CONSTRUCTOR QImage(uchar *data, int w, int h, int depth, int pbl, const QRgb *colortable, int numColors, Endian bitOrder);
+#endif
+ inline QT3_SUPPORT Endian bitOrder() const {
+ Format f = format();
+ return f == Format_Mono ? BigEndian : (f == Format_MonoLSB ? LittleEndian : IgnoreEndian);
+ }
+ QT3_SUPPORT QImage convertDepth(int, Qt::ImageConversionFlags flags = Qt::AutoColor) const;
+ QT3_SUPPORT QImage convertDepthWithPalette(int, QRgb* p, int pc, Qt::ImageConversionFlags flags = Qt::AutoColor) const;
+ QT3_SUPPORT QImage convertBitOrder(Endian) const;
+ QT3_SUPPORT bool hasAlphaBuffer() const;
+ QT3_SUPPORT void setAlphaBuffer(bool);
+ QT3_SUPPORT uchar **jumpTable();
+ QT3_SUPPORT const uchar * const *jumpTable() const;
+ inline QT3_SUPPORT void reset() { *this = QImage(); }
+ static inline QT3_SUPPORT Endian systemByteOrder()
+ { return QSysInfo::ByteOrder == QSysInfo::BigEndian ? BigEndian : LittleEndian; }
+ inline QT3_SUPPORT QImage swapRGB() const { return rgbSwapped(); }
+ inline QT3_SUPPORT QImage mirror(bool horizontally = false, bool vertically = true) const
+ { return mirrored(horizontally, vertically); }
+ QT3_SUPPORT bool create(const QSize&, int depth, int numColors=0, Endian bitOrder=IgnoreEndian);
+ QT3_SUPPORT bool create(int width, int height, int depth, int numColors=0, Endian bitOrder=IgnoreEndian);
+ inline QT3_SUPPORT QImage xForm(const QMatrix &matrix) const { return transformed(QTransform(matrix)); }
+ inline QT3_SUPPORT QImage smoothScale(int w, int h, Qt::AspectRatioMode mode = Qt::IgnoreAspectRatio) const
+ { return scaled(QSize(w, h), mode, Qt::SmoothTransformation); }
+ inline QImage QT3_SUPPORT smoothScale(const QSize &s, Qt::AspectRatioMode mode = Qt::IgnoreAspectRatio) const
+ { return scaled(s, mode, Qt::SmoothTransformation); }
+ inline QT3_SUPPORT QImage scaleWidth(int w) const { return scaledToWidth(w); }
+ inline QT3_SUPPORT QImage scaleHeight(int h) const { return scaledToHeight(h); }
+ inline QT3_SUPPORT void invertPixels(bool invertAlpha) { invertAlpha ? invertPixels(InvertRgba) : invertPixels(InvertRgb); }
+ inline QT3_SUPPORT QImage copy(int x, int y, int w, int h, Qt::ImageConversionFlags) const
+ { return copy(QRect(x, y, w, h)); }
+ inline QT3_SUPPORT QImage copy(const QRect &rect, Qt::ImageConversionFlags) const
+ { return copy(rect); }
+ static QT3_SUPPORT Endian systemBitOrder();
+ inline QT3_SUPPORT_CONSTRUCTOR QImage(const QByteArray &data)
+ { d = 0; *this = QImage::fromData(data); }
+#endif
+
+protected:
+ virtual int metric(PaintDeviceMetric metric) const;
+
+private:
+ friend class QWSOnScreenSurface;
+ QImageData *d;
+
+ friend class QRasterPixmapData;
+ friend class QBlittablePixmapData;
+ friend class QPixmapCacheEntry;
+ friend Q_GUI_EXPORT qint64 qt_image_id(const QImage &image);
+ friend const QVector<QRgb> *qt_image_colortable(const QImage &image);
+
+public:
+ typedef QImageData * DataPtr;
+ inline DataPtr &data_ptr() { return d; }
+};
+
+Q_DECLARE_SHARED(QImage)
+Q_DECLARE_TYPEINFO(QImage, Q_MOVABLE_TYPE);
+
+// Inline functions...
+
+Q_GUI_EXPORT_INLINE bool QImage::valid(const QPoint &pt) const { return valid(pt.x(), pt.y()); }
+Q_GUI_EXPORT_INLINE int QImage::pixelIndex(const QPoint &pt) const { return pixelIndex(pt.x(), pt.y());}
+Q_GUI_EXPORT_INLINE QRgb QImage::pixel(const QPoint &pt) const { return pixel(pt.x(), pt.y()); }
+Q_GUI_EXPORT_INLINE void QImage::setPixel(const QPoint &pt, uint index_or_rgb) { setPixel(pt.x(), pt.y(), index_or_rgb); }
+
+// QImage stream functions
+
+#if !defined(QT_NO_DATASTREAM)
+Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QImage &);
+Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QImage &);
+#endif
+
+#ifdef QT3_SUPPORT
+Q_GUI_EXPORT QT3_SUPPORT void bitBlt(QImage* dst, int dx, int dy, const QImage* src,
+ int sx=0, int sy=0, int sw=-1, int sh=-1, Qt::ImageConversionFlags flags = Qt::AutoColor);
+#endif
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QIMAGE_H
diff --git a/src/gui/image/qimage_neon.cpp b/src/gui/image/qimage_neon.cpp
new file mode 100644
index 0000000000..89b4bab038
--- /dev/null
+++ b/src/gui/image/qimage_neon.cpp
@@ -0,0 +1,114 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qimage.h>
+#include <private/qimage_p.h>
+#include <private/qsimd_p.h>
+
+#ifdef QT_HAVE_NEON
+
+QT_BEGIN_NAMESPACE
+
+Q_GUI_EXPORT void QT_FASTCALL qt_convert_rgb888_to_rgb32_neon(quint32 *dst, const uchar *src, int len)
+{
+ if (!len)
+ return;
+
+ const quint32 *const end = dst + len;
+
+ // align dst on 64 bits
+ const int offsetToAlignOn8Bytes = (reinterpret_cast<quintptr>(dst) >> 2) & 0x1;
+ for (int i = 0; i < offsetToAlignOn8Bytes; ++i) {
+ *dst++ = qRgb(src[0], src[1], src[2]);
+ src += 3;
+ }
+
+ if ((len - offsetToAlignOn8Bytes) >= 8) {
+ const quint32 *const simdEnd = end - 7;
+ register uint8x8_t fullVector asm ("d3") = vdup_n_u8(0xff);
+ do {
+#if Q_BYTE_ORDER == Q_BIG_ENDIAN
+ asm volatile (
+ "vld3.8 { d4, d5, d6 }, [%[SRC]] !\n\t"
+ "vst4.8 { d3, d4, d5, d6 }, [%[DST],:64] !\n\t"
+ : [DST]"+r" (dst), [SRC]"+r" (src)
+ : "w"(fullVector)
+ : "memory", "d4", "d5", "d6"
+ );
+#else
+ asm volatile (
+ "vld3.8 { d0, d1, d2 }, [%[SRC]] !\n\t"
+ "vswp d0, d2\n\t"
+ "vst4.8 { d0, d1, d2, d3 }, [%[DST],:64] !\n\t"
+ : [DST]"+r" (dst), [SRC]"+r" (src)
+ : "w"(fullVector)
+ : "memory", "d0", "d1", "d2"
+ );
+#endif
+ } while (dst < simdEnd);
+ }
+
+ while (dst != end) {
+ *dst++ = qRgb(src[0], src[1], src[2]);
+ src += 3;
+ }
+}
+
+void convert_RGB888_to_RGB32_neon(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
+{
+ Q_ASSERT(src->format == QImage::Format_RGB888);
+ Q_ASSERT(dest->format == QImage::Format_RGB32 || dest->format == QImage::Format_ARGB32 || dest->format == QImage::Format_ARGB32_Premultiplied);
+ Q_ASSERT(src->width == dest->width);
+ Q_ASSERT(src->height == dest->height);
+
+ const uchar *src_data = (uchar *) src->data;
+ quint32 *dest_data = (quint32 *) dest->data;
+
+ for (int i = 0; i < src->height; ++i) {
+ qt_convert_rgb888_to_rgb32_neon(dest_data, src_data, src->width);
+ src_data += src->bytes_per_line;
+ dest_data = (quint32 *)((uchar*)dest_data + dest->bytes_per_line);
+ }
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_HAVE_NEON
diff --git a/src/gui/image/qimage_p.h b/src/gui/image/qimage_p.h
new file mode 100644
index 0000000000..db6620b39c
--- /dev/null
+++ b/src/gui/image/qimage_p.h
@@ -0,0 +1,154 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QIMAGE_P_H
+#define QIMAGE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qglobal.h>
+
+#include <QVector>
+
+#ifndef QT_NO_IMAGE_TEXT
+#include <QMap>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QImageWriter;
+
+struct Q_GUI_EXPORT QImageData { // internal image data
+ QImageData();
+ ~QImageData();
+ static QImageData *create(const QSize &size, QImage::Format format, int numColors = 0);
+ static QImageData *create(uchar *data, int w, int h, int bpl, QImage::Format format, bool readOnly);
+
+ QAtomicInt ref;
+
+ int width;
+ int height;
+ int depth;
+ int nbytes; // number of bytes data
+ QVector<QRgb> colortable;
+ uchar *data;
+#ifdef QT3_SUPPORT
+ uchar **jumptable;
+#endif
+ QImage::Format format;
+ int bytes_per_line;
+ int ser_no; // serial number
+ int detach_no;
+
+ qreal dpmx; // dots per meter X (or 0)
+ qreal dpmy; // dots per meter Y (or 0)
+ QPoint offset; // offset in pixels
+
+ uint own_data : 1;
+ uint ro_data : 1;
+ uint has_alpha_clut : 1;
+ uint is_cached : 1;
+
+ bool checkForAlphaPixels() const;
+
+ // Convert the image in-place, minimizing memory reallocation
+ // Return false if the conversion cannot be done in-place.
+ bool convertInPlace(QImage::Format newFormat, Qt::ImageConversionFlags);
+
+#ifndef QT_NO_IMAGE_TEXT
+ QMap<QString, QString> text;
+#endif
+ bool doImageIO(const QImage *image, QImageWriter* io, int quality) const;
+
+ QPaintEngine *paintEngine;
+};
+
+void qInitImageConversions();
+Q_GUI_EXPORT void qGamma_correct_back_to_linear_cs(QImage *image);
+
+inline int qt_depthForFormat(QImage::Format format)
+{
+ int depth = 0;
+ switch(format) {
+ case QImage::Format_Invalid:
+ case QImage::NImageFormats:
+ Q_ASSERT(false);
+ case QImage::Format_Mono:
+ case QImage::Format_MonoLSB:
+ depth = 1;
+ break;
+ case QImage::Format_Indexed8:
+ depth = 8;
+ break;
+ case QImage::Format_RGB32:
+ case QImage::Format_ARGB32:
+ case QImage::Format_ARGB32_Premultiplied:
+ depth = 32;
+ break;
+ case QImage::Format_RGB555:
+ case QImage::Format_RGB16:
+ case QImage::Format_RGB444:
+ case QImage::Format_ARGB4444_Premultiplied:
+ depth = 16;
+ break;
+ case QImage::Format_RGB666:
+ case QImage::Format_ARGB6666_Premultiplied:
+ case QImage::Format_ARGB8565_Premultiplied:
+ case QImage::Format_ARGB8555_Premultiplied:
+ case QImage::Format_RGB888:
+ depth = 24;
+ break;
+ }
+ return depth;
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/image/qimage_sse2.cpp b/src/gui/image/qimage_sse2.cpp
new file mode 100644
index 0000000000..e9bbb6fa36
--- /dev/null
+++ b/src/gui/image/qimage_sse2.cpp
@@ -0,0 +1,109 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qimage.h"
+#include <private/qimage_p.h>
+#include <private/qsimd_p.h>
+#include <private/qdrawhelper_p.h>
+#include <private/qdrawingprimitive_sse2_p.h>
+
+#ifdef QT_HAVE_SSE2
+
+QT_BEGIN_NAMESPACE
+
+bool convert_ARGB_to_ARGB_PM_inplace_sse2(QImageData *data, Qt::ImageConversionFlags)
+{
+ Q_ASSERT(data->format == QImage::Format_ARGB32);
+
+ // extra pixels on each line
+ const int spare = data->width & 3;
+ // width in pixels of the pad at the end of each line
+ const int pad = (data->bytes_per_line >> 2) - data->width;
+ const int iter = data->width >> 2;
+ int height = data->height;
+
+ const __m128i alphaMask = _mm_set1_epi32(0xff000000);
+ const __m128i nullVector = _mm_setzero_si128();
+ const __m128i half = _mm_set1_epi16(0x80);
+ const __m128i colorMask = _mm_set1_epi32(0x00ff00ff);
+
+ __m128i *d = reinterpret_cast<__m128i*>(data->data);
+ while (height--) {
+ const __m128i *end = d + iter;
+
+ for (; d != end; ++d) {
+ const __m128i srcVector = _mm_loadu_si128(d);
+ const __m128i srcVectorAlpha = _mm_and_si128(srcVector, alphaMask);
+ if (_mm_movemask_epi8(_mm_cmpeq_epi32(srcVectorAlpha, alphaMask)) == 0xffff) {
+ // opaque, data is unchanged
+ } else if (_mm_movemask_epi8(_mm_cmpeq_epi32(srcVectorAlpha, nullVector)) == 0xffff) {
+ // fully transparent
+ _mm_storeu_si128(d, nullVector);
+ } else {
+ __m128i alphaChannel = _mm_srli_epi32(srcVector, 24);
+ alphaChannel = _mm_or_si128(alphaChannel, _mm_slli_epi32(alphaChannel, 16));
+
+ __m128i result;
+ BYTE_MUL_SSE2(result, srcVector, alphaChannel, colorMask, half);
+ result = _mm_or_si128(_mm_andnot_si128(alphaMask, result), srcVectorAlpha);
+ _mm_storeu_si128(d, result);
+ }
+ }
+
+ QRgb *p = reinterpret_cast<QRgb*>(d);
+ QRgb *pe = p+spare;
+ for (; p != pe; ++p) {
+ if (*p < 0x00ffffff)
+ *p = 0;
+ else if (*p < 0xff000000)
+ *p = PREMUL(*p);
+ }
+
+ d = reinterpret_cast<__m128i*>(p+pad);
+ }
+
+ data->format = QImage::Format_ARGB32_Premultiplied;
+ return true;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_HAVE_SSE2
diff --git a/src/gui/image/qimage_ssse3.cpp b/src/gui/image/qimage_ssse3.cpp
new file mode 100644
index 0000000000..81e817eb73
--- /dev/null
+++ b/src/gui/image/qimage_ssse3.cpp
@@ -0,0 +1,149 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qimage.h>
+#include <private/qimage_p.h>
+#include <private/qsimd_p.h>
+
+#ifdef QT_HAVE_SSSE3
+
+QT_BEGIN_NAMESPACE
+
+// Convert a scanline of RGB888 (src) to RGB32 (dst)
+// src must be at least len * 3 bytes
+// dst must be at least len * 4 bytes
+Q_GUI_EXPORT void QT_FASTCALL qt_convert_rgb888_to_rgb32_ssse3(quint32 *dst, const uchar *src, int len)
+{
+ quint32 *const end = dst + len;
+
+ // Prologue, align dst to 16 bytes. The alignment is done on dst because it has 4 store()
+ // for each 3 load() of src.
+ const int offsetToAlignOn16Bytes = (4 - ((reinterpret_cast<quintptr>(dst) >> 2) & 0x3)) & 0x3;
+ const int prologLength = qMin(len, offsetToAlignOn16Bytes);
+
+ for (int i = 0; i < prologLength; ++i) {
+ *dst++ = qRgb(src[0], src[1], src[2]);
+ src += 3;
+ }
+
+ // Mask the 4 first colors of the RGB888 vector
+ const __m128i shuffleMask = _mm_set_epi8(0xff, 9, 10, 11, 0xff, 6, 7, 8, 0xff, 3, 4, 5, 0xff, 0, 1, 2);
+
+ // Mask the 4 last colors of a RGB888 vector with an offset of 1 (so the last 3 bytes are RGB)
+ const __m128i shuffleMaskEnd = _mm_set_epi8(0xff, 13, 14, 15, 0xff, 10, 11, 12, 0xff, 7, 8, 9, 0xff, 4, 5, 6);
+
+ // Mask to have alpha = 0xff
+ const __m128i alphaMask = _mm_set1_epi32(0xff000000);
+
+ __m128i *inVectorPtr = (__m128i *)src;
+ __m128i *dstVectorPtr = (__m128i *)dst;
+
+ const int simdRoundCount = (len - prologLength) / 16; // one iteration in the loop converts 16 pixels
+ for (int i = 0; i < simdRoundCount; ++i) {
+ /*
+ RGB888 has 5 pixels per vector, + 1 byte from the next pixel. The idea here is
+ to load vectors of RGB888 and use palignr to select a vector out of two vectors.
+
+ After 3 loads of RGB888 and 3 stores of RGB32, we have 4 pixels left in the last
+ vector of RGB888, we can mask it directly to get a last store or RGB32. After that,
+ the first next byte is a R, and we can loop for the next 16 pixels.
+
+ The conversion itself is done with a byte permutation (pshufb).
+ */
+ __m128i firstSrcVector = _mm_lddqu_si128(inVectorPtr);
+ __m128i outputVector = _mm_shuffle_epi8(firstSrcVector, shuffleMask);
+ _mm_store_si128(dstVectorPtr, _mm_or_si128(outputVector, alphaMask));
+ ++inVectorPtr;
+ ++dstVectorPtr;
+
+ // There are 4 unused bytes left in srcVector, we need to load the next 16 bytes
+ // and load the next input with palignr
+ __m128i secondSrcVector = _mm_lddqu_si128(inVectorPtr);
+ __m128i srcVector = _mm_alignr_epi8(secondSrcVector, firstSrcVector, 12);
+ outputVector = _mm_shuffle_epi8(srcVector, shuffleMask);
+ _mm_store_si128(dstVectorPtr, _mm_or_si128(outputVector, alphaMask));
+ ++inVectorPtr;
+ ++dstVectorPtr;
+ firstSrcVector = secondSrcVector;
+
+ // We now have 8 unused bytes left in firstSrcVector
+ secondSrcVector = _mm_lddqu_si128(inVectorPtr);
+ srcVector = _mm_alignr_epi8(secondSrcVector, firstSrcVector, 8);
+ outputVector = _mm_shuffle_epi8(srcVector, shuffleMask);
+ _mm_store_si128(dstVectorPtr, _mm_or_si128(outputVector, alphaMask));
+ ++inVectorPtr;
+ ++dstVectorPtr;
+
+ // There are now 12 unused bytes in firstSrcVector.
+ // We can mask them directly, almost there.
+ outputVector = _mm_shuffle_epi8(secondSrcVector, shuffleMaskEnd);
+ _mm_store_si128(dstVectorPtr, _mm_or_si128(outputVector, alphaMask));
+ ++dstVectorPtr;
+ }
+ src = (uchar *)inVectorPtr;
+ dst = (quint32 *)dstVectorPtr;
+
+ while (dst != end) {
+ *dst++ = qRgb(src[0], src[1], src[2]);
+ src += 3;
+ }
+}
+
+void convert_RGB888_to_RGB32_ssse3(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
+{
+ Q_ASSERT(src->format == QImage::Format_RGB888);
+ Q_ASSERT(dest->format == QImage::Format_RGB32 || dest->format == QImage::Format_ARGB32 || dest->format == QImage::Format_ARGB32_Premultiplied);
+ Q_ASSERT(src->width == dest->width);
+ Q_ASSERT(src->height == dest->height);
+
+ const uchar *src_data = (uchar *) src->data;
+ quint32 *dest_data = (quint32 *) dest->data;
+
+ for (int i = 0; i < src->height; ++i) {
+ qt_convert_rgb888_to_rgb32_ssse3(dest_data, src_data, src->width);
+ src_data += src->bytes_per_line;
+ dest_data = (quint32 *)((uchar*)dest_data + dest->bytes_per_line);
+ }
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_HAVE_SSSE3
diff --git a/src/gui/image/qimageiohandler.cpp b/src/gui/image/qimageiohandler.cpp
new file mode 100644
index 0000000000..da1d236998
--- /dev/null
+++ b/src/gui/image/qimageiohandler.cpp
@@ -0,0 +1,570 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \class QImageIOHandler
+ \brief The QImageIOHandler class defines the common image I/O
+ interface for all image formats in Qt.
+ \reentrant
+
+ Qt uses QImageIOHandler for reading and writing images through
+ QImageReader and QImageWriter. You can also derive from this class
+ to write your own image format handler using Qt's plugin mechanism.
+
+ Call setDevice() to assign a device to the handler, and
+ setFormat() to assign a format to it. One QImageIOHandler may
+ support more than one image format. canRead() returns true if an
+ image can be read from the device, and read() and write() return
+ true if reading or writing an image was completed successfully.
+
+ QImageIOHandler also has support for animations formats, through
+ the functions loopCount(), imageCount(), nextImageDelay() and
+ currentImageNumber().
+
+ In order to determine what options an image handler supports, Qt
+ will call supportsOption() and setOption(). Make sure to
+ reimplement these functions if you can provide support for any of
+ the options in the ImageOption enum.
+
+ To write your own image handler, you must at least reimplement
+ canRead() and read(). Then create a QImageIOPlugin that
+ can create the handler. Finally, install your plugin, and
+ QImageReader and QImageWriter will then automatically load the
+ plugin, and start using it.
+
+ \sa QImageIOPlugin, QImageReader, QImageWriter
+*/
+
+/*! \enum QImageIOHandler::ImageOption
+
+ This enum describes the different options supported by
+ QImageIOHandler. Some options are used to query an image for
+ properties, and others are used to toggle the way in which an
+ image should be written.
+
+ \value Size The original size of an image. A handler that supports
+ this option is expected to read the size of the image from the
+ image metadata, and return this size from option() as a QSize.
+
+ \value ClipRect The clip rect, or ROI (Region Of Interest). A
+ handler that supports this option is expected to only read the
+ provided QRect area from the original image in read(), before any
+ other transformation is applied.
+
+ \value ScaledSize The scaled size of the image. A handler that
+ supports this option is expected to scale the image to the
+ provided size (a QSize), after applying any clip rect
+ transformation (ClipRect). If the handler does not support this
+ option, QImageReader will perform the scaling after the image has
+ been read.
+
+ \value ScaledClipRect The scaled clip rect (or ROI, Region Of
+ Interest) of the image. A handler that supports this option is
+ expected to apply the provided clip rect (a QRect), after applying
+ any scaling (ScaleSize) or regular clipping (ClipRect). If the
+ handler does not support this option, QImageReader will apply the
+ scaled clip rect after the image has been read.
+
+ \value Description The image description. Some image formats,
+ such as GIF and PNG, allow embedding of text
+ or comments into the image data (e.g., for storing copyright
+ information). It's common that the text is stored in key-value
+ pairs, but some formats store all text in one continuous block.
+ QImageIOHandler returns the text as one
+ QString, where keys and values are separated by a ':', and
+ keys-value pairs are separated by two newlines (\\n\\n). For example,
+ "Title: Sunset\\n\\nAuthor: Jim Smith\\nSarah Jones\\n\\n". Formats that
+ store text in a single block can use "Description" as the key.
+
+ \value CompressionRatio The compression ratio of the image data. A
+ handler that supports this option is expected to set its
+ compression rate depending on the value of this option (an int)
+ when writing.
+
+ \value Gamma The gamma level of the image. A handler that supports
+ this option is expected to set the image gamma level depending on
+ the value of this option (a float) when writing.
+
+ \value Quality The quality level of the image. A handler that
+ supports this option is expected to set the image quality level
+ depending on the value of this option (an int) when writing.
+
+ \value Name The name of the image. A handler that supports this
+ option is expected to read the name from the image metadata and
+ return this as a QString, or when writing an image it is expected
+ to store the name in the image metadata.
+
+ \value SubType The subtype of the image. A handler that supports
+ this option can use the subtype value to help when reading and
+ writing images. For example, a PPM handler may have a subtype
+ value of "ppm" or "ppmraw".
+
+ \value IncrementalReading A handler that supports this option is
+ expected to read the image in several passes, as if it was an
+ animation. QImageReader will treat the image as an animation.
+
+ \value Endianness The endianness of the image. Certain image
+ formats can be stored as BigEndian or LittleEndian. A handler that
+ supports Endianness uses the value of this option to determine how
+ the image should be stored.
+
+ \value Animation Image formats that support animation return
+ true for this value in supportsOption(); otherwise, false is returned.
+
+ \value BackgroundColor Certain image formats allow the
+ background color to be specified. A handler that supports
+ BackgroundColor initializes the background color to this option
+ (a QColor) when reading an image.
+
+ \value ImageFormat The image's data format returned by the handler.
+ This can be any of the formats listed in QImage::Format.
+*/
+
+/*!
+ \class QImageIOPlugin
+ \brief The QImageIOPlugin class defines an interface for writing
+ an image format plugin.
+ \reentrant
+
+ \ingroup plugins
+
+ QImageIOPlugin is a factory for creating QImageIOHandler objects,
+ which are used internally by QImageReader and QImageWriter to add
+ support for different image formats to Qt.
+
+ Writing an image I/O plugin is achieved by subclassing this
+ base class, reimplementing the pure virtual functions capabilities(),
+ create(), and keys(), and exporting the class with the
+ Q_EXPORT_PLUGIN2() macro. See \l{How to Create Qt Plugins} for details.
+
+ An image format plugin can support three capabilities: reading (\l
+ CanRead), writing (\l CanWrite) and \e incremental reading (\l
+ CanReadIncremental). Reimplement capabilities() in you subclass to
+ expose the capabilities of your image format.
+
+ create() should create an instance of your QImageIOHandler
+ subclass, with the provided device and format properly set, and
+ return this handler. You must also reimplement keys() so that Qt
+ knows which image formats your plugin supports.
+
+ Different plugins can support different capabilities. For example,
+ you may have one plugin that supports reading the GIF format, and
+ another that supports writing. Qt will select the correct plugin
+ for the job, depending on the return value of capabilities(). If
+ several plugins support the same capability, Qt will select one
+ arbitrarily.
+
+ \sa QImageIOHandler, {How to Create Qt Plugins}
+*/
+
+/*!
+ \enum QImageIOPlugin::Capability
+
+ This enum describes the capabilities of a QImageIOPlugin.
+
+ \value CanRead The plugin can read images.
+ \value CanWrite The plugin can write images.
+ \value CanReadIncremental The plugin can read images incrementally.
+*/
+
+/*!
+ \class QImageIOHandlerFactoryInterface
+ \brief The QImageIOHandlerFactoryInterface class provides the factory
+ interface for QImageIOPlugin.
+ \reentrant
+
+ \internal
+
+ \sa QImageIOPlugin
+*/
+
+#include "qimageiohandler.h"
+
+#include <qbytearray.h>
+#include <qimage.h>
+#include <qvariant.h>
+
+QT_BEGIN_NAMESPACE
+
+class QIODevice;
+
+class QImageIOHandlerPrivate
+{
+ Q_DECLARE_PUBLIC(QImageIOHandler)
+public:
+ QImageIOHandlerPrivate(QImageIOHandler *q);
+ virtual ~QImageIOHandlerPrivate();
+
+ QIODevice *device;
+ mutable QByteArray format;
+
+ QImageIOHandler *q_ptr;
+};
+
+QImageIOHandlerPrivate::QImageIOHandlerPrivate(QImageIOHandler *q)
+{
+ device = 0;
+ q_ptr = q;
+}
+
+QImageIOHandlerPrivate::~QImageIOHandlerPrivate()
+{
+}
+
+/*!
+ Constructs a QImageIOHandler object.
+*/
+QImageIOHandler::QImageIOHandler()
+ : d_ptr(new QImageIOHandlerPrivate(this))
+{
+}
+
+/*! \internal
+
+ Constructs a QImageIOHandler object, using the private member \a
+ dd.
+*/
+QImageIOHandler::QImageIOHandler(QImageIOHandlerPrivate &dd)
+ : d_ptr(&dd)
+{
+}
+
+/*!
+ Destructs the QImageIOHandler object.
+*/
+QImageIOHandler::~QImageIOHandler()
+{
+}
+
+/*!
+ Sets the device of the QImageIOHandler to \a device. The image
+ handler will use this device when reading and writing images.
+
+ The device can only be set once and must be set before calling
+ canRead(), read(), write(), etc. If you need to read multiple
+ files, construct multiple instances of the appropriate
+ QImageIOHandler subclass.
+
+ \sa device()
+*/
+void QImageIOHandler::setDevice(QIODevice *device)
+{
+ Q_D(QImageIOHandler);
+ d->device = device;
+}
+
+/*!
+ Returns the device currently assigned to the QImageIOHandler. If
+ not device has been assigned, 0 is returned.
+*/
+QIODevice *QImageIOHandler::device() const
+{
+ Q_D(const QImageIOHandler);
+ return d->device;
+}
+
+/*!
+ Sets the format of the QImageIOHandler to \a format. The format is
+ most useful for handlers that support multiple image formats.
+
+ \sa format()
+*/
+void QImageIOHandler::setFormat(const QByteArray &format)
+{
+ Q_D(QImageIOHandler);
+ d->format = format;
+}
+
+/*!
+ Sets the format of the QImageIOHandler to \a format. The format is
+ most useful for handlers that support multiple image formats.
+
+ This function is declared const so that it can be called from canRead().
+
+ \sa format()
+*/
+void QImageIOHandler::setFormat(const QByteArray &format) const
+{
+ Q_D(const QImageIOHandler);
+ d->format = format;
+}
+
+/*!
+ Returns the format that is currently assigned to
+ QImageIOHandler. If no format has been assigned, an empty string
+ is returned.
+
+ \sa setFormat()
+*/
+QByteArray QImageIOHandler::format() const
+{
+ Q_D(const QImageIOHandler);
+ return d->format;
+}
+
+/*!
+ \fn bool QImageIOHandler::read(QImage *image)
+
+ Read an image from the device, and stores it in \a image.
+ Returns true if the image is successfully read; otherwise returns
+ false.
+
+ For image formats that support incremental loading, and for animation
+ formats, the image handler can assume that \a image points to the
+ previous frame.
+
+ \sa canRead()
+*/
+
+/*!
+ \fn bool QImageIOHandler::canRead() const
+
+ Returns true if an image can be read from the device (i.e., the
+ image format is supported, the device can be read from and the
+ initial header information suggests that the image can be read);
+ otherwise returns false.
+
+ When reimplementing canRead(), make sure that the I/O device
+ (device()) is left in its original state (e.g., by using peek()
+ rather than read()).
+
+ \sa read(), QIODevice::peek()
+*/
+
+/*!
+ \obsolete
+
+ Use format() instead.
+*/
+
+QByteArray QImageIOHandler::name() const
+{
+ return format();
+}
+
+/*!
+ Writes the image \a image to the assigned device. Returns true on
+ success; otherwise returns false.
+
+ The default implementation does nothing, and simply returns false.
+*/
+bool QImageIOHandler::write(const QImage &image)
+{
+ Q_UNUSED(image);
+ return false;
+}
+
+/*!
+ Sets the option \a option with the value \a value.
+
+ \sa option(), ImageOption
+*/
+void QImageIOHandler::setOption(ImageOption option, const QVariant &value)
+{
+ Q_UNUSED(option);
+ Q_UNUSED(value);
+}
+
+/*!
+ Returns the value assigned to \a option as a QVariant. The type of
+ the value depends on the option. For example, option(Size) returns
+ a QSize variant.
+
+ \sa setOption(), supportsOption()
+*/
+QVariant QImageIOHandler::option(ImageOption option) const
+{
+ Q_UNUSED(option);
+ return QVariant();
+}
+
+/*!
+ Returns true if the QImageIOHandler supports the option \a option;
+ otherwise returns false. For example, if the QImageIOHandler
+ supports the \l Size option, supportsOption(Size) must return
+ true.
+
+ \sa setOption(), option()
+*/
+bool QImageIOHandler::supportsOption(ImageOption option) const
+{
+ Q_UNUSED(option);
+ return false;
+}
+
+/*!
+ For image formats that support animation, this function returns
+ the sequence number of the current image in the animation. If
+ this function is called before any image is read(), -1 is
+ returned. The number of the first image in the sequence is 0.
+
+ If the image format does not support animation, 0 is returned.
+
+ \sa read()
+*/
+int QImageIOHandler::currentImageNumber() const
+{
+ return 0;
+}
+
+/*!
+ Returns the rect of the current image. If no rect is defined for the
+ image, and empty QRect() is returned.
+
+ This function is useful for animations, where only parts of the frame
+ may be updated at a time.
+*/
+QRect QImageIOHandler::currentImageRect() const
+{
+ return QRect();
+}
+
+/*!
+ For image formats that support animation, this function returns
+ the number of images in the animation. If the image format does
+ not support animation, or if it is unable to determine the number
+ of images, 0 is returned.
+
+ The default implementation returns 1 if canRead() returns true;
+ otherwise 0 is returned.
+*/
+int QImageIOHandler::imageCount() const
+{
+ return canRead() ? 1 : 0;
+}
+
+/*!
+ For image formats that support animation, this function jumps to the
+ next image.
+
+ The default implementation does nothing, and returns false.
+*/
+bool QImageIOHandler::jumpToNextImage()
+{
+ return false;
+}
+
+/*!
+ For image formats that support animation, this function jumps to the image
+ whose sequence number is \a imageNumber. The next call to read() will
+ attempt to read this image.
+
+ The default implementation does nothing, and returns false.
+*/
+bool QImageIOHandler::jumpToImage(int imageNumber)
+{
+ Q_UNUSED(imageNumber);
+ return false;
+}
+
+/*!
+ For image formats that support animation, this function returns
+ the number of times the animation should loop. If the image format
+ does not support animation, 0 is returned.
+*/
+int QImageIOHandler::loopCount() const
+{
+ return 0;
+}
+
+/*!
+ For image formats that support animation, this function returns
+ the number of milliseconds to wait until reading the next
+ image. If the image format does not support animation, 0 is
+ returned.
+*/
+int QImageIOHandler::nextImageDelay() const
+{
+ return 0;
+}
+
+/*!
+ Constructs an image plugin with the given \a parent. This is
+ invoked automatically by the Q_EXPORT_PLUGIN2() macro.
+*/
+QImageIOPlugin::QImageIOPlugin(QObject *parent)
+ : QObject(parent)
+{
+}
+
+/*!
+ Destroys the picture format plugin.
+
+ You never have to call this explicitly. Qt destroys a plugin
+ automatically when it is no longer used.
+*/
+QImageIOPlugin::~QImageIOPlugin()
+{
+}
+
+/*! \fn QImageIOPlugin::capabilities(QIODevice *device, const QByteArray &format) const
+
+ Returns the capabilities on the plugin, based on the data in \a
+ device and the format \a format. For example, if the
+ QImageIOHandler supports the BMP format, and the data in the
+ device starts with the characters "BM", this function should
+ return \l CanRead. If \a format is "bmp" and the handler supports
+ both reading and writing, this function should return \l CanRead |
+ \l CanWrite.
+*/
+
+/*!
+ \fn QImageIOPlugin::keys() const
+
+ Returns the list of image keys this plugin supports.
+
+ These keys are usually the names of the image formats that are implemented
+ in the plugin (e.g., "jpg" or "gif").
+
+ \sa capabilities()
+*/
+
+/*!
+ \fn QImageIOHandler *QImageIOPlugin::create(QIODevice *device, const QByteArray &format) const
+
+ Creates and returns a QImageIOHandler subclass, with \a device
+ and \a format set. The \a format must come from the list returned by keys().
+ Format names are case sensitive.
+
+ \sa keys()
+*/
+
+QT_END_NAMESPACE
diff --git a/src/gui/image/qimageiohandler.h b/src/gui/image/qimageiohandler.h
new file mode 100644
index 0000000000..05e1853f97
--- /dev/null
+++ b/src/gui/image/qimageiohandler.h
@@ -0,0 +1,152 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QIMAGEIOHANDLER_H
+#define QIMAGEIOHANDLER_H
+
+#include <QtCore/qplugin.h>
+#include <QtCore/qfactoryinterface.h>
+#include <QtCore/qscopedpointer.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QImage;
+class QRect;
+class QSize;
+class QVariant;
+
+class QImageIOHandlerPrivate;
+class Q_GUI_EXPORT QImageIOHandler
+{
+ Q_DECLARE_PRIVATE(QImageIOHandler)
+public:
+ QImageIOHandler();
+ virtual ~QImageIOHandler();
+
+ void setDevice(QIODevice *device);
+ QIODevice *device() const;
+
+ void setFormat(const QByteArray &format);
+ void setFormat(const QByteArray &format) const;
+ QByteArray format() const;
+
+ virtual QByteArray name() const;
+
+ virtual bool canRead() const = 0;
+ virtual bool read(QImage *image) = 0;
+ virtual bool write(const QImage &image);
+
+ enum ImageOption {
+ Size,
+ ClipRect,
+ Description,
+ ScaledClipRect,
+ ScaledSize,
+ CompressionRatio,
+ Gamma,
+ Quality,
+ Name,
+ SubType,
+ IncrementalReading,
+ Endianness,
+ Animation,
+ BackgroundColor,
+ ImageFormat
+ };
+ virtual QVariant option(ImageOption option) const;
+ virtual void setOption(ImageOption option, const QVariant &value);
+ virtual bool supportsOption(ImageOption option) const;
+
+ // incremental loading
+ virtual bool jumpToNextImage();
+ virtual bool jumpToImage(int imageNumber);
+ virtual int loopCount() const;
+ virtual int imageCount() const;
+ virtual int nextImageDelay() const;
+ virtual int currentImageNumber() const;
+ virtual QRect currentImageRect() const;
+
+protected:
+ QImageIOHandler(QImageIOHandlerPrivate &dd);
+ QScopedPointer<QImageIOHandlerPrivate> d_ptr;
+private:
+ Q_DISABLE_COPY(QImageIOHandler)
+};
+
+struct Q_GUI_EXPORT QImageIOHandlerFactoryInterface : public QFactoryInterface
+{
+ virtual QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const = 0;
+};
+
+#define QImageIOHandlerFactoryInterface_iid "com.trolltech.Qt.QImageIOHandlerFactoryInterface"
+Q_DECLARE_INTERFACE(QImageIOHandlerFactoryInterface, QImageIOHandlerFactoryInterface_iid)
+
+class Q_GUI_EXPORT QImageIOPlugin : public QObject, public QImageIOHandlerFactoryInterface
+{
+ Q_OBJECT
+ Q_INTERFACES(QImageIOHandlerFactoryInterface:QFactoryInterface)
+public:
+ explicit QImageIOPlugin(QObject *parent = 0);
+ virtual ~QImageIOPlugin();
+
+ enum Capability {
+ CanRead = 0x1,
+ CanWrite = 0x2,
+ CanReadIncremental = 0x4
+ };
+ Q_DECLARE_FLAGS(Capabilities, Capability)
+
+ virtual Capabilities capabilities(QIODevice *device, const QByteArray &format) const = 0;
+ virtual QStringList keys() const = 0;
+ virtual QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const = 0;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QImageIOPlugin::Capabilities)
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QIMAGEIOHANDLER_H
diff --git a/src/gui/image/qimagepixmapcleanuphooks.cpp b/src/gui/image/qimagepixmapcleanuphooks.cpp
new file mode 100644
index 0000000000..cd7b4682b7
--- /dev/null
+++ b/src/gui/image/qimagepixmapcleanuphooks.cpp
@@ -0,0 +1,163 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qimagepixmapcleanuphooks_p.h"
+#include "private/qpixmapdata_p.h"
+#include "private/qimage_p.h"
+
+
+QT_BEGIN_NAMESPACE
+
+// Legacy, single instance hooks: ### Qt 5: remove
+typedef void (*_qt_pixmap_cleanup_hook)(int);
+typedef void (*_qt_pixmap_cleanup_hook_64)(qint64);
+typedef void (*_qt_image_cleanup_hook)(int);
+Q_GUI_EXPORT _qt_pixmap_cleanup_hook qt_pixmap_cleanup_hook = 0;
+Q_GUI_EXPORT _qt_pixmap_cleanup_hook_64 qt_pixmap_cleanup_hook_64 = 0;
+Q_GUI_EXPORT _qt_image_cleanup_hook qt_image_cleanup_hook = 0;
+Q_GUI_EXPORT _qt_image_cleanup_hook_64 qt_image_cleanup_hook_64 = 0;
+
+Q_GLOBAL_STATIC(QImagePixmapCleanupHooks, qt_image_and_pixmap_cleanup_hooks)
+
+QImagePixmapCleanupHooks *QImagePixmapCleanupHooks::instance()
+{
+ return qt_image_and_pixmap_cleanup_hooks();
+}
+
+void QImagePixmapCleanupHooks::addPixmapDataModificationHook(_qt_pixmap_cleanup_hook_pmd hook)
+{
+ pixmapModificationHooks.append(hook);
+}
+
+void QImagePixmapCleanupHooks::addPixmapDataDestructionHook(_qt_pixmap_cleanup_hook_pmd hook)
+{
+ pixmapDestructionHooks.append(hook);
+}
+
+
+void QImagePixmapCleanupHooks::addImageHook(_qt_image_cleanup_hook_64 hook)
+{
+ imageHooks.append(hook);
+}
+
+void QImagePixmapCleanupHooks::removePixmapDataModificationHook(_qt_pixmap_cleanup_hook_pmd hook)
+{
+ pixmapModificationHooks.removeAll(hook);
+}
+
+void QImagePixmapCleanupHooks::removePixmapDataDestructionHook(_qt_pixmap_cleanup_hook_pmd hook)
+{
+ pixmapDestructionHooks.removeAll(hook);
+}
+
+void QImagePixmapCleanupHooks::removeImageHook(_qt_image_cleanup_hook_64 hook)
+{
+ imageHooks.removeAll(hook);
+}
+
+void QImagePixmapCleanupHooks::executePixmapDataModificationHooks(QPixmapData* pmd)
+{
+ QImagePixmapCleanupHooks *h = qt_image_and_pixmap_cleanup_hooks();
+ // the global destructor for the pixmap and image hooks might have
+ // been called already if the app is "leaking" global
+ // pixmaps/images
+ if (!h)
+ return;
+ for (int i = 0; i < h->pixmapModificationHooks.count(); ++i)
+ h->pixmapModificationHooks[i](pmd);
+
+ if (qt_pixmap_cleanup_hook_64)
+ qt_pixmap_cleanup_hook_64(pmd->cacheKey());
+}
+
+void QImagePixmapCleanupHooks::executePixmapDataDestructionHooks(QPixmapData* pmd)
+{
+ QImagePixmapCleanupHooks *h = qt_image_and_pixmap_cleanup_hooks();
+ // the global destructor for the pixmap and image hooks might have
+ // been called already if the app is "leaking" global
+ // pixmaps/images
+ if (!h)
+ return;
+ for (int i = 0; i < h->pixmapDestructionHooks.count(); ++i)
+ h->pixmapDestructionHooks[i](pmd);
+
+ if (qt_pixmap_cleanup_hook_64)
+ qt_pixmap_cleanup_hook_64(pmd->cacheKey());
+}
+
+void QImagePixmapCleanupHooks::executeImageHooks(qint64 key)
+{
+ for (int i = 0; i < qt_image_and_pixmap_cleanup_hooks()->imageHooks.count(); ++i)
+ qt_image_and_pixmap_cleanup_hooks()->imageHooks[i](key);
+
+ if (qt_image_cleanup_hook_64)
+ qt_image_cleanup_hook_64(key);
+}
+
+
+void QImagePixmapCleanupHooks::enableCleanupHooks(QPixmapData *pixmapData)
+{
+ pixmapData->is_cached = true;
+}
+
+void QImagePixmapCleanupHooks::enableCleanupHooks(const QPixmap &pixmap)
+{
+ enableCleanupHooks(const_cast<QPixmap &>(pixmap).data_ptr().data());
+}
+
+void QImagePixmapCleanupHooks::enableCleanupHooks(const QImage &image)
+{
+ const_cast<QImage &>(image).data_ptr()->is_cached = true;
+}
+
+bool QImagePixmapCleanupHooks::isImageCached(const QImage &image)
+{
+ return const_cast<QImage &>(image).data_ptr()->is_cached;
+}
+
+bool QImagePixmapCleanupHooks::isPixmapCached(const QPixmap &pixmap)
+{
+ return const_cast<QPixmap&>(pixmap).data_ptr().data()->is_cached;
+}
+
+
+
+QT_END_NAMESPACE
diff --git a/src/gui/image/qimagepixmapcleanuphooks_p.h b/src/gui/image/qimagepixmapcleanuphooks_p.h
new file mode 100644
index 0000000000..aa6a986f08
--- /dev/null
+++ b/src/gui/image/qimagepixmapcleanuphooks_p.h
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QIMAGEPIXMAP_CLEANUPHOOKS_P_H
+#define QIMAGEPIXMAP_CLEANUPHOOKS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/qpixmap.h>
+
+QT_BEGIN_NAMESPACE
+
+typedef void (*_qt_image_cleanup_hook_64)(qint64);
+typedef void (*_qt_pixmap_cleanup_hook_pmd)(QPixmapData*);
+
+
+class QImagePixmapCleanupHooks;
+
+class Q_GUI_EXPORT QImagePixmapCleanupHooks
+{
+public:
+ static QImagePixmapCleanupHooks *instance();
+
+ static void enableCleanupHooks(const QImage &image);
+ static void enableCleanupHooks(const QPixmap &pixmap);
+ static void enableCleanupHooks(QPixmapData *pixmapData);
+
+ static bool isImageCached(const QImage &image);
+ static bool isPixmapCached(const QPixmap &pixmap);
+
+ // Gets called when a pixmap data is about to be modified:
+ void addPixmapDataModificationHook(_qt_pixmap_cleanup_hook_pmd);
+
+ // Gets called when a pixmap data is about to be destroyed:
+ void addPixmapDataDestructionHook(_qt_pixmap_cleanup_hook_pmd);
+
+ // Gets called when an image is about to be modified or destroyed:
+ void addImageHook(_qt_image_cleanup_hook_64);
+
+ void removePixmapDataModificationHook(_qt_pixmap_cleanup_hook_pmd);
+ void removePixmapDataDestructionHook(_qt_pixmap_cleanup_hook_pmd);
+ void removeImageHook(_qt_image_cleanup_hook_64);
+
+ static void executePixmapDataModificationHooks(QPixmapData*);
+ static void executePixmapDataDestructionHooks(QPixmapData*);
+ static void executeImageHooks(qint64 key);
+
+private:
+ QList<_qt_image_cleanup_hook_64> imageHooks;
+ QList<_qt_pixmap_cleanup_hook_pmd> pixmapModificationHooks;
+ QList<_qt_pixmap_cleanup_hook_pmd> pixmapDestructionHooks;
+};
+
+QT_END_NAMESPACE
+
+#endif // QIMAGEPIXMAP_CLEANUPHOOKS_P_H
diff --git a/src/gui/image/qimagereader.cpp b/src/gui/image/qimagereader.cpp
new file mode 100644
index 0000000000..0a0dc35988
--- /dev/null
+++ b/src/gui/image/qimagereader.cpp
@@ -0,0 +1,1515 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//#define QIMAGEREADER_DEBUG
+
+/*!
+ \class QImageReader
+ \brief The QImageReader class provides a format independent interface
+ for reading images from files or other devices.
+
+ \reentrant
+ \ingroup painting
+ \ingroup io
+
+ The most common way to read images is through QImage and QPixmap's
+ constructors, or by calling QImage::load() and
+ QPixmap::load(). QImageReader is a specialized class which gives
+ you more control when reading images. For example, you can read an
+ image into a specific size by calling setScaledSize(), and you can
+ select a clip rect, effectively loading only parts of an image, by
+ calling setClipRect(). Depending on the underlying support in the
+ image format, this can save memory and speed up loading of images.
+
+ To read an image, you start by constructing a QImageReader object.
+ Pass either a file name or a device pointer, and the image format
+ to QImageReader's constructor. You can then set several options,
+ such as the clip rect (by calling setClipRect()) and scaled size
+ (by calling setScaledSize()). canRead() returns the image if the
+ QImageReader can read the image (i.e., the image format is
+ supported and the device is open for reading). Call read() to read
+ the image.
+
+ If any error occurs when reading the image, read() will return a
+ null QImage. You can then call error() to find the type of error
+ that occurred, or errorString() to get a human readable
+ description of what went wrong.
+
+ Call supportedImageFormats() for a list of formats that
+ QImageReader can read. QImageReader supports all built-in image
+ formats, in addition to any image format plugins that support
+ reading.
+
+ QImageReader autodetects the image format by default, by looking at the
+ provided (optional) format string, the file name suffix, and the data
+ stream contents. You can enable or disable this feature, by calling
+ setAutoDetectImageFormat().
+
+ \sa QImageWriter, QImageIOHandler, QImageIOPlugin
+*/
+
+/*!
+ \enum QImageReader::ImageReaderError
+
+ This enum describes the different types of errors that can occur
+ when reading images with QImageReader.
+
+ \value FileNotFoundError QImageReader was used with a file name,
+ but not file was found with that name. This can also happen if the
+ file name contained no extension, and the file with the correct
+ extension is not supported by Qt.
+
+ \value DeviceError QImageReader encountered a device error when
+ reading the image. You can consult your particular device for more
+ details on what went wrong.
+
+ \value UnsupportedFormatError Qt does not support the requested
+ image format.
+
+ \value InvalidDataError The image data was invalid, and
+ QImageReader was unable to read an image from it. The can happen
+ if the image file is damaged.
+
+ \value UnknownError An unknown error occurred. If you get this
+ value after calling read(), it is most likely caused by a bug in
+ QImageReader.
+*/
+#include "qimagereader.h"
+
+#include <qbytearray.h>
+#ifdef QIMAGEREADER_DEBUG
+#include <qdebug.h>
+#endif
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qimage.h>
+#include <qimageiohandler.h>
+#include <qlist.h>
+#include <qrect.h>
+#include <qset.h>
+#include <qsize.h>
+#include <qcolor.h>
+#include <qvariant.h>
+
+// factory loader
+#include <qcoreapplication.h>
+#include <private/qfactoryloader_p.h>
+
+// image handlers
+#include <private/qbmphandler_p.h>
+#include <private/qppmhandler_p.h>
+#include <private/qxbmhandler_p.h>
+#include <private/qxpmhandler_p.h>
+#ifndef QT_NO_IMAGEFORMAT_PNG
+#include <private/qpnghandler_p.h>
+#endif
+#ifndef QT_NO_IMAGEFORMAT_JPEG
+#include <private/qjpeghandler_p.h>
+#endif
+#ifndef QT_NO_IMAGEFORMAT_MNG
+#include <private/qmnghandler_p.h>
+#endif
+#ifndef QT_NO_IMAGEFORMAT_TIFF
+#include <private/qtiffhandler_p.h>
+#endif
+#ifdef QT_BUILTIN_GIF_READER
+#include <private/qgifhandler_p.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_LIBRARY
+Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
+ (QImageIOHandlerFactoryInterface_iid, QLatin1String("/imageformats")))
+#endif
+
+enum _qt_BuiltInFormatType {
+#ifndef QT_NO_IMAGEFORMAT_PNG
+ _qt_PngFormat,
+#endif
+#ifndef QT_NO_IMAGEFORMAT_JPEG
+ _qt_JpgFormat,
+#endif
+#ifndef QT_NO_IMAGEFORMAT_MNG
+ _qt_MngFormat,
+#endif
+#ifndef QT_NO_IMAGEFORMAT_TIFF
+ _qt_TifFormat,
+#endif
+#ifdef QT_BUILTIN_GIF_READER
+ _qt_GifFormat,
+#endif
+ _qt_BmpFormat,
+#ifndef QT_NO_IMAGEFORMAT_PPM
+ _qt_PpmFormat,
+ _qt_PgmFormat,
+ _qt_PbmFormat,
+#endif
+#ifndef QT_NO_IMAGEFORMAT_XBM
+ _qt_XbmFormat,
+#endif
+#ifndef QT_NO_IMAGEFORMAT_XPM
+ _qt_XpmFormat,
+#endif
+ _qt_NumFormats,
+ _qt_NoFormat = -1
+};
+
+struct _qt_BuiltInFormatStruct
+{
+ _qt_BuiltInFormatType type;
+ const char *extension;
+};
+
+static const _qt_BuiltInFormatStruct _qt_BuiltInFormats[] = {
+#ifndef QT_NO_IMAGEFORMAT_PNG
+ {_qt_PngFormat, "png"},
+#endif
+#ifndef QT_NO_IMAGEFORMAT_JPEG
+ {_qt_JpgFormat, "jpg"},
+#endif
+#ifndef QT_NO_IMAGEFORMAT_MNG
+ {_qt_MngFormat, "mng"},
+#endif
+#ifndef QT_NO_IMAGEFORMAT_TIFF
+ {_qt_TifFormat, "tif"},
+#endif
+#ifdef QT_BUILTIN_GIF_READER
+ {_qt_GifFormat, "gif"},
+#endif
+ {_qt_BmpFormat, "bmp"},
+#ifndef QT_NO_IMAGEFORMAT_PPM
+ {_qt_PpmFormat, "ppm"},
+ {_qt_PgmFormat, "pgm"},
+ {_qt_PbmFormat, "pbm"},
+#endif
+#ifndef QT_NO_IMAGEFORMAT_XBM
+ {_qt_XbmFormat, "xbm"},
+#endif
+#ifndef QT_NO_IMAGEFORMAT_XPM
+ {_qt_XpmFormat, "xpm"},
+#endif
+ {_qt_NoFormat, ""}
+};
+
+static QImageIOHandler *createReadHandlerHelper(QIODevice *device,
+ const QByteArray &format,
+ bool autoDetectImageFormat,
+ bool ignoresFormatAndExtension)
+{
+ if (!autoDetectImageFormat && format.isEmpty())
+ return 0;
+
+ QByteArray form = format.toLower();
+ QImageIOHandler *handler = 0;
+
+#ifndef QT_NO_LIBRARY
+ // check if we have plugins that support the image format
+ QFactoryLoader *l = loader();
+ QStringList keys = l->keys();
+#endif
+ QByteArray suffix;
+
+#ifdef QIMAGEREADER_DEBUG
+ qDebug() << "QImageReader::createReadHandler( device =" << (void *)device << ", format =" << format << "),"
+ << keys.size() << "plugins available: " << keys;
+#endif
+
+#ifndef QT_NO_LIBRARY
+ int suffixPluginIndex = -1;
+ if (device && format.isEmpty() && autoDetectImageFormat && !ignoresFormatAndExtension) {
+ // if there's no format, see if \a device is a file, and if so, find
+ // the file suffix and find support for that format among our plugins.
+ // this allows plugins to override our built-in handlers.
+ if (QFile *file = qobject_cast<QFile *>(device)) {
+#ifdef QIMAGEREADER_DEBUG
+ qDebug() << "QImageReader::createReadHandler: device is a file:" << file->fileName();
+#endif
+ if (!(suffix = QFileInfo(file->fileName()).suffix().toLower().toLatin1()).isEmpty()) {
+ int index = keys.indexOf(QString::fromLatin1(suffix));
+ if (index != -1) {
+#ifdef QIMAGEREADER_DEBUG
+ qDebug() << "QImageReader::createReadHandler: suffix recognized; the"
+ << suffix << "plugin might be able to read this";
+#endif
+ suffixPluginIndex = index;
+ }
+ }
+ }
+ }
+#endif // QT_NO_LIBRARY
+
+ QByteArray testFormat = !form.isEmpty() ? form : suffix;
+
+ if (ignoresFormatAndExtension)
+ testFormat = QByteArray();
+
+#ifndef QT_NO_LIBRARY
+ if (suffixPluginIndex != -1) {
+ // check if the plugin that claims support for this format can load
+ // from this device with this format.
+ const qint64 pos = device ? device->pos() : 0;
+ QImageIOPlugin *plugin = qobject_cast<QImageIOPlugin *>(l->instance(QString::fromLatin1(suffix)));
+ if (plugin && plugin->capabilities(device, testFormat) & QImageIOPlugin::CanRead) {
+ handler = plugin->create(device, testFormat);
+#ifdef QIMAGEREADER_DEBUG
+ qDebug() << "QImageReader::createReadHandler: using the" << suffix
+ << "plugin";
+#endif
+ }
+ if (device && !device->isSequential())
+ device->seek(pos);
+ }
+
+ if (!handler && !testFormat.isEmpty() && !ignoresFormatAndExtension) {
+ // check if any plugin supports the format (they are not allowed to
+ // read from the device yet).
+ const qint64 pos = device ? device->pos() : 0;
+
+ if (autoDetectImageFormat) {
+ for (int i = 0; i < keys.size(); ++i) {
+ if (i != suffixPluginIndex) {
+ QImageIOPlugin *plugin = qobject_cast<QImageIOPlugin *>(l->instance(keys.at(i)));
+ if (plugin && plugin->capabilities(device, testFormat) & QImageIOPlugin::CanRead) {
+#ifdef QIMAGEREADER_DEBUG
+ qDebug() << "QImageReader::createReadHandler: the" << keys.at(i) << "plugin can read this format";
+#endif
+ handler = plugin->create(device, testFormat);
+ break;
+ }
+ }
+ }
+ } else {
+ QImageIOPlugin *plugin = qobject_cast<QImageIOPlugin *>(l->instance(QLatin1String(testFormat)));
+ if (plugin && plugin->capabilities(device, testFormat) & QImageIOPlugin::CanRead) {
+#ifdef QIMAGEREADER_DEBUG
+ qDebug() << "QImageReader::createReadHandler: the" << testFormat << "plugin can read this format";
+#endif
+ handler = plugin->create(device, testFormat);
+ }
+ }
+ if (device && !device->isSequential())
+ device->seek(pos);
+ }
+
+#endif // QT_NO_LIBRARY
+
+ // if we don't have a handler yet, check if we have built-in support for
+ // the format
+ if (!handler && !testFormat.isEmpty()) {
+ if (false) {
+#ifndef QT_NO_IMAGEFORMAT_PNG
+ } else if (testFormat == "png") {
+ handler = new QPngHandler;
+#endif
+#ifndef QT_NO_IMAGEFORMAT_JPEG
+ } else if (testFormat == "jpg" || testFormat == "jpeg") {
+ handler = new QJpegHandler;
+#endif
+#ifndef QT_NO_IMAGEFORMAT_MNG
+ } else if (testFormat == "mng") {
+ handler = new QMngHandler;
+#endif
+#ifndef QT_NO_IMAGEFORMAT_TIFF
+ } else if (testFormat == "tif" || testFormat == "tiff") {
+ handler = new QTiffHandler;
+#endif
+#ifdef QT_BUILTIN_GIF_READER
+ } else if (testFormat == "gif") {
+ handler = new QGifHandler;
+#endif
+#ifndef QT_NO_IMAGEFORMAT_BMP
+ } else if (testFormat == "bmp") {
+ handler = new QBmpHandler;
+#endif
+#ifndef QT_NO_IMAGEFORMAT_XPM
+ } else if (testFormat == "xpm") {
+ handler = new QXpmHandler;
+#endif
+#ifndef QT_NO_IMAGEFORMAT_XBM
+ } else if (testFormat == "xbm") {
+ handler = new QXbmHandler;
+ handler->setOption(QImageIOHandler::SubType, testFormat);
+#endif
+#ifndef QT_NO_IMAGEFORMAT_PPM
+ } else if (testFormat == "pbm" || testFormat == "pbmraw" || testFormat == "pgm"
+ || testFormat == "pgmraw" || testFormat == "ppm" || testFormat == "ppmraw") {
+ handler = new QPpmHandler;
+ handler->setOption(QImageIOHandler::SubType, testFormat);
+#endif
+ }
+
+#ifdef QIMAGEREADER_DEBUG
+ if (handler)
+ qDebug() << "QImageReader::createReadHandler: using the built-in handler for" << testFormat;
+#endif
+ }
+
+#ifndef QT_NO_LIBRARY
+ if (!handler && (autoDetectImageFormat || ignoresFormatAndExtension)) {
+ // check if any of our plugins recognize the file from its contents.
+ const qint64 pos = device ? device->pos() : 0;
+ for (int i = 0; i < keys.size(); ++i) {
+ if (i != suffixPluginIndex) {
+ QImageIOPlugin *plugin = qobject_cast<QImageIOPlugin *>(l->instance(keys.at(i)));
+ if (plugin && plugin->capabilities(device, QByteArray()) & QImageIOPlugin::CanRead) {
+ handler = plugin->create(device, testFormat);
+#ifdef QIMAGEREADER_DEBUG
+ qDebug() << "QImageReader::createReadHandler: the" << keys.at(i) << "plugin can read this data";
+#endif
+ break;
+ }
+ }
+ }
+ if (device && !device->isSequential())
+ device->seek(pos);
+ }
+#endif // QT_NO_LIBRARY
+
+ if (!handler && (autoDetectImageFormat || ignoresFormatAndExtension)) {
+ // check if any of our built-in handlers recognize the file from its
+ // contents.
+ int currentFormat = 0;
+ if (!suffix.isEmpty()) {
+ // If reading from a file with a suffix, start testing our
+ // built-in handler for that suffix first.
+ for (int i = 0; i < _qt_NumFormats; ++i) {
+ if (_qt_BuiltInFormats[i].extension == suffix) {
+ currentFormat = i;
+ break;
+ }
+ }
+ }
+
+ QByteArray subType;
+ int numFormats = _qt_NumFormats;
+ while (device && numFormats >= 0) {
+ const _qt_BuiltInFormatStruct *formatStruct = &_qt_BuiltInFormats[currentFormat];
+
+ const qint64 pos = device->pos();
+ switch (formatStruct->type) {
+#ifndef QT_NO_IMAGEFORMAT_PNG
+ case _qt_PngFormat:
+ if (QPngHandler::canRead(device))
+ handler = new QPngHandler;
+ break;
+#endif
+#ifndef QT_NO_IMAGEFORMAT_JPEG
+ case _qt_JpgFormat:
+ if (QJpegHandler::canRead(device))
+ handler = new QJpegHandler;
+ break;
+#endif
+#ifndef QT_NO_IMAGEFORMAT_MNG
+ case _qt_MngFormat:
+ if (QMngHandler::canRead(device))
+ handler = new QMngHandler;
+ break;
+#endif
+#ifndef QT_NO_IMAGEFORMAT_TIFF
+ case _qt_TifFormat:
+ if (QTiffHandler::canRead(device))
+ handler = new QTiffHandler;
+ break;
+#endif
+#ifdef QT_BUILTIN_GIF_READER
+ case _qt_GifFormat:
+ if (QGifHandler::canRead(device))
+ handler = new QGifHandler;
+ break;
+#endif
+#ifndef QT_NO_IMAGEFORMAT_BMP
+ case _qt_BmpFormat:
+ if (QBmpHandler::canRead(device))
+ handler = new QBmpHandler;
+ break;
+#endif
+#ifndef QT_NO_IMAGEFORMAT_XPM
+ case _qt_XpmFormat:
+ if (QXpmHandler::canRead(device))
+ handler = new QXpmHandler;
+ break;
+#endif
+#ifndef QT_NO_IMAGEFORMAT_PPM
+ case _qt_PbmFormat:
+ case _qt_PgmFormat:
+ case _qt_PpmFormat:
+ if (QPpmHandler::canRead(device, &subType)) {
+ handler = new QPpmHandler;
+ handler->setOption(QImageIOHandler::SubType, subType);
+ }
+ break;
+#endif
+#ifndef QT_NO_IMAGEFORMAT_XBM
+ case _qt_XbmFormat:
+ if (QXbmHandler::canRead(device))
+ handler = new QXbmHandler;
+ break;
+#endif
+ default:
+ break;
+ }
+ if (!device->isSequential())
+ device->seek(pos);
+
+ if (handler) {
+#ifdef QIMAGEREADER_DEBUG
+ qDebug() << "QImageReader::createReadHandler: the" << formatStruct->extension
+ << "built-in handler can read this data";
+#endif
+ break;
+ }
+
+ --numFormats;
+ ++currentFormat;
+ currentFormat %= _qt_NumFormats;
+ }
+ }
+
+ if (!handler) {
+#ifdef QIMAGEREADER_DEBUG
+ qDebug() << "QImageReader::createReadHandler: no handlers found. giving up.";
+#endif
+ // no handler: give up.
+ return 0;
+ }
+
+ handler->setDevice(device);
+ if (!form.isEmpty())
+ handler->setFormat(form);
+ return handler;
+}
+
+class QImageReaderPrivate
+{
+public:
+ QImageReaderPrivate(QImageReader *qq);
+ ~QImageReaderPrivate();
+
+ // device
+ QByteArray format;
+ bool autoDetectImageFormat;
+ bool ignoresFormatAndExtension;
+ QIODevice *device;
+ bool deleteDevice;
+ QImageIOHandler *handler;
+ bool initHandler();
+
+ // image options
+ QRect clipRect;
+ QSize scaledSize;
+ QRect scaledClipRect;
+ int quality;
+ QMap<QString, QString> text;
+ void getText();
+
+ // error
+ QImageReader::ImageReaderError imageReaderError;
+ QString errorString;
+
+ QImageReader *q;
+};
+
+/*!
+ \internal
+*/
+QImageReaderPrivate::QImageReaderPrivate(QImageReader *qq)
+ : autoDetectImageFormat(true), ignoresFormatAndExtension(false)
+{
+ device = 0;
+ deleteDevice = false;
+ handler = 0;
+ quality = -1;
+ imageReaderError = QImageReader::UnknownError;
+
+ q = qq;
+}
+
+/*!
+ \internal
+*/
+QImageReaderPrivate::~QImageReaderPrivate()
+{
+ if (deleteDevice)
+ delete device;
+ delete handler;
+}
+
+/*!
+ \internal
+*/
+bool QImageReaderPrivate::initHandler()
+{
+ // check some preconditions
+ if (!device || (!deleteDevice && !device->isOpen() && !device->open(QIODevice::ReadOnly))) {
+ imageReaderError = QImageReader::DeviceError;
+ errorString = QLatin1String(QT_TRANSLATE_NOOP(QImageReader, "Invalid device"));
+ return false;
+ }
+
+ // probe the file extension
+ if (deleteDevice && !device->isOpen() && !device->open(QIODevice::ReadOnly) && autoDetectImageFormat) {
+ QList<QByteArray> extensions = QImageReader::supportedImageFormats();
+ if (!format.isEmpty()) {
+ // Try the most probable extension first
+ int currentFormatIndex = extensions.indexOf(format.toLower());
+ if (currentFormatIndex > 0)
+ extensions.swap(0, currentFormatIndex);
+ }
+
+ int currentExtension = 0;
+
+ QFile *file = static_cast<QFile *>(device);
+ QString fileName = file->fileName();
+
+ do {
+ file->setFileName(fileName + QLatin1Char('.')
+ + QString::fromLatin1(extensions.at(currentExtension++).constData()));
+ file->open(QIODevice::ReadOnly);
+ } while (!file->isOpen() && currentExtension < extensions.size());
+
+ if (!device->isOpen()) {
+ imageReaderError = QImageReader::FileNotFoundError;
+ errorString = QLatin1String(QT_TRANSLATE_NOOP(QImageReader, "File not found"));
+ file->setFileName(fileName); // restore the old file name
+ return false;
+ }
+ }
+
+ // assign a handler
+ if (!handler && (handler = createReadHandlerHelper(device, format, autoDetectImageFormat, ignoresFormatAndExtension)) == 0) {
+ imageReaderError = QImageReader::UnsupportedFormatError;
+ errorString = QLatin1String(QT_TRANSLATE_NOOP(QImageReader, "Unsupported image format"));
+ return false;
+ }
+ return true;
+}
+
+/*!
+ \internal
+*/
+void QImageReaderPrivate::getText()
+{
+ if (!text.isEmpty() || (!handler && !initHandler()) || !handler->supportsOption(QImageIOHandler::Description))
+ return;
+ foreach (QString pair, handler->option(QImageIOHandler::Description).toString().split(
+ QLatin1String("\n\n"))) {
+ int index = pair.indexOf(QLatin1Char(':'));
+ if (index >= 0 && pair.indexOf(QLatin1Char(' ')) < index) {
+ text.insert(QLatin1String("Description"), pair.simplified());
+ } else {
+ QString key = pair.left(index);
+ text.insert(key, pair.mid(index + 2).simplified());
+ }
+ }
+}
+
+/*!
+ Constructs an empty QImageReader object. Before reading an image,
+ call setDevice() or setFileName().
+*/
+QImageReader::QImageReader()
+ : d(new QImageReaderPrivate(this))
+{
+}
+
+/*!
+ Constructs a QImageReader object with the device \a device and the
+ image format \a format.
+*/
+QImageReader::QImageReader(QIODevice *device, const QByteArray &format)
+ : d(new QImageReaderPrivate(this))
+{
+ d->device = device;
+ d->format = format;
+}
+
+/*!
+ Constructs a QImageReader object with the file name \a fileName
+ and the image format \a format.
+
+ \sa setFileName()
+*/
+QImageReader::QImageReader(const QString &fileName, const QByteArray &format)
+ : d(new QImageReaderPrivate(this))
+{
+ QFile *file = new QFile(fileName);
+ d->device = file;
+ d->deleteDevice = true;
+ d->format = format;
+}
+
+/*!
+ Destructs the QImageReader object.
+*/
+QImageReader::~QImageReader()
+{
+ delete d;
+}
+
+/*!
+ Sets the format QImageReader will use when reading images, to \a
+ format. \a format is a case insensitive text string. Example:
+
+ \snippet doc/src/snippets/code/src_gui_image_qimagereader.cpp 0
+
+ You can call supportedImageFormats() for the full list of formats
+ QImageReader supports.
+
+ \sa format()
+*/
+void QImageReader::setFormat(const QByteArray &format)
+{
+ d->format = format;
+}
+
+/*!
+ Returns the format QImageReader uses for reading images.
+
+ You can call this function after assigning a device to the
+ reader to determine the format of the device. For example:
+
+ \snippet doc/src/snippets/code/src_gui_image_qimagereader.cpp 1
+
+ If the reader cannot read any image from the device (e.g., there is no
+ image there, or the image has already been read), or if the format is
+ unsupported, this function returns an empty QByteArray().
+
+ \sa setFormat(), supportedImageFormats()
+*/
+QByteArray QImageReader::format() const
+{
+ if (d->format.isEmpty()) {
+ if (!d->initHandler())
+ return QByteArray();
+ return d->handler->canRead() ? d->handler->format() : QByteArray();
+ }
+
+ return d->format;
+}
+
+/*!
+ If \a enabled is true, image format autodetection is enabled; otherwise,
+ it is disabled. By default, autodetection is enabled.
+
+ QImageReader uses an extensive approach to detecting the image format;
+ firstly, if you pass a file name to QImageReader, it will attempt to
+ detect the file extension if the given file name does not point to an
+ existing file, by appending supported default extensions to the given file
+ name, one at a time. It then uses the following approach to detect the
+ image format:
+
+ \list
+
+ \o Image plugins are queried first, based on either the optional format
+ string, or the file name suffix (if the source device is a file). No
+ content detection is done at this stage. QImageReader will choose the
+ first plugin that supports reading for this format.
+
+ \o If no plugin supports the image format, Qt's built-in handlers are
+ checked based on either the optional format string, or the file name
+ suffix.
+
+ \o If no capable plugins or built-in handlers are found, each plugin is
+ tested by inspecting the content of the data stream.
+
+ \o If no plugins could detect the image format based on data contents,
+ each built-in image handler is tested by inspecting the contents.
+
+ \o Finally, if all above approaches fail, QImageReader will report failure
+ when trying to read the image.
+
+ \endlist
+
+ By disabling image format autodetection, QImageReader will only query the
+ plugins and built-in handlers based on the format string (i.e., no file
+ name extensions are tested).
+
+ \sa QImageIOHandler::canRead(), QImageIOPlugin::capabilities()
+*/
+void QImageReader::setAutoDetectImageFormat(bool enabled)
+{
+ d->autoDetectImageFormat = enabled;
+}
+
+/*!
+ Returns true if image format autodetection is enabled on this image
+ reader; otherwise returns false. By default, autodetection is enabled.
+
+ \sa setAutoDetectImageFormat()
+*/
+bool QImageReader::autoDetectImageFormat() const
+{
+ return d->autoDetectImageFormat;
+}
+
+
+/*!
+ If \a ignored is set to true, then the image reader will ignore
+ specified formats or file extensions and decide which plugin to
+ use only based on the contents in the datastream.
+
+ Setting this flag means that all image plugins gets loaded. Each
+ plugin will read the first bytes in the image data and decide if
+ the plugin is compatible or not.
+
+ This also disables auto detecting the image format.
+
+ \sa decideFormatFromContent()
+*/
+
+void QImageReader::setDecideFormatFromContent(bool ignored)
+{
+ d->ignoresFormatAndExtension = ignored;
+}
+
+
+/*!
+ Returns whether the image reader should decide which plugin to use
+ only based on the contents of the datastream rather than on the file
+ extension.
+
+ \sa setDecideFormatFromContent()
+*/
+
+bool QImageReader::decideFormatFromContent() const
+{
+ return d->ignoresFormatAndExtension;
+}
+
+
+/*!
+ Sets QImageReader's device to \a device. If a device has already
+ been set, the old device is removed from QImageReader and is
+ otherwise left unchanged.
+
+ If the device is not already open, QImageReader will attempt to
+ open the device in \l QIODevice::ReadOnly mode by calling
+ open(). Note that this does not work for certain devices, such as
+ QProcess, QTcpSocket and QUdpSocket, where more logic is required
+ to open the device.
+
+ \sa device(), setFileName()
+*/
+void QImageReader::setDevice(QIODevice *device)
+{
+ if (d->device && d->deleteDevice)
+ delete d->device;
+ d->device = device;
+ d->deleteDevice = false;
+ delete d->handler;
+ d->handler = 0;
+ d->text.clear();
+}
+
+/*!
+ Returns the device currently assigned to QImageReader, or 0 if no
+ device has been assigned.
+*/
+QIODevice *QImageReader::device() const
+{
+ return d->device;
+}
+
+/*!
+ Sets the file name of QImageReader to \a fileName. Internally,
+ QImageReader will create a QFile object and open it in \l
+ QIODevice::ReadOnly mode, and use this when reading images.
+
+ If \a fileName does not include a file extension (e.g., .png or .bmp),
+ QImageReader will cycle through all supported extensions until it finds
+ a matching file.
+
+ \sa fileName(), setDevice(), supportedImageFormats()
+*/
+void QImageReader::setFileName(const QString &fileName)
+{
+ setDevice(new QFile(fileName));
+ d->deleteDevice = true;
+}
+
+/*!
+ If the currently assigned device is a QFile, or if setFileName()
+ has been called, this function returns the name of the file
+ QImageReader reads from. Otherwise (i.e., if no device has been
+ assigned or the device is not a QFile), an empty QString is
+ returned.
+
+ \sa setFileName(), setDevice()
+*/
+QString QImageReader::fileName() const
+{
+ QFile *file = qobject_cast<QFile *>(d->device);
+ return file ? file->fileName() : QString();
+}
+
+/*!
+ \since 4.2
+
+ This is an image format specific function that sets the quality
+ level of the image to \a quality. For image formats that do not
+ support setting the quality, this value is ignored.
+
+ The value range of \a quality depends on the image format. For
+ example, the "jpeg" format supports a quality range from 0 (low
+ quality, high compression) to 100 (high quality, low compression).
+
+ \sa quality()
+*/
+void QImageReader::setQuality(int quality)
+{
+ d->quality = quality;
+}
+
+/*!
+ \since 4.2
+
+ Returns the quality level of the image.
+
+ \sa setQuality()
+*/
+int QImageReader::quality() const
+{
+ return d->quality;
+}
+
+
+/*!
+ Returns the size of the image, without actually reading the image
+ contents.
+
+ If the image format does not support this feature, this function returns
+ an invalid size. Qt's built-in image handlers all support this feature,
+ but custom image format plugins are not required to do so.
+
+ \sa QImageIOHandler::ImageOption, QImageIOHandler::option(), QImageIOHandler::supportsOption()
+*/
+QSize QImageReader::size() const
+{
+ if (!d->initHandler())
+ return QSize();
+
+ if (d->handler->supportsOption(QImageIOHandler::Size))
+ return d->handler->option(QImageIOHandler::Size).toSize();
+
+ return QSize();
+}
+
+/*!
+ \since 4.5
+
+ Returns the format of the image, without actually reading the image
+ contents. The format describes the image format \l QImageReader::read()
+ returns, not the format of the actual image.
+
+ If the image format does not support this feature, this function returns
+ an invalid format.
+
+ \sa QImageIOHandler::ImageOption, QImageIOHandler::option(), QImageIOHandler::supportsOption()
+*/
+QImage::Format QImageReader::imageFormat() const
+{
+ if (!d->initHandler())
+ return QImage::Format_Invalid;
+
+ if (d->handler->supportsOption(QImageIOHandler::ImageFormat))
+ return (QImage::Format)d->handler->option(QImageIOHandler::ImageFormat).toInt();
+
+ return QImage::Format_Invalid;
+}
+
+/*!
+ \since 4.1
+
+ Returns the text keys for this image. You can use
+ these keys with text() to list the image text for
+ a certain key.
+
+ Support for this option is implemented through
+ QImageIOHandler::Description.
+
+ \sa text(), QImageWriter::setText(), QImage::textKeys()
+*/
+QStringList QImageReader::textKeys() const
+{
+ d->getText();
+ return d->text.keys();
+}
+
+/*!
+ \since 4.1
+
+ Returns the image text associated with \a key.
+
+ Support for this option is implemented through
+ QImageIOHandler::Description.
+
+ \sa textKeys(), QImageWriter::setText()
+*/
+QString QImageReader::text(const QString &key) const
+{
+ d->getText();
+ return d->text.value(key);
+}
+
+/*!
+ Sets the image clip rect (also known as the ROI, or Region Of
+ Interest) to \a rect. The coordinates of \a rect are relative to
+ the untransformed image size, as returned by size().
+
+ \sa clipRect(), setScaledSize(), setScaledClipRect()
+*/
+void QImageReader::setClipRect(const QRect &rect)
+{
+ d->clipRect = rect;
+}
+
+/*!
+ Returns the clip rect (also known as the ROI, or Region Of
+ Interest) of the image. If no clip rect has been set, an invalid
+ QRect is returned.
+
+ \sa setClipRect()
+*/
+QRect QImageReader::clipRect() const
+{
+ return d->clipRect;
+}
+
+/*!
+ Sets the scaled size of the image to \a size. The scaling is
+ performed after the initial clip rect, but before the scaled clip
+ rect is applied. The algorithm used for scaling depends on the
+ image format. By default (i.e., if the image format does not
+ support scaling), QImageReader will use QImage::scale() with
+ Qt::SmoothScaling.
+
+ \sa scaledSize(), setClipRect(), setScaledClipRect()
+*/
+void QImageReader::setScaledSize(const QSize &size)
+{
+ d->scaledSize = size;
+}
+
+/*!
+ Returns the scaled size of the image.
+
+ \sa setScaledSize()
+*/
+QSize QImageReader::scaledSize() const
+{
+ return d->scaledSize;
+}
+
+/*!
+ Sets the scaled clip rect to \a rect. The scaled clip rect is the
+ clip rect (also known as ROI, or Region Of Interest) that is
+ applied after the image has been scaled.
+
+ \sa scaledClipRect(), setScaledSize()
+*/
+void QImageReader::setScaledClipRect(const QRect &rect)
+{
+ d->scaledClipRect = rect;
+}
+
+/*!
+ Returns the scaled clip rect of the image.
+
+ \sa setScaledClipRect()
+*/
+QRect QImageReader::scaledClipRect() const
+{
+ return d->scaledClipRect;
+}
+
+/*!
+ \since 4.1
+
+ Sets the background color to \a color.
+ Image formats that support this operation are expected to
+ initialize the background to \a color before reading an image.
+
+ \sa backgroundColor(), read()
+*/
+void QImageReader::setBackgroundColor(const QColor &color)
+{
+ if (!d->initHandler())
+ return;
+ if (d->handler->supportsOption(QImageIOHandler::BackgroundColor))
+ d->handler->setOption(QImageIOHandler::BackgroundColor, color);
+}
+
+/*!
+ \since 4.1
+
+ Returns the background color that's used when reading an image.
+ If the image format does not support setting the background color
+ an invalid color is returned.
+
+ \sa setBackgroundColor(), read()
+*/
+QColor QImageReader::backgroundColor() const
+{
+ if (!d->initHandler())
+ return QColor();
+ if (d->handler->supportsOption(QImageIOHandler::BackgroundColor))
+ return qvariant_cast<QColor>(d->handler->option(QImageIOHandler::BackgroundColor));
+ return QColor();
+}
+
+/*!
+ \since 4.1
+
+ Returns true if the image format supports animation;
+ otherwise, false is returned.
+
+ \sa QMovie::supportedFormats()
+*/
+bool QImageReader::supportsAnimation() const
+{
+ if (!d->initHandler())
+ return false;
+ if (d->handler->supportsOption(QImageIOHandler::Animation))
+ return d->handler->option(QImageIOHandler::Animation).toBool();
+ return false;
+}
+
+/*!
+ Returns true if an image can be read for the device (i.e., the
+ image format is supported, and the device seems to contain valid
+ data); otherwise returns false.
+
+ canRead() is a lightweight function that only does a quick test to
+ see if the image data is valid. read() may still return false
+ after canRead() returns true, if the image data is corrupt.
+
+ For images that support animation, canRead() returns false when
+ all frames have been read.
+
+ \sa read(), supportedImageFormats()
+*/
+bool QImageReader::canRead() const
+{
+ if (!d->initHandler())
+ return false;
+
+ return d->handler->canRead();
+}
+
+/*!
+ Reads an image from the device. On success, the image that was
+ read is returned; otherwise, a null QImage is returned. You can
+ then call error() to find the type of error that occurred, or
+ errorString() to get a human readable description of the error.
+
+ For image formats that support animation, calling read()
+ repeatedly will return the next frame. When all frames have been
+ read, a null image will be returned.
+
+ \sa canRead(), supportedImageFormats(), supportsAnimation(), QMovie
+*/
+QImage QImageReader::read()
+{
+ // Because failed image reading might have side effects, we explicitly
+ // return a null image instead of the image we've just created.
+ QImage image;
+ return read(&image) ? image : QImage();
+}
+
+/*!
+ \overload
+
+ Reads an image from the device into \a image, which must point to a
+ QImage. Returns true on success; otherwise, returns false.
+
+ If \a image has same format and size as the image data that is about to be
+ read, this function may not need to allocate a new image before
+ reading. Because of this, it can be faster than the other read() overload,
+ which always constructs a new image; especially when reading several
+ images with the same format and size.
+
+ \snippet doc/src/snippets/code/src_gui_image_qimagereader.cpp 2
+
+ For image formats that support animation, calling read() repeatedly will
+ return the next frame. When all frames have been read, a null image will
+ be returned.
+
+ \sa canRead(), supportedImageFormats(), supportsAnimation(), QMovie
+*/
+bool QImageReader::read(QImage *image)
+{
+ if (!image) {
+ qWarning("QImageReader::read: cannot read into null pointer");
+ return false;
+ }
+
+ if (!d->handler && !d->initHandler())
+ return false;
+
+ // set the handler specific options.
+ if (d->handler->supportsOption(QImageIOHandler::ScaledSize) && d->scaledSize.isValid()) {
+ if ((d->handler->supportsOption(QImageIOHandler::ClipRect) && !d->clipRect.isNull())
+ || d->clipRect.isNull()) {
+ // Only enable the ScaledSize option if there is no clip rect, or
+ // if the handler also supports ClipRect.
+ d->handler->setOption(QImageIOHandler::ScaledSize, d->scaledSize);
+ }
+ }
+ if (d->handler->supportsOption(QImageIOHandler::ClipRect) && !d->clipRect.isNull())
+ d->handler->setOption(QImageIOHandler::ClipRect, d->clipRect);
+ if (d->handler->supportsOption(QImageIOHandler::ScaledClipRect) && !d->scaledClipRect.isNull())
+ d->handler->setOption(QImageIOHandler::ScaledClipRect, d->scaledClipRect);
+ if (d->handler->supportsOption(QImageIOHandler::Quality))
+ d->handler->setOption(QImageIOHandler::Quality, d->quality);
+
+ // read the image
+ if (!d->handler->read(image)) {
+ d->imageReaderError = InvalidDataError;
+ d->errorString = QLatin1String(QT_TRANSLATE_NOOP(QImageReader, "Unable to read image data"));
+ return false;
+ }
+
+ // provide default implementations for any unsupported image
+ // options
+ if (d->handler->supportsOption(QImageIOHandler::ClipRect) && !d->clipRect.isNull()) {
+ if (d->handler->supportsOption(QImageIOHandler::ScaledSize) && d->scaledSize.isValid()) {
+ if (d->handler->supportsOption(QImageIOHandler::ScaledClipRect) && !d->scaledClipRect.isNull()) {
+ // all features are supported by the handler; nothing to do.
+ } else {
+ // the image is already scaled, so apply scaled clipping.
+ if (!d->scaledClipRect.isNull())
+ *image = image->copy(d->scaledClipRect);
+ }
+ } else {
+ if (d->handler->supportsOption(QImageIOHandler::ScaledClipRect) && !d->scaledClipRect.isNull()) {
+ // supports scaled clipping but not scaling, most
+ // likely a broken handler.
+ } else {
+ if (d->scaledSize.isValid()) {
+ *image = image->scaled(d->scaledSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+ }
+ if (d->scaledClipRect.isValid()) {
+ *image = image->copy(d->scaledClipRect);
+ }
+ }
+ }
+ } else {
+ if (d->handler->supportsOption(QImageIOHandler::ScaledSize) && d->scaledSize.isValid()) {
+ // in this case, there's nothing we can do. if the
+ // plugin supports scaled size but not ClipRect, then
+ // we have to ignore ClipRect."
+
+ if (d->handler->supportsOption(QImageIOHandler::ScaledClipRect) && !d->scaledClipRect.isNull()) {
+ // nothing to do (ClipRect is ignored!)
+ } else {
+ // provide all workarounds.
+ if (d->scaledClipRect.isValid()) {
+ *image = image->copy(d->scaledClipRect);
+ }
+ }
+ } else {
+ if (d->handler->supportsOption(QImageIOHandler::ScaledClipRect) && !d->scaledClipRect.isNull()) {
+ // this makes no sense; a handler that supports
+ // ScaledClipRect but not ScaledSize is broken, and we
+ // can't work around it.
+ } else {
+ // provide all workarounds.
+ if (d->clipRect.isValid())
+ *image = image->copy(d->clipRect);
+ if (d->scaledSize.isValid())
+ *image = image->scaled(d->scaledSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+ if (d->scaledClipRect.isValid())
+ *image = image->copy(d->scaledClipRect);
+ }
+ }
+ }
+
+ return true;
+}
+
+/*!
+ For image formats that support animation, this function steps over the
+ current image, returning true if successful or false if there is no
+ following image in the animation.
+
+ The default implementation calls read(), then discards the resulting
+ image, but the image handler may have a more efficient way of implementing
+ this operation.
+
+ \sa jumpToImage(), QImageIOHandler::jumpToNextImage()
+*/
+bool QImageReader::jumpToNextImage()
+{
+ if (!d->initHandler())
+ return false;
+ return d->handler->jumpToNextImage();
+}
+
+/*!
+ For image formats that support animation, this function skips to the image
+ whose sequence number is \a imageNumber, returning true if successful
+ or false if the corresponding image cannot be found.
+
+ The next call to read() will attempt to read this image.
+
+ \sa jumpToNextImage(), QImageIOHandler::jumpToImage()
+*/
+bool QImageReader::jumpToImage(int imageNumber)
+{
+ if (!d->initHandler())
+ return false;
+ return d->handler->jumpToImage(imageNumber);
+}
+
+/*!
+ For image formats that support animation, this function returns the number
+ of times the animation should loop. If this function returns -1, it can
+ either mean the animation should loop forever, or that an error occurred.
+ If an error occurred, canRead() will return false.
+
+ \sa supportsAnimation(), QImageIOHandler::loopCount(), canRead()
+*/
+int QImageReader::loopCount() const
+{
+ if (!d->initHandler())
+ return -1;
+ return d->handler->loopCount();
+}
+
+/*!
+ For image formats that support animation, this function returns the total
+ number of images in the animation. If the format does not support
+ animation, 0 is returned.
+
+ This function returns -1 if an error occurred.
+
+ \sa supportsAnimation(), QImageIOHandler::imageCount(), canRead()
+*/
+int QImageReader::imageCount() const
+{
+ if (!d->initHandler())
+ return -1;
+ return d->handler->imageCount();
+}
+
+/*!
+ For image formats that support animation, this function returns the number
+ of milliseconds to wait until displaying the next frame in the animation.
+ If the image format doesn't support animation, 0 is returned.
+
+ This function returns -1 if an error occurred.
+
+ \sa supportsAnimation(), QImageIOHandler::nextImageDelay(), canRead()
+*/
+int QImageReader::nextImageDelay() const
+{
+ if (!d->initHandler())
+ return -1;
+ return d->handler->nextImageDelay();
+}
+
+/*!
+ For image formats that support animation, this function returns the
+ sequence number of the current frame. If the image format doesn't support
+ animation, 0 is returned.
+
+ This function returns -1 if an error occurred.
+
+ \sa supportsAnimation(), QImageIOHandler::currentImageNumber(), canRead()
+*/
+int QImageReader::currentImageNumber() const
+{
+ if (!d->initHandler())
+ return -1;
+ return d->handler->currentImageNumber();
+}
+
+/*!
+ For image formats that support animation, this function returns
+ the rect for the current frame. Otherwise, a null rect is returned.
+
+ \sa supportsAnimation(), QImageIOHandler::currentImageRect()
+*/
+QRect QImageReader::currentImageRect() const
+{
+ if (!d->initHandler())
+ return QRect();
+ return d->handler->currentImageRect();
+}
+
+/*!
+ Returns the type of error that occurred last.
+
+ \sa ImageReaderError, errorString()
+*/
+QImageReader::ImageReaderError QImageReader::error() const
+{
+ return d->imageReaderError;
+}
+
+/*!
+ Returns a human readable description of the last error that
+ occurred.
+
+ \sa error()
+*/
+QString QImageReader::errorString() const
+{
+ if (d->errorString.isEmpty())
+ return QLatin1String(QT_TRANSLATE_NOOP(QImageReader, "Unknown error"));
+ return d->errorString;
+}
+
+/*!
+ \since 4.2
+
+ Returns true if the reader supports \a option; otherwise returns
+ false.
+
+ Different image formats support different options. Call this function to
+ determine whether a certain option is supported by the current format. For
+ example, the PNG format allows you to embed text into the image's metadata
+ (see text()), and the BMP format allows you to determine the image's size
+ without loading the whole image into memory (see size()).
+
+ \snippet doc/src/snippets/code/src_gui_image_qimagereader.cpp 3
+
+ \sa QImageWriter::supportsOption()
+*/
+bool QImageReader::supportsOption(QImageIOHandler::ImageOption option) const
+{
+ if (!d->initHandler())
+ return false;
+ return d->handler->supportsOption(option);
+}
+
+/*!
+ If supported, this function returns the image format of the file
+ \a fileName. Otherwise, an empty string is returned.
+*/
+QByteArray QImageReader::imageFormat(const QString &fileName)
+{
+ QFile file(fileName);
+ if (!file.open(QFile::ReadOnly))
+ return QByteArray();
+
+ return imageFormat(&file);
+}
+
+/*!
+ If supported, this function returns the image format of the device
+ \a device. Otherwise, an empty string is returned.
+
+ \sa QImageReader::autoDetectImageFormat()
+*/
+QByteArray QImageReader::imageFormat(QIODevice *device)
+{
+ QByteArray format;
+ QImageIOHandler *handler = createReadHandlerHelper(device, format, /* autoDetectImageFormat = */ true, false);
+ if (handler) {
+ if (handler->canRead())
+ format = handler->format();
+ delete handler;
+ }
+ return format;
+}
+
+/*!
+ Returns the list of image formats supported by QImageReader.
+
+ By default, Qt can read the following formats:
+
+ \table
+ \header \o Format \o Description
+ \row \o BMP \o Windows Bitmap
+ \row \o GIF \o Graphic Interchange Format (optional)
+ \row \o JPG \o Joint Photographic Experts Group
+ \row \o JPEG \o Joint Photographic Experts Group
+ \row \o MNG \o Multiple-image Network Graphics
+ \row \o PNG \o Portable Network Graphics
+ \row \o PBM \o Portable Bitmap
+ \row \o PGM \o Portable Graymap
+ \row \o PPM \o Portable Pixmap
+ \row \o TIFF \o Tagged Image File Format
+ \row \o XBM \o X11 Bitmap
+ \row \o XPM \o X11 Pixmap
+ \row \o SVG \o Scalable Vector Graphics
+ \endtable
+
+ Reading and writing SVG files is supported through Qt's
+ \l{QtSvg Module}{SVG Module}.
+
+ To configure Qt with GIF support, pass \c -qt-gif to the \c
+ configure script or check the appropriate option in the graphical
+ installer.
+
+ Note that the QApplication instance must be created before this function is
+ called.
+
+ \sa setFormat(), QImageWriter::supportedImageFormats(), QImageIOPlugin
+*/
+QList<QByteArray> QImageReader::supportedImageFormats()
+{
+ QSet<QByteArray> formats;
+ for (int i = 0; i < _qt_NumFormats; ++i)
+ formats << _qt_BuiltInFormats[i].extension;
+
+#ifndef QT_NO_LIBRARY
+ QFactoryLoader *l = loader();
+ QStringList keys = l->keys();
+
+ for (int i = 0; i < keys.count(); ++i) {
+ QImageIOPlugin *plugin = qobject_cast<QImageIOPlugin *>(l->instance(keys.at(i)));
+ if (plugin && plugin->capabilities(0, keys.at(i).toLatin1()) & QImageIOPlugin::CanRead)
+ formats << keys.at(i).toLatin1();
+ }
+#endif // QT_NO_LIBRARY
+
+ QList<QByteArray> sortedFormats;
+ for (QSet<QByteArray>::ConstIterator it = formats.constBegin(); it != formats.constEnd(); ++it)
+ sortedFormats << *it;
+
+ qSort(sortedFormats);
+ return sortedFormats;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/image/qimagereader.h b/src/gui/image/qimagereader.h
new file mode 100644
index 0000000000..cdac4555ca
--- /dev/null
+++ b/src/gui/image/qimagereader.h
@@ -0,0 +1,147 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QIMAGEREADER_H
+#define QIMAGEREADER_H
+
+#include <QtCore/qbytearray.h>
+#include <QtGui/qimage.h>
+#include <QtGui/qimageiohandler.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QColor;
+class QIODevice;
+class QRect;
+class QSize;
+class QStringList;
+
+class QImageReaderPrivate;
+class Q_GUI_EXPORT QImageReader
+{
+public:
+ enum ImageReaderError {
+ UnknownError,
+ FileNotFoundError,
+ DeviceError,
+ UnsupportedFormatError,
+ InvalidDataError
+ };
+
+ QImageReader();
+ explicit QImageReader(QIODevice *device, const QByteArray &format = QByteArray());
+ explicit QImageReader(const QString &fileName, const QByteArray &format = QByteArray());
+ ~QImageReader();
+
+ void setFormat(const QByteArray &format);
+ QByteArray format() const;
+
+ void setAutoDetectImageFormat(bool enabled);
+ bool autoDetectImageFormat() const;
+
+ void setDecideFormatFromContent(bool ignored);
+ bool decideFormatFromContent() const;
+
+ void setDevice(QIODevice *device);
+ QIODevice *device() const;
+
+ void setFileName(const QString &fileName);
+ QString fileName() const;
+
+ QSize size() const;
+
+ QImage::Format imageFormat() const;
+
+ QStringList textKeys() const;
+ QString text(const QString &key) const;
+
+ void setClipRect(const QRect &rect);
+ QRect clipRect() const;
+
+ void setScaledSize(const QSize &size);
+ QSize scaledSize() const;
+
+ void setQuality(int quality);
+ int quality() const;
+
+ void setScaledClipRect(const QRect &rect);
+ QRect scaledClipRect() const;
+
+ void setBackgroundColor(const QColor &color);
+ QColor backgroundColor() const;
+
+ bool supportsAnimation() const;
+
+ bool canRead() const;
+ QImage read();
+ bool read(QImage *image);
+
+ bool jumpToNextImage();
+ bool jumpToImage(int imageNumber);
+ int loopCount() const;
+ int imageCount() const;
+ int nextImageDelay() const;
+ int currentImageNumber() const;
+ QRect currentImageRect() const;
+
+ ImageReaderError error() const;
+ QString errorString() const;
+
+ bool supportsOption(QImageIOHandler::ImageOption option) const;
+
+ static QByteArray imageFormat(const QString &fileName);
+ static QByteArray imageFormat(QIODevice *device);
+ static QList<QByteArray> supportedImageFormats();
+
+private:
+ Q_DISABLE_COPY(QImageReader)
+ QImageReaderPrivate *d;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QIMAGEREADER_H
diff --git a/src/gui/image/qimagewriter.cpp b/src/gui/image/qimagewriter.cpp
new file mode 100644
index 0000000000..504260aa47
--- /dev/null
+++ b/src/gui/image/qimagewriter.cpp
@@ -0,0 +1,734 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \class QImageWriter
+ \brief The QImageWriter class provides a format independent interface
+ for writing images to files or other devices.
+
+ \reentrant
+ \ingroup painting
+ \ingroup io
+
+ QImageWriter supports setting format specific options, such as the
+ gamma level, compression level and quality, prior to storing the
+ image. If you do not need such options, you can use QImage::save()
+ or QPixmap::save() instead.
+
+ To store an image, you start by constructing a QImageWriter
+ object. Pass either a file name or a device pointer, and the
+ image format to QImageWriter's constructor. You can then set
+ several options, such as the gamma level (by calling setGamma())
+ and quality (by calling setQuality()). canWrite() returns true if
+ QImageWriter can write the image (i.e., the image format is
+ supported and the device is open for writing). Call write() to
+ write the image to the device.
+
+ If any error occurs when writing the image, write() will return
+ false. You can then call error() to find the type of error that
+ occurred, or errorString() to get a human readable description of
+ what went wrong.
+
+ Call supportedImageFormats() for a list of formats that
+ QImageWriter can write. QImageWriter supports all built-in image
+ formats, in addition to any image format plugins that support
+ writing.
+
+ \sa QImageReader, QImageIOHandler, QImageIOPlugin
+*/
+
+/*!
+ \enum QImageWriter::ImageWriterError
+
+ This enum describes errors that can occur when writing images with
+ QImageWriter.
+
+ \value DeviceError QImageWriter encountered a device error when
+ writing the image data. Consult your device for more details on
+ what went wrong.
+
+ \value UnsupportedFormatError Qt does not support the requested
+ image format.
+
+ \value UnknownError An unknown error occurred. If you get this
+ value after calling write(), it is most likely caused by a bug in
+ QImageWriter.
+*/
+
+#include "qimagewriter.h"
+
+#include <qbytearray.h>
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qimageiohandler.h>
+#include <qset.h>
+#include <qvariant.h>
+
+// factory loader
+#include <qcoreapplication.h>
+#include <private/qfactoryloader_p.h>
+
+// image handlers
+#include <private/qbmphandler_p.h>
+#include <private/qppmhandler_p.h>
+#include <private/qxbmhandler_p.h>
+#include <private/qxpmhandler_p.h>
+#ifndef QT_NO_IMAGEFORMAT_PNG
+#include <private/qpnghandler_p.h>
+#endif
+#ifndef QT_NO_IMAGEFORMAT_JPEG
+#include <private/qjpeghandler_p.h>
+#endif
+#ifndef QT_NO_IMAGEFORMAT_MNG
+#include <private/qmnghandler_p.h>
+#endif
+#ifndef QT_NO_IMAGEFORMAT_TIFF
+#include <private/qtiffhandler_p.h>
+#endif
+#ifdef QT_BUILTIN_GIF_READER
+#include <private/qgifhandler_p.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_LIBRARY
+Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
+ (QImageIOHandlerFactoryInterface_iid, QLatin1String("/imageformats")))
+#endif
+
+static QImageIOHandler *createWriteHandlerHelper(QIODevice *device,
+ const QByteArray &format)
+{
+ QByteArray form = format.toLower();
+ QByteArray suffix;
+ QImageIOHandler *handler = 0;
+
+#ifndef QT_NO_LIBRARY
+ // check if any plugins can write the image
+ QFactoryLoader *l = loader();
+ QStringList keys = l->keys();
+ int suffixPluginIndex = -1;
+#endif
+
+ if (device && format.isEmpty()) {
+ // if there's no format, see if \a device is a file, and if so, find
+ // the file suffix and find support for that format among our plugins.
+ // this allows plugins to override our built-in handlers.
+ if (QFile *file = qobject_cast<QFile *>(device)) {
+ if (!(suffix = QFileInfo(file->fileName()).suffix().toLower().toLatin1()).isEmpty()) {
+#ifndef QT_NO_LIBRARY
+ int index = keys.indexOf(QString::fromLatin1(suffix));
+ if (index != -1)
+ suffixPluginIndex = index;
+#endif
+ }
+ }
+ }
+
+ QByteArray testFormat = !form.isEmpty() ? form : suffix;
+
+#ifndef QT_NO_LIBRARY
+ if (suffixPluginIndex != -1) {
+ // when format is missing, check if we can find a plugin for the
+ // suffix.
+ QImageIOPlugin *plugin = qobject_cast<QImageIOPlugin *>(l->instance(QString::fromLatin1(suffix)));
+ if (plugin && (plugin->capabilities(device, suffix) & QImageIOPlugin::CanWrite))
+ handler = plugin->create(device, suffix);
+ }
+#endif // QT_NO_LIBRARY
+
+ // check if any built-in handlers can write the image
+ if (!handler && !testFormat.isEmpty()) {
+ if (false) {
+#ifndef QT_NO_IMAGEFORMAT_PNG
+ } else if (testFormat == "png") {
+ handler = new QPngHandler;
+#endif
+#ifndef QT_NO_IMAGEFORMAT_JPEG
+ } else if (testFormat == "jpg" || testFormat == "jpeg") {
+ handler = new QJpegHandler;
+#endif
+#ifndef QT_NO_IMAGEFORMAT_MNG
+ } else if (testFormat == "mng") {
+ handler = new QMngHandler;
+#endif
+#ifndef QT_NO_IMAGEFORMAT_TIFF
+ } else if (testFormat == "tif" || testFormat == "tiff") {
+ handler = new QTiffHandler;
+#endif
+#ifdef QT_BUILTIN_GIF_READER
+ } else if (testFormat == "gif") {
+ handler = new QGifHandler;
+#endif
+#ifndef QT_NO_IMAGEFORMAT_BMP
+ } else if (testFormat == "bmp") {
+ handler = new QBmpHandler;
+#endif
+#ifndef QT_NO_IMAGEFORMAT_XPM
+ } else if (testFormat == "xpm") {
+ handler = new QXpmHandler;
+#endif
+#ifndef QT_NO_IMAGEFORMAT_XBM
+ } else if (testFormat == "xbm") {
+ handler = new QXbmHandler;
+ handler->setOption(QImageIOHandler::SubType, testFormat);
+#endif
+#ifndef QT_NO_IMAGEFORMAT_PPM
+ } else if (testFormat == "pbm" || testFormat == "pbmraw" || testFormat == "pgm"
+ || testFormat == "pgmraw" || testFormat == "ppm" || testFormat == "ppmraw") {
+ handler = new QPpmHandler;
+ handler->setOption(QImageIOHandler::SubType, testFormat);
+#endif
+ }
+ }
+
+#ifndef QT_NO_LIBRARY
+ if (!testFormat.isEmpty()) {
+ for (int i = 0; i < keys.size(); ++i) {
+ QImageIOPlugin *plugin = qobject_cast<QImageIOPlugin *>(l->instance(keys.at(i)));
+ if (plugin && (plugin->capabilities(device, testFormat) & QImageIOPlugin::CanWrite)) {
+ delete handler;
+ handler = plugin->create(device, testFormat);
+ break;
+ }
+ }
+ }
+#endif // QT_NO_LIBRARY
+
+ if (!handler)
+ return 0;
+
+ handler->setDevice(device);
+ if (!testFormat.isEmpty())
+ handler->setFormat(testFormat);
+ return handler;
+}
+
+class QImageWriterPrivate
+{
+public:
+ QImageWriterPrivate(QImageWriter *qq);
+
+ // device
+ QByteArray format;
+ QIODevice *device;
+ bool deleteDevice;
+ QImageIOHandler *handler;
+
+ // image options
+ int quality;
+ int compression;
+ float gamma;
+ QString description;
+ QString text;
+
+ // error
+ QImageWriter::ImageWriterError imageWriterError;
+ QString errorString;
+
+ QImageWriter *q;
+};
+
+/*!
+ \internal
+*/
+QImageWriterPrivate::QImageWriterPrivate(QImageWriter *qq)
+{
+ device = 0;
+ deleteDevice = false;
+ handler = 0;
+ quality = -1;
+ compression = 0;
+ gamma = 0.0;
+ imageWriterError = QImageWriter::UnknownError;
+ errorString = QT_TRANSLATE_NOOP(QImageWriter, QLatin1String("Unknown error"));
+
+ q = qq;
+}
+
+/*!
+ Constructs an empty QImageWriter object. Before writing, you must
+ call setFormat() to set an image format, then setDevice() or
+ setFileName().
+*/
+QImageWriter::QImageWriter()
+ : d(new QImageWriterPrivate(this))
+{
+}
+
+/*!
+ Constructs a QImageWriter object using the device \a device and
+ image format \a format.
+*/
+QImageWriter::QImageWriter(QIODevice *device, const QByteArray &format)
+ : d(new QImageWriterPrivate(this))
+{
+ d->device = device;
+ d->format = format;
+}
+
+/*!
+ Constructs a QImageWriter objects that will write to a file with
+ the name \a fileName, using the image format \a format. If \a
+ format is not provided, QImageWriter will detect the image format
+ by inspecting the extension of \a fileName.
+*/
+QImageWriter::QImageWriter(const QString &fileName, const QByteArray &format)
+ : d(new QImageWriterPrivate(this))
+{
+ QFile *file = new QFile(fileName);
+ d->device = file;
+ d->deleteDevice = true;
+ d->format = format;
+}
+
+/*!
+ Destructs the QImageWriter object.
+*/
+QImageWriter::~QImageWriter()
+{
+ if (d->deleteDevice)
+ delete d->device;
+ delete d->handler;
+ delete d;
+}
+
+/*!
+ Sets the format QImageWriter will use when writing images, to \a
+ format. \a format is a case insensitive text string. Example:
+
+ \snippet doc/src/snippets/code/src_gui_image_qimagewriter.cpp 0
+
+ You can call supportedImageFormats() for the full list of formats
+ QImageWriter supports.
+
+ \sa format()
+*/
+void QImageWriter::setFormat(const QByteArray &format)
+{
+ d->format = format;
+}
+
+/*!
+ Returns the format QImageWriter uses for writing images.
+
+ \sa setFormat()
+*/
+QByteArray QImageWriter::format() const
+{
+ return d->format;
+}
+
+/*!
+ Sets QImageWriter's device to \a device. If a device has already
+ been set, the old device is removed from QImageWriter and is
+ otherwise left unchanged.
+
+ If the device is not already open, QImageWriter will attempt to
+ open the device in \l QIODevice::WriteOnly mode by calling
+ open(). Note that this does not work for certain devices, such as
+ QProcess, QTcpSocket and QUdpSocket, where more logic is required
+ to open the device.
+
+ \sa device(), setFileName()
+*/
+void QImageWriter::setDevice(QIODevice *device)
+{
+ if (d->device && d->deleteDevice)
+ delete d->device;
+
+ d->device = device;
+ d->deleteDevice = false;
+ delete d->handler;
+ d->handler = 0;
+}
+
+/*!
+ Returns the device currently assigned to QImageWriter, or 0 if no
+ device has been assigned.
+*/
+QIODevice *QImageWriter::device() const
+{
+ return d->device;
+}
+
+/*!
+ Sets the file name of QImageWriter to \a fileName. Internally,
+ QImageWriter will create a QFile and open it in \l
+ QIODevice::WriteOnly mode, and use this file when writing images.
+
+ \sa fileName(), setDevice()
+*/
+void QImageWriter::setFileName(const QString &fileName)
+{
+ setDevice(new QFile(fileName));
+ d->deleteDevice = true;
+}
+
+/*!
+ If the currently assigned device is a QFile, or if setFileName()
+ has been called, this function returns the name of the file
+ QImageWriter writes to. Otherwise (i.e., if no device has been
+ assigned or the device is not a QFile), an empty QString is
+ returned.
+
+ \sa setFileName(), setDevice()
+*/
+QString QImageWriter::fileName() const
+{
+ QFile *file = qobject_cast<QFile *>(d->device);
+ return file ? file->fileName() : QString();
+}
+
+/*!
+ This is an image format specific function that sets the quality
+ level of the image to \a quality. For image formats that do not
+ support setting the quality, this value is ignored.
+
+ The value range of \a quality depends on the image format. For
+ example, the "jpeg" format supports a quality range from 0 (low
+ quality, high compression) to 100 (high quality, low compression).
+
+ \sa quality()
+*/
+void QImageWriter::setQuality(int quality)
+{
+ d->quality = quality;
+}
+
+/*!
+ Returns the quality level of the image.
+
+ \sa setQuality()
+*/
+int QImageWriter::quality() const
+{
+ return d->quality;
+}
+
+/*!
+ This is an image format specific function that set the compression
+ of an image. For image formats that do not support setting the
+ compression, this value is ignored.
+
+ The value range of \a compression depends on the image format. For
+ example, the "tiff" format supports two values, 0(no compression) and
+ 1(LZW-compression).
+
+ \sa compression()
+*/
+void QImageWriter::setCompression(int compression)
+{
+ d->compression = compression;
+}
+
+/*!
+ Returns the compression of the image.
+
+ \sa setCompression()
+*/
+int QImageWriter::compression() const
+{
+ return d->compression;
+}
+
+/*!
+ This is an image format specific function that sets the gamma
+ level of the image to \a gamma. For image formats that do not
+ support setting the gamma level, this value is ignored.
+
+ The value range of \a gamma depends on the image format. For
+ example, the "png" format supports a gamma range from 0.0 to 1.0.
+
+ \sa quality()
+*/
+void QImageWriter::setGamma(float gamma)
+{
+ d->gamma = gamma;
+}
+
+/*!
+ Returns the gamma level of the image.
+
+ \sa setGamma()
+*/
+float QImageWriter::gamma() const
+{
+ return d->gamma;
+}
+
+/*!
+ \obsolete
+
+ Use setText() instead.
+
+ This is an image format specific function that sets the
+ description of the image to \a description. For image formats that
+ do not support setting the description, this value is ignored.
+
+ The contents of \a description depends on the image format.
+
+ \sa description()
+*/
+void QImageWriter::setDescription(const QString &description)
+{
+ d->description = description;
+}
+
+/*!
+ \obsolete
+
+ Use QImageReader::text() instead.
+
+ Returns the description of the image.
+
+ \sa setDescription()
+*/
+QString QImageWriter::description() const
+{
+ return d->description;
+}
+
+/*!
+ \since 4.1
+
+ Sets the image text associated with the key \a key to
+ \a text. This is useful for storing copyright information
+ or other information about the image. Example:
+
+ \snippet doc/src/snippets/code/src_gui_image_qimagewriter.cpp 1
+
+ If you want to store a single block of data
+ (e.g., a comment), you can pass an empty key, or use
+ a generic key like "Description".
+
+ The key and text will be embedded into the
+ image data after calling write().
+
+ Support for this option is implemented through
+ QImageIOHandler::Description.
+
+ \sa QImage::setText(), QImageReader::text()
+*/
+void QImageWriter::setText(const QString &key, const QString &text)
+{
+ if (!d->description.isEmpty())
+ d->description += QLatin1String("\n\n");
+ d->description += key.simplified() + QLatin1String(": ") + text.simplified();
+}
+
+/*!
+ Returns true if QImageWriter can write the image; i.e., the image
+ format is supported and the assigned device is open for reading.
+
+ \sa write(), setDevice(), setFormat()
+*/
+bool QImageWriter::canWrite() const
+{
+ if (d->device && !d->handler && (d->handler = createWriteHandlerHelper(d->device, d->format)) == 0) {
+ d->imageWriterError = QImageWriter::UnsupportedFormatError;
+ d->errorString = QT_TRANSLATE_NOOP(QImageWriter,
+ QLatin1String("Unsupported image format"));
+ return false;
+ }
+ if (d->device && !d->device->isOpen())
+ d->device->open(QIODevice::WriteOnly);
+ if (!d->device || !d->device->isWritable()) {
+ d->imageWriterError = QImageWriter::DeviceError;
+ d->errorString = QT_TRANSLATE_NOOP(QImageWriter,
+ QLatin1String("Device not writable"));
+ return false;
+ }
+ return true;
+}
+
+/*!
+ Writes the image \a image to the assigned device or file
+ name. Returns true on success; otherwise returns false. If the
+ operation fails, you can call error() to find the type of error
+ that occurred, or errorString() to get a human readable
+ description of the error.
+
+ \sa canWrite(), error(), errorString()
+*/
+bool QImageWriter::write(const QImage &image)
+{
+ if (!canWrite())
+ return false;
+
+ if (d->handler->supportsOption(QImageIOHandler::Quality))
+ d->handler->setOption(QImageIOHandler::Quality, d->quality);
+ if (d->handler->supportsOption(QImageIOHandler::CompressionRatio))
+ d->handler->setOption(QImageIOHandler::CompressionRatio, d->compression);
+ if (d->handler->supportsOption(QImageIOHandler::Gamma))
+ d->handler->setOption(QImageIOHandler::Gamma, d->gamma);
+ if (!d->description.isEmpty() && d->handler->supportsOption(QImageIOHandler::Description))
+ d->handler->setOption(QImageIOHandler::Description, d->description);
+
+ if (!d->handler->write(image))
+ return false;
+ if (QFile *file = qobject_cast<QFile *>(d->device))
+ file->flush();
+ return true;
+}
+
+/*!
+ Returns the type of error that last occurred.
+
+ \sa ImageWriterError, errorString()
+*/
+QImageWriter::ImageWriterError QImageWriter::error() const
+{
+ return d->imageWriterError;
+}
+
+/*!
+ Returns a human readable description of the last error that occurred.
+
+ \sa error()
+*/
+QString QImageWriter::errorString() const
+{
+ return d->errorString;
+}
+
+/*!
+ \since 4.2
+
+ Returns true if the writer supports \a option; otherwise returns
+ false.
+
+ Different image formats support different options. Call this function to
+ determine whether a certain option is supported by the current format. For
+ example, the PNG format allows you to embed text into the image's metadata
+ (see text()).
+
+ \snippet doc/src/snippets/code/src_gui_image_qimagewriter.cpp 2
+
+ Options can be tested after the writer has been associated with a format.
+
+ \sa QImageReader::supportsOption(), setFormat()
+*/
+bool QImageWriter::supportsOption(QImageIOHandler::ImageOption option) const
+{
+ if (!d->handler && (d->handler = createWriteHandlerHelper(d->device, d->format)) == 0) {
+ d->imageWriterError = QImageWriter::UnsupportedFormatError;
+ d->errorString = QT_TRANSLATE_NOOP(QImageWriter,
+ QLatin1String("Unsupported image format"));
+ return false;
+ }
+
+ return d->handler->supportsOption(option);
+}
+
+/*!
+ Returns the list of image formats supported by QImageWriter.
+
+ By default, Qt can write the following formats:
+
+ \table
+ \header \o Format \o Description
+ \row \o BMP \o Windows Bitmap
+ \row \o JPG \o Joint Photographic Experts Group
+ \row \o JPEG \o Joint Photographic Experts Group
+ \row \o PNG \o Portable Network Graphics
+ \row \o PPM \o Portable Pixmap
+ \row \o TIFF \o Tagged Image File Format
+ \row \o XBM \o X11 Bitmap
+ \row \o XPM \o X11 Pixmap
+ \endtable
+
+ Reading and writing SVG files is supported through Qt's
+ \l{QtSvg Module}{SVG Module}.
+
+ Note that the QApplication instance must be created before this function is
+ called.
+
+ \sa setFormat(), QImageReader::supportedImageFormats(), QImageIOPlugin
+*/
+QList<QByteArray> QImageWriter::supportedImageFormats()
+{
+ QSet<QByteArray> formats;
+ formats << "bmp";
+#ifndef QT_NO_IMAGEFORMAT_PPM
+ formats << "ppm";
+#endif
+#ifndef QT_NO_IMAGEFORMAT_XBM
+ formats << "xbm";
+#endif
+#ifndef QT_NO_IMAGEFORMAT_XPM
+ formats << "xpm";
+#endif
+#ifndef QT_NO_IMAGEFORMAT_PNG
+ formats << "png";
+#endif
+#ifndef QT_NO_IMAGEFORMAT_JPEG
+ formats << "jpg" << "jpeg";
+#endif
+#ifndef QT_NO_IMAGEFORMAT_MNG
+ formats << "mng";
+#endif
+#ifndef QT_NO_IMAGEFORMAT_TIFF
+ formats << "tif" << "tiff";
+#endif
+#ifdef QT_BUILTIN_GIF_READER
+ formats << "gif";
+#endif
+
+#ifndef QT_NO_LIBRARY
+ QFactoryLoader *l = loader();
+ QStringList keys = l->keys();
+ for (int i = 0; i < keys.count(); ++i) {
+ QImageIOPlugin *plugin = qobject_cast<QImageIOPlugin *>(l->instance(keys.at(i)));
+ if (plugin && (plugin->capabilities(0, keys.at(i).toLatin1()) & QImageIOPlugin::CanWrite) != 0)
+ formats << keys.at(i).toLatin1();
+ }
+#endif // QT_NO_LIBRARY
+
+ QList<QByteArray> sortedFormats;
+ for (QSet<QByteArray>::ConstIterator it = formats.constBegin(); it != formats.constEnd(); ++it)
+ sortedFormats << *it;
+
+ qSort(sortedFormats);
+ return sortedFormats;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/image/qimagewriter.h b/src/gui/image/qimagewriter.h
new file mode 100644
index 0000000000..5e1b9bf677
--- /dev/null
+++ b/src/gui/image/qimagewriter.h
@@ -0,0 +1,116 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QIMAGEWRITER_H
+#define QIMAGEWRITER_H
+
+#include <QtCore/qbytearray.h>
+#include <QtCore/qlist.h>
+#include <QtGui/qimageiohandler.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QIODevice;
+class QImage;
+
+class QImageWriterPrivate;
+class Q_GUI_EXPORT QImageWriter
+{
+public:
+ enum ImageWriterError {
+ UnknownError,
+ DeviceError,
+ UnsupportedFormatError
+ };
+
+ QImageWriter();
+ explicit QImageWriter(QIODevice *device, const QByteArray &format);
+ explicit QImageWriter(const QString &fileName, const QByteArray &format = QByteArray());
+ ~QImageWriter();
+
+ void setFormat(const QByteArray &format);
+ QByteArray format() const;
+
+ void setDevice(QIODevice *device);
+ QIODevice *device() const;
+
+ void setFileName(const QString &fileName);
+ QString fileName() const;
+
+ void setQuality(int quality);
+ int quality() const;
+
+ void setCompression(int compression);
+ int compression() const;
+
+ void setGamma(float gamma);
+ float gamma() const;
+
+ // Obsolete as of 4.1
+ void setDescription(const QString &description);
+ QString description() const;
+
+ void setText(const QString &key, const QString &text);
+
+ bool canWrite() const;
+ bool write(const QImage &image);
+
+ ImageWriterError error() const;
+ QString errorString() const;
+
+ bool supportsOption(QImageIOHandler::ImageOption option) const;
+
+ static QList<QByteArray> supportedImageFormats();
+
+private:
+ Q_DISABLE_COPY(QImageWriter)
+ QImageWriterPrivate *d;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QIMAGEWRITER_H
diff --git a/src/gui/image/qjpeghandler.cpp b/src/gui/image/qjpeghandler.cpp
new file mode 100644
index 0000000000..d57e0c1244
--- /dev/null
+++ b/src/gui/image/qjpeghandler.cpp
@@ -0,0 +1,915 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qjpeghandler_p.h"
+
+#include <qimage.h>
+#include <qvariant.h>
+#include <qvector.h>
+#include <qbuffer.h>
+#include <private/qsimd_p.h>
+
+#include <stdio.h> // jpeglib needs this to be pre-included
+#include <setjmp.h>
+
+#ifdef FAR
+#undef FAR
+#endif
+
+// 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
+
+void QT_FASTCALL convert_rgb888_to_rgb32_C(quint32 *dst, const uchar *src, int len)
+{
+ // Expand 24->32 bpp.
+ for (int i = 0; i < len; ++i) {
+ *dst++ = qRgb(src[0], src[1], src[2]);
+ src += 3;
+ }
+}
+
+typedef void (QT_FASTCALL *Rgb888ToRgb32Converter)(quint32 *dst, const uchar *src, int len);
+
+static Rgb888ToRgb32Converter rgb888ToRgb32ConverterPtr = convert_rgb888_to_rgb32_C;
+
+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];
+ const QBuffer *memDevice;
+
+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)
+{
+ my_jpeg_source_mgr* src = (my_jpeg_source_mgr*)cinfo->src;
+ qint64 num_read = 0;
+ if (src->memDevice) {
+ src->next_input_byte = (const JOCTET *)(src->memDevice->data().constData() + src->memDevice->pos());
+ num_read = src->memDevice->data().size() - src->memDevice->pos();
+ src->device->seek(src->memDevice->data().size());
+ } else {
+ 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->next_input_byte = src->buffer;
+ 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) { // Should not happen in case of memDevice
+ 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;
+ memDevice = qobject_cast<QBuffer *>(device);
+ bytes_in_buffer = 0;
+ next_input_byte = buffer;
+}
+
+
+inline static bool read_jpeg_size(int &w, int &h, j_decompress_ptr cinfo)
+{
+ (void) jpeg_calc_output_dimensions(cinfo);
+
+ w = cinfo->output_width;
+ h = cinfo->output_height;
+ return true;
+}
+
+#define HIGH_QUALITY_THRESHOLD 50
+
+inline static bool read_jpeg_format(QImage::Format &format, j_decompress_ptr cinfo)
+{
+
+ bool 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;
+ return result;
+}
+
+static bool ensureValidImage(QImage *dest, struct jpeg_decompress_struct *info,
+ const QSize& size)
+{
+ 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
+ }
+
+ if (dest->size() != size || dest->format() != format) {
+ *dest = QImage(size, format);
+
+ if (format == QImage::Format_Indexed8) {
+ dest->setColorCount(256);
+ for (int i = 0; i < 256; i++)
+ dest->setColor(i, qRgb(i,i,i));
+ }
+ }
+
+ return !dest->isNull();
+}
+
+static bool read_jpeg_image(QImage *outImage,
+ QSize scaledSize, QRect scaledClipRect,
+ QRect clipRect, int inQuality, j_decompress_ptr info, struct my_error_mgr* err )
+{
+ if (!setjmp(err->setjmp_buffer)) {
+ // -1 means default quality.
+ int quality = inQuality;
+ if (quality < 0)
+ quality = 75;
+
+ // If possible, merge the scaledClipRect into either scaledSize
+ // or clipRect to avoid doing a separate scaled clipping pass.
+ // Best results are achieved by clipping before scaling, not after.
+ if (!scaledClipRect.isEmpty()) {
+ if (scaledSize.isEmpty() && clipRect.isEmpty()) {
+ // No clipping or scaling before final clip.
+ clipRect = scaledClipRect;
+ scaledClipRect = QRect();
+ } else if (scaledSize.isEmpty()) {
+ // Clipping, but no scaling: combine the clip regions.
+ scaledClipRect.translate(clipRect.topLeft());
+ clipRect = scaledClipRect.intersected(clipRect);
+ scaledClipRect = QRect();
+ } else if (clipRect.isEmpty()) {
+ // No clipping, but scaling: if we can map back to an
+ // integer pixel boundary, then clip before scaling.
+ if ((info->image_width % scaledSize.width()) == 0 &&
+ (info->image_height % scaledSize.height()) == 0) {
+ int x = scaledClipRect.x() * info->image_width /
+ scaledSize.width();
+ int y = scaledClipRect.y() * info->image_height /
+ scaledSize.height();
+ int width = (scaledClipRect.right() + 1) *
+ info->image_width / scaledSize.width() - x;
+ int height = (scaledClipRect.bottom() + 1) *
+ info->image_height / scaledSize.height() - y;
+ clipRect = QRect(x, y, width, height);
+ scaledSize = scaledClipRect.size();
+ scaledClipRect = QRect();
+ }
+ } else {
+ // Clipping and scaling: too difficult to figure out,
+ // and not a likely use case, so do it the long way.
+ }
+ }
+
+ // Determine the scale factor to pass to libjpeg for quick downscaling.
+ if (!scaledSize.isEmpty()) {
+ if (clipRect.isEmpty()) {
+ info->scale_denom =
+ qMin(info->image_width / scaledSize.width(),
+ info->image_height / scaledSize.height());
+ } else {
+ info->scale_denom =
+ qMin(clipRect.width() / scaledSize.width(),
+ clipRect.height() / scaledSize.height());
+ }
+ if (info->scale_denom < 2) {
+ info->scale_denom = 1;
+ } else if (info->scale_denom < 4) {
+ info->scale_denom = 2;
+ } else if (info->scale_denom < 8) {
+ info->scale_denom = 4;
+ } else {
+ info->scale_denom = 8;
+ }
+ info->scale_num = 1;
+ if (!clipRect.isEmpty()) {
+ // Correct the scale factor so that we clip accurately.
+ // It is recommended that the clip rectangle be aligned
+ // on an 8-pixel boundary for best performance.
+ while (info->scale_denom > 1 &&
+ ((clipRect.x() % info->scale_denom) != 0 ||
+ (clipRect.y() % info->scale_denom) != 0 ||
+ (clipRect.width() % info->scale_denom) != 0 ||
+ (clipRect.height() % info->scale_denom) != 0)) {
+ info->scale_denom /= 2;
+ }
+ }
+ }
+
+ // If high quality not required, use fast decompression
+ if( quality < HIGH_QUALITY_THRESHOLD ) {
+ info->dct_method = JDCT_IFAST;
+ info->do_fancy_upsampling = FALSE;
+ }
+
+ (void) jpeg_calc_output_dimensions(info);
+
+ // Determine the clip region to extract.
+ QRect imageRect(0, 0, info->output_width, info->output_height);
+ QRect clip;
+ if (clipRect.isEmpty()) {
+ clip = imageRect;
+ } else if (info->scale_denom == info->scale_num) {
+ clip = clipRect.intersected(imageRect);
+ } else {
+ // The scale factor was corrected above to ensure that
+ // we don't miss pixels when we scale the clip rectangle.
+ clip = QRect(clipRect.x() / int(info->scale_denom),
+ clipRect.y() / int(info->scale_denom),
+ clipRect.width() / int(info->scale_denom),
+ clipRect.height() / int(info->scale_denom));
+ clip = clip.intersected(imageRect);
+ }
+
+ // Allocate memory for the clipped QImage.
+ if (!ensureValidImage(outImage, info, clip.size()))
+ longjmp(err->setjmp_buffer, 1);
+
+ // Avoid memcpy() overhead if grayscale with no clipping.
+ bool quickGray = (info->output_components == 1 &&
+ clip == imageRect);
+ if (!quickGray) {
+ // Ask the jpeg library to allocate a temporary row.
+ // The library will automatically delete it for us later.
+ // The libjpeg docs say we should do this before calling
+ // jpeg_start_decompress(). We can't use "new" here
+ // because we are inside the setjmp() block and an error
+ // in the jpeg input stream would cause a memory leak.
+ JSAMPARRAY rows = (info->mem->alloc_sarray)
+ ((j_common_ptr)info, JPOOL_IMAGE,
+ info->output_width * info->output_components, 1);
+
+ (void) jpeg_start_decompress(info);
+
+ while (info->output_scanline < info->output_height) {
+ int y = int(info->output_scanline) - clip.y();
+ if (y >= clip.height())
+ break; // We've read the entire clip region, so abort.
+
+ (void) jpeg_read_scanlines(info, rows, 1);
+
+ if (y < 0)
+ continue; // Haven't reached the starting line yet.
+
+ if (info->output_components == 3) {
+ uchar *in = rows[0] + clip.x() * 3;
+ QRgb *out = (QRgb*)outImage->scanLine(y);
+ rgb888ToRgb32ConverterPtr(out, in, clip.width());
+ } else if (info->out_color_space == JCS_CMYK) {
+ // Convert CMYK->RGB.
+ uchar *in = rows[0] + clip.x() * 4;
+ QRgb *out = (QRgb*)outImage->scanLine(y);
+ for (int i = 0; i < clip.width(); ++i) {
+ int k = in[3];
+ *out++ = qRgb(k * in[0] / 255, k * in[1] / 255,
+ k * in[2] / 255);
+ in += 4;
+ }
+ } else if (info->output_components == 1) {
+ // Grayscale.
+ memcpy(outImage->scanLine(y),
+ rows[0] + clip.x(), clip.width());
+ }
+ }
+ } else {
+ // Load unclipped grayscale data directly into the QImage.
+ (void) jpeg_start_decompress(info);
+ while (info->output_scanline < info->output_height) {
+ uchar *row = outImage->scanLine(info->output_scanline);
+ (void) jpeg_read_scanlines(info, &row, 1);
+ }
+ }
+
+ if (info->output_scanline == info->output_height)
+ (void) jpeg_finish_decompress(info);
+
+ if (info->density_unit == 1) {
+ outImage->setDotsPerMeterX(int(100. * info->X_density / 2.54));
+ outImage->setDotsPerMeterY(int(100. * info->Y_density / 2.54));
+ } else if (info->density_unit == 2) {
+ outImage->setDotsPerMeterX(int(100. * info->X_density));
+ outImage->setDotsPerMeterY(int(100. * info->Y_density));
+ }
+
+ if (scaledSize.isValid() && scaledSize != clip.size()) {
+ *outImage = outImage->scaled(scaledSize, Qt::IgnoreAspectRatio, quality >= HIGH_QUALITY_THRESHOLD ? Qt::SmoothTransformation : Qt::FastTransformation);
+ }
+
+ if (!scaledClipRect.isEmpty())
+ *outImage = outImage->copy(scaledClipRect);
+ return !outImage->isNull();
+ }
+ else
+ return false;
+}
+
+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 &image, QIODevice *device, int sourceQuality)
+{
+ bool success = false;
+ 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.colorCount(); 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.constScanLine(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.constScanLine(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.constScanLine(cinfo.next_scanline);
+ for (int i=0; i<w; i++) {
+ *row = qRed(cmap[*pix]);
+ ++row; ++pix;
+ }
+ } else {
+ const uchar* pix = image.constScanLine(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.constScanLine(cinfo.next_scanline), w * 3);
+ break;
+ case QImage::Format_RGB32:
+ case QImage::Format_ARGB32:
+ case QImage::Format_ARGB32_Premultiplied:
+ {
+ const QRgb* rgb = (const QRgb*)image.constScanLine(cinfo.next_scanline);
+ for (int i=0; i<w; i++) {
+ *row++ = qRed(*rgb);
+ *row++ = qGreen(*rgb);
+ *row++ = qBlue(*rgb);
+ ++rgb;
+ }
+ }
+ break;
+ default:
+ {
+ // (Testing shows that this way is actually faster than converting to RGB888 + memcpy)
+ QImage rowImg = image.copy(0, cinfo.next_scanline, w, 1).convertToFormat(QImage::Format_RGB32);
+ const QRgb* rgb = (const QRgb*)rowImg.constScanLine(0);
+ for (int i=0; i<w; i++) {
+ *row++ = qRed(*rgb);
+ *row++ = qGreen(*rgb);
+ *row++ = qBlue(*rgb);
+ ++rgb;
+ }
+ }
+ 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;
+}
+
+class QJpegHandlerPrivate
+{
+public:
+ enum State {
+ Ready,
+ ReadHeader,
+ Error
+ };
+
+ QJpegHandlerPrivate(QJpegHandler *qq)
+ : quality(75), iod_src(0), state(Ready), q(qq)
+ {}
+
+ ~QJpegHandlerPrivate()
+ {
+ if(iod_src)
+ {
+ jpeg_destroy_decompress(&info);
+ delete iod_src;
+ iod_src = 0;
+ }
+ }
+
+ bool readJpegHeader(QIODevice*);
+ bool read(QImage *image);
+
+ int quality;
+ QVariant size;
+ QImage::Format format;
+ QSize scaledSize;
+ QRect scaledClipRect;
+ QRect clipRect;
+ struct jpeg_decompress_struct info;
+ struct my_jpeg_source_mgr * iod_src;
+ struct my_error_mgr err;
+
+ State state;
+
+ QJpegHandler *q;
+};
+
+/*!
+ \internal
+*/
+bool QJpegHandlerPrivate::readJpegHeader(QIODevice *device)
+{
+ if(state == Ready)
+ {
+ state = Error;
+ iod_src = new my_jpeg_source_mgr(device);
+
+ jpeg_create_decompress(&info);
+ info.src = iod_src;
+ info.err = jpeg_std_error(&err);
+ err.error_exit = my_error_exit;
+
+ if (!setjmp(err.setjmp_buffer)) {
+ #if defined(Q_OS_UNIXWARE)
+ (void) jpeg_read_header(&info, B_TRUE);
+ #else
+ (void) jpeg_read_header(&info, true);
+ #endif
+
+ int width = 0;
+ int height = 0;
+ read_jpeg_size(width, height, &info);
+ size = QSize(width, height);
+
+ format = QImage::Format_Invalid;
+ read_jpeg_format(format, &info);
+ state = ReadHeader;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else if(state == Error)
+ return false;
+ return true;
+}
+
+bool QJpegHandlerPrivate::read(QImage *image)
+{
+ if(state == Ready)
+ readJpegHeader(q->device());
+
+ if(state == ReadHeader)
+ {
+ bool success = read_jpeg_image(image, scaledSize, scaledClipRect, clipRect, quality, &info, &err);
+ state = success ? Ready : Error;
+ return success;
+ }
+
+ return false;
+
+}
+
+QJpegHandler::QJpegHandler()
+ : d(new QJpegHandlerPrivate(this))
+{
+ const uint features = qDetectCPUFeatures();
+ Q_UNUSED(features);
+#if defined(QT_HAVE_NEON)
+ // from qimage_neon.cpp
+ Q_GUI_EXPORT void QT_FASTCALL qt_convert_rgb888_to_rgb32_neon(quint32 *dst, const uchar *src, int len);
+
+ if (features & NEON)
+ rgb888ToRgb32ConverterPtr = qt_convert_rgb888_to_rgb32_neon;
+#endif // QT_HAVE_NEON
+#if defined(QT_HAVE_SSSE3)
+ // from qimage_ssse3.cpp
+ Q_GUI_EXPORT void QT_FASTCALL qt_convert_rgb888_to_rgb32_ssse3(quint32 *dst, const uchar *src, int len);
+
+ if (features & SSSE3)
+ rgb888ToRgb32ConverterPtr = qt_convert_rgb888_to_rgb32_ssse3;
+#endif // QT_HAVE_SSSE3
+}
+
+QJpegHandler::~QJpegHandler()
+{
+ delete d;
+}
+
+bool QJpegHandler::canRead() const
+{
+ if(d->state == QJpegHandlerPrivate::Ready && !canRead(device()))
+ return false;
+
+ if (d->state != QJpegHandlerPrivate::Error) {
+ setFormat("jpeg");
+ return true;
+ }
+
+ return false;
+}
+
+bool QJpegHandler::canRead(QIODevice *device)
+{
+ if (!device) {
+ qWarning("QJpegHandler::canRead() called with no device");
+ return false;
+ }
+
+ char buffer[2];
+ if (device->peek(buffer, 2) != 2)
+ return false;
+ return uchar(buffer[0]) == 0xff && uchar(buffer[1]) == 0xd8;
+}
+
+bool QJpegHandler::read(QImage *image)
+{
+ if (!canRead())
+ return false;
+ return d->read(image);
+}
+
+bool QJpegHandler::write(const QImage &image)
+{
+ return write_jpeg_image(image, device(), d->quality);
+}
+
+bool QJpegHandler::supportsOption(ImageOption option) const
+{
+ return option == Quality
+ || option == ScaledSize
+ || option == ScaledClipRect
+ || option == ClipRect
+ || option == Size
+ || option == ImageFormat;
+}
+
+QVariant QJpegHandler::option(ImageOption option) const
+{
+ switch(option) {
+ case Quality:
+ return d->quality;
+ case ScaledSize:
+ return d->scaledSize;
+ case ScaledClipRect:
+ return d->scaledClipRect;
+ case ClipRect:
+ return d->clipRect;
+ case Size:
+ d->readJpegHeader(device());
+ return d->size;
+ case ImageFormat:
+ d->readJpegHeader(device());
+ return d->format;
+ default:
+ return QVariant();
+ }
+}
+
+void QJpegHandler::setOption(ImageOption option, const QVariant &value)
+{
+ switch(option) {
+ case Quality:
+ d->quality = value.toInt();
+ break;
+ case ScaledSize:
+ d->scaledSize = value.toSize();
+ break;
+ case ScaledClipRect:
+ d->scaledClipRect = value.toRect();
+ break;
+ case ClipRect:
+ d->clipRect = value.toRect();
+ break;
+ default:
+ break;
+ }
+}
+
+QByteArray QJpegHandler::name() const
+{
+ return "jpeg";
+}
+
+
+
+
+QT_END_NAMESPACE
diff --git a/src/gui/image/qjpeghandler.pri b/src/gui/image/qjpeghandler.pri
new file mode 100644
index 0000000000..3cb35c95ed
--- /dev/null
+++ b/src/gui/image/qjpeghandler.pri
@@ -0,0 +1,10 @@
+# common to plugin and built-in forms
+INCLUDEPATH *= $$PWD
+HEADERS += $$PWD/qjpeghandler_p.h
+SOURCES += $$PWD/qjpeghandler.cpp
+contains(QT_CONFIG, system-jpeg) {
+ if(unix|win32-g++*): LIBS += -ljpeg
+ else:win32: LIBS += libjpeg.lib
+} else {
+ include($$PWD/../../3rdparty/libjpeg.pri)
+}
diff --git a/src/gui/image/qjpeghandler_p.h b/src/gui/image/qjpeghandler_p.h
new file mode 100644
index 0000000000..0bd7894482
--- /dev/null
+++ b/src/gui/image/qjpeghandler_p.h
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QJPEGHANDLER_P_H
+#define QJPEGHANDLER_P_H
+
+#include <QtGui/qimageiohandler.h>
+#include <QtCore/QSize>
+#include <QtCore/QRect>
+
+QT_BEGIN_NAMESPACE
+
+class QJpegHandlerPrivate;
+class QJpegHandler : public QImageIOHandler
+{
+public:
+ QJpegHandler();
+ ~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:
+ QJpegHandlerPrivate *d;
+};
+
+QT_END_NAMESPACE
+
+#endif // QJPEGHANDLER_P_H
diff --git a/src/gui/image/qmnghandler.cpp b/src/gui/image/qmnghandler.cpp
new file mode 100644
index 0000000000..6b918af0c3
--- /dev/null
+++ b/src/gui/image/qmnghandler.cpp
@@ -0,0 +1,497 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qmnghandler_p.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;
+ 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()
+{
+}
+
+/*! \reimp */
+bool QMngHandler::canRead() const
+{
+ Q_D(const QMngHandler);
+ if ((!d->haveReadNone
+ && (!d->haveReadAll || (d->haveReadAll && (d->nextIndex < d->frameCount))))
+ || 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(qvariant_cast<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/gui/image/qmnghandler.pri b/src/gui/image/qmnghandler.pri
new file mode 100644
index 0000000000..ffb98de92b
--- /dev/null
+++ b/src/gui/image/qmnghandler.pri
@@ -0,0 +1,10 @@
+# common to plugin and built-in forms
+INCLUDEPATH *= $$PWD
+HEADERS += $$PWD/qmnghandler_p.h
+SOURCES += $$PWD/qmnghandler.cpp
+contains(QT_CONFIG, system-mng) {
+ if(unix|win32-g++*):LIBS += -lmng
+ else:win32: LIBS += libmng.lib
+} else {
+ include($$PWD/../../3rdparty/libmng.pri)
+}
diff --git a/src/gui/image/qmnghandler_p.h b/src/gui/image/qmnghandler_p.h
new file mode 100644
index 0000000000..8436b9610b
--- /dev/null
+++ b/src/gui/image/qmnghandler_p.h
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMNGHANDLER_P_H
+#define QMNGHANDLER_P_H
+
+#include <QtCore/qscopedpointer.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)
+ QScopedPointer<QMngHandlerPrivate> d_ptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QMNGHANDLER_P_H
diff --git a/src/gui/image/qmovie.cpp b/src/gui/image/qmovie.cpp
new file mode 100644
index 0000000000..8b6e360096
--- /dev/null
+++ b/src/gui/image/qmovie.cpp
@@ -0,0 +1,1089 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \class QMovie
+
+ \brief The QMovie class is a convenience class for playing movies
+ with QImageReader.
+
+ \ingroup painting
+
+ This class is used to show simple animations without sound. If you want
+ to display video and media content, use the \l{Phonon Module}{Phonon}
+ multimedia framework instead.
+
+ First, create a QMovie object by passing either the name of a file or a
+ pointer to a QIODevice containing an animated image format to QMovie's
+ constructor. You can call isValid() to check if the image data is valid,
+ before starting the movie. To start the movie, call start(). QMovie will
+ enter \l Running state, and emit started() and stateChanged(). To get the
+ current state of the movie, call state().
+
+ To display the movie in your application, you can pass your QMovie object
+ to QLabel::setMovie(). Example:
+
+ \snippet doc/src/snippets/code/src_gui_image_qmovie.cpp 0
+
+ Whenever a new frame is available in the movie, QMovie will emit
+ updated(). If the size of the frame changes, resized() is emitted. You can
+ call currentImage() or currentPixmap() to get a copy of the current
+ frame. When the movie is done, QMovie emits finished(). If any error
+ occurs during playback (i.e, the image file is corrupt), QMovie will emit
+ error().
+
+ You can control the speed of the movie playback by calling setSpeed(),
+ which takes the percentage of the original speed as an argument. Pause the
+ movie by calling setPaused(true). QMovie will then enter \l Paused state
+ and emit stateChanged(). If you call setPaused(false), QMovie will reenter
+ \l Running state and start the movie again. To stop the movie, call
+ stop().
+
+ Certain animation formats allow you to set the background color. You can
+ call setBackgroundColor() to set the color, or backgroundColor() to
+ retrieve the current background color.
+
+ currentFrameNumber() returns the sequence number of the current frame. The
+ first frame in the animation has the sequence number 0. frameCount()
+ returns the total number of frames in the animation, if the image format
+ supports this. You can call loopCount() to get the number of times the
+ movie should loop before finishing. nextFrameDelay() returns the number of
+ milliseconds the current frame should be displayed.
+
+ QMovie can be instructed to cache frames of an animation by calling
+ setCacheMode().
+
+ Call supportedFormats() for a list of formats that QMovie supports.
+
+ \sa QLabel, QImageReader, {Movie Example}
+*/
+
+/*! \enum QMovie::MovieState
+
+ This enum describes the different states of QMovie.
+
+ \value NotRunning The movie is not running. This is QMovie's initial
+ state, and the state it enters after stop() has been called or the movie
+ is finished.
+
+ \value Paused The movie is paused, and QMovie stops emitting updated() or
+ resized(). This state is entered after calling pause() or
+ setPaused(true). The current frame number it kept, and the movie will
+ continue with the next frame when unpause() or setPaused(false) is called.
+
+ \value Running The movie is running.
+*/
+
+/*! \enum QMovie::CacheMode
+
+ This enum describes the different cache modes of QMovie.
+
+ \value CacheNone No frames are cached (the default).
+
+ \value CacheAll All frames are cached.
+*/
+
+/*! \fn void QMovie::started()
+
+ This signal is emitted after QMovie::start() has been called, and QMovie
+ has entered QMovie::Running state.
+*/
+
+/*! \fn void QMovie::resized(const QSize &size)
+
+ This signal is emitted when the current frame has been resized to \a
+ size. This effect is sometimes used in animations as an alternative to
+ replacing the frame. You can call currentImage() or currentPixmap() to get a
+ copy of the updated frame.
+*/
+
+/*! \fn void QMovie::updated(const QRect &rect)
+
+ This signal is emitted when the rect \a rect in the current frame has been
+ updated. You can call currentImage() or currentPixmap() to get a copy of the
+ updated frame.
+*/
+
+/*! \fn void QMovie::frameChanged(int frameNumber)
+ \since 4.1
+
+ This signal is emitted when the frame number has changed to
+ \a frameNumber. You can call currentImage() or currentPixmap() to get a
+ copy of the frame.
+*/
+
+/*!
+ \fn void QMovie::stateChanged(QMovie::MovieState state)
+
+ This signal is emitted every time the state of the movie changes. The new
+ state is specified by \a state.
+
+ \sa QMovie::state()
+*/
+
+/*! \fn void QMovie::error(QImageReader::ImageReaderError error)
+
+ This signal is emitted by QMovie when the error \a error occurred during
+ playback. QMovie will stop the movie, and enter QMovie::NotRunning state.
+*/
+
+/*! \fn void QMovie::finished()
+
+ This signal is emitted when the movie has finished.
+
+ \sa QMovie::stop()
+*/
+
+#include "qglobal.h"
+
+#ifndef QT_NO_MOVIE
+
+#include "qmovie.h"
+#include "qimage.h"
+#include "qimagereader.h"
+#include "qpixmap.h"
+#include "qrect.h"
+#include "qdatetime.h"
+#include "qtimer.h"
+#include "qpair.h"
+#include "qmap.h"
+#include "qlist.h"
+#include "qbuffer.h"
+#include "qdir.h"
+#include "private/qobject_p.h"
+
+#define QMOVIE_INVALID_DELAY -1
+
+QT_BEGIN_NAMESPACE
+
+class QFrameInfo
+{
+public:
+ QPixmap pixmap;
+ int delay;
+ bool endMark;
+ inline QFrameInfo(bool endMark)
+ : pixmap(QPixmap()), delay(QMOVIE_INVALID_DELAY), endMark(endMark)
+ { }
+
+ inline QFrameInfo()
+ : pixmap(QPixmap()), delay(QMOVIE_INVALID_DELAY), endMark(false)
+ { }
+
+ inline QFrameInfo(const QPixmap &pixmap, int delay)
+ : pixmap(pixmap), delay(delay), endMark(false)
+ { }
+
+ inline bool isValid()
+ {
+ return endMark || !(pixmap.isNull() && (delay == QMOVIE_INVALID_DELAY));
+ }
+
+ inline bool isEndMarker()
+ { return endMark; }
+
+ static inline QFrameInfo endMarker()
+ { return QFrameInfo(true); }
+};
+
+class QMoviePrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QMovie)
+
+public:
+ QMoviePrivate(QMovie *qq);
+ bool isDone();
+ bool next();
+ int speedAdjustedDelay(int delay) const;
+ bool isValid() const;
+ bool jumpToFrame(int frameNumber);
+ int frameCount() const;
+ bool jumpToNextFrame();
+ QFrameInfo infoForFrame(int frameNumber);
+ void reset();
+
+ inline void enterState(QMovie::MovieState newState) {
+ movieState = newState;
+ emit q_func()->stateChanged(newState);
+ }
+
+ // private slots
+ void _q_loadNextFrame();
+ void _q_loadNextFrame(bool starting);
+
+ QImageReader *reader;
+ int speed;
+ QMovie::MovieState movieState;
+ QRect frameRect;
+ QPixmap currentPixmap;
+ int currentFrameNumber;
+ int nextFrameNumber;
+ int greatestFrameNumber;
+ int nextDelay;
+ int playCounter;
+ qint64 initialDevicePos;
+ QMovie::CacheMode cacheMode;
+ bool haveReadAll;
+ bool isFirstIteration;
+ QMap<int, QFrameInfo> frameMap;
+ QString absoluteFilePath;
+
+ QTimer nextImageTimer;
+};
+
+/*! \internal
+ */
+QMoviePrivate::QMoviePrivate(QMovie *qq)
+ : reader(0), speed(100), movieState(QMovie::NotRunning),
+ currentFrameNumber(-1), nextFrameNumber(0), greatestFrameNumber(-1),
+ nextDelay(0), playCounter(-1),
+ cacheMode(QMovie::CacheNone), haveReadAll(false), isFirstIteration(true)
+{
+ q_ptr = qq;
+ nextImageTimer.setSingleShot(true);
+}
+
+/*! \internal
+ */
+void QMoviePrivate::reset()
+{
+ nextImageTimer.stop();
+ if (reader->device())
+ initialDevicePos = reader->device()->pos();
+ currentFrameNumber = -1;
+ nextFrameNumber = 0;
+ greatestFrameNumber = -1;
+ nextDelay = 0;
+ playCounter = -1;
+ haveReadAll = false;
+ isFirstIteration = true;
+ frameMap.clear();
+}
+
+/*! \internal
+ */
+bool QMoviePrivate::isDone()
+{
+ return (playCounter == 0);
+}
+
+/*!
+ \internal
+
+ Given the original \a delay, this function returns the
+ actual number of milliseconds to delay according to
+ the current speed. E.g. if the speed is 200%, the
+ result will be half of the original delay.
+*/
+int QMoviePrivate::speedAdjustedDelay(int delay) const
+{
+ return int( (qint64(delay) * qint64(100) ) / qint64(speed) );
+}
+
+/*!
+ \internal
+
+ Returns the QFrameInfo for the given \a frameNumber.
+
+ If the frame number is invalid, an invalid QFrameInfo is
+ returned.
+
+ If the end of the animation has been reached, a
+ special end marker QFrameInfo is returned.
+
+*/
+QFrameInfo QMoviePrivate::infoForFrame(int frameNumber)
+{
+ if (frameNumber < 0)
+ return QFrameInfo(); // Invalid
+
+ if (haveReadAll && (frameNumber > greatestFrameNumber)) {
+ if (frameNumber == greatestFrameNumber+1)
+ return QFrameInfo::endMarker();
+ return QFrameInfo(); // Invalid
+ }
+
+ if (cacheMode == QMovie::CacheNone) {
+ if (frameNumber != currentFrameNumber+1) {
+ // Non-sequential frame access
+ if (!reader->jumpToImage(frameNumber)) {
+ if (frameNumber == 0) {
+ // Special case: Attempt to "rewind" so we can loop
+ // ### This could be implemented as QImageReader::rewind()
+ if (reader->device()->isSequential())
+ return QFrameInfo(); // Invalid
+ QString fileName = reader->fileName();
+ QByteArray format = reader->format();
+ QIODevice *device = reader->device();
+ QColor bgColor = reader->backgroundColor();
+ QSize scaledSize = reader->scaledSize();
+ delete reader;
+ if (fileName.isEmpty())
+ reader = new QImageReader(device, format);
+ else
+ reader = new QImageReader(absoluteFilePath, format);
+ (void)reader->canRead(); // Provoke a device->open() call
+ reader->device()->seek(initialDevicePos);
+ reader->setBackgroundColor(bgColor);
+ reader->setScaledSize(scaledSize);
+ } else {
+ return QFrameInfo(); // Invalid
+ }
+ }
+ }
+ if (reader->canRead()) {
+ // reader says we can read. Attempt to actually read image
+ QImage anImage = reader->read();
+ if (anImage.isNull()) {
+ // Reading image failed.
+ return QFrameInfo(); // Invalid
+ }
+ if (frameNumber > greatestFrameNumber)
+ greatestFrameNumber = frameNumber;
+ QPixmap aPixmap = QPixmap::fromImage(anImage);
+ int aDelay = reader->nextImageDelay();
+ return QFrameInfo(aPixmap, aDelay);
+ } else if (frameNumber != 0) {
+ // We've read all frames now. Return an end marker
+ haveReadAll = true;
+ return QFrameInfo::endMarker();
+ } else {
+ // No readable frames
+ haveReadAll = true;
+ return QFrameInfo();
+ }
+ }
+
+ // CacheMode == CacheAll
+ if (frameNumber > greatestFrameNumber) {
+ // Frame hasn't been read from file yet. Try to do it
+ for (int i = greatestFrameNumber + 1; i <= frameNumber; ++i) {
+ if (reader->canRead()) {
+ // reader says we can read. Attempt to actually read image
+ QImage anImage = reader->read();
+ if (anImage.isNull()) {
+ // Reading image failed.
+ return QFrameInfo(); // Invalid
+ }
+ greatestFrameNumber = i;
+ QPixmap aPixmap = QPixmap::fromImage(anImage);
+ int aDelay = reader->nextImageDelay();
+ QFrameInfo info(aPixmap, aDelay);
+ // Cache it!
+ frameMap.insert(i, info);
+ if (i == frameNumber) {
+ return info;
+ }
+ } else {
+ // We've read all frames now. Return an end marker
+ haveReadAll = true;
+ return QFrameInfo::endMarker();
+ }
+ }
+ }
+ // Return info for requested (cached) frame
+ return frameMap.value(frameNumber);
+}
+
+/*!
+ \internal
+
+ Attempts to advance the animation to the next frame.
+ If successful, currentFrameNumber, currentPixmap and
+ nextDelay are updated accordingly, and true is returned.
+ Otherwise, false is returned.
+ When false is returned, isDone() can be called to
+ determine whether the animation ended gracefully or
+ an error occurred when reading the frame.
+*/
+bool QMoviePrivate::next()
+{
+ QTime time;
+ time.start();
+ QFrameInfo info = infoForFrame(nextFrameNumber);
+ if (!info.isValid())
+ return false;
+ if (info.isEndMarker()) {
+ // We reached the end of the animation.
+ if (isFirstIteration) {
+ if (nextFrameNumber == 0) {
+ // No frames could be read at all (error).
+ return false;
+ }
+ // End of first iteration. Initialize play counter
+ playCounter = reader->loopCount();
+ isFirstIteration = false;
+ }
+ // Loop as appropriate
+ if (playCounter != 0) {
+ if (playCounter != -1) // Infinite?
+ playCounter--; // Nope
+ nextFrameNumber = 0;
+ return next();
+ }
+ // Loop no more. Done
+ return false;
+ }
+ // Image and delay OK, update internal state
+ currentFrameNumber = nextFrameNumber++;
+ QSize scaledSize = reader->scaledSize();
+ if (scaledSize.isValid() && (scaledSize != info.pixmap.size()))
+ currentPixmap = QPixmap::fromImage( info.pixmap.toImage().scaled(scaledSize) );
+ else
+ currentPixmap = info.pixmap;
+ nextDelay = speedAdjustedDelay(info.delay);
+ // Adjust delay according to the time it took to read the frame
+ int processingTime = time.elapsed();
+ if (processingTime > nextDelay)
+ nextDelay = 0;
+ else
+ nextDelay = nextDelay - processingTime;
+ return true;
+}
+
+/*! \internal
+ */
+void QMoviePrivate::_q_loadNextFrame()
+{
+ _q_loadNextFrame(false);
+}
+
+void QMoviePrivate::_q_loadNextFrame(bool starting)
+{
+ Q_Q(QMovie);
+ if (next()) {
+ if (starting && movieState == QMovie::NotRunning) {
+ enterState(QMovie::Running);
+ emit q->started();
+ }
+
+ if (frameRect.size() != currentPixmap.rect().size()) {
+ frameRect = currentPixmap.rect();
+ emit q->resized(frameRect.size());
+ }
+
+ emit q->updated(frameRect);
+ emit q->frameChanged(currentFrameNumber);
+
+ if (movieState == QMovie::Running)
+ nextImageTimer.start(nextDelay);
+ } else {
+ // Could not read another frame
+ if (!isDone()) {
+ emit q->error(reader->error());
+ }
+
+ // Graceful finish
+ if (movieState != QMovie::Paused) {
+ nextFrameNumber = 0;
+ isFirstIteration = true;
+ playCounter = -1;
+ enterState(QMovie::NotRunning);
+ emit q->finished();
+ }
+ }
+}
+
+/*!
+ \internal
+*/
+bool QMoviePrivate::isValid() const
+{
+ return (greatestFrameNumber >= 0) // have we seen valid data
+ || reader->canRead(); // or does the reader see valid data
+}
+
+/*!
+ \internal
+*/
+bool QMoviePrivate::jumpToFrame(int frameNumber)
+{
+ if (frameNumber < 0)
+ return false;
+ if (currentFrameNumber == frameNumber)
+ return true;
+ nextFrameNumber = frameNumber;
+ if (movieState == QMovie::Running)
+ nextImageTimer.stop();
+ _q_loadNextFrame();
+ return (nextFrameNumber == currentFrameNumber+1);
+}
+
+/*!
+ \internal
+*/
+int QMoviePrivate::frameCount() const
+{
+ int result;
+ if ((result = reader->imageCount()) != 0)
+ return result;
+ if (haveReadAll)
+ return greatestFrameNumber+1;
+ return 0; // Don't know
+}
+
+/*!
+ \internal
+*/
+bool QMoviePrivate::jumpToNextFrame()
+{
+ return jumpToFrame(currentFrameNumber+1);
+}
+
+/*!
+ Constructs a QMovie object, passing the \a parent object to QObject's
+ constructor.
+
+ \sa setFileName(), setDevice(), setFormat()
+ */
+QMovie::QMovie(QObject *parent)
+ : QObject(*new QMoviePrivate(this), parent)
+{
+ Q_D(QMovie);
+ d->reader = new QImageReader;
+ connect(&d->nextImageTimer, SIGNAL(timeout()), this, SLOT(_q_loadNextFrame()));
+}
+
+/*!
+ Constructs a QMovie object. QMovie will use read image data from \a
+ device, which it assumes is open and readable. If \a format is not empty,
+ QMovie will use the image format \a format for decoding the image
+ data. Otherwise, QMovie will attempt to guess the format.
+
+ The \a parent object is passed to QObject's constructor.
+ */
+QMovie::QMovie(QIODevice *device, const QByteArray &format, QObject *parent)
+ : QObject(*new QMoviePrivate(this), parent)
+{
+ Q_D(QMovie);
+ d->reader = new QImageReader(device, format);
+ d->initialDevicePos = device->pos();
+ connect(&d->nextImageTimer, SIGNAL(timeout()), this, SLOT(_q_loadNextFrame()));
+}
+
+/*!
+ Constructs a QMovie object. QMovie will use read image data from \a
+ fileName. If \a format is not empty, QMovie will use the image format \a
+ format for decoding the image data. Otherwise, QMovie will attempt to
+ guess the format.
+
+ The \a parent object is passed to QObject's constructor.
+ */
+QMovie::QMovie(const QString &fileName, const QByteArray &format, QObject *parent)
+ : QObject(*new QMoviePrivate(this), parent)
+{
+ Q_D(QMovie);
+ d->absoluteFilePath = QDir(fileName).absolutePath();
+ d->reader = new QImageReader(fileName, format);
+ if (d->reader->device())
+ d->initialDevicePos = d->reader->device()->pos();
+ connect(&d->nextImageTimer, SIGNAL(timeout()), this, SLOT(_q_loadNextFrame()));
+}
+
+/*!
+ Destructs the QMovie object.
+*/
+QMovie::~QMovie()
+{
+ Q_D(QMovie);
+ delete d->reader;
+}
+
+/*!
+ Sets the current device to \a device. QMovie will read image data from
+ this device when the movie is running.
+
+ \sa device(), setFormat()
+*/
+void QMovie::setDevice(QIODevice *device)
+{
+ Q_D(QMovie);
+ d->reader->setDevice(device);
+ d->reset();
+}
+
+/*!
+ Returns the device QMovie reads image data from. If no device has
+ currently been assigned, 0 is returned.
+
+ \sa setDevice(), fileName()
+*/
+QIODevice *QMovie::device() const
+{
+ Q_D(const QMovie);
+ return d->reader->device();
+}
+
+/*!
+ Sets the name of the file that QMovie reads image data from, to \a
+ fileName.
+
+ \sa fileName(), setDevice(), setFormat()
+*/
+void QMovie::setFileName(const QString &fileName)
+{
+ Q_D(QMovie);
+ d->absoluteFilePath = QDir(fileName).absolutePath();
+ d->reader->setFileName(fileName);
+ d->reset();
+}
+
+/*!
+ Returns the name of the file that QMovie reads image data from. If no file
+ name has been assigned, or if the assigned device is not a file, an empty
+ QString is returned.
+
+ \sa setFileName(), device()
+*/
+QString QMovie::fileName() const
+{
+ Q_D(const QMovie);
+ return d->reader->fileName();
+}
+
+/*!
+ Sets the format that QMovie will use when decoding image data, to \a
+ format. By default, QMovie will attempt to guess the format of the image
+ data.
+
+ You can call supportedFormats() for the full list of formats
+ QMovie supports.
+
+ \sa QImageReader::supportedImageFormats()
+*/
+void QMovie::setFormat(const QByteArray &format)
+{
+ Q_D(QMovie);
+ d->reader->setFormat(format);
+}
+
+/*!
+ Returns the format that QMovie uses when decoding image data. If no format
+ has been assigned, an empty QByteArray() is returned.
+
+ \sa setFormat()
+*/
+QByteArray QMovie::format() const
+{
+ Q_D(const QMovie);
+ return d->reader->format();
+}
+
+/*!
+ For image formats that support it, this function sets the background color
+ to \a color.
+
+ \sa backgroundColor()
+*/
+void QMovie::setBackgroundColor(const QColor &color)
+{
+ Q_D(QMovie);
+ d->reader->setBackgroundColor(color);
+}
+
+/*!
+ Returns the background color of the movie. If no background color has been
+ assigned, an invalid QColor is returned.
+
+ \sa setBackgroundColor()
+*/
+QColor QMovie::backgroundColor() const
+{
+ Q_D(const QMovie);
+ return d->reader->backgroundColor();
+}
+
+/*!
+ Returns the current state of QMovie.
+
+ \sa MovieState, stateChanged()
+*/
+QMovie::MovieState QMovie::state() const
+{
+ Q_D(const QMovie);
+ return d->movieState;
+}
+
+/*!
+ Returns the rect of the last frame. If no frame has yet been updated, an
+ invalid QRect is returned.
+
+ \sa currentImage(), currentPixmap()
+*/
+QRect QMovie::frameRect() const
+{
+ Q_D(const QMovie);
+ return d->frameRect;
+}
+
+/*! \fn QImage QMovie::framePixmap() const
+
+ Use currentPixmap() instead.
+*/
+
+/*! \fn void QMovie::pause()
+
+ Use setPaused(true) instead.
+*/
+
+/*! \fn void QMovie::unpause()
+
+ Use setPaused(false) instead.
+*/
+
+/*!
+ Returns the current frame as a QPixmap.
+
+ \sa currentImage(), updated()
+*/
+QPixmap QMovie::currentPixmap() const
+{
+ Q_D(const QMovie);
+ return d->currentPixmap;
+}
+
+/*! \fn QImage QMovie::frameImage() const
+
+ Use currentImage() instead.
+*/
+
+/*!
+ Returns the current frame as a QImage.
+
+ \sa currentPixmap(), updated()
+*/
+QImage QMovie::currentImage() const
+{
+ Q_D(const QMovie);
+ return d->currentPixmap.toImage();
+}
+
+/*!
+ Returns true if the movie is valid (e.g., the image data is readable and
+ the image format is supported); otherwise returns false.
+*/
+bool QMovie::isValid() const
+{
+ Q_D(const QMovie);
+ return d->isValid();
+}
+
+/*! \fn bool QMovie::running() const
+
+ Use state() instead.
+*/
+
+/*! \fn bool QMovie::isNull() const
+
+ Use isValid() instead.
+*/
+
+/*! \fn int QMovie::frameNumber() const
+
+ Use currentFrameNumber() instead.
+*/
+
+/*! \fn bool QMovie::paused() const
+
+ Use state() instead.
+*/
+
+/*! \fn bool QMovie::finished() const
+
+ Use state() instead.
+*/
+
+/*! \fn void QMovie::restart()
+
+ Use stop() and start() instead.
+*/
+
+/*!
+ \fn void QMovie::step()
+
+ Use jumpToNextFrame() instead.
+*/
+
+/*!
+ Returns the number of frames in the movie.
+
+ Certain animation formats do not support this feature, in which
+ case 0 is returned.
+*/
+int QMovie::frameCount() const
+{
+ Q_D(const QMovie);
+ return d->frameCount();
+}
+
+/*!
+ Returns the number of milliseconds QMovie will wait before updating the
+ next frame in the animation.
+*/
+int QMovie::nextFrameDelay() const
+{
+ Q_D(const QMovie);
+ return d->nextDelay;
+}
+
+/*!
+ Returns the sequence number of the current frame. The number of the first
+ frame in the movie is 0.
+*/
+int QMovie::currentFrameNumber() const
+{
+ Q_D(const QMovie);
+ return d->currentFrameNumber;
+}
+
+/*!
+ Jumps to the next frame. Returns true on success; otherwise returns false.
+*/
+bool QMovie::jumpToNextFrame()
+{
+ Q_D(QMovie);
+ return d->jumpToNextFrame();
+}
+
+/*!
+ Jumps to frame number \a frameNumber. Returns true on success; otherwise
+ returns false.
+*/
+bool QMovie::jumpToFrame(int frameNumber)
+{
+ Q_D(QMovie);
+ return d->jumpToFrame(frameNumber);
+}
+
+/*!
+ Returns the number of times the movie will loop before it finishes.
+ If the movie will only play once (no looping), loopCount returns 0.
+ If the movie loops forever, loopCount returns -1.
+
+ Note that, if the image data comes from a sequential device (e.g. a
+ socket), QMovie can only loop the movie if the cacheMode is set to
+ QMovie::CacheAll.
+*/
+int QMovie::loopCount() const
+{
+ Q_D(const QMovie);
+ return d->reader->loopCount();
+}
+
+/*!
+ If \a paused is true, QMovie will enter \l Paused state and emit
+ stateChanged(Paused); otherwise it will enter \l Running state and emit
+ stateChanged(Running).
+
+ \sa state()
+*/
+void QMovie::setPaused(bool paused)
+{
+ Q_D(QMovie);
+ if (paused) {
+ if (d->movieState == NotRunning)
+ return;
+ d->enterState(Paused);
+ d->nextImageTimer.stop();
+ } else {
+ if (d->movieState == Running)
+ return;
+ d->enterState(Running);
+ d->nextImageTimer.start(nextFrameDelay());
+ }
+}
+
+/*!
+ \property QMovie::speed
+ \brief the movie's speed
+
+ The speed is measured in percentage of the original movie speed.
+ The default speed is 100%.
+ Example:
+
+ \snippet doc/src/snippets/code/src_gui_image_qmovie.cpp 1
+*/
+void QMovie::setSpeed(int percentSpeed)
+{
+ Q_D(QMovie);
+ d->speed = percentSpeed;
+}
+
+int QMovie::speed() const
+{
+ Q_D(const QMovie);
+ return d->speed;
+}
+
+/*!
+ Starts the movie. QMovie will enter \l Running state, and start emitting
+ updated() and resized() as the movie progresses.
+
+ If QMovie is in the \l Paused state, this function is equivalent
+ to calling setPaused(false). If QMovie is already in the \l
+ Running state, this function does nothing.
+
+ \sa stop(), setPaused()
+*/
+void QMovie::start()
+{
+ Q_D(QMovie);
+ if (d->movieState == NotRunning) {
+ d->_q_loadNextFrame(true);
+ } else if (d->movieState == Paused) {
+ setPaused(false);
+ }
+}
+
+/*!
+ Stops the movie. QMovie enters \l NotRunning state, and stops emitting
+ updated() and resized(). If start() is called again, the movie will
+ restart from the beginning.
+
+ If QMovie is already in the \l NotRunning state, this function
+ does nothing.
+
+ \sa start(), setPaused()
+*/
+void QMovie::stop()
+{
+ Q_D(QMovie);
+ if (d->movieState == NotRunning)
+ return;
+ d->enterState(NotRunning);
+ d->nextImageTimer.stop();
+ d->nextFrameNumber = 0;
+}
+
+/*!
+ \since 4.1
+
+ Returns the scaled size of frames.
+
+ \sa QImageReader::scaledSize()
+*/
+QSize QMovie::scaledSize()
+{
+ Q_D(QMovie);
+ return d->reader->scaledSize();
+}
+
+/*!
+ \since 4.1
+
+ Sets the scaled frame size to \a size.
+
+ \sa QImageReader::setScaledSize()
+*/
+void QMovie::setScaledSize(const QSize &size)
+{
+ Q_D(QMovie);
+ d->reader->setScaledSize(size);
+}
+
+/*!
+ \since 4.1
+
+ Returns the list of image formats supported by QMovie.
+
+ \sa QImageReader::supportedImageFormats()
+*/
+QList<QByteArray> QMovie::supportedFormats()
+{
+ QList<QByteArray> list = QImageReader::supportedImageFormats();
+ QMutableListIterator<QByteArray> it(list);
+ QBuffer buffer;
+ buffer.open(QIODevice::ReadOnly);
+ while (it.hasNext()) {
+ QImageReader reader(&buffer, it.next());
+ if (!reader.supportsAnimation())
+ it.remove();
+ }
+ return list;
+}
+
+/*!
+ \property QMovie::cacheMode
+ \brief the movie's cache mode
+
+ Caching frames can be useful when the underlying animation format handler
+ that QMovie relies on to decode the animation data does not support
+ jumping to particular frames in the animation, or even "rewinding" the
+ animation to the beginning (for looping). Furthermore, if the image data
+ comes from a sequential device, it is not possible for the underlying
+ animation handler to seek back to frames whose data has already been read
+ (making looping altogether impossible).
+
+ To aid in such situations, a QMovie object can be instructed to cache the
+ frames, at the added memory cost of keeping the frames in memory for the
+ lifetime of the object.
+
+ By default, this property is set to \l CacheNone.
+
+ \sa QMovie::CacheMode
+*/
+
+QMovie::CacheMode QMovie::cacheMode() const
+{
+ Q_D(const QMovie);
+ return d->cacheMode;
+}
+
+void QMovie::setCacheMode(CacheMode cacheMode)
+{
+ Q_D(QMovie);
+ d->cacheMode = cacheMode;
+}
+
+/*!
+ \internal
+*/
+QMovie::CacheMode QMovie::cacheMode()
+{
+ Q_D(QMovie);
+ return d->cacheMode;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qmovie.cpp"
+
+#endif // QT_NO_MOVIE
diff --git a/src/gui/image/qmovie.h b/src/gui/image/qmovie.h
new file mode 100644
index 0000000000..b64df29c41
--- /dev/null
+++ b/src/gui/image/qmovie.h
@@ -0,0 +1,177 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMOVIE_H
+#define QMOVIE_H
+
+#include <QtCore/qobject.h>
+
+#ifndef QT_NO_MOVIE
+
+#include <QtCore/qbytearray.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qobject.h>
+#include <QtGui/qimagereader.h>
+
+#ifdef QT3_SUPPORT
+#include <QtGui/qimage.h>
+#include <QtGui/qpixmap.h>
+#endif
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QByteArray;
+class QColor;
+class QIODevice;
+class QImage;
+class QPixmap;
+class QRect;
+class QSize;
+
+class QMoviePrivate;
+class Q_GUI_EXPORT QMovie : public QObject
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QMovie)
+ Q_ENUMS(MovieState CacheMode)
+ Q_PROPERTY(int speed READ speed WRITE setSpeed)
+ Q_PROPERTY(CacheMode cacheMode READ cacheMode WRITE setCacheMode)
+public:
+ enum MovieState {
+ NotRunning,
+ Paused,
+ Running
+ };
+ enum CacheMode {
+ CacheNone,
+ CacheAll
+ };
+
+ QMovie(QObject *parent = 0);
+ explicit QMovie(QIODevice *device, const QByteArray &format = QByteArray(), QObject *parent = 0);
+ explicit QMovie(const QString &fileName, const QByteArray &format = QByteArray(), QObject *parent = 0);
+ ~QMovie();
+
+ static QList<QByteArray> supportedFormats();
+
+ void setDevice(QIODevice *device);
+ QIODevice *device() const;
+
+ void setFileName(const QString &fileName);
+ QString fileName() const;
+
+ void setFormat(const QByteArray &format);
+ QByteArray format() const;
+
+ void setBackgroundColor(const QColor &color);
+ QColor backgroundColor() const;
+
+ MovieState state() const;
+
+ QRect frameRect() const;
+ QImage currentImage() const;
+ QPixmap currentPixmap() const;
+
+ bool isValid() const;
+
+ bool jumpToFrame(int frameNumber);
+ int loopCount() const;
+ int frameCount() const;
+ int nextFrameDelay() const;
+ int currentFrameNumber() const;
+
+ int speed() const;
+
+ QSize scaledSize();
+ void setScaledSize(const QSize &size);
+
+ CacheMode cacheMode() const;
+ void setCacheMode(CacheMode mode);
+
+ CacheMode cacheMode(); // ### Qt 5: remove me
+
+Q_SIGNALS:
+ void started();
+ void resized(const QSize &size);
+ void updated(const QRect &rect);
+ void stateChanged(QMovie::MovieState state);
+ void error(QImageReader::ImageReaderError error);
+ void finished();
+ void frameChanged(int frameNumber);
+
+public Q_SLOTS:
+ void start();
+ bool jumpToNextFrame();
+ void setPaused(bool paused);
+ void stop();
+ void setSpeed(int percentSpeed);
+
+private:
+ Q_DISABLE_COPY(QMovie)
+ Q_PRIVATE_SLOT(d_func(), void _q_loadNextFrame())
+
+#ifdef QT3_SUPPORT
+public:
+ inline QT3_SUPPORT bool isNull() const { return isValid(); }
+ inline QT3_SUPPORT int frameNumber() const { return currentFrameNumber(); }
+ inline QT3_SUPPORT bool running() const { return state() == Running; }
+ inline QT3_SUPPORT bool paused() const { return state() == Paused; }
+ inline QT3_SUPPORT bool finished() const { return state() == NotRunning; }
+ inline QT3_SUPPORT void restart() { stop(); start(); }
+ inline QT3_SUPPORT QImage frameImage() const { return currentImage(); }
+ inline QT3_SUPPORT QPixmap framePixmap() const { return currentPixmap(); }
+ inline QT3_SUPPORT void step() { jumpToNextFrame(); }
+ inline QT3_SUPPORT void pause() { setPaused(true); }
+ inline QT3_SUPPORT void unpause() { setPaused(false); }
+#endif
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QT_NO_MOVIE
+
+#endif // QMOVIE_H
diff --git a/src/gui/image/qnativeimage.cpp b/src/gui/image/qnativeimage.cpp
new file mode 100644
index 0000000000..8face87a92
--- /dev/null
+++ b/src/gui/image/qnativeimage.cpp
@@ -0,0 +1,315 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qdebug.h>
+#include "qnativeimage_p.h"
+#include "qcolormap.h"
+
+#include "private/qpaintengine_raster_p.h"
+
+#include "private/qapplication_p.h"
+#include "private/qgraphicssystem_p.h"
+
+#if defined(Q_WS_X11) && !defined(QT_NO_MITSHM)
+#include <qx11info_x11.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <qwidget.h>
+#endif
+
+#ifdef Q_WS_MAC
+#include <private/qpaintengine_mac_p.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#ifdef Q_WS_WIN
+typedef struct {
+ BITMAPINFOHEADER bmiHeader;
+ DWORD redMask;
+ DWORD greenMask;
+ DWORD blueMask;
+} BITMAPINFO_MASK;
+
+
+QNativeImage::QNativeImage(int width, int height, QImage::Format format, bool isTextBuffer, QWidget *)
+{
+#ifndef Q_WS_WINCE
+ Q_UNUSED(isTextBuffer);
+#endif
+ BITMAPINFO_MASK bmi;
+ memset(&bmi, 0, sizeof(bmi));
+ bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bmi.bmiHeader.biWidth = width;
+ bmi.bmiHeader.biHeight = -height;
+ bmi.bmiHeader.biPlanes = 1;
+ bmi.bmiHeader.biSizeImage = 0;
+
+ if (format == QImage::Format_RGB16) {
+ bmi.bmiHeader.biBitCount = 16;
+#ifdef Q_WS_WINCE
+ if (isTextBuffer) {
+ bmi.bmiHeader.biCompression = BI_RGB;
+ bmi.redMask = 0;
+ bmi.greenMask = 0;
+ bmi.blueMask = 0;
+ } else
+#endif
+ {
+ bmi.bmiHeader.biCompression = BI_BITFIELDS;
+ bmi.redMask = 0xF800;
+ bmi.greenMask = 0x07E0;
+ bmi.blueMask = 0x001F;
+ }
+ } else {
+ bmi.bmiHeader.biBitCount = 32;
+ bmi.bmiHeader.biCompression = BI_RGB;
+ bmi.redMask = 0;
+ bmi.greenMask = 0;
+ bmi.blueMask = 0;
+ }
+
+ HDC display_dc = GetDC(0);
+ hdc = CreateCompatibleDC(display_dc);
+ ReleaseDC(0, display_dc);
+ Q_ASSERT(hdc);
+
+ uchar *bits = 0;
+ bitmap = CreateDIBSection(hdc, reinterpret_cast<BITMAPINFO *>(&bmi), DIB_RGB_COLORS, (void**) &bits, 0, 0);
+ Q_ASSERT(bitmap);
+ Q_ASSERT(bits);
+
+ null_bitmap = (HBITMAP)SelectObject(hdc, bitmap);
+ image = QImage(bits, width, height, format);
+
+ Q_ASSERT(image.paintEngine()->type() == QPaintEngine::Raster);
+ static_cast<QRasterPaintEngine *>(image.paintEngine())->setDC(hdc);
+
+#ifndef Q_WS_WINCE
+ GdiFlush();
+#endif
+}
+
+QNativeImage::~QNativeImage()
+{
+ if (bitmap || hdc) {
+ Q_ASSERT(hdc);
+ Q_ASSERT(bitmap);
+ if (null_bitmap)
+ SelectObject(hdc, null_bitmap);
+ DeleteDC(hdc);
+ DeleteObject(bitmap);
+ }
+}
+
+QImage::Format QNativeImage::systemFormat()
+{
+ if (QColormap::instance().depth() == 16)
+ return QImage::Format_RGB16;
+ return QImage::Format_RGB32;
+}
+
+
+#elif defined(Q_WS_X11) && !defined(QT_NO_MITSHM)
+
+QNativeImage::QNativeImage(int width, int height, QImage::Format format,bool /* isTextBuffer */, QWidget *widget)
+ : xshmimg(0), xshmpm(0)
+{
+ if (!X11->use_mitshm) {
+ image = QImage(width, height, format);
+ // follow good coding practice and set xshminfo attributes, though values not used in this case
+ xshminfo.readOnly = true;
+ xshminfo.shmaddr = 0;
+ xshminfo.shmid = 0;
+ xshminfo.shmseg = 0;
+ return;
+ }
+
+ QX11Info info = widget->x11Info();
+
+ int dd = info.depth();
+ Visual *vis = (Visual*) info.visual();
+
+ xshmimg = XShmCreateImage(X11->display, vis, dd, ZPixmap, 0, &xshminfo, width, height);
+ if (!xshmimg) {
+ qWarning("QNativeImage: Unable to create shared XImage.");
+ return;
+ }
+
+ bool ok;
+ xshminfo.shmid = shmget(IPC_PRIVATE, xshmimg->bytes_per_line * xshmimg->height,
+ IPC_CREAT | 0777);
+ ok = xshminfo.shmid != -1;
+ if (ok) {
+ xshmimg->data = (char*)shmat(xshminfo.shmid, 0, 0);
+ xshminfo.shmaddr = xshmimg->data;
+ ok = (xshminfo.shmaddr != (char*)-1);
+ if (ok)
+ image = QImage((uchar *)xshmimg->data, width, height, format);
+ }
+ xshminfo.readOnly = false;
+ if (ok) {
+ ok = XShmAttach(X11->display, &xshminfo);
+ XSync(X11->display, False);
+ if (shmctl(xshminfo.shmid, IPC_RMID, 0) == -1)
+ qWarning() << "Error while marking the shared memory segment to be destroyed";
+ }
+ if (!ok) {
+ qWarning() << "QNativeImage: Unable to attach to shared memory segment.";
+ if (xshmimg->data) {
+ free(xshmimg->data);
+ xshmimg->data = 0;
+ }
+ XDestroyImage(xshmimg);
+ xshmimg = 0;
+ if (xshminfo.shmaddr)
+ shmdt(xshminfo.shmaddr);
+ if (xshminfo.shmid != -1)
+ shmctl(xshminfo.shmid, IPC_RMID, 0);
+ return;
+ }
+ if (X11->use_mitshm_pixmaps) {
+ xshmpm = XShmCreatePixmap(X11->display, DefaultRootWindow(X11->display), xshmimg->data,
+ &xshminfo, width, height, dd);
+ if (!xshmpm) {
+ qWarning() << "QNativeImage: Unable to create shared Pixmap.";
+ }
+ }
+}
+
+
+QNativeImage::~QNativeImage()
+{
+ if (!xshmimg)
+ return;
+
+ if (xshmpm) {
+ XFreePixmap(X11->display, xshmpm);
+ xshmpm = 0;
+ }
+ XShmDetach(X11->display, &xshminfo);
+ xshmimg->data = 0;
+ XDestroyImage(xshmimg);
+ xshmimg = 0;
+ shmdt(xshminfo.shmaddr);
+ shmctl(xshminfo.shmid, IPC_RMID, 0);
+}
+
+QImage::Format QNativeImage::systemFormat()
+{
+ if (QX11Info::appDepth() == 16)
+ return QImage::Format_RGB16;
+ return QImage::Format_RGB32;
+}
+
+#elif defined(Q_WS_MAC)
+
+QNativeImage::QNativeImage(int width, int height, QImage::Format format, bool /* isTextBuffer */, QWidget *widget)
+ : image(width, height, format)
+{
+
+ uint cgflags = kCGImageAlphaNoneSkipFirst;
+ switch (format) {
+ case QImage::Format_ARGB32:
+ cgflags = kCGImageAlphaFirst;
+ break;
+ case QImage::Format_ARGB32_Premultiplied:
+ case QImage::Format_ARGB8565_Premultiplied:
+ case QImage::Format_ARGB6666_Premultiplied:
+ case QImage::Format_ARGB8555_Premultiplied:
+ case QImage::Format_ARGB4444_Premultiplied:
+ cgflags = kCGImageAlphaPremultipliedFirst;
+ break;
+ default:
+ break;
+ }
+
+#ifdef kCGBitmapByteOrder32Host //only needed because CGImage.h added symbols in the minor version
+ cgflags |= kCGBitmapByteOrder32Host;
+#endif
+
+ cg = CGBitmapContextCreate(image.bits(), width, height, 8, image.bytesPerLine(),
+ QCoreGraphicsPaintEngine::macDisplayColorSpace(widget), cgflags);
+ CGContextTranslateCTM(cg, 0, height);
+ CGContextScaleCTM(cg, 1, -1);
+
+ Q_ASSERT(image.paintEngine()->type() == QPaintEngine::Raster);
+ static_cast<QRasterPaintEngine *>(image.paintEngine())->setCGContext(cg);
+}
+
+
+QNativeImage::~QNativeImage()
+{
+ CGContextRelease(cg);
+}
+
+QImage::Format QNativeImage::systemFormat()
+{
+ return QImage::Format_RGB32;
+}
+
+
+#else // other platforms...
+
+QNativeImage::QNativeImage(int width, int height, QImage::Format format, bool /* isTextBuffer */, QWidget *)
+ : image(width, height, format)
+{
+
+}
+
+
+QNativeImage::~QNativeImage()
+{
+}
+
+QImage::Format QNativeImage::systemFormat()
+{
+#ifdef Q_WS_QPA
+ return QApplicationPrivate::platformIntegration()->screens().at(0)->format();
+#else
+ return QImage::Format_RGB32;
+#endif
+}
+
+#endif // platforms
+
+QT_END_NAMESPACE
+
diff --git a/src/gui/image/qnativeimage_p.h b/src/gui/image/qnativeimage_p.h
new file mode 100644
index 0000000000..12aa0f021a
--- /dev/null
+++ b/src/gui/image/qnativeimage_p.h
@@ -0,0 +1,109 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNATIVEIMAGE_P_H
+#define QNATIVEIMAGE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qimage.h"
+
+#ifdef Q_WS_WIN
+#include "qt_windows.h"
+
+#elif defined(Q_WS_X11)
+#include <private/qt_x11_p.h>
+
+#elif defined(Q_WS_MAC)
+#include <private/qt_mac_p.h>
+
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QWidget;
+
+class QNativeImage
+{
+public:
+ QNativeImage(int width, int height, QImage::Format format, bool isTextBuffer = false, QWidget *widget = 0);
+ ~QNativeImage();
+
+ inline int width() const;
+ inline int height() const;
+
+ QImage image;
+
+ static QImage::Format systemFormat();
+
+#ifdef Q_WS_WIN
+ HDC hdc;
+ HBITMAP bitmap;
+ HBITMAP null_bitmap;
+
+#elif defined(Q_WS_X11) && !defined(QT_NO_MITSHM)
+ XImage *xshmimg;
+ Pixmap xshmpm;
+ XShmSegmentInfo xshminfo;
+
+#elif defined(Q_WS_MAC)
+ CGContextRef cg;
+#endif
+
+private:
+ Q_DISABLE_COPY(QNativeImage)
+};
+
+inline int QNativeImage::width() const { return image.width(); }
+inline int QNativeImage::height() const { return image.height(); }
+
+QT_END_NAMESPACE
+
+#endif // QNATIVEIMAGE_P_H
diff --git a/src/gui/image/qnativeimagehandleprovider_p.h b/src/gui/image/qnativeimagehandleprovider_p.h
new file mode 100644
index 0000000000..4e6ed3852a
--- /dev/null
+++ b/src/gui/image/qnativeimagehandleprovider_p.h
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNATIVEIMAGEHANDLEPROVIDER_P_H
+#define QNATIVEIMAGEHANDLEPROVIDER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qstring.h>
+
+QT_BEGIN_NAMESPACE
+
+class QNativeImageHandleProvider
+{
+public:
+ virtual void get(void **handle, QString *type) = 0;
+ virtual void release(void *handle, const QString &type) = 0;
+};
+
+QT_END_NAMESPACE
+
+#endif // QNATIVEIMAGEHANDLEPROVIDER_P_H
diff --git a/src/gui/image/qpaintengine_pic.cpp b/src/gui/image/qpaintengine_pic.cpp
new file mode 100644
index 0000000000..6f0979a4b2
--- /dev/null
+++ b/src/gui/image/qpaintengine_pic.cpp
@@ -0,0 +1,539 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "private/qpaintengine_p.h"
+#include "private/qpainter_p.h"
+#include "private/qpicture_p.h"
+#include "private/qfont_p.h"
+
+#ifndef QT_NO_PICTURE
+
+#include "qbuffer.h"
+#include "qbytearray.h"
+#include "qdatastream.h"
+#include "qmath.h"
+#include "qpaintengine_pic_p.h"
+#include "qpicture.h"
+#include "qpolygon.h"
+#include "qrect.h"
+#include <private/qtextengine_p.h>
+
+//#define QT_PICTURE_DEBUG
+#include <qdebug.h>
+
+
+QT_BEGIN_NAMESPACE
+
+class QPicturePaintEnginePrivate : public QPaintEnginePrivate
+{
+ Q_DECLARE_PUBLIC(QPicturePaintEngine)
+public:
+ QDataStream s;
+ QPainter *pt;
+ QPicturePrivate *pic_d;
+};
+
+QPicturePaintEngine::QPicturePaintEngine()
+ : QPaintEngine(*(new QPicturePaintEnginePrivate), AllFeatures)
+{
+ Q_D(QPicturePaintEngine);
+ d->pt = 0;
+}
+
+QPicturePaintEngine::QPicturePaintEngine(QPaintEnginePrivate &dptr)
+ : QPaintEngine(dptr, AllFeatures)
+{
+ Q_D(QPicturePaintEngine);
+ d->pt = 0;
+}
+
+QPicturePaintEngine::~QPicturePaintEngine()
+{
+}
+
+bool QPicturePaintEngine::begin(QPaintDevice *pd)
+{
+ Q_D(QPicturePaintEngine);
+#ifdef QT_PICTURE_DEBUG
+ qDebug() << "QPicturePaintEngine::begin()";
+#endif
+ Q_ASSERT(pd);
+ QPicture *pic = static_cast<QPicture *>(pd);
+
+ d->pdev = pd;
+ d->pic_d = pic->d_func();
+ Q_ASSERT(d->pic_d);
+
+ d->s.setDevice(&d->pic_d->pictb);
+ d->s.setVersion(d->pic_d->formatMajor);
+
+ d->pic_d->pictb.open(QIODevice::WriteOnly | QIODevice::Truncate);
+ d->s.writeRawData(qt_mfhdr_tag, 4);
+ d->s << (quint16) 0 << (quint16) d->pic_d->formatMajor << (quint16) d->pic_d->formatMinor;
+ d->s << (quint8) QPicturePrivate::PdcBegin << (quint8) sizeof(qint32);
+ d->pic_d->brect = QRect();
+ if (d->pic_d->formatMajor >= 4) {
+ QRect r = pic->boundingRect();
+ d->s << (qint32) r.left() << (qint32) r.top() << (qint32) r.width()
+ << (qint32) r.height();
+ }
+ d->pic_d->trecs = 0;
+ d->s << (quint32)d->pic_d->trecs; // total number of records
+ d->pic_d->formatOk = false;
+ setActive(true);
+ return true;
+}
+
+bool QPicturePaintEngine::end()
+{
+ Q_D(QPicturePaintEngine);
+#ifdef QT_PICTURE_DEBUG
+ qDebug() << "QPicturePaintEngine::end()";
+#endif
+ d->pic_d->trecs++;
+ d->s << (quint8) QPicturePrivate::PdcEnd << (quint8) 0;
+ int cs_start = sizeof(quint32); // pos of checksum word
+ int data_start = cs_start + sizeof(quint16);
+ int brect_start = data_start + 2*sizeof(qint16) + 2*sizeof(quint8);
+ int pos = d->pic_d->pictb.pos();
+ d->pic_d->pictb.seek(brect_start);
+ if (d->pic_d->formatMajor >= 4) { // bounding rectangle
+ QRect r = static_cast<QPicture *>(d->pdev)->boundingRect();
+ d->s << (qint32) r.left() << (qint32) r.top() << (qint32) r.width()
+ << (qint32) r.height();
+ }
+ d->s << (quint32) d->pic_d->trecs; // write number of records
+ d->pic_d->pictb.seek(cs_start);
+ QByteArray buf = d->pic_d->pictb.buffer();
+ quint16 cs = (quint16) qChecksum(buf.constData() + data_start, pos - data_start);
+ d->s << cs; // write checksum
+ d->pic_d->pictb.close();
+ setActive(false);
+ return true;
+}
+
+#define SERIALIZE_CMD(c) \
+ d->pic_d->trecs++; \
+ d->s << (quint8) c; \
+ d->s << (quint8) 0; \
+ pos = d->pic_d->pictb.pos()
+
+void QPicturePaintEngine::updatePen(const QPen &pen)
+{
+ Q_D(QPicturePaintEngine);
+#ifdef QT_PICTURE_DEBUG
+ qDebug() << " -> updatePen(): width:" << pen.width() << "style:"
+ << pen.style() << "color:" << pen.color();
+#endif
+ int pos;
+ SERIALIZE_CMD(QPicturePrivate::PdcSetPen);
+ if (d->pic_d->in_memory_only) {
+ int index = d->pic_d->pen_list.size();
+ d->pic_d->pen_list.append(pen);
+ d->s << index;
+ } else {
+ d->s << pen;
+ }
+ writeCmdLength(pos, QRect(), false);
+}
+
+void QPicturePaintEngine::updateCompositionMode(QPainter::CompositionMode cmode)
+{
+ Q_D(QPicturePaintEngine);
+#ifdef QT_PICTURE_DEBUG
+ qDebug() << " -> updateCompositionMode():" << cmode;
+#endif
+ int pos;
+ SERIALIZE_CMD(QPicturePrivate::PdcSetCompositionMode);
+ d->s << (qint32)cmode;
+ writeCmdLength(pos, QRectF(), false);
+}
+
+void QPicturePaintEngine::updateClipEnabled(bool enabled)
+{
+ Q_D(QPicturePaintEngine);
+#ifdef QT_PICTURE_DEBUG
+ qDebug() << " -> updateClipEnabled():" << enabled;
+#endif
+ int pos;
+ SERIALIZE_CMD(QPicturePrivate::PdcSetClipEnabled);
+ d->s << enabled;
+ writeCmdLength(pos, QRectF(), false);
+}
+
+void QPicturePaintEngine::updateOpacity(qreal opacity)
+{
+ Q_D(QPicturePaintEngine);
+#ifdef QT_PICTURE_DEBUG
+ qDebug() << " -> updateOpacity():" << opacity;
+#endif
+ int pos;
+ SERIALIZE_CMD(QPicturePrivate::PdcSetOpacity);
+ d->s << double(opacity);
+ writeCmdLength(pos, QRectF(), false);
+}
+
+void QPicturePaintEngine::updateBrush(const QBrush &brush)
+{
+ Q_D(QPicturePaintEngine);
+#ifdef QT_PICTURE_DEBUG
+ qDebug() << " -> updateBrush(): style:" << brush.style();
+#endif
+ int pos;
+ SERIALIZE_CMD(QPicturePrivate::PdcSetBrush);
+ if (d->pic_d->in_memory_only) {
+ int index = d->pic_d->brush_list.size();
+ d->pic_d->brush_list.append(brush);
+ d->s << index;
+ } else {
+ d->s << brush;
+ }
+ writeCmdLength(pos, QRect(), false);
+}
+
+void QPicturePaintEngine::updateBrushOrigin(const QPointF &p)
+{
+ Q_D(QPicturePaintEngine);
+#ifdef QT_PICTURE_DEBUG
+ qDebug() << " -> updateBrushOrigin(): " << p;
+#endif
+ int pos;
+ SERIALIZE_CMD(QPicturePrivate::PdcSetBrushOrigin);
+ d->s << p;
+ writeCmdLength(pos, QRect(), false);
+}
+
+void QPicturePaintEngine::updateFont(const QFont &font)
+{
+ Q_D(QPicturePaintEngine);
+#ifdef QT_PICTURE_DEBUG
+ qDebug() << " -> updateFont(): pt sz:" << font.pointSize();
+#endif
+ int pos;
+ SERIALIZE_CMD(QPicturePrivate::PdcSetFont);
+ QFont fnt = font;
+ d->s << fnt;
+ writeCmdLength(pos, QRectF(), false);
+}
+
+void QPicturePaintEngine::updateBackground(Qt::BGMode bgMode, const QBrush &bgBrush)
+{
+ Q_D(QPicturePaintEngine);
+#ifdef QT_PICTURE_DEBUG
+ qDebug() << " -> updateBackground(): mode:" << bgMode << "style:" << bgBrush.style();
+#endif
+ int pos;
+ SERIALIZE_CMD(QPicturePrivate::PdcSetBkColor);
+ d->s << bgBrush.color();
+ writeCmdLength(pos, QRect(), false);
+
+ SERIALIZE_CMD(QPicturePrivate::PdcSetBkMode);
+ d->s << (qint8) bgMode;
+ writeCmdLength(pos, QRectF(), false);
+}
+
+void QPicturePaintEngine::updateMatrix(const QTransform &matrix)
+{
+ Q_D(QPicturePaintEngine);
+#ifdef QT_PICTURE_DEBUG
+ qDebug() << " -> updateMatrix():" << matrix;
+#endif
+ int pos;
+ SERIALIZE_CMD(QPicturePrivate::PdcSetWMatrix);
+ d->s << matrix << (qint8) false;
+ writeCmdLength(pos, QRectF(), false);
+}
+
+void QPicturePaintEngine::updateClipRegion(const QRegion &region, Qt::ClipOperation op)
+{
+ Q_D(QPicturePaintEngine);
+#ifdef QT_PICTURE_DEBUG
+ qDebug() << " -> updateClipRegion(): op:" << op
+ << "bounding rect:" << region.boundingRect();
+#endif
+ int pos;
+ SERIALIZE_CMD(QPicturePrivate::PdcSetClipRegion);
+ d->s << region << qint8(op);
+ writeCmdLength(pos, QRectF(), false);
+}
+
+void QPicturePaintEngine::updateClipPath(const QPainterPath &path, Qt::ClipOperation op)
+{
+ Q_D(QPicturePaintEngine);
+#ifdef QT_PICTURE_DEBUG
+ qDebug() << " -> updateClipPath(): op:" << op
+ << "bounding rect:" << path.boundingRect();
+#endif
+ int pos;
+
+ SERIALIZE_CMD(QPicturePrivate::PdcSetClipPath);
+ d->s << path << qint8(op);
+ writeCmdLength(pos, QRectF(), false);
+}
+
+void QPicturePaintEngine::updateRenderHints(QPainter::RenderHints hints)
+{
+ Q_D(QPicturePaintEngine);
+#ifdef QT_PICTURE_DEBUG
+ qDebug() << " -> updateRenderHints(): " << hints;
+#endif
+ int pos;
+ SERIALIZE_CMD(QPicturePrivate::PdcSetRenderHint);
+ d->s << (quint32) hints;
+ writeCmdLength(pos, QRect(), false);
+}
+
+void QPicturePaintEngine::writeCmdLength(int pos, const QRectF &r, bool corr)
+{
+ Q_D(QPicturePaintEngine);
+ int newpos = d->pic_d->pictb.pos(); // new position
+ int length = newpos - pos;
+ QRectF br(r);
+
+ if (length < 255) { // write 8-bit length
+ d->pic_d->pictb.seek(pos - 1); // position to right index
+ d->s << (quint8)length;
+ } else { // write 32-bit length
+ d->s << (quint32)0; // extend the buffer
+ d->pic_d->pictb.seek(pos - 1); // position to right index
+ d->s << (quint8)255; // indicate 32-bit length
+ char *p = d->pic_d->pictb.buffer().data();
+ memmove(p+pos+4, p+pos, length); // make room for 4 byte
+ d->s << (quint32)length;
+ newpos += 4;
+ }
+ d->pic_d->pictb.seek(newpos); // set to new position
+
+ if (br.width() > 0.0 || br.height() > 0.0) {
+ if (corr) { // widen bounding rect
+ int w2 = painter()->pen().width() / 2;
+ br.setCoords(br.left() - w2, br.top() - w2,
+ br.right() + w2, br.bottom() + w2);
+ }
+ br = painter()->transform().mapRect(br);
+ if (painter()->hasClipping()) {
+ QRect cr = painter()->clipRegion().boundingRect();
+ br &= cr;
+ }
+
+ if (br.width() > 0.0 || br.height() > 0.0) {
+ int minx = qFloor(br.left());
+ int miny = qFloor(br.top());
+ int maxx = qCeil(br.right());
+ int maxy = qCeil(br.bottom());
+
+ if (d->pic_d->brect.width() > 0 || d->pic_d->brect.height() > 0) {
+ minx = qMin(minx, d->pic_d->brect.left());
+ miny = qMin(miny, d->pic_d->brect.top());
+ maxx = qMax(maxx, d->pic_d->brect.x() + d->pic_d->brect.width());
+ maxy = qMax(maxy, d->pic_d->brect.y() + d->pic_d->brect.height());
+ d->pic_d->brect = QRect(minx, miny, maxx - minx, maxy - miny);
+ } else {
+ d->pic_d->brect = QRect(minx, miny, maxx - minx, maxy - miny);
+ }
+ }
+ }
+}
+
+void QPicturePaintEngine::drawEllipse(const QRectF &rect)
+{
+ Q_D(QPicturePaintEngine);
+#ifdef QT_PICTURE_DEBUG
+ qDebug() << " -> drawEllipse():" << rect;
+#endif
+ int pos;
+ SERIALIZE_CMD(QPicturePrivate::PdcDrawEllipse);
+ d->s << rect;
+ writeCmdLength(pos, rect, true);
+}
+
+void QPicturePaintEngine::drawPath(const QPainterPath &path)
+{
+ Q_D(QPicturePaintEngine);
+#ifdef QT_PICTURE_DEBUG
+ qDebug() << " -> drawPath():" << path.boundingRect();
+#endif
+ int pos;
+ SERIALIZE_CMD(QPicturePrivate::PdcDrawPath);
+ d->s << path;
+ writeCmdLength(pos, path.boundingRect(), true);
+}
+
+void QPicturePaintEngine::drawPolygon(const QPointF *points, int numPoints, PolygonDrawMode mode)
+{
+ Q_D(QPicturePaintEngine);
+#ifdef QT_PICTURE_DEBUG
+ qDebug() << " -> drawPolygon(): size=" << numPoints;
+#endif
+ int pos;
+
+ QPolygonF polygon;
+ for (int i=0; i<numPoints; ++i)
+ polygon << points[i];
+
+ if (mode == PolylineMode) {
+ SERIALIZE_CMD(QPicturePrivate::PdcDrawPolyline);
+ d->s << polygon;
+ } else {
+ SERIALIZE_CMD(QPicturePrivate::PdcDrawPolygon);
+ d->s << polygon;
+ d->s << (qint8)(mode == OddEvenMode ? 0 : 1);
+ }
+
+ writeCmdLength(pos, polygon.boundingRect(), true);
+}
+
+void QPicturePaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
+{
+ Q_D(QPicturePaintEngine);
+#ifdef QT_PICTURE_DEBUG
+ qDebug() << " -> drawPixmap():" << r;
+#endif
+ int pos;
+ SERIALIZE_CMD(QPicturePrivate::PdcDrawPixmap);
+
+ if (d->pic_d->in_memory_only) {
+ int index = d->pic_d->pixmap_list.size();
+ d->pic_d->pixmap_list.append(pm);
+ d->s << r << index << sr;
+ } else {
+ d->s << r << pm << sr;
+ }
+ writeCmdLength(pos, r, false);
+}
+
+void QPicturePaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s)
+{
+ Q_D(QPicturePaintEngine);
+#ifdef QT_PICTURE_DEBUG
+ qDebug() << " -> drawTiledPixmap():" << r << s;
+#endif
+ int pos;
+ SERIALIZE_CMD(QPicturePrivate::PdcDrawTiledPixmap);
+ if (d->pic_d->in_memory_only) {
+ int index = d->pic_d->pixmap_list.size();
+ d->pic_d->pixmap_list.append(pixmap);
+ d->s << r << index << s;
+ } else {
+ d->s << r << pixmap << s;
+ }
+ writeCmdLength(pos, r, false);
+}
+
+void QPicturePaintEngine::drawImage(const QRectF &r, const QImage &image, const QRectF &sr,
+ Qt::ImageConversionFlags flags)
+{
+ Q_D(QPicturePaintEngine);
+#ifdef QT_PICTURE_DEBUG
+ qDebug() << " -> drawImage():" << r << sr;
+#endif
+ int pos;
+ SERIALIZE_CMD(QPicturePrivate::PdcDrawImage);
+ if (d->pic_d->in_memory_only) {
+ int index = d->pic_d->image_list.size();
+ d->pic_d->image_list.append(image);
+ d->s << r << index << sr << (quint32) flags;
+ } else {
+ d->s << r << image << sr << (quint32) flags;
+ }
+ writeCmdLength(pos, r, false);
+}
+
+void QPicturePaintEngine::drawTextItem(const QPointF &p , const QTextItem &ti)
+{
+ Q_D(QPicturePaintEngine);
+#ifdef QT_PICTURE_DEBUG
+ qDebug() << " -> drawTextItem():" << p << ti.text();
+#endif
+
+ const QTextItemInt &si = static_cast<const QTextItemInt &>(ti);
+ if (si.chars == 0)
+ QPaintEngine::drawTextItem(p, ti); // Draw as path
+
+ if (d->pic_d->formatMajor >= 9) {
+ int pos;
+ SERIALIZE_CMD(QPicturePrivate::PdcDrawTextItem);
+ QFont fnt = ti.font();
+ fnt.setUnderline(false);
+ fnt.setStrikeOut(false);
+ fnt.setOverline(false);
+
+ qreal justificationWidth = 0;
+ if (si.justified)
+ justificationWidth = si.width.toReal();
+
+ d->s << p << ti.text() << fnt << ti.renderFlags() << double(fnt.d->dpi)/qt_defaultDpi() << justificationWidth;
+ writeCmdLength(pos, /*brect=*/QRectF(), /*corr=*/false);
+ } else if (d->pic_d->formatMajor >= 8) {
+ // old old (buggy) format
+ int pos;
+ SERIALIZE_CMD(QPicturePrivate::PdcDrawTextItem);
+ d->s << QPointF(p.x(), p.y() - ti.ascent()) << ti.text() << ti.font() << ti.renderFlags();
+ writeCmdLength(pos, /*brect=*/QRectF(), /*corr=*/false);
+ } else {
+ // old (buggy) format
+ int pos;
+ SERIALIZE_CMD(QPicturePrivate::PdcDrawText2);
+ d->s << p << ti.text();
+ writeCmdLength(pos, QRectF(p, QSizeF(1,1)), true);
+ }
+}
+
+void QPicturePaintEngine::updateState(const QPaintEngineState &state)
+{
+ QPaintEngine::DirtyFlags flags = state.state();
+ if (flags & DirtyPen) updatePen(state.pen());
+ if (flags & DirtyBrush) updateBrush(state.brush());
+ if (flags & DirtyBrushOrigin) updateBrushOrigin(state.brushOrigin());
+ if (flags & DirtyFont) updateFont(state.font());
+ if (flags & DirtyBackground) updateBackground(state.backgroundMode(), state.backgroundBrush());
+ if (flags & DirtyTransform) updateMatrix(state.transform());
+ if (flags & DirtyClipEnabled) updateClipEnabled(state.isClipEnabled());
+ if (flags & DirtyClipRegion) updateClipRegion(state.clipRegion(), state.clipOperation());
+ if (flags & DirtyClipPath) updateClipPath(state.clipPath(), state.clipOperation());
+ if (flags & DirtyHints) updateRenderHints(state.renderHints());
+ if (flags & DirtyCompositionMode) updateCompositionMode(state.compositionMode());
+ if (flags & DirtyOpacity) updateOpacity(state.opacity());
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PICTURE
diff --git a/src/gui/image/qpaintengine_pic_p.h b/src/gui/image/qpaintengine_pic_p.h
new file mode 100644
index 0000000000..0d6a327fb7
--- /dev/null
+++ b/src/gui/image/qpaintengine_pic_p.h
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPAINTENGINE_PIC_P_H
+#define QPAINTENGINE_PIC_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of QAbstractItemModel*. This header file may change from version
+// to version without notice, or even be removed.
+//
+// We mean it.
+//
+//
+
+#include "QtGui/qpaintengine.h"
+
+#ifndef QT_NO_PICTURE
+
+QT_BEGIN_NAMESPACE
+
+class QPicturePaintEnginePrivate;
+class QBuffer;
+
+class QPicturePaintEngine : public QPaintEngine
+{
+ Q_DECLARE_PRIVATE(QPicturePaintEngine)
+public:
+ QPicturePaintEngine();
+ ~QPicturePaintEngine();
+
+ bool begin(QPaintDevice *pdev);
+ bool end();
+
+ void updateState(const QPaintEngineState &state);
+
+ void updatePen(const QPen &pen);
+ void updateBrush(const QBrush &brush);
+ void updateBrushOrigin(const QPointF &origin);
+ void updateFont(const QFont &font);
+ void updateBackground(Qt::BGMode bgmode, const QBrush &bgBrush);
+ void updateMatrix(const QTransform &matrix);
+ void updateClipRegion(const QRegion &region, Qt::ClipOperation op);
+ void updateClipPath(const QPainterPath &path, Qt::ClipOperation op);
+ void updateRenderHints(QPainter::RenderHints hints);
+ void updateCompositionMode(QPainter::CompositionMode cmode);
+ void updateClipEnabled(bool enabled);
+ void updateOpacity(qreal opacity);
+
+ void drawEllipse(const QRectF &rect);
+ void drawPath(const QPainterPath &path);
+ void drawPolygon(const QPointF *points, int numPoints, PolygonDrawMode mode);
+#ifdef Q_NO_USING_KEYWORD
+ inline void drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
+ { QPaintEngine::drawPolygon(points, pointCount, mode); }
+#else
+ using QPaintEngine::drawPolygon;
+#endif
+
+ void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr);
+ void drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s);
+ void drawImage(const QRectF &r, const QImage &image, const QRectF &sr,
+ Qt::ImageConversionFlags flags = Qt::AutoColor);
+ void drawTextItem(const QPointF &p, const QTextItem &ti);
+
+ Type type() const { return Picture; }
+
+protected:
+ QPicturePaintEngine(QPaintEnginePrivate &dptr);
+
+private:
+ Q_DISABLE_COPY(QPicturePaintEngine)
+
+ void writeCmdLength(int pos, const QRectF &r, bool corr);
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PICTURE
+
+#endif // QPAINTENGINE_PIC_P_H
diff --git a/src/gui/image/qpicture.cpp b/src/gui/image/qpicture.cpp
new file mode 100644
index 0000000000..3f38c0ce2a
--- /dev/null
+++ b/src/gui/image/qpicture.cpp
@@ -0,0 +1,1999 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpicture.h"
+#include <private/qpicture_p.h>
+
+#ifndef QT_NO_PICTURE
+
+#include <private/qfactoryloader_p.h>
+#include <private/qpaintengine_pic_p.h>
+#include <private/qfont_p.h>
+
+#include "qdatastream.h"
+#include "qfile.h"
+#include "qimage.h"
+#include "qmutex.h"
+#include "qpainter.h"
+#include "qpainterpath.h"
+#include "qpixmap.h"
+#include "qregion.h"
+#include "qdebug.h"
+
+QT_BEGIN_NAMESPACE
+
+void qt_format_text(const QFont &fnt, const QRectF &_r,
+ int tf, const QTextOption *opt, const QString& str, QRectF *brect,
+ int tabstops, int *, int tabarraylen,
+ QPainter *painter);
+
+/*!
+ \class QPicture
+ \brief The QPicture class is a paint device that records and
+ replays QPainter commands.
+
+ \ingroup painting
+ \ingroup shared
+
+
+ A picture serializes painter commands to an IO device in a
+ platform-independent format. They are sometimes referred to as meta-files.
+
+ Qt pictures use a proprietary binary format. Unlike native picture
+ (meta-file) formats on many window systems, Qt pictures have no
+ limitations regarding their contents. Everything that can be
+ painted on a widget or pixmap (e.g., fonts, pixmaps, regions,
+ transformed graphics, etc.) can also be stored in a picture.
+
+ QPicture is resolution independent, i.e. a QPicture can be
+ displayed on different devices (for example svg, pdf, ps, printer
+ and screen) looking the same. This is, for instance, needed for
+ WYSIWYG print preview. QPicture runs in the default system dpi,
+ and scales the painter to match differences in resolution
+ depending on the window system.
+
+ Example of how to record a picture:
+ \snippet doc/src/snippets/picture/picture.cpp 0
+
+ Note that the list of painter commands is reset on each call to
+ the QPainter::begin() function.
+
+ Example of how to replay a picture:
+ \snippet doc/src/snippets/picture/picture.cpp 1
+
+ Pictures can also be drawn using play(). Some basic data about a
+ picture is available, for example, size(), isNull() and
+ boundingRect().
+
+ \sa QMovie
+*/
+
+const char *qt_mfhdr_tag = "QPIC"; // header tag
+static const quint16 mfhdr_maj = 11; // major version #
+static const quint16 mfhdr_min = 0; // minor version #
+
+/*!
+ Constructs an empty picture.
+
+ The \a formatVersion parameter may be used to \e create a QPicture
+ that can be read by applications that are compiled with earlier
+ versions of Qt.
+
+ Note that the default formatVersion is -1 which signifies the
+ current release, i.e. for Qt 4.0 a formatVersion of 7 is the same
+ as the default formatVersion of -1.
+
+ Reading pictures generated by earlier versions of Qt is not
+ supported in Qt 4.0.
+*/
+
+QPicture::QPicture(int formatVersion)
+ : QPaintDevice(),
+ d_ptr(new QPicturePrivate)
+{
+ Q_D(QPicture);
+
+ if (formatVersion == 0)
+ qWarning("QPicture: invalid format version 0");
+
+ // still accept the 0 default from before Qt 3.0.
+ if (formatVersion > 0 && formatVersion != (int)mfhdr_maj) {
+ d->formatMajor = formatVersion;
+ d->formatMinor = 0;
+ d->formatOk = false;
+ } else {
+ d->resetFormat();
+ }
+}
+
+/*!
+ Constructs a copy of \a pic.
+
+ This constructor is fast thanks to \l{implicit sharing}.
+*/
+
+QPicture::QPicture(const QPicture &pic)
+ : QPaintDevice(), d_ptr(pic.d_ptr)
+{
+}
+
+/*! \internal */
+QPicture::QPicture(QPicturePrivate &dptr)
+ : QPaintDevice(),
+ d_ptr(&dptr)
+{
+}
+
+/*!
+ Destroys the picture.
+*/
+QPicture::~QPicture()
+{
+}
+
+/*!
+ \internal
+*/
+int QPicture::devType() const
+{
+ return QInternal::Picture;
+}
+
+/*!
+ \fn bool QPicture::isNull() const
+
+ Returns true if the picture contains no data; otherwise returns
+ false.
+*/
+
+/*!
+ \fn uint QPicture::size() const
+
+ Returns the size of the picture data.
+
+ \sa data()
+*/
+
+/*!
+ \fn const char* QPicture::data() const
+
+ Returns a pointer to the picture data. The pointer is only valid
+ until the next non-const function is called on this picture. The
+ returned pointer is 0 if the picture contains no data.
+
+ \sa size(), isNull()
+*/
+
+
+bool QPicture::isNull() const
+{
+ return d_func()->pictb.buffer().isNull();
+}
+
+uint QPicture::size() const
+{
+ return d_func()->pictb.buffer().size();
+}
+
+const char* QPicture::data() const
+{
+ return d_func()->pictb.buffer();
+}
+
+void QPicture::detach()
+{
+ d_ptr.detach();
+}
+
+bool QPicture::isDetached() const
+{
+ return d_func()->ref == 1;
+}
+
+/*!
+ Sets the picture data directly from \a data and \a size. This
+ function copies the input data.
+
+ \sa data(), size()
+*/
+
+void QPicture::setData(const char* data, uint size)
+{
+ detach();
+ d_func()->pictb.setData(data, size);
+ d_func()->resetFormat(); // we'll have to check
+}
+
+
+/*!
+ Loads a picture from the file specified by \a fileName and returns
+ true if successful; otherwise returns false.
+
+ Please note that the \a format parameter has been deprecated and
+ will have no effect.
+
+ \sa save()
+*/
+
+bool QPicture::load(const QString &fileName, const char *format)
+{
+ QFile f(fileName);
+ if (!f.open(QIODevice::ReadOnly))
+ return false;
+ return load(&f, format);
+}
+
+/*!
+ \overload
+
+ \a dev is the device to use for loading.
+*/
+
+bool QPicture::load(QIODevice *dev, const char *format)
+{
+ if(format) {
+#ifndef QT_NO_PICTUREIO
+ QPictureIO io(dev, format);
+ bool result = io.read();
+ if (result) {
+ operator=(io.picture());
+
+ } else if (format)
+#else
+ bool result = false;
+#endif
+ {
+ qWarning("QPicture::load: No such picture format: %s", format);
+ }
+ return result;
+ }
+
+ detach();
+ QByteArray a = dev->readAll();
+
+ d_func()->pictb.setData(a); // set byte array in buffer
+ return d_func()->checkFormat();
+}
+
+/*!
+ Saves a picture to the file specified by \a fileName and returns
+ true if successful; otherwise returns false.
+
+ Please note that the \a format parameter has been deprecated and
+ will have no effect.
+
+ \sa load()
+*/
+
+bool QPicture::save(const QString &fileName, const char *format)
+{
+ if (paintingActive()) {
+ qWarning("QPicture::save: still being painted on. "
+ "Call QPainter::end() first");
+ return false;
+ }
+
+
+ if(format) {
+#ifndef QT_NO_PICTUREIO
+ QPictureIO io(fileName, format);
+ bool result = io.write();
+ if (result) {
+ operator=(io.picture());
+ } else if (format)
+#else
+ bool result = false;
+#endif
+ {
+ qWarning("QPicture::save: No such picture format: %s", format);
+ }
+ return result;
+ }
+
+ QFile f(fileName);
+ if (!f.open(QIODevice::WriteOnly))
+ return false;
+ return save(&f, format);
+}
+
+/*!
+ \overload
+
+ \a dev is the device to use for saving.
+*/
+
+bool QPicture::save(QIODevice *dev, const char *format)
+{
+ if (paintingActive()) {
+ qWarning("QPicture::save: still being painted on. "
+ "Call QPainter::end() first");
+ return false;
+ }
+
+ if(format) {
+#ifndef QT_NO_PICTUREIO
+ QPictureIO io(dev, format);
+ bool result = io.write();
+ if (result) {
+ operator=(io.picture());
+ } else if (format)
+#else
+ bool result = false;
+#endif
+ {
+ qWarning("QPicture::save: No such picture format: %s", format);
+ }
+ return result;
+ }
+
+ dev->write(d_func()->pictb.buffer(), d_func()->pictb.buffer().size());
+ return true;
+}
+
+/*!
+ Returns the picture's bounding rectangle or an invalid rectangle
+ if the picture contains no data.
+*/
+
+QRect QPicture::boundingRect() const
+{
+ Q_D(const QPicture);
+ // Use override rect where possible.
+ if (!d->override_rect.isEmpty())
+ return d->override_rect;
+
+ if (!d->formatOk)
+ d_ptr->checkFormat();
+
+ return d->brect;
+}
+
+/*!
+ Sets the picture's bounding rectangle to \a r. The automatically
+ calculated value is overridden.
+*/
+
+void QPicture::setBoundingRect(const QRect &r)
+{
+ d_func()->override_rect = r;
+}
+
+/*!
+ Replays the picture using \a painter, and returns true if
+ successful; otherwise returns false.
+
+ This function does exactly the same as QPainter::drawPicture()
+ with (x, y) = (0, 0).
+*/
+
+bool QPicture::play(QPainter *painter)
+{
+ Q_D(QPicture);
+
+ if (d->pictb.size() == 0) // nothing recorded
+ return true;
+
+ if (!d->formatOk && !d->checkFormat())
+ return false;
+
+ d->pictb.open(QIODevice::ReadOnly); // open buffer device
+ QDataStream s;
+ s.setDevice(&d->pictb); // attach data stream to buffer
+ s.device()->seek(10); // go directly to the data
+ s.setVersion(d->formatMajor == 4 ? 3 : d->formatMajor);
+
+ quint8 c, clen;
+ quint32 nrecords;
+ s >> c >> clen;
+ Q_ASSERT(c == QPicturePrivate::PdcBegin);
+ // bounding rect was introduced in ver 4. Read in checkFormat().
+ if (d->formatMajor >= 4) {
+ qint32 dummy;
+ s >> dummy >> dummy >> dummy >> dummy;
+ }
+ s >> nrecords;
+ if (!exec(painter, s, nrecords)) {
+ qWarning("QPicture::play: Format error");
+ d->pictb.close();
+ return false;
+ }
+ d->pictb.close();
+ return true; // no end-command
+}
+
+
+//
+// QFakeDevice is used to create fonts with a custom DPI
+//
+class QFakeDevice : public QPaintDevice
+{
+public:
+ QFakeDevice() { dpi_x = qt_defaultDpiX(); dpi_y = qt_defaultDpiY(); }
+ void setDpiX(int dpi) { dpi_x = dpi; }
+ void setDpiY(int dpi) { dpi_y = dpi; }
+ QPaintEngine *paintEngine() const { return 0; }
+ int metric(PaintDeviceMetric m) const
+ {
+ switch(m) {
+ case PdmPhysicalDpiX:
+ case PdmDpiX:
+ return dpi_x;
+ case PdmPhysicalDpiY:
+ case PdmDpiY:
+ return dpi_y;
+ default:
+ return QPaintDevice::metric(m);
+ }
+ }
+
+private:
+ int dpi_x;
+ int dpi_y;
+};
+
+/*!
+ \internal
+ Iterates over the internal picture data and draws the picture using
+ \a painter.
+*/
+
+bool QPicture::exec(QPainter *painter, QDataStream &s, int nrecords)
+{
+ Q_D(QPicture);
+#if defined(QT_DEBUG)
+ int strm_pos;
+#endif
+ quint8 c; // command id
+ quint8 tiny_len; // 8-bit length descriptor
+ qint32 len; // 32-bit length descriptor
+ qint16 i_16, i1_16, i2_16; // parameters...
+ qint8 i_8;
+ quint32 ul;
+ double dbl;
+ bool bl;
+ QByteArray str1;
+ QString str;
+ QPointF p, p1, p2;
+ QPoint ip, ip1, ip2;
+ QRect ir;
+ QRectF r;
+ QPolygonF a;
+ QPolygon ia;
+ QColor color;
+ QFont font;
+ QPen pen;
+ QBrush brush;
+ QRegion rgn;
+ QMatrix wmatrix;
+ QTransform matrix;
+
+ QTransform worldMatrix = painter->transform();
+ worldMatrix.scale(qreal(painter->device()->logicalDpiX()) / qreal(qt_defaultDpiX()),
+ qreal(painter->device()->logicalDpiY()) / qreal(qt_defaultDpiY()));
+ painter->setTransform(worldMatrix);
+
+ while (nrecords-- && !s.atEnd()) {
+ s >> c; // read cmd
+ s >> tiny_len; // read param length
+ if (tiny_len == 255) // longer than 254 bytes
+ s >> len;
+ else
+ len = tiny_len;
+#if defined(QT_DEBUG)
+ strm_pos = s.device()->pos();
+#endif
+ switch (c) { // exec cmd
+ case QPicturePrivate::PdcNOP:
+ break;
+ case QPicturePrivate::PdcDrawPoint:
+ if (d->formatMajor <= 5) {
+ s >> ip;
+ painter->drawPoint(ip);
+ } else {
+ s >> p;
+ painter->drawPoint(p);
+ }
+ break;
+ case QPicturePrivate::PdcDrawPoints:
+// ## implement me in the picture paint engine
+// s >> a >> i1_32 >> i2_32;
+// painter->drawPoints(a.mid(i1_32, i2_32));
+ break;
+ case QPicturePrivate::PdcDrawPath: {
+ QPainterPath path;
+ s >> path;
+ painter->drawPath(path);
+ break;
+ }
+ case QPicturePrivate::PdcDrawLine:
+ if (d->formatMajor <= 5) {
+ s >> ip1 >> ip2;
+ painter->drawLine(ip1, ip2);
+ } else {
+ s >> p1 >> p2;
+ painter->drawLine(p1, p2);
+ }
+ break;
+ case QPicturePrivate::PdcDrawRect:
+ if (d->formatMajor <= 5) {
+ s >> ir;
+ painter->drawRect(ir);
+ } else {
+ s >> r;
+ painter->drawRect(r);
+ }
+ break;
+ case QPicturePrivate::PdcDrawRoundRect:
+ if (d->formatMajor <= 5) {
+ s >> ir >> i1_16 >> i2_16;
+ painter->drawRoundedRect(ir, i1_16, i2_16, Qt::RelativeSize);
+ } else {
+ s >> r >> i1_16 >> i2_16;
+ painter->drawRoundedRect(r, i1_16, i2_16, Qt::RelativeSize);
+ }
+ break;
+ case QPicturePrivate::PdcDrawEllipse:
+ if (d->formatMajor <= 5) {
+ s >> ir;
+ painter->drawEllipse(ir);
+ } else {
+ s >> r;
+ painter->drawEllipse(r);
+ }
+ break;
+ case QPicturePrivate::PdcDrawArc:
+ if (d->formatMajor <= 5) {
+ s >> ir;
+ r = ir;
+ } else {
+ s >> r;
+ }
+ s >> i1_16 >> i2_16;
+ painter->drawArc(r, i1_16, i2_16);
+ break;
+ case QPicturePrivate::PdcDrawPie:
+ if (d->formatMajor <= 5) {
+ s >> ir;
+ r = ir;
+ } else {
+ s >> r;
+ }
+ s >> i1_16 >> i2_16;
+ painter->drawPie(r, i1_16, i2_16);
+ break;
+ case QPicturePrivate::PdcDrawChord:
+ if (d->formatMajor <= 5) {
+ s >> ir;
+ r = ir;
+ } else {
+ s >> r;
+ }
+ s >> i1_16 >> i2_16;
+ painter->drawChord(r, i1_16, i2_16);
+ break;
+ case QPicturePrivate::PdcDrawLineSegments:
+ s >> ia;
+ painter->drawLines(ia);
+ ia.clear();
+ break;
+ case QPicturePrivate::PdcDrawPolyline:
+ if (d->formatMajor <= 5) {
+ s >> ia;
+ painter->drawPolyline(ia);
+ ia.clear();
+ } else {
+ s >> a;
+ painter->drawPolyline(a);
+ a.clear();
+ }
+ break;
+ case QPicturePrivate::PdcDrawPolygon:
+ if (d->formatMajor <= 5) {
+ s >> ia >> i_8;
+ painter->drawPolygon(ia, i_8 ? Qt::WindingFill : Qt::OddEvenFill);
+ a.clear();
+ } else {
+ s >> a >> i_8;
+ painter->drawPolygon(a, i_8 ? Qt::WindingFill : Qt::OddEvenFill);
+ a.clear();
+ }
+ break;
+ case QPicturePrivate::PdcDrawCubicBezier: {
+ s >> ia;
+ QPainterPath path;
+ Q_ASSERT(ia.size() == 4);
+ path.moveTo(ia.at(0));
+ path.cubicTo(ia.at(1), ia.at(2), ia.at(3));
+ painter->strokePath(path, painter->pen());
+ a.clear();
+ }
+ break;
+ case QPicturePrivate::PdcDrawText:
+ s >> ip >> str1;
+ painter->drawText(ip, QString::fromLatin1(str1));
+ break;
+ case QPicturePrivate::PdcDrawTextFormatted:
+ s >> ir >> i_16 >> str1;
+ painter->drawText(ir, i_16, QString::fromLatin1(str1));
+ break;
+ case QPicturePrivate::PdcDrawText2:
+ if (d->formatMajor <= 5) {
+ s >> ip >> str;
+ painter->drawText(ip, str);
+ } else {
+ s >> p >> str;
+ painter->drawText(p, str);
+ }
+ break;
+ case QPicturePrivate::PdcDrawText2Formatted:
+ s >> ir;
+ s >> i_16;
+ s >> str;
+ painter->drawText(ir, i_16, str);
+ break;
+ case QPicturePrivate::PdcDrawTextItem: {
+ s >> p >> str >> font >> ul;
+
+ // the text layout direction is not used here because it's already
+ // aligned when QPicturePaintEngine::drawTextItem() serializes the
+ // drawText() call, therefore ul is unsed in this context
+
+ if (d->formatMajor >= 9) {
+ s >> dbl;
+ QFont fnt(font);
+ if (dbl != 1.0) {
+ QFakeDevice fake;
+ fake.setDpiX(qRound(dbl*qt_defaultDpiX()));
+ fake.setDpiY(qRound(dbl*qt_defaultDpiY()));
+ fnt = QFont(font, &fake);
+ }
+
+ qreal justificationWidth;
+ s >> justificationWidth;
+
+ int flags = Qt::TextSingleLine | Qt::TextDontClip | Qt::TextForceLeftToRight;
+
+ QSizeF size(1, 1);
+ if (justificationWidth > 0) {
+ size.setWidth(justificationWidth);
+ flags |= Qt::TextJustificationForced;
+ flags |= Qt::AlignJustify;
+ }
+
+ QFontMetrics fm(fnt);
+ QPointF pt(p.x(), p.y() - fm.ascent());
+ qt_format_text(fnt, QRectF(pt, size), flags, /*opt*/0,
+ str, /*brect=*/0, /*tabstops=*/0, /*...*/0, /*tabarraylen=*/0, painter);
+ } else {
+ qt_format_text(font, QRectF(p, QSizeF(1, 1)), Qt::TextSingleLine | Qt::TextDontClip, /*opt*/0,
+ str, /*brect=*/0, /*tabstops=*/0, /*...*/0, /*tabarraylen=*/0, painter);
+ }
+
+ break;
+ }
+ case QPicturePrivate::PdcDrawPixmap: {
+ QPixmap pixmap;
+ if (d->formatMajor < 4) {
+ s >> ip >> pixmap;
+ painter->drawPixmap(ip, pixmap);
+ } else if (d->formatMajor <= 5) {
+ s >> ir >> pixmap;
+ painter->drawPixmap(ir, pixmap);
+ } else {
+ QRectF sr;
+ if (d->in_memory_only) {
+ int index;
+ s >> r >> index >> sr;
+ Q_ASSERT(index < d->pixmap_list.size());
+ pixmap = d->pixmap_list.at(index);
+ } else {
+ s >> r >> pixmap >> sr;
+ }
+ painter->drawPixmap(r, pixmap, sr);
+ }
+ }
+ break;
+ case QPicturePrivate::PdcDrawTiledPixmap: {
+ QPixmap pixmap;
+ if (d->in_memory_only) {
+ int index;
+ s >> r >> index >> p;
+ Q_ASSERT(index < d->pixmap_list.size());
+ pixmap = d->pixmap_list.at(index);
+ } else {
+ s >> r >> pixmap >> p;
+ }
+ painter->drawTiledPixmap(r, pixmap, p);
+ }
+ break;
+ case QPicturePrivate::PdcDrawImage: {
+ QImage image;
+ if (d->formatMajor < 4) {
+ s >> p >> image;
+ painter->drawImage(p, image);
+ } else if (d->formatMajor <= 5){
+ s >> ir >> image;
+ painter->drawImage(ir, image, QRect(0, 0, ir.width(), ir.height()));
+ } else {
+ QRectF sr;
+ if (d->in_memory_only) {
+ int index;
+ s >> r >> index >> sr >> ul;
+ Q_ASSERT(index < d->image_list.size());
+ image = d->image_list.at(index);
+ } else {
+ s >> r >> image >> sr >> ul;
+ }
+ painter->drawImage(r, image, sr, Qt::ImageConversionFlags(ul));
+ }
+ }
+ break;
+ case QPicturePrivate::PdcBegin:
+ s >> ul; // number of records
+ if (!exec(painter, s, ul))
+ return false;
+ break;
+ case QPicturePrivate::PdcEnd:
+ if (nrecords == 0)
+ return true;
+ break;
+ case QPicturePrivate::PdcSave:
+ painter->save();
+ break;
+ case QPicturePrivate::PdcRestore:
+ painter->restore();
+ break;
+ case QPicturePrivate::PdcSetBkColor:
+ s >> color;
+ painter->setBackground(color);
+ break;
+ case QPicturePrivate::PdcSetBkMode:
+ s >> i_8;
+ painter->setBackgroundMode((Qt::BGMode)i_8);
+ break;
+ case QPicturePrivate::PdcSetROP: // NOP
+ s >> i_8;
+ break;
+ case QPicturePrivate::PdcSetBrushOrigin:
+ if (d->formatMajor <= 5) {
+ s >> ip;
+ painter->setBrushOrigin(ip);
+ } else {
+ s >> p;
+ painter->setBrushOrigin(p);
+ }
+ break;
+ case QPicturePrivate::PdcSetFont:
+ s >> font;
+ painter->setFont(font);
+ break;
+ case QPicturePrivate::PdcSetPen:
+ if (d->in_memory_only) {
+ int index;
+ s >> index;
+ Q_ASSERT(index < d->pen_list.size());
+ pen = d->pen_list.at(index);
+ } else {
+ s >> pen;
+ }
+ painter->setPen(pen);
+ break;
+ case QPicturePrivate::PdcSetBrush:
+ if (d->in_memory_only) {
+ int index;
+ s >> index;
+ Q_ASSERT(index < d->brush_list.size());
+ brush = d->brush_list.at(index);
+ } else {
+ s >> brush;
+ }
+ painter->setBrush(brush);
+ break;
+// #ifdef Q_Q3PAINTER
+// case QPicturePrivate::PdcSetTabStops:
+// s >> i_16;
+// painter->setTabStops(i_16);
+// break;
+// case QPicturePrivate::PdcSetTabArray:
+// s >> i_16;
+// if (i_16 == 0) {
+// painter->setTabArray(0);
+// } else {
+// int *ta = new int[i_16];
+// for (int i=0; i<i_16; i++) {
+// s >> i1_16;
+// ta[i] = i1_16;
+// }
+// painter->setTabArray(ta);
+// delete [] ta;
+// }
+// break;
+// #endif
+ case QPicturePrivate::PdcSetVXform:
+ s >> i_8;
+ painter->setViewTransformEnabled(i_8);
+ break;
+ case QPicturePrivate::PdcSetWindow:
+ if (d->formatMajor <= 5) {
+ s >> ir;
+ painter->setWindow(ir);
+ } else {
+ s >> r;
+ painter->setWindow(r.toRect());
+ }
+ break;
+ case QPicturePrivate::PdcSetViewport:
+ if (d->formatMajor <= 5) {
+ s >> ir;
+ painter->setViewport(ir);
+ } else {
+ s >> r;
+ painter->setViewport(r.toRect());
+ }
+ break;
+ case QPicturePrivate::PdcSetWXform:
+ s >> i_8;
+ painter->setMatrixEnabled(i_8);
+ break;
+ case QPicturePrivate::PdcSetWMatrix:
+ if (d->formatMajor >= 8) {
+ s >> matrix >> i_8;
+ } else {
+ s >> wmatrix >> i_8;
+ matrix = QTransform(wmatrix);
+ }
+ // i_8 is always false due to updateXForm() in qpaintengine_pic.cpp
+ painter->setTransform(matrix * worldMatrix, i_8);
+ break;
+// #ifdef Q_Q3PAINTER
+// case QPicturePrivate::PdcSaveWMatrix:
+// painter->saveWorldMatrix();
+// break;
+// case QPicturePrivate::PdcRestoreWMatrix:
+// painter->restoreWorldMatrix();
+// break;
+// #endif
+ case QPicturePrivate::PdcSetClip:
+ s >> i_8;
+ painter->setClipping(i_8);
+ break;
+ case QPicturePrivate::PdcSetClipRegion:
+ s >> rgn >> i_8;
+ if (d->formatMajor >= 9) {
+ painter->setClipRegion(rgn, Qt::ClipOperation(i_8));
+ } else {
+ painter->setClipRegion(rgn);
+ }
+ break;
+ case QPicturePrivate::PdcSetClipPath:
+ {
+ QPainterPath path;
+ s >> path >> i_8;
+ painter->setClipPath(path, Qt::ClipOperation(i_8));
+ break;
+ }
+ case QPicturePrivate::PdcSetRenderHint:
+ s >> ul;
+ painter->setRenderHint(QPainter::Antialiasing,
+ bool(ul & QPainter::Antialiasing));
+ painter->setRenderHint(QPainter::SmoothPixmapTransform,
+ bool(ul & QPainter::SmoothPixmapTransform));
+ break;
+ case QPicturePrivate::PdcSetCompositionMode:
+ s >> ul;
+ painter->setCompositionMode((QPainter::CompositionMode)ul);
+ break;
+ case QPicturePrivate::PdcSetClipEnabled:
+ s >> bl;
+ painter->setClipping(bl);
+ break;
+ case QPicturePrivate::PdcSetOpacity:
+ s >> dbl;
+ painter->setOpacity(qreal(dbl));
+ break;
+ default:
+ qWarning("QPicture::play: Invalid command %d", c);
+ if (len) // skip unknown command
+ s.device()->seek(s.device()->pos()+len);
+ }
+#if defined(QT_DEBUG)
+ //qDebug("device->at(): %i, strm_pos: %i len: %i", (int)s.device()->pos(), strm_pos, len);
+ Q_ASSERT(qint32(s.device()->pos() - strm_pos) == len);
+#endif
+ }
+ return false;
+}
+
+/*!
+ \internal
+
+ Internal implementation of the virtual QPaintDevice::metric()
+ function.
+
+ A picture has the following hard-coded values: numcolors=16777216
+ and depth=24.
+
+ \a m is the metric to get.
+*/
+
+int QPicture::metric(PaintDeviceMetric m) const
+{
+ int val;
+ QRect brect = boundingRect();
+ switch (m) {
+ case PdmWidth:
+ val = brect.width();
+ break;
+ case PdmHeight:
+ val = brect.height();
+ break;
+ case PdmWidthMM:
+ val = int(25.4/qt_defaultDpiX()*brect.width());
+ break;
+ case PdmHeightMM:
+ val = int(25.4/qt_defaultDpiY()*brect.height());
+ break;
+ case PdmDpiX:
+ case PdmPhysicalDpiX:
+ val = qt_defaultDpiX();
+ break;
+ case PdmDpiY:
+ case PdmPhysicalDpiY:
+ val = qt_defaultDpiY();
+ break;
+ case PdmNumColors:
+ val = 16777216;
+ break;
+ case PdmDepth:
+ val = 24;
+ break;
+ default:
+ val = 0;
+ qWarning("QPicture::metric: Invalid metric command");
+ }
+ return val;
+}
+
+/*!
+ \fn void QPicture::detach()
+ \internal
+ Detaches from shared picture data and makes sure that this picture
+ is the only one referring to the data.
+
+ If multiple pictures share common data, this picture makes a copy
+ of the data and detaches itself from the sharing mechanism.
+ Nothing is done if there is just a single reference.
+*/
+
+/*! \fn bool QPicture::isDetached() const
+\internal
+*/
+
+/*! \internal
+### Qt 5 - remove me
+ */
+void QPicture::detach_helper()
+{
+ // QExplicitelySharedDataPointer takes care of cloning using
+ // QPicturePrivate's copy constructor. Do not call detach_helper() anymore
+ // and remove in Qt 5, please.
+ Q_ASSERT_X(false, "QPicture::detach_helper()", "Do not call this function");
+}
+
+/*!
+ Assigns picture \a p to this picture and returns a reference to
+ this picture.
+*/
+QPicture& QPicture::operator=(const QPicture &p)
+{
+ d_ptr = p.d_ptr;
+ return *this;
+}
+
+/*!
+ \fn void QPicture::swap(QPicture &other)
+ \since 4.8
+
+ Swaps picture \a other with this picture. This operation is very
+ fast and never fails.
+*/
+
+/*!
+ \internal
+
+ Constructs a QPicturePrivate
+*/
+QPicturePrivate::QPicturePrivate()
+ : in_memory_only(false)
+{
+}
+
+/*!
+ \internal
+
+ Copy-Constructs a QPicturePrivate. Needed when detaching.
+*/
+QPicturePrivate::QPicturePrivate(const QPicturePrivate &other)
+ : trecs(other.trecs),
+ formatOk(other.formatOk),
+ formatMinor(other.formatMinor),
+ brect(other.brect),
+ override_rect(other.override_rect),
+ in_memory_only(false)
+{
+ pictb.setData(other.pictb.data(), other.pictb.size());
+ if (other.pictb.isOpen()) {
+ pictb.open(other.pictb.openMode());
+ pictb.seek(other.pictb.pos());
+ }
+}
+
+/*!
+ \internal
+
+ Sets formatOk to false and resets the format version numbers to default
+*/
+
+void QPicturePrivate::resetFormat()
+{
+ formatOk = false;
+ formatMajor = mfhdr_maj;
+ formatMinor = mfhdr_min;
+}
+
+
+/*!
+ \internal
+
+ Checks data integrity and format version number. Set formatOk to
+ true on success, to false otherwise. Returns the resulting formatOk
+ value.
+*/
+bool QPicturePrivate::checkFormat()
+{
+ resetFormat();
+
+ // can't check anything in an empty buffer
+ if (pictb.size() == 0 || pictb.isOpen())
+ return false;
+
+ pictb.open(QIODevice::ReadOnly); // open buffer device
+ QDataStream s;
+ s.setDevice(&pictb); // attach data stream to buffer
+
+ char mf_id[4]; // picture header tag
+ s.readRawData(mf_id, 4); // read actual tag
+ if (memcmp(mf_id, qt_mfhdr_tag, 4) != 0) { // wrong header id
+ qWarning("QPicturePaintEngine::checkFormat: Incorrect header");
+ pictb.close();
+ return false;
+ }
+
+ int cs_start = sizeof(quint32); // pos of checksum word
+ int data_start = cs_start + sizeof(quint16);
+ quint16 cs,ccs;
+ QByteArray buf = pictb.buffer(); // pointer to data
+
+ s >> cs; // read checksum
+ ccs = (quint16) qChecksum(buf.constData() + data_start, buf.size() - data_start);
+ if (ccs != cs) {
+ qWarning("QPicturePaintEngine::checkFormat: Invalid checksum %x, %x expected",
+ ccs, cs);
+ pictb.close();
+ return false;
+ }
+
+ quint16 major, minor;
+ s >> major >> minor; // read version number
+ if (major > mfhdr_maj) { // new, incompatible version
+ qWarning("QPicturePaintEngine::checkFormat: Incompatible version %d.%d",
+ major, minor);
+ pictb.close();
+ return false;
+ }
+ s.setVersion(major != 4 ? major : 3);
+
+ quint8 c, clen;
+ s >> c >> clen;
+ if (c == QPicturePrivate::PdcBegin) {
+ if (!(major >= 1 && major <= 3)) {
+ qint32 l, t, w, h;
+ s >> l >> t >> w >> h;
+ brect = QRect(l, t, w, h);
+ }
+ } else {
+ qWarning("QPicturePaintEngine::checkFormat: Format error");
+ pictb.close();
+ return false;
+ }
+ pictb.close();
+
+ formatOk = true; // picture seems to be ok
+ formatMajor = major;
+ formatMinor = minor;
+ return true;
+}
+
+/*! \internal */
+QPaintEngine *QPicture::paintEngine() const
+{
+ if (!d_func()->paintEngine)
+ const_cast<QPicture*>(this)->d_func()->paintEngine.reset(new QPicturePaintEngine);
+ return d_func()->paintEngine.data();
+}
+
+/*****************************************************************************
+ QPicture stream functions
+ *****************************************************************************/
+
+#ifndef QT_NO_DATASTREAM
+/*!
+ \relates QPicture
+
+ Writes picture \a r to the stream \a s and returns a reference to
+ the stream.
+*/
+
+QDataStream &operator<<(QDataStream &s, const QPicture &r)
+{
+ quint32 size = r.d_func()->pictb.buffer().size();
+ s << size;
+ // null picture ?
+ if (size == 0)
+ return s;
+ // just write the whole buffer to the stream
+ s.writeRawData (r.d_func()->pictb.buffer(), r.d_func()->pictb.buffer().size());
+ return s;
+}
+
+/*!
+ \relates QPicture
+
+ Reads a picture from the stream \a s into picture \a r and returns
+ a reference to the stream.
+*/
+
+QDataStream &operator>>(QDataStream &s, QPicture &r)
+{
+ QDataStream sr;
+
+ // "init"; this code is similar to the beginning of QPicture::cmd()
+ sr.setDevice(&r.d_func()->pictb);
+ sr.setVersion(r.d_func()->formatMajor);
+ quint32 len;
+ s >> len;
+ QByteArray data;
+ if (len > 0) {
+ data.resize(len);
+ s.readRawData(data.data(), len);
+ }
+
+ r.d_func()->pictb.setData(data);
+ r.d_func()->resetFormat();
+ return s;
+}
+#endif // QT_NO_DATASTREAM
+
+
+#ifndef QT_NO_PICTUREIO
+
+QT_BEGIN_INCLUDE_NAMESPACE
+#include "qregexp.h"
+#include "qapplication.h"
+#include "qpictureformatplugin.h"
+QT_END_INCLUDE_NAMESPACE
+
+/*!
+ \obsolete
+
+ Returns a string that specifies the picture format of the file \a
+ fileName, or 0 if the file cannot be read or if the format is not
+ recognized.
+
+ \sa load() save()
+*/
+
+const char* QPicture::pictureFormat(const QString &fileName)
+{
+ return QPictureIO::pictureFormat(fileName);
+}
+
+/*!
+ \obsolete
+
+ Returns a list of picture formats that are supported for picture
+ input.
+
+ \sa outputFormats() inputFormatList() QPictureIO
+*/
+QList<QByteArray> QPicture::inputFormats()
+{
+ return QPictureIO::inputFormats();
+}
+
+static QStringList qToStringList(const QList<QByteArray> arr)
+{
+ QStringList list;
+ for (int i = 0; i < arr.count(); ++i)
+ list.append(QString::fromLatin1(arr.at(i)));
+ return list;
+}
+
+/*!
+ \obsolete
+
+ Returns a list of picture formats that are supported for picture
+ input.
+
+ Note that if you want to iterate over the list, you should iterate
+ over a copy, e.g.
+ \snippet doc/src/snippets/picture/picture.cpp 2
+
+ \sa outputFormatList() inputFormats() QPictureIO
+*/
+QStringList QPicture::inputFormatList()
+{
+ return qToStringList(QPictureIO::inputFormats());
+}
+
+
+/*!
+ \obsolete
+
+ Returns a list of picture formats that are supported for picture
+ output.
+
+ Note that if you want to iterate over the list, you should iterate
+ over a copy, e.g.
+ \snippet doc/src/snippets/picture/picture.cpp 3
+
+ \sa inputFormatList() outputFormats() QPictureIO
+*/
+QStringList QPicture::outputFormatList()
+{
+ return qToStringList(QPictureIO::outputFormats());
+}
+
+/*!
+ \obsolete
+
+ Returns a list of picture formats that are supported for picture
+ output.
+
+ \sa inputFormats() outputFormatList() QPictureIO
+*/
+QList<QByteArray> QPicture::outputFormats()
+{
+ return QPictureIO::outputFormats();
+}
+
+/*****************************************************************************
+ QPictureIO member functions
+ *****************************************************************************/
+
+/*!
+ \obsolete
+
+ \class QPictureIO
+
+ \brief The QPictureIO class contains parameters for loading and
+ saving pictures.
+
+ \ingroup painting
+ \ingroup io
+
+ QPictureIO contains a QIODevice object that is used for picture data
+ I/O. The programmer can install new picture file formats in addition
+ to those that Qt provides.
+
+ You don't normally need to use this class; QPicture::load(),
+ QPicture::save().
+
+ \sa QPicture QPixmap QFile
+*/
+
+struct QPictureIOData
+{
+ QPicture pi; // picture
+ int iostat; // IO status
+ QByteArray frmt; // picture format
+ QIODevice *iodev; // IO device
+ QString fname; // file name
+ QString descr; // picture description
+ const char *parameters;
+ int quality;
+ float gamma;
+};
+
+/*!
+ Constructs a QPictureIO object with all parameters set to zero.
+*/
+
+QPictureIO::QPictureIO()
+{
+ init();
+}
+
+/*!
+ Constructs a QPictureIO object with the I/O device \a ioDevice and a
+ \a format tag.
+*/
+
+QPictureIO::QPictureIO(QIODevice *ioDevice, const char *format)
+{
+ init();
+ d->iodev = ioDevice;
+ d->frmt = format;
+}
+
+/*!
+ Constructs a QPictureIO object with the file name \a fileName and a
+ \a format tag.
+*/
+
+QPictureIO::QPictureIO(const QString &fileName, const char* format)
+{
+ init();
+ d->frmt = format;
+ d->fname = fileName;
+}
+
+/*!
+ Contains initialization common to all QPictureIO constructors.
+*/
+
+void QPictureIO::init()
+{
+ d = new QPictureIOData();
+ d->parameters = 0;
+ d->quality = -1; // default quality of the current format
+ d->gamma=0.0f;
+ d->iostat = 0;
+ d->iodev = 0;
+}
+
+/*!
+ Destroys the object and all related data.
+*/
+
+QPictureIO::~QPictureIO()
+{
+ if (d->parameters)
+ delete [] (char*)d->parameters;
+ delete d;
+}
+
+
+/*****************************************************************************
+ QPictureIO picture handler functions
+ *****************************************************************************/
+
+class QPictureHandler
+{
+public:
+ QPictureHandler(const char *f, const char *h, const QByteArray& fl,
+ picture_io_handler r, picture_io_handler w);
+ QByteArray format; // picture format
+ QRegExp header; // picture header pattern
+ enum TMode { Untranslated=0, TranslateIn, TranslateInOut } text_mode;
+ picture_io_handler read_picture; // picture read function
+ picture_io_handler write_picture; // picture write function
+ bool obsolete; // support not "published"
+};
+
+QPictureHandler::QPictureHandler(const char *f, const char *h, const QByteArray& fl,
+ picture_io_handler r, picture_io_handler w)
+ : format(f), header(QString::fromLatin1(h))
+{
+ text_mode = Untranslated;
+ if (fl.contains('t'))
+ text_mode = TranslateIn;
+ else if (fl.contains('T'))
+ text_mode = TranslateInOut;
+ obsolete = fl.contains('O');
+ read_picture = r;
+ write_picture = w;
+}
+
+typedef QList<QPictureHandler *> QPHList;
+Q_GLOBAL_STATIC(QPHList, pictureHandlers)
+
+#ifndef QT_NO_LIBRARY
+Q_GLOBAL_STATIC(QMutex, mutex)
+Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, factoryLoader,
+ (QPictureFormatInterface_iid,
+ QLatin1String("/pictureformats")))
+#endif
+void qt_init_picture_plugins()
+{
+#ifndef QT_NO_LIBRARY
+ QMutexLocker locker(mutex());
+ QFactoryLoader *loader = factoryLoader();
+ QStringList keys = loader->keys();
+ for (int i = 0; i < keys.count(); ++i)
+ if (QPictureFormatInterface *format = qobject_cast<QPictureFormatInterface*>(loader->instance(keys.at(i))))
+ format->installIOHandler(keys.at(i));
+#endif
+}
+
+static void cleanup()
+{
+ // make sure that picture handlers are delete before plugin manager
+ if (QPHList *list = pictureHandlers()) {
+ qDeleteAll(*list);
+ list->clear();
+ }
+}
+
+void qt_init_picture_handlers() // initialize picture handlers
+{
+ static QBasicAtomicInt done = Q_BASIC_ATOMIC_INITIALIZER(0);
+ if (done.testAndSetRelaxed(0, 1)) {
+ qAddPostRoutine(cleanup);
+ }
+}
+
+static QPictureHandler *get_picture_handler(const char *format)
+{ // get pointer to handler
+ qt_init_picture_handlers();
+ qt_init_picture_plugins();
+ if (QPHList *list = pictureHandlers()) {
+ for (int i = 0; i < list->size(); ++i) {
+ if (list->at(i)->format == format)
+ return list->at(i);
+ }
+ }
+ return 0; // no such handler
+}
+
+
+/*!
+ Defines a picture I/O handler for the picture format called \a
+ format, which is recognized using the regular
+ expression defined in \a header, read using \a readPicture and
+ written using \a writePicture.
+
+ \a flags is a string of single-character flags for this format.
+ The only flag defined currently is T (upper case), so the only
+ legal value for \a flags are "T" and the empty string. The "T"
+ flag means that the picture file is a text file, and Qt should treat
+ all newline conventions as equivalent. (XPM files and some PPM
+ files are text files for example.)
+
+ \a format is used to select a handler to write a QPicture; \a header
+ is used to select a handler to read an picture file.
+
+ If \a readPicture is a null pointer, the QPictureIO will not be able
+ to read pictures in \a format. If \a writePicture is a null pointer,
+ the QPictureIO will not be able to write pictures in \a format. If
+ both are null, the QPictureIO object is valid but useless.
+
+ Example:
+ \snippet doc/src/snippets/picture/picture.cpp 6
+ \codeline
+ \snippet doc/src/snippets/picture/picture.cpp 7
+ \codeline
+ \snippet doc/src/snippets/picture/picture.cpp 8
+
+ Before the regular expression test, all the 0 bytes in the file header are
+ converted to 1 bytes. This is done because when Qt was ASCII-based, QRegExp
+ could not handle 0 bytes in strings.
+
+ The regexp is only applied on the first 14 bytes of the file.
+
+ (Note that if one handlerIO supports writing a format and another
+ supports reading it, Qt supports both reading and writing. If two
+ handlers support the same operation, Qt chooses one arbitrarily.)
+*/
+
+void QPictureIO::defineIOHandler(const char *format,
+ const char *header,
+ const char *flags,
+ picture_io_handler readPicture,
+ picture_io_handler writePicture)
+{
+ qt_init_picture_handlers();
+ if (QPHList *list = pictureHandlers()) {
+ QPictureHandler *p;
+ p = new QPictureHandler(format, header, QByteArray(flags), readPicture, writePicture);
+ list->prepend(p);
+ }
+}
+
+
+/*****************************************************************************
+ QPictureIO normal member functions
+ *****************************************************************************/
+
+/*!
+ Returns the picture currently set.
+
+ \sa setPicture()
+*/
+const QPicture &QPictureIO::picture() const { return d->pi; }
+
+/*!
+ Returns the picture's IO status. A non-zero value indicates an
+ error, whereas 0 means that the IO operation was successful.
+
+ \sa setStatus()
+*/
+int QPictureIO::status() const { return d->iostat; }
+
+/*!
+ Returns the picture format string or 0 if no format has been
+ explicitly set.
+*/
+const char *QPictureIO::format() const { return d->frmt; }
+
+/*!
+ Returns the IO device currently set.
+
+ \sa setIODevice()
+*/
+QIODevice *QPictureIO::ioDevice() const { return d->iodev; }
+
+/*!
+ Returns the file name currently set.
+
+ \sa setFileName()
+*/
+QString QPictureIO::fileName() const { return d->fname; }
+
+
+/*!
+ Returns the picture description string.
+
+ \sa setDescription()
+*/
+QString QPictureIO::description() const { return d->descr; }
+
+/*!
+ Sets the picture to \a picture.
+
+ \sa picture()
+*/
+void QPictureIO::setPicture(const QPicture &picture)
+{
+ d->pi = picture;
+}
+
+/*!
+ Sets the picture IO status to \a status. A non-zero value indicates
+ an error, whereas 0 means that the IO operation was successful.
+
+ \sa status()
+*/
+void QPictureIO::setStatus(int status)
+{
+ d->iostat = status;
+}
+
+/*!
+ Sets the picture format to \a format for the picture to be read or
+ written.
+
+ It is necessary to specify a format before writing an picture, but
+ it is not necessary to specify a format before reading an picture.
+
+ If no format has been set, Qt guesses the picture format before
+ reading it. If a format is set the picture will only be read if it
+ has that format.
+
+ \sa read() write() format()
+*/
+void QPictureIO::setFormat(const char *format)
+{
+ d->frmt = format;
+}
+
+/*!
+ Sets the IO device to be used for reading or writing an picture.
+
+ Setting the IO device allows pictures to be read/written to any
+ block-oriented QIODevice.
+
+ If \a ioDevice is not null, this IO device will override file name
+ settings.
+
+ \sa setFileName()
+*/
+void QPictureIO::setIODevice(QIODevice *ioDevice)
+{
+ d->iodev = ioDevice;
+}
+
+/*!
+ Sets the name of the file to read or write an picture from to \a
+ fileName.
+
+ \sa setIODevice()
+*/
+void QPictureIO::setFileName(const QString &fileName)
+{
+ d->fname = fileName;
+}
+
+/*!
+ Returns the quality of the written picture, related to the
+ compression ratio.
+
+ \sa setQuality() QPicture::save()
+*/
+int QPictureIO::quality() const
+{
+ return d->quality;
+}
+
+/*!
+ Sets the quality of the written picture to \a q, related to the
+ compression ratio.
+
+ \a q must be in the range -1..100. Specify 0 to obtain small
+ compressed files, 100 for large uncompressed files. (-1 signifies
+ the default compression.)
+
+ \sa quality() QPicture::save()
+*/
+
+void QPictureIO::setQuality(int q)
+{
+ d->quality = q;
+}
+
+/*!
+ Returns the picture's parameters string.
+
+ \sa setParameters()
+*/
+
+const char *QPictureIO::parameters() const
+{
+ return d->parameters;
+}
+
+/*!
+ Sets the picture's parameter string to \a parameters. This is for
+ picture handlers that require special parameters.
+
+ Although the current picture formats supported by Qt ignore the
+ parameters string, it may be used in future extensions or by
+ contributions (for example, JPEG).
+
+ \sa parameters()
+*/
+
+void QPictureIO::setParameters(const char *parameters)
+{
+ if (d->parameters)
+ delete [] (char*)d->parameters;
+ d->parameters = qstrdup(parameters);
+}
+
+/*!
+ Sets the gamma value at which the picture will be viewed to \a
+ gamma. If the picture format stores a gamma value for which the
+ picture is intended to be used, then this setting will be used to
+ modify the picture. Setting to 0.0 will disable gamma correction
+ (i.e. any specification in the file will be ignored).
+
+ The default value is 0.0.
+
+ \sa gamma()
+*/
+void QPictureIO::setGamma(float gamma)
+{
+ d->gamma=gamma;
+}
+
+/*!
+ Returns the gamma value at which the picture will be viewed.
+
+ \sa setGamma()
+*/
+float QPictureIO::gamma() const
+{
+ return d->gamma;
+}
+
+/*!
+ Sets the picture description string for picture handlers that support
+ picture descriptions to \a description.
+
+ Currently, no picture format supported by Qt uses the description
+ string.
+*/
+
+void QPictureIO::setDescription(const QString &description)
+{
+ d->descr = description;
+}
+
+
+/*!
+ Returns a string that specifies the picture format of the file \a
+ fileName, or null if the file cannot be read or if the format is
+ not recognized.
+*/
+
+QByteArray QPictureIO::pictureFormat(const QString &fileName)
+{
+ QFile file(fileName);
+ QByteArray format;
+ if (!file.open(QIODevice::ReadOnly))
+ return format;
+ format = pictureFormat(&file);
+ file.close();
+ return format;
+}
+
+/*!
+ \overload
+
+ Returns a string that specifies the picture format of the picture read
+ from IO device \a d, or 0 if the device cannot be read or if the
+ format is not recognized.
+
+ Make sure that \a d is at the right position in the device (for
+ example, at the beginning of the file).
+
+ \sa QIODevice::at()
+*/
+
+QByteArray QPictureIO::pictureFormat(QIODevice *d)
+{
+ // if you change this change the documentation for defineIOHandler()
+ const int buflen = 14;
+
+ char buf[buflen];
+ char buf2[buflen];
+ qt_init_picture_handlers();
+ qt_init_picture_plugins();
+ int pos = d->pos(); // save position
+ int rdlen = d->read(buf, buflen); // read a few bytes
+
+ QByteArray format;
+ if (rdlen != buflen)
+ return format;
+
+ memcpy(buf2, buf, buflen);
+
+ for (int n = 0; n < rdlen; n++)
+ if (buf[n] == '\0')
+ buf[n] = '\001';
+ if (rdlen > 0) {
+ buf[rdlen - 1] = '\0';
+ QString bufStr = QString::fromLatin1(buf);
+ if (QPHList *list = pictureHandlers()) {
+ for (int i = 0; i < list->size(); ++i) {
+ if (list->at(i)->header.indexIn(bufStr) != -1) { // try match with headers
+ format = list->at(i)->format;
+ break;
+ }
+ }
+ }
+ }
+ d->seek(pos); // restore position
+ return format;
+}
+
+/*!
+ Returns a sorted list of picture formats that are supported for
+ picture input.
+*/
+QList<QByteArray> QPictureIO::inputFormats()
+{
+ QList<QByteArray> result;
+
+ qt_init_picture_handlers();
+ qt_init_picture_plugins();
+
+ if (QPHList *list = pictureHandlers()) {
+ for (int i = 0; i < list->size(); ++i) {
+ QPictureHandler *p = list->at(i);
+ if (p->read_picture && !p->obsolete && !result.contains(p->format))
+ result.append(p->format);
+ }
+ }
+ qSort(result);
+
+ return result;
+}
+
+/*!
+ Returns a sorted list of picture formats that are supported for
+ picture output.
+*/
+QList<QByteArray> QPictureIO::outputFormats()
+{
+ qt_init_picture_handlers();
+ qt_init_picture_plugins();
+
+ QList<QByteArray> result;
+ if (QPHList *list = pictureHandlers()) {
+ for (int i = 0; i < list->size(); ++i) {
+ QPictureHandler *p = list->at(i);
+ if (p->write_picture && !p->obsolete && !result.contains(p->format))
+ result.append(p->format);
+ }
+ }
+ return result;
+}
+
+
+
+/*!
+ Reads an picture into memory and returns true if the picture was
+ successfully read; otherwise returns false.
+
+ Before reading an picture you must set an IO device or a file name.
+ If both an IO device and a file name have been set, the IO device
+ will be used.
+
+ Setting the picture file format string is optional.
+
+ Note that this function does \e not set the \link format()
+ format\endlink used to read the picture. If you need that
+ information, use the pictureFormat() static functions.
+
+ Example:
+
+ \snippet doc/src/snippets/picture/picture.cpp 4
+
+ \sa setIODevice() setFileName() setFormat() write() QPixmap::load()
+*/
+bool QPictureIO::read()
+{
+ QFile file;
+ const char *picture_format;
+ QPictureHandler *h;
+
+ if (d->iodev) { // read from io device
+ // ok, already open
+ } else if (!d->fname.isEmpty()) { // read from file
+ file.setFileName(d->fname);
+ if (!file.open(QIODevice::ReadOnly))
+ return false; // cannot open file
+ d->iodev = &file;
+ } else { // no file name or io device
+ return false;
+ }
+ if (d->frmt.isEmpty()) {
+ // Try to guess format
+ picture_format = pictureFormat(d->iodev); // get picture format
+ if (!picture_format) {
+ if (file.isOpen()) { // unknown format
+ file.close();
+ d->iodev = 0;
+ }
+ return false;
+ }
+ } else {
+ picture_format = d->frmt;
+ }
+
+ h = get_picture_handler(picture_format);
+ if (file.isOpen()) {
+#if !defined(Q_OS_UNIX)
+ if (h && h->text_mode) { // reopen in translated mode
+ file.close();
+ file.open(QIODevice::ReadOnly | QIODevice::Text);
+ }
+ else
+#endif
+ file.seek(0); // position to start
+ }
+ d->iostat = 1; // assume error
+
+ if (h && h->read_picture)
+ (*h->read_picture)(this);
+
+ if (file.isOpen()) { // picture was read using file
+ file.close();
+ d->iodev = 0;
+ }
+ return d->iostat == 0; // picture successfully read?
+}
+
+
+/*!
+ Writes an picture to an IO device and returns true if the picture was
+ successfully written; otherwise returns false.
+
+ Before writing an picture you must set an IO device or a file name.
+ If both an IO device and a file name have been set, the IO device
+ will be used.
+
+ The picture will be written using the specified picture format.
+
+ Example:
+ \snippet doc/src/snippets/picture/picture.cpp 5
+
+ \sa setIODevice() setFileName() setFormat() read() QPixmap::save()
+*/
+bool QPictureIO::write()
+{
+ if (d->frmt.isEmpty())
+ return false;
+ QPictureHandler *h = get_picture_handler(d->frmt);
+ if (!h || !h->write_picture) {
+ qWarning("QPictureIO::write: No such picture format handler: %s",
+ format());
+ return false;
+ }
+ QFile file;
+ if (!d->iodev && !d->fname.isEmpty()) {
+ file.setFileName(d->fname);
+ bool translate = h->text_mode==QPictureHandler::TranslateInOut;
+ QIODevice::OpenMode fmode = translate ? QIODevice::WriteOnly | QIODevice::Text : QIODevice::OpenMode(QIODevice::WriteOnly);
+ if (!file.open(fmode)) // couldn't create file
+ return false;
+ d->iodev = &file;
+ }
+ d->iostat = 1;
+ (*h->write_picture)(this);
+ if (file.isOpen()) { // picture was written using file
+ file.close();
+ d->iodev = 0;
+ }
+ return d->iostat == 0; // picture successfully written?
+}
+#endif //QT_NO_PICTUREIO
+
+/*!
+ \fn QPicture QPicture::copy() const
+
+ Use simple assignment instead.
+*/
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PICTURE
+
+/*!
+ \typedef QPicture::DataPtr
+ \internal
+*/
+
+/*!
+ \fn DataPtr &QPicture::data_ptr()
+ \internal
+*/
diff --git a/src/gui/image/qpicture.h b/src/gui/image/qpicture.h
new file mode 100644
index 0000000000..c478ed962e
--- /dev/null
+++ b/src/gui/image/qpicture.h
@@ -0,0 +1,204 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPICTURE_H
+#define QPICTURE_H
+
+#include <QtCore/qstringlist.h>
+#include <QtCore/qsharedpointer.h>
+#include <QtGui/qpaintdevice.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_PICTURE
+
+class QPicturePrivate;
+class Q_GUI_EXPORT QPicture : public QPaintDevice
+{
+ Q_DECLARE_PRIVATE(QPicture)
+public:
+ explicit QPicture(int formatVersion = -1);
+ QPicture(const QPicture &);
+ ~QPicture();
+
+ bool isNull() const;
+
+ int devType() const;
+ uint size() const;
+ const char* data() const;
+ virtual void setData(const char* data, uint size);
+
+ bool play(QPainter *p);
+
+ bool load(QIODevice *dev, const char *format = 0);
+ bool load(const QString &fileName, const char *format = 0);
+ bool save(QIODevice *dev, const char *format = 0);
+ bool save(const QString &fileName, const char *format = 0);
+
+ QRect boundingRect() const;
+ void setBoundingRect(const QRect &r);
+
+ QPicture& operator=(const QPicture &p);
+#ifdef Q_COMPILER_RVALUE_REFS
+ inline QPicture &operator=(QPicture &&other)
+ { qSwap(d_ptr, other.d_ptr); return *this; }
+#endif
+ inline void swap(QPicture &other) { d_ptr.swap(other.d_ptr); }
+ void detach();
+ bool isDetached() const;
+
+ friend Q_GUI_EXPORT QDataStream &operator<<(QDataStream &in, const QPicture &p);
+ friend Q_GUI_EXPORT QDataStream &operator>>(QDataStream &in, QPicture &p);
+
+ static const char* pictureFormat(const QString &fileName);
+ static QList<QByteArray> inputFormats();
+ static QList<QByteArray> outputFormats();
+ static QStringList inputFormatList();
+ static QStringList outputFormatList();
+
+ QPaintEngine *paintEngine() const;
+
+protected:
+ QPicture(QPicturePrivate &data);
+
+ int metric(PaintDeviceMetric m) const;
+#ifdef QT3_SUPPORT
+ inline QT3_SUPPORT QPicture copy() const { QPicture p(*this); p.detach(); return p; }
+#endif
+
+private:
+ bool exec(QPainter *p, QDataStream &ds, int i);
+ void detach_helper();
+
+ QExplicitlySharedDataPointer<QPicturePrivate> d_ptr;
+ friend class QPicturePaintEngine;
+ friend class Q3Picture;
+ friend class QAlphaPaintEngine;
+ friend class QPreviewPaintEngine;
+
+public:
+ typedef QExplicitlySharedDataPointer<QPicturePrivate> DataPtr;
+ inline DataPtr &data_ptr() { return d_ptr; }
+};
+
+Q_DECLARE_SHARED(QPicture)
+
+
+#ifndef QT_NO_PICTUREIO
+class QIODevice;
+class QPictureIO;
+typedef void (*picture_io_handler)(QPictureIO *); // picture IO handler
+
+struct QPictureIOData;
+
+class Q_GUI_EXPORT QPictureIO
+{
+public:
+ QPictureIO();
+ QPictureIO(QIODevice *ioDevice, const char *format);
+ QPictureIO(const QString &fileName, const char *format);
+ ~QPictureIO();
+
+ const QPicture &picture() const;
+ int status() const;
+ const char *format() const;
+ QIODevice *ioDevice() const;
+ QString fileName() const;
+ int quality() const;
+ QString description() const;
+ const char *parameters() const;
+ float gamma() const;
+
+ void setPicture(const QPicture &);
+ void setStatus(int);
+ void setFormat(const char *);
+ void setIODevice(QIODevice *);
+ void setFileName(const QString &);
+ void setQuality(int);
+ void setDescription(const QString &);
+ void setParameters(const char *);
+ void setGamma(float);
+
+ bool read();
+ bool write();
+
+ static QByteArray pictureFormat(const QString &fileName);
+ static QByteArray pictureFormat(QIODevice *);
+ static QList<QByteArray> inputFormats();
+ static QList<QByteArray> outputFormats();
+
+ static void defineIOHandler(const char *format,
+ const char *header,
+ const char *flags,
+ picture_io_handler read_picture,
+ picture_io_handler write_picture);
+
+private:
+ Q_DISABLE_COPY(QPictureIO)
+
+ void init();
+
+ QPictureIOData *d;
+};
+
+#endif //QT_NO_PICTUREIO
+
+
+/*****************************************************************************
+ QPicture stream functions
+ *****************************************************************************/
+
+#ifndef QT_NO_DATASTREAM
+Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QPicture &);
+Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QPicture &);
+#endif
+
+#endif // QT_NO_PICTURE
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QPICTURE_H
diff --git a/src/gui/image/qpicture_p.h b/src/gui/image/qpicture_p.h
new file mode 100644
index 0000000000..e06bf1fb69
--- /dev/null
+++ b/src/gui/image/qpicture_p.h
@@ -0,0 +1,169 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPICTURE_P_H
+#define QPICTURE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtCore/qatomic.h"
+#include "QtCore/qbuffer.h"
+#include "QtCore/qobjectdefs.h"
+#include "QtGui/qpicture.h"
+#include "QtGui/qpixmap.h"
+#include "QtGui/qpen.h"
+#include "QtGui/qbrush.h"
+#include "QtCore/qrect.h"
+#include "private/qobject_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QPaintEngine;
+
+extern const char *qt_mfhdr_tag;
+
+class QPicturePrivate
+{
+ friend class QPicturePaintEngine;
+ friend Q_GUI_EXPORT QDataStream &operator<<(QDataStream &s, const QPicture &r);
+ friend Q_GUI_EXPORT QDataStream &operator>>(QDataStream &s, QPicture &r);
+
+public:
+ enum PaintCommand {
+ PdcNOP = 0, // <void>
+ PdcDrawPoint = 1, // point
+ PdcDrawFirst = PdcDrawPoint,
+ PdcMoveTo = 2, // point
+ PdcLineTo = 3, // point
+ PdcDrawLine = 4, // point,point
+ PdcDrawRect = 5, // rect
+ PdcDrawRoundRect = 6, // rect,ival,ival
+ PdcDrawEllipse = 7, // rect
+ PdcDrawArc = 8, // rect,ival,ival
+ PdcDrawPie = 9, // rect,ival,ival
+ PdcDrawChord = 10, // rect,ival,ival
+ PdcDrawLineSegments = 11, // ptarr
+ PdcDrawPolyline = 12, // ptarr
+ PdcDrawPolygon = 13, // ptarr,ival
+ PdcDrawCubicBezier = 14, // ptarr
+ PdcDrawText = 15, // point,str
+ PdcDrawTextFormatted = 16, // rect,ival,str
+ PdcDrawPixmap = 17, // rect,pixmap
+ PdcDrawImage = 18, // rect,image
+ PdcDrawText2 = 19, // point,str
+ PdcDrawText2Formatted = 20, // rect,ival,str
+ PdcDrawTextItem = 21, // pos,text,font,flags
+ PdcDrawLast = PdcDrawTextItem,
+ PdcDrawPoints = 22, // ptarr,ival,ival
+ PdcDrawWinFocusRect = 23, // rect,color
+ PdcDrawTiledPixmap = 24, // rect,pixmap,point
+ PdcDrawPath = 25, // path
+
+ // no painting commands below PdcDrawLast.
+
+ PdcBegin = 30, // <void>
+ PdcEnd = 31, // <void>
+ PdcSave = 32, // <void>
+ PdcRestore = 33, // <void>
+ PdcSetdev = 34, // device - PRIVATE
+ PdcSetBkColor = 40, // color
+ PdcSetBkMode = 41, // ival
+ PdcSetROP = 42, // ival
+ PdcSetBrushOrigin = 43, // point
+ PdcSetFont = 45, // font
+ PdcSetPen = 46, // pen
+ PdcSetBrush = 47, // brush
+ PdcSetTabStops = 48, // ival
+ PdcSetTabArray = 49, // ival,ivec
+ PdcSetUnit = 50, // ival
+ PdcSetVXform = 51, // ival
+ PdcSetWindow = 52, // rect
+ PdcSetViewport = 53, // rect
+ PdcSetWXform = 54, // ival
+ PdcSetWMatrix = 55, // matrix,ival
+ PdcSaveWMatrix = 56,
+ PdcRestoreWMatrix = 57,
+ PdcSetClip = 60, // ival
+ PdcSetClipRegion = 61, // rgn
+ PdcSetClipPath = 62, // path
+ PdcSetRenderHint = 63, // ival
+ PdcSetCompositionMode = 64, // ival
+ PdcSetClipEnabled = 65, // bool
+ PdcSetOpacity = 66, // qreal
+
+ PdcReservedStart = 0, // codes 0-199 are reserved
+ PdcReservedStop = 199 // for Qt
+ };
+
+ QPicturePrivate();
+ QPicturePrivate(const QPicturePrivate &other);
+ QAtomicInt ref;
+
+ bool checkFormat();
+ void resetFormat();
+
+ QBuffer pictb;
+ int trecs;
+ bool formatOk;
+ int formatMajor;
+ int formatMinor;
+ QRect brect;
+ QRect override_rect;
+ QScopedPointer<QPaintEngine> paintEngine;
+ bool in_memory_only;
+ QList<QImage> image_list;
+ QList<QPixmap> pixmap_list;
+ QList<QBrush> brush_list;
+ QList<QPen> pen_list;
+};
+
+QT_END_NAMESPACE
+
+#endif // QPICTURE_P_H
diff --git a/src/gui/image/qpictureformatplugin.cpp b/src/gui/image/qpictureformatplugin.cpp
new file mode 100644
index 0000000000..8ece9e9c56
--- /dev/null
+++ b/src/gui/image/qpictureformatplugin.cpp
@@ -0,0 +1,139 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpictureformatplugin.h"
+#if !defined(QT_NO_LIBRARY) && !defined(QT_NO_PICTURE)
+#include "qpicture.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \obsolete
+
+ \class QPictureFormatPlugin
+ \brief The QPictureFormatPlugin class provides an abstract base
+ for custom picture format plugins.
+
+ \ingroup plugins
+
+ The picture format plugin is a simple plugin interface that makes
+ it easy to create custom picture formats that can be used
+ transparently by applications.
+
+ Writing an picture format plugin is achieved by subclassing this
+ base class, reimplementing the pure virtual functions keys(),
+ loadPicture(), savePicture(), and installIOHandler(), and
+ exporting the class with the Q_EXPORT_PLUGIN2() macro.
+
+ \sa {How to Create Qt Plugins}
+*/
+
+/*!
+ \fn QStringList QPictureFormatPlugin::keys() const
+
+ Returns the list of picture formats this plugin supports.
+
+ \sa installIOHandler()
+*/
+
+/*!
+ \fn bool QPictureFormatPlugin::installIOHandler(const QString &format)
+
+ Installs a QPictureIO picture I/O handler for the picture format \a
+ format.
+
+ \sa keys()
+*/
+
+
+/*!
+ Constructs an picture format plugin with the given \a parent.
+ This is invoked automatically by the Q_EXPORT_PLUGIN2() macro.
+*/
+QPictureFormatPlugin::QPictureFormatPlugin(QObject *parent)
+ : QObject(parent)
+{
+}
+
+/*!
+ Destroys the picture format plugin.
+
+ You never have to call this explicitly. Qt destroys a plugin
+ automatically when it is no longer used.
+*/
+QPictureFormatPlugin::~QPictureFormatPlugin()
+{
+}
+
+
+/*!
+ Loads the picture stored in the file called \a fileName, with the
+ given \a format, into *\a picture. Returns true on success;
+ otherwise returns false.
+
+ \sa savePicture()
+*/
+bool QPictureFormatPlugin::loadPicture(const QString &format, const QString &fileName, QPicture *picture)
+{
+ Q_UNUSED(format)
+ Q_UNUSED(fileName)
+ Q_UNUSED(picture)
+ return false;
+}
+
+/*!
+ Saves the given \a picture into the file called \a fileName,
+ using the specified \a format. Returns true on success; otherwise
+ returns false.
+
+ \sa loadPicture()
+*/
+bool QPictureFormatPlugin::savePicture(const QString &format, const QString &fileName, const QPicture &picture)
+{
+ Q_UNUSED(format)
+ Q_UNUSED(fileName)
+ Q_UNUSED(picture)
+ return false;
+}
+
+#endif // QT_NO_LIBRARY || QT_NO_PICTURE
+
+QT_END_NAMESPACE
diff --git a/src/gui/image/qpictureformatplugin.h b/src/gui/image/qpictureformatplugin.h
new file mode 100644
index 0000000000..7b9ada1fe0
--- /dev/null
+++ b/src/gui/image/qpictureformatplugin.h
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPICTUREFORMATPLUGIN_H
+#define QPICTUREFORMATPLUGIN_H
+
+#include <QtCore/qplugin.h>
+#include <QtCore/qfactoryinterface.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#if !defined(QT_NO_LIBRARY) && !defined(QT_NO_PICTURE)
+
+class QPicture;
+class QImage;
+class QString;
+class QStringList;
+
+struct Q_GUI_EXPORT QPictureFormatInterface : public QFactoryInterface
+{
+ virtual bool loadPicture(const QString &format, const QString &filename, QPicture *) = 0;
+ virtual bool savePicture(const QString &format, const QString &filename, const QPicture &) = 0;
+
+ virtual bool installIOHandler(const QString &) = 0;
+};
+
+#define QPictureFormatInterface_iid "com.trolltech.Qt.QPictureFormatInterface"
+Q_DECLARE_INTERFACE(QPictureFormatInterface, QPictureFormatInterface_iid)
+
+
+class Q_GUI_EXPORT QPictureFormatPlugin : public QObject, public QPictureFormatInterface
+{
+ Q_OBJECT
+ Q_INTERFACES(QPictureFormatInterface:QFactoryInterface)
+public:
+ explicit QPictureFormatPlugin(QObject *parent = 0);
+ ~QPictureFormatPlugin();
+
+ virtual QStringList keys() const = 0;
+ virtual bool loadPicture(const QString &format, const QString &filename, QPicture *pic);
+ virtual bool savePicture(const QString &format, const QString &filename, const QPicture &pic);
+ virtual bool installIOHandler(const QString &format) = 0;
+
+};
+
+#endif // QT_NO_LIBRARY || QT_NO_PICTURE
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QPICTUREFORMATPLUGIN_H
diff --git a/src/gui/image/qpixmap.cpp b/src/gui/image/qpixmap.cpp
new file mode 100644
index 0000000000..34804e5311
--- /dev/null
+++ b/src/gui/image/qpixmap.cpp
@@ -0,0 +1,2297 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qglobal.h>
+
+#include "qpixmap.h"
+#include "qpixmapdata_p.h"
+#include "qimagepixmapcleanuphooks_p.h"
+
+#include "qbitmap.h"
+#include "qcolormap.h"
+#include "qimage.h"
+#include "qwidget.h"
+#include "qpainter.h"
+#include "qdatastream.h"
+#include "qbuffer.h"
+#include "qapplication.h"
+#include <private/qapplication_p.h>
+#include <private/qgraphicssystem_p.h>
+#include <private/qwidget_p.h>
+#include "qevent.h"
+#include "qfile.h"
+#include "qfileinfo.h"
+#include "qpixmapcache.h"
+#include "qdatetime.h"
+#include "qimagereader.h"
+#include "qimagewriter.h"
+#include "qpaintengine.h"
+#include "qthread.h"
+
+#ifdef Q_WS_MAC
+# include "private/qt_mac_p.h"
+# include "private/qpixmap_mac_p.h"
+#endif
+
+#ifdef Q_WS_QPA
+# include "qplatformintegration_qpa.h"
+#endif
+
+#if defined(Q_WS_X11)
+# include "qx11info_x11.h"
+# include <private/qt_x11_p.h>
+# include <private/qpixmap_x11_p.h>
+#endif
+
+#if defined(Q_OS_SYMBIAN)
+# include <private/qt_s60_p.h>
+#endif
+
+#include "qpixmap_raster_p.h"
+#include "private/qstylehelper_p.h"
+
+QT_BEGIN_NAMESPACE
+
+// ### Qt 5: remove
+Q_GUI_EXPORT qint64 qt_pixmap_id(const QPixmap &pixmap)
+{
+ return pixmap.cacheKey();
+}
+
+static bool qt_pixmap_thread_test()
+{
+ if (!qApp) {
+ qFatal("QPixmap: Must construct a QApplication before a QPaintDevice");
+ return false;
+ }
+
+ if (qApp->thread() != QThread::currentThread()) {
+ bool fail = false;
+#if defined (Q_WS_X11)
+ if (!QApplication::testAttribute(Qt::AA_X11InitThreads))
+ fail = true;
+#elif defined (Q_WS_QPA)
+ if (!QApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedPixmaps)) {
+ printf("Lighthouse plugin does not support threaded pixmaps!\n");
+ fail = true;
+ }
+#else
+ if (QApplicationPrivate::graphics_system_name != QLatin1String("raster"))
+ fail = true;
+#endif
+ if (fail) {
+ qWarning("QPixmap: It is not safe to use pixmaps outside the GUI thread");
+ return false;
+ }
+ }
+ return true;
+}
+
+void QPixmap::init(int w, int h, Type type)
+{
+ init(w, h, int(type));
+}
+
+extern QApplication::Type qt_appType;
+
+void QPixmap::init(int w, int h, int type)
+{
+ if (qt_appType == QApplication::Tty) {
+ qWarning("QPixmap: Cannot create a QPixmap when no GUI is being used");
+ data = 0;
+ return;
+ }
+
+ if ((w > 0 && h > 0) || type == QPixmapData::BitmapType)
+ data = QPixmapData::create(w, h, (QPixmapData::PixelType) type);
+ else
+ data = 0;
+}
+
+/*!
+ \enum QPixmap::ColorMode
+
+ \compat
+
+ This enum type defines the color modes that exist for converting
+ QImage objects to QPixmap. It is provided here for compatibility
+ with earlier versions of Qt.
+
+ Use Qt::ImageConversionFlags instead.
+
+ \value Auto Select \c Color or \c Mono on a case-by-case basis.
+ \value Color Always create colored pixmaps.
+ \value Mono Always create bitmaps.
+*/
+
+/*!
+ Constructs a null pixmap.
+
+ \sa isNull()
+*/
+
+QPixmap::QPixmap()
+ : QPaintDevice()
+{
+ (void) qt_pixmap_thread_test();
+ init(0, 0, QPixmapData::PixmapType);
+}
+
+/*!
+ \fn QPixmap::QPixmap(int width, int height)
+
+ Constructs a pixmap with the given \a width and \a height. If
+ either \a width or \a height is zero, a null pixmap is
+ constructed.
+
+ \warning This will create a QPixmap with uninitialized data. Call
+ fill() to fill the pixmap with an appropriate color before drawing
+ onto it with QPainter.
+
+ \sa isNull()
+*/
+
+QPixmap::QPixmap(int w, int h)
+ : QPaintDevice()
+{
+ if (!qt_pixmap_thread_test())
+ init(0, 0, QPixmapData::PixmapType);
+ else
+ init(w, h, QPixmapData::PixmapType);
+}
+
+/*!
+ \overload
+
+ Constructs a pixmap of the given \a size.
+
+ \warning This will create a QPixmap with uninitialized data. Call
+ fill() to fill the pixmap with an appropriate color before drawing
+ onto it with QPainter.
+*/
+
+QPixmap::QPixmap(const QSize &size)
+ : QPaintDevice()
+{
+ if (!qt_pixmap_thread_test())
+ init(0, 0, QPixmapData::PixmapType);
+ else
+ init(size.width(), size.height(), QPixmapData::PixmapType);
+}
+
+/*!
+ \internal
+*/
+QPixmap::QPixmap(const QSize &s, Type type)
+{
+ if (!qt_pixmap_thread_test())
+ init(0, 0, type);
+ else
+ init(s.width(), s.height(), type);
+}
+
+/*!
+ \internal
+*/
+QPixmap::QPixmap(const QSize &s, int type)
+{
+ if (!qt_pixmap_thread_test())
+ init(0, 0, static_cast<QPixmapData::PixelType>(type));
+ else
+ init(s.width(), s.height(), static_cast<QPixmapData::PixelType>(type));
+}
+
+/*!
+ \internal
+*/
+QPixmap::QPixmap(QPixmapData *d)
+ : QPaintDevice(), data(d)
+{
+}
+
+/*!
+ Constructs a pixmap from the file with the given \a fileName. If the
+ file does not exist or is of an unknown format, the pixmap becomes a
+ null pixmap.
+
+ The loader attempts to read the pixmap using the specified \a
+ format. If the \a format is not specified (which is the default),
+ the loader probes the file for a header to guess the file format.
+
+ The file name can either refer to an actual file on disk or to
+ one of the application's embedded resources. See the
+ \l{resources.html}{Resource System} overview for details on how
+ to embed images and other resource files in the application's
+ executable.
+
+ If the image needs to be modified to fit in a lower-resolution
+ result (e.g. converting from 32-bit to 8-bit), use the \a
+ flags to control the conversion.
+
+ The \a fileName, \a format and \a flags parameters are
+ passed on to load(). This means that the data in \a fileName is
+ not compiled into the binary. If \a fileName contains a relative
+ path (e.g. the filename only) the relevant file must be found
+ relative to the runtime working directory.
+
+ \sa {QPixmap#Reading and Writing Image Files}{Reading and Writing
+ Image Files}
+*/
+
+QPixmap::QPixmap(const QString& fileName, const char *format, Qt::ImageConversionFlags flags)
+ : QPaintDevice()
+{
+ init(0, 0, QPixmapData::PixmapType);
+ if (!qt_pixmap_thread_test())
+ return;
+
+ load(fileName, format, flags);
+}
+
+/*!
+ Constructs a pixmap that is a copy of the given \a pixmap.
+
+ \sa copy()
+*/
+
+QPixmap::QPixmap(const QPixmap &pixmap)
+ : QPaintDevice()
+{
+ if (!qt_pixmap_thread_test()) {
+ init(0, 0, QPixmapData::PixmapType);
+ return;
+ }
+ if (pixmap.paintingActive()) { // make a deep copy
+ operator=(pixmap.copy());
+ } else {
+ data = pixmap.data;
+ }
+}
+
+/*!
+ Constructs a pixmap from the given \a xpm data, which must be a
+ valid XPM image.
+
+ Errors are silently ignored.
+
+ Note that it's possible to squeeze the XPM variable a little bit
+ by using an unusual declaration:
+
+ \snippet doc/src/snippets/code/src_gui_image_qpixmap.cpp 0
+
+ The extra \c const makes the entire definition read-only, which is
+ slightly more efficient (for example, when the code is in a shared
+ library) and ROMable when the application is to be stored in ROM.
+*/
+#ifndef QT_NO_IMAGEFORMAT_XPM
+QPixmap::QPixmap(const char * const xpm[])
+ : QPaintDevice()
+{
+ init(0, 0, QPixmapData::PixmapType);
+ if (!xpm)
+ return;
+
+ QImage image(xpm);
+ if (!image.isNull()) {
+ if (data && data->pixelType() == QPixmapData::BitmapType)
+ *this = QBitmap::fromImage(image);
+ else
+ *this = fromImage(image);
+ }
+}
+#endif
+
+
+/*!
+ Destroys the pixmap.
+*/
+
+QPixmap::~QPixmap()
+{
+ Q_ASSERT(!data || data->ref >= 1); // Catch if ref-counting changes again
+}
+
+/*!
+ \internal
+*/
+int QPixmap::devType() const
+{
+ return QInternal::Pixmap;
+}
+
+/*!
+ \fn QPixmap QPixmap::copy(int x, int y, int width, int height) const
+ \overload
+
+ Returns a deep copy of the subset of the pixmap that is specified
+ by the rectangle QRect( \a x, \a y, \a width, \a height).
+*/
+
+/*!
+ \fn QPixmap QPixmap::copy(const QRect &rectangle) const
+
+ Returns a deep copy of the subset of the pixmap that is specified
+ by the given \a rectangle. For more information on deep copies,
+ see the \l {Implicit Data Sharing} documentation.
+
+ If the given \a rectangle is empty, the whole image is copied.
+
+ \sa operator=(), QPixmap(), {QPixmap#Pixmap
+ Transformations}{Pixmap Transformations}
+*/
+QPixmap QPixmap::copy(const QRect &rect) const
+{
+ if (isNull())
+ return QPixmap();
+
+ QRect r(0, 0, width(), height());
+ if (!rect.isEmpty())
+ r = r.intersected(rect);
+
+ QPixmapData *d = data->createCompatiblePixmapData();
+ d->copy(data.data(), r);
+ return QPixmap(d);
+}
+
+/*!
+ \fn QPixmap::scroll(int dx, int dy, int x, int y, int width, int height, QRegion *exposed)
+ \since 4.6
+
+ This convenience function is equivalent to calling QPixmap::scroll(\a dx,
+ \a dy, QRect(\a x, \a y, \a width, \a height), \a exposed).
+
+ \sa QWidget::scroll(), QGraphicsItem::scroll()
+*/
+
+/*!
+ \since 4.6
+
+ Scrolls the area \a rect of this pixmap by (\a dx, \a dy). The exposed
+ region is left unchanged. You can optionally pass a pointer to an empty
+ QRegion to get the region that is \a exposed by the scroll operation.
+
+ \snippet doc/src/snippets/code/src_gui_image_qpixmap.cpp 2
+
+ You cannot scroll while there is an active painter on the pixmap.
+
+ \sa QWidget::scroll(), QGraphicsItem::scroll()
+*/
+void QPixmap::scroll(int dx, int dy, const QRect &rect, QRegion *exposed)
+{
+ if (isNull() || (dx == 0 && dy == 0))
+ return;
+ QRect dest = rect & this->rect();
+ QRect src = dest.translated(-dx, -dy) & dest;
+ if (src.isEmpty()) {
+ if (exposed)
+ *exposed += dest;
+ return;
+ }
+
+ detach();
+
+ if (!data->scroll(dx, dy, src)) {
+ // Fallback
+ QPixmap pix = *this;
+ QPainter painter(&pix);
+ painter.setCompositionMode(QPainter::CompositionMode_Source);
+ painter.drawPixmap(src.translated(dx, dy), *this, src);
+ painter.end();
+ *this = pix;
+ }
+
+ if (exposed) {
+ *exposed += dest;
+ *exposed -= src.translated(dx, dy);
+ }
+}
+
+/*!
+ Assigns the given \a pixmap to this pixmap and returns a reference
+ to this pixmap.
+
+ \sa copy(), QPixmap()
+*/
+
+QPixmap &QPixmap::operator=(const QPixmap &pixmap)
+{
+ if (paintingActive()) {
+ qWarning("QPixmap::operator=: Cannot assign to pixmap during painting");
+ return *this;
+ }
+ if (pixmap.paintingActive()) { // make a deep copy
+ *this = pixmap.copy();
+ } else {
+ data = pixmap.data;
+ }
+ return *this;
+}
+
+/*!
+ \fn void QPixmap::swap(QPixmap &other)
+ \since 4.8
+
+ Swaps pixmap \a other with this pixmap. This operation is very
+ fast and never fails.
+*/
+
+/*!
+ Returns the pixmap as a QVariant.
+*/
+QPixmap::operator QVariant() const
+{
+ return QVariant(QVariant::Pixmap, this);
+}
+
+/*!
+ \fn bool QPixmap::operator!() const
+
+ Returns true if this is a null pixmap; otherwise returns false.
+
+ \sa isNull()
+*/
+
+/*!
+ \fn QPixmap::operator QImage() const
+
+ Returns the pixmap as a QImage.
+
+ Use the toImage() function instead.
+*/
+
+/*!
+ Converts the pixmap to a QImage. Returns a null image if the
+ conversion fails.
+
+ If the pixmap has 1-bit depth, the returned image will also be 1
+ bit deep. Images with more bits will be returned in a format
+ closely represents the underlying system. Usually this will be
+ QImage::Format_ARGB32_Premultiplied for pixmaps with an alpha and
+ QImage::Format_RGB32 or QImage::Format_RGB16 for pixmaps without
+ alpha.
+
+ Note that for the moment, alpha masks on monochrome images are
+ ignored.
+
+ \sa fromImage(), {QImage#Image Formats}{Image Formats}
+*/
+QImage QPixmap::toImage() const
+{
+ if (isNull())
+ return QImage();
+
+ return data->toImage();
+}
+
+/*!
+ \fn QMatrix QPixmap::trueMatrix(const QTransform &matrix, int width, int height)
+
+ Returns the actual matrix used for transforming a pixmap with the
+ given \a width, \a height and \a matrix.
+
+ When transforming a pixmap using the transformed() function, the
+ transformation matrix is internally adjusted to compensate for
+ unwanted translation, i.e. transformed() returns the smallest
+ pixmap containing all transformed points of the original
+ pixmap. This function returns the modified matrix, which maps
+ points correctly from the original pixmap into the new pixmap.
+
+ \sa transformed(), {QPixmap#Pixmap Transformations}{Pixmap
+ Transformations}
+*/
+QTransform QPixmap::trueMatrix(const QTransform &m, int w, int h)
+{
+ return QImage::trueMatrix(m, w, h);
+}
+
+/*!
+ \overload
+
+ This convenience function loads the matrix \a m into a
+ QTransform and calls the overloaded function with the
+ QTransform and the width \a w and the height \a h.
+ */
+QMatrix QPixmap::trueMatrix(const QMatrix &m, int w, int h)
+{
+ return trueMatrix(QTransform(m), w, h).toAffine();
+}
+
+
+/*!
+ \fn bool QPixmap::isQBitmap() const
+
+ Returns true if this is a QBitmap; otherwise returns false.
+*/
+
+bool QPixmap::isQBitmap() const
+{
+ return data->type == QPixmapData::BitmapType;
+}
+
+/*!
+ \fn bool QPixmap::isNull() const
+
+ Returns true if this is a null pixmap; otherwise returns false.
+
+ A null pixmap has zero width, zero height and no contents. You
+ cannot draw in a null pixmap.
+*/
+bool QPixmap::isNull() const
+{
+ return !data || data->isNull();
+}
+
+/*!
+ \fn int QPixmap::width() const
+
+ Returns the width of the pixmap.
+
+ \sa size(), {QPixmap#Pixmap Information}{Pixmap Information}
+*/
+int QPixmap::width() const
+{
+ return data ? data->width() : 0;
+}
+
+/*!
+ \fn int QPixmap::height() const
+
+ Returns the height of the pixmap.
+
+ \sa size(), {QPixmap#Pixmap Information}{Pixmap Information}
+*/
+int QPixmap::height() const
+{
+ return data ? data->height() : 0;
+}
+
+/*!
+ \fn QSize QPixmap::size() const
+
+ Returns the size of the pixmap.
+
+ \sa width(), height(), {QPixmap#Pixmap Information}{Pixmap
+ Information}
+*/
+QSize QPixmap::size() const
+{
+ return data ? QSize(data->width(), data->height()) : QSize(0, 0);
+}
+
+/*!
+ \fn QRect QPixmap::rect() const
+
+ Returns the pixmap's enclosing rectangle.
+
+ \sa {QPixmap#Pixmap Information}{Pixmap Information}
+*/
+QRect QPixmap::rect() const
+{
+ return data ? QRect(0, 0, data->width(), data->height()) : QRect();
+}
+
+/*!
+ \fn int QPixmap::depth() const
+
+ Returns the depth of the pixmap.
+
+ The pixmap depth is also called bits per pixel (bpp) or bit planes
+ of a pixmap. A null pixmap has depth 0.
+
+ \sa defaultDepth(), {QPixmap#Pixmap Information}{Pixmap
+ Information}
+*/
+int QPixmap::depth() const
+{
+ return data ? data->depth() : 0;
+}
+
+/*!
+ \fn void QPixmap::resize(const QSize &size)
+ \overload
+ \compat
+
+ Use QPixmap::copy() instead to get the pixmap with the new size.
+
+ \oldcode
+ pixmap.resize(size);
+ \newcode
+ pixmap = pixmap.copy(QRect(QPoint(0, 0), size));
+ \endcode
+*/
+#ifdef QT3_SUPPORT
+void QPixmap::resize_helper(const QSize &s)
+{
+ int w = s.width();
+ int h = s.height();
+ if (w < 1 || h < 1) {
+ *this = QPixmap();
+ return;
+ }
+
+ if (size() == s)
+ return;
+
+ // QPixmap.data member may be QRuntimePixmapData so use pixmapData() function to get
+ // the actual underlaying runtime pixmap data.
+ QPixmapData *pd = pixmapData();
+
+ // Create new pixmap
+ QPixmap pm(QSize(w, h), pd ? pd->type : QPixmapData::PixmapType);
+ bool uninit = false;
+#if defined(Q_WS_X11)
+ QX11PixmapData *x11Data = pd && pd->classId() == QPixmapData::X11Class ? static_cast<QX11PixmapData*>(pd) : 0;
+ if (x11Data) {
+ pm.x11SetScreen(x11Data->xinfo.screen());
+ uninit = x11Data->flags & QX11PixmapData::Uninitialized;
+ }
+#elif defined(Q_WS_MAC)
+ QMacPixmapData *macData = pd && pd->classId() == QPixmapData::MacClass ? static_cast<QMacPixmapData*>(pd) : 0;
+ if (macData)
+ uninit = macData->uninit;
+#endif
+ if (!uninit && !isNull()) {
+ // Copy old pixmap
+ if (hasAlphaChannel())
+ pm.fill(Qt::transparent);
+ QPainter p(&pm);
+ p.drawPixmap(0, 0, *this, 0, 0, qMin(width(), w), qMin(height(), h));
+ }
+
+#if defined(Q_WS_X11)
+ if (x11Data && x11Data->x11_mask) {
+ QPixmapData *newPd = pm.pixmapData();
+ QX11PixmapData *pmData = (newPd && newPd->classId() == QPixmapData::X11Class)
+ ? static_cast<QX11PixmapData*>(newPd) : 0;
+ if (pmData) {
+ pmData->x11_mask = (Qt::HANDLE)XCreatePixmap(X11->display,
+ RootWindow(x11Data->xinfo.display(),
+ x11Data->xinfo.screen()),
+ w, h, 1);
+ GC gc = XCreateGC(X11->display, pmData->x11_mask, 0, 0);
+ XCopyArea(X11->display, x11Data->x11_mask, pmData->x11_mask, gc, 0, 0,
+ qMin(width(), w), qMin(height(), h), 0, 0);
+ XFreeGC(X11->display, gc);
+ }
+ }
+#endif
+ *this = pm;
+}
+#endif
+
+/*!
+ \fn void QPixmap::resize(int width, int height)
+ \compat
+
+ Use QPixmap::copy() instead to get the pixmap with the new size.
+
+ \oldcode
+ pixmap.resize(10, 20);
+ \newcode
+ pixmap = pixmap.copy(0, 0, 10, 20);
+ \endcode
+*/
+
+/*!
+ \fn bool QPixmap::selfMask() const
+ \compat
+
+ Returns whether the pixmap is its own mask or not.
+
+ This function is no longer relevant since the concept of self
+ masking doesn't exists anymore.
+*/
+
+/*!
+ Sets a mask bitmap.
+
+ This function merges the \a mask with the pixmap's alpha channel. A pixel
+ value of 1 on the mask means the pixmap's pixel is unchanged; a value of 0
+ means the pixel is transparent. The mask must have the same size as this
+ pixmap.
+
+ Setting a null mask resets the mask, leaving the previously transparent
+ pixels black. The effect of this function is undefined when the pixmap is
+ being painted on.
+
+ \warning This is potentially an expensive operation.
+
+ \sa mask(), {QPixmap#Pixmap Transformations}{Pixmap Transformations},
+ QBitmap
+*/
+void QPixmap::setMask(const QBitmap &mask)
+{
+ if (paintingActive()) {
+ qWarning("QPixmap::setMask: Cannot set mask while pixmap is being painted on");
+ return;
+ }
+
+ if (!mask.isNull() && mask.size() != size()) {
+ qWarning("QPixmap::setMask() mask size differs from pixmap size");
+ return;
+ }
+
+ if (isNull())
+ return;
+
+ if (static_cast<const QPixmap &>(mask).data == data) // trying to selfmask
+ return;
+
+ detach();
+ data->setMask(mask);
+}
+
+#ifndef QT_NO_IMAGE_HEURISTIC_MASK
+/*!
+ Creates and returns a heuristic mask for this pixmap.
+
+ The function works by selecting a color from one of the corners
+ and then chipping away pixels of that color, starting at all the
+ edges. If \a clipTight is true (the default) the mask is just
+ large enough to cover the pixels; otherwise, the mask is larger
+ than the data pixels.
+
+ The mask may not be perfect but it should be reasonable, so you
+ can do things such as the following:
+
+ \snippet doc/src/snippets/code/src_gui_image_qpixmap.cpp 1
+
+ This function is slow because it involves converting to/from a
+ QImage, and non-trivial computations.
+
+ \sa QImage::createHeuristicMask(), createMaskFromColor()
+*/
+QBitmap QPixmap::createHeuristicMask(bool clipTight) const
+{
+ QBitmap m = QBitmap::fromImage(toImage().createHeuristicMask(clipTight));
+ return m;
+}
+#endif
+
+/*!
+ Creates and returns a mask for this pixmap based on the given \a
+ maskColor. If the \a mode is Qt::MaskInColor, all pixels matching the
+ maskColor will be transparent. If \a mode is Qt::MaskOutColor, all pixels
+ matching the maskColor will be opaque.
+
+ This function is slow because it involves converting to/from a
+ QImage.
+
+ \sa createHeuristicMask(), QImage::createMaskFromColor()
+*/
+QBitmap QPixmap::createMaskFromColor(const QColor &maskColor, Qt::MaskMode mode) const
+{
+ QImage image = toImage().convertToFormat(QImage::Format_ARGB32);
+ return QBitmap::fromImage(image.createMaskFromColor(maskColor.rgba(), mode));
+}
+
+/*! \overload
+
+ Creates and returns a mask for this pixmap based on the given \a
+ maskColor. Same as calling createMaskFromColor(maskColor,
+ Qt::MaskInColor)
+
+ \sa createHeuristicMask(), QImage::createMaskFromColor()
+*/
+QBitmap QPixmap::createMaskFromColor(const QColor &maskColor) const
+{
+ return createMaskFromColor(maskColor, Qt::MaskInColor);
+}
+
+/*!
+ Loads a pixmap from the file with the given \a fileName. Returns
+ true if the pixmap was successfully loaded; otherwise returns
+ false.
+
+ The loader attempts to read the pixmap using the specified \a
+ format. If the \a format is not specified (which is the default),
+ the loader probes the file for a header to guess the file format.
+
+ The file name can either refer to an actual file on disk or to one
+ of the application's embedded resources. See the
+ \l{resources.html}{Resource System} overview for details on how to
+ embed pixmaps and other resource files in the application's
+ executable.
+
+ If the data needs to be modified to fit in a lower-resolution
+ result (e.g. converting from 32-bit to 8-bit), use the \a flags to
+ control the conversion.
+
+ Note that QPixmaps are automatically added to the QPixmapCache
+ when loaded from a file; the key used is internal and can not
+ be acquired.
+
+ \sa loadFromData(), {QPixmap#Reading and Writing Image
+ Files}{Reading and Writing Image Files}
+*/
+
+bool QPixmap::load(const QString &fileName, const char *format, Qt::ImageConversionFlags flags)
+{
+ if (fileName.isEmpty())
+ return false;
+
+ QFileInfo info(fileName);
+ QString key = QLatin1Literal("qt_pixmap")
+ % info.absoluteFilePath()
+ % HexString<uint>(info.lastModified().toTime_t())
+ % HexString<quint64>(info.size())
+ % HexString<uint>(data ? data->pixelType() : QPixmapData::PixmapType);
+
+ // Note: If no extension is provided, we try to match the
+ // file against known plugin extensions
+ if (!info.completeSuffix().isEmpty() && !info.exists())
+ return false;
+
+ if (QPixmapCache::find(key, *this))
+ return true;
+
+ QScopedPointer<QPixmapData> tmp(QPixmapData::create(0, 0, data ? data->type : QPixmapData::PixmapType));
+ if (tmp->fromFile(fileName, format, flags)) {
+ data = tmp.take();
+ QPixmapCache::insert(key, *this);
+ return true;
+ }
+
+ return false;
+}
+
+/*!
+ \fn bool QPixmap::loadFromData(const uchar *data, uint len, const char *format, Qt::ImageConversionFlags flags)
+
+ Loads a pixmap from the \a len first bytes of the given binary \a
+ data. Returns true if the pixmap was loaded successfully;
+ otherwise returns false.
+
+ The loader attempts to read the pixmap using the specified \a
+ format. If the \a format is not specified (which is the default),
+ the loader probes the file for a header to guess the file format.
+
+ If the data needs to be modified to fit in a lower-resolution
+ result (e.g. converting from 32-bit to 8-bit), use the \a flags to
+ control the conversion.
+
+ \sa load(), {QPixmap#Reading and Writing Image Files}{Reading and
+ Writing Image Files}
+*/
+
+bool QPixmap::loadFromData(const uchar *buf, uint len, const char *format, Qt::ImageConversionFlags flags)
+{
+ if (len == 0 || buf == 0)
+ return false;
+
+ if (!data)
+ data = QPixmapData::create(0, 0, QPixmapData::PixmapType);
+
+ return data->fromData(buf, len, format, flags);
+}
+
+/*!
+ \fn bool QPixmap::loadFromData(const QByteArray &data, const char *format, Qt::ImageConversionFlags flags)
+
+ \overload
+
+ Loads a pixmap from the binary \a data using the specified \a
+ format and conversion \a flags.
+*/
+
+
+/*!
+ Saves the pixmap to the file with the given \a fileName using the
+ specified image file \a format and \a quality factor. Returns true
+ if successful; otherwise returns false.
+
+ The \a quality factor must be in the range [0,100] or -1. Specify
+ 0 to obtain small compressed files, 100 for large uncompressed
+ files, and -1 to use the default settings.
+
+ If \a format is 0, an image format will be chosen from \a fileName's
+ suffix.
+
+ \sa {QPixmap#Reading and Writing Image Files}{Reading and Writing
+ Image Files}
+*/
+
+bool QPixmap::save(const QString &fileName, const char *format, int quality) const
+{
+ if (isNull())
+ return false; // nothing to save
+ QImageWriter writer(fileName, format);
+ return doImageIO(&writer, quality);
+}
+
+/*!
+ \overload
+
+ This function writes a QPixmap to the given \a device using the
+ specified image file \a format and \a quality factor. This can be
+ used, for example, to save a pixmap directly into a QByteArray:
+
+ \snippet doc/src/snippets/image/image.cpp 1
+*/
+
+bool QPixmap::save(QIODevice* device, const char* format, int quality) const
+{
+ if (isNull())
+ return false; // nothing to save
+ QImageWriter writer(device, format);
+ return doImageIO(&writer, quality);
+}
+
+/*! \internal
+*/
+bool QPixmap::doImageIO(QImageWriter *writer, int quality) const
+{
+ if (quality > 100 || quality < -1)
+ qWarning("QPixmap::save: quality out of range [-1,100]");
+ if (quality >= 0)
+ writer->setQuality(qMin(quality,100));
+ return writer->write(toImage());
+}
+
+
+// The implementation (and documentation) of
+// QPixmap::fill(const QWidget *, const QPoint &)
+// is in qwidget.cpp
+
+/*!
+ \fn void QPixmap::fill(const QWidget *widget, int x, int y)
+ \overload
+
+ Fills the pixmap with the \a widget's background color or pixmap.
+ The given point, (\a x, \a y), defines an offset in widget
+ coordinates to which the pixmap's top-left pixel will be mapped
+ to.
+*/
+
+/*!
+ Fills the pixmap with the given \a color.
+
+ The effect of this function is undefined when the pixmap is
+ being painted on.
+
+ \sa {QPixmap#Pixmap Transformations}{Pixmap Transformations}
+*/
+
+void QPixmap::fill(const QColor &color)
+{
+ if (isNull())
+ return;
+
+ // Some people are probably already calling fill while a painter is active, so to not break
+ // their programs, only print a warning and return when the fill operation could cause a crash.
+ if (paintingActive() && (color.alpha() != 255) && !hasAlphaChannel()) {
+ qWarning("QPixmap::fill: Cannot fill while pixmap is being painted on");
+ return;
+ }
+
+ if (data->ref == 1) {
+ // detach() will also remove this pixmap from caches, so
+ // it has to be called even when ref == 1.
+ detach();
+ } else {
+ // Don't bother to make a copy of the data object, since
+ // it will be filled with new pixel data anyway.
+ QPixmapData *d = data->createCompatiblePixmapData();
+ d->resize(data->width(), data->height());
+ data = d;
+ }
+ data->fill(color);
+}
+
+/*! \obsolete
+ Returns a number that identifies the contents of this QPixmap
+ object. Distinct QPixmap objects can only have the same serial
+ number if they refer to the same contents (but they don't have
+ to).
+
+ Use cacheKey() instead.
+
+ \warning The serial number doesn't necessarily change when
+ the pixmap is altered. This means that it may be dangerous to use
+ it as a cache key. For caching pixmaps, we recommend using the
+ QPixmapCache class whenever possible.
+*/
+int QPixmap::serialNumber() const
+{
+ if (isNull())
+ return 0;
+ return data->serialNumber();
+}
+
+/*!
+ Returns a number that identifies this QPixmap. Distinct QPixmap
+ objects can only have the same cache key if they refer to the same
+ contents.
+
+ The cacheKey() will change when the pixmap is altered.
+*/
+qint64 QPixmap::cacheKey() const
+{
+ if (isNull())
+ return 0;
+
+ Q_ASSERT(data);
+ return data->cacheKey();
+}
+
+static void sendResizeEvents(QWidget *target)
+{
+ QResizeEvent e(target->size(), QSize());
+ QApplication::sendEvent(target, &e);
+
+ const QObjectList children = target->children();
+ for (int i = 0; i < children.size(); ++i) {
+ QWidget *child = static_cast<QWidget*>(children.at(i));
+ if (child->isWidgetType() && !child->isWindow() && child->testAttribute(Qt::WA_PendingResizeEvent))
+ sendResizeEvents(child);
+ }
+}
+
+/*!
+ \fn QPixmap QPixmap::grabWidget(QWidget * widget, const QRect &rectangle)
+
+ Creates a pixmap and paints the given \a widget, restricted by the
+ given \a rectangle, in it. If the \a widget has any children, then
+ they are also painted in the appropriate positions.
+
+ If no rectangle is specified (the default) the entire widget is
+ painted.
+
+ If \a widget is 0, the specified rectangle doesn't overlap the
+ widget's rectangle, or an error occurs, the function will return a
+ null QPixmap. If the rectangle is a superset of the given \a
+ widget, the areas outside the \a widget are covered with the
+ widget's background.
+
+ This function actually asks \a widget to paint itself (and its
+ children to paint themselves) by calling paintEvent() with painter
+ redirection turned on. But QPixmap also provides the grabWindow()
+ function which is a bit faster by grabbing pixels directly off the
+ screen. In addition, if there are overlaying windows,
+ grabWindow(), unlike grabWidget(), will see them.
+
+ \warning Do not grab a widget from its QWidget::paintEvent().
+ However, it is safe to grab a widget from another widget's
+ \l {QWidget::}{paintEvent()}.
+
+ \sa grabWindow()
+*/
+
+QPixmap QPixmap::grabWidget(QWidget * widget, const QRect &rect)
+{
+ if (!widget)
+ return QPixmap();
+
+ if (widget->testAttribute(Qt::WA_PendingResizeEvent) || !widget->testAttribute(Qt::WA_WState_Created))
+ sendResizeEvents(widget);
+
+ widget->d_func()->prepareToRender(QRegion(),
+ QWidget::DrawWindowBackground | QWidget::DrawChildren | QWidget::IgnoreMask);
+
+ QRect r(rect);
+ if (r.width() < 0)
+ r.setWidth(widget->width() - rect.x());
+ if (r.height() < 0)
+ r.setHeight(widget->height() - rect.y());
+
+ if (!r.intersects(widget->rect()))
+ return QPixmap();
+
+ QPixmap res(r.size());
+ if (!qt_widget_private(widget)->isOpaque)
+ res.fill(Qt::transparent);
+
+ widget->d_func()->render(&res, QPoint(), r, QWidget::DrawWindowBackground
+ | QWidget::DrawChildren | QWidget::IgnoreMask, true);
+ return res;
+}
+
+/*!
+ \fn QPixmap QPixmap::grabWidget(QWidget *widget, int x, int y, int
+ width, int height)
+
+ \overload
+
+ Creates a pixmap and paints the given \a widget, restricted by
+ QRect(\a x, \a y, \a width, \a height), in it.
+
+ \warning Do not grab a widget from its QWidget::paintEvent().
+ However, it is safe to grab a widget from another widget's
+ \l {QWidget::}{paintEvent()}.
+*/
+
+
+/*!
+ \since 4.5
+
+ \enum QPixmap::ShareMode
+
+ This enum type defines the share modes that are available when
+ creating a QPixmap object from a raw X11 Pixmap handle.
+
+ \value ImplicitlyShared This mode will cause the QPixmap object to
+ create a copy of the internal data before it is modified, thus
+ keeping the original X11 pixmap intact.
+
+ \value ExplicitlyShared In this mode, the pixmap data will \e not be
+ copied before it is modified, which in effect will change the
+ original X11 pixmap.
+
+ \warning This enum is only used for X11 specific functions; using
+ it is non-portable.
+
+ \sa QPixmap::fromX11Pixmap()
+*/
+
+/*!
+ \since 4.5
+
+ \fn QPixmap QPixmap::fromX11Pixmap(Qt::HANDLE pixmap, QPixmap::ShareMode mode)
+
+ Creates a QPixmap from the native X11 Pixmap handle \a pixmap,
+ using \a mode as the share mode. The default share mode is
+ QPixmap::ImplicitlyShared, which means that a copy of the pixmap is
+ made if someone tries to modify it by e.g. drawing onto it.
+
+ QPixmap does \e not take ownership of the \a pixmap handle, and
+ have to be deleted by the user.
+
+ \warning This function is X11 specific; using it is non-portable.
+
+ \sa QPixmap::ShareMode
+*/
+
+
+#if defined(Q_WS_X11) || defined(Q_WS_QWS)
+
+/*!
+ Returns the pixmap's handle to the device context.
+
+ Note that, since QPixmap make use of \l {Implicit Data
+ Sharing}{implicit data sharing}, the detach() function must be
+ called explicitly to ensure that only \e this pixmap's data is
+ modified if the pixmap data is shared.
+
+ \warning This function is X11 specific; using it is non-portable.
+
+ \warning Since 4.8, pixmaps do not have an X11 handle unless
+ created with \l {QPixmap::}{fromX11Pixmap()}, or if the native
+ graphics system is explicitly enabled.
+
+ \sa detach()
+ \sa QApplication::setGraphicsSystem()
+*/
+
+Qt::HANDLE QPixmap::handle() const
+{
+#if defined(Q_WS_X11)
+ const QPixmapData *pd = pixmapData();
+ if (pd) {
+ if (pd->classId() == QPixmapData::X11Class)
+ return static_cast<const QX11PixmapData*>(pd)->handle();
+ else
+ qWarning("QPixmap::handle(): Pixmap is not an X11 class pixmap");
+ }
+#endif
+ return 0;
+}
+#endif
+
+
+#ifdef QT3_SUPPORT
+static Qt::ImageConversionFlags colorModeToFlags(QPixmap::ColorMode mode)
+{
+ Qt::ImageConversionFlags flags = Qt::AutoColor;
+ switch (mode) {
+ case QPixmap::Color:
+ flags |= Qt::ColorOnly;
+ break;
+ case QPixmap::Mono:
+ flags |= Qt::MonoOnly;
+ break;
+ default:
+ break;// Nothing.
+ }
+ return flags;
+}
+
+/*!
+ Use the constructor that takes a Qt::ImageConversionFlag instead.
+*/
+
+QPixmap::QPixmap(const QString& fileName, const char *format, ColorMode mode)
+ : QPaintDevice()
+{
+ init(0, 0, QPixmapData::PixmapType);
+ if (!qt_pixmap_thread_test())
+ return;
+
+ load(fileName, format, colorModeToFlags(mode));
+}
+
+/*!
+ Constructs a pixmap from the QImage \a image.
+
+ Use the static fromImage() function instead.
+*/
+QPixmap::QPixmap(const QImage& image)
+ : QPaintDevice()
+{
+ init(0, 0, QPixmapData::PixmapType);
+ if (!qt_pixmap_thread_test())
+ return;
+
+ if (data && data->pixelType() == QPixmapData::BitmapType)
+ *this = QBitmap::fromImage(image);
+ else
+ *this = fromImage(image);
+}
+
+/*!
+ \overload
+
+ Converts the given \a image to a pixmap that is assigned to this
+ pixmap.
+
+ Use the static fromImage() function instead.
+*/
+
+QPixmap &QPixmap::operator=(const QImage &image)
+{
+ if (data && data->pixelType() == QPixmapData::BitmapType)
+ *this = QBitmap::fromImage(image);
+ else
+ *this = fromImage(image);
+ return *this;
+}
+
+/*!
+ Use the load() function that takes a Qt::ImageConversionFlag instead.
+*/
+
+bool QPixmap::load(const QString &fileName, const char *format, ColorMode mode)
+{
+ return load(fileName, format, colorModeToFlags(mode));
+}
+
+/*!
+ Use the loadFromData() function that takes a Qt::ImageConversionFlag instead.
+*/
+
+bool QPixmap::loadFromData(const uchar *buf, uint len, const char *format, ColorMode mode)
+{
+ return loadFromData(buf, len, format, colorModeToFlags(mode));
+}
+
+/*!
+ Use the static fromImage() function instead.
+*/
+bool QPixmap::convertFromImage(const QImage &image, ColorMode mode)
+{
+ if (data && data->pixelType() == QPixmapData::BitmapType)
+ *this = QBitmap::fromImage(image, colorModeToFlags(mode));
+ else
+ *this = fromImage(image, colorModeToFlags(mode));
+ return !isNull();
+}
+
+#endif
+
+/*****************************************************************************
+ QPixmap stream functions
+ *****************************************************************************/
+#if !defined(QT_NO_DATASTREAM)
+/*!
+ \relates QPixmap
+
+ Writes the given \a pixmap to the given \a stream as a PNG
+ image. Note that writing the stream to a file will not produce a
+ valid image file.
+
+ \sa QPixmap::save(), {Serializing Qt Data Types}
+*/
+
+QDataStream &operator<<(QDataStream &stream, const QPixmap &pixmap)
+{
+ return stream << pixmap.toImage();
+}
+
+/*!
+ \relates QPixmap
+
+ Reads an image from the given \a stream into the given \a pixmap.
+
+ \sa QPixmap::load(), {Serializing Qt Data Types}
+*/
+
+QDataStream &operator>>(QDataStream &stream, QPixmap &pixmap)
+{
+ QImage image;
+ stream >> image;
+
+ if (image.isNull()) {
+ pixmap = QPixmap();
+ } else if (image.depth() == 1) {
+ pixmap = QBitmap::fromImage(image);
+ } else {
+ pixmap = QPixmap::fromImage(image);
+ }
+ return stream;
+}
+
+#endif // QT_NO_DATASTREAM
+
+#ifdef QT3_SUPPORT
+Q_GUI_EXPORT void copyBlt(QPixmap *dst, int dx, int dy,
+ const QPixmap *src, int sx, int sy, int sw, int sh)
+{
+ Q_ASSERT_X(dst, "::copyBlt", "Destination pixmap must be non-null");
+ Q_ASSERT_X(src, "::copyBlt", "Source pixmap must be non-null");
+
+ if (src->hasAlphaChannel()) {
+ if (dst->paintEngine()->hasFeature(QPaintEngine::PorterDuff)) {
+ QPainter p(dst);
+ p.setCompositionMode(QPainter::CompositionMode_Source);
+ p.drawPixmap(dx, dy, *src, sx, sy, sw, sh);
+ } else {
+ QImage image = dst->toImage().convertToFormat(QImage::Format_ARGB32_Premultiplied);
+ QPainter p(&image);
+ p.setCompositionMode(QPainter::CompositionMode_Source);
+ p.drawPixmap(dx, dy, *src, sx, sy, sw, sh);
+ p.end();
+ *dst = QPixmap::fromImage(image);
+ }
+ } else {
+ QPainter p(dst);
+ p.drawPixmap(dx, dy, *src, sx, sy, sw, sh);
+ }
+
+}
+#endif
+
+/*!
+ \internal
+*/
+
+bool QPixmap::isDetached() const
+{
+ return data && data->ref == 1;
+}
+
+/*! \internal
+ ### Qt5 - remove me.
+*/
+void QPixmap::deref()
+{
+ Q_ASSERT_X(false, "QPixmap::deref()", "Do not call this function anymore!");
+}
+
+/*!
+ \fn QImage QPixmap::convertToImage() const
+
+ Use the toImage() function instead.
+*/
+
+/*!
+ Replaces this pixmap's data with the given \a image using the
+ specified \a flags to control the conversion. The \a flags
+ argument is a bitwise-OR of the \l{Qt::ImageConversionFlags}.
+ Passing 0 for \a flags sets all the default options. Returns true
+ if the result is that this pixmap is not null.
+
+ Note: this function was part of Qt 3 support in Qt 4.6 and earlier.
+ It has been promoted to official API status in 4.7 to support updating
+ the pixmap's image without creating a new QPixmap as fromImage() would.
+
+ \sa fromImage()
+ \since 4.7
+*/
+bool QPixmap::convertFromImage(const QImage &image, Qt::ImageConversionFlags flags)
+{
+ if (image.isNull() || !data)
+ *this = QPixmap::fromImage(image, flags);
+ else
+ data->fromImage(image, flags);
+ return !isNull();
+}
+
+/*!
+ \fn QPixmap QPixmap::xForm(const QMatrix &matrix) const
+
+ Use transformed() instead.
+*/
+
+/*!
+ \fn QPixmap QPixmap::scaled(int width, int height,
+ Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode
+ transformMode) const
+
+ \overload
+
+ Returns a copy of the pixmap scaled to a rectangle with the given
+ \a width and \a height according to the given \a aspectRatioMode and
+ \a transformMode.
+
+ If either the \a width or the \a height is zero or negative, this
+ function returns a null pixmap.
+*/
+
+/*!
+ \fn QPixmap QPixmap::scaled(const QSize &size, Qt::AspectRatioMode
+ aspectRatioMode, Qt::TransformationMode transformMode) const
+
+ Scales the pixmap to the given \a size, using the aspect ratio and
+ transformation modes specified by \a aspectRatioMode and \a
+ transformMode.
+
+ \image qimage-scaling.png
+
+ \list
+ \i If \a aspectRatioMode is Qt::IgnoreAspectRatio, the pixmap
+ is scaled to \a size.
+ \i If \a aspectRatioMode is Qt::KeepAspectRatio, the pixmap is
+ scaled to a rectangle as large as possible inside \a size, preserving the aspect ratio.
+ \i If \a aspectRatioMode is Qt::KeepAspectRatioByExpanding,
+ the pixmap is scaled to a rectangle as small as possible
+ outside \a size, preserving the aspect ratio.
+ \endlist
+
+ If the given \a size is empty, this function returns a null
+ pixmap.
+
+
+ In some cases it can be more beneficial to draw the pixmap to a
+ painter with a scale set rather than scaling the pixmap. This is
+ the case when the painter is for instance based on OpenGL or when
+ the scale factor changes rapidly.
+
+ \sa isNull(), {QPixmap#Pixmap Transformations}{Pixmap
+ Transformations}
+
+*/
+QPixmap QPixmap::scaled(const QSize& s, Qt::AspectRatioMode aspectMode, Qt::TransformationMode mode) const
+{
+ if (isNull()) {
+ qWarning("QPixmap::scaled: Pixmap is a null pixmap");
+ return QPixmap();
+ }
+ if (s.isEmpty())
+ return QPixmap();
+
+ QSize newSize = size();
+ newSize.scale(s, aspectMode);
+ if (newSize == size())
+ return *this;
+
+ QTransform wm = QTransform::fromScale((qreal)newSize.width() / width(),
+ (qreal)newSize.height() / height());
+ QPixmap pix = transformed(wm, mode);
+ return pix;
+}
+
+/*!
+ \fn QPixmap QPixmap::scaledToWidth(int width, Qt::TransformationMode
+ mode) const
+
+ Returns a scaled copy of the image. The returned image is scaled
+ to the given \a width using the specified transformation \a mode.
+ The height of the pixmap is automatically calculated so that the
+ aspect ratio of the pixmap is preserved.
+
+ If \a width is 0 or negative, a null pixmap is returned.
+
+ \sa isNull(), {QPixmap#Pixmap Transformations}{Pixmap
+ Transformations}
+*/
+QPixmap QPixmap::scaledToWidth(int w, Qt::TransformationMode mode) const
+{
+ if (isNull()) {
+ qWarning("QPixmap::scaleWidth: Pixmap is a null pixmap");
+ return copy();
+ }
+ if (w <= 0)
+ return QPixmap();
+
+ qreal factor = (qreal) w / width();
+ QTransform wm = QTransform::fromScale(factor, factor);
+ return transformed(wm, mode);
+}
+
+/*!
+ \fn QPixmap QPixmap::scaledToHeight(int height,
+ Qt::TransformationMode mode) const
+
+ Returns a scaled copy of the image. The returned image is scaled
+ to the given \a height using the specified transformation \a mode.
+ The width of the pixmap is automatically calculated so that the
+ aspect ratio of the pixmap is preserved.
+
+ If \a height is 0 or negative, a null pixmap is returned.
+
+ \sa isNull(), {QPixmap#Pixmap Transformations}{Pixmap
+ Transformations}
+*/
+QPixmap QPixmap::scaledToHeight(int h, Qt::TransformationMode mode) const
+{
+ if (isNull()) {
+ qWarning("QPixmap::scaleHeight: Pixmap is a null pixmap");
+ return copy();
+ }
+ if (h <= 0)
+ return QPixmap();
+
+ qreal factor = (qreal) h / height();
+ QTransform wm = QTransform::fromScale(factor, factor);
+ return transformed(wm, mode);
+}
+
+/*!
+ Returns a copy of the pixmap that is transformed using the given
+ transformation \a transform and transformation \a mode. The original
+ pixmap is not changed.
+
+ The transformation \a transform is internally adjusted to compensate
+ for unwanted translation; i.e. the pixmap produced is the smallest
+ pixmap that contains all the transformed points of the original
+ pixmap. Use the trueMatrix() function to retrieve the actual
+ matrix used for transforming the pixmap.
+
+ This function is slow because it involves transformation to a
+ QImage, non-trivial computations and a transformation back to a
+ QPixmap.
+
+ \sa trueMatrix(), {QPixmap#Pixmap Transformations}{Pixmap
+ Transformations}
+*/
+QPixmap QPixmap::transformed(const QTransform &transform,
+ Qt::TransformationMode mode) const
+{
+ if (isNull() || transform.type() <= QTransform::TxTranslate)
+ return *this;
+
+ return data->transformed(transform, mode);
+}
+
+/*!
+ \overload
+
+ This convenience function loads the \a matrix into a
+ QTransform and calls the overloaded function.
+ */
+QPixmap QPixmap::transformed(const QMatrix &matrix, Qt::TransformationMode mode) const
+{
+ return transformed(QTransform(matrix), mode);
+}
+
+
+
+
+
+
+
+
+/*!
+ \class QPixmap
+
+ \brief The QPixmap class is an off-screen image representation
+ that can be used as a paint device.
+
+ \ingroup painting
+ \ingroup shared
+
+
+ Qt provides four classes for handling image data: QImage, QPixmap,
+ QBitmap and QPicture. QImage is designed and optimized for I/O,
+ and for direct pixel access and manipulation, while QPixmap is
+ designed and optimized for showing images on screen. QBitmap is
+ only a convenience class that inherits QPixmap, ensuring a depth
+ of 1. The isQBitmap() function returns true if a QPixmap object is
+ really a bitmap, otherwise returns false. Finally, the QPicture class
+ is a paint device that records and replays QPainter commands.
+
+ A QPixmap can easily be displayed on the screen using QLabel or
+ one of QAbstractButton's subclasses (such as QPushButton and
+ QToolButton). QLabel has a pixmap property, whereas
+ QAbstractButton has an icon property.
+
+ In addition to the ordinary constructors, a QPixmap can be
+ constructed using the static grabWidget() and grabWindow()
+ functions which creates a QPixmap and paints the given widget, or
+ window, into it.
+
+ QPixmap objects can be passed around by value since the QPixmap
+ class uses implicit data sharing. For more information, see the \l
+ {Implicit Data Sharing} documentation. QPixmap objects can also be
+ streamed.
+
+ Note that the pixel data in a pixmap is internal and is managed by
+ the underlying window system. Because QPixmap is a QPaintDevice
+ subclass, QPainter can be used to draw directly onto pixmaps.
+ Pixels can only be accessed through QPainter functions or by
+ converting the QPixmap to a QImage. However, the fill() function
+ is available for initializing the entire pixmap with a given color.
+
+ There are functions to convert between QImage and
+ QPixmap. Typically, the QImage class is used to load an image
+ file, optionally manipulating the image data, before the QImage
+ object is converted into a QPixmap to be shown on
+ screen. Alternatively, if no manipulation is desired, the image
+ file can be loaded directly into a QPixmap. On Windows, the
+ QPixmap class also supports conversion between \c HBITMAP and
+ QPixmap. On Symbian, the QPixmap class also supports conversion
+ between CFbsBitmap and QPixmap.
+
+ QPixmap provides a collection of functions that can be used to
+ obtain a variety of information about the pixmap. In addition,
+ there are several functions that enables transformation of the
+ pixmap.
+
+ \tableofcontents
+
+ \section1 Reading and Writing Image Files
+
+ QPixmap provides several ways of reading an image file: The file
+ can be loaded when constructing the QPixmap object, or by using
+ the load() or loadFromData() functions later on. When loading an
+ image, the file name can either refer to an actual file on disk or
+ to one of the application's embedded resources. See \l{The Qt
+ Resource System} overview for details on how to embed images and
+ other resource files in the application's executable.
+
+ Simply call the save() function to save a QPixmap object.
+
+ The complete list of supported file formats are available through
+ the QImageReader::supportedImageFormats() and
+ QImageWriter::supportedImageFormats() functions. New file formats
+ can be added as plugins. By default, Qt supports the following
+ formats:
+
+ \table
+ \header \o Format \o Description \o Qt's support
+ \row \o BMP \o Windows Bitmap \o Read/write
+ \row \o GIF \o Graphic Interchange Format (optional) \o Read
+ \row \o JPG \o Joint Photographic Experts Group \o Read/write
+ \row \o JPEG \o Joint Photographic Experts Group \o Read/write
+ \row \o PNG \o Portable Network Graphics \o Read/write
+ \row \o PBM \o Portable Bitmap \o Read
+ \row \o PGM \o Portable Graymap \o Read
+ \row \o PPM \o Portable Pixmap \o Read/write
+ \row \o XBM \o X11 Bitmap \o Read/write
+ \row \o XPM \o X11 Pixmap \o Read/write
+ \endtable
+
+ \section1 Pixmap Information
+
+ QPixmap provides a collection of functions that can be used to
+ obtain a variety of information about the pixmap:
+
+ \table
+ \header
+ \o \o Available Functions
+ \row
+ \o Geometry
+ \o
+ The size(), width() and height() functions provide information
+ about the pixmap's size. The rect() function returns the image's
+ enclosing rectangle.
+
+ \row
+ \o Alpha component
+ \o
+
+ The hasAlphaChannel() returns true if the pixmap has a format that
+ respects the alpha channel, otherwise returns false. The hasAlpha(),
+ setMask() and mask() functions are legacy and should not be used.
+ They are potentially very slow.
+
+ The createHeuristicMask() function creates and returns a 1-bpp
+ heuristic mask (i.e. a QBitmap) for this pixmap. It works by
+ selecting a color from one of the corners and then chipping away
+ pixels of that color, starting at all the edges. The
+ createMaskFromColor() function creates and returns a mask (i.e. a
+ QBitmap) for the pixmap based on a given color.
+
+ \row
+ \o Low-level information
+ \o
+
+ The depth() function returns the depth of the pixmap. The
+ defaultDepth() function returns the default depth, i.e. the depth
+ used by the application on the given screen.
+
+ The cacheKey() function returns a number that uniquely
+ identifies the contents of the QPixmap object.
+
+ The x11Info() function returns information about the configuration
+ of the X display used by the screen to which the pixmap currently
+ belongs. The x11PictureHandle() function returns the X11 Picture
+ handle of the pixmap for XRender support. Note that the two latter
+ functions are only available on x11.
+
+ \endtable
+
+ \section1 Pixmap Conversion
+
+ A QPixmap object can be converted into a QImage using the
+ toImage() function. Likewise, a QImage can be converted into a
+ QPixmap using the fromImage(). If this is too expensive an
+ operation, you can use QBitmap::fromImage() instead.
+
+ In addition, on Windows, the QPixmap class supports conversion to
+ and from HBITMAP: the toWinHBITMAP() function creates a HBITMAP
+ equivalent to the QPixmap, based on the given HBitmapFormat, and
+ returns the HBITMAP handle. The fromWinHBITMAP() function returns
+ a QPixmap that is equivalent to the given bitmap which has the
+ specified format. The QPixmap class also supports conversion to
+ and from HICON: the toWinHICON() function creates a HICON equivalent
+ to the QPixmap, and returns the HICON handle. The fromWinHICON()
+ function returns a QPixmap that is equivalent to the given icon.
+
+ In addition, on Symbian, the QPixmap class supports conversion to
+ and from CFbsBitmap: the toSymbianCFbsBitmap() function creates
+ CFbsBitmap equivalent to the QPixmap, based on given mode and returns
+ a CFbsBitmap object. The fromSymbianCFbsBitmap() function returns a
+ QPixmap that is equivalent to the given bitmap and given mode.
+
+ \section1 Pixmap Transformations
+
+ QPixmap supports a number of functions for creating a new pixmap
+ that is a transformed version of the original:
+
+ The scaled(), scaledToWidth() and scaledToHeight() functions
+ return scaled copies of the pixmap, while the copy() function
+ creates a QPixmap that is a plain copy of the original one.
+
+ The transformed() function returns a copy of the pixmap that is
+ transformed with the given transformation matrix and
+ transformation mode: Internally, the transformation matrix is
+ adjusted to compensate for unwanted translation,
+ i.e. transformed() returns the smallest pixmap containing all
+ transformed points of the original pixmap. The static trueMatrix()
+ function returns the actual matrix used for transforming the
+ pixmap.
+
+ \note When using the native X11 graphics system, the pixmap
+ becomes invalid when the QApplication instance is destroyed.
+
+ \sa QBitmap, QImage, QImageReader, QImageWriter
+*/
+
+
+/*!
+ \typedef QPixmap::DataPtr
+ \internal
+*/
+
+/*!
+ \fn DataPtr &QPixmap::data_ptr()
+ \internal
+*/
+
+/*!
+ Returns true if this pixmap has an alpha channel, \e or has a
+ mask, otherwise returns false.
+
+ \sa hasAlphaChannel(), mask()
+*/
+bool QPixmap::hasAlpha() const
+{
+#if defined(Q_WS_X11)
+ if (data && data->hasAlphaChannel())
+ return true;
+ QPixmapData *pd = pixmapData();
+ if (pd && pd->classId() == QPixmapData::X11Class) {
+ QX11PixmapData *x11Data = static_cast<QX11PixmapData*>(pd);
+#ifndef QT_NO_XRENDER
+ if (x11Data->picture && x11Data->d == 32)
+ return true;
+#endif
+ if (x11Data->d == 1 || x11Data->x11_mask)
+ return true;
+ }
+ return false;
+#else
+ return data && data->hasAlphaChannel();
+#endif
+}
+
+/*!
+ Returns true if the pixmap has a format that respects the alpha
+ channel, otherwise returns false.
+
+ \sa hasAlpha()
+*/
+bool QPixmap::hasAlphaChannel() const
+{
+ return data && data->hasAlphaChannel();
+}
+
+/*!
+ \internal
+*/
+int QPixmap::metric(PaintDeviceMetric metric) const
+{
+ return data ? data->metric(metric) : 0;
+}
+
+/*!
+ \fn void QPixmap::setAlphaChannel(const QPixmap &alphaChannel)
+ \obsolete
+
+ Sets the alpha channel of this pixmap to the given \a alphaChannel
+ by converting the \a alphaChannel into 32 bit and using the
+ intensity of the RGB pixel values.
+
+ The effect of this function is undefined when the pixmap is being
+ painted on.
+
+ \warning This is potentially an expensive operation. Most usecases
+ for this function are covered by QPainter and compositionModes
+ which will normally execute faster.
+
+ \sa alphaChannel(), {QPixmap#Pixmap Transformations}{Pixmap
+ Transformations}
+ */
+void QPixmap::setAlphaChannel(const QPixmap &alphaChannel)
+{
+ if (alphaChannel.isNull())
+ return;
+
+ if (paintingActive()) {
+ qWarning("QPixmap::setAlphaChannel: "
+ "Cannot set alpha channel while pixmap is being painted on");
+ return;
+ }
+
+ if (width() != alphaChannel.width() && height() != alphaChannel.height()) {
+ qWarning("QPixmap::setAlphaChannel: "
+ "The pixmap and the alpha channel pixmap must have the same size");
+ return;
+ }
+
+ detach();
+ data->setAlphaChannel(alphaChannel);
+}
+
+/*!
+ \obsolete
+
+ Returns the alpha channel of the pixmap as a new grayscale QPixmap in which
+ each pixel's red, green, and blue values are given the alpha value of the
+ original pixmap. The color depth of the returned pixmap is the system depth
+ on X11 and 8-bit on Windows and Mac OS X.
+
+ You can use this function while debugging
+ to get a visible image of the alpha channel. If the pixmap doesn't have an
+ alpha channel, i.e., the alpha channel's value for all pixels equals
+ 0xff), a null pixmap is returned. You can check this with the \c isNull()
+ function.
+
+ We show an example:
+
+ \snippet doc/src/snippets/alphachannel.cpp 0
+
+ \image alphachannelimage.png The pixmap and channelImage QPixmaps
+
+ \warning This is an expensive operation. The alpha channel of the
+ pixmap is extracted dynamically from the pixeldata. Most usecases of this
+ function are covered by QPainter and compositionModes which will normally
+ execute faster.
+
+ \sa setAlphaChannel(), {QPixmap#Pixmap Information}{Pixmap
+ Information}
+*/
+QPixmap QPixmap::alphaChannel() const
+{
+ return data ? data->alphaChannel() : QPixmap();
+}
+
+/*!
+ \internal
+*/
+QPaintEngine *QPixmap::paintEngine() const
+{
+ return data ? data->paintEngine() : 0;
+}
+
+/*!
+ \fn QBitmap QPixmap::mask() const
+
+ Extracts a bitmap mask from the pixmap's alpha channel.
+
+ \warning This is potentially an expensive operation. The mask of
+ the pixmap is extracted dynamically from the pixeldata.
+
+ \sa setMask(), {QPixmap#Pixmap Information}{Pixmap Information}
+*/
+QBitmap QPixmap::mask() const
+{
+ return data ? data->mask() : QBitmap();
+}
+
+/*!
+ Returns the default pixmap depth used by the application.
+
+ On Windows and Mac, the default depth is always 32. On X11 and
+ embedded, the depth of the screen will be returned by this
+ function.
+
+ \sa depth(), QColormap::depth(), {QPixmap#Pixmap Information}{Pixmap Information}
+
+*/
+int QPixmap::defaultDepth()
+{
+#if defined(Q_WS_QWS)
+ return QScreen::instance()->depth();
+#elif defined(Q_WS_X11)
+ return QX11Info::appDepth();
+#elif defined(Q_WS_WINCE)
+ return QColormap::instance().depth();
+#elif defined(Q_WS_WIN)
+ return 32; // XXX
+#elif defined(Q_WS_MAC)
+ return 32;
+#elif defined(Q_OS_SYMBIAN)
+ return S60->screenDepth;
+#elif defined(Q_WS_QPA)
+ return 32; //LITE: use graphicssystem (we should do that in general)
+#endif
+}
+
+/*!
+ Detaches the pixmap from shared pixmap data.
+
+ A pixmap is automatically detached by Qt whenever its contents are
+ about to change. This is done in almost all QPixmap member
+ functions that modify the pixmap (fill(), fromImage(),
+ load(), etc.), and in QPainter::begin() on a pixmap.
+
+ There are two exceptions in which detach() must be called
+ explicitly, that is when calling the handle() or the
+ x11PictureHandle() function (only available on X11). Otherwise,
+ any modifications done using system calls, will be performed on
+ the shared data.
+
+ The detach() function returns immediately if there is just a
+ single reference or if the pixmap has not been initialized yet.
+*/
+void QPixmap::detach()
+{
+ if (!data)
+ return;
+
+ // QPixmap.data member may be QRuntimePixmapData so use pixmapData() function to get
+ // the actual underlaying runtime pixmap data.
+ QPixmapData *pd = pixmapData();
+ QPixmapData::ClassId id = pd->classId();
+ if (id == QPixmapData::RasterClass) {
+ QRasterPixmapData *rasterData = static_cast<QRasterPixmapData*>(pd);
+ rasterData->image.detach();
+ }
+
+ if (data->is_cached && data->ref == 1)
+ QImagePixmapCleanupHooks::executePixmapDataModificationHooks(data.data());
+
+#if defined(Q_WS_MAC)
+ QMacPixmapData *macData = id == QPixmapData::MacClass ? static_cast<QMacPixmapData*>(pd) : 0;
+ if (macData) {
+ if (macData->cg_mask) {
+ CGImageRelease(macData->cg_mask);
+ macData->cg_mask = 0;
+ }
+ }
+#endif
+
+ if (data->ref != 1) {
+ *this = copy();
+ }
+ ++data->detach_no;
+
+#if defined(Q_WS_X11)
+ if (pd->classId() == QPixmapData::X11Class) {
+ QX11PixmapData *d = static_cast<QX11PixmapData*>(pd);
+ d->flags &= ~QX11PixmapData::Uninitialized;
+
+ // reset the cache data
+ if (d->hd2) {
+ XFreePixmap(X11->display, d->hd2);
+ d->hd2 = 0;
+ }
+ }
+#elif defined(Q_WS_MAC)
+ if (macData) {
+ macData->macReleaseCGImageRef();
+ macData->uninit = false;
+ }
+#endif
+}
+
+/*!
+ \fn QPixmap QPixmap::fromImage(const QImage &image, Qt::ImageConversionFlags flags)
+
+ Converts the given \a image to a pixmap using the specified \a
+ flags to control the conversion. The \a flags argument is a
+ bitwise-OR of the \l{Qt::ImageConversionFlags}. Passing 0 for \a
+ flags sets all the default options.
+
+ In case of monochrome and 8-bit images, the image is first
+ converted to a 32-bit pixmap and then filled with the colors in
+ the color table. If this is too expensive an operation, you can
+ use QBitmap::fromImage() instead.
+
+ \sa fromImageReader(), toImage(), {QPixmap#Pixmap Conversion}{Pixmap Conversion}
+*/
+QPixmap QPixmap::fromImage(const QImage &image, Qt::ImageConversionFlags flags)
+{
+ if (image.isNull())
+ return QPixmap();
+
+ QGraphicsSystem* gs = QApplicationPrivate::graphicsSystem();
+ QScopedPointer<QPixmapData> data(gs ? gs->createPixmapData(QPixmapData::PixmapType)
+ : QGraphicsSystem::createDefaultPixmapData(QPixmapData::PixmapType));
+ data->fromImage(image, flags);
+ return QPixmap(data.take());
+}
+
+/*!
+ \fn QPixmap QPixmap::fromImageReader(QImageReader *imageReader, Qt::ImageConversionFlags flags)
+
+ Create a QPixmap from an image read directly from an \a imageReader.
+ The \a flags argument is a bitwise-OR of the \l{Qt::ImageConversionFlags}.
+ Passing 0 for \a flags sets all the default options.
+
+ On some systems, reading an image directly to QPixmap can use less memory than
+ reading a QImage to convert it to QPixmap.
+
+ \sa fromImage(), toImage(), {QPixmap#Pixmap Conversion}{Pixmap Conversion}
+*/
+QPixmap QPixmap::fromImageReader(QImageReader *imageReader, Qt::ImageConversionFlags flags)
+{
+ QGraphicsSystem *gs = QApplicationPrivate::graphicsSystem();
+ QScopedPointer<QPixmapData> data(gs ? gs->createPixmapData(QPixmapData::PixmapType)
+ : QGraphicsSystem::createDefaultPixmapData(QPixmapData::PixmapType));
+ data->fromImageReader(imageReader, flags);
+ return QPixmap(data.take());
+}
+
+/*!
+ \fn QPixmap QPixmap::grabWindow(WId window, int x, int y, int
+ width, int height)
+
+ Creates and returns a pixmap constructed by grabbing the contents
+ of the given \a window restricted by QRect(\a x, \a y, \a width,
+ \a height).
+
+ The arguments (\a{x}, \a{y}) specify the offset in the window,
+ whereas (\a{width}, \a{height}) specify the area to be copied. If
+ \a width is negative, the function copies everything to the right
+ border of the window. If \a height is negative, the function
+ copies everything to the bottom of the window.
+
+ The window system identifier (\c WId) can be retrieved using the
+ QWidget::winId() function. The rationale for using a window
+ identifier and not a QWidget, is to enable grabbing of windows
+ that are not part of the application, window system frames, and so
+ on.
+
+ The grabWindow() function grabs pixels from the screen, not from
+ the window, i.e. if there is another window partially or entirely
+ over the one you grab, you get pixels from the overlying window,
+ too. The mouse cursor is generally not grabbed.
+
+ Note on X11 that if the given \a window doesn't have the same depth
+ as the root window, and another window partially or entirely
+ obscures the one you grab, you will \e not get pixels from the
+ overlying window. The contents of the obscured areas in the
+ pixmap will be undefined and uninitialized.
+
+ On Windows Vista and above grabbing a layered window, which is
+ created by setting the Qt::WA_TranslucentBackground attribute, will
+ not work. Instead grabbing the desktop widget should work.
+
+ \warning In general, grabbing an area outside the screen is not
+ safe. This depends on the underlying window system.
+
+ \sa grabWidget(), {Screenshot Example}
+*/
+
+/*!
+ \internal
+*/
+QPixmapData* QPixmap::pixmapData() const
+{
+ if (data) {
+ QPixmapData* pm = data.data();
+ return pm->runtimeData() ? pm->runtimeData() : pm;
+ }
+
+ return 0;
+}
+
+
+/*!
+ \enum QPixmap::HBitmapFormat
+
+ \bold{Win32 only:} This enum defines how the conversion between \c
+ HBITMAP and QPixmap is performed.
+
+ \warning This enum is only available on Windows.
+
+ \value NoAlpha The alpha channel is ignored and always treated as
+ being set to fully opaque. This is preferred if the \c HBITMAP is
+ used with standard GDI calls, such as \c BitBlt().
+
+ \value PremultipliedAlpha The \c HBITMAP is treated as having an
+ alpha channel and premultiplied colors. This is preferred if the
+ \c HBITMAP is accessed through the \c AlphaBlend() GDI function.
+
+ \value Alpha The \c HBITMAP is treated as having a plain alpha
+ channel. This is the preferred format if the \c HBITMAP is going
+ to be used as an application icon or systray icon.
+
+ \sa fromWinHBITMAP(), toWinHBITMAP()
+*/
+
+/*! \fn HBITMAP QPixmap::toWinHBITMAP(HBitmapFormat format) const
+ \bold{Win32 only:} Creates a \c HBITMAP equivalent to the QPixmap,
+ based on the given \a format. Returns the \c HBITMAP handle.
+
+ It is the caller's responsibility to free the \c HBITMAP data
+ after use.
+
+ \warning This function is only available on Windows.
+
+ \sa fromWinHBITMAP(), {QPixmap#Pixmap Conversion}{Pixmap Conversion}
+*/
+
+/*! \fn QPixmap QPixmap::fromWinHBITMAP(HBITMAP bitmap, HBitmapFormat format)
+ \bold{Win32 only:} Returns a QPixmap that is equivalent to the
+ given \a bitmap. The conversion is based on the specified \a
+ format.
+
+ \warning This function is only available on Windows.
+
+ \sa toWinHBITMAP(), {QPixmap#Pixmap Conversion}{Pixmap Conversion}
+
+*/
+
+/*! \fn HICON QPixmap::toWinHICON() const
+ \since 4.6
+
+ \bold{Win32 only:} Creates a \c HICON equivalent to the QPixmap.
+ Returns the \c HICON handle.
+
+ It is the caller's responsibility to free the \c HICON data after use.
+
+ \warning This function is only available on Windows.
+
+ \sa fromWinHICON(), {QPixmap#Pixmap Conversion}{Pixmap Conversion}
+*/
+
+/*! \fn QPixmap QPixmap::fromWinHICON(HICON icon)
+ \since 4.6
+
+ \bold{Win32 only:} Returns a QPixmap that is equivalent to the given
+ \a icon.
+
+ \warning This function is only available on Windows.
+
+ \sa toWinHICON(), {QPixmap#Pixmap Conversion}{Pixmap Conversion}
+
+*/
+
+/*! \fn const QX11Info &QPixmap::x11Info() const
+ \bold{X11 only:} Returns information about the configuration of
+ the X display used by the screen to which the pixmap currently belongs.
+
+ \warning This function is only available on X11.
+
+ \sa {QPixmap#Pixmap Information}{Pixmap Information}
+*/
+
+/*! \fn Qt::HANDLE QPixmap::x11PictureHandle() const
+ \bold{X11 only:} Returns the X11 Picture handle of the pixmap for
+ XRender support.
+
+ This function will return 0 if XRender support is not compiled
+ into Qt, if the XRender extension is not supported on the X11
+ display, or if the handle could not be created. Use of this
+ function is not portable.
+
+ \warning This function is only available on X11.
+
+ \sa {QPixmap#Pixmap Information}{Pixmap Information}
+*/
+
+/*! \fn int QPixmap::x11SetDefaultScreen(int screen)
+ \internal
+*/
+
+/*! \fn void QPixmap::x11SetScreen(int screen)
+ \internal
+*/
+
+/*! \fn QRgb* QPixmap::clut() const
+ \internal
+*/
+
+/*! \fn int QPixmap::numCols() const
+ \obsolete
+ \internal
+ \sa colorCount()
+*/
+
+/*! \fn int QPixmap::colorCount() const
+ \since 4.6
+ \internal
+*/
+
+/*! \fn const uchar* QPixmap::qwsBits() const
+ \internal
+ \since 4.1
+*/
+
+/*! \fn int QPixmap::qwsBytesPerLine() const
+ \internal
+ \since 4.1
+*/
+
+QT_END_NAMESPACE
diff --git a/src/gui/image/qpixmap.h b/src/gui/image/qpixmap.h
new file mode 100644
index 0000000000..f2e79c14f4
--- /dev/null
+++ b/src/gui/image/qpixmap.h
@@ -0,0 +1,335 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPIXMAP_H
+#define QPIXMAP_H
+
+#include <QtGui/qpaintdevice.h>
+#include <QtGui/qcolor.h>
+#include <QtCore/qnamespace.h>
+#include <QtCore/qstring.h> // char*->QString conversion
+#include <QtCore/qsharedpointer.h>
+#include <QtGui/qimage.h>
+#include <QtGui/qtransform.h>
+
+QT_BEGIN_HEADER
+
+#if defined(Q_OS_SYMBIAN)
+class CFbsBitmap;
+class RSgImage;
+#endif
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QImageWriter;
+class QImageReader;
+class QColor;
+class QVariant;
+class QX11Info;
+class QPixmapData;
+
+class Q_GUI_EXPORT QPixmap : public QPaintDevice
+{
+public:
+ QPixmap();
+ explicit QPixmap(QPixmapData *data);
+ QPixmap(int w, int h);
+ QPixmap(const QSize &);
+ QPixmap(const QString& fileName, const char *format = 0, Qt::ImageConversionFlags flags = Qt::AutoColor);
+#ifndef QT_NO_IMAGEFORMAT_XPM
+ QPixmap(const char * const xpm[]);
+#endif
+ QPixmap(const QPixmap &);
+ ~QPixmap();
+
+ QPixmap &operator=(const QPixmap &);
+#ifdef Q_COMPILER_RVALUE_REFS
+ inline QPixmap &operator=(QPixmap &&other)
+ { qSwap(data, other.data); return *this; }
+#endif
+ inline void swap(QPixmap &other) { qSwap(data, other.data); }
+
+ operator QVariant() const;
+
+ bool isNull() const; // ### Qt 5: make inline
+ int devType() const;
+
+ int width() const; // ### Qt 5: make inline
+ int height() const; // ### Qt 5: make inline
+ QSize size() const;
+ QRect rect() const;
+ int depth() const;
+
+ static int defaultDepth();
+
+ void fill(const QColor &fillColor = Qt::white);
+ void fill(const QWidget *widget, const QPoint &ofs);
+ inline void fill(const QWidget *widget, int xofs, int yofs) { fill(widget, QPoint(xofs, yofs)); }
+
+ QBitmap mask() const;
+ void setMask(const QBitmap &);
+
+ QPixmap alphaChannel() const;
+ void setAlphaChannel(const QPixmap &);
+
+ bool hasAlpha() const;
+ bool hasAlphaChannel() const;
+
+#ifndef QT_NO_IMAGE_HEURISTIC_MASK
+ QBitmap createHeuristicMask(bool clipTight = true) const;
+#endif
+ QBitmap createMaskFromColor(const QColor &maskColor) const; // ### Qt 5: remove
+ QBitmap createMaskFromColor(const QColor &maskColor, Qt::MaskMode mode) const;
+
+ static QPixmap grabWindow(WId, int x=0, int y=0, int w=-1, int h=-1);
+ static QPixmap grabWidget(QWidget *widget, const QRect &rect);
+ static inline QPixmap grabWidget(QWidget *widget, int x=0, int y=0, int w=-1, int h=-1)
+ { return grabWidget(widget, QRect(x, y, w, h)); }
+
+
+ inline QPixmap scaled(int w, int h, Qt::AspectRatioMode aspectMode = Qt::IgnoreAspectRatio,
+ Qt::TransformationMode mode = Qt::FastTransformation) const
+ { return scaled(QSize(w, h), aspectMode, mode); }
+ QPixmap scaled(const QSize &s, Qt::AspectRatioMode aspectMode = Qt::IgnoreAspectRatio,
+ Qt::TransformationMode mode = Qt::FastTransformation) const;
+ QPixmap scaledToWidth(int w, Qt::TransformationMode mode = Qt::FastTransformation) const;
+ QPixmap scaledToHeight(int h, Qt::TransformationMode mode = Qt::FastTransformation) const;
+ QPixmap transformed(const QMatrix &, Qt::TransformationMode mode = Qt::FastTransformation) const;
+ static QMatrix trueMatrix(const QMatrix &m, int w, int h);
+ QPixmap transformed(const QTransform &, Qt::TransformationMode mode = Qt::FastTransformation) const;
+ static QTransform trueMatrix(const QTransform &m, int w, int h);
+
+ QImage toImage() const;
+ static QPixmap fromImage(const QImage &image, Qt::ImageConversionFlags flags = Qt::AutoColor);
+ static QPixmap fromImageReader(QImageReader *imageReader, Qt::ImageConversionFlags flags = Qt::AutoColor);
+
+ bool load(const QString& fileName, const char *format = 0, Qt::ImageConversionFlags flags = Qt::AutoColor);
+ bool loadFromData(const uchar *buf, uint len, const char* format = 0, Qt::ImageConversionFlags flags = Qt::AutoColor);
+ inline bool loadFromData(const QByteArray &data, const char* format = 0, Qt::ImageConversionFlags flags = Qt::AutoColor);
+ bool save(const QString& fileName, const char* format = 0, int quality = -1) const;
+ bool save(QIODevice* device, const char* format = 0, int quality = -1) const;
+
+ bool convertFromImage(const QImage &img, Qt::ImageConversionFlags flags = Qt::AutoColor);
+
+#if defined(Q_WS_WIN)
+ enum HBitmapFormat {
+ NoAlpha,
+ PremultipliedAlpha,
+ Alpha
+ };
+
+ HBITMAP toWinHBITMAP(HBitmapFormat format = NoAlpha) const;
+ HICON toWinHICON() const;
+
+ static QPixmap fromWinHBITMAP(HBITMAP hbitmap, HBitmapFormat format = NoAlpha);
+ static QPixmap fromWinHICON(HICON hicon);
+#endif
+
+#if defined(Q_WS_MAC)
+ CGImageRef toMacCGImageRef() const;
+ static QPixmap fromMacCGImageRef(CGImageRef image);
+#endif
+
+#if defined(Q_OS_SYMBIAN)
+ CFbsBitmap *toSymbianCFbsBitmap() const;
+ static QPixmap fromSymbianCFbsBitmap(CFbsBitmap *bitmap);
+ RSgImage* toSymbianRSgImage() const;
+ static QPixmap fromSymbianRSgImage(RSgImage *sgImage);
+#endif
+
+ inline QPixmap copy(int x, int y, int width, int height) const;
+ QPixmap copy(const QRect &rect = QRect()) const;
+
+ inline void scroll(int dx, int dy, int x, int y, int width, int height, QRegion *exposed = 0);
+ void scroll(int dx, int dy, const QRect &rect, QRegion *exposed = 0);
+
+ int serialNumber() const;
+ qint64 cacheKey() const;
+
+ bool isDetached() const;
+ void detach();
+
+ bool isQBitmap() const;
+
+#if defined(Q_WS_QWS)
+ const uchar *qwsBits() const;
+ int qwsBytesPerLine() const;
+ QRgb *clut() const;
+#ifdef QT_DEPRECATED
+ QT_DEPRECATED int numCols() const;
+#endif
+ int colorCount() const;
+#elif defined(Q_WS_MAC)
+ Qt::HANDLE macQDHandle() const;
+ Qt::HANDLE macQDAlphaHandle() const;
+ Qt::HANDLE macCGHandle() const;
+#elif defined(Q_WS_X11)
+ enum ShareMode { ImplicitlyShared, ExplicitlyShared };
+
+ static QPixmap fromX11Pixmap(Qt::HANDLE pixmap, ShareMode mode = ImplicitlyShared);
+ static int x11SetDefaultScreen(int screen);
+ void x11SetScreen(int screen);
+ const QX11Info &x11Info() const;
+ Qt::HANDLE x11PictureHandle() const;
+#endif
+
+#if defined(Q_WS_X11) || defined(Q_WS_QWS)
+ Qt::HANDLE handle() const;
+#endif
+
+ QPaintEngine *paintEngine() const;
+
+ inline bool operator!() const { return isNull(); }
+
+protected:
+ int metric(PaintDeviceMetric) const;
+
+#ifdef QT3_SUPPORT
+public:
+ enum ColorMode { Auto, Color, Mono };
+ QT3_SUPPORT_CONSTRUCTOR QPixmap(const QString& fileName, const char *format, ColorMode mode);
+ QT3_SUPPORT bool load(const QString& fileName, const char *format, ColorMode mode);
+ QT3_SUPPORT bool loadFromData(const uchar *buf, uint len, const char* format, ColorMode mode);
+ QT3_SUPPORT_CONSTRUCTOR QPixmap(const QImage& image);
+ QT3_SUPPORT QPixmap &operator=(const QImage &);
+ inline QT3_SUPPORT QImage convertToImage() const { return toImage(); }
+ QT3_SUPPORT bool convertFromImage(const QImage &, ColorMode mode);
+ inline QT3_SUPPORT operator QImage() const { return toImage(); }
+ inline QT3_SUPPORT QPixmap xForm(const QMatrix &matrix) const { return transformed(QTransform(matrix)); }
+ inline QT3_SUPPORT bool selfMask() const { return false; }
+private:
+ void resize_helper(const QSize &s);
+public:
+ inline QT3_SUPPORT void resize(const QSize &s) { resize_helper(s); }
+ inline QT3_SUPPORT void resize(int width, int height) { resize_helper(QSize(width, height)); }
+#endif
+
+private:
+ QExplicitlySharedDataPointer<QPixmapData> data;
+
+ bool doImageIO(QImageWriter *io, int quality) const;
+
+ // ### Qt5: remove the following three lines
+ enum Type { PixmapType, BitmapType }; // must match QPixmapData::PixelType
+ QPixmap(const QSize &s, Type);
+ void init(int, int, Type = PixmapType);
+
+ QPixmap(const QSize &s, int type);
+ void init(int, int, int);
+ void deref();
+#if defined(Q_WS_WIN)
+ void initAlphaPixmap(uchar *bytes, int length, struct tagBITMAPINFO *bmi);
+#endif
+ Q_DUMMY_COMPARISON_OPERATOR(QPixmap)
+#ifdef Q_WS_MAC
+ friend CGContextRef qt_mac_cg_context(const QPaintDevice*);
+ friend CGImageRef qt_mac_create_imagemask(const QPixmap&, const QRectF&);
+ friend IconRef qt_mac_create_iconref(const QPixmap&);
+ friend quint32 *qt_mac_pixmap_get_base(const QPixmap*);
+ friend int qt_mac_pixmap_get_bytes_per_line(const QPixmap*);
+#endif
+ friend class QPixmapData;
+ friend class QX11PixmapData;
+ friend class QMacPixmapData;
+ friend class QS60PixmapData;
+ friend class QBitmap;
+ friend class QPaintDevice;
+ friend class QPainter;
+ friend class QGLWidget;
+ friend class QX11PaintEngine;
+ friend class QCoreGraphicsPaintEngine;
+ friend class QWidgetPrivate;
+ friend class QRasterBuffer;
+#if !defined(QT_NO_DATASTREAM)
+ friend Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QPixmap &);
+#endif
+ friend Q_GUI_EXPORT qint64 qt_pixmap_id(const QPixmap &pixmap);
+
+public:
+ QPixmapData* pixmapData() const;
+
+public:
+ typedef QExplicitlySharedDataPointer<QPixmapData> DataPtr;
+ inline DataPtr &data_ptr() { return data; }
+};
+
+Q_DECLARE_SHARED(QPixmap)
+
+inline QPixmap QPixmap::copy(int ax, int ay, int awidth, int aheight) const
+{
+ return copy(QRect(ax, ay, awidth, aheight));
+}
+
+inline void QPixmap::scroll(int dx, int dy, int ax, int ay, int awidth, int aheight, QRegion *exposed)
+{
+ scroll(dx, dy, QRect(ax, ay, awidth, aheight), exposed);
+}
+
+inline bool QPixmap::loadFromData(const QByteArray &buf, const char *format,
+ Qt::ImageConversionFlags flags)
+{
+ return loadFromData(reinterpret_cast<const uchar *>(buf.constData()), buf.size(), format, flags);
+}
+
+/*****************************************************************************
+ QPixmap stream functions
+*****************************************************************************/
+
+#if !defined(QT_NO_DATASTREAM)
+Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QPixmap &);
+Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QPixmap &);
+#endif
+
+/*****************************************************************************
+ QPixmap (and QImage) helper functions
+*****************************************************************************/
+#ifdef QT3_SUPPORT
+QT3_SUPPORT Q_GUI_EXPORT void copyBlt(QPixmap *dst, int dx, int dy, const QPixmap *src,
+ int sx=0, int sy=0, int sw=-1, int sh=-1);
+#endif // QT3_SUPPORT
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QPIXMAP_H
diff --git a/src/gui/image/qpixmap_blitter.cpp b/src/gui/image/qpixmap_blitter.cpp
new file mode 100644
index 0000000000..e2cd745e7c
--- /dev/null
+++ b/src/gui/image/qpixmap_blitter.cpp
@@ -0,0 +1,310 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpixmap_blitter_p.h"
+
+#include <qpainter.h>
+#include <qimage.h>
+
+#include <private/qapplication_p.h>
+#include <private/qgraphicssystem_p.h>
+#include <private/qblittable_p.h>
+
+#include <private/qdrawhelper_p.h>
+#include <private/qfont_p.h>
+
+#ifndef QT_NO_BLITTABLE
+QT_BEGIN_NAMESPACE
+
+static int global_ser_no = 0;
+
+QBlittablePixmapData::QBlittablePixmapData()
+ : QPixmapData(QPixmapData::PixmapType,BlitterClass), m_engine(0), m_blittable(0)
+#ifdef QT_BLITTER_RASTEROVERLAY
+ ,m_rasterOverlay(0), m_unmergedCopy(0)
+#endif //QT_BLITTER_RASTEROVERLAY
+{
+ setSerialNumber(++global_ser_no);
+}
+
+QBlittablePixmapData::~QBlittablePixmapData()
+{
+ delete m_blittable;
+ delete m_engine;
+#ifdef QT_BLITTER_RASTEROVERLAY
+ delete m_rasterOverlay;
+ delete m_unmergedCopy;
+#endif //QT_BLITTER_RASTEROVERLAY
+}
+
+QBlittable *QBlittablePixmapData::blittable() const
+{
+ if (!m_blittable) {
+ QBlittablePixmapData *that = const_cast<QBlittablePixmapData *>(this);
+ that->m_blittable = this->createBlittable(QSize(w,h));
+ }
+
+ return m_blittable;
+}
+
+void QBlittablePixmapData::setBlittable(QBlittable *blittable)
+{
+ resize(blittable->size().width(),blittable->size().height());
+ m_blittable = blittable;
+}
+
+void QBlittablePixmapData::resize(int width, int height)
+{
+
+ delete m_blittable;
+ m_blittable = 0;
+ delete m_engine;
+ m_engine = 0;
+#ifdef Q_WS_QPA
+ d = QApplicationPrivate::platformIntegration()->screens().at(0)->depth();
+#endif
+ w = width;
+ h = height;
+ is_null = (w <= 0 || h <= 0);
+}
+
+int QBlittablePixmapData::metric(QPaintDevice::PaintDeviceMetric metric) const
+{
+ switch (metric) {
+ case QPaintDevice::PdmWidth:
+ return w;
+ case QPaintDevice::PdmHeight:
+ return h;
+ case QPaintDevice::PdmWidthMM:
+ return qRound(w * 25.4 / qt_defaultDpiX());
+ case QPaintDevice::PdmHeightMM:
+ return qRound(h * 25.4 / qt_defaultDpiY());
+ case QPaintDevice::PdmDepth:
+ return 32;
+ case QPaintDevice::PdmDpiX: // fall-through
+ case QPaintDevice::PdmPhysicalDpiX:
+ return qt_defaultDpiX();
+ case QPaintDevice::PdmDpiY: // fall-through
+ case QPaintDevice::PdmPhysicalDpiY:
+ return qt_defaultDpiY();
+ default:
+ qWarning("QRasterPixmapData::metric(): Unhandled metric type %d", metric);
+ break;
+ }
+
+ return 0;
+}
+
+void QBlittablePixmapData::fill(const QColor &color)
+{
+ //jlind: todo: change when blittables can support non opaque fillRects
+ if (color.alpha() == 255 && blittable()->capabilities() & QBlittable::SolidRectCapability) {
+ blittable()->unlock();
+ blittable()->fillRect(QRectF(0,0,w,h),color);
+ }else {
+ uint pixel;
+ switch (blittable()->lock()->format()) {
+ case QImage::Format_ARGB32_Premultiplied:
+ pixel = PREMUL(color.rgba());
+ break;
+ case QImage::Format_ARGB8565_Premultiplied:
+ pixel = qargb8565(color.rgba()).rawValue();
+ break;
+ case QImage::Format_ARGB8555_Premultiplied:
+ pixel = qargb8555(color.rgba()).rawValue();
+ break;
+ case QImage::Format_ARGB6666_Premultiplied:
+ pixel = qargb6666(color.rgba()).rawValue();
+ break;
+ case QImage::Format_ARGB4444_Premultiplied:
+ pixel = qargb4444(color.rgba()).rawValue();
+ break;
+ default:
+ pixel = color.rgba();
+ break;
+ }
+ //so premultiplied formats are supported and ARGB32 and RGB32
+ blittable()->lock()->fill(pixel);
+ }
+
+}
+
+QImage *QBlittablePixmapData::buffer()
+{
+ return blittable()->lock();
+}
+
+QImage QBlittablePixmapData::toImage() const
+{
+ return blittable()->lock()->copy();
+}
+
+bool QBlittablePixmapData::hasAlphaChannel() const
+{
+ return blittable()->lock()->hasAlphaChannel();
+}
+
+void QBlittablePixmapData::fromImage(const QImage &image,
+ Qt::ImageConversionFlags flags)
+{
+ resize(image.width(),image.height());
+ markRasterOverlay(QRect(0,0,w,h));
+ QImage *thisImg = buffer();
+
+ QImage correctFormatPic = image;
+ if (correctFormatPic.format() != thisImg->format())
+ correctFormatPic = correctFormatPic.convertToFormat(thisImg->format(), flags);
+
+ uchar *mem = thisImg->bits();
+ const uchar *bits = correctFormatPic.bits();
+ int bytesCopied = 0;
+ while (bytesCopied < correctFormatPic.byteCount()) {
+ memcpy(mem,bits,correctFormatPic.bytesPerLine());
+ mem += thisImg->bytesPerLine();
+ bits += correctFormatPic.bytesPerLine();
+ bytesCopied+=correctFormatPic.bytesPerLine();
+ }
+}
+
+QPaintEngine *QBlittablePixmapData::paintEngine() const
+{
+ if (!m_engine) {
+ QBlittablePixmapData *that = const_cast<QBlittablePixmapData *>(this);
+ that->m_engine = new QBlitterPaintEngine(that);
+ }
+ return m_engine;
+}
+
+#ifdef QT_BLITTER_RASTEROVERLAY
+
+static bool showRasterOverlay = !qgetenv("QT_BLITTER_RASTEROVERLAY").isEmpty();
+
+void QBlittablePixmapData::mergeOverlay()
+{
+ if (m_unmergedCopy || !showRasterOverlay)
+ return;
+ m_unmergedCopy = new QImage(buffer()->copy());
+ QPainter p(buffer());
+ p.setCompositionMode(QPainter::CompositionMode_SourceOver);
+ p.drawImage(0,0,*overlay());
+ p.end();
+}
+
+void QBlittablePixmapData::unmergeOverlay()
+{
+ if (!m_unmergedCopy || !showRasterOverlay)
+ return;
+ QPainter p(buffer());
+ p.setCompositionMode(QPainter::CompositionMode_Source);
+ p.drawImage(0,0,*m_unmergedCopy);
+ p.end();
+
+ delete m_unmergedCopy;
+ m_unmergedCopy = 0;
+}
+
+QImage *QBlittablePixmapData::overlay()
+{
+ if (!m_rasterOverlay||
+ m_rasterOverlay->size() != QSize(w,h)){
+ m_rasterOverlay = new QImage(w,h,QImage::Format_ARGB32_Premultiplied);
+ m_rasterOverlay->fill(0x00000000);
+ uint color = (qrand() % 11)+7;
+ m_overlayColor = QColor(Qt::GlobalColor(color));
+ m_overlayColor.setAlpha(0x88);
+
+ }
+ return m_rasterOverlay;
+}
+
+void QBlittablePixmapData::markRasterOverlayImpl(const QRectF &rect)
+{
+ if (!showRasterOverlay)
+ return;
+ QRectF transformationRect = clipAndTransformRect(rect);
+ if(!transformationRect.isEmpty()) {
+ QPainter p(overlay());
+ p.setBrush(m_overlayColor);
+ p.setCompositionMode(QPainter::CompositionMode_Source);
+ p.fillRect(transformationRect,QBrush(m_overlayColor));
+ }
+}
+
+void QBlittablePixmapData::unmarkRasterOverlayImpl(const QRectF &rect)
+{
+ if (!showRasterOverlay)
+ return;
+ QRectF transformationRect = clipAndTransformRect(rect);
+ if (!transformationRect.isEmpty()) {
+ QPainter p(overlay());
+ QColor color(0x00,0x00,0x00,0x00);
+ p.setBrush(color);
+ p.setCompositionMode(QPainter::CompositionMode_Source);
+ p.fillRect(transformationRect,QBrush(color));
+ }
+}
+
+QRectF QBlittablePixmapData::clipAndTransformRect(const QRectF &rect) const
+{
+ QRectF transformationRect = rect;
+ paintEngine();
+ if (m_engine->state()) {
+ transformationRect = m_engine->state()->matrix.mapRect(rect);
+ const QClipData *clipData = m_engine->clip();
+ if (clipData) {
+ if (clipData->hasRectClip) {
+ transformationRect &= clipData->clipRect;
+ } else if (clipData->hasRegionClip) {
+ const QVector<QRect> rects = clipData->clipRegion.rects();
+ for (int i = 0; i < rects.size(); i++) {
+ transformationRect &= rects.at(i);
+ }
+ }
+ }
+ }
+ return transformationRect;
+}
+
+#endif //QT_BLITTER_RASTEROVERLAY
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_BLITTABLE
diff --git a/src/gui/image/qpixmap_blitter_p.h b/src/gui/image/qpixmap_blitter_p.h
new file mode 100644
index 0000000000..9f4260c40d
--- /dev/null
+++ b/src/gui/image/qpixmap_blitter_p.h
@@ -0,0 +1,166 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPIXMAP_BLITTER_P_H
+#define QPIXMAP_BLITTER_P_H
+
+#include <private/qpixmapdata_p.h>
+#include <private/qpaintengine_blitter_p.h>
+
+#ifndef QT_NO_BLITTABLE
+QT_BEGIN_NAMESPACE
+
+class Q_GUI_EXPORT QBlittablePixmapData : public QPixmapData
+{
+// Q_DECLARE_PRIVATE(QBlittablePixmapData);
+public:
+ QBlittablePixmapData();
+ ~QBlittablePixmapData();
+
+ virtual QBlittable *createBlittable(const QSize &size) const = 0;
+ QBlittable *blittable() const;
+ void setBlittable(QBlittable *blittable);
+
+ void resize(int width, int height);
+ int metric(QPaintDevice::PaintDeviceMetric metric) const;
+ void fill(const QColor &color);
+ QImage *buffer();
+ QImage toImage() const;
+ bool hasAlphaChannel() const;
+ void fromImage(const QImage &image, Qt::ImageConversionFlags flags);
+
+ QPaintEngine *paintEngine() const;
+
+ void markRasterOverlay(const QRectF &);
+ void markRasterOverlay(const QPointF &, const QTextItem &);
+ void markRasterOverlay(const QVectorPath &);
+ void markRasterOverlay(const QRect *rects, int rectCount);
+ void markRasterOverlay(const QRectF *rects, int rectCount);
+ void unmarkRasterOverlay(const QRectF &);
+
+#ifdef QT_BLITTER_RASTEROVERLAY
+ void mergeOverlay();
+ void unmergeOverlay();
+ QImage *overlay();
+
+#endif //QT_BLITTER_RASTEROVERLAY
+protected:
+ QBlitterPaintEngine *m_engine;
+ QBlittable *m_blittable;
+
+#ifdef QT_BLITTER_RASTEROVERLAY
+ QImage *m_rasterOverlay;
+ QImage *m_unmergedCopy;
+ QColor m_overlayColor;
+
+ void markRasterOverlayImpl(const QRectF &);
+ void unmarkRasterOverlayImpl(const QRectF &);
+ QRectF clipAndTransformRect(const QRectF &) const;
+#endif //QT_BLITTER_RASTEROVERLAY
+
+};
+
+inline void QBlittablePixmapData::markRasterOverlay(const QRectF &rect)
+{
+#ifdef QT_BLITTER_RASTEROVERLAY
+ markRasterOverlayImpl(rect);
+#else
+ Q_UNUSED(rect)
+#endif
+}
+
+inline void QBlittablePixmapData::markRasterOverlay(const QVectorPath &path)
+{
+#ifdef QT_BLITTER_RASTEROVERLAY
+ markRasterOverlayImpl(path.convertToPainterPath().boundingRect());
+#else
+ Q_UNUSED(path)
+#endif
+}
+
+inline void QBlittablePixmapData::markRasterOverlay(const QPointF &pos, const QTextItem &ti)
+{
+#ifdef QT_BLITTER_RASTEROVERLAY
+ QFontMetricsF fm(ti.font());
+ QRectF rect = fm.tightBoundingRect(ti.text());
+ rect.moveBottomLeft(pos);
+ markRasterOverlay(rect);
+#else
+ Q_UNUSED(pos)
+ Q_UNUSED(ti)
+#endif
+}
+
+inline void QBlittablePixmapData::markRasterOverlay(const QRect *rects, int rectCount)
+{
+#ifdef QT_BLITTER_RASTEROVERLAY
+ for (int i = 0; i < rectCount; i++) {
+ markRasterOverlay(rects[i]);
+ }
+#else
+ Q_UNUSED(rects)
+ Q_UNUSED(rectCount)
+#endif
+}
+inline void QBlittablePixmapData::markRasterOverlay(const QRectF *rects, int rectCount)
+{
+#ifdef QT_BLITTER_RASTEROVERLAY
+ for (int i = 0; i < rectCount; i++) {
+ markRasterOverlay(rects[i]);
+ }
+#else
+ Q_UNUSED(rects)
+ Q_UNUSED(rectCount)
+#endif
+}
+
+inline void QBlittablePixmapData::unmarkRasterOverlay(const QRectF &rect)
+{
+#ifdef QT_BLITTER_RASTEROVERLAY
+ unmarkRasterOverlayImpl(rect);
+#else
+ Q_UNUSED(rect)
+#endif
+}
+
+QT_END_NAMESPACE
+#endif // QT_NO_BLITTABLE
+#endif // QPIXMAP_BLITTER_P_H
diff --git a/src/gui/image/qpixmap_mac.cpp b/src/gui/image/qpixmap_mac.cpp
new file mode 100644
index 0000000000..72e2aa6e04
--- /dev/null
+++ b/src/gui/image/qpixmap_mac.cpp
@@ -0,0 +1,1195 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpixmap.h"
+#include "qimage.h"
+#include "qapplication.h"
+#include "qbitmap.h"
+#include "qmatrix.h"
+#include "qtransform.h"
+#include "qlibrary.h"
+#include "qvarlengtharray.h"
+#include "qdebug.h"
+#include <private/qdrawhelper_p.h>
+#include <private/qpixmap_mac_p.h>
+#include <private/qpixmap_raster_p.h>
+#include <private/qpaintengine_mac_p.h>
+#include <private/qt_mac_p.h>
+#include <private/qt_cocoa_helpers_mac_p.h>
+
+#include <limits.h>
+#include <string.h>
+
+QT_BEGIN_NAMESPACE
+
+/*****************************************************************************
+ Externals
+ *****************************************************************************/
+extern const uchar *qt_get_bitflip_array(); //qimage.cpp
+extern CGContextRef qt_mac_cg_context(const QPaintDevice *pdev); //qpaintdevice_mac.cpp
+extern RgnHandle qt_mac_get_rgn(); //qregion_mac.cpp
+extern void qt_mac_dispose_rgn(RgnHandle r); //qregion_mac.cpp
+extern QRegion qt_mac_convert_mac_region(RgnHandle rgn); //qregion_mac.cpp
+
+static int qt_pixmap_serial = 0;
+
+Q_GUI_EXPORT quint32 *qt_mac_pixmap_get_base(const QPixmap *pix)
+{
+ return static_cast<QMacPixmapData*>(pix->data.data())->pixels;
+}
+
+Q_GUI_EXPORT int qt_mac_pixmap_get_bytes_per_line(const QPixmap *pix)
+{
+ return static_cast<QMacPixmapData*>(pix->data.data())->bytesPerRow;
+}
+
+void qt_mac_cgimage_data_free(void *info, const void *memoryToFree, size_t)
+{
+ QMacPixmapData *pmdata = static_cast<QMacPixmapData *>(info);
+ if (!pmdata) {
+ free(const_cast<void *>(memoryToFree));
+ } else {
+ if (QMacPixmapData::validDataPointers.contains(pmdata) == false) {
+ free(const_cast<void *>(memoryToFree));
+ return;
+ }
+ if (pmdata->pixels == pmdata->pixelsToFree) {
+ // something we aren't expecting, just free it.
+ Q_ASSERT(memoryToFree != pmdata->pixelsToFree);
+ free(const_cast<void *>(memoryToFree));
+ } else {
+ free(pmdata->pixelsToFree);
+ pmdata->pixelsToFree = static_cast<quint32 *>(const_cast<void *>(memoryToFree));
+ }
+ pmdata->cg_dataBeingReleased = 0;
+ }
+}
+
+CGImageRef qt_mac_image_to_cgimage(const QImage &image)
+{
+ int bitsPerColor = 8;
+ int bitsPerPixel = 32;
+ if (image.depth() == 1) {
+ bitsPerColor = 1;
+ bitsPerPixel = 1;
+ }
+ QCFType<CGDataProviderRef> provider =
+ CGDataProviderCreateWithData(0, image.bits(), image.bytesPerLine() * image.height(),
+ 0);
+
+ uint cgflags = kCGImageAlphaPremultipliedFirst;
+#ifdef kCGBitmapByteOrder32Host //only needed because CGImage.h added symbols in the minor version
+ cgflags |= kCGBitmapByteOrder32Host;
+#endif
+
+ CGImageRef cgImage = CGImageCreate(image.width(), image.height(), bitsPerColor, bitsPerPixel,
+ image.bytesPerLine(),
+ QCoreGraphicsPaintEngine::macGenericColorSpace(),
+ cgflags, provider,
+ 0,
+ 0,
+ kCGRenderingIntentDefault);
+
+ return cgImage;
+}
+
+/*****************************************************************************
+ QPixmap member functions
+ *****************************************************************************/
+
+static inline QRgb qt_conv16ToRgb(ushort c) {
+ static const int qt_rbits = (565/100);
+ static const int qt_gbits = (565/10%10);
+ static const int qt_bbits = (565%10);
+ static const int qt_red_shift = qt_bbits+qt_gbits-(8-qt_rbits);
+ static const int qt_green_shift = qt_bbits-(8-qt_gbits);
+ static const int qt_neg_blue_shift = 8-qt_bbits;
+ static const int qt_blue_mask = (1<<qt_bbits)-1;
+ static const int qt_green_mask = (1<<(qt_gbits+qt_bbits))-((1<<qt_bbits)-1);
+ static const int qt_red_mask = (1<<(qt_rbits+qt_gbits+qt_bbits))-(1<<(qt_gbits+qt_bbits));
+
+ const int r=(c & qt_red_mask);
+ const int g=(c & qt_green_mask);
+ const int b=(c & qt_blue_mask);
+ const int tr = r >> qt_red_shift;
+ const int tg = g >> qt_green_shift;
+ const int tb = b << qt_neg_blue_shift;
+
+ return qRgb(tr,tg,tb);
+}
+
+QSet<QMacPixmapData*> QMacPixmapData::validDataPointers;
+
+QMacPixmapData::QMacPixmapData(PixelType type)
+ : QPixmapData(type, MacClass), has_alpha(0), has_mask(0),
+ uninit(true), pixels(0), pixelsSize(0), pixelsToFree(0),
+ bytesPerRow(0), cg_data(0), cg_dataBeingReleased(0), cg_mask(0),
+ pengine(0)
+{
+}
+
+QPixmapData *QMacPixmapData::createCompatiblePixmapData() const
+{
+ return new QMacPixmapData(pixelType());
+}
+
+#define BEST_BYTE_ALIGNMENT 16
+#define COMPTUE_BEST_BYTES_PER_ROW(bpr) \
+ (((bpr) + (BEST_BYTE_ALIGNMENT - 1)) & ~(BEST_BYTE_ALIGNMENT - 1))
+
+void QMacPixmapData::resize(int width, int height)
+{
+ setSerialNumber(++qt_pixmap_serial);
+
+ w = width;
+ h = height;
+ is_null = (w <= 0 || h <= 0);
+ d = (pixelType() == BitmapType ? 1 : 32);
+ bool make_null = w <= 0 || h <= 0; // create null pixmap
+ if (make_null || d == 0) {
+ w = 0;
+ h = 0;
+ is_null = true;
+ d = 0;
+ if (!make_null)
+ qWarning("Qt: QPixmap: Invalid pixmap parameters");
+ return;
+ }
+
+ if (w < 1 || h < 1)
+ return;
+
+ //create the pixels
+ bytesPerRow = w * sizeof(quint32); // Minimum bytes per row.
+
+ // Quartz2D likes things as a multple of 16 (for now).
+ bytesPerRow = COMPTUE_BEST_BYTES_PER_ROW(bytesPerRow);
+ macCreatePixels();
+}
+
+#undef COMPUTE_BEST_BYTES_PER_ROW
+
+void QMacPixmapData::fromImage(const QImage &img,
+ Qt::ImageConversionFlags flags)
+{
+ setSerialNumber(++qt_pixmap_serial);
+
+ // the conversion code only handles format >=
+ // Format_ARGB32_Premultiplied at the moment..
+ if (img.format() > QImage::Format_ARGB32_Premultiplied) {
+ QImage image;
+ if (img.hasAlphaChannel())
+ image = img.convertToFormat(QImage::Format_ARGB32_Premultiplied);
+ else
+ image = img.convertToFormat(QImage::Format_RGB32);
+ fromImage(image, flags);
+ return;
+ }
+
+ w = img.width();
+ h = img.height();
+ is_null = (w <= 0 || h <= 0);
+ d = (pixelType() == BitmapType ? 1 : img.depth());
+
+ QImage image = img;
+ int dd = QPixmap::defaultDepth();
+ bool force_mono = (dd == 1 ||
+ (flags & Qt::ColorMode_Mask)==Qt::MonoOnly);
+ if (force_mono) { // must be monochrome
+ if (d != 1) {
+ image = image.convertToFormat(QImage::Format_MonoLSB, flags); // dither
+ d = 1;
+ }
+ } else { // can be both
+ bool conv8 = false;
+ if(d > 8 && dd <= 8) { // convert to 8 bit
+ if ((flags & Qt::DitherMode_Mask) == Qt::AutoDither)
+ flags = (flags & ~Qt::DitherMode_Mask)
+ | Qt::PreferDither;
+ conv8 = true;
+ } else if ((flags & Qt::ColorMode_Mask) == Qt::ColorOnly) {
+ conv8 = d == 1; // native depth wanted
+ } else if (d == 1) {
+ if (image.colorCount() == 2) {
+ QRgb c0 = image.color(0); // Auto: convert to best
+ QRgb c1 = image.color(1);
+ conv8 = qMin(c0,c1) != qRgb(0,0,0) || qMax(c0,c1) != qRgb(255,255,255);
+ } else {
+ // eg. 1-color monochrome images (they do exist).
+ conv8 = true;
+ }
+ }
+ if (conv8) {
+ image = image.convertToFormat(QImage::Format_Indexed8, flags);
+ d = 8;
+ }
+ }
+
+ if (image.depth()==1) {
+ image.setColor(0, QColor(Qt::color0).rgba());
+ image.setColor(1, QColor(Qt::color1).rgba());
+ }
+
+ if (d == 16 || d == 24) {
+ image = image.convertToFormat(QImage::Format_RGB32, flags);
+ fromImage(image, flags);
+ return;
+ }
+
+ // different size or depth, make a new pixmap
+ resize(w, h);
+
+ quint32 *dptr = pixels, *drow;
+ const uint dbpr = bytesPerRow;
+
+ const QImage::Format sfmt = image.format();
+ const unsigned short sbpr = image.bytesPerLine();
+
+ // use const_cast to prevent a detach
+ const uchar *sptr = const_cast<const QImage &>(image).bits(), *srow;
+
+ for (int y = 0; y < h; ++y) {
+ drow = dptr + (y * (dbpr / 4));
+ srow = sptr + (y * sbpr);
+ switch(sfmt) {
+ case QImage::Format_MonoLSB:
+ case QImage::Format_Mono:{
+ for (int x = 0; x < w; ++x) {
+ char one_bit = *(srow + (x / 8));
+ if (sfmt == QImage::Format_Mono)
+ one_bit = one_bit >> (7 - (x % 8));
+ else
+ one_bit = one_bit >> (x % 8);
+ if ((one_bit & 0x01))
+ *(drow+x) = 0xFF000000;
+ else
+ *(drow+x) = 0xFFFFFFFF;
+ }
+ break;
+ }
+ case QImage::Format_Indexed8: {
+ int numColors = image.numColors();
+ if (numColors > 0) {
+ for (int x = 0; x < w; ++x) {
+ int index = *(srow + x);
+ *(drow+x) = PREMUL(image.color(qMin(index, numColors)));
+ }
+ }
+ } break;
+ case QImage::Format_RGB32:
+ for (int x = 0; x < w; ++x)
+ *(drow+x) = *(((quint32*)srow) + x) | 0xFF000000;
+ break;
+ case QImage::Format_ARGB32:
+ case QImage::Format_ARGB32_Premultiplied:
+ for (int x = 0; x < w; ++x) {
+ if(sfmt == QImage::Format_RGB32)
+ *(drow+x) = 0xFF000000 | (*(((quint32*)srow) + x) & 0x00FFFFFF);
+ else if(sfmt == QImage::Format_ARGB32_Premultiplied)
+ *(drow+x) = *(((quint32*)srow) + x);
+ else
+ *(drow+x) = PREMUL(*(((quint32*)srow) + x));
+ }
+ break;
+ default:
+ qWarning("Qt: internal: Oops: Forgot a format [%d] %s:%d", sfmt,
+ __FILE__, __LINE__);
+ break;
+ }
+ }
+ if (sfmt != QImage::Format_RGB32) { //setup the alpha
+ bool alphamap = image.depth() == 32;
+ if (sfmt == QImage::Format_Indexed8) {
+ const QVector<QRgb> rgb = image.colorTable();
+ for (int i = 0, count = image.colorCount(); i < count; ++i) {
+ const int alpha = qAlpha(rgb[i]);
+ if (alpha != 0xff) {
+ alphamap = true;
+ break;
+ }
+ }
+ }
+ macSetHasAlpha(alphamap);
+ }
+ uninit = false;
+}
+
+int get_index(QImage * qi,QRgb mycol)
+{
+ int loopc;
+ for(loopc=0;loopc<qi->colorCount();loopc++) {
+ if(qi->color(loopc)==mycol)
+ return loopc;
+ }
+ qi->setColorCount(qi->colorCount()+1);
+ qi->setColor(qi->colorCount(),mycol);
+ return qi->colorCount();
+}
+
+QImage QMacPixmapData::toImage() const
+{
+ QImage::Format format = QImage::Format_MonoLSB;
+ if (d != 1) //Doesn't support index color modes
+ format = (has_alpha ? QImage::Format_ARGB32_Premultiplied :
+ QImage::Format_RGB32);
+
+ QImage image(w, h, format);
+ quint32 *sptr = pixels, *srow;
+ const uint sbpr = bytesPerRow;
+ if (format == QImage::Format_MonoLSB) {
+ image.fill(0);
+ image.setColorCount(2);
+ image.setColor(0, QColor(Qt::color0).rgba());
+ image.setColor(1, QColor(Qt::color1).rgba());
+ for (int y = 0; y < h; ++y) {
+ uchar *scanLine = image.scanLine(y);
+ srow = sptr + (y * (sbpr/4));
+ for (int x = 0; x < w; ++x) {
+ if (!(*(srow + x) & RGB_MASK))
+ scanLine[x >> 3] |= (1 << (x & 7));
+ }
+ }
+ } else {
+ for (int y = 0; y < h; ++y) {
+ srow = sptr + (y * (sbpr / 4));
+ memcpy(image.scanLine(y), srow, w * 4);
+ }
+
+ }
+
+ return image;
+}
+
+void QMacPixmapData::fill(const QColor &fillColor)
+
+{
+ { //we don't know what backend to use so we cannot paint here
+ quint32 *dptr = pixels;
+ Q_ASSERT_X(dptr, "QPixmap::fill", "No dptr");
+ const quint32 colr = PREMUL(fillColor.rgba());
+ const int nbytes = bytesPerRow * h;
+ if (!colr) {
+ memset(dptr, 0, nbytes);
+ } else {
+ for (uint i = 0; i < nbytes / sizeof(quint32); ++i)
+ *(dptr + i) = colr;
+ }
+ }
+
+ // If we had an alpha channel from before, don't
+ // switch it off. Only go from no alpha to alpha:
+ if (fillColor.alpha() != 255)
+ macSetHasAlpha(true);
+}
+
+QPixmap QMacPixmapData::alphaChannel() const
+{
+ if (!has_alpha)
+ return QPixmap();
+
+ QMacPixmapData *alpha = new QMacPixmapData(PixmapType);
+ alpha->resize(w, h);
+ macGetAlphaChannel(alpha, false);
+ return QPixmap(alpha);
+}
+
+void QMacPixmapData::setAlphaChannel(const QPixmap &alpha)
+{
+ has_mask = true;
+ QMacPixmapData *alphaData = static_cast<QMacPixmapData*>(alpha.data.data());
+ macSetAlphaChannel(alphaData, false);
+}
+
+QBitmap QMacPixmapData::mask() const
+{
+ if (!has_mask && !has_alpha)
+ return QBitmap();
+
+ QMacPixmapData *mask = new QMacPixmapData(BitmapType);
+ mask->resize(w, h);
+ macGetAlphaChannel(mask, true);
+ return QPixmap(mask);
+}
+
+void QMacPixmapData::setMask(const QBitmap &mask)
+{
+ if (mask.isNull()) {
+ QMacPixmapData opaque(PixmapType);
+ opaque.resize(w, h);
+ opaque.fill(QColor(255, 255, 255, 255));
+ macSetAlphaChannel(&opaque, true);
+ has_alpha = has_mask = false;
+ return;
+ }
+
+ has_alpha = false;
+ has_mask = true;
+ QMacPixmapData *maskData = static_cast<QMacPixmapData*>(mask.data.data());
+ macSetAlphaChannel(maskData, true);
+}
+
+int QMacPixmapData::metric(QPaintDevice::PaintDeviceMetric theMetric) const
+{
+ switch (theMetric) {
+ case QPaintDevice::PdmWidth:
+ return w;
+ case QPaintDevice::PdmHeight:
+ return h;
+ case QPaintDevice::PdmWidthMM:
+ return qRound(metric(QPaintDevice::PdmWidth) * 25.4 / qreal(metric(QPaintDevice::PdmDpiX)));
+ case QPaintDevice::PdmHeightMM:
+ return qRound(metric(QPaintDevice::PdmHeight) * 25.4 / qreal(metric(QPaintDevice::PdmDpiY)));
+ case QPaintDevice::PdmNumColors:
+ return 1 << d;
+ case QPaintDevice::PdmDpiX:
+ case QPaintDevice::PdmPhysicalDpiX: {
+ extern float qt_mac_defaultDpi_x(); //qpaintdevice_mac.cpp
+ return int(qt_mac_defaultDpi_x());
+ }
+ case QPaintDevice::PdmDpiY:
+ case QPaintDevice::PdmPhysicalDpiY: {
+ extern float qt_mac_defaultDpi_y(); //qpaintdevice_mac.cpp
+ return int(qt_mac_defaultDpi_y());
+ }
+ case QPaintDevice::PdmDepth:
+ return d;
+ default:
+ qWarning("QPixmap::metric: Invalid metric command");
+ }
+ return 0;
+}
+
+QMacPixmapData::~QMacPixmapData()
+{
+ validDataPointers.remove(this);
+ if (cg_mask) {
+ CGImageRelease(cg_mask);
+ cg_mask = 0;
+ }
+
+ delete pengine; // Make sure we aren't drawing on the context anymore.
+ if (cg_data) {
+ CGImageRelease(cg_data);
+ } else if (!cg_dataBeingReleased && pixels != pixelsToFree) {
+ free(pixels);
+ }
+ free(pixelsToFree);
+}
+
+void QMacPixmapData::macSetAlphaChannel(const QMacPixmapData *pix, bool asMask)
+{
+ if (!pixels || !h || !w || pix->w != w || pix->h != h)
+ return;
+
+ quint32 *dptr = pixels, *drow;
+ const uint dbpr = bytesPerRow;
+ const unsigned short sbpr = pix->bytesPerRow;
+ quint32 *sptr = pix->pixels, *srow;
+ for (int y=0; y < h; ++y) {
+ drow = dptr + (y * (dbpr/4));
+ srow = sptr + (y * (sbpr/4));
+ if(d == 1) {
+ for (int x=0; x < w; ++x) {
+ if((*(srow+x) & RGB_MASK))
+ *(drow+x) = 0xFFFFFFFF;
+ }
+ } else if(d == 8) {
+ for (int x=0; x < w; ++x)
+ *(drow+x) = (*(drow+x) & RGB_MASK) | (*(srow+x) << 24);
+ } else if(asMask) {
+ for (int x=0; x < w; ++x) {
+ if(*(srow+x) & RGB_MASK)
+ *(drow+x) = (*(drow+x) & RGB_MASK);
+ else
+ *(drow+x) = (*(drow+x) & RGB_MASK) | 0xFF000000;
+ *(drow+x) = PREMUL(*(drow+x));
+ }
+ } else {
+ for (int x=0; x < w; ++x) {
+ const uchar alpha = qGray(qRed(*(srow+x)), qGreen(*(srow+x)), qBlue(*(srow+x)));
+ const uchar destAlpha = qt_div_255(alpha * qAlpha(*(drow+x)));
+#if 1
+ *(drow+x) = (*(drow+x) & RGB_MASK) | (destAlpha << 24);
+#else
+ *(drow+x) = qRgba(qt_div_255(qRed(*(drow+x) * alpha)),
+ qt_div_255(qGreen(*(drow+x) * alpha)),
+ qt_div_255(qBlue(*(drow+x) * alpha)), destAlpha);
+#endif
+ *(drow+x) = PREMUL(*(drow+x));
+ }
+ }
+ }
+ macSetHasAlpha(true);
+}
+
+void QMacPixmapData::macGetAlphaChannel(QMacPixmapData *pix, bool asMask) const
+{
+ quint32 *dptr = pix->pixels, *drow;
+ const uint dbpr = pix->bytesPerRow;
+ const unsigned short sbpr = bytesPerRow;
+ quint32 *sptr = pixels, *srow;
+ for(int y=0; y < h; ++y) {
+ drow = dptr + (y * (dbpr/4));
+ srow = sptr + (y * (sbpr/4));
+ if(asMask) {
+ for (int x = 0; x < w; ++x) {
+ if (*(srow + x) & qRgba(0, 0, 0, 255))
+ *(drow + x) = 0x00000000;
+ else
+ *(drow + x) = 0xFFFFFFFF;
+ }
+ } else {
+ for (int x = 0; x < w; ++x) {
+ const int alpha = qAlpha(*(srow + x));
+ *(drow + x) = qRgb(alpha, alpha, alpha);
+ }
+ }
+ }
+}
+
+void QMacPixmapData::macSetHasAlpha(bool b)
+{
+ has_alpha = b;
+ macReleaseCGImageRef();
+}
+
+void QMacPixmapData::macCreateCGImageRef()
+{
+ Q_ASSERT(cg_data == 0);
+ //create the cg data
+ CGColorSpaceRef colorspace = QCoreGraphicsPaintEngine::macDisplayColorSpace();
+ QCFType<CGDataProviderRef> provider = CGDataProviderCreateWithData(this,
+ pixels, bytesPerRow * h,
+ qt_mac_cgimage_data_free);
+ validDataPointers.insert(this);
+ uint cgflags = kCGImageAlphaPremultipliedFirst;
+#ifdef kCGBitmapByteOrder32Host //only needed because CGImage.h added symbols in the minor version
+ cgflags |= kCGBitmapByteOrder32Host;
+#endif
+ cg_data = CGImageCreate(w, h, 8, 32, bytesPerRow, colorspace,
+ cgflags, provider, 0, 0, kCGRenderingIntentDefault);
+}
+
+void QMacPixmapData::macReleaseCGImageRef()
+{
+ if (!cg_data)
+ return; // There's nothing we need to do
+
+ cg_dataBeingReleased = cg_data;
+ CGImageRelease(cg_data);
+ cg_data = 0;
+
+ if (pixels != pixelsToFree) {
+ macCreatePixels();
+ } else {
+ pixelsToFree = 0;
+ }
+}
+
+
+// We create our space in memory to paint on here. If we already have existing pixels
+// copy them over. This is to preserve the fact that CGImageRef's are immutable.
+void QMacPixmapData::macCreatePixels()
+{
+ const int numBytes = bytesPerRow * h;
+ quint32 *base_pixels;
+ if (pixelsToFree && pixelsToFree != pixels) {
+ // Reuse unused block of memory lying around from a previous callback.
+ base_pixels = pixelsToFree;
+ pixelsToFree = 0;
+ } else {
+ // We need a block of memory to do stuff with.
+ base_pixels = static_cast<quint32 *>(malloc(numBytes));
+ }
+
+ if (pixels)
+ memcpy(base_pixels, pixels, pixelsSize);
+ pixels = base_pixels;
+ pixelsSize = numBytes;
+}
+
+#if 0
+QPixmap QMacPixmapData::transformed(const QTransform &transform,
+ Qt::TransformationMode mode) const
+{
+ int w, h; // size of target pixmap
+ const int ws = width();
+ const int hs = height();
+
+ QTransform mat(transform.m11(), transform.m12(),
+ transform.m21(), transform.m22(), 0., 0.);
+ if (transform.m12() == 0.0F && transform.m21() == 0.0F &&
+ transform.m11() >= 0.0F && transform.m22() >= 0.0F)
+ {
+ h = int(qAbs(mat.m22()) * hs + 0.9999);
+ w = int(qAbs(mat.m11()) * ws + 0.9999);
+ h = qAbs(h);
+ w = qAbs(w);
+ } else { // rotation or shearing
+ QPolygonF a(QRectF(0,0,ws+1,hs+1));
+ a = mat.map(a);
+ QRectF r = a.boundingRect().normalized();
+ w = int(r.width() + 0.9999);
+ h = int(r.height() + 0.9999);
+ }
+ mat = QPixmap::trueMatrix(mat, ws, hs);
+ if (!h || !w)
+ return QPixmap();
+
+ // create destination
+ QMacPixmapData *pm = new QMacPixmapData(pixelType(), w, h);
+ const quint32 *sptr = pixels;
+ quint32 *dptr = pm->pixels;
+ memset(dptr, 0, (pm->bytesPerRow * pm->h));
+
+ // do the transform
+ if (mode == Qt::SmoothTransformation) {
+#warning QMacPixmapData::transformed not properly implemented
+ qWarning("QMacPixmapData::transformed not properly implemented");
+#if 0
+ QPainter p(&pm);
+ p.setRenderHint(QPainter::Antialiasing);
+ p.setRenderHint(QPainter::SmoothPixmapTransform);
+ p.setTransform(mat);
+ p.drawPixmap(0, 0, *this);
+#endif
+ } else {
+ bool invertible;
+ mat = mat.inverted(&invertible);
+ if (!invertible)
+ return QPixmap();
+
+ const int bpp = 32;
+ const int xbpl = (w * bpp) / 8;
+ if (!qt_xForm_helper(mat, 0, QT_XFORM_TYPE_MSBFIRST, bpp,
+ (uchar*)dptr, xbpl, (pm->bytesPerRow) - xbpl,
+ h, (uchar*)sptr, (bytesPerRow), ws, hs)) {
+ qWarning("QMacPixmapData::transform(): failure");
+ return QPixmap();
+ }
+ }
+
+ // update the alpha
+ pm->macSetHasAlpha(true);
+ return QPixmap(pm);
+}
+#endif
+
+QT_BEGIN_INCLUDE_NAMESPACE
+#include <OpenGL/OpenGL.h>
+#include <OpenGL/gl.h>
+QT_END_INCLUDE_NAMESPACE
+
+// Load and resolve the symbols we need from OpenGL manually so QtGui doesn't have to link against the OpenGL framework.
+typedef CGLError (*PtrCGLChoosePixelFormat)(const CGLPixelFormatAttribute *, CGLPixelFormatObj *, long *);
+typedef CGLError (*PtrCGLClearDrawable)(CGLContextObj);
+typedef CGLError (*PtrCGLCreateContext)(CGLPixelFormatObj, CGLContextObj, CGLContextObj *);
+typedef CGLError (*PtrCGLDestroyContext)(CGLContextObj);
+typedef CGLError (*PtrCGLDestroyPixelFormat)(CGLPixelFormatObj);
+typedef CGLError (*PtrCGLSetCurrentContext)(CGLContextObj);
+typedef CGLError (*PtrCGLSetFullScreen)(CGLContextObj);
+typedef void (*PtrglFinish)();
+typedef void (*PtrglPixelStorei)(GLenum, GLint);
+typedef void (*PtrglReadBuffer)(GLenum);
+typedef void (*PtrglReadPixels)(GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, GLvoid *);
+
+static PtrCGLChoosePixelFormat ptrCGLChoosePixelFormat = 0;
+static PtrCGLClearDrawable ptrCGLClearDrawable = 0;
+static PtrCGLCreateContext ptrCGLCreateContext = 0;
+static PtrCGLDestroyContext ptrCGLDestroyContext = 0;
+static PtrCGLDestroyPixelFormat ptrCGLDestroyPixelFormat = 0;
+static PtrCGLSetCurrentContext ptrCGLSetCurrentContext = 0;
+static PtrCGLSetFullScreen ptrCGLSetFullScreen = 0;
+static PtrglFinish ptrglFinish = 0;
+static PtrglPixelStorei ptrglPixelStorei = 0;
+static PtrglReadBuffer ptrglReadBuffer = 0;
+static PtrglReadPixels ptrglReadPixels = 0;
+
+static bool resolveOpenGLSymbols()
+{
+ if (ptrCGLChoosePixelFormat == 0) {
+ QLibrary library(QLatin1String("/System/Library/Frameworks/OpenGL.framework/OpenGL"));
+ ptrCGLChoosePixelFormat = (PtrCGLChoosePixelFormat)(library.resolve("CGLChoosePixelFormat"));
+ ptrCGLClearDrawable = (PtrCGLClearDrawable)(library.resolve("CGLClearDrawable"));
+ ptrCGLCreateContext = (PtrCGLCreateContext)(library.resolve("CGLCreateContext"));
+ ptrCGLDestroyContext = (PtrCGLDestroyContext)(library.resolve("CGLDestroyContext"));
+ ptrCGLDestroyPixelFormat = (PtrCGLDestroyPixelFormat)(library.resolve("CGLDestroyPixelFormat"));
+ ptrCGLSetCurrentContext = (PtrCGLSetCurrentContext)(library.resolve("CGLSetCurrentContext"));
+ ptrCGLSetFullScreen = (PtrCGLSetFullScreen)(library.resolve("CGLSetFullScreen"));
+ ptrglFinish = (PtrglFinish)(library.resolve("glFinish"));
+ ptrglPixelStorei = (PtrglPixelStorei)(library.resolve("glPixelStorei"));
+ ptrglReadBuffer = (PtrglReadBuffer)(library.resolve("glReadBuffer"));
+ ptrglReadPixels = (PtrglReadPixels)(library.resolve("glReadPixels"));
+ }
+ return ptrCGLChoosePixelFormat && ptrCGLClearDrawable && ptrCGLCreateContext
+ && ptrCGLDestroyContext && ptrCGLDestroyPixelFormat && ptrCGLSetCurrentContext
+ && ptrCGLSetFullScreen && ptrglFinish && ptrglPixelStorei
+ && ptrglReadBuffer && ptrglReadPixels;
+}
+
+// Inverts the given pixmap in the y direction.
+static void qt_mac_flipPixmap(void *data, int rowBytes, int height)
+{
+ int bottom = height - 1;
+ void *base = data;
+ void *buffer = malloc(rowBytes);
+
+ int top = 0;
+ while ( top < bottom )
+ {
+ void *topP = (void *)((top * rowBytes) + (intptr_t)base);
+ void *bottomP = (void *)((bottom * rowBytes) + (intptr_t)base);
+
+ bcopy( topP, buffer, rowBytes );
+ bcopy( bottomP, topP, rowBytes );
+ bcopy( buffer, bottomP, rowBytes );
+
+ ++top;
+ --bottom;
+ }
+ free(buffer);
+}
+
+// Grabs displayRect from display and places it into buffer.
+static void qt_mac_grabDisplayRect(CGDirectDisplayID display, const QRect &displayRect, void *buffer)
+{
+ if (display == kCGNullDirectDisplay)
+ return;
+
+ CGLPixelFormatAttribute attribs[] = {
+ kCGLPFAFullScreen,
+ kCGLPFADisplayMask,
+ (CGLPixelFormatAttribute)0, /* Display mask bit goes here */
+ (CGLPixelFormatAttribute)0
+ };
+
+ attribs[2] = (CGLPixelFormatAttribute)CGDisplayIDToOpenGLDisplayMask(display);
+
+ // Build a full-screen GL context
+ CGLPixelFormatObj pixelFormatObj;
+ long numPixelFormats;
+
+ ptrCGLChoosePixelFormat( attribs, &pixelFormatObj, &numPixelFormats );
+
+ if (!pixelFormatObj) // No full screen context support
+ return;
+
+ CGLContextObj glContextObj;
+ ptrCGLCreateContext(pixelFormatObj, 0, &glContextObj);
+ ptrCGLDestroyPixelFormat(pixelFormatObj) ;
+ if (!glContextObj)
+ return;
+
+ ptrCGLSetCurrentContext(glContextObj);
+ ptrCGLSetFullScreen(glContextObj) ;
+
+ ptrglReadBuffer(GL_FRONT);
+
+ ptrglFinish(); // Finish all OpenGL commands
+ ptrglPixelStorei(GL_PACK_ALIGNMENT, 4); // Force 4-byte alignment
+ ptrglPixelStorei(GL_PACK_ROW_LENGTH, 0);
+ ptrglPixelStorei(GL_PACK_SKIP_ROWS, 0);
+ ptrglPixelStorei(GL_PACK_SKIP_PIXELS, 0);
+
+ // Fetch the data in XRGB format, matching the bitmap context.
+ ptrglReadPixels(GLint(displayRect.x()), GLint(displayRect.y()),
+ GLint(displayRect.width()), GLint(displayRect.height()),
+#ifdef __BIG_ENDIAN__
+ GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, buffer
+#else
+ GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, buffer
+#endif
+ );
+
+ ptrCGLSetCurrentContext(0);
+ ptrCGLClearDrawable(glContextObj); // disassociate from full screen
+ ptrCGLDestroyContext(glContextObj); // and destroy the context
+}
+
+// Returns a pixmap containing the screen contents at rect.
+static QPixmap qt_mac_grabScreenRect(const QRect &rect)
+{
+ if (!resolveOpenGLSymbols())
+ return QPixmap();
+
+ const int maxDisplays = 128; // 128 displays should be enough for everyone.
+ CGDirectDisplayID displays[maxDisplays];
+ CGDisplayCount displayCount;
+ const CGRect cgRect = CGRectMake(rect.x(), rect.y(), rect.width(), rect.height());
+ const CGDisplayErr err = CGGetDisplaysWithRect(cgRect, maxDisplays, displays, &displayCount);
+
+ if (err && displayCount == 0)
+ return QPixmap();
+
+ long bytewidth = rect.width() * 4; // Assume 4 bytes/pixel for now
+ bytewidth = (bytewidth + 3) & ~3; // Align to 4 bytes
+ QVarLengthArray<char> buffer(rect.height() * bytewidth);
+
+ for (uint i = 0; i < displayCount; ++i) {
+ const CGRect bounds = CGDisplayBounds(displays[i]);
+ // Translate to display-local coordinates
+ QRect displayRect = rect.translated(qRound(-bounds.origin.x), qRound(-bounds.origin.y));
+ // Adjust for inverted y axis.
+ displayRect.moveTop(qRound(bounds.size.height) - displayRect.y() - rect.height());
+ qt_mac_grabDisplayRect(displays[i], displayRect, buffer.data());
+ }
+
+ qt_mac_flipPixmap(buffer.data(), bytewidth, rect.height());
+ QCFType<CGContextRef> bitmap = CGBitmapContextCreate(buffer.data(), rect.width(),
+ rect.height(), 8, bytewidth,
+ QCoreGraphicsPaintEngine::macGenericColorSpace(),
+ kCGImageAlphaNoneSkipFirst);
+ QCFType<CGImageRef> image = CGBitmapContextCreateImage(bitmap);
+ return QPixmap::fromMacCGImageRef(image);
+}
+
+#ifndef QT_MAC_USE_COCOA // no QuickDraw in 64-bit mode
+static QPixmap qt_mac_grabScreenRect_10_3(int x, int y, int w, int h, QWidget *widget)
+{
+ QPixmap pm = QPixmap(w, h);
+ extern WindowPtr qt_mac_window_for(const QWidget *); // qwidget_mac.cpp
+ const BitMap *windowPort = 0;
+ if((widget->windowType() == Qt::Desktop)) {
+ GDHandle gdh;
+ if(!(gdh=GetMainDevice()))
+ qDebug("Qt: internal: Unexpected condition reached: %s:%d", __FILE__, __LINE__);
+ windowPort = (BitMap*)(*(*gdh)->gdPMap);
+ } else {
+ windowPort = GetPortBitMapForCopyBits(GetWindowPort(qt_mac_window_for(widget)));
+ }
+ const BitMap *pixmapPort = GetPortBitMapForCopyBits(static_cast<GWorldPtr>(pm.macQDHandle()));
+ Rect macSrcRect, macDstRect;
+ SetRect(&macSrcRect, x, y, x + w, y + h);
+ SetRect(&macDstRect, 0, 0, w, h);
+ CopyBits(windowPort, pixmapPort, &macSrcRect, &macDstRect, srcCopy, 0);
+ return pm;
+}
+#endif
+
+QPixmap QPixmap::grabWindow(WId window, int x, int y, int w, int h)
+{
+ QWidget *widget = QWidget::find(window);
+ if (widget == 0)
+ return QPixmap();
+
+ if(w == -1)
+ w = widget->width() - x;
+ if(h == -1)
+ h = widget->height() - y;
+
+ QPoint globalCoord(0, 0);
+ globalCoord = widget->mapToGlobal(globalCoord);
+ QRect rect(globalCoord.x() + x, globalCoord.y() + y, w, h);
+
+#ifdef QT_MAC_USE_COCOA
+ return qt_mac_grabScreenRect(rect);
+#else
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) {
+ return qt_mac_grabScreenRect(rect);
+ } else
+#endif
+ {
+ return qt_mac_grabScreenRect_10_3(x, y, w, h, widget);
+ }
+#endif // ifdef Q_WS_MAC64
+}
+
+/*! \internal
+
+ Returns the QuickDraw CGrafPtr of the pixmap. 0 is returned if it can't
+ be obtained. Do not hold the pointer around for long as it can be
+ relocated.
+
+ \warning This function is only available on Mac OS X.
+ \warning As of Qt 4.6, this function \e{always} returns zero.
+*/
+
+Qt::HANDLE QPixmap::macQDHandle() const
+{
+ return 0;
+}
+
+/*! \internal
+
+ Returns the QuickDraw CGrafPtr of the pixmap's alpha channel. 0 is
+ returned if it can't be obtained. Do not hold the pointer around for
+ long as it can be relocated.
+
+ \warning This function is only available on Mac OS X.
+ \warning As of Qt 4.6, this function \e{always} returns zero.
+*/
+
+Qt::HANDLE QPixmap::macQDAlphaHandle() const
+{
+ return 0;
+}
+
+/*! \internal
+
+ Returns the CoreGraphics CGContextRef of the pixmap. 0 is returned if
+ it can't be obtained. It is the caller's responsiblity to
+ CGContextRelease the context when finished using it.
+
+ \warning This function is only available on Mac OS X.
+*/
+
+Qt::HANDLE QPixmap::macCGHandle() const
+{
+ if (isNull())
+ return 0;
+
+ if (data->classId() == QPixmapData::MacClass) {
+ QMacPixmapData *d = static_cast<QMacPixmapData *>(data.data());
+ if (!d->cg_data)
+ d->macCreateCGImageRef();
+ CGImageRef ret = d->cg_data;
+ CGImageRetain(ret);
+ return ret;
+ } else if (data->classId() == QPixmapData::RasterClass) {
+ return qt_mac_image_to_cgimage(static_cast<QRasterPixmapData *>(data.data())->image);
+ }
+ return 0;
+}
+
+bool QMacPixmapData::hasAlphaChannel() const
+{
+ return has_alpha;
+}
+
+CGImageRef qt_mac_create_imagemask(const QPixmap &pixmap, const QRectF &sr)
+{
+ QMacPixmapData *px = static_cast<QMacPixmapData*>(pixmap.data.data());
+ if (px->cg_mask) {
+ if (px->cg_mask_rect == sr) {
+ CGImageRetain(px->cg_mask); //reference for the caller
+ return px->cg_mask;
+ }
+ CGImageRelease(px->cg_mask);
+ px->cg_mask = 0;
+ }
+
+ const int sx = qRound(sr.x()), sy = qRound(sr.y()), sw = qRound(sr.width()), sh = qRound(sr.height());
+ const int sbpr = px->bytesPerRow;
+ const uint nbytes = sw * sh;
+ // alpha is always 255 for bitmaps, ignore it in this case.
+ const quint32 mask = px->depth() == 1 ? 0x00ffffff : 0xffffffff;
+ quint8 *dptr = static_cast<quint8 *>(malloc(nbytes));
+ quint32 *sptr = px->pixels, *srow;
+ for(int y = sy, offset=0; y < sh; ++y) {
+ srow = sptr + (y * (sbpr / 4));
+ for(int x = sx; x < sw; ++x)
+ *(dptr+(offset++)) = (*(srow+x) & mask) ? 255 : 0;
+ }
+ QCFType<CGDataProviderRef> provider = CGDataProviderCreateWithData(0, dptr, nbytes, qt_mac_cgimage_data_free);
+ px->cg_mask = CGImageMaskCreate(sw, sh, 8, 8, nbytes / sh, provider, 0, 0);
+ px->cg_mask_rect = sr;
+ CGImageRetain(px->cg_mask); //reference for the caller
+ return px->cg_mask;
+}
+
+#ifndef QT_MAC_USE_COCOA
+IconRef qt_mac_create_iconref(const QPixmap &px)
+{
+ if (px.isNull())
+ return 0;
+
+ //create icon
+ IconFamilyHandle iconFamily = reinterpret_cast<IconFamilyHandle>(NewHandle(0));
+ //create data
+ {
+ struct {
+ OSType mac_type;
+ int width, height, depth;
+ bool mask;
+ } images[] = {
+ { kThumbnail32BitData, 128, 128, 32, false },
+ { kThumbnail8BitMask, 128, 128, 8, true },
+ { 0, 0, 0, 0, false } //end marker
+ };
+ for(int i = 0; images[i].mac_type; i++) {
+ //get QPixmap data
+ QImage scaled_px = px.toImage().scaled(images[i].width, images[i].height);
+
+ quint32 *sptr = (quint32 *) scaled_px.bits();
+ quint32 *srow;
+ uint sbpr = scaled_px.bytesPerLine();
+
+ //get Handle data
+ const int dbpr = images[i].width * (images[i].depth/8);
+ Handle hdl = NewHandle(dbpr*images[i].height);
+ if(!sptr) { //handle null pixmap
+ memset((*hdl), '\0', dbpr*images[i].height);
+ } else if(images[i].mask) {
+ if(images[i].mac_type == kThumbnail8BitMask) {
+ for(int y = 0, hindex = 0; y < images[i].height; ++y) {
+ srow = sptr + (y * (sbpr/4));
+ for(int x = 0; x < images[i].width; ++x)
+ *((*hdl)+(hindex++)) = qAlpha(*(srow+x));
+ }
+ }
+ } else {
+ char *dest = (*hdl);
+#if defined(__i386__)
+ if(images[i].depth == 32) {
+ for(int y = 0; y < images[i].height; ++y) {
+ uint *source = (uint*)((const uchar*)sptr+(sbpr*y));
+ for(int x = 0; x < images[i].width; ++x, dest += 4)
+ *((uint*)dest) = CFSwapInt32(*(source + x));
+ }
+ } else
+#endif
+ {
+ for(int y = 0; y < images[i].height; ++y)
+ memcpy(dest+(y*dbpr), ((const uchar*)sptr+(sbpr*y)), dbpr);
+ }
+ }
+
+ //set the family data to the Handle
+ OSStatus set = SetIconFamilyData(iconFamily, images[i].mac_type, hdl);
+ if(set != noErr)
+ qWarning("%s: %d -- Unable to create icon data[%d]!! %ld",
+ __FILE__, __LINE__, i, long(set));
+ DisposeHandle(hdl);
+ }
+ }
+
+ //acquire and cleanup
+ IconRef ret;
+ static int counter = 0;
+ const OSType kQtCreator = 'CUTE';
+ RegisterIconRefFromIconFamily(kQtCreator, (OSType)counter, iconFamily, &ret);
+ AcquireIconRef(ret);
+ UnregisterIconRef(kQtCreator, (OSType)counter);
+ DisposeHandle(reinterpret_cast<Handle>(iconFamily));
+ counter++;
+ return ret;
+
+}
+#endif
+
+/*! \internal */
+QPaintEngine* QMacPixmapData::paintEngine() const
+{
+ if (!pengine) {
+ QMacPixmapData *that = const_cast<QMacPixmapData*>(this);
+ that->pengine = new QCoreGraphicsPaintEngine();
+ }
+ return pengine;
+}
+
+void QMacPixmapData::copy(const QPixmapData *data, const QRect &rect)
+{
+ if (data->pixelType() == BitmapType) {
+ QBitmap::fromImage(toImage().copy(rect));
+ return;
+ }
+
+ const QMacPixmapData *macData = static_cast<const QMacPixmapData*>(data);
+
+ resize(rect.width(), rect.height());
+
+ has_alpha = macData->has_alpha;
+ has_mask = macData->has_mask;
+ uninit = false;
+
+ const int x = rect.x();
+ const int y = rect.y();
+ char *dest = reinterpret_cast<char*>(pixels);
+ const char *src = reinterpret_cast<const char*>(macData->pixels + x) + y * macData->bytesPerRow;
+ for (int i = 0; i < h; ++i) {
+ memcpy(dest, src, w * 4);
+ dest += bytesPerRow;
+ src += macData->bytesPerRow;
+ }
+
+ has_alpha = macData->has_alpha;
+ has_mask = macData->has_mask;
+}
+
+bool QMacPixmapData::scroll(int dx, int dy, const QRect &rect)
+{
+ Q_UNUSED(dx);
+ Q_UNUSED(dy);
+ Q_UNUSED(rect);
+ return false;
+}
+
+/*!
+ \since 4.2
+
+ Creates a \c CGImageRef equivalent to the QPixmap. Returns the \c CGImageRef handle.
+
+ It is the caller's responsibility to release the \c CGImageRef data
+ after use.
+
+ \warning This function is only available on Mac OS X.
+
+ \sa fromMacCGImageRef()
+*/
+CGImageRef QPixmap::toMacCGImageRef() const
+{
+ return (CGImageRef)macCGHandle();
+}
+
+/*!
+ \since 4.2
+
+ Returns a QPixmap that is equivalent to the given \a image.
+
+ \warning This function is only available on Mac OS X.
+
+ \sa toMacCGImageRef(), {QPixmap#Pixmap Conversion}{Pixmap Conversion}
+*/
+QPixmap QPixmap::fromMacCGImageRef(CGImageRef image)
+{
+ const size_t w = CGImageGetWidth(image),
+ h = CGImageGetHeight(image);
+ QPixmap ret(w, h);
+ ret.fill(Qt::transparent);
+ CGRect rect = CGRectMake(0, 0, w, h);
+ CGContextRef ctx = qt_mac_cg_context(&ret);
+ qt_mac_drawCGImage(ctx, &rect, image);
+ CGContextRelease(ctx);
+ return ret;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/image/qpixmap_mac_p.h b/src/gui/image/qpixmap_mac_p.h
new file mode 100644
index 0000000000..307e38aceb
--- /dev/null
+++ b/src/gui/image/qpixmap_mac_p.h
@@ -0,0 +1,134 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPIXMAP_MAC_P_H
+#define QPIXMAP_MAC_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/private/qpixmapdata_p.h>
+#include <QtGui/private/qpixmapdatafactory_p.h>
+#include <QtGui/private/qt_mac_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QMacPixmapData : public QPixmapData
+{
+public:
+ QMacPixmapData(PixelType type);
+ ~QMacPixmapData();
+
+ QPixmapData *createCompatiblePixmapData() const;
+
+ void resize(int width, int height);
+ void fromImage(const QImage &image, Qt::ImageConversionFlags flags);
+ void copy(const QPixmapData *data, const QRect &rect);
+ bool scroll(int dx, int dy, const QRect &rect);
+
+ int metric(QPaintDevice::PaintDeviceMetric metric) const;
+ void fill(const QColor &color);
+ QBitmap mask() const;
+ void setMask(const QBitmap &mask);
+ bool hasAlphaChannel() const;
+// QPixmap transformed(const QTransform &matrix,
+// Qt::TransformationMode mode) const;
+ void setAlphaChannel(const QPixmap &alphaChannel);
+ QPixmap alphaChannel() const;
+ QImage toImage() const;
+ QPaintEngine* paintEngine() const;
+
+private:
+
+ uint has_alpha : 1, has_mask : 1, uninit : 1;
+
+ void macSetHasAlpha(bool b);
+ void macGetAlphaChannel(QMacPixmapData *, bool asMask) const;
+ void macSetAlphaChannel(const QMacPixmapData *, bool asMask);
+ void macCreateCGImageRef();
+ void macCreatePixels();
+ void macReleaseCGImageRef();
+ /*
+ pixels stores the pixmap data. pixelsToFree is either 0 or some memory
+ block that was bound to a CGImageRef and released, and for which the
+ release callback has been called. There are two uses to pixelsToFree:
+
+ 1. If pixels == pixelsToFree, then we know that the CGImageRef is done\
+ with the data and we can modify pixels without breaking CGImageRef's
+ mutability invariant.
+
+ 2. If pixels != pixelsToFree and pixelsToFree != 0, then we can reuse
+ pixelsToFree later on instead of malloc'ing memory.
+ */
+ quint32 *pixels;
+ uint pixelsSize;
+ quint32 *pixelsToFree;
+ uint bytesPerRow;
+ QRectF cg_mask_rect;
+ CGImageRef cg_data, cg_dataBeingReleased, cg_mask;
+ static QSet<QMacPixmapData*> validDataPointers;
+
+ QPaintEngine *pengine;
+
+ friend class QPixmap;
+ friend class QRasterBuffer;
+ friend class QRasterPaintEngine;
+ friend class QCoreGraphicsPaintEngine;
+ friend CGImageRef qt_mac_create_imagemask(const QPixmap&, const QRectF&);
+ friend quint32 *qt_mac_pixmap_get_base(const QPixmap*);
+ friend int qt_mac_pixmap_get_bytes_per_line(const QPixmap*);
+ friend void qt_mac_cgimage_data_free(void *, const void*, size_t);
+ friend IconRef qt_mac_create_iconref(const QPixmap&);
+ friend CGContextRef qt_mac_cg_context(const QPaintDevice*);
+ friend QColor qcolorForThemeTextColor(ThemeTextColor themeColor);
+};
+
+QT_END_NAMESPACE
+
+#endif // QPIXMAP_MAC_P_H
diff --git a/src/gui/image/qpixmap_qpa.cpp b/src/gui/image/qpixmap_qpa.cpp
new file mode 100644
index 0000000000..61be2169d0
--- /dev/null
+++ b/src/gui/image/qpixmap_qpa.cpp
@@ -0,0 +1,49 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qpixmap.h>
+#include <private/qgraphicssystem_p.h>
+#include <private/qapplication_p.h>
+
+QPixmap QPixmap::grabWindow(WId window, int x, int y, int w, int h)
+{
+ return QApplicationPrivate::platformIntegration()->grabWindow(window, x, y, w, h);
+}
diff --git a/src/gui/image/qpixmap_qws.cpp b/src/gui/image/qpixmap_qws.cpp
new file mode 100644
index 0000000000..3c1907089d
--- /dev/null
+++ b/src/gui/image/qpixmap_qws.cpp
@@ -0,0 +1,160 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qpixmap.h>
+#include <qapplication.h>
+#include <qwidget.h>
+#include <qdesktopwidget.h>
+#include <qscreen_qws.h>
+#include <qwsdisplay_qws.h>
+#include <private/qdrawhelper_p.h>
+#include <private/qpixmap_raster_p.h>
+
+
+QT_BEGIN_NAMESPACE
+
+QPixmap QPixmap::grabWindow(WId window, int x, int y, int w, int h)
+{
+ QWidget *widget = QWidget::find(window);
+ if (!widget)
+ return QPixmap();
+
+ QRect grabRect = widget->frameGeometry();
+ if (!widget->isWindow())
+ grabRect.translate(widget->parentWidget()->mapToGlobal(QPoint()));
+ if (w < 0)
+ w = widget->width() - x;
+ if (h < 0)
+ h = widget->height() - y;
+ grabRect &= QRect(x, y, w, h).translated(widget->mapToGlobal(QPoint()));
+
+ QScreen *screen = qt_screen;
+ QDesktopWidget *desktop = QApplication::desktop();
+ if (!desktop)
+ return QPixmap();
+ if (desktop->numScreens() > 1) {
+ const int screenNo = desktop->screenNumber(widget);
+ if (screenNo != -1)
+ screen = qt_screen->subScreens().at(screenNo);
+ grabRect = grabRect.translated(-screen->region().boundingRect().topLeft());
+ }
+
+ if (screen->pixelFormat() == QImage::Format_Invalid) {
+ qWarning("QPixmap::grabWindow(): Unable to copy pixels from framebuffer");
+ return QPixmap();
+ }
+
+ if (screen->isTransformed()) {
+ const QSize screenSize(screen->width(), screen->height());
+ grabRect = screen->mapToDevice(grabRect, screenSize);
+ }
+
+ QWSDisplay::grab(false);
+ QPixmap pixmap;
+ QImage img(screen->base(),
+ screen->deviceWidth(), screen->deviceHeight(),
+ screen->linestep(), screen->pixelFormat());
+ img = img.copy(grabRect);
+ QWSDisplay::ungrab();
+
+ if (screen->isTransformed()) {
+ QMatrix matrix;
+ switch (screen->transformOrientation()) {
+ case 1: matrix.rotate(90); break;
+ case 2: matrix.rotate(180); break;
+ case 3: matrix.rotate(270); break;
+ default: break;
+ }
+ img = img.transformed(matrix);
+ }
+
+ if (screen->pixelType() == QScreen::BGRPixel)
+ img = img.rgbSwapped();
+
+ return QPixmap::fromImage(img);
+}
+
+QRgb* QPixmap::clut() const
+{
+ if (data && data->classId() == QPixmapData::RasterClass) {
+ const QRasterPixmapData *d = static_cast<const QRasterPixmapData*>(data.data());
+ return d->image.colorTable().data();
+ }
+
+ return 0;
+}
+
+int QPixmap::numCols() const
+{
+ return colorCount();
+}
+
+int QPixmap::colorCount() const
+{
+ if (data && data->classId() == QPixmapData::RasterClass) {
+ const QRasterPixmapData *d = static_cast<const QRasterPixmapData*>(data.data());
+ return d->image.colorCount();
+ }
+
+ return 0;
+}
+
+const uchar* QPixmap::qwsBits() const
+{
+ if (data && data->classId() == QPixmapData::RasterClass) {
+ const QRasterPixmapData *d = static_cast<const QRasterPixmapData*>(data.data());
+ return d->image.bits();
+ }
+
+ return 0;
+}
+
+int QPixmap::qwsBytesPerLine() const
+{
+ if (data && data->classId() == QPixmapData::RasterClass) {
+ const QRasterPixmapData *d = static_cast<const QRasterPixmapData*>(data.data());
+ return d->image.bytesPerLine();
+ }
+
+ return 0;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/image/qpixmap_raster.cpp b/src/gui/image/qpixmap_raster.cpp
new file mode 100644
index 0000000000..0a4d921795
--- /dev/null
+++ b/src/gui/image/qpixmap_raster.cpp
@@ -0,0 +1,492 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpixmap.h"
+
+#include <private/qfont_p.h>
+
+#include "qpixmap_raster_p.h"
+#include "qnativeimage_p.h"
+#include "qimage_p.h"
+#include "qpaintengine.h"
+
+#include "qbitmap.h"
+#include "qimage.h"
+#include <QBuffer>
+#include <QImageReader>
+#include <private/qimage_p.h>
+#include <private/qsimd_p.h>
+#include <private/qwidget_p.h>
+#include <private/qdrawhelper_p.h>
+
+QT_BEGIN_NAMESPACE
+
+const uchar qt_pixmap_bit_mask[] = { 0x01, 0x02, 0x04, 0x08,
+ 0x10, 0x20, 0x40, 0x80 };
+
+QPixmap qt_toRasterPixmap(const QImage &image)
+{
+ QPixmapData *data =
+ new QRasterPixmapData(image.depth() == 1
+ ? QPixmapData::BitmapType
+ : QPixmapData::PixmapType);
+
+ data->fromImage(image, Qt::AutoColor);
+
+ return QPixmap(data);
+}
+
+QPixmap qt_toRasterPixmap(const QPixmap &pixmap)
+{
+ if (pixmap.isNull())
+ return QPixmap();
+
+ if (QPixmap(pixmap).data_ptr()->classId() == QPixmapData::RasterClass)
+ return pixmap;
+
+ return qt_toRasterPixmap(pixmap.toImage());
+}
+
+QRasterPixmapData::QRasterPixmapData(PixelType type)
+ : QPixmapData(type, RasterClass)
+{
+}
+
+QRasterPixmapData::~QRasterPixmapData()
+{
+}
+
+QPixmapData *QRasterPixmapData::createCompatiblePixmapData() const
+{
+ return new QRasterPixmapData(pixelType());
+}
+
+void QRasterPixmapData::resize(int width, int height)
+{
+ QImage::Format format;
+#ifdef Q_WS_QWS
+ if (pixelType() == BitmapType) {
+ format = QImage::Format_Mono;
+ } else {
+ format = QScreen::instance()->pixelFormat();
+ if (format == QImage::Format_Invalid)
+ format = QImage::Format_ARGB32_Premultiplied;
+ else if (format == QImage::Format_Indexed8) // currently not supported
+ format = QImage::Format_RGB444;
+ }
+#else
+ if (pixelType() == BitmapType)
+ format = QImage::Format_MonoLSB;
+ else
+ format = QNativeImage::systemFormat();
+#endif
+
+ image = QImage(width, height, format);
+ w = width;
+ h = height;
+ d = image.depth();
+ is_null = (w <= 0 || h <= 0);
+
+ if (pixelType() == BitmapType && !image.isNull()) {
+ image.setColorCount(2);
+ image.setColor(0, QColor(Qt::color0).rgba());
+ image.setColor(1, QColor(Qt::color1).rgba());
+ }
+
+ setSerialNumber(image.serialNumber());
+}
+
+bool QRasterPixmapData::fromData(const uchar *buffer, uint len, const char *format,
+ Qt::ImageConversionFlags flags)
+{
+ QByteArray a = QByteArray::fromRawData(reinterpret_cast<const char *>(buffer), len);
+ QBuffer b(&a);
+ b.open(QIODevice::ReadOnly);
+ QImage image = QImageReader(&b, format).read();
+ if (image.isNull())
+ return false;
+
+ createPixmapForImage(image, flags, /* inplace = */true);
+ return !isNull();
+}
+
+void QRasterPixmapData::fromImage(const QImage &sourceImage,
+ Qt::ImageConversionFlags flags)
+{
+ Q_UNUSED(flags);
+ QImage image = sourceImage;
+ createPixmapForImage(image, flags, /* inplace = */false);
+}
+
+void QRasterPixmapData::fromImageReader(QImageReader *imageReader,
+ Qt::ImageConversionFlags flags)
+{
+ Q_UNUSED(flags);
+ QImage image = imageReader->read();
+ if (image.isNull())
+ return;
+
+ createPixmapForImage(image, flags, /* inplace = */true);
+}
+
+// from qwindowsurface.cpp
+extern void qt_scrollRectInImage(QImage &img, const QRect &rect, const QPoint &offset);
+
+void QRasterPixmapData::copy(const QPixmapData *data, const QRect &rect)
+{
+ fromImage(data->toImage(rect).copy(), Qt::NoOpaqueDetection);
+}
+
+bool QRasterPixmapData::scroll(int dx, int dy, const QRect &rect)
+{
+ if (!image.isNull())
+ qt_scrollRectInImage(image, rect, QPoint(dx, dy));
+ return true;
+}
+
+void QRasterPixmapData::fill(const QColor &color)
+{
+ uint pixel;
+
+ if (image.depth() == 1) {
+ int gray = qGray(color.rgba());
+ // Pick the best approximate color in the image's colortable.
+ if (qAbs(qGray(image.color(0)) - gray) < qAbs(qGray(image.color(1)) - gray))
+ pixel = 0;
+ else
+ pixel = 1;
+ } else if (image.depth() >= 15) {
+ int alpha = color.alpha();
+ if (alpha != 255) {
+ if (!image.hasAlphaChannel()) {
+ QImage::Format toFormat;
+#if !(defined(QT_HAVE_NEON) || defined(QT_ALWAYS_HAVE_SSE2))
+ if (image.format() == QImage::Format_RGB16)
+ toFormat = QImage::Format_ARGB8565_Premultiplied;
+ else if (image.format() == QImage::Format_RGB666)
+ toFormat = QImage::Format_ARGB6666_Premultiplied;
+ else if (image.format() == QImage::Format_RGB555)
+ toFormat = QImage::Format_ARGB8555_Premultiplied;
+ else if (image.format() == QImage::Format_RGB444)
+ toFormat = QImage::Format_ARGB4444_Premultiplied;
+ else
+#endif
+ toFormat = QImage::Format_ARGB32_Premultiplied;
+
+ if (!image.isNull() && qt_depthForFormat(image.format()) == qt_depthForFormat(toFormat)) {
+ image.detach();
+ image.d->format = toFormat;
+ } else {
+ image = QImage(image.width(), image.height(), toFormat);
+ }
+ }
+
+ switch (image.format()) {
+ case QImage::Format_ARGB8565_Premultiplied:
+ pixel = qargb8565(color.rgba()).rawValue();
+ break;
+ case QImage::Format_ARGB8555_Premultiplied:
+ pixel = qargb8555(color.rgba()).rawValue();
+ break;
+ case QImage::Format_ARGB6666_Premultiplied:
+ pixel = qargb6666(color.rgba()).rawValue();
+ break;
+ case QImage::Format_ARGB4444_Premultiplied:
+ pixel = qargb4444(color.rgba()).rawValue();
+ break;
+ default:
+ pixel = PREMUL(color.rgba());
+ break;
+ }
+ } else {
+ switch (image.format()) {
+ case QImage::Format_RGB16:
+ pixel = qt_colorConvert<quint16, quint32>(color.rgba(), 0);
+ break;
+ case QImage::Format_RGB444:
+ pixel = qrgb444(color.rgba()).rawValue();
+ break;
+ case QImage::Format_RGB555:
+ pixel = qrgb555(color.rgba()).rawValue();
+ break;
+ case QImage::Format_RGB666:
+ pixel = qrgb666(color.rgba()).rawValue();
+ break;
+ case QImage::Format_RGB888:
+ pixel = qrgb888(color.rgba()).rawValue();
+ break;
+ default:
+ pixel = color.rgba();
+ break;
+ }
+ }
+ } else {
+ pixel = 0;
+ // ### what about 8 bits
+ }
+
+ image.fill(pixel);
+}
+
+void QRasterPixmapData::setMask(const QBitmap &mask)
+{
+ if (mask.size().isEmpty()) {
+ if (image.depth() != 1) { // hw: ????
+ image = image.convertToFormat(QImage::Format_RGB32);
+ }
+ } else {
+ const int w = image.width();
+ const int h = image.height();
+
+ switch (image.depth()) {
+ case 1: {
+ const QImage imageMask = mask.toImage().convertToFormat(image.format());
+ for (int y = 0; y < h; ++y) {
+ const uchar *mscan = imageMask.scanLine(y);
+ uchar *tscan = image.scanLine(y);
+ int bytesPerLine = image.bytesPerLine();
+ for (int i = 0; i < bytesPerLine; ++i)
+ tscan[i] &= mscan[i];
+ }
+ break;
+ }
+ default: {
+ const QImage imageMask = mask.toImage().convertToFormat(QImage::Format_MonoLSB);
+ image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
+ for (int y = 0; y < h; ++y) {
+ const uchar *mscan = imageMask.scanLine(y);
+ QRgb *tscan = (QRgb *)image.scanLine(y);
+ for (int x = 0; x < w; ++x) {
+ if (!(mscan[x>>3] & qt_pixmap_bit_mask[x&7]))
+ tscan[x] = 0;
+ }
+ }
+ break;
+ }
+ }
+ }
+}
+
+bool QRasterPixmapData::hasAlphaChannel() const
+{
+ return image.hasAlphaChannel();
+}
+
+QImage QRasterPixmapData::toImage() const
+{
+ if (!image.isNull()) {
+ QImageData *data = const_cast<QImage &>(image).data_ptr();
+ if (data->paintEngine && data->paintEngine->isActive()
+ && data->paintEngine->paintDevice() == &image)
+ {
+ return image.copy();
+ }
+ }
+
+ return image;
+}
+
+QImage QRasterPixmapData::toImage(const QRect &rect) const
+{
+ if (rect.isNull())
+ return image;
+
+ QRect clipped = rect.intersected(QRect(0, 0, w, h));
+ if (d % 8 == 0)
+ return QImage(image.scanLine(clipped.y()) + clipped.x() * (d / 8),
+ clipped.width(), clipped.height(),
+ image.bytesPerLine(), image.format());
+ else
+ return image.copy(clipped);
+}
+
+void QRasterPixmapData::setAlphaChannel(const QPixmap &alphaChannel)
+{
+ image.setAlphaChannel(alphaChannel.toImage());
+}
+
+QPaintEngine* QRasterPixmapData::paintEngine() const
+{
+ return image.paintEngine();
+}
+
+int QRasterPixmapData::metric(QPaintDevice::PaintDeviceMetric metric) const
+{
+ QImageData *d = image.d;
+ if (!d)
+ return 0;
+
+ // override the image dpi with the screen dpi when rendering to a pixmap
+ switch (metric) {
+ case QPaintDevice::PdmWidth:
+ return w;
+ case QPaintDevice::PdmHeight:
+ return h;
+ case QPaintDevice::PdmWidthMM:
+ return qRound(d->width * 25.4 / qt_defaultDpiX());
+ case QPaintDevice::PdmHeightMM:
+ return qRound(d->height * 25.4 / qt_defaultDpiY());
+ case QPaintDevice::PdmNumColors:
+ return d->colortable.size();
+ case QPaintDevice::PdmDepth:
+ return this->d;
+ case QPaintDevice::PdmDpiX: // fall-through
+ case QPaintDevice::PdmPhysicalDpiX:
+ return qt_defaultDpiX();
+ case QPaintDevice::PdmDpiY: // fall-through
+ case QPaintDevice::PdmPhysicalDpiY:
+ return qt_defaultDpiY();
+ default:
+ qWarning("QRasterPixmapData::metric(): Unhandled metric type %d", metric);
+ break;
+ }
+
+ return 0;
+}
+
+void QRasterPixmapData::createPixmapForImage(QImage &sourceImage, Qt::ImageConversionFlags flags, bool inPlace)
+{
+ QImage::Format format;
+ if (flags & Qt::NoFormatConversion)
+ format = sourceImage.format();
+ else
+#ifdef Q_WS_QWS
+ if (pixelType() == BitmapType) {
+ format = QImage::Format_Mono;
+ } else {
+ format = QScreen::instance()->pixelFormat();
+ if (format == QImage::Format_Invalid)
+ format = QImage::Format_ARGB32_Premultiplied;
+ else if (format == QImage::Format_Indexed8) // currently not supported
+ format = QImage::Format_RGB444;
+ }
+
+ if (sourceImage.hasAlphaChannel()
+ && ((flags & Qt::NoOpaqueDetection)
+ || const_cast<QImage &>(sourceImage).data_ptr()->checkForAlphaPixels())) {
+ switch (format) {
+ case QImage::Format_RGB16:
+ format = QImage::Format_ARGB8565_Premultiplied;
+ break;
+ case QImage::Format_RGB666:
+ format = QImage::Format_ARGB6666_Premultiplied;
+ break;
+ case QImage::Format_RGB555:
+ format = QImage::Format_ARGB8555_Premultiplied;
+ break;
+ case QImage::Format_RGB444:
+ format = QImage::Format_ARGB4444_Premultiplied;
+ break;
+ default:
+ format = QImage::Format_ARGB32_Premultiplied;
+ break;
+ }
+ } else if (format == QImage::Format_Invalid) {
+ format = QImage::Format_ARGB32_Premultiplied;
+ }
+#else
+ if (pixelType() == BitmapType) {
+ format = QImage::Format_MonoLSB;
+ } else {
+ if (sourceImage.depth() == 1) {
+ format = sourceImage.hasAlphaChannel()
+ ? QImage::Format_ARGB32_Premultiplied
+ : QImage::Format_RGB32;
+ } else {
+ QImage::Format opaqueFormat = QNativeImage::systemFormat();
+ QImage::Format alphaFormat = QImage::Format_ARGB32_Premultiplied;
+
+#if !defined(QT_HAVE_NEON) && !defined(QT_ALWAYS_HAVE_SSE2)
+ switch (opaqueFormat) {
+ case QImage::Format_RGB16:
+ alphaFormat = QImage::Format_ARGB8565_Premultiplied;
+ break;
+ default: // We don't care about the others...
+ break;
+ }
+#endif
+
+ if (!sourceImage.hasAlphaChannel()) {
+ format = opaqueFormat;
+ } else if ((flags & Qt::NoOpaqueDetection) == 0
+ && !const_cast<QImage &>(sourceImage).data_ptr()->checkForAlphaPixels())
+ {
+ // image has alpha format but is really opaque, so try to do a
+ // more efficient conversion
+ if (sourceImage.format() == QImage::Format_ARGB32
+ || sourceImage.format() == QImage::Format_ARGB32_Premultiplied)
+ {
+ if (!inPlace)
+ sourceImage.detach();
+ sourceImage.d->format = QImage::Format_RGB32;
+ }
+ format = opaqueFormat;
+ } else {
+ format = alphaFormat;
+ }
+ }
+ }
+#endif
+
+ if (inPlace && sourceImage.d->convertInPlace(format, flags)) {
+ image = sourceImage;
+ } else {
+ image = sourceImage.convertToFormat(format);
+ }
+
+ if (image.d) {
+ w = image.d->width;
+ h = image.d->height;
+ d = image.d->depth;
+ } else {
+ w = h = d = 0;
+ }
+ is_null = (w <= 0 || h <= 0);
+
+ setSerialNumber(image.serialNumber());
+}
+
+QImage* QRasterPixmapData::buffer()
+{
+ return &image;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/image/qpixmap_raster_p.h b/src/gui/image/qpixmap_raster_p.h
new file mode 100644
index 0000000000..88bdb32569
--- /dev/null
+++ b/src/gui/image/qpixmap_raster_p.h
@@ -0,0 +1,107 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPIXMAPDATA_RASTER_P_H
+#define QPIXMAPDATA_RASTER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/private/qpixmapdata_p.h>
+#include <QtGui/private/qpixmapdatafactory_p.h>
+
+#ifdef Q_WS_WIN
+# include "qt_windows.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class Q_GUI_EXPORT QRasterPixmapData : public QPixmapData
+{
+public:
+ QRasterPixmapData(PixelType type);
+ ~QRasterPixmapData();
+
+ QPixmapData *createCompatiblePixmapData() const;
+
+ void resize(int width, int height);
+ void fromFile(const QString &filename, Qt::ImageConversionFlags flags);
+ bool fromData(const uchar *buffer, uint len, const char *format, Qt::ImageConversionFlags flags);
+ void fromImage(const QImage &image, Qt::ImageConversionFlags flags);
+ void fromImageReader(QImageReader *imageReader, Qt::ImageConversionFlags flags);
+
+ void copy(const QPixmapData *data, const QRect &rect);
+ bool scroll(int dx, int dy, const QRect &rect);
+ void fill(const QColor &color);
+ void setMask(const QBitmap &mask);
+ bool hasAlphaChannel() const;
+ void setAlphaChannel(const QPixmap &alphaChannel);
+ QImage toImage() const;
+ QImage toImage(const QRect &rect) const;
+ QPaintEngine* paintEngine() const;
+ QImage* buffer();
+
+protected:
+ int metric(QPaintDevice::PaintDeviceMetric metric) const;
+ void createPixmapForImage(QImage &sourceImage, Qt::ImageConversionFlags flags, bool inPlace);
+ void setImage(const QImage &image);
+ QImage image;
+
+private:
+ friend class QPixmap;
+ friend class QBitmap;
+ friend class QPixmapCacheEntry;
+ friend class QRasterPaintEngine;
+};
+
+QT_END_NAMESPACE
+
+#endif // QPIXMAPDATA_RASTER_P_H
+
+
diff --git a/src/gui/image/qpixmap_s60.cpp b/src/gui/image/qpixmap_s60.cpp
new file mode 100644
index 0000000000..c8aa003ffa
--- /dev/null
+++ b/src/gui/image/qpixmap_s60.cpp
@@ -0,0 +1,1040 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <exception>
+#include <w32std.h>
+#include <fbs.h>
+
+#include <private/qapplication_p.h>
+#include <private/qgraphicssystem_p.h>
+#include <private/qt_s60_p.h>
+#include <private/qpaintengine_s60_p.h>
+#include <private/qfont_p.h>
+
+#include "qpixmap.h"
+#include "qpixmap_raster_p.h"
+#include <qwidget.h>
+#include "qpixmap_s60_p.h"
+#include "qnativeimage_p.h"
+#include "qbitmap.h"
+#include "qimage.h"
+#include "qimage_p.h"
+
+#include <fbs.h>
+
+QT_BEGIN_NAMESPACE
+
+const uchar qt_pixmap_bit_mask[] = { 0x01, 0x02, 0x04, 0x08,
+ 0x10, 0x20, 0x40, 0x80 };
+
+static bool cleanup_function_registered = false;
+static QS60PixmapData *firstPixmap = 0;
+
+// static
+void QS60PixmapData::qt_symbian_register_pixmap(QS60PixmapData *pd)
+{
+ if (!cleanup_function_registered) {
+ qAddPostRoutine(qt_symbian_release_pixmaps);
+ cleanup_function_registered = true;
+ }
+
+ pd->next = firstPixmap;
+ pd->prev = 0;
+ if (firstPixmap)
+ firstPixmap->prev = pd;
+ firstPixmap = pd;
+}
+
+// static
+void QS60PixmapData::qt_symbian_unregister_pixmap(QS60PixmapData *pd)
+{
+ if (pd->next)
+ pd->next->prev = pd->prev;
+ if (pd->prev)
+ pd->prev->next = pd->next;
+ else
+ firstPixmap = pd->next;
+}
+
+// static
+void QS60PixmapData::qt_symbian_release_pixmaps()
+{
+ // Scan all QS60PixmapData objects in the system and destroy them.
+ QS60PixmapData *pd = firstPixmap;
+ while (pd != 0) {
+ pd->release();
+ pd = pd->next;
+ }
+}
+
+/*
+ \class QSymbianFbsClient
+ \since 4.6
+ \internal
+
+ Symbian Font And Bitmap server client that is
+ used to lock the global bitmap heap. Only used in
+ S60 v3.1 and S60 v3.2.
+*/
+_LIT(KFBSERVLargeBitmapAccessName,"FbsLargeBitmapAccess");
+class QSymbianFbsClient
+{
+public:
+
+ QSymbianFbsClient() : heapLocked(false)
+ {
+ heapLock.OpenGlobal(KFBSERVLargeBitmapAccessName);
+ }
+
+ ~QSymbianFbsClient()
+ {
+ heapLock.Close();
+ }
+
+ bool lockHeap()
+ {
+ bool wasLocked = heapLocked;
+
+ if (heapLock.Handle() && !heapLocked) {
+ heapLock.Wait();
+ heapLocked = true;
+ }
+
+ return wasLocked;
+ }
+
+ bool unlockHeap()
+ {
+ bool wasLocked = heapLocked;
+
+ if (heapLock.Handle() && heapLocked) {
+ heapLock.Signal();
+ heapLocked = false;
+ }
+
+ return wasLocked;
+ }
+
+
+private:
+
+ RMutex heapLock;
+ bool heapLocked;
+};
+
+Q_GLOBAL_STATIC(QSymbianFbsClient, qt_symbianFbsClient);
+
+
+
+// QSymbianFbsHeapLock
+
+QSymbianFbsHeapLock::QSymbianFbsHeapLock(LockAction a)
+: action(a), wasLocked(false)
+{
+ QSysInfo::SymbianVersion symbianVersion = QSysInfo::symbianVersion();
+ if (symbianVersion == QSysInfo::SV_9_2 || symbianVersion == QSysInfo::SV_9_3)
+ wasLocked = qt_symbianFbsClient()->unlockHeap();
+}
+
+QSymbianFbsHeapLock::~QSymbianFbsHeapLock()
+{
+ // Do nothing
+}
+
+void QSymbianFbsHeapLock::relock()
+{
+ QSysInfo::SymbianVersion symbianVersion = QSysInfo::symbianVersion();
+ if (wasLocked && (symbianVersion == QSysInfo::SV_9_2 || symbianVersion == QSysInfo::SV_9_3))
+ qt_symbianFbsClient()->lockHeap();
+}
+
+/*
+ \class QSymbianBitmapDataAccess
+ \since 4.6
+ \internal
+
+ Data access class that is used to locks/unlocks pixel data
+ when drawing or modifying CFbsBitmap pixel data.
+*/
+class QSymbianBitmapDataAccess
+{
+public:
+
+ static int heapRefCount;
+ QSysInfo::SymbianVersion symbianVersion;
+
+ explicit QSymbianBitmapDataAccess()
+ {
+ symbianVersion = QSysInfo::symbianVersion();
+ };
+
+ ~QSymbianBitmapDataAccess() {};
+
+ inline void beginDataAccess(CFbsBitmap *bitmap)
+ {
+ if (symbianVersion == QSysInfo::SV_9_2) {
+ if (heapRefCount == 0)
+ qt_symbianFbsClient()->lockHeap();
+ } else {
+ bitmap->LockHeap(ETrue);
+ }
+
+ heapRefCount++;
+ }
+
+ inline void endDataAccess(CFbsBitmap *bitmap)
+ {
+ heapRefCount--;
+
+ if (symbianVersion == QSysInfo::SV_9_2) {
+ if (heapRefCount == 0)
+ qt_symbianFbsClient()->unlockHeap();
+ } else {
+ bitmap->UnlockHeap(ETrue);
+ }
+ }
+};
+
+int QSymbianBitmapDataAccess::heapRefCount = 0;
+
+
+#define UPDATE_BUFFER() \
+ { \
+ beginDataAccess(); \
+ endDataAccess(); \
+}
+
+
+static CFbsBitmap* createSymbianCFbsBitmap(const TSize& size, TDisplayMode mode)
+{
+ QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock);
+
+ CFbsBitmap* bitmap = 0;
+ QT_TRAP_THROWING(bitmap = new (ELeave) CFbsBitmap);
+
+ if (bitmap->Create(size, mode) != KErrNone) {
+ delete bitmap;
+ bitmap = 0;
+ }
+
+ lock.relock();
+
+ return bitmap;
+}
+
+static CFbsBitmap* uncompress(CFbsBitmap* bitmap)
+{
+ if(bitmap->IsCompressedInRAM()) {
+ QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock);
+
+ CFbsBitmap *uncompressed = 0;
+ QT_TRAP_THROWING(uncompressed = new (ELeave) CFbsBitmap);
+
+ if (uncompressed->Create(bitmap->SizeInPixels(), bitmap->DisplayMode()) != KErrNone) {
+ delete bitmap;
+ bitmap = 0;
+ lock.relock();
+
+ return bitmap;
+ }
+
+ lock.relock();
+
+ CFbsBitmapDevice* bitmapDevice = 0;
+ CFbsBitGc *bitmapGc = 0;
+ QT_TRAP_THROWING(bitmapDevice = CFbsBitmapDevice::NewL(uncompressed));
+ QT_TRAP_THROWING(bitmapGc = CFbsBitGc::NewL());
+ bitmapGc->Activate(bitmapDevice);
+
+ bitmapGc->BitBlt(TPoint(), bitmap);
+
+ delete bitmapGc;
+ delete bitmapDevice;
+
+ return uncompressed;
+ } else {
+ return bitmap;
+ }
+}
+
+QPixmap QPixmap::grabWindow(WId winId, int x, int y, int w, int h)
+{
+ CWsScreenDevice* screenDevice = S60->screenDevice();
+ TSize screenSize = screenDevice->SizeInPixels();
+
+ TSize srcSize;
+ // Find out if this is one of our windows.
+ QSymbianControl *sControl;
+ sControl = winId->MopGetObject(sControl);
+ if (sControl && sControl->widget()->windowType() == Qt::Desktop) {
+ // Grabbing desktop widget
+ srcSize = screenSize;
+ } else {
+ TPoint relativePos = winId->PositionRelativeToScreen();
+ x += relativePos.iX;
+ y += relativePos.iY;
+ srcSize = winId->Size();
+ }
+
+ TRect srcRect(TPoint(x, y), srcSize);
+ // Clip to the screen
+ srcRect.Intersection(TRect(screenSize));
+
+ if (w > 0 && h > 0) {
+ TRect subRect(TPoint(x, y), TSize(w, h));
+ // Clip to the subRect
+ srcRect.Intersection(subRect);
+ }
+
+ if (srcRect.IsEmpty())
+ return QPixmap();
+
+ CFbsBitmap* temporary = createSymbianCFbsBitmap(srcRect.Size(), screenDevice->DisplayMode());
+
+ QPixmap pix;
+
+ if (temporary && screenDevice->CopyScreenToBitmap(temporary, srcRect) == KErrNone) {
+ pix = QPixmap::fromSymbianCFbsBitmap(temporary);
+ }
+
+ delete temporary;
+ return pix;
+}
+
+/*!
+ \fn CFbsBitmap *QPixmap::toSymbianCFbsBitmap() const
+ \since 4.6
+
+ Creates a \c CFbsBitmap that is equivalent to the QPixmap. Internally this
+ function will try to duplicate the handle instead of copying the data,
+ however in scenarios where this is not possible the data will be copied.
+ If the creation fails or the pixmap is null, then this function returns 0.
+
+ It is the caller's responsibility to release the \c CFbsBitmap data
+ after use either by deleting the bitmap or calling \c Reset().
+
+ \warning On S60 3.1 and S60 3.2, semi-transparent pixmaps are always copied
+ and not duplicated.
+ \warning This function is only available on Symbian OS.
+
+ \sa fromSymbianCFbsBitmap()
+*/
+CFbsBitmap *QPixmap::toSymbianCFbsBitmap() const
+{
+ QPixmapData *data = pixmapData();
+ if (!data || data->isNull())
+ return 0;
+
+ return reinterpret_cast<CFbsBitmap*>(data->toNativeType(QPixmapData::FbsBitmap));
+}
+
+/*!
+ \fn QPixmap QPixmap::fromSymbianCFbsBitmap(CFbsBitmap *bitmap)
+ \since 4.6
+
+ Creates a QPixmap from a \c CFbsBitmap \a bitmap. Internally this function
+ will try to duplicate the bitmap handle instead of copying the data, however
+ in scenarios where this is not possible the data will be copied.
+ To be sure that QPixmap does not modify your original instance, you should
+ make a copy of your \c CFbsBitmap before calling this function.
+ If the CFbsBitmap is not valid this function will return a null QPixmap.
+ For performance reasons it is recommended to use a \a bitmap with a display
+ mode of EColor16MAP or EColor16MU whenever possible.
+
+ \warning This function is only available on Symbian OS.
+
+ \sa toSymbianCFbsBitmap(), {QPixmap#Pixmap Conversion}{Pixmap Conversion}
+*/
+QPixmap QPixmap::fromSymbianCFbsBitmap(CFbsBitmap *bitmap)
+{
+ if (!bitmap)
+ return QPixmap();
+
+ QScopedPointer<QPixmapData> data(QPixmapData::create(0,0, QPixmapData::PixmapType));
+ data->fromNativeType(reinterpret_cast<void*>(bitmap), QPixmapData::FbsBitmap);
+ QPixmap pixmap(data.take());
+ return pixmap;
+}
+
+QS60PixmapData::QS60PixmapData(PixelType type) : QRasterPixmapData(type),
+ symbianBitmapDataAccess(new QSymbianBitmapDataAccess),
+ cfbsBitmap(0),
+ pengine(0),
+ bytes(0),
+ formatLocked(false),
+ next(0),
+ prev(0)
+{
+ qt_symbian_register_pixmap(this);
+}
+
+QS60PixmapData::~QS60PixmapData()
+{
+ release();
+ delete symbianBitmapDataAccess;
+ qt_symbian_unregister_pixmap(this);
+}
+
+void QS60PixmapData::resize(int width, int height)
+{
+ if (width <= 0 || height <= 0) {
+ w = width;
+ h = height;
+ is_null = true;
+
+ release();
+ return;
+ } else if (!cfbsBitmap) {
+ TDisplayMode mode;
+ if (pixelType() == BitmapType)
+ mode = EGray2;
+ else
+ mode = EColor16MU;
+
+ CFbsBitmap* bitmap = createSymbianCFbsBitmap(TSize(width, height), mode);
+ fromSymbianBitmap(bitmap);
+ } else {
+
+ TSize newSize(width, height);
+
+ if(cfbsBitmap->SizeInPixels() != newSize) {
+ cfbsBitmap->Resize(TSize(width, height));
+ if(pengine) {
+ delete pengine;
+ pengine = 0;
+ }
+ }
+
+ UPDATE_BUFFER();
+ }
+}
+
+void QS60PixmapData::release()
+{
+ if (cfbsBitmap) {
+ QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock);
+ delete cfbsBitmap;
+ lock.relock();
+ }
+
+ delete pengine;
+ image = QImage();
+ cfbsBitmap = 0;
+ pengine = 0;
+ bytes = 0;
+}
+
+/*!
+ * Takes ownership of bitmap. Used by window surface
+ */
+void QS60PixmapData::fromSymbianBitmap(CFbsBitmap* bitmap, bool lockFormat)
+{
+ Q_ASSERT(bitmap);
+
+ release();
+
+ cfbsBitmap = bitmap;
+ formatLocked = lockFormat;
+
+ setSerialNumber(cfbsBitmap->Handle());
+
+ UPDATE_BUFFER();
+
+ // Create default palette if needed
+ if (cfbsBitmap->DisplayMode() == EGray2) {
+ image.setColorCount(2);
+ image.setColor(0, QColor(Qt::color0).rgba());
+ image.setColor(1, QColor(Qt::color1).rgba());
+
+ //Symbian thinks set pixels are white/transparent, Qt thinks they are foreground/solid
+ //So invert mono bitmaps so that masks work correctly.
+ image.invertPixels();
+ } else if (cfbsBitmap->DisplayMode() == EGray256) {
+ for (int i=0; i < 256; ++i)
+ image.setColor(i, qRgb(i, i, i));
+ } else if (cfbsBitmap->DisplayMode() == EColor256) {
+ const TColor256Util *palette = TColor256Util::Default();
+ for (int i=0; i < 256; ++i)
+ image.setColor(i, (QRgb)(palette->Color256(i).Value()));
+ }
+}
+
+QImage QS60PixmapData::toImage(const QRect &r) const
+{
+ QS60PixmapData *that = const_cast<QS60PixmapData*>(this);
+ that->beginDataAccess();
+ QImage copy = that->image.copy(r);
+ that->endDataAccess();
+
+ return copy;
+}
+
+void QS60PixmapData::fromImage(const QImage &img, Qt::ImageConversionFlags flags)
+{
+ release();
+
+ QImage sourceImage;
+
+ if (pixelType() == BitmapType) {
+ sourceImage = img.convertToFormat(QImage::Format_MonoLSB);
+ } else {
+ if (img.depth() == 1) {
+ sourceImage = img.hasAlphaChannel()
+ ? img.convertToFormat(QImage::Format_ARGB32_Premultiplied)
+ : img.convertToFormat(QImage::Format_RGB32);
+ } else {
+
+ QImage::Format opaqueFormat = QNativeImage::systemFormat();
+ QImage::Format alphaFormat = QImage::Format_ARGB32_Premultiplied;
+
+ if (!img.hasAlphaChannel()
+ || ((flags & Qt::NoOpaqueDetection) == 0
+ && !const_cast<QImage &>(img).data_ptr()->checkForAlphaPixels())) {
+ sourceImage = img.convertToFormat(opaqueFormat);
+ } else {
+ sourceImage = img.convertToFormat(alphaFormat);
+ }
+ }
+ }
+
+
+ QImage::Format destFormat = sourceImage.format();
+ TDisplayMode mode;
+ switch (destFormat) {
+ case QImage::Format_MonoLSB:
+ mode = EGray2;
+ break;
+ case QImage::Format_RGB32:
+ mode = EColor16MU;
+ break;
+ case QImage::Format_ARGB32_Premultiplied:
+ if (S60->supportsPremultipliedAlpha) {
+ mode = Q_SYMBIAN_ECOLOR16MAP;
+ break;
+ } else {
+ destFormat = QImage::Format_ARGB32;
+ }
+ // Fall through intended
+ case QImage::Format_ARGB32:
+ mode = EColor16MA;
+ break;
+ case QImage::Format_Invalid:
+ return;
+ default:
+ qWarning("Image format not supported: %d", image.format());
+ return;
+ }
+
+ cfbsBitmap = createSymbianCFbsBitmap(TSize(sourceImage.width(), sourceImage.height()), mode);
+ if (!cfbsBitmap) {
+ qWarning("Could not create CFbsBitmap");
+ release();
+ return;
+ }
+
+ setSerialNumber(cfbsBitmap->Handle());
+
+ const uchar *sptr = const_cast<const QImage &>(sourceImage).bits();
+ symbianBitmapDataAccess->beginDataAccess(cfbsBitmap);
+ uchar *dptr = (uchar*)cfbsBitmap->DataAddress();
+ Mem::Copy(dptr, sptr, sourceImage.byteCount());
+ symbianBitmapDataAccess->endDataAccess(cfbsBitmap);
+
+ UPDATE_BUFFER();
+
+ if (destFormat == QImage::Format_MonoLSB) {
+ image.setColorCount(2);
+ image.setColor(0, QColor(Qt::color0).rgba());
+ image.setColor(1, QColor(Qt::color1).rgba());
+ } else {
+ image.setColorTable(sourceImage.colorTable());
+ }
+}
+
+void QS60PixmapData::copy(const QPixmapData *data, const QRect &rect)
+{
+ const QS60PixmapData *s60Data = static_cast<const QS60PixmapData*>(data);
+ fromImage(s60Data->toImage(rect), Qt::AutoColor | Qt::OrderedAlphaDither);
+}
+
+bool QS60PixmapData::scroll(int dx, int dy, const QRect &rect)
+{
+ beginDataAccess();
+ bool res = QRasterPixmapData::scroll(dx, dy, rect);
+ endDataAccess();
+ return res;
+}
+
+int QS60PixmapData::metric(QPaintDevice::PaintDeviceMetric metric) const
+{
+ if (!cfbsBitmap)
+ return 0;
+
+ switch (metric) {
+ case QPaintDevice::PdmWidth:
+ return cfbsBitmap->SizeInPixels().iWidth;
+ case QPaintDevice::PdmHeight:
+ return cfbsBitmap->SizeInPixels().iHeight;
+ case QPaintDevice::PdmWidthMM:
+ return qRound(cfbsBitmap->SizeInPixels().iWidth * 25.4 / qt_defaultDpiX());
+ case QPaintDevice::PdmHeightMM:
+ return qRound(cfbsBitmap->SizeInPixels().iHeight * 25.4 / qt_defaultDpiY());
+ case QPaintDevice::PdmNumColors:
+ return TDisplayModeUtils::NumDisplayModeColors(cfbsBitmap->DisplayMode());
+ case QPaintDevice::PdmDpiX:
+ case QPaintDevice::PdmPhysicalDpiX:
+ return qt_defaultDpiX();
+ case QPaintDevice::PdmDpiY:
+ case QPaintDevice::PdmPhysicalDpiY:
+ return qt_defaultDpiY();
+ case QPaintDevice::PdmDepth:
+ return TDisplayModeUtils::NumDisplayModeBitsPerPixel(cfbsBitmap->DisplayMode());
+ default:
+ qWarning("QPixmap::metric: Invalid metric command");
+ }
+ return 0;
+
+}
+
+void QS60PixmapData::fill(const QColor &color)
+{
+ if (color.alpha() != 255) {
+ QImage im(width(), height(), QImage::Format_ARGB32_Premultiplied);
+ im.fill(PREMUL(color.rgba()));
+ release();
+ fromImage(im, Qt::AutoColor | Qt::OrderedAlphaDither);
+ } else {
+ beginDataAccess();
+ QRasterPixmapData::fill(color);
+ endDataAccess();
+ }
+}
+
+void QS60PixmapData::setMask(const QBitmap &mask)
+{
+ if (mask.size().isEmpty()) {
+ if (image.depth() != 1) {
+ QImage newImage = image.convertToFormat(QImage::Format_RGB32);
+ release();
+ fromImage(newImage, Qt::AutoColor | Qt::OrderedAlphaDither);
+ }
+ } else if (image.depth() == 1) {
+ beginDataAccess();
+ QRasterPixmapData::setMask(mask);
+ endDataAccess();
+ } else {
+ const int w = image.width();
+ const int h = image.height();
+
+ const QImage imageMask = mask.toImage().convertToFormat(QImage::Format_MonoLSB);
+ QImage newImage = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
+ for (int y = 0; y < h; ++y) {
+ const uchar *mscan = imageMask.scanLine(y);
+ QRgb *tscan = (QRgb *)newImage.scanLine(y);
+ for (int x = 0; x < w; ++x) {
+ if (!(mscan[x>>3] & qt_pixmap_bit_mask[x&7]))
+ tscan[x] = 0;
+ }
+ }
+ release();
+ fromImage(newImage, Qt::AutoColor | Qt::OrderedAlphaDither);
+ }
+}
+
+void QS60PixmapData::setAlphaChannel(const QPixmap &alphaChannel)
+{
+ QImage img(toImage());
+ img.setAlphaChannel(alphaChannel.toImage());
+ release();
+ fromImage(img, Qt::OrderedDither | Qt::OrderedAlphaDither);
+}
+
+QImage QS60PixmapData::toImage() const
+{
+ return toImage(QRect());
+}
+
+QPaintEngine* QS60PixmapData::paintEngine() const
+{
+ if (!pengine) {
+ QS60PixmapData *that = const_cast<QS60PixmapData*>(this);
+ that->pengine = new QS60PaintEngine(&that->image, that);
+ }
+ return pengine;
+}
+
+void QS60PixmapData::beginDataAccess()
+{
+ if(!cfbsBitmap)
+ return;
+
+ symbianBitmapDataAccess->beginDataAccess(cfbsBitmap);
+
+ uchar* newBytes = (uchar*)cfbsBitmap->DataAddress();
+
+ TSize size = cfbsBitmap->SizeInPixels();
+
+ if (newBytes == bytes && image.width() == size.iWidth && image.height() == size.iHeight)
+ return;
+
+ bytes = newBytes;
+ TDisplayMode mode = cfbsBitmap->DisplayMode();
+ QImage::Format format = qt_TDisplayMode2Format(mode);
+ // On S60 3.1, premultiplied alpha pixels are stored in a bitmap with 16MA type.
+ // S60 window surface needs backing store pixmap for transparent window in ARGB32 format.
+ // In that case formatLocked is true.
+ if (!formatLocked && format == QImage::Format_ARGB32)
+ format = QImage::Format_ARGB32_Premultiplied; // pixel data is actually in premultiplied format
+
+ QVector<QRgb> savedColorTable;
+ if (!image.isNull())
+ savedColorTable = image.colorTable();
+
+ image = QImage(bytes, size.iWidth, size.iHeight, format);
+
+ // Restore the palette or create a default
+ if (!savedColorTable.isEmpty()) {
+ image.setColorTable(savedColorTable);
+ }
+
+ w = size.iWidth;
+ h = size.iHeight;
+ d = image.depth();
+ is_null = (w <= 0 || h <= 0);
+
+ if (pengine) {
+ QS60PaintEngine *engine = static_cast<QS60PaintEngine *>(pengine);
+ engine->prepare(&image);
+ }
+}
+
+void QS60PixmapData::endDataAccess(bool readOnly) const
+{
+ Q_UNUSED(readOnly);
+
+ if(!cfbsBitmap)
+ return;
+
+ symbianBitmapDataAccess->endDataAccess(cfbsBitmap);
+}
+
+/*!
+ \since 4.6
+
+ Returns a QPixmap that wraps given \a sgImage graphics resource.
+ The data should be valid even when original RSgImage handle has been
+ closed.
+
+ \warning This function is only available on Symbian OS.
+
+ \sa toSymbianRSgImage(), {QPixmap#Pixmap Conversion}{Pixmap Conversion}
+*/
+
+QPixmap QPixmap::fromSymbianRSgImage(RSgImage *sgImage)
+{
+ // It is expected that RSgImage will
+ // CURRENTLY be used in conjuction with
+ // OpenVG graphics system
+ //
+ // Surely things might change in future
+
+ if (!sgImage)
+ return QPixmap();
+
+ QScopedPointer<QPixmapData> data(QPixmapData::create(0,0, QPixmapData::PixmapType));
+ data->fromNativeType(reinterpret_cast<void*>(sgImage), QPixmapData::SgImage);
+ QPixmap pixmap(data.take());
+ return pixmap;
+}
+
+/*!
+\since 4.6
+
+Returns a \c RSgImage that is equivalent to the QPixmap by copying the data.
+
+It is the caller's responsibility to close/delete the \c RSgImage after use.
+
+\warning This function is only available on Symbian OS.
+
+\sa fromSymbianRSgImage()
+*/
+
+RSgImage *QPixmap::toSymbianRSgImage() const
+{
+ // It is expected that RSgImage will
+ // CURRENTLY be used in conjuction with
+ // OpenVG graphics system
+ //
+ // Surely things might change in future
+
+ if (isNull())
+ return 0;
+
+ RSgImage *sgImage = reinterpret_cast<RSgImage*>(pixmapData()->toNativeType(QPixmapData::SgImage));
+
+ return sgImage;
+}
+
+void* QS60PixmapData::toNativeType(NativeType type)
+{
+ if (type == QPixmapData::SgImage) {
+ return 0;
+ } else if (type == QPixmapData::FbsBitmap) {
+
+ if (isNull() || !cfbsBitmap)
+ return 0;
+
+ bool convertToArgb32 = false;
+ bool needsCopy = false;
+
+ if (!(S60->supportsPremultipliedAlpha)) {
+ // Convert argb32_premultiplied to argb32 since Symbian 9.2 does
+ // not support premultipied format.
+
+ if (image.format() == QImage::Format_ARGB32_Premultiplied) {
+ needsCopy = true;
+ convertToArgb32 = true;
+ }
+ }
+
+ CFbsBitmap *bitmap = 0;
+
+ TDisplayMode displayMode = cfbsBitmap->DisplayMode();
+
+ if(displayMode == EGray2) {
+ //Symbian thinks set pixels are white/transparent, Qt thinks they are foreground/solid
+ //So invert mono bitmaps so that masks work correctly.
+ beginDataAccess();
+ image.invertPixels();
+ endDataAccess();
+ needsCopy = true;
+ }
+
+ if (needsCopy) {
+ QImage source;
+
+ if (convertToArgb32) {
+ beginDataAccess();
+ source = image.convertToFormat(QImage::Format_ARGB32);
+ endDataAccess();
+ displayMode = EColor16MA;
+ } else {
+ source = image;
+ }
+
+ CFbsBitmap *newBitmap = createSymbianCFbsBitmap(TSize(source.width(), source.height()), displayMode);
+ const uchar *sptr = source.bits();
+ symbianBitmapDataAccess->beginDataAccess(newBitmap);
+
+ uchar *dptr = (uchar*)newBitmap->DataAddress();
+ Mem::Copy(dptr, sptr, source.byteCount());
+
+ symbianBitmapDataAccess->endDataAccess(newBitmap);
+
+ bitmap = newBitmap;
+ } else {
+
+ QT_TRAP_THROWING(bitmap = new (ELeave) CFbsBitmap);
+
+ TInt err = bitmap->Duplicate(cfbsBitmap->Handle());
+ if (err != KErrNone) {
+ qWarning("Could not duplicate CFbsBitmap");
+ delete bitmap;
+ bitmap = 0;
+ }
+ }
+
+ if(displayMode == EGray2) {
+ // restore pixels
+ beginDataAccess();
+ image.invertPixels();
+ endDataAccess();
+ }
+
+ return reinterpret_cast<void*>(bitmap);
+
+ }
+
+ return 0;
+}
+
+void QS60PixmapData::fromNativeType(void* pixmap, NativeType nativeType)
+{
+ if (nativeType == QPixmapData::SgImage) {
+ return;
+ } else if (nativeType == QPixmapData::FbsBitmap && pixmap) {
+
+ CFbsBitmap *bitmap = reinterpret_cast<CFbsBitmap*>(pixmap);
+
+ bool deleteSourceBitmap = false;
+ bool needsCopy = false;
+
+#ifdef Q_SYMBIAN_HAS_EXTENDED_BITMAP_TYPE
+
+ // Rasterize extended bitmaps
+
+ TUid extendedBitmapType = bitmap->ExtendedBitmapType();
+ if (extendedBitmapType != KNullUid) {
+ CFbsBitmap *rasterBitmap = createSymbianCFbsBitmap(bitmap->SizeInPixels(), EColor16MA);
+
+ CFbsBitmapDevice *rasterBitmapDev = 0;
+ QT_TRAP_THROWING(rasterBitmapDev = CFbsBitmapDevice::NewL(rasterBitmap));
+
+ CFbsBitGc *rasterBitmapGc = 0;
+ TInt err = rasterBitmapDev->CreateContext(rasterBitmapGc);
+ if (err != KErrNone) {
+ delete rasterBitmap;
+ delete rasterBitmapDev;
+ rasterBitmapDev = 0;
+ return;
+ }
+
+ rasterBitmapGc->BitBlt(TPoint( 0, 0), bitmap);
+
+ bitmap = rasterBitmap;
+
+ delete rasterBitmapDev;
+ delete rasterBitmapGc;
+
+ rasterBitmapDev = 0;
+ rasterBitmapGc = 0;
+
+ deleteSourceBitmap = true;
+ }
+#endif
+
+
+ deleteSourceBitmap = bitmap->IsCompressedInRAM();
+ CFbsBitmap *sourceBitmap = uncompress(bitmap);
+
+ TDisplayMode displayMode = sourceBitmap->DisplayMode();
+ QImage::Format format = qt_TDisplayMode2Format(displayMode);
+
+ QImage::Format opaqueFormat = QNativeImage::systemFormat();
+ QImage::Format alphaFormat = QImage::Format_ARGB32_Premultiplied;
+
+ if (format != opaqueFormat && format != alphaFormat && format != QImage::Format_MonoLSB)
+ needsCopy = true;
+
+
+ type = (format != QImage::Format_MonoLSB)
+ ? QPixmapData::PixmapType
+ : QPixmapData::BitmapType;
+
+ if (needsCopy) {
+
+ TSize size = sourceBitmap->SizeInPixels();
+ int bytesPerLine = sourceBitmap->ScanLineLength(size.iWidth, displayMode);
+
+ QSymbianBitmapDataAccess da;
+ da.beginDataAccess(sourceBitmap);
+ uchar *bytes = (uchar*)sourceBitmap->DataAddress();
+ QImage img = QImage(bytes, size.iWidth, size.iHeight, bytesPerLine, format);
+ img = img.copy();
+ da.endDataAccess(sourceBitmap);
+
+ if(displayMode == EGray2) {
+ //Symbian thinks set pixels are white/transparent, Qt thinks they are foreground/solid
+ //So invert mono bitmaps so that masks work correctly.
+ img.invertPixels();
+ } else if(displayMode == EColor16M) {
+ img = img.rgbSwapped(); // EColor16M is BGR
+ }
+
+ fromImage(img, Qt::AutoColor);
+
+ if(deleteSourceBitmap)
+ delete sourceBitmap;
+ } else {
+ CFbsBitmap* duplicate = 0;
+ QT_TRAP_THROWING(duplicate = new (ELeave) CFbsBitmap);
+
+ TInt err = duplicate->Duplicate(sourceBitmap->Handle());
+ if (err != KErrNone) {
+ qWarning("Could not duplicate CFbsBitmap");
+
+ if(deleteSourceBitmap)
+ delete sourceBitmap;
+
+ delete duplicate;
+ return;
+ }
+
+ fromSymbianBitmap(duplicate);
+
+ if(deleteSourceBitmap)
+ delete sourceBitmap;
+ }
+ }
+}
+
+void QS60PixmapData::convertToDisplayMode(int mode)
+{
+ const TDisplayMode displayMode = static_cast<TDisplayMode>(mode);
+ if (!cfbsBitmap || cfbsBitmap->DisplayMode() == displayMode)
+ return;
+ if (image.depth() != TDisplayModeUtils::NumDisplayModeBitsPerPixel(displayMode)) {
+ qWarning("Cannot convert display mode due to depth mismatch");
+ return;
+ }
+
+ const TSize size = cfbsBitmap->SizeInPixels();
+ QScopedPointer<CFbsBitmap> newBitmap(createSymbianCFbsBitmap(size, displayMode));
+
+ const uchar *sptr = const_cast<const QImage &>(image).bits();
+ symbianBitmapDataAccess->beginDataAccess(newBitmap.data());
+ uchar *dptr = (uchar*)newBitmap->DataAddress();
+ Mem::Copy(dptr, sptr, image.byteCount());
+ symbianBitmapDataAccess->endDataAccess(newBitmap.data());
+
+ QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock);
+ delete cfbsBitmap;
+ lock.relock();
+ cfbsBitmap = newBitmap.take();
+ setSerialNumber(cfbsBitmap->Handle());
+ UPDATE_BUFFER();
+}
+
+QPixmapData *QS60PixmapData::createCompatiblePixmapData() const
+{
+ return new QS60PixmapData(pixelType());
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/image/qpixmap_s60_p.h b/src/gui/image/qpixmap_s60_p.h
new file mode 100644
index 0000000000..c440bbc33a
--- /dev/null
+++ b/src/gui/image/qpixmap_s60_p.h
@@ -0,0 +1,141 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPIXMAPDATA_S60_P_H
+#define QPIXMAPDATA_S60_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/private/qpixmap_raster_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class CFbsBitmap;
+class CFbsBitmapDevice;
+class CFbsBitGc;
+
+class QSymbianBitmapDataAccess;
+
+class QSymbianFbsHeapLock
+{
+public:
+
+ enum LockAction {
+ Unlock
+ };
+
+ explicit QSymbianFbsHeapLock(LockAction a);
+ ~QSymbianFbsHeapLock();
+ void relock();
+
+private:
+
+ LockAction action;
+ bool wasLocked;
+};
+
+class QS60PixmapData : public QRasterPixmapData
+{
+public:
+ QS60PixmapData(PixelType type);
+ ~QS60PixmapData();
+
+ QPixmapData *createCompatiblePixmapData() const;
+
+ void resize(int width, int height);
+ void fromImage(const QImage &image, Qt::ImageConversionFlags flags);
+ void copy(const QPixmapData *data, const QRect &rect);
+ bool scroll(int dx, int dy, const QRect &rect);
+
+ int metric(QPaintDevice::PaintDeviceMetric metric) const;
+ void fill(const QColor &color);
+ void setMask(const QBitmap &mask);
+ void setAlphaChannel(const QPixmap &alphaChannel);
+ QImage toImage() const;
+ QPaintEngine* paintEngine() const;
+
+ void beginDataAccess();
+ void endDataAccess(bool readOnly=false) const;
+
+ void* toNativeType(NativeType type);
+ void fromNativeType(void* pixmap, NativeType type);
+
+ void convertToDisplayMode(int mode);
+
+private:
+ void release();
+ void fromSymbianBitmap(CFbsBitmap* bitmap, bool lockFormat=false);
+ QImage toImage(const QRect &r) const;
+
+ QSymbianBitmapDataAccess *symbianBitmapDataAccess;
+
+ CFbsBitmap *cfbsBitmap;
+ QPaintEngine *pengine;
+ uchar* bytes;
+
+ bool formatLocked;
+
+ QS60PixmapData *next;
+ QS60PixmapData *prev;
+
+ static void qt_symbian_register_pixmap(QS60PixmapData *pd);
+ static void qt_symbian_unregister_pixmap(QS60PixmapData *pd);
+ static void qt_symbian_release_pixmaps();
+
+ friend class QPixmap;
+ friend class QS60WindowSurface;
+ friend class QS60PaintEngine;
+ friend class QS60Data;
+};
+
+QT_END_NAMESPACE
+
+#endif // QPIXMAPDATA_S60_P_H
+
diff --git a/src/gui/image/qpixmap_win.cpp b/src/gui/image/qpixmap_win.cpp
new file mode 100644
index 0000000000..9c14ac7726
--- /dev/null
+++ b/src/gui/image/qpixmap_win.cpp
@@ -0,0 +1,477 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpixmap.h"
+#include "qpixmap_raster_p.h"
+
+#include "qbitmap.h"
+#include "qimage.h"
+#include "qwidget.h"
+#include "qpainter.h"
+#include "qdatastream.h"
+#include "qbuffer.h"
+#include "qapplication.h"
+#include "qevent.h"
+#include "qfile.h"
+#include "qfileinfo.h"
+#include "qdatetime.h"
+#include "qpixmapcache.h"
+#include "qimagereader.h"
+#include "qimagewriter.h"
+#include "qdebug.h"
+#include "qt_windows.h"
+
+#if defined(Q_WS_WINCE)
+#include <winbase.h>
+#include "qguifunctions_wince.h"
+extern bool qt_wince_is_high_dpi();
+extern bool qt_wince_is_pocket_pc();
+#endif
+
+#ifndef CAPTUREBLT
+#define CAPTUREBLT ((DWORD)0x40000000)
+#endif
+
+QT_BEGIN_NAMESPACE
+
+QPixmap QPixmap::grabWindow(WId winId, int x, int y, int w, int h )
+{
+ RECT r;
+ GetClientRect(winId, &r);
+
+ if (w < 0) w = r.right - r.left;
+ if (h < 0) h = r.bottom - r.top;
+
+#ifdef Q_WS_WINCE_WM
+ if (qt_wince_is_pocket_pc()) {
+ QWidget *widget = QWidget::find(winId);
+ if (qobject_cast<QDesktopWidget *>(widget)) {
+ RECT rect = {0,0,0,0};
+ AdjustWindowRectEx(&rect, WS_BORDER | WS_CAPTION, FALSE, 0);
+ int magicNumber = qt_wince_is_high_dpi() ? 4 : 2;
+ y += rect.top - magicNumber;
+ }
+ }
+#endif
+
+ // Create and setup bitmap
+ HDC display_dc = GetDC(0);
+ HDC bitmap_dc = CreateCompatibleDC(display_dc);
+ HBITMAP bitmap = CreateCompatibleBitmap(display_dc, w, h);
+ HGDIOBJ null_bitmap = SelectObject(bitmap_dc, bitmap);
+
+ // copy data
+ HDC window_dc = GetDC(winId);
+ BitBlt(bitmap_dc, 0, 0, w, h, window_dc, x, y, SRCCOPY
+#ifndef Q_WS_WINCE
+ | CAPTUREBLT
+#endif
+ );
+
+ // clean up all but bitmap
+ ReleaseDC(winId, window_dc);
+ SelectObject(bitmap_dc, null_bitmap);
+ DeleteDC(bitmap_dc);
+
+ QPixmap pixmap = QPixmap::fromWinHBITMAP(bitmap);
+
+ DeleteObject(bitmap);
+ ReleaseDC(0, display_dc);
+
+ return pixmap;
+}
+
+HBITMAP QPixmap::toWinHBITMAP(HBitmapFormat format) const
+{
+ if (isNull())
+ return 0;
+
+ HBITMAP bitmap = 0;
+ if (data->classId() == QPixmapData::RasterClass) {
+ QRasterPixmapData* d = static_cast<QRasterPixmapData*>(data.data());
+ int w = d->image.width();
+ int h = d->image.height();
+
+ HDC display_dc = GetDC(0);
+
+ // Define the header
+ BITMAPINFO bmi;
+ memset(&bmi, 0, sizeof(bmi));
+ bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bmi.bmiHeader.biWidth = w;
+ bmi.bmiHeader.biHeight = -h;
+ bmi.bmiHeader.biPlanes = 1;
+ bmi.bmiHeader.biBitCount = 32;
+ bmi.bmiHeader.biCompression = BI_RGB;
+ bmi.bmiHeader.biSizeImage = w * h * 4;
+
+ // Create the pixmap
+ uchar *pixels = 0;
+ bitmap = CreateDIBSection(display_dc, &bmi, DIB_RGB_COLORS, (void **) &pixels, 0, 0);
+ ReleaseDC(0, display_dc);
+ if (!bitmap) {
+ qErrnoWarning("QPixmap::toWinHBITMAP(), failed to create dibsection");
+ return 0;
+ }
+ if (!pixels) {
+ qErrnoWarning("QPixmap::toWinHBITMAP(), did not allocate pixel data");
+ return 0;
+ }
+
+ // Copy over the data
+ QImage::Format imageFormat = QImage::Format_ARGB32;
+ if (format == NoAlpha)
+ imageFormat = QImage::Format_RGB32;
+ else if (format == PremultipliedAlpha)
+ imageFormat = QImage::Format_ARGB32_Premultiplied;
+ const QImage image = d->image.convertToFormat(imageFormat);
+ int bytes_per_line = w * 4;
+ for (int y=0; y<h; ++y)
+ memcpy(pixels + y * bytes_per_line, image.scanLine(y), bytes_per_line);
+
+ } else {
+ QPixmapData *data = new QRasterPixmapData(depth() == 1 ?
+ QPixmapData::BitmapType : QPixmapData::PixmapType);
+ data->fromImage(toImage(), Qt::AutoColor);
+ return QPixmap(data).toWinHBITMAP(format);
+ }
+ return bitmap;
+}
+
+QPixmap QPixmap::fromWinHBITMAP(HBITMAP bitmap, HBitmapFormat format)
+{
+ // Verify size
+ BITMAP bitmap_info;
+ memset(&bitmap_info, 0, sizeof(BITMAP));
+
+ int res = GetObject(bitmap, sizeof(BITMAP), &bitmap_info);
+ if (!res) {
+ qErrnoWarning("QPixmap::fromWinHBITMAP(), failed to get bitmap info");
+ return QPixmap();
+ }
+ int w = bitmap_info.bmWidth;
+ int h = bitmap_info.bmHeight;
+
+ BITMAPINFO bmi;
+ memset(&bmi, 0, sizeof(bmi));
+ bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bmi.bmiHeader.biWidth = w;
+ bmi.bmiHeader.biHeight = -h;
+ bmi.bmiHeader.biPlanes = 1;
+ bmi.bmiHeader.biBitCount = 32;
+ bmi.bmiHeader.biCompression = BI_RGB;
+ bmi.bmiHeader.biSizeImage = w * h * 4;
+
+ QImage result;
+ // Get bitmap bits
+ uchar *data = (uchar *) qMalloc(bmi.bmiHeader.biSizeImage);
+
+ HDC display_dc = GetDC(0);
+ if (GetDIBits(display_dc, bitmap, 0, h, data, &bmi, DIB_RGB_COLORS)) {
+
+ QImage::Format imageFormat = QImage::Format_ARGB32_Premultiplied;
+ uint mask = 0;
+ if (format == NoAlpha) {
+ imageFormat = QImage::Format_RGB32;
+ mask = 0xff000000;
+ }
+
+ // Create image and copy data into image.
+ QImage image(w, h, imageFormat);
+ if (!image.isNull()) { // failed to alloc?
+ int bytes_per_line = w * sizeof(QRgb);
+ for (int y=0; y<h; ++y) {
+ QRgb *dest = (QRgb *) image.scanLine(y);
+ const QRgb *src = (const QRgb *) (data + y * bytes_per_line);
+ for (int x=0; x<w; ++x) {
+ const uint pixel = src[x];
+ if ((pixel & 0xff000000) == 0 && (pixel & 0x00ffffff) != 0)
+ dest[x] = pixel | 0xff000000;
+ else
+ dest[x] = pixel | mask;
+ }
+ }
+ }
+ result = image;
+ } else {
+ qWarning("QPixmap::fromWinHBITMAP(), failed to get bitmap bits");
+ }
+ ReleaseDC(0, display_dc);
+ qFree(data);
+ return fromImage(result);
+}
+
+HBITMAP qt_createIconMask(const QBitmap &bitmap)
+{
+ QImage bm = bitmap.toImage().convertToFormat(QImage::Format_Mono);
+ int w = bm.width();
+ int h = bm.height();
+ int bpl = ((w+15)/16)*2; // bpl, 16 bit alignment
+ uchar *bits = new uchar[bpl*h];
+ bm.invertPixels();
+ for (int y=0; y<h; y++)
+ memcpy(bits+y*bpl, bm.scanLine(y), bpl);
+ HBITMAP hbm = CreateBitmap(w, h, 1, 1, bits);
+ delete [] bits;
+ return hbm;
+}
+
+HICON QPixmap::toWinHICON() const
+{
+ QBitmap maskBitmap = mask();
+ if (maskBitmap.isNull()) {
+ maskBitmap= QBitmap(size());
+ maskBitmap.fill(Qt::color1);
+ }
+
+ ICONINFO ii;
+ ii.fIcon = true;
+ ii.hbmMask = qt_createIconMask(maskBitmap);
+ ii.hbmColor = toWinHBITMAP(QPixmap::Alpha);
+ ii.xHotspot = 0;
+ ii.yHotspot = 0;
+
+ HICON hIcon = CreateIconIndirect(&ii);
+
+ DeleteObject(ii.hbmColor);
+ DeleteObject(ii.hbmMask);
+
+ return hIcon;
+}
+
+#ifdef Q_WS_WIN
+#ifndef Q_WS_WINCE
+
+static QImage qt_fromWinHBITMAP(HDC hdc, HBITMAP bitmap, int w, int h)
+{
+ BITMAPINFO bmi;
+ memset(&bmi, 0, sizeof(bmi));
+ bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bmi.bmiHeader.biWidth = w;
+ bmi.bmiHeader.biHeight = -h;
+ bmi.bmiHeader.biPlanes = 1;
+ bmi.bmiHeader.biBitCount = 32;
+ bmi.bmiHeader.biCompression = BI_RGB;
+ bmi.bmiHeader.biSizeImage = w * h * 4;
+
+ QImage image(w, h, QImage::Format_ARGB32_Premultiplied);
+ if (image.isNull())
+ return image;
+
+ // Get bitmap bits
+ uchar *data = (uchar *) qMalloc(bmi.bmiHeader.biSizeImage);
+
+ if (GetDIBits(hdc, bitmap, 0, h, data, &bmi, DIB_RGB_COLORS)) {
+ // Create image and copy data into image.
+ for (int y=0; y<h; ++y) {
+ void *dest = (void *) image.scanLine(y);
+ void *src = data + y * image.bytesPerLine();
+ memcpy(dest, src, image.bytesPerLine());
+ }
+ } else {
+ qWarning("qt_fromWinHBITMAP(), failed to get bitmap bits");
+ }
+ qFree(data);
+
+ return image;
+}
+
+QPixmap QPixmap::fromWinHICON(HICON icon)
+{
+ bool foundAlpha = false;
+ HDC screenDevice = GetDC(0);
+ HDC hdc = CreateCompatibleDC(screenDevice);
+ ReleaseDC(0, screenDevice);
+
+ ICONINFO iconinfo;
+ bool result = GetIconInfo(icon, &iconinfo); //x and y Hotspot describes the icon center
+ if (!result)
+ qWarning("QPixmap::fromWinHICON(), failed to GetIconInfo()");
+
+ int w = iconinfo.xHotspot * 2;
+ int h = iconinfo.yHotspot * 2;
+
+ BITMAPINFOHEADER bitmapInfo;
+ bitmapInfo.biSize = sizeof(BITMAPINFOHEADER);
+ bitmapInfo.biWidth = w;
+ bitmapInfo.biHeight = h;
+ bitmapInfo.biPlanes = 1;
+ bitmapInfo.biBitCount = 32;
+ bitmapInfo.biCompression = BI_RGB;
+ bitmapInfo.biSizeImage = 0;
+ bitmapInfo.biXPelsPerMeter = 0;
+ bitmapInfo.biYPelsPerMeter = 0;
+ bitmapInfo.biClrUsed = 0;
+ bitmapInfo.biClrImportant = 0;
+ DWORD* bits;
+
+ HBITMAP winBitmap = CreateDIBSection(hdc, (BITMAPINFO*)&bitmapInfo, DIB_RGB_COLORS, (VOID**)&bits, NULL, 0);
+ HGDIOBJ oldhdc = (HBITMAP)SelectObject(hdc, winBitmap);
+ DrawIconEx( hdc, 0, 0, icon, iconinfo.xHotspot * 2, iconinfo.yHotspot * 2, 0, 0, DI_NORMAL);
+ QImage image = qt_fromWinHBITMAP(hdc, winBitmap, w, h);
+
+ for (int y = 0 ; y < h && !foundAlpha ; y++) {
+ QRgb *scanLine= reinterpret_cast<QRgb *>(image.scanLine(y));
+ for (int x = 0; x < w ; x++) {
+ if (qAlpha(scanLine[x]) != 0) {
+ foundAlpha = true;
+ break;
+ }
+ }
+ }
+ if (!foundAlpha) {
+ //If no alpha was found, we use the mask to set alpha values
+ DrawIconEx( hdc, 0, 0, icon, w, h, 0, 0, DI_MASK);
+ QImage mask = qt_fromWinHBITMAP(hdc, winBitmap, w, h);
+
+ for (int y = 0 ; y < h ; y++){
+ QRgb *scanlineImage = reinterpret_cast<QRgb *>(image.scanLine(y));
+ QRgb *scanlineMask = mask.isNull() ? 0 : reinterpret_cast<QRgb *>(mask.scanLine(y));
+ for (int x = 0; x < w ; x++){
+ if (scanlineMask && qRed(scanlineMask[x]) != 0)
+ scanlineImage[x] = 0; //mask out this pixel
+ else
+ scanlineImage[x] |= 0xff000000; // set the alpha channel to 255
+ }
+ }
+ }
+ //dispose resources created by iconinfo call
+ DeleteObject(iconinfo.hbmMask);
+ DeleteObject(iconinfo.hbmColor);
+
+ SelectObject(hdc, oldhdc); //restore state
+ DeleteObject(winBitmap);
+ DeleteDC(hdc);
+ return QPixmap::fromImage(image);
+}
+#else //ifndef Q_WS_WINCE
+QPixmap QPixmap::fromWinHICON(HICON icon)
+{
+ HDC screenDevice = GetDC(0);
+ HDC hdc = CreateCompatibleDC(screenDevice);
+ ReleaseDC(0, screenDevice);
+
+ ICONINFO iconinfo;
+ bool result = GetIconInfo(icon, &iconinfo);
+ if (!result)
+ qWarning("QPixmap::fromWinHICON(), failed to GetIconInfo()");
+
+ int w = 0;
+ int h = 0;
+ if (!iconinfo.xHotspot || !iconinfo.yHotspot) {
+ // We could not retrieve the icon size via GetIconInfo,
+ // so we try again using the icon bitmap.
+ BITMAP bm;
+ int result = GetObject(iconinfo.hbmColor, sizeof(BITMAP), &bm);
+ if (!result) result = GetObject(iconinfo.hbmMask, sizeof(BITMAP), &bm);
+ if (!result) {
+ qWarning("QPixmap::fromWinHICON(), failed to retrieve icon size");
+ return QPixmap();
+ }
+ w = bm.bmWidth;
+ h = bm.bmHeight;
+ } else {
+ // x and y Hotspot describes the icon center
+ w = iconinfo.xHotspot * 2;
+ h = iconinfo.yHotspot * 2;
+ }
+ const DWORD dwImageSize = w * h * 4;
+
+ BITMAPINFO bmi;
+ memset(&bmi, 0, sizeof(bmi));
+ bmi.bmiHeader.biSize = sizeof(BITMAPINFO);
+ bmi.bmiHeader.biWidth = w;
+ bmi.bmiHeader.biHeight = -h;
+ bmi.bmiHeader.biPlanes = 1;
+ bmi.bmiHeader.biBitCount = 32;
+ bmi.bmiHeader.biCompression = BI_RGB;
+ bmi.bmiHeader.biSizeImage = dwImageSize;
+
+ uchar* bits;
+
+ HBITMAP winBitmap = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, (void**) &bits, 0, 0);
+ if (winBitmap )
+ memset(bits, 0xff, dwImageSize);
+ if (!winBitmap) {
+ qWarning("QPixmap::fromWinHICON(), failed to CreateDIBSection()");
+ return QPixmap();
+ }
+
+ HGDIOBJ oldhdc = (HBITMAP)SelectObject(hdc, winBitmap);
+ if (!DrawIconEx( hdc, 0, 0, icon, w, h, 0, 0, DI_NORMAL))
+ qWarning("QPixmap::fromWinHICON(), failed to DrawIcon()");
+
+ uint mask = 0xff000000;
+ // Create image and copy data into image.
+ QImage image(w, h, QImage::Format_ARGB32);
+
+ if (!image.isNull()) { // failed to alloc?
+ int bytes_per_line = w * sizeof(QRgb);
+ for (int y=0; y < h; ++y) {
+ QRgb *dest = (QRgb *) image.scanLine(y);
+ const QRgb *src = (const QRgb *) (bits + y * bytes_per_line);
+ for (int x=0; x < w; ++x) {
+ dest[x] = src[x];
+ }
+ }
+ }
+ if (!DrawIconEx( hdc, 0, 0, icon, w, h, 0, 0, DI_MASK))
+ qWarning("QPixmap::fromWinHICON(), failed to DrawIcon()");
+ if (!image.isNull()) { // failed to alloc?
+ int bytes_per_line = w * sizeof(QRgb);
+ for (int y=0; y < h; ++y) {
+ QRgb *dest = (QRgb *) image.scanLine(y);
+ const QRgb *src = (const QRgb *) (bits + y * bytes_per_line);
+ for (int x=0; x < w; ++x) {
+ if (!src[x])
+ dest[x] = dest[x] | mask;
+ }
+ }
+ }
+ SelectObject(hdc, oldhdc); //restore state
+ DeleteObject(winBitmap);
+ DeleteDC(hdc);
+ return QPixmap::fromImage(image);
+}
+#endif //ifndef Q_WS_WINCE
+#endif //ifdef Q_WS_WIN
+
+QT_END_NAMESPACE
diff --git a/src/gui/image/qpixmap_x11.cpp b/src/gui/image/qpixmap_x11.cpp
new file mode 100644
index 0000000000..bc468cb7ec
--- /dev/null
+++ b/src/gui/image/qpixmap_x11.cpp
@@ -0,0 +1,2419 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+// Uncomment the next line to enable the MIT Shared Memory extension
+//
+// WARNING: This has some problems:
+//
+// 1. Consumes a 800x600 pixmap
+// 2. Qt does not handle the ShmCompletion message, so you will
+// get strange effects if you xForm() repeatedly.
+//
+// #define QT_MITSHM
+
+#if defined(Q_OS_WIN32) && defined(QT_MITSHM)
+#undef QT_MITSHM
+#endif
+
+#include "qplatformdefs.h"
+
+#include "qdebug.h"
+#include "qiodevice.h"
+#include "qpixmap_x11_p.h"
+#include "qbitmap.h"
+#include "qcolormap.h"
+#include "qimage.h"
+#include "qmatrix.h"
+#include "qapplication.h"
+#include <private/qpaintengine_x11_p.h>
+#include <private/qt_x11_p.h>
+#include "qx11info_x11.h"
+#include <private/qdrawhelper_p.h>
+#include <private/qimage_p.h>
+#include <private/qimagepixmapcleanuphooks_p.h>
+
+#include <stdlib.h>
+
+#if defined(Q_CC_MIPS)
+# define for if(0){}else for
+#endif
+
+QT_BEGIN_NAMESPACE
+
+QPixmap qt_toX11Pixmap(const QImage &image)
+{
+ QPixmapData *data =
+ new QX11PixmapData(image.depth() == 1
+ ? QPixmapData::BitmapType
+ : QPixmapData::PixmapType);
+
+ data->fromImage(image, Qt::AutoColor);
+
+ return QPixmap(data);
+}
+
+QPixmap qt_toX11Pixmap(const QPixmap &pixmap)
+{
+ if (pixmap.isNull())
+ return QPixmap();
+
+ if (QPixmap(pixmap).data_ptr()->classId() == QPixmapData::X11Class)
+ return pixmap;
+
+ return qt_toX11Pixmap(pixmap.toImage());
+}
+
+// For thread-safety:
+// image->data does not belong to X11, so we must free it ourselves.
+
+inline static void qSafeXDestroyImage(XImage *x)
+{
+ if (x->data) {
+ free(x->data);
+ x->data = 0;
+ }
+ XDestroyImage(x);
+}
+
+QBitmap QX11PixmapData::mask_to_bitmap(int screen) const
+{
+ if (!x11_mask)
+ return QBitmap();
+ QPixmap::x11SetDefaultScreen(screen);
+ QBitmap bm(w, h);
+ GC gc = XCreateGC(X11->display, bm.handle(), 0, 0);
+ XCopyArea(X11->display, x11_mask, bm.handle(), gc, 0, 0,
+ bm.data->width(), bm.data->height(), 0, 0);
+ XFreeGC(X11->display, gc);
+ return bm;
+}
+
+Qt::HANDLE QX11PixmapData::bitmap_to_mask(const QBitmap &bitmap, int screen)
+{
+ if (bitmap.isNull())
+ return 0;
+ QBitmap bm = bitmap;
+ bm.x11SetScreen(screen);
+
+ Pixmap mask = XCreatePixmap(X11->display, RootWindow(X11->display, screen),
+ bm.data->width(), bm.data->height(), 1);
+ GC gc = XCreateGC(X11->display, mask, 0, 0);
+ XCopyArea(X11->display, bm.handle(), mask, gc, 0, 0,
+ bm.data->width(), bm.data->height(), 0, 0);
+ XFreeGC(X11->display, gc);
+ return mask;
+}
+
+
+/*****************************************************************************
+ MIT Shared Memory Extension support: makes xForm noticeably (~20%) faster.
+ *****************************************************************************/
+
+#if defined(QT_MITSHM)
+
+static bool xshminit = false;
+static XShmSegmentInfo xshminfo;
+static XImage *xshmimg = 0;
+static Pixmap xshmpm = 0;
+
+static void qt_cleanup_mitshm()
+{
+ if (xshmimg == 0)
+ return;
+ Display *dpy = QX11Info::appDisplay();
+ if (xshmpm) {
+ XFreePixmap(dpy, xshmpm);
+ xshmpm = 0;
+ }
+ XShmDetach(dpy, &xshminfo); xshmimg->data = 0;
+ qSafeXDestroyImage(xshmimg); xshmimg = 0;
+ shmdt(xshminfo.shmaddr);
+ shmctl(xshminfo.shmid, IPC_RMID, 0);
+}
+
+static bool qt_create_mitshm_buffer(const QPaintDevice* dev, int w, int h)
+{
+ static int major, minor;
+ static Bool pixmaps_ok;
+ Display *dpy = dev->data->xinfo->display();
+ int dd = dev->x11Depth();
+ Visual *vis = (Visual*)dev->x11Visual();
+
+ if (xshminit) {
+ qt_cleanup_mitshm();
+ } else {
+ if (!XShmQueryVersion(dpy, &major, &minor, &pixmaps_ok))
+ return false; // MIT Shm not supported
+ qAddPostRoutine(qt_cleanup_mitshm);
+ xshminit = true;
+ }
+
+ xshmimg = XShmCreateImage(dpy, vis, dd, ZPixmap, 0, &xshminfo, w, h);
+ if (!xshmimg)
+ return false;
+
+ bool ok;
+ xshminfo.shmid = shmget(IPC_PRIVATE,
+ xshmimg->bytes_per_line * xshmimg->height,
+ IPC_CREAT | 0777);
+ ok = xshminfo.shmid != -1;
+ if (ok) {
+ xshmimg->data = (char*)shmat(xshminfo.shmid, 0, 0);
+ xshminfo.shmaddr = xshmimg->data;
+ ok = (xshminfo.shmaddr != (char*)-1);
+ }
+ xshminfo.readOnly = false;
+ if (ok)
+ ok = XShmAttach(dpy, &xshminfo);
+ if (!ok) {
+ qSafeXDestroyImage(xshmimg);
+ xshmimg = 0;
+ if (xshminfo.shmaddr)
+ shmdt(xshminfo.shmaddr);
+ if (xshminfo.shmid != -1)
+ shmctl(xshminfo.shmid, IPC_RMID, 0);
+ return false;
+ }
+ if (pixmaps_ok)
+ xshmpm = XShmCreatePixmap(dpy, DefaultRootWindow(dpy), xshmimg->data,
+ &xshminfo, w, h, dd);
+
+ return true;
+}
+
+#else
+
+// If extern, need a dummy.
+//
+// static bool qt_create_mitshm_buffer(QPaintDevice*, int, int)
+// {
+// return false;
+// }
+
+#endif // QT_MITSHM
+
+
+/*****************************************************************************
+ Internal functions
+ *****************************************************************************/
+
+extern const uchar *qt_get_bitflip_array(); // defined in qimage.cpp
+
+// Returns position of highest bit set or -1 if none
+static int highest_bit(uint v)
+{
+ int i;
+ uint b = (uint)1 << 31;
+ for (i=31; ((b & v) == 0) && i>=0; i--)
+ b >>= 1;
+ return i;
+}
+
+// Returns position of lowest set bit in 'v' as an integer (0-31), or -1
+static int lowest_bit(uint v)
+{
+ int i;
+ ulong lb;
+ lb = 1;
+ for (i=0; ((v & lb) == 0) && i<32; i++, lb<<=1) {}
+ return i==32 ? -1 : i;
+}
+
+// Counts the number of bits set in 'v'
+static uint n_bits(uint v)
+{
+ int i = 0;
+ while (v) {
+ v = v & (v - 1);
+ i++;
+ }
+ return i;
+}
+
+static uint *red_scale_table = 0;
+static uint *green_scale_table = 0;
+static uint *blue_scale_table = 0;
+
+static void cleanup_scale_tables()
+{
+ delete[] red_scale_table;
+ delete[] green_scale_table;
+ delete[] blue_scale_table;
+}
+
+/*
+ Could do smart bitshifting, but the "obvious" algorithm only works for
+ nBits >= 4. This is more robust.
+*/
+static void build_scale_table(uint **table, uint nBits)
+{
+ if (nBits > 7) {
+ qWarning("build_scale_table: internal error, nBits = %i", nBits);
+ return;
+ }
+ if (!*table) {
+ static bool firstTable = true;
+ if (firstTable) {
+ qAddPostRoutine(cleanup_scale_tables);
+ firstTable = false;
+ }
+ *table = new uint[256];
+ }
+ int maxVal = (1 << nBits) - 1;
+ int valShift = 8 - nBits;
+ int i;
+ for(i = 0 ; i < maxVal + 1 ; i++)
+ (*table)[i << valShift] = i*255/maxVal;
+}
+
+static int defaultScreen = -1;
+
+/*****************************************************************************
+ QPixmap member functions
+ *****************************************************************************/
+
+QBasicAtomicInt qt_pixmap_serial = Q_BASIC_ATOMIC_INITIALIZER(0);
+int Q_GUI_EXPORT qt_x11_preferred_pixmap_depth = 0;
+
+QX11PixmapData::QX11PixmapData(PixelType type)
+ : QPixmapData(type, X11Class), gl_surface(0), hd(0),
+ flags(Uninitialized), x11_mask(0), picture(0), mask_picture(0), hd2(0),
+ share_mode(QPixmap::ImplicitlyShared), pengine(0)
+{
+}
+
+QPixmapData *QX11PixmapData::createCompatiblePixmapData() const
+{
+ return new QX11PixmapData(pixelType());
+}
+
+void QX11PixmapData::resize(int width, int height)
+{
+ setSerialNumber(qt_pixmap_serial.fetchAndAddRelaxed(1));
+
+ w = width;
+ h = height;
+ is_null = (w <= 0 || h <= 0);
+
+ if (defaultScreen >= 0 && defaultScreen != xinfo.screen()) {
+ QX11InfoData* xd = xinfo.getX11Data(true);
+ xd->screen = defaultScreen;
+ xd->depth = QX11Info::appDepth(xd->screen);
+ xd->cells = QX11Info::appCells(xd->screen);
+ xd->colormap = QX11Info::appColormap(xd->screen);
+ xd->defaultColormap = QX11Info::appDefaultColormap(xd->screen);
+ xd->visual = (Visual *)QX11Info::appVisual(xd->screen);
+ xd->defaultVisual = QX11Info::appDefaultVisual(xd->screen);
+ xinfo.setX11Data(xd);
+ }
+
+ int dd = xinfo.depth();
+
+ if (qt_x11_preferred_pixmap_depth)
+ dd = qt_x11_preferred_pixmap_depth;
+
+ bool make_null = w <= 0 || h <= 0; // create null pixmap
+ d = (pixelType() == BitmapType ? 1 : dd);
+ if (make_null || d == 0) {
+ w = 0;
+ h = 0;
+ is_null = true;
+ hd = 0;
+ picture = 0;
+ d = 0;
+ if (!make_null)
+ qWarning("QPixmap: Invalid pixmap parameters");
+ return;
+ }
+ hd = (Qt::HANDLE)XCreatePixmap(X11->display,
+ RootWindow(X11->display, xinfo.screen()),
+ w, h, d);
+#ifndef QT_NO_XRENDER
+ if (X11->use_xrender) {
+ XRenderPictFormat *format = d == 1
+ ? XRenderFindStandardFormat(X11->display, PictStandardA1)
+ : XRenderFindVisualFormat(X11->display, (Visual *)xinfo.visual());
+ picture = XRenderCreatePicture(X11->display, hd, format, 0, 0);
+ }
+#endif // QT_NO_XRENDER
+}
+
+struct QX11AlphaDetector
+{
+ bool hasAlpha() const {
+ if (checked)
+ return has;
+ // Will implicitly also check format and return quickly for opaque types...
+ checked = true;
+ has = image->isNull() ? false : const_cast<QImage *>(image)->data_ptr()->checkForAlphaPixels();
+ return has;
+ }
+
+ bool hasXRenderAndAlpha() const {
+ if (!X11->use_xrender)
+ return false;
+ return hasAlpha();
+ }
+
+ QX11AlphaDetector(const QImage *i, Qt::ImageConversionFlags flags)
+ : image(i), checked(false), has(false)
+ {
+ if (flags & Qt::NoOpaqueDetection) {
+ checked = true;
+ has = image->hasAlphaChannel();
+ }
+ }
+
+ const QImage *image;
+ mutable bool checked;
+ mutable bool has;
+};
+
+void QX11PixmapData::fromImage(const QImage &img,
+ Qt::ImageConversionFlags flags)
+{
+ setSerialNumber(qt_pixmap_serial.fetchAndAddRelaxed(1));
+
+ w = img.width();
+ h = img.height();
+ d = img.depth();
+ is_null = (w <= 0 || h <= 0);
+
+ if (is_null) {
+ w = h = 0;
+ return;
+ }
+
+ if (defaultScreen >= 0 && defaultScreen != xinfo.screen()) {
+ QX11InfoData* xd = xinfo.getX11Data(true);
+ xd->screen = defaultScreen;
+ xd->depth = QX11Info::appDepth(xd->screen);
+ xd->cells = QX11Info::appCells(xd->screen);
+ xd->colormap = QX11Info::appColormap(xd->screen);
+ xd->defaultColormap = QX11Info::appDefaultColormap(xd->screen);
+ xd->visual = (Visual *)QX11Info::appVisual(xd->screen);
+ xd->defaultVisual = QX11Info::appDefaultVisual(xd->screen);
+ xinfo.setX11Data(xd);
+ }
+
+ if (pixelType() == BitmapType) {
+ bitmapFromImage(img);
+ return;
+ }
+
+ if (uint(w) >= 32768 || uint(h) >= 32768) {
+ w = h = 0;
+ is_null = true;
+ return;
+ }
+
+ QX11AlphaDetector alphaCheck(&img, flags);
+ int dd = alphaCheck.hasXRenderAndAlpha() ? 32 : xinfo.depth();
+
+ if (qt_x11_preferred_pixmap_depth)
+ dd = qt_x11_preferred_pixmap_depth;
+
+ QImage image = img;
+
+ // must be monochrome
+ if (dd == 1 || (flags & Qt::ColorMode_Mask) == Qt::MonoOnly) {
+ if (d != 1) {
+ // dither
+ image = image.convertToFormat(QImage::Format_MonoLSB, flags);
+ d = 1;
+ }
+ } else { // can be both
+ bool conv8 = false;
+ if (d > 8 && dd <= 8) { // convert to 8 bit
+ if ((flags & Qt::DitherMode_Mask) == Qt::AutoDither)
+ flags = (flags & ~Qt::DitherMode_Mask)
+ | Qt::PreferDither;
+ conv8 = true;
+ } else if ((flags & Qt::ColorMode_Mask) == Qt::ColorOnly) {
+ conv8 = (d == 1); // native depth wanted
+ } else if (d == 1) {
+ if (image.colorCount() == 2) {
+ QRgb c0 = image.color(0); // Auto: convert to best
+ QRgb c1 = image.color(1);
+ conv8 = qMin(c0,c1) != qRgb(0,0,0) || qMax(c0,c1) != qRgb(255,255,255);
+ } else {
+ // eg. 1-color monochrome images (they do exist).
+ conv8 = true;
+ }
+ }
+ if (conv8) {
+ image = image.convertToFormat(QImage::Format_Indexed8, flags);
+ d = 8;
+ }
+ }
+
+ if (d == 1 || d == 16 || d == 24) {
+ image = image.convertToFormat(QImage::Format_RGB32, flags);
+ fromImage(image, Qt::AutoColor);
+ return;
+ }
+
+ Display *dpy = X11->display;
+ Visual *visual = (Visual *)xinfo.visual();
+ XImage *xi = 0;
+ bool trucol = (visual->c_class >= TrueColor);
+ int nbytes = image.byteCount();
+ uchar *newbits= 0;
+
+#ifndef QT_NO_XRENDER
+ if (alphaCheck.hasXRenderAndAlpha()) {
+ const QImage &cimage = image;
+
+ d = 32;
+
+ if (QX11Info::appDepth() != d) {
+ if (xinfo.x11data) {
+ xinfo.x11data->depth = d;
+ } else {
+ QX11InfoData *xd = xinfo.getX11Data(true);
+ xd->screen = QX11Info::appScreen();
+ xd->depth = d;
+ xd->cells = QX11Info::appCells();
+ xd->colormap = QX11Info::appColormap();
+ xd->defaultColormap = QX11Info::appDefaultColormap();
+ xd->visual = (Visual *)QX11Info::appVisual();
+ xd->defaultVisual = QX11Info::appDefaultVisual();
+ xinfo.setX11Data(xd);
+ }
+ }
+
+ hd = (Qt::HANDLE)XCreatePixmap(dpy, RootWindow(dpy, xinfo.screen()),
+ w, h, d);
+ picture = XRenderCreatePicture(X11->display, hd,
+ XRenderFindStandardFormat(X11->display, PictStandardARGB32), 0, 0);
+
+ xi = XCreateImage(dpy, visual, d, ZPixmap, 0, 0, w, h, 32, 0);
+ Q_CHECK_PTR(xi);
+ newbits = (uchar *)malloc(xi->bytes_per_line*h);
+ Q_CHECK_PTR(newbits);
+ xi->data = (char *)newbits;
+
+ switch(cimage.format()) {
+ case QImage::Format_Indexed8: {
+ QVector<QRgb> colorTable = cimage.colorTable();
+ uint *xidata = (uint *)xi->data;
+ for (int y = 0; y < h; ++y) {
+ const uchar *p = cimage.scanLine(y);
+ for (int x = 0; x < w; ++x) {
+ const QRgb rgb = colorTable[p[x]];
+ const int a = qAlpha(rgb);
+ if (a == 0xff)
+ *xidata = rgb;
+ else
+ // RENDER expects premultiplied alpha
+ *xidata = qRgba(qt_div_255(qRed(rgb) * a),
+ qt_div_255(qGreen(rgb) * a),
+ qt_div_255(qBlue(rgb) * a),
+ a);
+ ++xidata;
+ }
+ }
+ }
+ break;
+ case QImage::Format_RGB32: {
+ uint *xidata = (uint *)xi->data;
+ for (int y = 0; y < h; ++y) {
+ const QRgb *p = (const QRgb *) cimage.scanLine(y);
+ for (int x = 0; x < w; ++x)
+ *xidata++ = p[x] | 0xff000000;
+ }
+ }
+ break;
+ case QImage::Format_ARGB32: {
+ uint *xidata = (uint *)xi->data;
+ for (int y = 0; y < h; ++y) {
+ const QRgb *p = (const QRgb *) cimage.scanLine(y);
+ for (int x = 0; x < w; ++x) {
+ const QRgb rgb = p[x];
+ const int a = qAlpha(rgb);
+ if (a == 0xff)
+ *xidata = rgb;
+ else
+ // RENDER expects premultiplied alpha
+ *xidata = qRgba(qt_div_255(qRed(rgb) * a),
+ qt_div_255(qGreen(rgb) * a),
+ qt_div_255(qBlue(rgb) * a),
+ a);
+ ++xidata;
+ }
+ }
+
+ }
+ break;
+ case QImage::Format_ARGB32_Premultiplied: {
+ uint *xidata = (uint *)xi->data;
+ for (int y = 0; y < h; ++y) {
+ const QRgb *p = (const QRgb *) cimage.scanLine(y);
+ memcpy(xidata, p, w*sizeof(QRgb));
+ xidata += w;
+ }
+ }
+ break;
+ default:
+ Q_ASSERT(false);
+ }
+
+ if ((xi->byte_order == MSBFirst) != (QSysInfo::ByteOrder == QSysInfo::BigEndian)) {
+ uint *xidata = (uint *)xi->data;
+ uint *xiend = xidata + w*h;
+ while (xidata < xiend) {
+ *xidata = (*xidata >> 24)
+ | ((*xidata >> 8) & 0xff00)
+ | ((*xidata << 8) & 0xff0000)
+ | (*xidata << 24);
+ ++xidata;
+ }
+ }
+
+ GC gc = XCreateGC(dpy, hd, 0, 0);
+ XPutImage(dpy, hd, gc, xi, 0, 0, 0, 0, w, h);
+ XFreeGC(dpy, gc);
+
+ qSafeXDestroyImage(xi);
+
+ return;
+ }
+#endif // QT_NO_XRENDER
+
+ if (trucol) { // truecolor display
+ if (image.format() == QImage::Format_ARGB32_Premultiplied)
+ image = image.convertToFormat(QImage::Format_ARGB32);
+
+ const QImage &cimage = image;
+ QRgb pix[256]; // pixel translation table
+ const bool d8 = (d == 8);
+ const uint red_mask = (uint)visual->red_mask;
+ const uint green_mask = (uint)visual->green_mask;
+ const uint blue_mask = (uint)visual->blue_mask;
+ const int red_shift = highest_bit(red_mask) - 7;
+ const int green_shift = highest_bit(green_mask) - 7;
+ const int blue_shift = highest_bit(blue_mask) - 7;
+ const uint rbits = highest_bit(red_mask) - lowest_bit(red_mask) + 1;
+ const uint gbits = highest_bit(green_mask) - lowest_bit(green_mask) + 1;
+ const uint bbits = highest_bit(blue_mask) - lowest_bit(blue_mask) + 1;
+
+ if (d8) { // setup pixel translation
+ QVector<QRgb> ctable = cimage.colorTable();
+ for (int i=0; i < cimage.colorCount(); i++) {
+ int r = qRed (ctable[i]);
+ int g = qGreen(ctable[i]);
+ int b = qBlue (ctable[i]);
+ r = red_shift > 0 ? r << red_shift : r >> -red_shift;
+ g = green_shift > 0 ? g << green_shift : g >> -green_shift;
+ b = blue_shift > 0 ? b << blue_shift : b >> -blue_shift;
+ pix[i] = (b & blue_mask) | (g & green_mask) | (r & red_mask)
+ | ~(blue_mask | green_mask | red_mask);
+ }
+ }
+
+ xi = XCreateImage(dpy, visual, dd, ZPixmap, 0, 0, w, h, 32, 0);
+ Q_CHECK_PTR(xi);
+ newbits = (uchar *)malloc(xi->bytes_per_line*h);
+ Q_CHECK_PTR(newbits);
+ if (!newbits) // no memory
+ return;
+ int bppc = xi->bits_per_pixel;
+
+ bool contig_bits = n_bits(red_mask) == rbits &&
+ n_bits(green_mask) == gbits &&
+ n_bits(blue_mask) == bbits;
+ bool dither_tc =
+ // Want it?
+ (flags & Qt::Dither_Mask) != Qt::ThresholdDither &&
+ (flags & Qt::DitherMode_Mask) != Qt::AvoidDither &&
+ // Need it?
+ bppc < 24 && !d8 &&
+ // Can do it? (Contiguous bits?)
+ contig_bits;
+
+ static bool init=false;
+ static int D[16][16];
+ if (dither_tc && !init) {
+ // I also contributed this code to XV - WWA.
+ /*
+ The dither matrix, D, is obtained with this formula:
+
+ D2 = [0 2]
+ [3 1]
+
+
+ D2*n = [4*Dn 4*Dn+2*Un]
+ [4*Dn+3*Un 4*Dn+1*Un]
+ */
+ int n,i,j;
+ init=1;
+
+ /* Set D2 */
+ D[0][0]=0;
+ D[1][0]=2;
+ D[0][1]=3;
+ D[1][1]=1;
+
+ /* Expand using recursive definition given above */
+ for (n=2; n<16; n*=2) {
+ for (i=0; i<n; i++) {
+ for (j=0; j<n; j++) {
+ D[i][j]*=4;
+ D[i+n][j]=D[i][j]+2;
+ D[i][j+n]=D[i][j]+3;
+ D[i+n][j+n]=D[i][j]+1;
+ }
+ }
+ }
+ init=true;
+ }
+
+ enum { BPP8,
+ BPP16_565, BPP16_555,
+ BPP16_MSB, BPP16_LSB,
+ BPP24_888,
+ BPP24_MSB, BPP24_LSB,
+ BPP32_8888,
+ BPP32_MSB, BPP32_LSB
+ } mode = BPP8;
+
+ bool same_msb_lsb = (xi->byte_order == MSBFirst) == (QSysInfo::ByteOrder == QSysInfo::BigEndian);
+
+ if(bppc == 8) // 8 bit
+ mode = BPP8;
+ else if(bppc == 16) { // 16 bit MSB/LSB
+ if(red_shift == 8 && green_shift == 3 && blue_shift == -3 && !d8 && same_msb_lsb)
+ mode = BPP16_565;
+ else if(red_shift == 7 && green_shift == 2 && blue_shift == -3 && !d8 && same_msb_lsb)
+ mode = BPP16_555;
+ else
+ mode = (xi->byte_order == LSBFirst) ? BPP16_LSB : BPP16_MSB;
+ } else if(bppc == 24) { // 24 bit MSB/LSB
+ if (red_shift == 16 && green_shift == 8 && blue_shift == 0 && !d8 && same_msb_lsb)
+ mode = BPP24_888;
+ else
+ mode = (xi->byte_order == LSBFirst) ? BPP24_LSB : BPP24_MSB;
+ } else if(bppc == 32) { // 32 bit MSB/LSB
+ if(red_shift == 16 && green_shift == 8 && blue_shift == 0 && !d8 && same_msb_lsb)
+ mode = BPP32_8888;
+ else
+ mode = (xi->byte_order == LSBFirst) ? BPP32_LSB : BPP32_MSB;
+ } else
+ qFatal("Logic error 3");
+
+#define GET_PIXEL \
+ uint pixel; \
+ if (d8) pixel = pix[*src++]; \
+ else { \
+ int r = qRed (*p); \
+ int g = qGreen(*p); \
+ int b = qBlue (*p++); \
+ r = red_shift > 0 \
+ ? r << red_shift : r >> -red_shift; \
+ g = green_shift > 0 \
+ ? g << green_shift : g >> -green_shift; \
+ b = blue_shift > 0 \
+ ? b << blue_shift : b >> -blue_shift; \
+ pixel = (r & red_mask)|(g & green_mask) | (b & blue_mask) \
+ | ~(blue_mask | green_mask | red_mask); \
+ }
+
+#define GET_PIXEL_DITHER_TC \
+ int r = qRed (*p); \
+ int g = qGreen(*p); \
+ int b = qBlue (*p++); \
+ const int thres = D[x%16][y%16]; \
+ if (r <= (255-(1<<(8-rbits))) && ((r<<rbits) & 255) \
+ > thres) \
+ r += (1<<(8-rbits)); \
+ if (g <= (255-(1<<(8-gbits))) && ((g<<gbits) & 255) \
+ > thres) \
+ g += (1<<(8-gbits)); \
+ if (b <= (255-(1<<(8-bbits))) && ((b<<bbits) & 255) \
+ > thres) \
+ b += (1<<(8-bbits)); \
+ r = red_shift > 0 \
+ ? r << red_shift : r >> -red_shift; \
+ g = green_shift > 0 \
+ ? g << green_shift : g >> -green_shift; \
+ b = blue_shift > 0 \
+ ? b << blue_shift : b >> -blue_shift; \
+ uint pixel = (r & red_mask)|(g & green_mask) | (b & blue_mask);
+
+// again, optimized case
+// can't be optimized that much :(
+#define GET_PIXEL_DITHER_TC_OPT(red_shift,green_shift,blue_shift,red_mask,green_mask,blue_mask, \
+ rbits,gbits,bbits) \
+ const int thres = D[x%16][y%16]; \
+ int r = qRed (*p); \
+ if (r <= (255-(1<<(8-rbits))) && ((r<<rbits) & 255) \
+ > thres) \
+ r += (1<<(8-rbits)); \
+ int g = qGreen(*p); \
+ if (g <= (255-(1<<(8-gbits))) && ((g<<gbits) & 255) \
+ > thres) \
+ g += (1<<(8-gbits)); \
+ int b = qBlue (*p++); \
+ if (b <= (255-(1<<(8-bbits))) && ((b<<bbits) & 255) \
+ > thres) \
+ b += (1<<(8-bbits)); \
+ uint pixel = ((r red_shift) & red_mask) \
+ | ((g green_shift) & green_mask) \
+ | ((b blue_shift) & blue_mask);
+
+#define CYCLE(body) \
+ for (int y=0; y<h; y++) { \
+ const uchar* src = cimage.scanLine(y); \
+ uchar* dst = newbits + xi->bytes_per_line*y; \
+ const QRgb* p = (const QRgb *)src; \
+ body \
+ }
+
+ if (dither_tc) {
+ switch (mode) {
+ case BPP16_565:
+ CYCLE(
+ quint16* dst16 = (quint16*)dst;
+ for (int x=0; x<w; x++) {
+ GET_PIXEL_DITHER_TC_OPT(<<8,<<3,>>3,0xf800,0x7e0,0x1f,5,6,5)
+ *dst16++ = pixel;
+ }
+ )
+ break;
+ case BPP16_555:
+ CYCLE(
+ quint16* dst16 = (quint16*)dst;
+ for (int x=0; x<w; x++) {
+ GET_PIXEL_DITHER_TC_OPT(<<7,<<2,>>3,0x7c00,0x3e0,0x1f,5,5,5)
+ *dst16++ = pixel;
+ }
+ )
+ break;
+ case BPP16_MSB: // 16 bit MSB
+ CYCLE(
+ for (int x=0; x<w; x++) {
+ GET_PIXEL_DITHER_TC
+ *dst++ = (pixel >> 8);
+ *dst++ = pixel;
+ }
+ )
+ break;
+ case BPP16_LSB: // 16 bit LSB
+ CYCLE(
+ for (int x=0; x<w; x++) {
+ GET_PIXEL_DITHER_TC
+ *dst++ = pixel;
+ *dst++ = pixel >> 8;
+ }
+ )
+ break;
+ default:
+ qFatal("Logic error");
+ }
+ } else {
+ switch (mode) {
+ case BPP8: // 8 bit
+ CYCLE(
+ Q_UNUSED(p);
+ for (int x=0; x<w; x++)
+ *dst++ = pix[*src++];
+ )
+ break;
+ case BPP16_565:
+ CYCLE(
+ quint16* dst16 = (quint16*)dst;
+ for (int x = 0; x < w; x++) {
+ *dst16++ = ((*p >> 8) & 0xf800)
+ | ((*p >> 5) & 0x7e0)
+ | ((*p >> 3) & 0x1f);
+ ++p;
+ }
+ )
+ break;
+ case BPP16_555:
+ CYCLE(
+ quint16* dst16 = (quint16*)dst;
+ for (int x=0; x<w; x++) {
+ *dst16++ = ((*p >> 9) & 0x7c00)
+ | ((*p >> 6) & 0x3e0)
+ | ((*p >> 3) & 0x1f);
+ ++p;
+ }
+ )
+ break;
+ case BPP16_MSB: // 16 bit MSB
+ CYCLE(
+ for (int x=0; x<w; x++) {
+ GET_PIXEL
+ *dst++ = (pixel >> 8);
+ *dst++ = pixel;
+ }
+ )
+ break;
+ case BPP16_LSB: // 16 bit LSB
+ CYCLE(
+ for (int x=0; x<w; x++) {
+ GET_PIXEL
+ *dst++ = pixel;
+ *dst++ = pixel >> 8;
+ }
+ )
+ break;
+ case BPP24_888: // 24 bit MSB
+ CYCLE(
+ for (int x=0; x<w; x++) {
+ *dst++ = qRed (*p);
+ *dst++ = qGreen(*p);
+ *dst++ = qBlue (*p++);
+ }
+ )
+ break;
+ case BPP24_MSB: // 24 bit MSB
+ CYCLE(
+ for (int x=0; x<w; x++) {
+ GET_PIXEL
+ *dst++ = pixel >> 16;
+ *dst++ = pixel >> 8;
+ *dst++ = pixel;
+ }
+ )
+ break;
+ case BPP24_LSB: // 24 bit LSB
+ CYCLE(
+ for (int x=0; x<w; x++) {
+ GET_PIXEL
+ *dst++ = pixel;
+ *dst++ = pixel >> 8;
+ *dst++ = pixel >> 16;
+ }
+ )
+ break;
+ case BPP32_8888:
+ CYCLE(
+ memcpy(dst, p, w * 4);
+ )
+ break;
+ case BPP32_MSB: // 32 bit MSB
+ CYCLE(
+ for (int x=0; x<w; x++) {
+ GET_PIXEL
+ *dst++ = pixel >> 24;
+ *dst++ = pixel >> 16;
+ *dst++ = pixel >> 8;
+ *dst++ = pixel;
+ }
+ )
+ break;
+ case BPP32_LSB: // 32 bit LSB
+ CYCLE(
+ for (int x=0; x<w; x++) {
+ GET_PIXEL
+ *dst++ = pixel;
+ *dst++ = pixel >> 8;
+ *dst++ = pixel >> 16;
+ *dst++ = pixel >> 24;
+ }
+ )
+ break;
+ default:
+ qFatal("Logic error 2");
+ }
+ }
+ xi->data = (char *)newbits;
+ }
+
+ if (d == 8 && !trucol) { // 8 bit pixmap
+ int pop[256]; // pixel popularity
+
+ if (image.colorCount() == 0)
+ image.setColorCount(1);
+
+ const QImage &cimage = image;
+ memset(pop, 0, sizeof(int)*256); // reset popularity array
+ for (int i = 0; i < h; i++) { // for each scanline...
+ const uchar* p = cimage.scanLine(i);
+ const uchar *end = p + w;
+ while (p < end) // compute popularity
+ pop[*p++]++;
+ }
+
+ newbits = (uchar *)malloc(nbytes); // copy image into newbits
+ Q_CHECK_PTR(newbits);
+ if (!newbits) // no memory
+ return;
+ uchar* p = newbits;
+ memcpy(p, cimage.bits(), nbytes); // copy image data into newbits
+
+ /*
+ * The code below picks the most important colors. It is based on the
+ * diversity algorithm, implemented in XV 3.10. XV is (C) by John Bradley.
+ */
+
+ struct PIX { // pixel sort element
+ uchar r,g,b,n; // color + pad
+ int use; // popularity
+ int index; // index in colormap
+ int mindist;
+ };
+ int ncols = 0;
+ for (int i=0; i< cimage.colorCount(); i++) { // compute number of colors
+ if (pop[i] > 0)
+ ncols++;
+ }
+ for (int i = cimage.colorCount(); i < 256; i++) // ignore out-of-range pixels
+ pop[i] = 0;
+
+ // works since we make sure above to have at least
+ // one color in the image
+ if (ncols == 0)
+ ncols = 1;
+
+ PIX pixarr[256]; // pixel array
+ PIX pixarr_sorted[256]; // pixel array (sorted)
+ memset(pixarr, 0, ncols*sizeof(PIX));
+ PIX *px = &pixarr[0];
+ int maxpop = 0;
+ int maxpix = 0;
+ uint j = 0;
+ QVector<QRgb> ctable = cimage.colorTable();
+ for (int i = 0; i < 256; i++) { // init pixel array
+ if (pop[i] > 0) {
+ px->r = qRed (ctable[i]);
+ px->g = qGreen(ctable[i]);
+ px->b = qBlue (ctable[i]);
+ px->n = 0;
+ px->use = pop[i];
+ if (pop[i] > maxpop) { // select most popular entry
+ maxpop = pop[i];
+ maxpix = j;
+ }
+ px->index = i;
+ px->mindist = 1000000;
+ px++;
+ j++;
+ }
+ }
+ pixarr_sorted[0] = pixarr[maxpix];
+ pixarr[maxpix].use = 0;
+
+ for (int i = 1; i < ncols; i++) { // sort pixels
+ int minpix = -1, mindist = -1;
+ px = &pixarr_sorted[i-1];
+ int r = px->r;
+ int g = px->g;
+ int b = px->b;
+ int dist;
+ if ((i & 1) || i<10) { // sort on max distance
+ for (int j=0; j<ncols; j++) {
+ px = &pixarr[j];
+ if (px->use) {
+ dist = (px->r - r)*(px->r - r) +
+ (px->g - g)*(px->g - g) +
+ (px->b - b)*(px->b - b);
+ if (px->mindist > dist)
+ px->mindist = dist;
+ if (px->mindist > mindist) {
+ mindist = px->mindist;
+ minpix = j;
+ }
+ }
+ }
+ } else { // sort on max popularity
+ for (int j=0; j<ncols; j++) {
+ px = &pixarr[j];
+ if (px->use) {
+ dist = (px->r - r)*(px->r - r) +
+ (px->g - g)*(px->g - g) +
+ (px->b - b)*(px->b - b);
+ if (px->mindist > dist)
+ px->mindist = dist;
+ if (px->use > mindist) {
+ mindist = px->use;
+ minpix = j;
+ }
+ }
+ }
+ }
+ pixarr_sorted[i] = pixarr[minpix];
+ pixarr[minpix].use = 0;
+ }
+
+ QColormap cmap = QColormap::instance(xinfo.screen());
+ uint pix[256]; // pixel translation table
+ px = &pixarr_sorted[0];
+ for (int i = 0; i < ncols; i++) { // allocate colors
+ QColor c(px->r, px->g, px->b);
+ pix[px->index] = cmap.pixel(c);
+ px++;
+ }
+
+ p = newbits;
+ for (int i = 0; i < nbytes; i++) { // translate pixels
+ *p = pix[*p];
+ p++;
+ }
+ }
+
+ if (!xi) { // X image not created
+ xi = XCreateImage(dpy, visual, dd, ZPixmap, 0, 0, w, h, 32, 0);
+ if (xi->bits_per_pixel == 16) { // convert 8 bpp ==> 16 bpp
+ ushort *p2;
+ int p2inc = xi->bytes_per_line/sizeof(ushort);
+ ushort *newerbits = (ushort *)malloc(xi->bytes_per_line * h);
+ Q_CHECK_PTR(newerbits);
+ if (!newerbits) // no memory
+ return;
+ uchar* p = newbits;
+ for (int y = 0; y < h; y++) { // OOPS: Do right byte order!!
+ p2 = newerbits + p2inc*y;
+ for (int x = 0; x < w; x++)
+ *p2++ = *p++;
+ }
+ free(newbits);
+ newbits = (uchar *)newerbits;
+ } else if (xi->bits_per_pixel != 8) {
+ qWarning("QPixmap::fromImage: Display not supported "
+ "(bpp=%d)", xi->bits_per_pixel);
+ }
+ xi->data = (char *)newbits;
+ }
+
+ hd = (Qt::HANDLE)XCreatePixmap(X11->display,
+ RootWindow(X11->display, xinfo.screen()),
+ w, h, dd);
+
+ GC gc = XCreateGC(dpy, hd, 0, 0);
+ XPutImage(dpy, hd, gc, xi, 0, 0, 0, 0, w, h);
+ XFreeGC(dpy, gc);
+
+ qSafeXDestroyImage(xi);
+ d = dd;
+
+#ifndef QT_NO_XRENDER
+ if (X11->use_xrender) {
+ XRenderPictFormat *format = d == 1
+ ? XRenderFindStandardFormat(X11->display, PictStandardA1)
+ : XRenderFindVisualFormat(X11->display, (Visual *)xinfo.visual());
+ picture = XRenderCreatePicture(X11->display, hd, format, 0, 0);
+ }
+#endif
+
+ if (alphaCheck.hasAlpha()) {
+ QBitmap m = QBitmap::fromImage(image.createAlphaMask(flags));
+ setMask(m);
+ }
+}
+
+Qt::HANDLE QX11PixmapData::createBitmapFromImage(const QImage &image)
+{
+ QImage img = image.convertToFormat(QImage::Format_MonoLSB);
+ const QRgb c0 = QColor(Qt::black).rgb();
+ const QRgb c1 = QColor(Qt::white).rgb();
+ if (img.color(0) == c0 && img.color(1) == c1) {
+ img.invertPixels();
+ img.setColor(0, c1);
+ img.setColor(1, c0);
+ }
+
+ char *bits;
+ uchar *tmp_bits;
+ int w = img.width();
+ int h = img.height();
+ int bpl = (w + 7) / 8;
+ int ibpl = img.bytesPerLine();
+ if (bpl != ibpl) {
+ tmp_bits = new uchar[bpl*h];
+ bits = (char *)tmp_bits;
+ uchar *p, *b;
+ int y;
+ b = tmp_bits;
+ p = img.scanLine(0);
+ for (y = 0; y < h; y++) {
+ memcpy(b, p, bpl);
+ b += bpl;
+ p += ibpl;
+ }
+ } else {
+ bits = (char *)img.bits();
+ tmp_bits = 0;
+ }
+ Qt::HANDLE hd = (Qt::HANDLE)XCreateBitmapFromData(X11->display,
+ QX11Info::appRootWindow(),
+ bits, w, h);
+ if (tmp_bits) // Avoid purify complaint
+ delete [] tmp_bits;
+ return hd;
+}
+
+void QX11PixmapData::bitmapFromImage(const QImage &image)
+{
+ w = image.width();
+ h = image.height();
+ d = 1;
+ is_null = (w <= 0 || h <= 0);
+ hd = createBitmapFromImage(image);
+#ifndef QT_NO_XRENDER
+ if (X11->use_xrender)
+ picture = XRenderCreatePicture(X11->display, hd,
+ XRenderFindStandardFormat(X11->display, PictStandardA1), 0, 0);
+#endif // QT_NO_XRENDER
+}
+
+void QX11PixmapData::fill(const QColor &fillColor)
+{
+ if (fillColor.alpha() != 255) {
+#ifndef QT_NO_XRENDER
+ if (X11->use_xrender) {
+ if (!picture || d != 32)
+ convertToARGB32(/*preserveContents = */false);
+
+ ::Picture src = X11->getSolidFill(xinfo.screen(), fillColor);
+ XRenderComposite(X11->display, PictOpSrc, src, 0, picture,
+ 0, 0, width(), height(),
+ 0, 0, width(), height());
+ } else
+#endif
+ {
+ QImage im(width(), height(), QImage::Format_ARGB32_Premultiplied);
+ im.fill(PREMUL(fillColor.rgba()));
+ release();
+ fromImage(im, Qt::AutoColor | Qt::OrderedAlphaDither);
+ }
+ return;
+ }
+
+ GC gc = XCreateGC(X11->display, hd, 0, 0);
+ if (depth() == 1) {
+ XSetForeground(X11->display, gc, qGray(fillColor.rgb()) > 127 ? 0 : 1);
+ } else if (X11->use_xrender && d >= 24) {
+ XSetForeground(X11->display, gc, fillColor.rgba());
+ } else {
+ XSetForeground(X11->display, gc,
+ QColormap::instance(xinfo.screen()).pixel(fillColor));
+ }
+ XFillRectangle(X11->display, hd, gc, 0, 0, width(), height());
+ XFreeGC(X11->display, gc);
+}
+
+QX11PixmapData::~QX11PixmapData()
+{
+ // Cleanup hooks have to be called before the handles are freed
+ if (is_cached) {
+ QImagePixmapCleanupHooks::executePixmapDataDestructionHooks(this);
+ is_cached = false;
+ }
+
+ release();
+}
+
+void QX11PixmapData::release()
+{
+ delete pengine;
+ pengine = 0;
+
+ if (!X11) {
+ // At this point, the X server will already have freed our resources,
+ // so there is nothing to do.
+ return;
+ }
+
+ if (x11_mask) {
+#ifndef QT_NO_XRENDER
+ if (mask_picture)
+ XRenderFreePicture(X11->display, mask_picture);
+ mask_picture = 0;
+#endif
+ XFreePixmap(X11->display, x11_mask);
+ x11_mask = 0;
+ }
+
+ if (hd) {
+#ifndef QT_NO_XRENDER
+ if (picture) {
+ XRenderFreePicture(X11->display, picture);
+ picture = 0;
+ }
+#endif // QT_NO_XRENDER
+
+ if (hd2) {
+ XFreePixmap(xinfo.display(), hd2);
+ hd2 = 0;
+ }
+ if (!(flags & Readonly))
+ XFreePixmap(xinfo.display(), hd);
+ hd = 0;
+ }
+}
+
+QPixmap QX11PixmapData::alphaChannel() const
+{
+ if (!hasAlphaChannel()) {
+ QPixmap pm(w, h);
+ pm.fill(Qt::white);
+ return pm;
+ }
+ QImage im(toImage());
+ return QPixmap::fromImage(im.alphaChannel(), Qt::OrderedDither);
+}
+
+void QX11PixmapData::setAlphaChannel(const QPixmap &alpha)
+{
+ QImage image(toImage());
+ image.setAlphaChannel(alpha.toImage());
+ release();
+ fromImage(image, Qt::OrderedDither | Qt::OrderedAlphaDither);
+}
+
+
+QBitmap QX11PixmapData::mask() const
+{
+ QBitmap mask;
+#ifndef QT_NO_XRENDER
+ if (picture && d == 32) {
+ // #### slow - there must be a better way..
+ mask = QBitmap::fromImage(toImage().createAlphaMask());
+ } else
+#endif
+ if (d == 1) {
+ QX11PixmapData *that = const_cast<QX11PixmapData*>(this);
+ mask = QPixmap(that);
+ } else {
+ mask = mask_to_bitmap(xinfo.screen());
+ }
+ return mask;
+}
+
+/*!
+ Sets a mask bitmap.
+
+ The \a newmask bitmap defines the clip mask for this pixmap. Every
+ pixel in \a newmask corresponds to a pixel in this pixmap. Pixel
+ value 1 means opaque and pixel value 0 means transparent. The mask
+ must have the same size as this pixmap.
+
+ \warning Setting the mask on a pixmap will cause any alpha channel
+ data to be cleared. For example:
+ \snippet doc/src/snippets/image/image.cpp 2
+ Now, alpha and alphacopy are visually different.
+
+ Setting a null mask resets the mask.
+
+ The effect of this function is undefined when the pixmap is being
+ painted on.
+
+ \sa mask(), {QPixmap#Pixmap Transformations}{Pixmap
+ Transformations}, QBitmap
+*/
+void QX11PixmapData::setMask(const QBitmap &newmask)
+{
+ if (newmask.isNull()) { // clear mask
+#ifndef QT_NO_XRENDER
+ if (picture && d == 32) {
+ QX11PixmapData newData(pixelType());
+ newData.resize(w, h);
+ newData.fill(Qt::black);
+ XRenderComposite(X11->display, PictOpOver,
+ picture, 0, newData.picture,
+ 0, 0, 0, 0, 0, 0, w, h);
+ release();
+ *this = newData;
+ // the new QX11PixmapData object isn't referenced yet, so
+ // ref it
+ ref.ref();
+
+ // the below is to make sure the QX11PixmapData destructor
+ // doesn't delete our newly created render picture
+ newData.hd = 0;
+ newData.x11_mask = 0;
+ newData.picture = 0;
+ newData.mask_picture = 0;
+ newData.hd2 = 0;
+ } else
+#endif
+ if (x11_mask) {
+#ifndef QT_NO_XRENDER
+ if (picture) {
+ XRenderPictureAttributes attrs;
+ attrs.alpha_map = 0;
+ XRenderChangePicture(X11->display, picture, CPAlphaMap,
+ &attrs);
+ }
+ if (mask_picture)
+ XRenderFreePicture(X11->display, mask_picture);
+ mask_picture = 0;
+#endif
+ XFreePixmap(X11->display, x11_mask);
+ x11_mask = 0;
+ }
+ return;
+ }
+
+#ifndef QT_NO_XRENDER
+ if (picture && d == 32) {
+ XRenderComposite(X11->display, PictOpSrc,
+ picture, newmask.x11PictureHandle(),
+ picture, 0, 0, 0, 0, 0, 0, w, h);
+ } else
+#endif
+ if (depth() == 1) {
+ XGCValues vals;
+ vals.function = GXand;
+ GC gc = XCreateGC(X11->display, hd, GCFunction, &vals);
+ XCopyArea(X11->display, newmask.handle(), hd, gc, 0, 0,
+ width(), height(), 0, 0);
+ XFreeGC(X11->display, gc);
+ } else {
+ // ##### should or the masks together
+ if (x11_mask) {
+ XFreePixmap(X11->display, x11_mask);
+#ifndef QT_NO_XRENDER
+ if (mask_picture)
+ XRenderFreePicture(X11->display, mask_picture);
+#endif
+ }
+ x11_mask = QX11PixmapData::bitmap_to_mask(newmask, xinfo.screen());
+#ifndef QT_NO_XRENDER
+ if (picture) {
+ mask_picture = XRenderCreatePicture(X11->display, x11_mask,
+ XRenderFindStandardFormat(X11->display, PictStandardA1), 0, 0);
+ XRenderPictureAttributes attrs;
+ attrs.alpha_map = mask_picture;
+ XRenderChangePicture(X11->display, picture, CPAlphaMap, &attrs);
+ }
+#endif
+ }
+}
+
+int QX11PixmapData::metric(QPaintDevice::PaintDeviceMetric metric) const
+{
+ switch (metric) {
+ case QPaintDevice::PdmWidth:
+ return w;
+ case QPaintDevice::PdmHeight:
+ return h;
+ case QPaintDevice::PdmNumColors:
+ return 1 << d;
+ case QPaintDevice::PdmDepth:
+ return d;
+ case QPaintDevice::PdmWidthMM: {
+ const int screen = xinfo.screen();
+ const int mm = DisplayWidthMM(X11->display, screen) * w
+ / DisplayWidth(X11->display, screen);
+ return mm;
+ }
+ case QPaintDevice::PdmHeightMM: {
+ const int screen = xinfo.screen();
+ const int mm = (DisplayHeightMM(X11->display, screen) * h)
+ / DisplayHeight(X11->display, screen);
+ return mm;
+ }
+ case QPaintDevice::PdmDpiX:
+ case QPaintDevice::PdmPhysicalDpiX:
+ return QX11Info::appDpiX(xinfo.screen());
+ case QPaintDevice::PdmDpiY:
+ case QPaintDevice::PdmPhysicalDpiY:
+ return QX11Info::appDpiY(xinfo.screen());
+ default:
+ qWarning("QX11PixmapData::metric(): Invalid metric");
+ return 0;
+ }
+}
+
+struct QXImageWrapper
+{
+ XImage *xi;
+};
+
+bool QX11PixmapData::canTakeQImageFromXImage(const QXImageWrapper &xiWrapper) const
+{
+ XImage *xi = xiWrapper.xi;
+
+ // ARGB32_Premultiplied
+ if (picture && depth() == 32)
+ return true;
+
+ Visual *visual = (Visual *)xinfo.visual();
+
+ // RGB32
+ if (depth() == 24 && xi->bits_per_pixel == 32 && visual->red_mask == 0xff0000
+ && visual->green_mask == 0xff00 && visual->blue_mask == 0xff)
+ return true;
+
+ // RGB16
+ if (depth() == 16 && xi->bits_per_pixel == 16 && visual->red_mask == 0xf800
+ && visual->green_mask == 0x7e0 && visual->blue_mask == 0x1f)
+ return true;
+
+ return false;
+}
+
+QImage QX11PixmapData::takeQImageFromXImage(const QXImageWrapper &xiWrapper) const
+{
+ XImage *xi = xiWrapper.xi;
+
+ QImage::Format format = QImage::Format_ARGB32_Premultiplied;
+ if (depth() == 24)
+ format = QImage::Format_RGB32;
+ else if (depth() == 16)
+ format = QImage::Format_RGB16;
+
+ QImage image((uchar *)xi->data, xi->width, xi->height, xi->bytes_per_line, format);
+ // take ownership
+ image.data_ptr()->own_data = true;
+ xi->data = 0;
+
+ // we may have to swap the byte order
+ if ((QSysInfo::ByteOrder == QSysInfo::LittleEndian && xi->byte_order == MSBFirst)
+ || (QSysInfo::ByteOrder == QSysInfo::BigEndian && xi->byte_order == LSBFirst))
+ {
+ for (int i=0; i < image.height(); i++) {
+ if (depth() == 16) {
+ ushort *p = (ushort*)image.scanLine(i);
+ ushort *end = p + image.width();
+ while (p < end) {
+ *p = ((*p << 8) & 0xff00) | ((*p >> 8) & 0x00ff);
+ p++;
+ }
+ } else {
+ uint *p = (uint*)image.scanLine(i);
+ uint *end = p + image.width();
+ while (p < end) {
+ *p = ((*p << 24) & 0xff000000) | ((*p << 8) & 0x00ff0000)
+ | ((*p >> 8) & 0x0000ff00) | ((*p >> 24) & 0x000000ff);
+ p++;
+ }
+ }
+ }
+ }
+
+ // fix-up alpha channel
+ if (format == QImage::Format_RGB32) {
+ QRgb *p = (QRgb *)image.bits();
+ for (int y = 0; y < xi->height; ++y) {
+ for (int x = 0; x < xi->width; ++x)
+ p[x] |= 0xff000000;
+ p += xi->bytes_per_line / 4;
+ }
+ }
+
+ XDestroyImage(xi);
+ return image;
+}
+
+QImage QX11PixmapData::toImage(const QRect &rect) const
+{
+ QXImageWrapper xiWrapper;
+ xiWrapper.xi = XGetImage(X11->display, hd, rect.x(), rect.y(), rect.width(), rect.height(),
+ AllPlanes, (depth() == 1) ? XYPixmap : ZPixmap);
+
+ Q_CHECK_PTR(xiWrapper.xi);
+ if (!xiWrapper.xi)
+ return QImage();
+
+ if (!x11_mask && canTakeQImageFromXImage(xiWrapper))
+ return takeQImageFromXImage(xiWrapper);
+
+ QImage image = toImage(xiWrapper, rect);
+ qSafeXDestroyImage(xiWrapper.xi);
+ return image;
+}
+
+/*!
+ Converts the pixmap to a QImage. Returns a null image if the
+ conversion fails.
+
+ If the pixmap has 1-bit depth, the returned image will also be 1
+ bit deep. If the pixmap has 2- to 8-bit depth, the returned image
+ has 8-bit depth. If the pixmap has greater than 8-bit depth, the
+ returned image has 32-bit depth.
+
+ Note that for the moment, alpha masks on monochrome images are
+ ignored.
+
+ \sa fromImage(), {QImage#Image Formats}{Image Formats}
+*/
+
+QImage QX11PixmapData::toImage() const
+{
+ return toImage(QRect(0, 0, w, h));
+}
+
+QImage QX11PixmapData::toImage(const QXImageWrapper &xiWrapper, const QRect &rect) const
+{
+ XImage *xi = xiWrapper.xi;
+
+ int d = depth();
+ Visual *visual = (Visual *)xinfo.visual();
+ bool trucol = (visual->c_class >= TrueColor) && d > 1;
+
+ QImage::Format format = QImage::Format_Mono;
+ if (d > 1 && d <= 8) {
+ d = 8;
+ format = QImage::Format_Indexed8;
+ }
+ // we could run into the situation where d == 8 AND trucol is true, which can
+ // cause problems when converting to and from images. in this case, always treat
+ // the depth as 32...
+ if (d > 8 || trucol) {
+ d = 32;
+ format = QImage::Format_RGB32;
+ }
+
+ if (d == 1 && xi->bitmap_bit_order == LSBFirst)
+ format = QImage::Format_MonoLSB;
+ if (x11_mask && format == QImage::Format_RGB32)
+ format = QImage::Format_ARGB32;
+
+ QImage image(xi->width, xi->height, format);
+ if (image.isNull()) // could not create image
+ return image;
+
+ QImage alpha;
+ if (x11_mask) {
+ if (rect.contains(QRect(0, 0, w, h)))
+ alpha = mask().toImage();
+ else
+ alpha = mask().toImage().copy(rect);
+ }
+ bool ale = alpha.format() == QImage::Format_MonoLSB;
+
+ if (trucol) { // truecolor
+ const uint red_mask = (uint)visual->red_mask;
+ const uint green_mask = (uint)visual->green_mask;
+ const uint blue_mask = (uint)visual->blue_mask;
+ const int red_shift = highest_bit(red_mask) - 7;
+ const int green_shift = highest_bit(green_mask) - 7;
+ const int blue_shift = highest_bit(blue_mask) - 7;
+
+ const uint red_bits = n_bits(red_mask);
+ const uint green_bits = n_bits(green_mask);
+ const uint blue_bits = n_bits(blue_mask);
+
+ static uint red_table_bits = 0;
+ static uint green_table_bits = 0;
+ static uint blue_table_bits = 0;
+
+ if (red_bits < 8 && red_table_bits != red_bits) {
+ build_scale_table(&red_scale_table, red_bits);
+ red_table_bits = red_bits;
+ }
+ if (blue_bits < 8 && blue_table_bits != blue_bits) {
+ build_scale_table(&blue_scale_table, blue_bits);
+ blue_table_bits = blue_bits;
+ }
+ if (green_bits < 8 && green_table_bits != green_bits) {
+ build_scale_table(&green_scale_table, green_bits);
+ green_table_bits = green_bits;
+ }
+
+ int r, g, b;
+
+ QRgb *dst;
+ uchar *src;
+ uint pixel;
+ int bppc = xi->bits_per_pixel;
+
+ if (bppc > 8 && xi->byte_order == LSBFirst)
+ bppc++;
+
+ for (int y = 0; y < xi->height; ++y) {
+ uchar* asrc = x11_mask ? alpha.scanLine(y) : 0;
+ dst = (QRgb *)image.scanLine(y);
+ src = (uchar *)xi->data + xi->bytes_per_line*y;
+ for (int x = 0; x < xi->width; x++) {
+ switch (bppc) {
+ case 8:
+ pixel = *src++;
+ break;
+ case 16: // 16 bit MSB
+ pixel = src[1] | (uint)src[0] << 8;
+ src += 2;
+ break;
+ case 17: // 16 bit LSB
+ pixel = src[0] | (uint)src[1] << 8;
+ src += 2;
+ break;
+ case 24: // 24 bit MSB
+ pixel = src[2] | (uint)src[1] << 8 | (uint)src[0] << 16;
+ src += 3;
+ break;
+ case 25: // 24 bit LSB
+ pixel = src[0] | (uint)src[1] << 8 | (uint)src[2] << 16;
+ src += 3;
+ break;
+ case 32: // 32 bit MSB
+ pixel = src[3] | (uint)src[2] << 8 | (uint)src[1] << 16 | (uint)src[0] << 24;
+ src += 4;
+ break;
+ case 33: // 32 bit LSB
+ pixel = src[0] | (uint)src[1] << 8 | (uint)src[2] << 16 | (uint)src[3] << 24;
+ src += 4;
+ break;
+ default: // should not really happen
+ x = xi->width; // leave loop
+ y = xi->height;
+ pixel = 0; // eliminate compiler warning
+ qWarning("QPixmap::convertToImage: Invalid depth %d", bppc);
+ }
+ if (red_shift > 0)
+ r = (pixel & red_mask) >> red_shift;
+ else
+ r = (pixel & red_mask) << -red_shift;
+ if (green_shift > 0)
+ g = (pixel & green_mask) >> green_shift;
+ else
+ g = (pixel & green_mask) << -green_shift;
+ if (blue_shift > 0)
+ b = (pixel & blue_mask) >> blue_shift;
+ else
+ b = (pixel & blue_mask) << -blue_shift;
+
+ if (red_bits < 8)
+ r = red_scale_table[r];
+ if (green_bits < 8)
+ g = green_scale_table[g];
+ if (blue_bits < 8)
+ b = blue_scale_table[b];
+
+ if (x11_mask) {
+ if (ale) {
+ *dst++ = (asrc[x >> 3] & (1 << (x & 7))) ? qRgba(r, g, b, 0xff) : 0;
+ } else {
+ *dst++ = (asrc[x >> 3] & (0x80 >> (x & 7))) ? qRgba(r, g, b, 0xff) : 0;
+ }
+ } else {
+ *dst++ = qRgb(r, g, b);
+ }
+ }
+ }
+ } else if (xi->bits_per_pixel == d) { // compatible depth
+ char *xidata = xi->data; // copy each scanline
+ int bpl = qMin(image.bytesPerLine(),xi->bytes_per_line);
+ for (int y=0; y<xi->height; y++) {
+ memcpy(image.scanLine(y), xidata, bpl);
+ xidata += xi->bytes_per_line;
+ }
+ } else {
+ /* Typically 2 or 4 bits display depth */
+ qWarning("QPixmap::convertToImage: Display not supported (bpp=%d)",
+ xi->bits_per_pixel);
+ return QImage();
+ }
+
+ if (d == 1) { // bitmap
+ image.setColorCount(2);
+ image.setColor(0, qRgb(255,255,255));
+ image.setColor(1, qRgb(0,0,0));
+ } else if (!trucol) { // pixmap with colormap
+ register uchar *p;
+ uchar *end;
+ uchar use[256]; // pixel-in-use table
+ uchar pix[256]; // pixel translation table
+ int ncols, bpl;
+ memset(use, 0, 256);
+ memset(pix, 0, 256);
+ bpl = image.bytesPerLine();
+
+ if (x11_mask) { // which pixels are used?
+ for (int i = 0; i < xi->height; i++) {
+ uchar* asrc = alpha.scanLine(i);
+ p = image.scanLine(i);
+ if (ale) {
+ for (int x = 0; x < xi->width; x++) {
+ if (asrc[x >> 3] & (1 << (x & 7)))
+ use[*p] = 1;
+ ++p;
+ }
+ } else {
+ for (int x = 0; x < xi->width; x++) {
+ if (asrc[x >> 3] & (0x80 >> (x & 7)))
+ use[*p] = 1;
+ ++p;
+ }
+ }
+ }
+ } else {
+ for (int i = 0; i < xi->height; i++) {
+ p = image.scanLine(i);
+ end = p + bpl;
+ while (p < end)
+ use[*p++] = 1;
+ }
+ }
+ ncols = 0;
+ for (int i = 0; i < 256; i++) { // build translation table
+ if (use[i])
+ pix[i] = ncols++;
+ }
+ for (int i = 0; i < xi->height; i++) { // translate pixels
+ p = image.scanLine(i);
+ end = p + bpl;
+ while (p < end) {
+ *p = pix[*p];
+ p++;
+ }
+ }
+ if (x11_mask) {
+ int trans;
+ if (ncols < 256) {
+ trans = ncols++;
+ image.setColorCount(ncols); // create color table
+ image.setColor(trans, 0x00000000);
+ } else {
+ image.setColorCount(ncols); // create color table
+ // oh dear... no spare "transparent" pixel.
+ // use first pixel in image (as good as any).
+ trans = image.scanLine(0)[0];
+ }
+ for (int i = 0; i < xi->height; i++) {
+ uchar* asrc = alpha.scanLine(i);
+ p = image.scanLine(i);
+ if (ale) {
+ for (int x = 0; x < xi->width; x++) {
+ if (!(asrc[x >> 3] & (1 << (x & 7))))
+ *p = trans;
+ ++p;
+ }
+ } else {
+ for (int x = 0; x < xi->width; x++) {
+ if (!(asrc[x >> 3] & (1 << (7 -(x & 7)))))
+ *p = trans;
+ ++p;
+ }
+ }
+ }
+ } else {
+ image.setColorCount(ncols); // create color table
+ }
+ QVector<QColor> colors = QColormap::instance(xinfo.screen()).colormap();
+ int j = 0;
+ for (int i=0; i<colors.size(); i++) { // translate pixels
+ if (use[i])
+ image.setColor(j++, 0xff000000 | colors.at(i).rgb());
+ }
+ }
+
+ return image;
+}
+
+/*!
+ Returns a copy of the pixmap that is transformed using the given
+ transformation \a matrix and transformation \a mode. The original
+ pixmap is not changed.
+
+ The transformation \a matrix is internally adjusted to compensate
+ for unwanted translation; i.e. the pixmap produced is the smallest
+ pixmap that contains all the transformed points of the original
+ pixmap. Use the trueMatrix() function to retrieve the actual
+ matrix used for transforming the pixmap.
+
+ This function is slow because it involves transformation to a
+ QImage, non-trivial computations and a transformation back to a
+ QPixmap.
+
+ \sa trueMatrix(), {QPixmap#Pixmap Transformations}{Pixmap
+ Transformations}
+*/
+QPixmap QX11PixmapData::transformed(const QTransform &transform,
+ Qt::TransformationMode mode ) const
+{
+ if (mode == Qt::SmoothTransformation || transform.type() >= QTransform::TxProject) {
+ QImage image = toImage();
+ return QPixmap::fromImage(image.transformed(transform, mode));
+ }
+
+ uint w = 0;
+ uint h = 0; // size of target pixmap
+ uint ws, hs; // size of source pixmap
+ uchar *dptr; // data in target pixmap
+ uint dbpl, dbytes; // bytes per line/bytes total
+ uchar *sptr; // data in original pixmap
+ int sbpl; // bytes per line in original
+ int bpp; // bits per pixel
+ bool depth1 = depth() == 1;
+ Display *dpy = X11->display;
+
+ ws = width();
+ hs = height();
+
+ QTransform mat(transform.m11(), transform.m12(), transform.m13(),
+ transform.m21(), transform.m22(), transform.m23(),
+ 0., 0., 1);
+ bool complex_xform = false;
+ qreal scaledWidth;
+ qreal scaledHeight;
+
+ if (mat.type() <= QTransform::TxScale) {
+ scaledHeight = qAbs(mat.m22()) * hs + 0.9999;
+ scaledWidth = qAbs(mat.m11()) * ws + 0.9999;
+ h = qAbs(int(scaledHeight));
+ w = qAbs(int(scaledWidth));
+ } else { // rotation or shearing
+ QPolygonF a(QRectF(0, 0, ws, hs));
+ a = mat.map(a);
+ QRect r = a.boundingRect().toAlignedRect();
+ w = r.width();
+ h = r.height();
+ scaledWidth = w;
+ scaledHeight = h;
+ complex_xform = true;
+ }
+ mat = QPixmap::trueMatrix(mat, ws, hs); // true matrix
+
+ bool invertible;
+ mat = mat.inverted(&invertible); // invert matrix
+
+ if (h == 0 || w == 0 || !invertible
+ || qAbs(scaledWidth) >= 32768 || qAbs(scaledHeight) >= 32768 )
+ // error, return null pixmap
+ return QPixmap();
+
+#if defined(QT_MITSHM)
+ static bool try_once = true;
+ if (try_once) {
+ try_once = false;
+ if (!xshminit)
+ qt_create_mitshm_buffer(this, 800, 600);
+ }
+
+ bool use_mitshm = xshmimg && !depth1 &&
+ xshmimg->width >= w && xshmimg->height >= h;
+#endif
+ XImage *xi = XGetImage(X11->display, handle(), 0, 0, ws, hs, AllPlanes,
+ depth1 ? XYPixmap : ZPixmap);
+
+ if (!xi)
+ return QPixmap();
+
+ sbpl = xi->bytes_per_line;
+ sptr = (uchar *)xi->data;
+ bpp = xi->bits_per_pixel;
+
+ if (depth1)
+ dbpl = (w+7)/8;
+ else
+ dbpl = ((w*bpp+31)/32)*4;
+ dbytes = dbpl*h;
+
+#if defined(QT_MITSHM)
+ if (use_mitshm) {
+ dptr = (uchar *)xshmimg->data;
+ uchar fillbyte = bpp == 8 ? white.pixel() : 0xff;
+ for (int y=0; y<h; y++)
+ memset(dptr + y*xshmimg->bytes_per_line, fillbyte, dbpl);
+ } else {
+#endif
+ dptr = (uchar *)malloc(dbytes); // create buffer for bits
+ Q_CHECK_PTR(dptr);
+ if (depth1) // fill with zeros
+ memset(dptr, 0, dbytes);
+ else if (bpp == 8) // fill with background color
+ memset(dptr, WhitePixel(X11->display, xinfo.screen()), dbytes);
+ else
+ memset(dptr, 0, dbytes);
+#if defined(QT_MITSHM)
+ }
+#endif
+
+ // #define QT_DEBUG_XIMAGE
+#if defined(QT_DEBUG_XIMAGE)
+ qDebug("----IMAGE--INFO--------------");
+ qDebug("width............. %d", xi->width);
+ qDebug("height............ %d", xi->height);
+ qDebug("xoffset........... %d", xi->xoffset);
+ qDebug("format............ %d", xi->format);
+ qDebug("byte order........ %d", xi->byte_order);
+ qDebug("bitmap unit....... %d", xi->bitmap_unit);
+ qDebug("bitmap bit order.. %d", xi->bitmap_bit_order);
+ qDebug("depth............. %d", xi->depth);
+ qDebug("bytes per line.... %d", xi->bytes_per_line);
+ qDebug("bits per pixel.... %d", xi->bits_per_pixel);
+#endif
+
+ int type;
+ if (xi->bitmap_bit_order == MSBFirst)
+ type = QT_XFORM_TYPE_MSBFIRST;
+ else
+ type = QT_XFORM_TYPE_LSBFIRST;
+ int xbpl, p_inc;
+ if (depth1) {
+ xbpl = (w+7)/8;
+ p_inc = dbpl - xbpl;
+ } else {
+ xbpl = (w*bpp)/8;
+ p_inc = dbpl - xbpl;
+#if defined(QT_MITSHM)
+ if (use_mitshm)
+ p_inc = xshmimg->bytes_per_line - xbpl;
+#endif
+ }
+
+ if (!qt_xForm_helper(mat, xi->xoffset, type, bpp, dptr, xbpl, p_inc, h, sptr, sbpl, ws, hs)){
+ qWarning("QPixmap::transform: display not supported (bpp=%d)",bpp);
+ QPixmap pm;
+ return pm;
+ }
+
+ qSafeXDestroyImage(xi);
+
+ if (depth1) { // mono bitmap
+ QBitmap bm = QBitmap::fromData(QSize(w, h), dptr,
+ BitmapBitOrder(X11->display) == MSBFirst
+ ? QImage::Format_Mono
+ : QImage::Format_MonoLSB);
+ free(dptr);
+ return bm;
+ } else { // color pixmap
+ QX11PixmapData *x11Data = new QX11PixmapData(QPixmapData::PixmapType);
+ QPixmap pm(x11Data);
+ x11Data->flags &= ~QX11PixmapData::Uninitialized;
+ x11Data->xinfo = xinfo;
+ x11Data->d = d;
+ x11Data->w = w;
+ x11Data->h = h;
+ x11Data->is_null = (w <= 0 || h <= 0);
+ x11Data->hd = (Qt::HANDLE)XCreatePixmap(X11->display,
+ RootWindow(X11->display, xinfo.screen()),
+ w, h, d);
+ x11Data->setSerialNumber(qt_pixmap_serial.fetchAndAddRelaxed(1));
+
+#ifndef QT_NO_XRENDER
+ if (X11->use_xrender) {
+ XRenderPictFormat *format = x11Data->d == 32
+ ? XRenderFindStandardFormat(X11->display, PictStandardARGB32)
+ : XRenderFindVisualFormat(X11->display, (Visual *) x11Data->xinfo.visual());
+ x11Data->picture = XRenderCreatePicture(X11->display, x11Data->hd, format, 0, 0);
+ }
+#endif // QT_NO_XRENDER
+
+ GC gc = XCreateGC(X11->display, x11Data->hd, 0, 0);
+#if defined(QT_MITSHM)
+ if (use_mitshm) {
+ XCopyArea(dpy, xshmpm, x11Data->hd, gc, 0, 0, w, h, 0, 0);
+ } else
+#endif
+ {
+ xi = XCreateImage(dpy, (Visual*)x11Data->xinfo.visual(),
+ x11Data->d,
+ ZPixmap, 0, (char *)dptr, w, h, 32, 0);
+ XPutImage(dpy, pm.handle(), gc, xi, 0, 0, 0, 0, w, h);
+ qSafeXDestroyImage(xi);
+ }
+ XFreeGC(X11->display, gc);
+
+ if (x11_mask) { // xform mask, too
+ pm.setMask(mask_to_bitmap(xinfo.screen()).transformed(transform));
+ } else if (d != 32 && complex_xform) { // need a mask!
+ QBitmap mask(ws, hs);
+ mask.fill(Qt::color1);
+ pm.setMask(mask.transformed(transform));
+ }
+ return pm;
+ }
+}
+
+int QPixmap::x11SetDefaultScreen(int screen)
+{
+ int old = defaultScreen;
+ defaultScreen = screen;
+ return old;
+}
+
+void QPixmap::x11SetScreen(int screen)
+{
+ if (paintingActive()) {
+ qWarning("QPixmap::x11SetScreen(): Cannot change screens during painting");
+ return;
+ }
+
+ if (isNull())
+ return;
+
+ if (data->classId() != QPixmapData::X11Class)
+ return;
+
+ if (screen < 0)
+ screen = QX11Info::appScreen();
+
+ QX11PixmapData *x11Data = static_cast<QX11PixmapData*>(data.data());
+ if (screen == x11Data->xinfo.screen())
+ return; // nothing to do
+
+ if (isNull()) {
+ QX11InfoData* xd = x11Data->xinfo.getX11Data(true);
+ xd->screen = screen;
+ xd->depth = QX11Info::appDepth(screen);
+ xd->cells = QX11Info::appCells(screen);
+ xd->colormap = QX11Info::appColormap(screen);
+ xd->defaultColormap = QX11Info::appDefaultColormap(screen);
+ xd->visual = (Visual *)QX11Info::appVisual(screen);
+ xd->defaultVisual = QX11Info::appDefaultVisual(screen);
+ x11Data->xinfo.setX11Data(xd);
+ return;
+ }
+#if 0
+ qDebug("QPixmap::x11SetScreen for %p from %d to %d. Size is %d/%d", x11Data, x11Data->xinfo.screen(), screen, width(), height());
+#endif
+
+ x11SetDefaultScreen(screen);
+ *this = qt_toX11Pixmap(toImage());
+}
+
+QPixmap QPixmap::grabWindow(WId window, int x, int y, int w, int h)
+{
+ if (w == 0 || h == 0)
+ return QPixmap();
+
+ Display *dpy = X11->display;
+ XWindowAttributes window_attr;
+ if (!XGetWindowAttributes(dpy, window, &window_attr))
+ return QPixmap();
+
+ if (w < 0)
+ w = window_attr.width - x;
+ if (h < 0)
+ h = window_attr.height - y;
+
+ // determine the screen
+ int scr;
+ for (scr = 0; scr < ScreenCount(dpy); ++scr) {
+ if (window_attr.root == RootWindow(dpy, scr)) // found it
+ break;
+ }
+ if (scr >= ScreenCount(dpy)) // sanity check
+ return QPixmap();
+
+
+ // get the depth of the root window
+ XWindowAttributes root_attr;
+ if (!XGetWindowAttributes(dpy, window_attr.root, &root_attr))
+ return QPixmap();
+
+ if (window_attr.depth == root_attr.depth) {
+ // if the depth of the specified window and the root window are the
+ // same, grab pixels from the root window (so that we get the any
+ // overlapping windows and window manager frames)
+
+ // map x and y to the root window
+ WId unused;
+ if (!XTranslateCoordinates(dpy, window, window_attr.root, x, y,
+ &x, &y, &unused))
+ return QPixmap();
+
+ window = window_attr.root;
+ window_attr = root_attr;
+ }
+
+ QX11PixmapData *data = new QX11PixmapData(QPixmapData::PixmapType);
+
+ void qt_x11_getX11InfoForWindow(QX11Info * xinfo, const XWindowAttributes &a);
+ qt_x11_getX11InfoForWindow(&data->xinfo,window_attr);
+
+ data->resize(w, h);
+
+ QPixmap pm(data);
+
+ data->flags &= ~QX11PixmapData::Uninitialized;
+ pm.x11SetScreen(scr);
+
+ GC gc = XCreateGC(dpy, pm.handle(), 0, 0);
+ XSetSubwindowMode(dpy, gc, IncludeInferiors);
+ XCopyArea(dpy, window, pm.handle(), gc, x, y, w, h, 0, 0);
+ XFreeGC(dpy, gc);
+
+ return pm;
+}
+
+bool QX11PixmapData::hasAlphaChannel() const
+{
+ return d == 32;
+}
+
+const QX11Info &QPixmap::x11Info() const
+{
+ if (data && data->classId() == QPixmapData::X11Class)
+ return static_cast<QX11PixmapData*>(data.data())->xinfo;
+ else {
+ static QX11Info nullX11Info;
+ return nullX11Info;
+ }
+}
+
+#if !defined(QT_NO_XRENDER)
+static XRenderPictFormat *qt_renderformat_for_depth(const QX11Info &xinfo, int depth)
+{
+ if (depth == 1)
+ return XRenderFindStandardFormat(X11->display, PictStandardA1);
+ else if (depth == 32)
+ return XRenderFindStandardFormat(X11->display, PictStandardARGB32);
+ else
+ return XRenderFindVisualFormat(X11->display, (Visual *)xinfo.visual());
+}
+#endif
+
+QPaintEngine* QX11PixmapData::paintEngine() const
+{
+ QX11PixmapData *that = const_cast<QX11PixmapData*>(this);
+
+ if ((flags & Readonly) && share_mode == QPixmap::ImplicitlyShared) {
+ // if someone wants to draw onto us, copy the shared contents
+ // and turn it into a fully fledged QPixmap
+ ::Pixmap hd_copy = XCreatePixmap(X11->display, RootWindow(X11->display, xinfo.screen()),
+ w, h, d);
+#if !defined(QT_NO_XRENDER)
+ XRenderPictFormat *format = qt_renderformat_for_depth(xinfo, d);
+ ::Picture picture_copy = XRenderCreatePicture(X11->display, hd_copy, format, 0, 0);
+
+ if (picture && d == 32) {
+ XRenderComposite(X11->display, PictOpSrc, picture, 0, picture_copy,
+ 0, 0, 0, 0, 0, 0, w, h);
+ XRenderFreePicture(X11->display, picture);
+ that->picture = picture_copy;
+ } else
+#endif
+ {
+ GC gc = XCreateGC(X11->display, hd_copy, 0, 0);
+ XCopyArea(X11->display, hd, hd_copy, gc, 0, 0, w, h, 0, 0);
+ XFreeGC(X11->display, gc);
+ }
+ that->hd = hd_copy;
+ that->flags &= ~QX11PixmapData::Readonly;
+ }
+
+ if (!that->pengine)
+ that->pengine = new QX11PaintEngine;
+ return that->pengine;
+}
+
+Qt::HANDLE QPixmap::x11PictureHandle() const
+{
+#ifndef QT_NO_XRENDER
+ if (data && data->classId() == QPixmapData::X11Class)
+ return static_cast<const QX11PixmapData*>(data.data())->picture;
+ else
+ return 0;
+#else
+ return 0;
+#endif // QT_NO_XRENDER
+}
+
+Qt::HANDLE QX11PixmapData::x11ConvertToDefaultDepth()
+{
+#ifndef QT_NO_XRENDER
+ if (d == QX11Info::appDepth() || !X11->use_xrender)
+ return hd;
+ if (!hd2) {
+ hd2 = XCreatePixmap(xinfo.display(), hd, w, h, QX11Info::appDepth());
+ XRenderPictFormat *format = XRenderFindVisualFormat(xinfo.display(),
+ (Visual*) xinfo.visual());
+ Picture pic = XRenderCreatePicture(xinfo.display(), hd2, format, 0, 0);
+ XRenderComposite(xinfo.display(), PictOpSrc, picture,
+ XNone, pic, 0, 0, 0, 0, 0, 0, w, h);
+ XRenderFreePicture(xinfo.display(), pic);
+ }
+ return hd2;
+#else
+ return hd;
+#endif
+}
+
+void QX11PixmapData::copy(const QPixmapData *data, const QRect &rect)
+{
+ if (data->pixelType() == BitmapType) {
+ fromImage(data->toImage().copy(rect), Qt::AutoColor);
+ return;
+ }
+
+ const QX11PixmapData *x11Data = static_cast<const QX11PixmapData*>(data);
+
+ setSerialNumber(qt_pixmap_serial.fetchAndAddRelaxed(1));
+
+ flags &= ~Uninitialized;
+ xinfo = x11Data->xinfo;
+ d = x11Data->d;
+ w = rect.width();
+ h = rect.height();
+ is_null = (w <= 0 || h <= 0);
+ hd = (Qt::HANDLE)XCreatePixmap(X11->display,
+ RootWindow(X11->display, x11Data->xinfo.screen()),
+ w, h, d);
+#ifndef QT_NO_XRENDER
+ if (X11->use_xrender) {
+ XRenderPictFormat *format = d == 32
+ ? XRenderFindStandardFormat(X11->display, PictStandardARGB32)
+ : XRenderFindVisualFormat(X11->display, (Visual *)xinfo.visual());
+ picture = XRenderCreatePicture(X11->display, hd, format, 0, 0);
+ }
+#endif // QT_NO_XRENDER
+ if (x11Data->x11_mask) {
+ x11_mask = XCreatePixmap(X11->display, hd, w, h, 1);
+#ifndef QT_NO_XRENDER
+ if (X11->use_xrender) {
+ mask_picture = XRenderCreatePicture(X11->display, x11_mask,
+ XRenderFindStandardFormat(X11->display, PictStandardA1), 0, 0);
+ XRenderPictureAttributes attrs;
+ attrs.alpha_map = x11Data->mask_picture;
+ XRenderChangePicture(X11->display, x11Data->picture, CPAlphaMap, &attrs);
+ }
+#endif
+ }
+
+#if !defined(QT_NO_XRENDER)
+ if (x11Data->picture && x11Data->d == 32) {
+ XRenderComposite(X11->display, PictOpSrc,
+ x11Data->picture, 0, picture,
+ rect.x(), rect.y(), 0, 0, 0, 0, w, h);
+ } else
+#endif
+ {
+ GC gc = XCreateGC(X11->display, hd, 0, 0);
+ XCopyArea(X11->display, x11Data->hd, hd, gc,
+ rect.x(), rect.y(), w, h, 0, 0);
+ if (x11Data->x11_mask) {
+ GC monogc = XCreateGC(X11->display, x11_mask, 0, 0);
+ XCopyArea(X11->display, x11Data->x11_mask, x11_mask, monogc,
+ rect.x(), rect.y(), w, h, 0, 0);
+ XFreeGC(X11->display, monogc);
+ }
+ XFreeGC(X11->display, gc);
+ }
+}
+
+bool QX11PixmapData::scroll(int dx, int dy, const QRect &rect)
+{
+ GC gc = XCreateGC(X11->display, hd, 0, 0);
+ XCopyArea(X11->display, hd, hd, gc,
+ rect.left(), rect.top(), rect.width(), rect.height(),
+ rect.left() + dx, rect.top() + dy);
+ XFreeGC(X11->display, gc);
+ return true;
+}
+
+#if !defined(QT_NO_XRENDER)
+void QX11PixmapData::convertToARGB32(bool preserveContents)
+{
+ if (!X11->use_xrender)
+ return;
+
+ // Q_ASSERT(count == 1);
+ if ((flags & Readonly) && share_mode == QPixmap::ExplicitlyShared)
+ return;
+
+ Pixmap pm = XCreatePixmap(X11->display, RootWindow(X11->display, xinfo.screen()),
+ w, h, 32);
+ Picture p = XRenderCreatePicture(X11->display, pm,
+ XRenderFindStandardFormat(X11->display, PictStandardARGB32), 0, 0);
+ if (picture) {
+ if (preserveContents)
+ XRenderComposite(X11->display, PictOpSrc, picture, 0, p, 0, 0, 0, 0, 0, 0, w, h);
+ if (!(flags & Readonly))
+ XRenderFreePicture(X11->display, picture);
+ }
+ if (hd && !(flags & Readonly))
+ XFreePixmap(X11->display, hd);
+ if (x11_mask) {
+ XFreePixmap(X11->display, x11_mask);
+ if (mask_picture)
+ XRenderFreePicture(X11->display, mask_picture);
+ x11_mask = 0;
+ mask_picture = 0;
+ }
+ hd = pm;
+ picture = p;
+ d = 32;
+}
+#endif
+
+QPixmap QPixmap::fromX11Pixmap(Qt::HANDLE pixmap, QPixmap::ShareMode mode)
+{
+ Window root;
+ int x;
+ int y;
+ uint width;
+ uint height;
+ uint border_width;
+ uint depth;
+ XWindowAttributes win_attribs;
+ int num_screens = ScreenCount(X11->display);
+ int screen = 0;
+
+ XGetGeometry(X11->display, pixmap, &root, &x, &y, &width, &height, &border_width, &depth);
+ XGetWindowAttributes(X11->display, root, &win_attribs);
+
+ for (; screen < num_screens; ++screen) {
+ if (win_attribs.screen == ScreenOfDisplay(X11->display, screen))
+ break;
+ }
+
+ QX11PixmapData *data = new QX11PixmapData(depth == 1 ? QPixmapData::BitmapType : QPixmapData::PixmapType);
+ data->setSerialNumber(qt_pixmap_serial.fetchAndAddRelaxed(1));
+ data->flags = QX11PixmapData::Readonly;
+ data->share_mode = mode;
+ data->w = width;
+ data->h = height;
+ data->is_null = (width <= 0 || height <= 0);
+ data->d = depth;
+ data->hd = pixmap;
+
+ if (defaultScreen >= 0 && defaultScreen != screen) {
+ QX11InfoData* xd = data->xinfo.getX11Data(true);
+ xd->screen = defaultScreen;
+ xd->depth = QX11Info::appDepth(xd->screen);
+ xd->cells = QX11Info::appCells(xd->screen);
+ xd->colormap = QX11Info::appColormap(xd->screen);
+ xd->defaultColormap = QX11Info::appDefaultColormap(xd->screen);
+ xd->visual = (Visual *)QX11Info::appVisual(xd->screen);
+ xd->defaultVisual = QX11Info::appDefaultVisual(xd->screen);
+ data->xinfo.setX11Data(xd);
+ }
+
+#ifndef QT_NO_XRENDER
+ if (X11->use_xrender) {
+ XRenderPictFormat *format = qt_renderformat_for_depth(data->xinfo, depth);
+ data->picture = XRenderCreatePicture(X11->display, data->hd, format, 0, 0);
+ }
+#endif // QT_NO_XRENDER
+
+ return QPixmap(data);
+}
+
+
+QT_END_NAMESPACE
diff --git a/src/gui/image/qpixmap_x11_p.h b/src/gui/image/qpixmap_x11_p.h
new file mode 100644
index 0000000000..eb8e5819ad
--- /dev/null
+++ b/src/gui/image/qpixmap_x11_p.h
@@ -0,0 +1,156 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPIXMAPDATA_X11_P_H
+#define QPIXMAPDATA_X11_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/private/qpixmapdata_p.h>
+#include <QtGui/private/qpixmapdatafactory_p.h>
+
+#include "QtGui/qx11info_x11.h"
+
+QT_BEGIN_NAMESPACE
+
+class QX11PaintEngine;
+
+struct QXImageWrapper;
+
+class Q_GUI_EXPORT QX11PixmapData : public QPixmapData
+{
+public:
+ QX11PixmapData(PixelType type);
+// QX11PixmapData(PixelType type, int width, int height);
+// QX11PixmapData(PixelType type, const QImage &image,
+// Qt::ImageConversionFlags flags);
+ ~QX11PixmapData();
+
+ QPixmapData *createCompatiblePixmapData() const;
+
+ void resize(int width, int height);
+ void fromImage(const QImage &image, Qt::ImageConversionFlags flags);
+ void copy(const QPixmapData *data, const QRect &rect);
+ bool scroll(int dx, int dy, const QRect &rect);
+
+ void fill(const QColor &color);
+ QBitmap mask() const;
+ void setMask(const QBitmap &mask);
+ bool hasAlphaChannel() const;
+ void setAlphaChannel(const QPixmap &alphaChannel);
+ QPixmap alphaChannel() const;
+ QPixmap transformed(const QTransform &transform,
+ Qt::TransformationMode mode) const;
+ QImage toImage() const;
+ QImage toImage(const QRect &rect) const;
+ QPaintEngine* paintEngine() const;
+
+ Qt::HANDLE handle() const { return hd; }
+ Qt::HANDLE x11ConvertToDefaultDepth();
+
+ static Qt::HANDLE createBitmapFromImage(const QImage &image);
+
+ void* gl_surface;
+#ifndef QT_NO_XRENDER
+ void convertToARGB32(bool preserveContents = true);
+#endif
+
+protected:
+ int metric(QPaintDevice::PaintDeviceMetric metric) const;
+
+private:
+ friend class QPixmap;
+ friend class QBitmap;
+ friend class QX11PaintEngine;
+ friend class QX11WindowSurface;
+ friend class QRasterWindowSurface;
+ friend class QGLContextPrivate; // Needs to access xinfo, gl_surface & flags
+ friend class QEglContext; // Needs gl_surface
+ friend class QGLContext; // Needs gl_surface
+ friend class QX11GLPixmapData; // Needs gl_surface
+ friend class QMeeGoLivePixmapData; // Needs gl_surface and flags
+ friend bool qt_createEGLSurfaceForPixmap(QPixmapData*, bool); // Needs gl_surface
+
+ void release();
+
+ QImage toImage(const QXImageWrapper &xi, const QRect &rect) const;
+
+ QBitmap mask_to_bitmap(int screen) const;
+ static Qt::HANDLE bitmap_to_mask(const QBitmap &, int screen);
+ void bitmapFromImage(const QImage &image);
+
+ bool canTakeQImageFromXImage(const QXImageWrapper &xi) const;
+ QImage takeQImageFromXImage(const QXImageWrapper &xi) const;
+
+ Qt::HANDLE hd;
+
+ enum Flag {
+ NoFlags = 0x0,
+ Uninitialized = 0x1,
+ Readonly = 0x2,
+ InvertedWhenBoundToTexture = 0x4,
+ GlSurfaceCreatedWithAlpha = 0x8
+ };
+ uint flags;
+
+ QX11Info xinfo;
+ Qt::HANDLE x11_mask;
+ Qt::HANDLE picture;
+ Qt::HANDLE mask_picture;
+ Qt::HANDLE hd2; // sorted in the default display depth
+ QPixmap::ShareMode share_mode;
+
+ QX11PaintEngine *pengine;
+};
+
+QT_END_NAMESPACE
+
+#endif // QPIXMAPDATA_X11_P_H
+
diff --git a/src/gui/image/qpixmapcache.cpp b/src/gui/image/qpixmapcache.cpp
new file mode 100644
index 0000000000..ae772d8bc3
--- /dev/null
+++ b/src/gui/image/qpixmapcache.cpp
@@ -0,0 +1,680 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#define Q_TEST_QPIXMAPCACHE
+#include "qpixmapcache.h"
+#include "qobject.h"
+#include "qdebug.h"
+#include "qpixmapcache_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QPixmapCache
+
+ \brief The QPixmapCache class provides an application-wide cache for pixmaps.
+
+ \ingroup painting
+
+ This class is a tool for optimized drawing with QPixmap. You can
+ use it to store temporary pixmaps that are expensive to generate
+ without using more storage space than cacheLimit(). Use insert()
+ to insert pixmaps, find() to find them, and clear() to empty the
+ cache.
+
+ QPixmapCache contains no member data, only static functions to
+ access the global pixmap cache. It creates an internal QCache
+ object for caching the pixmaps.
+
+ The cache associates a pixmap with a user-provided string as a key,
+ or with a QPixmapCache::Key that the cache generates.
+ Using QPixmapCache::Key for keys is faster than using strings. The string API is
+ very convenient for complex keys but the QPixmapCache::Key API will be very
+ efficient and convenient for a one-to-one object-to-pixmap mapping \mdash in
+ this case, you can store the keys as members of an object.
+
+ If two pixmaps are inserted into the cache using equal keys then the
+ last pixmap will replace the first pixmap in the cache. This follows the
+ behavior of the QHash and QCache classes.
+
+ The cache becomes full when the total size of all pixmaps in the
+ cache exceeds cacheLimit(). The initial cache limit is
+ 2048 KB (2 MB) on embedded platforms, 10240 KB (10 MB) on desktop
+ platforms; you can change this by calling setCacheLimit() with the
+ required value.
+ A pixmap takes roughly (\e{width} * \e{height} * \e{depth})/8 bytes of
+ memory.
+
+ The \e{Qt Quarterly} article
+ \l{http://qt.nokia.com/doc/qq/qq12-qpixmapcache.html}{Optimizing
+ with QPixmapCache} explains how to use QPixmapCache to speed up
+ applications by caching the results of painting.
+
+ \sa QCache, QPixmap
+*/
+
+#if defined(Q_WS_QWS) || defined(Q_WS_WINCE)
+static int cache_limit = 2048; // 2048 KB cache limit for embedded
+#else
+static int cache_limit = 10240; // 10 MB cache limit for desktop
+#endif
+
+/*!
+ \class QPixmapCache::Key
+ \brief The QPixmapCache::Key class can be used for efficient access
+ to the QPixmapCache.
+ \since 4.6
+
+ Use QPixmapCache::insert() to receive an instance of Key generated
+ by the pixmap cache. You can store the key in your own objects for
+ a very efficient one-to-one object-to-pixmap mapping.
+*/
+
+/*!
+ Constructs an empty Key object.
+*/
+QPixmapCache::Key::Key() : d(0)
+{
+}
+
+/*!
+ \internal
+ Constructs a copy of \a other.
+*/
+QPixmapCache::Key::Key(const Key &other)
+{
+ if (other.d)
+ ++(other.d->ref);
+ d = other.d;
+}
+
+/*!
+ Destroys the key.
+*/
+QPixmapCache::Key::~Key()
+{
+ if (d && --(d->ref) == 0)
+ delete d;
+}
+
+/*!
+ \internal
+
+ Returns true if this key is the same as the given \a key; otherwise returns
+ false.
+*/
+bool QPixmapCache::Key::operator ==(const Key &key) const
+{
+ return (d == key.d);
+}
+
+/*!
+ \fn bool QPixmapCache::Key::operator !=(const Key &key) const
+ \internal
+*/
+
+/*!
+ \internal
+*/
+QPixmapCache::Key &QPixmapCache::Key::operator =(const Key &other)
+{
+ if (d != other.d) {
+ if (other.d)
+ ++(other.d->ref);
+ if (d && --(d->ref) == 0)
+ delete d;
+ d = other.d;
+ }
+ return *this;
+}
+
+class QPMCache : public QObject, public QCache<QPixmapCache::Key, QPixmapCacheEntry>
+{
+ Q_OBJECT
+public:
+ QPMCache();
+ ~QPMCache();
+
+ void timerEvent(QTimerEvent *);
+ bool insert(const QString& key, const QPixmap &pixmap, int cost);
+ QPixmapCache::Key insert(const QPixmap &pixmap, int cost);
+ bool replace(const QPixmapCache::Key &key, const QPixmap &pixmap, int cost);
+ bool remove(const QString &key);
+ bool remove(const QPixmapCache::Key &key);
+
+ void resizeKeyArray(int size);
+ QPixmapCache::Key createKey();
+ void releaseKey(const QPixmapCache::Key &key);
+ void clear();
+
+ QPixmap *object(const QString &key) const;
+ QPixmap *object(const QPixmapCache::Key &key) const;
+
+ static inline QPixmapCache::KeyData *get(const QPixmapCache::Key &key)
+ {return key.d;}
+
+ static QPixmapCache::KeyData* getKeyData(QPixmapCache::Key *key);
+
+ QList< QPair<QString,QPixmap> > allPixmaps() const;
+ bool flushDetachedPixmaps(bool nt);
+
+private:
+ enum { soon_time = 10000, flush_time = 30000 };
+ int *keyArray;
+ int theid;
+ int ps;
+ int keyArraySize;
+ int freeKey;
+ QHash<QString, QPixmapCache::Key> cacheKeys;
+ bool t;
+};
+
+QT_BEGIN_INCLUDE_NAMESPACE
+#include "qpixmapcache.moc"
+QT_END_INCLUDE_NAMESPACE
+
+uint qHash(const QPixmapCache::Key &k)
+{
+ return qHash(QPMCache::get(k)->key);
+}
+
+QPMCache::QPMCache()
+ : QObject(0),
+ QCache<QPixmapCache::Key, QPixmapCacheEntry>(cache_limit * 1024),
+ keyArray(0), theid(0), ps(0), keyArraySize(0), freeKey(0), t(false)
+{
+}
+QPMCache::~QPMCache()
+{
+ clear();
+ free(keyArray);
+}
+
+/*
+ This is supposed to cut the cache size down by about 25% in a
+ minute once the application becomes idle, to let any inserted pixmap
+ remain in the cache for some time before it becomes a candidate for
+ cleaning-up, and to not cut down the size of the cache while the
+ cache is in active use.
+
+ When the last detached pixmap has been deleted from the cache, kill the
+ timer so Qt won't keep the CPU from going into sleep mode. Currently
+ the timer is not restarted when the pixmap becomes unused, but it does
+ restart once something else is added (i.e. the cache space is actually needed).
+
+ Returns true if any were removed.
+*/
+bool QPMCache::flushDetachedPixmaps(bool nt)
+{
+ int mc = maxCost();
+ setMaxCost(nt ? totalCost() * 3 / 4 : totalCost() -1);
+ setMaxCost(mc);
+ ps = totalCost();
+
+ bool any = false;
+ QHash<QString, QPixmapCache::Key>::iterator it = cacheKeys.begin();
+ while (it != cacheKeys.end()) {
+ if (!contains(it.value())) {
+ releaseKey(it.value());
+ it = cacheKeys.erase(it);
+ any = true;
+ } else {
+ ++it;
+ }
+ }
+
+ return any;
+}
+
+void QPMCache::timerEvent(QTimerEvent *)
+{
+ bool nt = totalCost() == ps;
+ if (!flushDetachedPixmaps(nt)) {
+ killTimer(theid);
+ theid = 0;
+ } else if (nt != t) {
+ killTimer(theid);
+ theid = startTimer(nt ? soon_time : flush_time);
+ t = nt;
+ }
+}
+
+
+QPixmap *QPMCache::object(const QString &key) const
+{
+ QPixmapCache::Key cacheKey = cacheKeys.value(key);
+ if (!cacheKey.d || !cacheKey.d->isValid) {
+ const_cast<QPMCache *>(this)->cacheKeys.remove(key);
+ return 0;
+ }
+ QPixmap *ptr = QCache<QPixmapCache::Key, QPixmapCacheEntry>::object(cacheKey);
+ //We didn't find the pixmap in the cache, the key is not valid anymore
+ if (!ptr) {
+ const_cast<QPMCache *>(this)->cacheKeys.remove(key);
+ }
+ return ptr;
+}
+
+QPixmap *QPMCache::object(const QPixmapCache::Key &key) const
+{
+ Q_ASSERT(key.d->isValid);
+ QPixmap *ptr = QCache<QPixmapCache::Key, QPixmapCacheEntry>::object(key);
+ //We didn't find the pixmap in the cache, the key is not valid anymore
+ if (!ptr)
+ const_cast<QPMCache *>(this)->releaseKey(key);
+ return ptr;
+}
+
+bool QPMCache::insert(const QString& key, const QPixmap &pixmap, int cost)
+{
+ QPixmapCache::Key cacheKey;
+ QPixmapCache::Key oldCacheKey = cacheKeys.value(key);
+ //If for the same key we add already a pixmap we should delete it
+ if (oldCacheKey.d) {
+ QCache<QPixmapCache::Key, QPixmapCacheEntry>::remove(oldCacheKey);
+ cacheKeys.remove(key);
+ }
+
+ //we create a new key the old one has been removed
+ cacheKey = createKey();
+
+ bool success = QCache<QPixmapCache::Key, QPixmapCacheEntry>::insert(cacheKey, new QPixmapCacheEntry(cacheKey, pixmap), cost);
+ if (success) {
+ cacheKeys.insert(key, cacheKey);
+ if (!theid) {
+ theid = startTimer(flush_time);
+ t = false;
+ }
+ } else {
+ //Insertion failed we released the new allocated key
+ releaseKey(cacheKey);
+ }
+ return success;
+}
+
+QPixmapCache::Key QPMCache::insert(const QPixmap &pixmap, int cost)
+{
+ QPixmapCache::Key cacheKey = createKey();
+ bool success = QCache<QPixmapCache::Key, QPixmapCacheEntry>::insert(cacheKey, new QPixmapCacheEntry(cacheKey, pixmap), cost);
+ if (success) {
+ if (!theid) {
+ theid = startTimer(flush_time);
+ t = false;
+ }
+ } else {
+ //Insertion failed we released the key and return an invalid one
+ releaseKey(cacheKey);
+ }
+ return cacheKey;
+}
+
+bool QPMCache::replace(const QPixmapCache::Key &key, const QPixmap &pixmap, int cost)
+{
+ Q_ASSERT(key.d->isValid);
+ //If for the same key we had already an entry so we should delete the pixmap and use the new one
+ QCache<QPixmapCache::Key, QPixmapCacheEntry>::remove(key);
+
+ QPixmapCache::Key cacheKey = createKey();
+
+ bool success = QCache<QPixmapCache::Key, QPixmapCacheEntry>::insert(cacheKey, new QPixmapCacheEntry(cacheKey, pixmap), cost);
+ if (success) {
+ if(!theid) {
+ theid = startTimer(flush_time);
+ t = false;
+ }
+ const_cast<QPixmapCache::Key&>(key) = cacheKey;
+ } else {
+ //Insertion failed we released the key
+ releaseKey(cacheKey);
+ }
+ return success;
+}
+
+bool QPMCache::remove(const QString &key)
+{
+ QPixmapCache::Key cacheKey = cacheKeys.value(key);
+ //The key was not in the cache
+ if (!cacheKey.d)
+ return false;
+ cacheKeys.remove(key);
+ return QCache<QPixmapCache::Key, QPixmapCacheEntry>::remove(cacheKey);
+}
+
+bool QPMCache::remove(const QPixmapCache::Key &key)
+{
+ return QCache<QPixmapCache::Key, QPixmapCacheEntry>::remove(key);
+}
+
+void QPMCache::resizeKeyArray(int size)
+{
+ if (size <= keyArraySize || size == 0)
+ return;
+ keyArray = q_check_ptr(reinterpret_cast<int *>(realloc(keyArray,
+ size * sizeof(int))));
+ for (int i = keyArraySize; i != size; ++i)
+ keyArray[i] = i + 1;
+ keyArraySize = size;
+}
+
+QPixmapCache::Key QPMCache::createKey()
+{
+ if (freeKey == keyArraySize)
+ resizeKeyArray(keyArraySize ? keyArraySize << 1 : 2);
+ int id = freeKey;
+ freeKey = keyArray[id];
+ QPixmapCache::Key key;
+ QPixmapCache::KeyData *d = QPMCache::getKeyData(&key);
+ d->key = ++id;
+ return key;
+}
+
+void QPMCache::releaseKey(const QPixmapCache::Key &key)
+{
+ if (key.d->key > keyArraySize || key.d->key <= 0)
+ return;
+ key.d->key--;
+ keyArray[key.d->key] = freeKey;
+ freeKey = key.d->key;
+ key.d->isValid = false;
+ key.d->key = 0;
+}
+
+void QPMCache::clear()
+{
+ free(keyArray);
+ keyArray = 0;
+ freeKey = 0;
+ keyArraySize = 0;
+ //Mark all keys as invalid
+ QList<QPixmapCache::Key> keys = QCache<QPixmapCache::Key, QPixmapCacheEntry>::keys();
+ for (int i = 0; i < keys.size(); ++i)
+ keys.at(i).d->isValid = false;
+ QCache<QPixmapCache::Key, QPixmapCacheEntry>::clear();
+}
+
+QPixmapCache::KeyData* QPMCache::getKeyData(QPixmapCache::Key *key)
+{
+ if (!key->d)
+ key->d = new QPixmapCache::KeyData;
+ return key->d;
+}
+
+QList< QPair<QString,QPixmap> > QPMCache::allPixmaps() const
+{
+ QList< QPair<QString,QPixmap> > r;
+ QHash<QString, QPixmapCache::Key>::const_iterator it = cacheKeys.begin();
+ while (it != cacheKeys.end()) {
+ QPixmap *ptr = QCache<QPixmapCache::Key, QPixmapCacheEntry>::object(it.value());
+ if (ptr)
+ r.append(QPair<QString,QPixmap>(it.key(),*ptr));
+ ++it;
+ }
+ return r;
+}
+
+
+Q_GLOBAL_STATIC(QPMCache, pm_cache)
+
+int Q_AUTOTEST_EXPORT q_QPixmapCache_keyHashSize()
+{
+ return pm_cache()->size();
+}
+
+QPixmapCacheEntry::~QPixmapCacheEntry()
+{
+ pm_cache()->releaseKey(key);
+}
+
+/*!
+ \obsolete
+ \overload
+
+ Returns the pixmap associated with the \a key in the cache, or
+ null if there is no such pixmap.
+
+ \warning If valid, you should copy the pixmap immediately (this is
+ fast). Subsequent insertions into the cache could cause the
+ pointer to become invalid. For this reason, we recommend you use
+ bool find(const QString&, QPixmap*) instead.
+
+ Example:
+ \snippet doc/src/snippets/code/src_gui_image_qpixmapcache.cpp 0
+*/
+
+QPixmap *QPixmapCache::find(const QString &key)
+{
+ return pm_cache()->object(key);
+}
+
+
+/*!
+ \obsolete
+
+ Use bool find(const QString&, QPixmap*) instead.
+*/
+
+bool QPixmapCache::find(const QString &key, QPixmap& pixmap)
+{
+ return find(key, &pixmap);
+}
+
+/*!
+ Looks for a cached pixmap associated with the given \a key in the cache.
+ If the pixmap is found, the function sets \a pixmap to that pixmap and
+ returns true; otherwise it leaves \a pixmap alone and returns false.
+
+ \since 4.6
+
+ Example:
+ \snippet doc/src/snippets/code/src_gui_image_qpixmapcache.cpp 1
+*/
+
+bool QPixmapCache::find(const QString &key, QPixmap* pixmap)
+{
+ QPixmap *ptr = pm_cache()->object(key);
+ if (ptr && pixmap)
+ *pixmap = *ptr;
+ return ptr != 0;
+}
+
+/*!
+ Looks for a cached pixmap associated with the given \a key in the cache.
+ If the pixmap is found, the function sets \a pixmap to that pixmap and
+ returns true; otherwise it leaves \a pixmap alone and returns false. If
+ the pixmap is not found, it means that the \a key is no longer valid,
+ so it will be released for the next insertion.
+
+ \since 4.6
+*/
+bool QPixmapCache::find(const Key &key, QPixmap* pixmap)
+{
+ //The key is not valid anymore, a flush happened before probably
+ if (!key.d || !key.d->isValid)
+ return false;
+ QPixmap *ptr = pm_cache()->object(key);
+ if (ptr && pixmap)
+ *pixmap = *ptr;
+ return ptr != 0;
+}
+
+/*!
+ Inserts a copy of the pixmap \a pixmap associated with the \a key into
+ the cache.
+
+ All pixmaps inserted by the Qt library have a key starting with
+ "$qt", so your own pixmap keys should never begin "$qt".
+
+ When a pixmap is inserted and the cache is about to exceed its
+ limit, it removes pixmaps until there is enough room for the
+ pixmap to be inserted.
+
+ The oldest pixmaps (least recently accessed in the cache) are
+ deleted when more space is needed.
+
+ The function returns true if the object was inserted into the
+ cache; otherwise it returns false.
+
+ \sa setCacheLimit()
+*/
+
+bool QPixmapCache::insert(const QString &key, const QPixmap &pixmap)
+{
+ return pm_cache()->insert(key, pixmap, pixmap.width() * pixmap.height() * pixmap.depth() / 8);
+}
+
+/*!
+ Inserts a copy of the given \a pixmap into the cache and returns a key
+ that can be used to retrieve it.
+
+ When a pixmap is inserted and the cache is about to exceed its
+ limit, it removes pixmaps until there is enough room for the
+ pixmap to be inserted.
+
+ The oldest pixmaps (least recently accessed in the cache) are
+ deleted when more space is needed.
+
+ \sa setCacheLimit(), replace()
+
+ \since 4.6
+*/
+QPixmapCache::Key QPixmapCache::insert(const QPixmap &pixmap)
+{
+ return pm_cache()->insert(pixmap, pixmap.width() * pixmap.height() * pixmap.depth() / 8);
+}
+
+/*!
+ Replaces the pixmap associated with the given \a key with the \a pixmap
+ specified. Returns true if the \a pixmap has been correctly inserted into
+ the cache; otherwise returns false.
+
+ \sa setCacheLimit(), insert()
+
+ \since 4.6
+*/
+bool QPixmapCache::replace(const Key &key, const QPixmap &pixmap)
+{
+ //The key is not valid anymore, a flush happened before probably
+ if (!key.d || !key.d->isValid)
+ return false;
+ return pm_cache()->replace(key, pixmap, pixmap.width() * pixmap.height() * pixmap.depth() / 8);
+}
+
+/*!
+ Returns the cache limit (in kilobytes).
+
+ The default cache limit is 2048 KB on embedded platforms, 10240 KB on
+ desktop platforms.
+
+ \sa setCacheLimit()
+*/
+
+int QPixmapCache::cacheLimit()
+{
+ return cache_limit;
+}
+
+/*!
+ Sets the cache limit to \a n kilobytes.
+
+ The default setting is 2048 KB on embedded platforms, 10240 KB on
+ desktop platforms.
+
+ \sa cacheLimit()
+*/
+
+void QPixmapCache::setCacheLimit(int n)
+{
+ cache_limit = n;
+ pm_cache()->setMaxCost(1024 * cache_limit);
+}
+
+/*!
+ Removes the pixmap associated with \a key from the cache.
+*/
+void QPixmapCache::remove(const QString &key)
+{
+ pm_cache()->remove(key);
+}
+
+/*!
+ Removes the pixmap associated with \a key from the cache and releases
+ the key for a future insertion.
+
+ \since 4.6
+*/
+void QPixmapCache::remove(const Key &key)
+{
+ //The key is not valid anymore, a flush happened before probably
+ if (!key.d || !key.d->isValid)
+ return;
+ pm_cache()->remove(key);
+}
+
+/*!
+ Removes all pixmaps from the cache.
+*/
+
+void QPixmapCache::clear()
+{
+ QT_TRY {
+ pm_cache()->clear();
+ } QT_CATCH(const std::bad_alloc &) {
+ // if we ran out of memory during pm_cache(), it's no leak,
+ // so just ignore it.
+ }
+}
+
+void QPixmapCache::flushDetachedPixmaps()
+{
+ pm_cache()->flushDetachedPixmaps(true);
+}
+
+int QPixmapCache::totalUsed()
+{
+ return (pm_cache()->totalCost()+1023) / 1024;
+}
+
+QList< QPair<QString,QPixmap> > QPixmapCache::allPixmaps()
+{
+ return pm_cache()->allPixmaps();
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/image/qpixmapcache.h b/src/gui/image/qpixmapcache.h
new file mode 100644
index 0000000000..77081572b1
--- /dev/null
+++ b/src/gui/image/qpixmapcache.h
@@ -0,0 +1,102 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPIXMAPCACHE_H
+#define QPIXMAPCACHE_H
+
+#include <QtGui/qpixmap.h>
+
+#ifdef Q_TEST_QPIXMAPCACHE
+#include <QtCore/qpair.h>
+#endif
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class Q_GUI_EXPORT QPixmapCache
+{
+public:
+ class KeyData;
+ class Q_GUI_EXPORT Key
+ {
+ public:
+ Key();
+ Key(const Key &other);
+ ~Key();
+ bool operator ==(const Key &key) const;
+ inline bool operator !=(const Key &key) const
+ { return !operator==(key); }
+ Key &operator =(const Key &other);
+
+ private:
+ KeyData *d;
+ friend class QPMCache;
+ friend class QPixmapCache;
+ };
+
+ static int cacheLimit();
+ static void setCacheLimit(int);
+ static QPixmap *find(const QString &key);
+ static bool find(const QString &key, QPixmap &pixmap);
+ static bool find(const QString &key, QPixmap *pixmap);
+ static bool find(const Key &key, QPixmap *pixmap);
+ static bool insert(const QString &key, const QPixmap &pixmap);
+ static Key insert(const QPixmap &pixmap);
+ static bool replace(const Key &key, const QPixmap &pixmap);
+ static void remove(const QString &key);
+ static void remove(const Key &key);
+ static void clear();
+
+#ifdef Q_TEST_QPIXMAPCACHE
+ static void flushDetachedPixmaps();
+ static int totalUsed();
+ static QList< QPair<QString,QPixmap> > allPixmaps();
+#endif
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QPIXMAPCACHE_H
diff --git a/src/gui/image/qpixmapcache_p.h b/src/gui/image/qpixmapcache_p.h
new file mode 100644
index 0000000000..b2ca00163c
--- /dev/null
+++ b/src/gui/image/qpixmapcache_p.h
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPIXMAPCACHE_P_H
+#define QPIXMAPCACHE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. This header
+// file may change from version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qpixmapcache.h"
+#include "qpaintengine.h"
+#include <private/qimage_p.h>
+#include <private/qpixmap_raster_p.h>
+#include "qcache.h"
+
+QT_BEGIN_NAMESPACE
+
+uint qHash(const QPixmapCache::Key &k);
+
+class QPixmapCache::KeyData
+{
+public:
+ KeyData() : isValid(true), key(0), ref(1) {}
+ KeyData(const KeyData &other)
+ : isValid(other.isValid), key(other.key), ref(1) {}
+ ~KeyData() {}
+
+ bool isValid;
+ int key;
+ int ref;
+};
+
+// XXX: hw: is this a general concept we need to abstract?
+class QPixmapCacheEntry : public QPixmap
+{
+public:
+ QPixmapCacheEntry(const QPixmapCache::Key &key, const QPixmap &pix) : QPixmap(pix), key(key)
+ {
+ QPixmapData *pd = pixmapData();
+ if (pd && pd->classId() == QPixmapData::RasterClass) {
+ QRasterPixmapData *d = static_cast<QRasterPixmapData*>(pd);
+ if (!d->image.isNull() && d->image.d->paintEngine
+ && !d->image.d->paintEngine->isActive())
+ {
+ delete d->image.d->paintEngine;
+ d->image.d->paintEngine = 0;
+ }
+ }
+ }
+ ~QPixmapCacheEntry();
+ QPixmapCache::Key key;
+};
+
+inline bool qIsDetached(QPixmapCacheEntry &t) { return t.isDetached(); }
+
+QT_END_NAMESPACE
+
+#endif // QPIXMAPCACHE_P_H
diff --git a/src/gui/image/qpixmapdata.cpp b/src/gui/image/qpixmapdata.cpp
new file mode 100644
index 0000000000..8222d714dc
--- /dev/null
+++ b/src/gui/image/qpixmapdata.cpp
@@ -0,0 +1,290 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpixmapdata_p.h"
+#include <QtCore/qbuffer.h>
+#include <QtGui/qbitmap.h>
+#include <QtGui/qimagereader.h>
+#include <private/qgraphicssystem_p.h>
+#include <private/qapplication_p.h>
+#include <private/qimagepixmapcleanuphooks_p.h>
+
+QT_BEGIN_NAMESPACE
+
+const uchar qt_pixmap_bit_mask[] = { 0x01, 0x02, 0x04, 0x08,
+ 0x10, 0x20, 0x40, 0x80 };
+
+QPixmapData *QPixmapData::create(int w, int h, PixelType type)
+{
+ QPixmapData *data;
+ QGraphicsSystem* gs = QApplicationPrivate::graphicsSystem();
+ if (gs)
+ data = gs->createPixmapData(static_cast<QPixmapData::PixelType>(type));
+ else
+ data = QGraphicsSystem::createDefaultPixmapData(static_cast<QPixmapData::PixelType>(type));
+ data->resize(w, h);
+ return data;
+}
+
+
+QPixmapData::QPixmapData(PixelType pixelType, int objectId)
+ : w(0),
+ h(0),
+ d(0),
+ is_null(true),
+ ref(0),
+ detach_no(0),
+ type(pixelType),
+ id(objectId),
+ ser_no(0),
+ is_cached(false)
+{
+}
+
+QPixmapData::~QPixmapData()
+{
+ // Sometimes the pixmap cleanup hooks will be called from derrived classes, which will
+ // then set is_cached to false. For example, on X11 QtOpenGL needs to delete the GLXPixmap
+ // or EGL Pixmap Surface for a given pixmap _before_ the native X11 pixmap is deleted,
+ // otherwise some drivers will leak the GL surface. In this case, QX11PixmapData will
+ // call the cleanup hooks itself before deleting the native pixmap and set is_cached to
+ // false.
+ if (is_cached) {
+ QImagePixmapCleanupHooks::executePixmapDataDestructionHooks(this);
+ is_cached = false;
+ }
+}
+
+QPixmapData *QPixmapData::createCompatiblePixmapData() const
+{
+ QPixmapData *d;
+ QGraphicsSystem *gs = QApplicationPrivate::graphicsSystem();
+ if (gs)
+ d = gs->createPixmapData(pixelType());
+ else
+ d = QGraphicsSystem::createDefaultPixmapData(pixelType());
+ return d;
+}
+
+static QImage makeBitmapCompliantIfNeeded(QPixmapData *d, const QImage &image, Qt::ImageConversionFlags flags)
+{
+ if (d->pixelType() == QPixmapData::BitmapType) {
+ QImage img = image.convertToFormat(QImage::Format_MonoLSB, flags);
+
+ // make sure image.color(0) == Qt::color0 (white)
+ // and image.color(1) == Qt::color1 (black)
+ const QRgb c0 = QColor(Qt::black).rgb();
+ const QRgb c1 = QColor(Qt::white).rgb();
+ if (img.color(0) == c0 && img.color(1) == c1) {
+ img.invertPixels();
+ img.setColor(0, c1);
+ img.setColor(1, c0);
+ }
+ return img;
+ }
+
+ return image;
+}
+
+void QPixmapData::fromImageReader(QImageReader *imageReader,
+ Qt::ImageConversionFlags flags)
+{
+ const QImage image = imageReader->read();
+ fromImage(image, flags);
+}
+
+bool QPixmapData::fromFile(const QString &fileName, const char *format,
+ Qt::ImageConversionFlags flags)
+{
+ QImage image = QImageReader(fileName, format).read();
+ if (image.isNull())
+ return false;
+ fromImage(makeBitmapCompliantIfNeeded(this, image, flags), flags);
+ return !isNull();
+}
+
+bool QPixmapData::fromData(const uchar *buf, uint len, const char *format, Qt::ImageConversionFlags flags)
+{
+ QByteArray a = QByteArray::fromRawData(reinterpret_cast<const char *>(buf), len);
+ QBuffer b(&a);
+ b.open(QIODevice::ReadOnly);
+ QImage image = QImageReader(&b, format).read();
+ fromImage(makeBitmapCompliantIfNeeded(this, image, flags), flags);
+ return !isNull();
+}
+
+void QPixmapData::copy(const QPixmapData *data, const QRect &rect)
+{
+ fromImage(data->toImage(rect), Qt::NoOpaqueDetection);
+}
+
+bool QPixmapData::scroll(int dx, int dy, const QRect &rect)
+{
+ Q_UNUSED(dx);
+ Q_UNUSED(dy);
+ Q_UNUSED(rect);
+ return false;
+}
+
+void QPixmapData::setMask(const QBitmap &mask)
+{
+ if (mask.size().isEmpty()) {
+ if (depth() != 1)
+ fromImage(toImage().convertToFormat(QImage::Format_RGB32),
+ Qt::AutoColor);
+ } else {
+ QImage image = toImage();
+ const int w = image.width();
+ const int h = image.height();
+
+ switch (image.depth()) {
+ case 1: {
+ const QImage imageMask = mask.toImage().convertToFormat(image.format());
+ for (int y = 0; y < h; ++y) {
+ const uchar *mscan = imageMask.scanLine(y);
+ uchar *tscan = image.scanLine(y);
+ int bytesPerLine = image.bytesPerLine();
+ for (int i = 0; i < bytesPerLine; ++i)
+ tscan[i] &= mscan[i];
+ }
+ break;
+ }
+ default: {
+ const QImage imageMask = mask.toImage().convertToFormat(QImage::Format_MonoLSB);
+ image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
+ for (int y = 0; y < h; ++y) {
+ const uchar *mscan = imageMask.scanLine(y);
+ QRgb *tscan = (QRgb *)image.scanLine(y);
+ for (int x = 0; x < w; ++x) {
+ if (!(mscan[x>>3] & qt_pixmap_bit_mask[x&7]))
+ tscan[x] = 0;
+ }
+ }
+ break;
+ }
+ }
+ fromImage(image, Qt::AutoColor);
+ }
+}
+
+QBitmap QPixmapData::mask() const
+{
+ if (!hasAlphaChannel())
+ return QBitmap();
+
+ const QImage img = toImage();
+ const QImage image = (img.depth() < 32 ? img.convertToFormat(QImage::Format_ARGB32_Premultiplied) : img);
+ const int w = image.width();
+ const int h = image.height();
+
+ QImage mask(w, h, QImage::Format_MonoLSB);
+ if (mask.isNull()) // allocation failed
+ return QBitmap();
+
+ mask.setColorCount(2);
+ mask.setColor(0, QColor(Qt::color0).rgba());
+ mask.setColor(1, QColor(Qt::color1).rgba());
+
+ const int bpl = mask.bytesPerLine();
+
+ for (int y = 0; y < h; ++y) {
+ const QRgb *src = reinterpret_cast<const QRgb*>(image.scanLine(y));
+ uchar *dest = mask.scanLine(y);
+ memset(dest, 0, bpl);
+ for (int x = 0; x < w; ++x) {
+ if (qAlpha(*src) > 0)
+ dest[x >> 3] |= qt_pixmap_bit_mask[x & 7];
+ ++src;
+ }
+ }
+
+ return QBitmap::fromImage(mask);
+}
+
+QPixmap QPixmapData::transformed(const QTransform &matrix,
+ Qt::TransformationMode mode) const
+{
+ return QPixmap::fromImage(toImage().transformed(matrix, mode));
+}
+
+void QPixmapData::setAlphaChannel(const QPixmap &alphaChannel)
+{
+ QImage image = toImage();
+ image.setAlphaChannel(alphaChannel.toImage());
+ fromImage(image, Qt::AutoColor);
+}
+
+QPixmap QPixmapData::alphaChannel() const
+{
+ return QPixmap::fromImage(toImage().alphaChannel());
+}
+
+void QPixmapData::setSerialNumber(int serNo)
+{
+ ser_no = serNo;
+}
+
+QImage QPixmapData::toImage(const QRect &rect) const
+{
+ if (rect.contains(QRect(0, 0, w, h)))
+ return toImage();
+ else
+ return toImage().copy(rect);
+}
+
+QImage* QPixmapData::buffer()
+{
+ return 0;
+}
+
+#if defined(Q_OS_SYMBIAN)
+void* QPixmapData::toNativeType(NativeType /* type */)
+{
+ return 0;
+}
+
+void QPixmapData::fromNativeType(void* /* pixmap */, NativeType /* typre */)
+{
+ return;
+}
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/gui/image/qpixmapdata_p.h b/src/gui/image/qpixmapdata_p.h
new file mode 100644
index 0000000000..6bbce09b6c
--- /dev/null
+++ b/src/gui/image/qpixmapdata_p.h
@@ -0,0 +1,180 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPIXMAPDATA_P_H
+#define QPIXMAPDATA_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/qpixmap.h>
+#include <QtCore/qatomic.h>
+
+QT_BEGIN_NAMESPACE
+
+class QImageReader;
+
+class Q_GUI_EXPORT QPixmapData
+{
+public:
+ enum PixelType {
+ // WARNING: Do not change the first two
+ // Must match QPixmap::Type
+ PixmapType, BitmapType
+ };
+#if defined(Q_OS_SYMBIAN)
+ enum NativeType {
+ FbsBitmap,
+ SgImage,
+ VolatileImage,
+ NativeImageHandleProvider
+ };
+#endif
+ enum ClassId { RasterClass, X11Class, MacClass, DirectFBClass,
+ OpenGLClass, OpenVGClass, RuntimeClass, BlitterClass,
+ CustomClass = 1024 };
+
+ QPixmapData(PixelType pixelType, int classId);
+ virtual ~QPixmapData();
+
+ virtual QPixmapData *createCompatiblePixmapData() const;
+
+ virtual void resize(int width, int height) = 0;
+ virtual void fromImage(const QImage &image,
+ Qt::ImageConversionFlags flags) = 0;
+ virtual void fromImageReader(QImageReader *imageReader,
+ Qt::ImageConversionFlags flags);
+
+ virtual bool fromFile(const QString &filename, const char *format,
+ Qt::ImageConversionFlags flags);
+ virtual bool fromData(const uchar *buffer, uint len, const char *format,
+ Qt::ImageConversionFlags flags);
+
+ virtual void copy(const QPixmapData *data, const QRect &rect);
+ virtual bool scroll(int dx, int dy, const QRect &rect);
+
+ virtual int metric(QPaintDevice::PaintDeviceMetric metric) const = 0;
+ virtual void fill(const QColor &color) = 0;
+ virtual QBitmap mask() const;
+ virtual void setMask(const QBitmap &mask);
+ virtual bool hasAlphaChannel() const = 0;
+ virtual QPixmap transformed(const QTransform &matrix,
+ Qt::TransformationMode mode) const;
+ virtual void setAlphaChannel(const QPixmap &alphaChannel);
+ virtual QPixmap alphaChannel() const;
+ virtual QImage toImage() const = 0;
+ virtual QImage toImage(const QRect &rect) const;
+ virtual QPaintEngine* paintEngine() const = 0;
+
+ inline int serialNumber() const { return ser_no; }
+
+ inline PixelType pixelType() const { return type; }
+ inline ClassId classId() const { return static_cast<ClassId>(id); }
+
+ virtual QImage* buffer();
+
+ inline int width() const { return w; }
+ inline int height() const { return h; }
+ QT_DEPRECATED inline int numColors() const { return metric(QPaintDevice::PdmNumColors); }
+ inline int colorCount() const { return metric(QPaintDevice::PdmNumColors); }
+ inline int depth() const { return d; }
+ inline bool isNull() const { return is_null; }
+ inline qint64 cacheKey() const {
+ int classKey = id;
+ if (classKey >= 1024)
+ classKey = -(classKey >> 10);
+ return ((((qint64) classKey) << 56)
+ | (((qint64) ser_no) << 32)
+ | ((qint64) detach_no));
+ }
+
+#if defined(Q_OS_SYMBIAN)
+ virtual void* toNativeType(NativeType type);
+ virtual void fromNativeType(void* pixmap, NativeType type);
+#endif
+
+ static QPixmapData *create(int w, int h, PixelType type);
+
+ virtual QPixmapData *runtimeData() const { return 0; }
+
+protected:
+
+ void setSerialNumber(int serNo);
+ int w;
+ int h;
+ int d;
+ bool is_null;
+
+private:
+ friend class QPixmap;
+ friend class QX11PixmapData;
+ friend class QS60PixmapData;
+ friend class QImagePixmapCleanupHooks; // Needs to set is_cached
+ friend class QGLTextureCache; //Needs to check the reference count
+ friend class QExplicitlySharedDataPointer<QPixmapData>;
+
+ QAtomicInt ref;
+ int detach_no;
+
+ PixelType type;
+ int id;
+ int ser_no;
+ uint is_cached;
+};
+
+# define QT_XFORM_TYPE_MSBFIRST 0
+# define QT_XFORM_TYPE_LSBFIRST 1
+# if defined(Q_WS_WIN)
+# define QT_XFORM_TYPE_WINDOWSPIXMAP 2
+# endif
+extern bool qt_xForm_helper(const QTransform&, int, int, int, uchar*, int, int, int, const uchar*, int, int, int);
+
+QT_END_NAMESPACE
+
+#endif // QPIXMAPDATA_P_H
diff --git a/src/gui/image/qpixmapdatafactory.cpp b/src/gui/image/qpixmapdatafactory.cpp
new file mode 100644
index 0000000000..ec53dcb9c1
--- /dev/null
+++ b/src/gui/image/qpixmapdatafactory.cpp
@@ -0,0 +1,115 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpixmapdatafactory_p.h"
+
+#ifdef Q_WS_QWS
+# include <QtGui/qscreen_qws.h>
+#endif
+#ifdef Q_WS_X11
+# include <private/qpixmap_x11_p.h>
+#endif
+#if defined(Q_WS_WIN)
+# include <private/qpixmap_raster_p.h>
+#endif
+#ifdef Q_WS_MAC
+# include <private/qpixmap_mac_p.h>
+#endif
+#ifdef Q_WS_QPA
+# include <private/qpixmap_raster_p.h>
+#endif
+#ifdef Q_OS_SYMBIAN
+# include <private/qpixmap_s60_p.h>
+#endif
+
+#include "private/qapplication_p.h"
+#include "private/qgraphicssystem_p.h"
+
+QT_BEGIN_NAMESPACE
+
+#if !defined(Q_WS_QWS)
+
+class QSimplePixmapDataFactory : public QPixmapDataFactory
+{
+public:
+ ~QSimplePixmapDataFactory() {}
+ QPixmapData* create(QPixmapData::PixelType type);
+};
+
+QPixmapData* QSimplePixmapDataFactory::create(QPixmapData::PixelType type)
+{
+ if (QApplicationPrivate::graphicsSystem())
+ return QApplicationPrivate::graphicsSystem()->createPixmapData(type);
+
+#if defined(Q_WS_X11)
+ return new QX11PixmapData(type);
+#elif defined(Q_WS_WIN)
+ return new QRasterPixmapData(type);
+#elif defined(Q_WS_MAC)
+ return new QMacPixmapData(type);
+#elif defined(Q_WS_QPA)
+ return new QRasterPixmapData(type);
+#elif defined(Q_OS_SYMBIAN)
+ return new QS60PixmapData(type);
+#else
+#error QSimplePixmapDataFactory::create() not implemented
+#endif
+}
+
+Q_GLOBAL_STATIC(QSimplePixmapDataFactory, factory)
+
+#endif // !defined(Q_WS_QWS)
+
+QPixmapDataFactory::~QPixmapDataFactory()
+{
+}
+
+QPixmapDataFactory* QPixmapDataFactory::instance(int screen)
+{
+ Q_UNUSED(screen);
+#ifdef Q_WS_QWS
+ return QScreen::instance()->pixmapDataFactory();
+#else
+ return factory();
+#endif
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/image/qpixmapdatafactory_p.h b/src/gui/image/qpixmapdatafactory_p.h
new file mode 100644
index 0000000000..4f2e5c4527
--- /dev/null
+++ b/src/gui/image/qpixmapdatafactory_p.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPIXMAPDATAFACTORY_P_H
+#define QPIXMAPDATAFACTORY_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qstring.h>
+#include <QtGui/qimage.h>
+#include <QtGui/private/qpixmapdata_p.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QPixmapData;
+
+class QPixmapDataFactory
+{
+public:
+ static QPixmapDataFactory* instance(int screen = 0);
+ virtual ~QPixmapDataFactory();
+
+ virtual QPixmapData* create(QPixmapData::PixelType type) = 0;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QPIXMAPDATAFACTORY_P_H
diff --git a/src/gui/image/qpixmapfilter.cpp b/src/gui/image/qpixmapfilter.cpp
new file mode 100644
index 0000000000..c05afb052e
--- /dev/null
+++ b/src/gui/image/qpixmapfilter.cpp
@@ -0,0 +1,1382 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qglobal.h>
+
+#include <QDebug>
+
+#include "qpainter.h"
+#include "qpixmap.h"
+#include "qpixmapfilter_p.h"
+#include "qvarlengtharray.h"
+
+#include "private/qapplication_p.h"
+#include "private/qgraphicssystem_p.h"
+#include "private/qpaintengineex_p.h"
+#include "private/qpaintengine_raster_p.h"
+#include "qmath.h"
+#include "private/qmath_p.h"
+#include "private/qmemrotate_p.h"
+#include "private/qdrawhelper_p.h"
+
+#ifndef QT_NO_GRAPHICSEFFECT
+QT_BEGIN_NAMESPACE
+
+class QPixmapFilterPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QPixmapFilter)
+public:
+ QPixmapFilter::FilterType type;
+};
+
+/*!
+ \class QPixmapFilter
+ \since 4.5
+ \ingroup painting
+
+ \brief The QPixmapFilter class provides the basic functionality for
+ pixmap filter classes. Pixmap filter can be for example colorize or blur.
+
+ QPixmapFilter is the base class for every pixmap filter. QPixmapFilter is
+ an abstract class and cannot itself be instantiated. It provides a standard
+ interface for filter processing.
+
+ \internal
+*/
+
+/*!
+ \enum QPixmapFilter::FilterType
+
+ \internal
+
+ This enum describes the types of filter that can be applied to pixmaps.
+
+ \value ConvolutionFilter A filter that is used to calculate the convolution
+ of the image with a kernel. See
+ QPixmapConvolutionFilter for more information.
+ \value ColorizeFilter A filter that is used to change the overall color
+ of an image. See QPixmapColorizeFilter for more
+ information.
+ \value DropShadowFilter A filter that is used to add a drop shadow to an
+ image. See QPixmapDropShadowFilter for more
+ information.
+ \value BlurFilter A filter that is used to blur an image using
+ a simple blur radius. See QPixmapBlurFilter
+ for more information.
+
+ \value UserFilter The first filter type that can be used for
+ application-specific purposes.
+*/
+
+
+/*!
+ Constructs a default QPixmapFilter with the given \a type.
+
+ This constructor should be used when subclassing QPixmapFilter to
+ create custom user filters.
+
+ \internal
+*/
+QPixmapFilter::QPixmapFilter(FilterType type, QObject *parent)
+ : QObject(*new QPixmapFilterPrivate, parent)
+{
+ d_func()->type = type;
+}
+
+
+
+/*!
+ \internal
+*/
+QPixmapFilter::QPixmapFilter(QPixmapFilterPrivate&d, QPixmapFilter::FilterType type, QObject *parent)
+ : QObject(d, parent)
+{
+ d_func()->type = type;
+}
+
+
+/*!
+ Destroys the pixmap filter.
+
+ \internal
+*/
+QPixmapFilter::~QPixmapFilter()
+{
+}
+
+/*!
+ Returns the type of the filter. All standard pixmap filter classes
+ are associated with a unique value.
+
+ \internal
+*/
+QPixmapFilter::FilterType QPixmapFilter::type() const
+{
+ Q_D(const QPixmapFilter);
+ return d->type;
+}
+
+/*!
+ Returns the bounding rectangle that is affected by the pixmap
+ filter if the filter is applied to the specified \a rect.
+
+ \internal
+*/
+QRectF QPixmapFilter::boundingRectFor(const QRectF &rect) const
+{
+ return rect;
+}
+
+/*!
+ \fn void QPixmapFilter::draw(QPainter *painter, const QPointF &p, const QPixmap &src, const QRectF& srcRect) const
+
+ Uses \a painter to draw filtered result of \a src at the point
+ specified by \a p. If \a srcRect is specified the it will
+ be used as a source rectangle to only draw a part of the source.
+
+ draw() will affect the area which boundingRectFor() returns.
+
+ \internal
+*/
+
+/*!
+ \class QPixmapConvolutionFilter
+ \since 4.5
+ \ingroup painting
+
+ \brief The QPixmapConvolutionFilter class provides convolution
+ filtering for pixmaps.
+
+ QPixmapConvolutionFilter implements a convolution pixmap filter,
+ which is applied when \l{QPixmapFilter::}{draw()} is called. A
+ convolution filter lets you distort an image by setting the values
+ of a matrix of qreal values called its
+ \l{setConvolutionKernel()}{kernel}. The matrix's values are
+ usually between -1.0 and 1.0.
+
+ \omit
+ In convolution filtering, the pixel value is calculated from the
+ neighboring pixels based on the weighting convolution kernel.
+ This needs explaining to be useful.
+ \endomit
+
+ Example:
+ \snippet doc/src/snippets/code/src_gui_image_qpixmapfilter.cpp 1
+
+ \sa {Pixmap Filters Example}, QPixmapColorizeFilter, QPixmapDropShadowFilter
+
+
+ \internal
+*/
+
+class QPixmapConvolutionFilterPrivate : public QPixmapFilterPrivate
+{
+public:
+ QPixmapConvolutionFilterPrivate(): convolutionKernel(0), kernelWidth(0), kernelHeight(0), convoluteAlpha(false) {}
+ ~QPixmapConvolutionFilterPrivate() {
+ delete[] convolutionKernel;
+ }
+
+ qreal *convolutionKernel;
+ int kernelWidth;
+ int kernelHeight;
+ bool convoluteAlpha;
+};
+
+
+/*!
+ Constructs a pixmap convolution filter.
+
+ By default there is no convolution kernel.
+
+ \internal
+*/
+QPixmapConvolutionFilter::QPixmapConvolutionFilter(QObject *parent)
+ : QPixmapFilter(*new QPixmapConvolutionFilterPrivate, ConvolutionFilter, parent)
+{
+ Q_D(QPixmapConvolutionFilter);
+ d->convoluteAlpha = true;
+}
+
+/*!
+ Destructor of pixmap convolution filter.
+
+ \internal
+*/
+QPixmapConvolutionFilter::~QPixmapConvolutionFilter()
+{
+}
+
+/*!
+ Sets convolution kernel with the given number of \a rows and \a columns.
+ Values from \a kernel are copied to internal data structure.
+
+ To preserve the intensity of the pixmap, the sum of all the
+ values in the convolution kernel should add up to 1.0. A sum
+ greater than 1.0 produces a lighter result and a sum less than 1.0
+ produces a darker and transparent result.
+
+ \internal
+*/
+void QPixmapConvolutionFilter::setConvolutionKernel(const qreal *kernel, int rows, int columns)
+{
+ Q_D(QPixmapConvolutionFilter);
+ delete [] d->convolutionKernel;
+ d->convolutionKernel = new qreal[rows * columns];
+ memcpy(d->convolutionKernel, kernel, sizeof(qreal) * rows * columns);
+ d->kernelWidth = columns;
+ d->kernelHeight = rows;
+}
+
+/*!
+ Gets the convolution kernel data.
+
+ \internal
+*/
+const qreal *QPixmapConvolutionFilter::convolutionKernel() const
+{
+ Q_D(const QPixmapConvolutionFilter);
+ return d->convolutionKernel;
+}
+
+/*!
+ Gets the number of rows in the convolution kernel.
+
+ \internal
+*/
+int QPixmapConvolutionFilter::rows() const
+{
+ Q_D(const QPixmapConvolutionFilter);
+ return d->kernelHeight;
+}
+
+/*!
+ Gets the number of columns in the convolution kernel.
+
+ \internal
+*/
+int QPixmapConvolutionFilter::columns() const
+{
+ Q_D(const QPixmapConvolutionFilter);
+ return d->kernelWidth;
+}
+
+
+/*!
+ \internal
+*/
+QRectF QPixmapConvolutionFilter::boundingRectFor(const QRectF &rect) const
+{
+ Q_D(const QPixmapConvolutionFilter);
+ return rect.adjusted(-d->kernelWidth / 2, -d->kernelHeight / 2, (d->kernelWidth - 1) / 2, (d->kernelHeight - 1) / 2);
+}
+
+// Convolutes the image
+static void convolute(
+ QImage *destImage,
+ const QPointF &pos,
+ const QImage &srcImage,
+ const QRectF &srcRect,
+ QPainter::CompositionMode mode,
+ qreal *kernel,
+ int kernelWidth,
+ int kernelHeight )
+{
+ const QImage processImage = (srcImage.format() != QImage::Format_ARGB32_Premultiplied ) ? srcImage.convertToFormat(QImage::Format_ARGB32_Premultiplied) : srcImage;
+ // TODO: support also other formats directly without copying
+
+ int *fixedKernel = new int[kernelWidth*kernelHeight];
+ for(int i = 0; i < kernelWidth*kernelHeight; i++)
+ {
+ fixedKernel[i] = (int)(65536 * kernel[i]);
+ }
+ QRectF trect = srcRect.isNull() ? processImage.rect() : srcRect;
+ trect.moveTo(pos);
+ QRectF bounded = trect.adjusted(-kernelWidth / 2, -kernelHeight / 2, (kernelWidth - 1) / 2, (kernelHeight - 1) / 2);
+ QRect rect = bounded.toAlignedRect();
+ QRect targetRect = rect.intersected(destImage->rect());
+
+ QRectF srect = srcRect.isNull() ? processImage.rect() : srcRect;
+ QRectF sbounded = srect.adjusted(-kernelWidth / 2, -kernelHeight / 2, (kernelWidth - 1) / 2, (kernelHeight - 1) / 2);
+ QPoint srcStartPoint = sbounded.toAlignedRect().topLeft()+(targetRect.topLeft()-rect.topLeft());
+
+ const uint *sourceStart = (uint*)processImage.scanLine(0);
+ uint *outputStart = (uint*)destImage->scanLine(0);
+
+ int yk = srcStartPoint.y();
+ for (int y = targetRect.top(); y <= targetRect.bottom(); y++) {
+ uint* output = outputStart + (destImage->bytesPerLine()/sizeof(uint))*y+targetRect.left();
+ int xk = srcStartPoint.x();
+ for(int x = targetRect.left(); x <= targetRect.right(); x++) {
+ int r = 0;
+ int g = 0;
+ int b = 0;
+ int a = 0;
+
+ // some out of bounds pre-checking to avoid inner-loop ifs
+ int kernely = -kernelHeight/2;
+ int starty = 0;
+ int endy = kernelHeight;
+ if(yk+kernely+endy >= srcImage.height())
+ endy = kernelHeight-((yk+kernely+endy)-srcImage.height())-1;
+ if(yk+kernely < 0)
+ starty = -(yk+kernely);
+
+ int kernelx = -kernelWidth/2;
+ int startx = 0;
+ int endx = kernelWidth;
+ if(xk+kernelx+endx >= srcImage.width())
+ endx = kernelWidth-((xk+kernelx+endx)-srcImage.width())-1;
+ if(xk+kernelx < 0)
+ startx = -(xk+kernelx);
+
+ for (int ys = starty; ys < endy; ys ++) {
+ const uint *pix = sourceStart + (processImage.bytesPerLine()/sizeof(uint))*(yk+kernely+ys) + ((xk+kernelx+startx));
+ const uint *endPix = pix+endx-startx;
+ int kernelPos = ys*kernelWidth+startx;
+ while (pix < endPix) {
+ int factor = fixedKernel[kernelPos++];
+ a += (((*pix) & 0xff000000)>>24) * factor;
+ r += (((*pix) & 0x00ff0000)>>16) * factor;
+ g += (((*pix) & 0x0000ff00)>>8 ) * factor;
+ b += (((*pix) & 0x000000ff) ) * factor;
+ pix++;
+ }
+ }
+
+ r = qBound((int)0, r >> 16, (int)255);
+ g = qBound((int)0, g >> 16, (int)255);
+ b = qBound((int)0, b >> 16, (int)255);
+ a = qBound((int)0, a >> 16, (int)255);
+ // composition mode checking could be moved outside of loop
+ if(mode == QPainter::CompositionMode_Source) {
+ uint color = (a<<24)+(r<<16)+(g<<8)+b;
+ *output++ = color;
+ } else {
+ uint current = *output;
+ uchar ca = (current&0xff000000)>>24;
+ uchar cr = (current&0x00ff0000)>>16;
+ uchar cg = (current&0x0000ff00)>>8;
+ uchar cb = (current&0x000000ff);
+ uint color =
+ (((ca*(255-a) >> 8)+a) << 24)+
+ (((cr*(255-a) >> 8)+r) << 16)+
+ (((cg*(255-a) >> 8)+g) << 8)+
+ (((cb*(255-a) >> 8)+b));
+ *output++ = color;;
+ }
+ xk++;
+ }
+ yk++;
+ }
+ delete[] fixedKernel;
+}
+
+/*!
+ \internal
+*/
+void QPixmapConvolutionFilter::draw(QPainter *painter, const QPointF &p, const QPixmap &src, const QRectF& srcRect) const
+{
+ Q_D(const QPixmapConvolutionFilter);
+ if (!painter->isActive())
+ return;
+
+ if(d->kernelWidth<=0 || d->kernelHeight <= 0)
+ return;
+
+ if (src.isNull())
+ return;
+
+ QPixmapFilter *filter = painter->paintEngine() && painter->paintEngine()->isExtended() ?
+ static_cast<QPaintEngineEx *>(painter->paintEngine())->pixmapFilter(type(), this) : 0;
+ QPixmapConvolutionFilter *convolutionFilter = static_cast<QPixmapConvolutionFilter*>(filter);
+ if (convolutionFilter) {
+ convolutionFilter->setConvolutionKernel(d->convolutionKernel, d->kernelWidth, d->kernelHeight);
+ convolutionFilter->d_func()->convoluteAlpha = d->convoluteAlpha;
+ convolutionFilter->draw(painter, p, src, srcRect);
+ return;
+ }
+
+ // falling back to raster implementation
+
+ QImage *target = 0;
+ if (painter->paintEngine()->paintDevice()->devType() == QInternal::Image) {
+ target = static_cast<QImage *>(painter->paintEngine()->paintDevice());
+
+ QTransform mat = painter->combinedTransform();
+
+ if (mat.type() > QTransform::TxTranslate) {
+ // Disabled because of transformation...
+ target = 0;
+ } else {
+ QRasterPaintEngine *pe = static_cast<QRasterPaintEngine *>(painter->paintEngine());
+ if (pe->clipType() == QRasterPaintEngine::ComplexClip)
+ // disabled because of complex clipping...
+ target = 0;
+ else {
+ QRectF clip = pe->clipBoundingRect();
+ QRectF rect = boundingRectFor(srcRect.isEmpty() ? src.rect() : srcRect);
+ QTransform x = painter->deviceTransform();
+ if (!clip.contains(rect.translated(x.dx() + p.x(), x.dy() + p.y()))) {
+ target = 0;
+ }
+
+ }
+ }
+ }
+
+ if (target) {
+ QTransform x = painter->deviceTransform();
+ QPointF offset(x.dx(), x.dy());
+
+ convolute(target, p+offset, src.toImage(), srcRect, QPainter::CompositionMode_SourceOver, d->convolutionKernel, d->kernelWidth, d->kernelHeight);
+ } else {
+ QRect srect = srcRect.isNull() ? src.rect() : srcRect.toRect();
+ QRect rect = boundingRectFor(srect).toRect();
+ QImage result = QImage(rect.size(), QImage::Format_ARGB32_Premultiplied);
+ QPoint offset = srect.topLeft() - rect.topLeft();
+ convolute(&result,
+ offset,
+ src.toImage(),
+ srect,
+ QPainter::CompositionMode_Source,
+ d->convolutionKernel,
+ d->kernelWidth,
+ d->kernelHeight);
+ painter->drawImage(p - offset, result);
+ }
+}
+
+/*!
+ \class QPixmapBlurFilter
+ \since 4.6
+ \ingroup multimedia
+
+ \brief The QPixmapBlurFilter class provides blur filtering
+ for pixmaps.
+
+ QPixmapBlurFilter implements a blur pixmap filter,
+ which is applied when \l{QPixmapFilter::}{draw()} is called.
+
+ The filter lets you specialize the radius of the blur as well
+ as hints as to whether to prefer performance or quality.
+
+ By default, the blur effect is produced by applying an exponential
+ filter generated from the specified blurRadius(). Paint engines
+ may override this with a custom blur that is faster on the
+ underlying hardware.
+
+ \sa {Pixmap Filters Example}, QPixmapConvolutionFilter, QPixmapDropShadowFilter
+
+ \internal
+*/
+
+class QPixmapBlurFilterPrivate : public QPixmapFilterPrivate
+{
+public:
+ QPixmapBlurFilterPrivate() : radius(5), hints(QGraphicsBlurEffect::PerformanceHint) {}
+
+ qreal radius;
+ QGraphicsBlurEffect::BlurHints hints;
+};
+
+
+/*!
+ Constructs a pixmap blur filter.
+
+ \internal
+*/
+QPixmapBlurFilter::QPixmapBlurFilter(QObject *parent)
+ : QPixmapFilter(*new QPixmapBlurFilterPrivate, BlurFilter, parent)
+{
+}
+
+/*!
+ Destructor of pixmap blur filter.
+
+ \internal
+*/
+QPixmapBlurFilter::~QPixmapBlurFilter()
+{
+}
+
+/*!
+ Sets the radius of the blur filter. Higher radius produces increased blurriness.
+
+ \internal
+*/
+void QPixmapBlurFilter::setRadius(qreal radius)
+{
+ Q_D(QPixmapBlurFilter);
+ d->radius = radius;
+}
+
+/*!
+ Gets the radius of the blur filter.
+
+ \internal
+*/
+qreal QPixmapBlurFilter::radius() const
+{
+ Q_D(const QPixmapBlurFilter);
+ return d->radius;
+}
+
+/*!
+ Setting the blur hints to PerformanceHint causes the implementation
+ to trade off visual quality to blur the image faster. Setting the
+ blur hints to QualityHint causes the implementation to improve
+ visual quality at the expense of speed.
+
+ AnimationHint causes the implementation to optimize for animating
+ the blur radius, possibly by caching blurred versions of the source
+ pixmap.
+
+ The implementation is free to ignore this value if it only has a single
+ blur algorithm.
+
+ \internal
+*/
+void QPixmapBlurFilter::setBlurHints(QGraphicsBlurEffect::BlurHints hints)
+{
+ Q_D(QPixmapBlurFilter);
+ d->hints = hints;
+}
+
+/*!
+ Gets the blur hints of the blur filter.
+
+ \internal
+*/
+QGraphicsBlurEffect::BlurHints QPixmapBlurFilter::blurHints() const
+{
+ Q_D(const QPixmapBlurFilter);
+ return d->hints;
+}
+
+const qreal radiusScale = qreal(2.5);
+
+/*!
+ \internal
+*/
+QRectF QPixmapBlurFilter::boundingRectFor(const QRectF &rect) const
+{
+ Q_D(const QPixmapBlurFilter);
+ const qreal delta = radiusScale * d->radius + 1;
+ return rect.adjusted(-delta, -delta, delta, delta);
+}
+
+template <int shift>
+inline int qt_static_shift(int value)
+{
+ if (shift == 0)
+ return value;
+ else if (shift > 0)
+ return value << (uint(shift) & 0x1f);
+ else
+ return value >> (uint(-shift) & 0x1f);
+}
+
+template<int aprec, int zprec>
+inline void qt_blurinner(uchar *bptr, int &zR, int &zG, int &zB, int &zA, int alpha)
+{
+ QRgb *pixel = (QRgb *)bptr;
+
+#define Z_MASK (0xff << zprec)
+ const int A_zprec = qt_static_shift<zprec - 24>(*pixel) & Z_MASK;
+ const int R_zprec = qt_static_shift<zprec - 16>(*pixel) & Z_MASK;
+ const int G_zprec = qt_static_shift<zprec - 8>(*pixel) & Z_MASK;
+ const int B_zprec = qt_static_shift<zprec>(*pixel) & Z_MASK;
+#undef Z_MASK
+
+ const int zR_zprec = zR >> aprec;
+ const int zG_zprec = zG >> aprec;
+ const int zB_zprec = zB >> aprec;
+ const int zA_zprec = zA >> aprec;
+
+ zR += alpha * (R_zprec - zR_zprec);
+ zG += alpha * (G_zprec - zG_zprec);
+ zB += alpha * (B_zprec - zB_zprec);
+ zA += alpha * (A_zprec - zA_zprec);
+
+#define ZA_MASK (0xff << (zprec + aprec))
+ *pixel =
+ qt_static_shift<24 - zprec - aprec>(zA & ZA_MASK)
+ | qt_static_shift<16 - zprec - aprec>(zR & ZA_MASK)
+ | qt_static_shift<8 - zprec - aprec>(zG & ZA_MASK)
+ | qt_static_shift<-zprec - aprec>(zB & ZA_MASK);
+#undef ZA_MASK
+}
+
+const int alphaIndex = (QSysInfo::ByteOrder == QSysInfo::BigEndian ? 0 : 3);
+
+template<int aprec, int zprec>
+inline void qt_blurinner_alphaOnly(uchar *bptr, int &z, int alpha)
+{
+ const int A_zprec = int(*(bptr)) << zprec;
+ const int z_zprec = z >> aprec;
+ z += alpha * (A_zprec - z_zprec);
+ *(bptr) = z >> (zprec + aprec);
+}
+
+template<int aprec, int zprec, bool alphaOnly>
+inline void qt_blurrow(QImage & im, int line, int alpha)
+{
+ uchar *bptr = im.scanLine(line);
+
+ int zR = 0, zG = 0, zB = 0, zA = 0;
+
+ if (alphaOnly && im.format() != QImage::Format_Indexed8)
+ bptr += alphaIndex;
+
+ const int stride = im.depth() >> 3;
+ const int im_width = im.width();
+ for (int index = 0; index < im_width; ++index) {
+ if (alphaOnly)
+ qt_blurinner_alphaOnly<aprec, zprec>(bptr, zA, alpha);
+ else
+ qt_blurinner<aprec, zprec>(bptr, zR, zG, zB, zA, alpha);
+ bptr += stride;
+ }
+
+ bptr -= stride;
+
+ for (int index = im_width - 2; index >= 0; --index) {
+ bptr -= stride;
+ if (alphaOnly)
+ qt_blurinner_alphaOnly<aprec, zprec>(bptr, zA, alpha);
+ else
+ qt_blurinner<aprec, zprec>(bptr, zR, zG, zB, zA, alpha);
+ }
+}
+
+/*
+* expblur(QImage &img, int radius)
+*
+* Based on exponential blur algorithm by Jani Huhtanen
+*
+* In-place blur of image 'img' with kernel
+* of approximate radius 'radius'.
+*
+* Blurs with two sided exponential impulse
+* response.
+*
+* aprec = precision of alpha parameter
+* in fixed-point format 0.aprec
+*
+* zprec = precision of state parameters
+* zR,zG,zB and zA in fp format 8.zprec
+*/
+template <int aprec, int zprec, bool alphaOnly>
+void expblur(QImage &img, qreal radius, bool improvedQuality = false, int transposed = 0)
+{
+ // halve the radius if we're using two passes
+ if (improvedQuality)
+ radius *= qreal(0.5);
+
+ Q_ASSERT(img.format() == QImage::Format_ARGB32_Premultiplied
+ || img.format() == QImage::Format_RGB32
+ || img.format() == QImage::Format_Indexed8);
+
+ // choose the alpha such that pixels at radius distance from a fully
+ // saturated pixel will have an alpha component of no greater than
+ // the cutOffIntensity
+ const qreal cutOffIntensity = 2;
+ int alpha = radius <= qreal(1e-5)
+ ? ((1 << aprec)-1)
+ : qRound((1<<aprec)*(1 - qPow(cutOffIntensity * (1 / qreal(255)), 1 / radius)));
+
+ int img_height = img.height();
+ for (int row = 0; row < img_height; ++row) {
+ for (int i = 0; i <= int(improvedQuality); ++i)
+ qt_blurrow<aprec, zprec, alphaOnly>(img, row, alpha);
+ }
+
+ QImage temp(img.height(), img.width(), img.format());
+ if (transposed >= 0) {
+ if (img.depth() == 8) {
+ qt_memrotate270(reinterpret_cast<const quint8*>(img.bits()),
+ img.width(), img.height(), img.bytesPerLine(),
+ reinterpret_cast<quint8*>(temp.bits()),
+ temp.bytesPerLine());
+ } else {
+ qt_memrotate270(reinterpret_cast<const quint32*>(img.bits()),
+ img.width(), img.height(), img.bytesPerLine(),
+ reinterpret_cast<quint32*>(temp.bits()),
+ temp.bytesPerLine());
+ }
+ } else {
+ if (img.depth() == 8) {
+ qt_memrotate90(reinterpret_cast<const quint8*>(img.bits()),
+ img.width(), img.height(), img.bytesPerLine(),
+ reinterpret_cast<quint8*>(temp.bits()),
+ temp.bytesPerLine());
+ } else {
+ qt_memrotate90(reinterpret_cast<const quint32*>(img.bits()),
+ img.width(), img.height(), img.bytesPerLine(),
+ reinterpret_cast<quint32*>(temp.bits()),
+ temp.bytesPerLine());
+ }
+ }
+
+ img_height = temp.height();
+ for (int row = 0; row < img_height; ++row) {
+ for (int i = 0; i <= int(improvedQuality); ++i)
+ qt_blurrow<aprec, zprec, alphaOnly>(temp, row, alpha);
+ }
+
+ if (transposed == 0) {
+ if (img.depth() == 8) {
+ qt_memrotate90(reinterpret_cast<const quint8*>(temp.bits()),
+ temp.width(), temp.height(), temp.bytesPerLine(),
+ reinterpret_cast<quint8*>(img.bits()),
+ img.bytesPerLine());
+ } else {
+ qt_memrotate90(reinterpret_cast<const quint32*>(temp.bits()),
+ temp.width(), temp.height(), temp.bytesPerLine(),
+ reinterpret_cast<quint32*>(img.bits()),
+ img.bytesPerLine());
+ }
+ } else {
+ img = temp;
+ }
+}
+#define AVG(a,b) ( ((((a)^(b)) & 0xfefefefeUL) >> 1) + ((a)&(b)) )
+#define AVG16(a,b) ( ((((a)^(b)) & 0xf7deUL) >> 1) + ((a)&(b)) )
+
+Q_GUI_EXPORT QImage qt_halfScaled(const QImage &source)
+{
+ if (source.width() < 2 || source.height() < 2)
+ return QImage();
+
+ QImage srcImage = source;
+
+ if (source.format() == QImage::Format_Indexed8) {
+ // assumes grayscale
+ QImage dest(source.width() / 2, source.height() / 2, srcImage.format());
+
+ const uchar *src = reinterpret_cast<const uchar*>(const_cast<const QImage &>(srcImage).bits());
+ int sx = srcImage.bytesPerLine();
+ int sx2 = sx << 1;
+
+ uchar *dst = reinterpret_cast<uchar*>(dest.bits());
+ int dx = dest.bytesPerLine();
+ int ww = dest.width();
+ int hh = dest.height();
+
+ for (int y = hh; y; --y, dst += dx, src += sx2) {
+ const uchar *p1 = src;
+ const uchar *p2 = src + sx;
+ uchar *q = dst;
+ for (int x = ww; x; --x, ++q, p1 += 2, p2 += 2)
+ *q = ((int(p1[0]) + int(p1[1]) + int(p2[0]) + int(p2[1])) + 2) >> 2;
+ }
+
+ return dest;
+ } else if (source.format() == QImage::Format_ARGB8565_Premultiplied) {
+ QImage dest(source.width() / 2, source.height() / 2, srcImage.format());
+
+ const uchar *src = reinterpret_cast<const uchar*>(const_cast<const QImage &>(srcImage).bits());
+ int sx = srcImage.bytesPerLine();
+ int sx2 = sx << 1;
+
+ uchar *dst = reinterpret_cast<uchar*>(dest.bits());
+ int dx = dest.bytesPerLine();
+ int ww = dest.width();
+ int hh = dest.height();
+
+ for (int y = hh; y; --y, dst += dx, src += sx2) {
+ const uchar *p1 = src;
+ const uchar *p2 = src + sx;
+ uchar *q = dst;
+ for (int x = ww; x; --x, q += 3, p1 += 6, p2 += 6) {
+ // alpha
+ q[0] = AVG(AVG(p1[0], p1[3]), AVG(p2[0], p2[3]));
+ // rgb
+ const quint16 p16_1 = (p1[2] << 8) | p1[1];
+ const quint16 p16_2 = (p1[5] << 8) | p1[4];
+ const quint16 p16_3 = (p2[2] << 8) | p2[1];
+ const quint16 p16_4 = (p2[5] << 8) | p2[4];
+ const quint16 result = AVG16(AVG16(p16_1, p16_2), AVG16(p16_3, p16_4));
+ q[1] = result & 0xff;
+ q[2] = result >> 8;
+ }
+ }
+
+ return dest;
+ } else if (source.format() != QImage::Format_ARGB32_Premultiplied
+ && source.format() != QImage::Format_RGB32)
+ {
+ srcImage = source.convertToFormat(QImage::Format_ARGB32_Premultiplied);
+ }
+
+ QImage dest(source.width() / 2, source.height() / 2, srcImage.format());
+
+ const quint32 *src = reinterpret_cast<const quint32*>(const_cast<const QImage &>(srcImage).bits());
+ int sx = srcImage.bytesPerLine() >> 2;
+ int sx2 = sx << 1;
+
+ quint32 *dst = reinterpret_cast<quint32*>(dest.bits());
+ int dx = dest.bytesPerLine() >> 2;
+ int ww = dest.width();
+ int hh = dest.height();
+
+ for (int y = hh; y; --y, dst += dx, src += sx2) {
+ const quint32 *p1 = src;
+ const quint32 *p2 = src + sx;
+ quint32 *q = dst;
+ for (int x = ww; x; --x, q++, p1 += 2, p2 += 2)
+ *q = AVG(AVG(p1[0], p1[1]), AVG(p2[0], p2[1]));
+ }
+
+ return dest;
+}
+
+Q_GUI_EXPORT void qt_blurImage(QPainter *p, QImage &blurImage, qreal radius, bool quality, bool alphaOnly, int transposed = 0)
+{
+ if (blurImage.format() != QImage::Format_ARGB32_Premultiplied
+ && blurImage.format() != QImage::Format_RGB32)
+ {
+ blurImage = blurImage.convertToFormat(QImage::Format_ARGB32_Premultiplied);
+ }
+
+ qreal scale = 1;
+ if (radius >= 4 && blurImage.width() >= 2 && blurImage.height() >= 2) {
+ blurImage = qt_halfScaled(blurImage);
+ scale = 2;
+ radius *= qreal(0.5);
+ }
+
+ if (alphaOnly)
+ expblur<12, 10, true>(blurImage, radius, quality, transposed);
+ else
+ expblur<12, 10, false>(blurImage, radius, quality, transposed);
+
+ if (p) {
+ p->scale(scale, scale);
+ p->setRenderHint(QPainter::SmoothPixmapTransform);
+ p->drawImage(QRect(0, 0, blurImage.width(), blurImage.height()), blurImage);
+ }
+}
+
+Q_GUI_EXPORT void qt_blurImage(QImage &blurImage, qreal radius, bool quality, int transposed = 0)
+{
+ if (blurImage.format() == QImage::Format_Indexed8)
+ expblur<12, 10, true>(blurImage, radius, quality, transposed);
+ else
+ expblur<12, 10, false>(blurImage, radius, quality, transposed);
+}
+
+Q_GUI_EXPORT bool qt_scaleForTransform(const QTransform &transform, qreal *scale);
+
+/*!
+ \internal
+*/
+void QPixmapBlurFilter::draw(QPainter *painter, const QPointF &p, const QPixmap &src, const QRectF &rect) const
+{
+ Q_D(const QPixmapBlurFilter);
+ if (!painter->isActive())
+ return;
+
+ if (src.isNull())
+ return;
+
+ QRectF srcRect = rect;
+ if (srcRect.isNull())
+ srcRect = src.rect();
+
+ if (d->radius <= 1) {
+ painter->drawPixmap(srcRect.translated(p), src, srcRect);
+ return;
+ }
+
+ qreal scaledRadius = radiusScale * d->radius;
+ qreal scale;
+ if (qt_scaleForTransform(painter->transform(), &scale))
+ scaledRadius /= scale;
+
+ QPixmapFilter *filter = painter->paintEngine() && painter->paintEngine()->isExtended() ?
+ static_cast<QPaintEngineEx *>(painter->paintEngine())->pixmapFilter(type(), this) : 0;
+ QPixmapBlurFilter *blurFilter = static_cast<QPixmapBlurFilter*>(filter);
+ if (blurFilter) {
+ blurFilter->setRadius(scaledRadius);
+ blurFilter->setBlurHints(d->hints);
+ blurFilter->draw(painter, p, src, srcRect);
+ return;
+ }
+
+ QImage srcImage;
+ QImage destImage;
+
+ if (srcRect == src.rect()) {
+ srcImage = src.toImage();
+ } else {
+ QRect rect = srcRect.toAlignedRect().intersected(src.rect());
+ srcImage = src.copy(rect).toImage();
+ }
+
+ QTransform transform = painter->worldTransform();
+ painter->translate(p);
+ qt_blurImage(painter, srcImage, scaledRadius, (d->hints & QGraphicsBlurEffect::QualityHint), false);
+ painter->setWorldTransform(transform);
+}
+
+// grayscales the image to dest (could be same). If rect isn't defined
+// destination image size is used to determine the dimension of grayscaling
+// process.
+static void grayscale(const QImage &image, QImage &dest, const QRect& rect = QRect())
+{
+ QRect destRect = rect;
+ QRect srcRect = rect;
+ if (rect.isNull()) {
+ srcRect = dest.rect();
+ destRect = dest.rect();
+ }
+ if (&image != &dest) {
+ destRect.moveTo(QPoint(0, 0));
+ }
+
+ unsigned int *data = (unsigned int *)image.bits();
+ unsigned int *outData = (unsigned int *)dest.bits();
+
+ if (dest.size() == image.size() && image.rect() == srcRect) {
+ // a bit faster loop for grayscaling everything
+ int pixels = dest.width() * dest.height();
+ for (int i = 0; i < pixels; ++i) {
+ int val = qGray(data[i]);
+ outData[i] = qRgba(val, val, val, qAlpha(data[i]));
+ }
+ } else {
+ int yd = destRect.top();
+ for (int y = srcRect.top(); y <= srcRect.bottom() && y < image.height(); y++) {
+ data = (unsigned int*)image.scanLine(y);
+ outData = (unsigned int*)dest.scanLine(yd++);
+ int xd = destRect.left();
+ for (int x = srcRect.left(); x <= srcRect.right() && x < image.width(); x++) {
+ int val = qGray(data[x]);
+ outData[xd++] = qRgba(val, val, val, qAlpha(data[x]));
+ }
+ }
+ }
+}
+
+/*!
+ \class QPixmapColorizeFilter
+ \since 4.5
+ \ingroup painting
+
+ \brief The QPixmapColorizeFilter class provides colorizing
+ filtering for pixmaps.
+
+ A colorize filter gives the pixmap a tint of its color(). The
+ filter first grayscales the pixmap and then converts those to
+ colorized values using QPainter::CompositionMode_Screen with the
+ chosen color. The alpha-channel is not changed.
+
+ Example:
+ \snippet doc/src/snippets/code/src_gui_image_qpixmapfilter.cpp 0
+
+ \sa QPainter::CompositionMode
+
+ \internal
+*/
+class QPixmapColorizeFilterPrivate : public QPixmapFilterPrivate
+{
+ Q_DECLARE_PUBLIC(QPixmapColorizeFilter)
+public:
+ QColor color;
+ qreal strength;
+ quint32 opaque : 1;
+ quint32 alphaBlend : 1;
+ quint32 padding : 30;
+};
+
+/*!
+ Constructs an pixmap colorize filter.
+
+ Default color value for colorizing is QColor(0, 0, 192).
+
+ \internal
+*/
+QPixmapColorizeFilter::QPixmapColorizeFilter(QObject *parent)
+ : QPixmapFilter(*new QPixmapColorizeFilterPrivate, ColorizeFilter, parent)
+{
+ Q_D(QPixmapColorizeFilter);
+ d->color = QColor(0, 0, 192);
+ d->strength = qreal(1);
+ d->opaque = true;
+ d->alphaBlend = false;
+}
+
+/*!
+ Gets the color of the colorize filter.
+
+ \internal
+*/
+QColor QPixmapColorizeFilter::color() const
+{
+ Q_D(const QPixmapColorizeFilter);
+ return d->color;
+}
+
+/*!
+ Sets the color of the colorize filter to the \a color specified.
+
+ \internal
+*/
+void QPixmapColorizeFilter::setColor(const QColor &color)
+{
+ Q_D(QPixmapColorizeFilter);
+ d->color = color;
+}
+
+/*!
+ Gets the strength of the colorize filter, 1.0 means full colorized while
+ 0.0 equals to no filtering at all.
+
+ \internal
+*/
+qreal QPixmapColorizeFilter::strength() const
+{
+ Q_D(const QPixmapColorizeFilter);
+ return d->strength;
+}
+
+/*!
+ Sets the strength of the colorize filter to \a strength.
+
+ \internal
+*/
+void QPixmapColorizeFilter::setStrength(qreal strength)
+{
+ Q_D(QPixmapColorizeFilter);
+ d->strength = qBound(qreal(0), strength, qreal(1));
+ d->opaque = !qFuzzyIsNull(d->strength);
+ d->alphaBlend = !qFuzzyIsNull(d->strength - 1);
+}
+
+/*!
+ \internal
+*/
+void QPixmapColorizeFilter::draw(QPainter *painter, const QPointF &dest, const QPixmap &src, const QRectF &srcRect) const
+{
+ Q_D(const QPixmapColorizeFilter);
+
+ if (src.isNull())
+ return;
+
+ QPixmapFilter *filter = painter->paintEngine() && painter->paintEngine()->isExtended() ?
+ static_cast<QPaintEngineEx *>(painter->paintEngine())->pixmapFilter(type(), this) : 0;
+ QPixmapColorizeFilter *colorizeFilter = static_cast<QPixmapColorizeFilter*>(filter);
+ if (colorizeFilter) {
+ colorizeFilter->setColor(d->color);
+ colorizeFilter->setStrength(d->strength);
+ colorizeFilter->draw(painter, dest, src, srcRect);
+ return;
+ }
+
+ // falling back to raster implementation
+
+ if (!d->opaque) {
+ painter->drawPixmap(dest, src, srcRect);
+ return;
+ }
+
+ QImage srcImage;
+ QImage destImage;
+
+ if (srcRect.isNull()) {
+ srcImage = src.toImage();
+ srcImage = srcImage.convertToFormat(srcImage.hasAlphaChannel() ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32);
+ destImage = QImage(srcImage.size(), srcImage.format());
+ } else {
+ QRect rect = srcRect.toAlignedRect().intersected(src.rect());
+
+ srcImage = src.copy(rect).toImage();
+ srcImage = srcImage.convertToFormat(srcImage.hasAlphaChannel() ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32);
+ destImage = QImage(rect.size(), srcImage.format());
+ }
+
+ // do colorizing
+ QPainter destPainter(&destImage);
+ grayscale(srcImage, destImage, srcImage.rect());
+ destPainter.setCompositionMode(QPainter::CompositionMode_Screen);
+ destPainter.fillRect(srcImage.rect(), d->color);
+ destPainter.end();
+
+ if (d->alphaBlend) {
+ // alpha blending srcImage and destImage
+ QImage buffer = srcImage;
+ QPainter bufPainter(&buffer);
+ bufPainter.setOpacity(d->strength);
+ bufPainter.drawImage(0, 0, destImage);
+ bufPainter.end();
+ destImage = buffer;
+ }
+
+ if (srcImage.hasAlphaChannel())
+ destImage.setAlphaChannel(srcImage.alphaChannel());
+
+ painter->drawImage(dest, destImage);
+}
+
+class QPixmapDropShadowFilterPrivate : public QPixmapFilterPrivate
+{
+public:
+ QPixmapDropShadowFilterPrivate()
+ : offset(8, 8), color(63, 63, 63, 180), radius(1) {}
+
+ QPointF offset;
+ QColor color;
+ qreal radius;
+};
+
+/*!
+ \class QPixmapDropShadowFilter
+ \since 4.5
+ \ingroup painting
+
+ \brief The QPixmapDropShadowFilter class is a convenience class
+ for drawing pixmaps with drop shadows.
+
+ The drop shadow is produced by taking a copy of the source pixmap
+ and applying a color to the copy using a
+ QPainter::CompositionMode_DestinationIn operation. This produces a
+ homogeneously-colored pixmap which is then drawn using a
+ QPixmapConvolutionFilter at an offset. The original pixmap is
+ drawn on top.
+
+ The QPixmapDropShadowFilter class provides some customization
+ options to specify how the drop shadow should appear. The color of
+ the drop shadow can be modified using the setColor() function, the
+ drop shadow offset can be modified using the setOffset() function,
+ and the blur radius of the drop shadow can be changed through the
+ setBlurRadius() function.
+
+ By default, the drop shadow is a dark gray shadow, blurred with a
+ radius of 1 at an offset of 8 pixels towards the lower right.
+
+ Example:
+ \snippet doc/src/snippets/code/src_gui_image_qpixmapfilter.cpp 2
+
+ \sa QPixmapColorizeFilter, QPixmapConvolutionFilter
+
+ \internal
+ */
+
+/*!
+ Constructs drop shadow filter.
+
+ \internal
+*/
+QPixmapDropShadowFilter::QPixmapDropShadowFilter(QObject *parent)
+ : QPixmapFilter(*new QPixmapDropShadowFilterPrivate, DropShadowFilter, parent)
+{
+}
+
+/*!
+ Destroys drop shadow filter.
+
+ \internal
+*/
+QPixmapDropShadowFilter::~QPixmapDropShadowFilter()
+{
+}
+
+/*!
+ Returns the radius in pixels of the blur on the drop shadow.
+
+ A smaller radius results in a sharper shadow.
+
+ \sa color(), offset()
+
+ \internal
+*/
+qreal QPixmapDropShadowFilter::blurRadius() const
+{
+ Q_D(const QPixmapDropShadowFilter);
+ return d->radius;
+}
+
+/*!
+ Sets the radius in pixels of the blur on the drop shadow to the \a radius specified.
+
+ Using a smaller radius results in a sharper shadow.
+
+ \sa setColor(), setOffset()
+
+ \internal
+*/
+void QPixmapDropShadowFilter::setBlurRadius(qreal radius)
+{
+ Q_D(QPixmapDropShadowFilter);
+ d->radius = radius;
+}
+
+/*!
+ Returns the color of the drop shadow.
+
+ \sa blurRadius(), offset()
+
+ \internal
+*/
+QColor QPixmapDropShadowFilter::color() const
+{
+ Q_D(const QPixmapDropShadowFilter);
+ return d->color;
+}
+
+/*!
+ Sets the color of the drop shadow to the \a color specified.
+
+ \sa setBlurRadius(), setOffset()
+
+ \internal
+*/
+void QPixmapDropShadowFilter::setColor(const QColor &color)
+{
+ Q_D(QPixmapDropShadowFilter);
+ d->color = color;
+}
+
+/*!
+ Returns the shadow offset in pixels.
+
+ \sa blurRadius(), color()
+
+ \internal
+*/
+QPointF QPixmapDropShadowFilter::offset() const
+{
+ Q_D(const QPixmapDropShadowFilter);
+ return d->offset;
+}
+
+/*!
+ Sets the shadow offset in pixels to the \a offset specified.
+
+ \sa setBlurRadius(), setColor()
+
+ \internal
+*/
+void QPixmapDropShadowFilter::setOffset(const QPointF &offset)
+{
+ Q_D(QPixmapDropShadowFilter);
+ d->offset = offset;
+}
+
+/*!
+ \fn void QPixmapDropShadowFilter::setOffset(qreal dx, qreal dy)
+ \overload
+
+ Sets the shadow offset in pixels to be the displacement specified by the
+ horizontal \a dx and vertical \a dy coordinates.
+
+ \sa setBlurRadius(), setColor()
+
+ \internal
+*/
+
+/*!
+ \internal
+ */
+QRectF QPixmapDropShadowFilter::boundingRectFor(const QRectF &rect) const
+{
+ Q_D(const QPixmapDropShadowFilter);
+ return rect.united(rect.translated(d->offset).adjusted(-d->radius, -d->radius, d->radius, d->radius));
+}
+
+/*!
+ \internal
+ */
+void QPixmapDropShadowFilter::draw(QPainter *p,
+ const QPointF &pos,
+ const QPixmap &px,
+ const QRectF &src) const
+{
+ Q_D(const QPixmapDropShadowFilter);
+
+ if (px.isNull())
+ return;
+
+ QPixmapFilter *filter = p->paintEngine() && p->paintEngine()->isExtended() ?
+ static_cast<QPaintEngineEx *>(p->paintEngine())->pixmapFilter(type(), this) : 0;
+ QPixmapDropShadowFilter *dropShadowFilter = static_cast<QPixmapDropShadowFilter*>(filter);
+ if (dropShadowFilter) {
+ dropShadowFilter->setColor(d->color);
+ dropShadowFilter->setBlurRadius(d->radius);
+ dropShadowFilter->setOffset(d->offset);
+ dropShadowFilter->draw(p, pos, px, src);
+ return;
+ }
+
+ QImage tmp(px.size(), QImage::Format_ARGB32_Premultiplied);
+ tmp.fill(0);
+ QPainter tmpPainter(&tmp);
+ tmpPainter.setCompositionMode(QPainter::CompositionMode_Source);
+ tmpPainter.drawPixmap(d->offset, px);
+ tmpPainter.end();
+
+ // blur the alpha channel
+ QImage blurred(tmp.size(), QImage::Format_ARGB32_Premultiplied);
+ blurred.fill(0);
+ QPainter blurPainter(&blurred);
+ qt_blurImage(&blurPainter, tmp, d->radius, false, true);
+ blurPainter.end();
+
+ tmp = blurred;
+
+ // blacken the image...
+ tmpPainter.begin(&tmp);
+ tmpPainter.setCompositionMode(QPainter::CompositionMode_SourceIn);
+ tmpPainter.fillRect(tmp.rect(), d->color);
+ tmpPainter.end();
+
+ // draw the blurred drop shadow...
+ p->drawImage(pos, tmp);
+
+ // Draw the actual pixmap...
+ p->drawPixmap(pos, px, src);
+}
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_GRAPHICSEFFECT
diff --git a/src/gui/image/qpixmapfilter_p.h b/src/gui/image/qpixmapfilter_p.h
new file mode 100644
index 0000000000..961866c19b
--- /dev/null
+++ b/src/gui/image/qpixmapfilter_p.h
@@ -0,0 +1,196 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPIXMAPFILTER_H
+#define QPIXMAPFILTER_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qnamespace.h>
+#include <QtGui/qpixmap.h>
+#include <QtGui/qgraphicseffect.h>
+
+#ifndef QT_NO_GRAPHICSEFFECT
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QPainter;
+class QPixmapData;
+
+class QPixmapFilterPrivate;
+
+class Q_GUI_EXPORT QPixmapFilter : public QObject
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QPixmapFilter)
+public:
+ virtual ~QPixmapFilter() = 0;
+
+ enum FilterType {
+ ConvolutionFilter,
+ ColorizeFilter,
+ DropShadowFilter,
+ BlurFilter,
+
+ UserFilter = 1024
+ };
+
+ FilterType type() const;
+
+ virtual QRectF boundingRectFor(const QRectF &rect) const;
+
+ virtual void draw(QPainter *painter, const QPointF &p, const QPixmap &src, const QRectF &srcRect = QRectF()) const = 0;
+
+protected:
+ QPixmapFilter(QPixmapFilterPrivate &d, FilterType type, QObject *parent);
+ QPixmapFilter(FilterType type, QObject *parent);
+};
+
+class QPixmapConvolutionFilterPrivate;
+
+class Q_GUI_EXPORT QPixmapConvolutionFilter : public QPixmapFilter
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QPixmapConvolutionFilter)
+
+public:
+ QPixmapConvolutionFilter(QObject *parent = 0);
+ ~QPixmapConvolutionFilter();
+
+ void setConvolutionKernel(const qreal *matrix, int rows, int columns);
+
+ QRectF boundingRectFor(const QRectF &rect) const;
+ void draw(QPainter *painter, const QPointF &dest, const QPixmap &src, const QRectF &srcRect = QRectF()) const;
+
+private:
+ friend class QGLPixmapConvolutionFilter;
+ friend class QVGPixmapConvolutionFilter;
+ const qreal *convolutionKernel() const;
+ int rows() const;
+ int columns() const;
+};
+
+class QPixmapBlurFilterPrivate;
+
+class Q_GUI_EXPORT QPixmapBlurFilter : public QPixmapFilter
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QPixmapBlurFilter)
+
+public:
+ QPixmapBlurFilter(QObject *parent = 0);
+ ~QPixmapBlurFilter();
+
+ void setRadius(qreal radius);
+ void setBlurHints(QGraphicsBlurEffect::BlurHints hints);
+
+ qreal radius() const;
+ QGraphicsBlurEffect::BlurHints blurHints() const;
+
+ QRectF boundingRectFor(const QRectF &rect) const;
+ void draw(QPainter *painter, const QPointF &dest, const QPixmap &src, const QRectF &srcRect = QRectF()) const;
+
+private:
+ friend class QGLPixmapBlurFilter;
+};
+
+class QPixmapColorizeFilterPrivate;
+
+class Q_GUI_EXPORT QPixmapColorizeFilter : public QPixmapFilter
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QPixmapColorizeFilter)
+
+public:
+ QPixmapColorizeFilter(QObject *parent = 0);
+
+ void setColor(const QColor& color);
+ QColor color() const;
+
+ void setStrength(qreal strength);
+ qreal strength() const;
+
+ void draw(QPainter *painter, const QPointF &dest, const QPixmap &src, const QRectF &srcRect = QRectF()) const;
+};
+
+class QPixmapDropShadowFilterPrivate;
+
+class Q_GUI_EXPORT QPixmapDropShadowFilter : public QPixmapFilter
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QPixmapDropShadowFilter)
+
+public:
+ QPixmapDropShadowFilter(QObject *parent = 0);
+ ~QPixmapDropShadowFilter();
+
+ QRectF boundingRectFor(const QRectF &rect) const;
+ void draw(QPainter *p, const QPointF &pos, const QPixmap &px, const QRectF &src = QRectF()) const;
+
+ qreal blurRadius() const;
+ void setBlurRadius(qreal radius);
+
+ QColor color() const;
+ void setColor(const QColor &color);
+
+ QPointF offset() const;
+ void setOffset(const QPointF &offset);
+ inline void setOffset(qreal dx, qreal dy) { setOffset(QPointF(dx, dy)); }
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif //QT_NO_GRAPHICSEFFECT
+#endif // QPIXMAPFILTER_H
diff --git a/src/gui/image/qpnghandler.cpp b/src/gui/image/qpnghandler.cpp
new file mode 100644
index 0000000000..c2a8b00ef5
--- /dev/null
+++ b/src/gui/image/qpnghandler.cpp
@@ -0,0 +1,991 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "private/qpnghandler_p.h"
+
+#ifndef QT_NO_IMAGEFORMAT_PNG
+#include <qcoreapplication.h>
+#include <qiodevice.h>
+#include <qimage.h>
+#include <qlist.h>
+#include <qtextcodec.h>
+#include <qvariant.h>
+#include <qvector.h>
+
+#ifdef QT_USE_BUNDLED_LIBPNG
+#include <../../3rdparty/libpng/png.h>
+#include <../../3rdparty/libpng/pngconf.h>
+#else
+#include <png.h>
+#include <pngconf.h>
+#endif
+
+#ifdef Q_OS_WINCE
+#define CALLBACK_CALL_TYPE __cdecl
+#else
+#define CALLBACK_CALL_TYPE
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#if defined(Q_OS_WINCE) && defined(STANDARDSHELL_UI_MODEL)
+# define Q_INTERNAL_WIN_NO_THROW __declspec(nothrow)
+#else
+# define Q_INTERNAL_WIN_NO_THROW
+#endif
+
+// avoid going through QImage::scanLine() which calls detach
+#define FAST_SCAN_LINE(data, bpl, y) (data + (y) * bpl)
+
+/*
+ All PNG files load to the minimal QImage equivalent.
+
+ All QImage formats output to reasonably efficient PNG equivalents.
+ Never to grayscale.
+*/
+
+class QPngHandlerPrivate
+{
+public:
+ enum State {
+ Ready,
+ ReadHeader,
+ ReadingEnd,
+ Error
+ };
+
+ QPngHandlerPrivate(QPngHandler *qq)
+ : gamma(0.0), quality(2), png_ptr(0), info_ptr(0),
+ end_info(0), row_pointers(0), state(Ready), q(qq)
+ { }
+
+ float gamma;
+ int quality;
+ QString description;
+ QStringList readTexts;
+
+ png_struct *png_ptr;
+ png_info *info_ptr;
+ png_info *end_info;
+ png_byte **row_pointers;
+
+ bool readPngHeader();
+ bool readPngImage(QImage *image);
+ void readPngTexts(png_info *info);
+
+ QImage::Format readImageFormat();
+
+ State state;
+
+ QPngHandler *q;
+};
+
+
+#if defined(Q_C_CALLBACKS)
+extern "C" {
+#endif
+
+class QPNGImageWriter {
+public:
+ explicit QPNGImageWriter(QIODevice*);
+ ~QPNGImageWriter();
+
+ enum DisposalMethod { Unspecified, NoDisposal, RestoreBackground, RestoreImage };
+ void setDisposalMethod(DisposalMethod);
+ void setLooping(int loops=0); // 0 == infinity
+ void setFrameDelay(int msecs);
+ void setGamma(float);
+
+ bool writeImage(const QImage& img, int x, int y);
+ bool writeImage(const QImage& img, int quality, const QString &description, int x, int y);
+ bool writeImage(const QImage& img)
+ { return writeImage(img, 0, 0); }
+ bool writeImage(const QImage& img, int quality, const QString &description)
+ { return writeImage(img, quality, description, 0, 0); }
+
+ QIODevice* device() { return dev; }
+
+private:
+ QIODevice* dev;
+ int frames_written;
+ DisposalMethod disposal;
+ int looping;
+ int ms_delay;
+ float gamma;
+};
+
+static
+void CALLBACK_CALL_TYPE iod_read_fn(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+ QPngHandlerPrivate *d = (QPngHandlerPrivate *)png_get_io_ptr(png_ptr);
+ QIODevice *in = d->q->device();
+
+ if (d->state == QPngHandlerPrivate::ReadingEnd && !in->isSequential() && (in->size() - in->pos()) < 4 && length == 4) {
+ // Workaround for certain malformed PNGs that lack the final crc bytes
+ uchar endcrc[4] = { 0xae, 0x42, 0x60, 0x82 };
+ qMemCopy(data, endcrc, 4);
+ in->seek(in->size());
+ return;
+ }
+
+ while (length) {
+ int nr = in->read((char*)data, length);
+ if (nr <= 0) {
+ png_error(png_ptr, "Read Error");
+ return;
+ }
+ length -= nr;
+ }
+}
+
+
+static
+void CALLBACK_CALL_TYPE qpiw_write_fn(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+ QPNGImageWriter* qpiw = (QPNGImageWriter*)png_get_io_ptr(png_ptr);
+ QIODevice* out = qpiw->device();
+
+ uint nr = out->write((char*)data, length);
+ if (nr != length) {
+ png_error(png_ptr, "Write Error");
+ return;
+ }
+}
+
+
+static
+void CALLBACK_CALL_TYPE qpiw_flush_fn(png_structp /* png_ptr */)
+{
+}
+
+#if defined(Q_C_CALLBACKS)
+}
+#endif
+
+static
+void setup_qt(QImage& image, png_structp png_ptr, png_infop info_ptr, float screen_gamma=0.0)
+{
+ if (screen_gamma != 0.0 && png_get_valid(png_ptr, info_ptr, PNG_INFO_gAMA)) {
+ double file_gamma;
+ png_get_gAMA(png_ptr, info_ptr, &file_gamma);
+ png_set_gamma(png_ptr, screen_gamma, file_gamma);
+ }
+
+ png_uint_32 width;
+ png_uint_32 height;
+ int bit_depth;
+ int color_type;
+ png_bytep trans_alpha = 0;
+ png_color_16p trans_color_p = 0;
+ int num_trans;
+ png_colorp palette = 0;
+ int num_palette;
+ png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, 0, 0, 0);
+ png_set_interlace_handling(png_ptr);
+
+ if (color_type == PNG_COLOR_TYPE_GRAY) {
+ // Black & White or 8-bit grayscale
+ if (bit_depth == 1 && png_get_channels(png_ptr, info_ptr) == 1) {
+ png_set_invert_mono(png_ptr);
+ png_read_update_info(png_ptr, info_ptr);
+ if (image.size() != QSize(width, height) || image.format() != QImage::Format_Mono) {
+ image = QImage(width, height, QImage::Format_Mono);
+ if (image.isNull())
+ return;
+ }
+ image.setColorCount(2);
+ image.setColor(1, qRgb(0,0,0));
+ image.setColor(0, qRgb(255,255,255));
+ } else if (bit_depth == 16 && png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
+ png_set_expand(png_ptr);
+ png_set_strip_16(png_ptr);
+ png_set_gray_to_rgb(png_ptr);
+ if (image.size() != QSize(width, height) || image.format() != QImage::Format_ARGB32) {
+ image = QImage(width, height, QImage::Format_ARGB32);
+ if (image.isNull())
+ return;
+ }
+ if (QSysInfo::ByteOrder == QSysInfo::BigEndian)
+ png_set_swap_alpha(png_ptr);
+
+ png_read_update_info(png_ptr, info_ptr);
+ } else {
+ if (bit_depth == 16)
+ png_set_strip_16(png_ptr);
+ else if (bit_depth < 8)
+ png_set_packing(png_ptr);
+ int ncols = bit_depth < 8 ? 1 << bit_depth : 256;
+ png_read_update_info(png_ptr, info_ptr);
+ if (image.size() != QSize(width, height) || image.format() != QImage::Format_Indexed8) {
+ image = QImage(width, height, QImage::Format_Indexed8);
+ if (image.isNull())
+ return;
+ }
+ image.setColorCount(ncols);
+ for (int i=0; i<ncols; i++) {
+ int c = i*255/(ncols-1);
+ image.setColor(i, qRgba(c,c,c,0xff));
+ }
+ if (png_get_tRNS(png_ptr, info_ptr, &trans_alpha, &num_trans, &trans_color_p) && trans_color_p) {
+ const int g = trans_color_p->gray;
+ if (g < ncols) {
+ image.setColor(g, 0);
+ }
+ }
+ }
+ } else if (color_type == PNG_COLOR_TYPE_PALETTE
+ && png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette)
+ && num_palette <= 256)
+ {
+ // 1-bit and 8-bit color
+ if (bit_depth != 1)
+ png_set_packing(png_ptr);
+ png_read_update_info(png_ptr, info_ptr);
+ png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, 0, 0, 0);
+ QImage::Format format = bit_depth == 1 ? QImage::Format_Mono : QImage::Format_Indexed8;
+ if (image.size() != QSize(width, height) || image.format() != format) {
+ image = QImage(width, height, format);
+ if (image.isNull())
+ return;
+ }
+ png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
+ image.setColorCount(num_palette);
+ int i = 0;
+ if (png_get_tRNS(png_ptr, info_ptr, &trans_alpha, &num_trans, &trans_color_p) && trans_alpha) {
+ while (i < num_trans) {
+ image.setColor(i, qRgba(
+ palette[i].red,
+ palette[i].green,
+ palette[i].blue,
+ trans_alpha[i]
+ )
+ );
+ i++;
+ }
+ }
+ while (i < num_palette) {
+ image.setColor(i, qRgba(
+ palette[i].red,
+ palette[i].green,
+ palette[i].blue,
+ 0xff
+ )
+ );
+ i++;
+ }
+ } else {
+ // 32-bit
+ if (bit_depth == 16)
+ png_set_strip_16(png_ptr);
+
+ png_set_expand(png_ptr);
+
+ if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+ png_set_gray_to_rgb(png_ptr);
+
+ QImage::Format format = QImage::Format_ARGB32;
+ // Only add filler if no alpha, or we can get 5 channel data.
+ if (!(color_type & PNG_COLOR_MASK_ALPHA)
+ && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
+ png_set_filler(png_ptr, 0xff, QSysInfo::ByteOrder == QSysInfo::BigEndian ?
+ PNG_FILLER_BEFORE : PNG_FILLER_AFTER);
+ // We want 4 bytes, but it isn't an alpha channel
+ format = QImage::Format_RGB32;
+ }
+ if (image.size() != QSize(width, height) || image.format() != format) {
+ image = QImage(width, height, format);
+ if (image.isNull())
+ return;
+ }
+
+ if (QSysInfo::ByteOrder == QSysInfo::BigEndian)
+ png_set_swap_alpha(png_ptr);
+
+ png_read_update_info(png_ptr, info_ptr);
+ }
+
+ // Qt==ARGB==Big(ARGB)==Little(BGRA)
+ if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) {
+ png_set_bgr(png_ptr);
+ }
+}
+
+
+#if defined(Q_C_CALLBACKS)
+extern "C" {
+#endif
+static void CALLBACK_CALL_TYPE qt_png_warning(png_structp /*png_ptr*/, png_const_charp message)
+{
+ qWarning("libpng warning: %s", message);
+}
+
+#if defined(Q_C_CALLBACKS)
+}
+#endif
+
+
+/*!
+ \internal
+*/
+void Q_INTERNAL_WIN_NO_THROW QPngHandlerPrivate::readPngTexts(png_info *info)
+{
+#ifndef QT_NO_IMAGE_TEXT
+ png_textp text_ptr;
+ int num_text=0;
+ png_get_text(png_ptr, info, &text_ptr, &num_text);
+
+ while (num_text--) {
+ QString key, value;
+ key = QString::fromLatin1(text_ptr->key);
+#if defined(PNG_iTXt_SUPPORTED)
+ if (text_ptr->itxt_length) {
+ value = QString::fromUtf8(text_ptr->text, int(text_ptr->itxt_length));
+ } else
+#endif
+ {
+ value = QString::fromLatin1(text_ptr->text, int(text_ptr->text_length));
+ }
+ if (!description.isEmpty())
+ description += QLatin1String("\n\n");
+ description += key + QLatin1String(": ") + value.simplified();
+ readTexts.append(key);
+ readTexts.append(value);
+ text_ptr++;
+ }
+#endif
+}
+
+
+/*!
+ \internal
+*/
+bool Q_INTERNAL_WIN_NO_THROW QPngHandlerPrivate::readPngHeader()
+{
+ state = Error;
+ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,0,0,0);
+ if (!png_ptr)
+ return false;
+
+ png_set_error_fn(png_ptr, 0, 0, qt_png_warning);
+
+ info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr) {
+ png_destroy_read_struct(&png_ptr, 0, 0);
+ png_ptr = 0;
+ return false;
+ }
+
+ end_info = png_create_info_struct(png_ptr);
+ if (!end_info) {
+ png_destroy_read_struct(&png_ptr, &info_ptr, 0);
+ png_ptr = 0;
+ return false;
+ }
+
+ if (setjmp(png_jmpbuf(png_ptr))) {
+ png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
+ png_ptr = 0;
+ return false;
+ }
+
+ png_set_read_fn(png_ptr, this, iod_read_fn);
+ png_read_info(png_ptr, info_ptr);
+
+ readPngTexts(info_ptr);
+
+ state = ReadHeader;
+ return true;
+}
+
+/*!
+ \internal
+*/
+bool Q_INTERNAL_WIN_NO_THROW QPngHandlerPrivate::readPngImage(QImage *outImage)
+{
+ if (state == Error)
+ return false;
+
+ if (state == Ready && !readPngHeader()) {
+ state = Error;
+ return false;
+ }
+
+ row_pointers = 0;
+ if (setjmp(png_jmpbuf(png_ptr))) {
+ png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
+ delete [] row_pointers;
+ png_ptr = 0;
+ state = Error;
+ return false;
+ }
+
+ setup_qt(*outImage, png_ptr, info_ptr, gamma);
+
+ if (outImage->isNull()) {
+ png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
+ delete [] row_pointers;
+ png_ptr = 0;
+ state = Error;
+ return false;
+ }
+
+ png_uint_32 width;
+ png_uint_32 height;
+ int bit_depth;
+ int color_type;
+ png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
+ 0, 0, 0);
+
+ uchar *data = outImage->bits();
+ int bpl = outImage->bytesPerLine();
+ row_pointers = new png_bytep[height];
+
+ for (uint y = 0; y < height; y++)
+ row_pointers[y] = data + y * bpl;
+
+ png_read_image(png_ptr, row_pointers);
+
+#if 0 // libpng takes care of this.
+ png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)
+ if (outImage->depth()==32 && png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
+ QRgb trans = 0xFF000000 | qRgb(
+ (info_ptr->trans_values.red << 8 >> bit_depth)&0xff,
+ (info_ptr->trans_values.green << 8 >> bit_depth)&0xff,
+ (info_ptr->trans_values.blue << 8 >> bit_depth)&0xff);
+ for (uint y=0; y<height; y++) {
+ for (uint x=0; x<info_ptr->width; x++) {
+ if (((uint**)jt)[y][x] == trans) {
+ ((uint**)jt)[y][x] &= 0x00FFFFFF;
+ } else {
+ }
+ }
+ }
+ }
+#endif
+
+ outImage->setDotsPerMeterX(png_get_x_pixels_per_meter(png_ptr,info_ptr));
+ outImage->setDotsPerMeterY(png_get_y_pixels_per_meter(png_ptr,info_ptr));
+
+ state = ReadingEnd;
+ png_read_end(png_ptr, end_info);
+
+#ifndef QT_NO_IMAGE_TEXT
+ readPngTexts(end_info);
+ for (int i = 0; i < readTexts.size()-1; i+=2)
+ outImage->setText(readTexts.at(i), readTexts.at(i+1));
+#endif
+
+ png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
+ delete [] row_pointers;
+ png_ptr = 0;
+ state = Ready;
+
+ // sanity check palette entries
+ if (color_type == PNG_COLOR_TYPE_PALETTE
+ && outImage->format() == QImage::Format_Indexed8) {
+ int color_table_size = outImage->colorCount();
+ for (int y=0; y<(int)height; ++y) {
+ uchar *p = FAST_SCAN_LINE(data, bpl, y);
+ uchar *end = p + width;
+ while (p < end) {
+ if (*p >= color_table_size)
+ *p = 0;
+ ++p;
+ }
+ }
+ }
+
+ return true;
+}
+
+QImage::Format QPngHandlerPrivate::readImageFormat()
+{
+ QImage::Format format = QImage::Format_Invalid;
+ png_uint_32 width, height;
+ int bit_depth, color_type;
+ png_colorp palette;
+ int num_palette;
+ png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, 0, 0, 0);
+ if (color_type == PNG_COLOR_TYPE_GRAY) {
+ // Black & White or 8-bit grayscale
+ if (bit_depth == 1 && png_get_channels(png_ptr, info_ptr) == 1) {
+ format = QImage::Format_Mono;
+ } else if (bit_depth == 16 && png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
+ format = QImage::Format_ARGB32;
+ } else {
+ format = QImage::Format_Indexed8;
+ }
+ } else if (color_type == PNG_COLOR_TYPE_PALETTE
+ && png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette)
+ && num_palette <= 256)
+ {
+ // 1-bit and 8-bit color
+ if (bit_depth != 1)
+ png_set_packing(png_ptr);
+ png_read_update_info(png_ptr, info_ptr);
+ png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, 0, 0, 0);
+ format = bit_depth == 1 ? QImage::Format_Mono : QImage::Format_Indexed8;
+ } else {
+ // 32-bit
+ if (bit_depth == 16)
+ png_set_strip_16(png_ptr);
+
+ format = QImage::Format_ARGB32;
+ // Only add filler if no alpha, or we can get 5 channel data.
+ if (!(color_type & PNG_COLOR_MASK_ALPHA)
+ && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
+ // We want 4 bytes, but it isn't an alpha channel
+ format = QImage::Format_RGB32;
+ }
+ }
+
+ return format;
+}
+
+QPNGImageWriter::QPNGImageWriter(QIODevice* iod) :
+ dev(iod),
+ frames_written(0),
+ disposal(Unspecified),
+ looping(-1),
+ ms_delay(-1),
+ gamma(0.0)
+{
+}
+
+QPNGImageWriter::~QPNGImageWriter()
+{
+}
+
+void QPNGImageWriter::setDisposalMethod(DisposalMethod dm)
+{
+ disposal = dm;
+}
+
+void QPNGImageWriter::setLooping(int loops)
+{
+ looping = loops;
+}
+
+void QPNGImageWriter::setFrameDelay(int msecs)
+{
+ ms_delay = msecs;
+}
+
+void QPNGImageWriter::setGamma(float g)
+{
+ gamma = g;
+}
+
+
+#ifndef QT_NO_IMAGE_TEXT
+static void set_text(const QImage &image, png_structp png_ptr, png_infop info_ptr,
+ const QString &description)
+{
+ QMap<QString, QString> text;
+ foreach (const QString &key, image.textKeys()) {
+ if (!key.isEmpty())
+ text.insert(key, image.text(key));
+ }
+ foreach (const QString &pair, description.split(QLatin1String("\n\n"))) {
+ int index = pair.indexOf(QLatin1Char(':'));
+ if (index >= 0 && pair.indexOf(QLatin1Char(' ')) < index) {
+ QString s = pair.simplified();
+ if (!s.isEmpty())
+ text.insert(QLatin1String("Description"), s);
+ } else {
+ QString key = pair.left(index);
+ if (!key.simplified().isEmpty())
+ text.insert(key, pair.mid(index + 2).simplified());
+ }
+ }
+
+ if (text.isEmpty())
+ return;
+
+ png_textp text_ptr = new png_text[text.size()];
+ qMemSet(text_ptr, 0, text.size() * sizeof(png_text));
+
+ QMap<QString, QString>::ConstIterator it = text.constBegin();
+ int i = 0;
+ while (it != text.constEnd()) {
+ text_ptr[i].key = qstrdup(it.key().left(79).toLatin1().constData());
+ bool noCompress = (it.value().length() < 40);
+
+#ifdef PNG_iTXt_SUPPORTED
+ bool needsItxt = false;
+ foreach(const QChar c, it.value()) {
+ uchar ch = c.cell();
+ if (c.row() || (ch < 0x20 && ch != '\n') || (ch > 0x7e && ch < 0xa0)) {
+ needsItxt = true;
+ break;
+ }
+ }
+
+ if (needsItxt) {
+ text_ptr[i].compression = noCompress ? PNG_ITXT_COMPRESSION_NONE : PNG_ITXT_COMPRESSION_zTXt;
+ QByteArray value = it.value().toUtf8();
+ text_ptr[i].text = qstrdup(value.constData());
+ text_ptr[i].itxt_length = value.size();
+ text_ptr[i].lang = const_cast<char*>("UTF-8");
+ text_ptr[i].lang_key = qstrdup(it.key().toUtf8().constData());
+ }
+ else
+#endif
+ {
+ text_ptr[i].compression = noCompress ? PNG_TEXT_COMPRESSION_NONE : PNG_TEXT_COMPRESSION_zTXt;
+ QByteArray value = it.value().toLatin1();
+ text_ptr[i].text = qstrdup(value.constData());
+ text_ptr[i].text_length = value.size();
+ }
+ ++i;
+ ++it;
+ }
+
+ png_set_text(png_ptr, info_ptr, text_ptr, i);
+ for (i = 0; i < text.size(); ++i) {
+ delete [] text_ptr[i].key;
+ delete [] text_ptr[i].text;
+#ifdef PNG_iTXt_SUPPORTED
+ delete [] text_ptr[i].lang_key;
+#endif
+ }
+ delete [] text_ptr;
+}
+#endif
+
+bool QPNGImageWriter::writeImage(const QImage& image, int off_x, int off_y)
+{
+ return writeImage(image, -1, QString(), off_x, off_y);
+}
+
+bool Q_INTERNAL_WIN_NO_THROW QPNGImageWriter::writeImage(const QImage& image, int quality_in, const QString &description,
+ int off_x_in, int off_y_in)
+{
+#ifdef QT_NO_IMAGE_TEXT
+ Q_UNUSED(description);
+#endif
+
+ QPoint offset = image.offset();
+ int off_x = off_x_in + offset.x();
+ int off_y = off_y_in + offset.y();
+
+ png_structp png_ptr;
+ png_infop info_ptr;
+
+ png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,0,0,0);
+ if (!png_ptr) {
+ return false;
+ }
+
+ png_set_error_fn(png_ptr, 0, 0, qt_png_warning);
+
+ info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr) {
+ png_destroy_write_struct(&png_ptr, 0);
+ return false;
+ }
+
+ if (setjmp(png_jmpbuf(png_ptr))) {
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+ return false;
+ }
+
+ int quality = quality_in;
+ if (quality >= 0) {
+ if (quality > 9) {
+ qWarning("PNG: Quality %d out of range", quality);
+ quality = 9;
+ }
+ png_set_compression_level(png_ptr, quality);
+ }
+
+ png_set_write_fn(png_ptr, (void*)this, qpiw_write_fn, qpiw_flush_fn);
+
+
+ int color_type = 0;
+ if (image.colorCount())
+ color_type = PNG_COLOR_TYPE_PALETTE;
+ else if (image.hasAlphaChannel())
+ color_type = PNG_COLOR_TYPE_RGB_ALPHA;
+ else
+ color_type = PNG_COLOR_TYPE_RGB;
+
+ png_set_IHDR(png_ptr, info_ptr, image.width(), image.height(),
+ image.depth() == 1 ? 1 : 8, // per channel
+ color_type, 0, 0, 0); // sets #channels
+
+ if (gamma != 0.0) {
+ png_set_gAMA(png_ptr, info_ptr, 1.0/gamma);
+ }
+
+ png_color_8 sig_bit;
+ sig_bit.red = 8;
+ sig_bit.green = 8;
+ sig_bit.blue = 8;
+ sig_bit.alpha = image.hasAlphaChannel() ? 8 : 0;
+ png_set_sBIT(png_ptr, info_ptr, &sig_bit);
+
+ if (image.format() == QImage::Format_MonoLSB)
+ png_set_packswap(png_ptr);
+
+ if (image.colorCount()) {
+ // Paletted
+ int num_palette = qMin(256, image.colorCount());
+ png_color palette[256];
+ png_byte trans[256];
+ int num_trans = 0;
+ for (int i=0; i<num_palette; i++) {
+ QRgb rgba=image.color(i);
+ palette[i].red = qRed(rgba);
+ palette[i].green = qGreen(rgba);
+ palette[i].blue = qBlue(rgba);
+ trans[i] = qAlpha(rgba);
+ if (trans[i] < 255) {
+ num_trans = i+1;
+ }
+ }
+ png_set_PLTE(png_ptr, info_ptr, palette, num_palette);
+
+ if (num_trans) {
+ png_set_tRNS(png_ptr, info_ptr, trans, num_trans, 0);
+ }
+ }
+
+ // Swap ARGB to RGBA (normal PNG format) before saving on
+ // BigEndian machines
+ if (QSysInfo::ByteOrder == QSysInfo::BigEndian) {
+ png_set_swap_alpha(png_ptr);
+ }
+
+ // Qt==ARGB==Big(ARGB)==Little(BGRA). But RGB888 is RGB regardless
+ if (QSysInfo::ByteOrder == QSysInfo::LittleEndian
+ && image.format() != QImage::Format_RGB888) {
+ png_set_bgr(png_ptr);
+ }
+
+ if (off_x || off_y) {
+ png_set_oFFs(png_ptr, info_ptr, off_x, off_y, PNG_OFFSET_PIXEL);
+ }
+
+ if (frames_written > 0)
+ png_set_sig_bytes(png_ptr, 8);
+
+ if (image.dotsPerMeterX() > 0 || image.dotsPerMeterY() > 0) {
+ png_set_pHYs(png_ptr, info_ptr,
+ image.dotsPerMeterX(), image.dotsPerMeterY(),
+ PNG_RESOLUTION_METER);
+ }
+
+#ifndef QT_NO_IMAGE_TEXT
+ set_text(image, png_ptr, info_ptr, description);
+#endif
+ png_write_info(png_ptr, info_ptr);
+
+ if (image.depth() != 1)
+ png_set_packing(png_ptr);
+
+ if (color_type == PNG_COLOR_TYPE_RGB && image.format() != QImage::Format_RGB888)
+ png_set_filler(png_ptr, 0,
+ QSysInfo::ByteOrder == QSysInfo::BigEndian ?
+ PNG_FILLER_BEFORE : PNG_FILLER_AFTER);
+
+ if (looping >= 0 && frames_written == 0) {
+ uchar data[13] = "NETSCAPE2.0";
+ // 0123456789aBC
+ data[0xB] = looping%0x100;
+ data[0xC] = looping/0x100;
+ png_write_chunk(png_ptr, (png_byte*)"gIFx", data, 13);
+ }
+ if (ms_delay >= 0 || disposal!=Unspecified) {
+ uchar data[4];
+ data[0] = disposal;
+ data[1] = 0;
+ data[2] = (ms_delay/10)/0x100; // hundredths
+ data[3] = (ms_delay/10)%0x100;
+ png_write_chunk(png_ptr, (png_byte*)"gIFg", data, 4);
+ }
+
+ int height = image.height();
+ int width = image.width();
+ switch (image.format()) {
+ case QImage::Format_Mono:
+ case QImage::Format_MonoLSB:
+ case QImage::Format_Indexed8:
+ case QImage::Format_RGB32:
+ case QImage::Format_ARGB32:
+ case QImage::Format_RGB888:
+ {
+ png_bytep* row_pointers = new png_bytep[height];
+ for (int y=0; y<height; y++)
+ row_pointers[y] = (png_bytep)image.constScanLine(y);
+ png_write_image(png_ptr, row_pointers);
+ delete [] row_pointers;
+ }
+ break;
+ default:
+ {
+ QImage::Format fmt = image.hasAlphaChannel() ? QImage::Format_ARGB32 : QImage::Format_RGB32;
+ QImage row;
+ png_bytep row_pointers[1];
+ for (int y=0; y<height; y++) {
+ row = image.copy(0, y, width, 1).convertToFormat(fmt);
+ row_pointers[0] = png_bytep(row.constScanLine(0));
+ png_write_rows(png_ptr, row_pointers, 1);
+ }
+ }
+ break;
+ }
+
+ png_write_end(png_ptr, info_ptr);
+ frames_written++;
+
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+
+ return true;
+}
+
+static bool write_png_image(const QImage &image, QIODevice *device,
+ int quality, float gamma, const QString &description)
+{
+ QPNGImageWriter writer(device);
+ if (quality >= 0) {
+ quality = qMin(quality, 100);
+ quality = (100-quality) * 9 / 91; // map [0,100] -> [9,0]
+ }
+ writer.setGamma(gamma);
+ return writer.writeImage(image, quality, description);
+}
+
+QPngHandler::QPngHandler()
+ : d(new QPngHandlerPrivate(this))
+{
+}
+
+QPngHandler::~QPngHandler()
+{
+ if (d->png_ptr)
+ png_destroy_read_struct(&d->png_ptr, &d->info_ptr, &d->end_info);
+ delete d;
+}
+
+bool QPngHandler::canRead() const
+{
+ if (d->state == QPngHandlerPrivate::Ready && !canRead(device()))
+ return false;
+
+ if (d->state != QPngHandlerPrivate::Error) {
+ setFormat("png");
+ return true;
+ }
+
+ return false;
+}
+
+bool QPngHandler::canRead(QIODevice *device)
+{
+ if (!device) {
+ qWarning("QPngHandler::canRead() called with no device");
+ return false;
+ }
+
+ return device->peek(8) == "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A";
+}
+
+bool QPngHandler::read(QImage *image)
+{
+ if (!canRead())
+ return false;
+ return d->readPngImage(image);
+}
+
+bool QPngHandler::write(const QImage &image)
+{
+ return write_png_image(image, device(), d->quality, d->gamma, d->description);
+}
+
+bool QPngHandler::supportsOption(ImageOption option) const
+{
+ return option == Gamma
+ || option == Description
+ || option == ImageFormat
+ || option == Quality
+ || option == Size;
+}
+
+QVariant QPngHandler::option(ImageOption option) const
+{
+ if (d->state == QPngHandlerPrivate::Error)
+ return QVariant();
+ if (d->state == QPngHandlerPrivate::Ready && !d->readPngHeader())
+ return QVariant();
+
+ if (option == Gamma)
+ return d->gamma;
+ else if (option == Quality)
+ return d->quality;
+ else if (option == Description)
+ return d->description;
+ else if (option == Size)
+ return QSize(png_get_image_width(d->png_ptr, d->info_ptr),
+ png_get_image_height(d->png_ptr, d->info_ptr));
+ else if (option == ImageFormat)
+ return d->readImageFormat();
+ return 0;
+}
+
+void QPngHandler::setOption(ImageOption option, const QVariant &value)
+{
+ if (option == Gamma)
+ d->gamma = value.toFloat();
+ else if (option == Quality)
+ d->quality = value.toInt();
+ else if (option == Description)
+ d->description = value.toString();
+}
+
+QByteArray QPngHandler::name() const
+{
+ return "png";
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_IMAGEFORMAT_PNG
diff --git a/src/gui/image/qpnghandler.pri b/src/gui/image/qpnghandler.pri
new file mode 100644
index 0000000000..bedf23ff12
--- /dev/null
+++ b/src/gui/image/qpnghandler.pri
@@ -0,0 +1,10 @@
+INCLUDEPATH *= $$PWD
+HEADERS += $$PWD/qpnghandler_p.h
+SOURCES += $$PWD/qpnghandler.cpp
+contains(QT_CONFIG, system-png) {
+ if(unix|win32-g++*): LIBS_PRIVATE += -lpng
+ else:win32: LIBS += libpng.lib
+
+} else {
+ include($$PWD/../../3rdparty/libpng.pri)
+}
diff --git a/src/gui/image/qpnghandler_p.h b/src/gui/image/qpnghandler_p.h
new file mode 100644
index 0000000000..3eaf9e0b30
--- /dev/null
+++ b/src/gui/image/qpnghandler_p.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPNGHANDLER_P_H
+#define QPNGHANDLER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the QLibrary class. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtGui/qimageiohandler.h"
+
+#ifndef QT_NO_IMAGEFORMAT_PNG
+
+QT_BEGIN_NAMESPACE
+
+class QPngHandlerPrivate;
+class QPngHandler : public QImageIOHandler
+{
+public:
+ QPngHandler();
+ ~QPngHandler();
+
+ bool canRead() const;
+ bool read(QImage *image);
+ bool write(const QImage &image);
+
+ QByteArray name() const;
+
+ QVariant option(ImageOption option) const;
+ void setOption(ImageOption option, const QVariant &value);
+ bool supportsOption(ImageOption option) const;
+
+ static bool canRead(QIODevice *device);
+
+private:
+ QPngHandlerPrivate *d;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_IMAGEFORMAT_PNG
+#endif // QPNGHANDLER_P_H
diff --git a/src/gui/image/qppmhandler.cpp b/src/gui/image/qppmhandler.cpp
new file mode 100644
index 0000000000..72b38222a0
--- /dev/null
+++ b/src/gui/image/qppmhandler.cpp
@@ -0,0 +1,533 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "private/qppmhandler_p.h"
+
+#ifndef QT_NO_IMAGEFORMAT_PPM
+
+#include <qimage.h>
+#include <qvariant.h>
+#include <qvector.h>
+#include <ctype.h>
+
+QT_BEGIN_NAMESPACE
+
+/*****************************************************************************
+ PBM/PGM/PPM (ASCII and RAW) image read/write functions
+ *****************************************************************************/
+
+static int read_pbm_int(QIODevice *d)
+{
+ char c;
+ int val = -1;
+ bool digit;
+ const int buflen = 100;
+ char buf[buflen];
+ for (;;) {
+ if (!d->getChar(&c)) // end of file
+ break;
+ digit = isdigit((uchar) c);
+ if (val != -1) {
+ if (digit) {
+ val = 10*val + c - '0';
+ continue;
+ } else {
+ if (c == '#') // comment
+ d->readLine(buf, buflen);
+ break;
+ }
+ }
+ if (digit) // first digit
+ val = c - '0';
+ else if (isspace((uchar) c))
+ continue;
+ else if (c == '#')
+ (void)d->readLine(buf, buflen);
+ else
+ break;
+ }
+ return val;
+}
+
+static bool read_pbm_header(QIODevice *device, char& type, int& w, int& h, int& mcc)
+{
+ char buf[3];
+ if (device->read(buf, 3) != 3) // read P[1-6]<white-space>
+ return false;
+
+ if (!(buf[0] == 'P' && isdigit((uchar) buf[1]) && isspace((uchar) buf[2])))
+ return false;
+
+ type = buf[1];
+ if (type < '1' || type > '6')
+ return false;
+
+ w = read_pbm_int(device); // get image width
+ h = read_pbm_int(device); // get image height
+
+ if (type == '1' || type == '4')
+ mcc = 1; // ignore max color component
+ else
+ mcc = read_pbm_int(device); // get max color component
+
+ if (w <= 0 || w > 32767 || h <= 0 || h > 32767 || mcc <= 0)
+ return false; // weird P.M image
+
+ return true;
+}
+
+static bool read_pbm_body(QIODevice *device, char type, int w, int h, int mcc, QImage *outImage)
+{
+ int nbits, y;
+ int pbm_bpl;
+ bool raw;
+
+ QImage::Format format;
+ switch (type) {
+ case '1': // ascii PBM
+ case '4': // raw PBM
+ nbits = 1;
+ format = QImage::Format_Mono;
+ break;
+ case '2': // ascii PGM
+ case '5': // raw PGM
+ nbits = 8;
+ format = QImage::Format_Indexed8;
+ break;
+ case '3': // ascii PPM
+ case '6': // raw PPM
+ nbits = 32;
+ format = QImage::Format_RGB32;
+ break;
+ default:
+ return false;
+ }
+ raw = type >= '4';
+
+ int maxc = mcc;
+ if (maxc > 255)
+ maxc = 255;
+ if (outImage->size() != QSize(w, h) || outImage->format() != format) {
+ *outImage = QImage(w, h, format);
+ if (outImage->isNull())
+ return false;
+ }
+
+ pbm_bpl = (nbits*w+7)/8; // bytes per scanline in PBM
+
+ if (raw) { // read raw data
+ if (nbits == 32) { // type 6
+ pbm_bpl = mcc < 256 ? 3*w : 6*w;
+ uchar *buf24 = new uchar[pbm_bpl], *b;
+ QRgb *p;
+ QRgb *end;
+ for (y=0; y<h; y++) {
+ if (device->read((char *)buf24, pbm_bpl) != pbm_bpl) {
+ delete[] buf24;
+ return false;
+ }
+ p = (QRgb *)outImage->scanLine(y);
+ end = p + w;
+ b = buf24;
+ while (p < end) {
+ if (mcc < 256) {
+ *p++ = qRgb(b[0],b[1],b[2]);
+ b += 3;
+ } else {
+ *p++ = qRgb(((int(b[0]) * 256 + int(b[1]) + 1) * 256) / (mcc + 1) - 1,
+ ((int(b[2]) * 256 + int(b[3]) + 1) * 256) / (mcc + 1) - 1,
+ ((int(b[4]) * 256 + int(b[5]) + 1) * 256) / (mcc + 1) - 1);
+ b += 6;
+ }
+ }
+ }
+ delete[] buf24;
+ } else { // type 4,5
+ for (y=0; y<h; y++) {
+ if (device->read((char *)outImage->scanLine(y), pbm_bpl)
+ != pbm_bpl)
+ return false;
+ }
+ }
+ } else { // read ascii data
+ register uchar *p;
+ int n;
+ for (y=0; y<h; y++) {
+ p = outImage->scanLine(y);
+ n = pbm_bpl;
+ if (nbits == 1) {
+ int b;
+ int bitsLeft = w;
+ while (n--) {
+ b = 0;
+ for (int i=0; i<8; i++) {
+ if (i < bitsLeft)
+ b = (b << 1) | (read_pbm_int(device) & 1);
+ else
+ b = (b << 1) | (0 & 1); // pad it our self if we need to
+ }
+ bitsLeft -= 8;
+ *p++ = b;
+ }
+ } else if (nbits == 8) {
+ if (mcc == maxc) {
+ while (n--) {
+ *p++ = read_pbm_int(device);
+ }
+ } else {
+ while (n--) {
+ *p++ = read_pbm_int(device) * maxc / mcc;
+ }
+ }
+ } else { // 32 bits
+ n /= 4;
+ int r, g, b;
+ if (mcc == maxc) {
+ while (n--) {
+ r = read_pbm_int(device);
+ g = read_pbm_int(device);
+ b = read_pbm_int(device);
+ *((QRgb*)p) = qRgb(r, g, b);
+ p += 4;
+ }
+ } else {
+ while (n--) {
+ r = read_pbm_int(device) * maxc / mcc;
+ g = read_pbm_int(device) * maxc / mcc;
+ b = read_pbm_int(device) * maxc / mcc;
+ *((QRgb*)p) = qRgb(r, g, b);
+ p += 4;
+ }
+ }
+ }
+ }
+ }
+
+ if (nbits == 1) { // bitmap
+ outImage->setColorCount(2);
+ outImage->setColor(0, qRgb(255,255,255)); // white
+ outImage->setColor(1, qRgb(0,0,0)); // black
+ } else if (nbits == 8) { // graymap
+ outImage->setColorCount(maxc+1);
+ for (int i=0; i<=maxc; i++)
+ outImage->setColor(i, qRgb(i*255/maxc,i*255/maxc,i*255/maxc));
+ }
+
+ return true;
+}
+
+static bool write_pbm_image(QIODevice *out, const QImage &sourceImage, const QByteArray &sourceFormat)
+{
+ QByteArray str;
+ QImage image = sourceImage;
+ QByteArray format = sourceFormat;
+
+ format = format.left(3); // ignore RAW part
+ bool gray = format == "pgm";
+
+ if (format == "pbm") {
+ image = image.convertToFormat(QImage::Format_Mono);
+ } else if (image.depth() == 1) {
+ image = image.convertToFormat(QImage::Format_Indexed8);
+ } else {
+ switch (image.format()) {
+ case QImage::Format_RGB16:
+ case QImage::Format_RGB666:
+ case QImage::Format_RGB555:
+ case QImage::Format_RGB888:
+ case QImage::Format_RGB444:
+ image = image.convertToFormat(QImage::Format_RGB32);
+ break;
+ case QImage::Format_ARGB8565_Premultiplied:
+ case QImage::Format_ARGB6666_Premultiplied:
+ case QImage::Format_ARGB8555_Premultiplied:
+ case QImage::Format_ARGB4444_Premultiplied:
+ image = image.convertToFormat(QImage::Format_ARGB32);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (image.depth() == 1 && image.colorCount() == 2) {
+ if (qGray(image.color(0)) < qGray(image.color(1))) {
+ // 0=dark/black, 1=light/white - invert
+ image.detach();
+ for (int y=0; y<image.height(); y++) {
+ uchar *p = image.scanLine(y);
+ uchar *end = p + image.bytesPerLine();
+ while (p < end)
+ *p++ ^= 0xff;
+ }
+ }
+ }
+
+ uint w = image.width();
+ uint h = image.height();
+
+ str = "P\n";
+ str += QByteArray::number(w);
+ str += ' ';
+ str += QByteArray::number(h);
+ str += '\n';
+
+ switch (image.depth()) {
+ case 1: {
+ str.insert(1, '4');
+ if (out->write(str, str.length()) != str.length())
+ return false;
+ w = (w+7)/8;
+ for (uint y=0; y<h; y++) {
+ uchar* line = image.scanLine(y);
+ if (w != (uint)out->write((char*)line, w))
+ return false;
+ }
+ }
+ break;
+
+ case 8: {
+ str.insert(1, gray ? '5' : '6');
+ str.append("255\n");
+ if (out->write(str, str.length()) != str.length())
+ return false;
+ QVector<QRgb> color = image.colorTable();
+ uint bpl = w*(gray ? 1 : 3);
+ uchar *buf = new uchar[bpl];
+ for (uint y=0; y<h; y++) {
+ uchar *b = image.scanLine(y);
+ uchar *p = buf;
+ uchar *end = buf+bpl;
+ if (gray) {
+ while (p < end) {
+ uchar g = (uchar)qGray(color[*b++]);
+ *p++ = g;
+ }
+ } else {
+ while (p < end) {
+ QRgb rgb = color[*b++];
+ *p++ = qRed(rgb);
+ *p++ = qGreen(rgb);
+ *p++ = qBlue(rgb);
+ }
+ }
+ if (bpl != (uint)out->write((char*)buf, bpl))
+ return false;
+ }
+ delete [] buf;
+ }
+ break;
+
+ case 32: {
+ str.insert(1, gray ? '5' : '6');
+ str.append("255\n");
+ if (out->write(str, str.length()) != str.length())
+ return false;
+ uint bpl = w*(gray ? 1 : 3);
+ uchar *buf = new uchar[bpl];
+ for (uint y=0; y<h; y++) {
+ QRgb *b = (QRgb*)image.scanLine(y);
+ uchar *p = buf;
+ uchar *end = buf+bpl;
+ if (gray) {
+ while (p < end) {
+ uchar g = (uchar)qGray(*b++);
+ *p++ = g;
+ }
+ } else {
+ while (p < end) {
+ QRgb rgb = *b++;
+ *p++ = qRed(rgb);
+ *p++ = qGreen(rgb);
+ *p++ = qBlue(rgb);
+ }
+ }
+ if (bpl != (uint)out->write((char*)buf, bpl))
+ return false;
+ }
+ delete [] buf;
+ }
+ break;
+
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+QPpmHandler::QPpmHandler()
+ : state(Ready)
+{
+}
+
+bool QPpmHandler::readHeader()
+{
+ state = Error;
+ if (!read_pbm_header(device(), type, width, height, mcc))
+ return false;
+ state = ReadHeader;
+ return true;
+}
+
+bool QPpmHandler::canRead() const
+{
+ if (state == Ready && !canRead(device(), &subType))
+ return false;
+
+ if (state != Error) {
+ setFormat(subType);
+ return true;
+ }
+
+ return false;
+}
+
+bool QPpmHandler::canRead(QIODevice *device, QByteArray *subType)
+{
+ if (!device) {
+ qWarning("QPpmHandler::canRead() called with no device");
+ return false;
+ }
+
+ char head[2];
+ if (device->peek(head, sizeof(head)) != sizeof(head))
+ return false;
+
+ if (head[0] != 'P')
+ return false;
+
+ if (head[1] == '1' || head[1] == '4') {
+ if (subType)
+ *subType = "pbm";
+ } else if (head[1] == '2' || head[1] == '5') {
+ if (subType)
+ *subType = "pgm";
+ } else if (head[1] == '3' || head[1] == '6') {
+ if (subType)
+ *subType = "ppm";
+ } else {
+ return false;
+ }
+ return true;
+}
+
+bool QPpmHandler::read(QImage *image)
+{
+ if (state == Error)
+ return false;
+
+ if (state == Ready && !readHeader()) {
+ state = Error;
+ return false;
+ }
+
+ if (!read_pbm_body(device(), type, width, height, mcc, image)) {
+ state = Error;
+ return false;
+ }
+
+ state = Ready;
+ return true;
+}
+
+bool QPpmHandler::write(const QImage &image)
+{
+ return write_pbm_image(device(), image, subType);
+}
+
+bool QPpmHandler::supportsOption(ImageOption option) const
+{
+ return option == SubType
+ || option == Size
+ || option == ImageFormat;
+}
+
+QVariant QPpmHandler::option(ImageOption option) const
+{
+ if (option == SubType) {
+ return subType;
+ } else if (option == Size) {
+ if (state == Error)
+ return QVariant();
+ if (state == Ready && !const_cast<QPpmHandler*>(this)->readHeader())
+ return QVariant();
+ return QSize(width, height);
+ } else if (option == ImageFormat) {
+ if (state == Error)
+ return QVariant();
+ if (state == Ready && !const_cast<QPpmHandler*>(this)->readHeader())
+ return QVariant();
+ QImage::Format format = QImage::Format_Invalid;
+ switch (type) {
+ case '1': // ascii PBM
+ case '4': // raw PBM
+ format = QImage::Format_Mono;
+ break;
+ case '2': // ascii PGM
+ case '5': // raw PGM
+ format = QImage::Format_Indexed8;
+ break;
+ case '3': // ascii PPM
+ case '6': // raw PPM
+ format = QImage::Format_RGB32;
+ break;
+ default:
+ break;
+ }
+ return format;
+ }
+ return QVariant();
+}
+
+void QPpmHandler::setOption(ImageOption option, const QVariant &value)
+{
+ if (option == SubType)
+ subType = value.toByteArray().toLower();
+}
+
+QByteArray QPpmHandler::name() const
+{
+ return subType.isEmpty() ? QByteArray("ppm") : subType;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_IMAGEFORMAT_PPM
diff --git a/src/gui/image/qppmhandler_p.h b/src/gui/image/qppmhandler_p.h
new file mode 100644
index 0000000000..03aa7520b5
--- /dev/null
+++ b/src/gui/image/qppmhandler_p.h
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPPMHANDLER_P_H
+#define QPPMHANDLER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the QLibrary class. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtGui/qimageiohandler.h"
+
+#ifndef QT_NO_IMAGEFORMAT_PPM
+
+QT_BEGIN_NAMESPACE
+
+class QByteArray;
+class QPpmHandler : public QImageIOHandler
+{
+public:
+ QPpmHandler();
+ bool canRead() const;
+ bool read(QImage *image);
+ bool write(const QImage &image);
+
+ QByteArray name() const;
+
+ static bool canRead(QIODevice *device, QByteArray *subType = 0);
+
+ QVariant option(ImageOption option) const;
+ void setOption(ImageOption option, const QVariant &value);
+ bool supportsOption(ImageOption option) const;
+
+private:
+ bool readHeader();
+ enum State {
+ Ready,
+ ReadHeader,
+ Error
+ };
+ State state;
+ char type;
+ int width;
+ int height;
+ int mcc;
+ mutable QByteArray subType;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_IMAGEFORMAT_PPM
+
+#endif // QPPMHANDLER_P_H
diff --git a/src/gui/image/qtiffhandler.cpp b/src/gui/image/qtiffhandler.cpp
new file mode 100644
index 0000000000..5939ad5830
--- /dev/null
+++ b/src/gui/image/qtiffhandler.cpp
@@ -0,0 +1,665 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtiffhandler_p.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*/)
+{
+}
+
+// for 32 bits images
+inline void rotate_right_mirror_horizontal(QImage *const image)// rotate right->mirrored horizontal
+{
+ const int height = image->height();
+ const int width = image->width();
+ QImage generated(/* width = */ height, /* height = */ width, image->format());
+ const uint32 *originalPixel = reinterpret_cast<const uint32*>(image->bits());
+ uint32 *const generatedPixels = reinterpret_cast<uint32*>(generated.bits());
+ for (int row=0; row < height; ++row) {
+ for (int col=0; col < width; ++col) {
+ int idx = col * height + row;
+ generatedPixels[idx] = *originalPixel;
+ ++originalPixel;
+ }
+ }
+ *image = generated;
+}
+
+inline void rotate_right_mirror_vertical(QImage *const image) // rotate right->mirrored vertical
+{
+ const int height = image->height();
+ const int width = image->width();
+ QImage generated(/* width = */ height, /* height = */ width, image->format());
+ const int lastCol = width - 1;
+ const int lastRow = height - 1;
+ const uint32 *pixel = reinterpret_cast<const uint32*>(image->bits());
+ uint32 *const generatedBits = reinterpret_cast<uint32*>(generated.bits());
+ for (int row=0; row < height; ++row) {
+ for (int col=0; col < width; ++col) {
+ int idx = (lastCol - col) * height + (lastRow - row);
+ generatedBits[idx] = *pixel;
+ ++pixel;
+ }
+ }
+ *image = generated;
+}
+
+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 *const tiff = TIFFClientOpen("foo",
+ "r",
+ this,
+ qtiffReadProc,
+ qtiffWriteProc,
+ qtiffSeekProc,
+ qtiffCloseProc,
+ qtiffSizeProc,
+ qtiffMapProc,
+ qtiffUnmapProc);
+
+ if (!tiff) {
+ return false;
+ }
+ uint32 width;
+ uint32 height;
+ uint16 photometric;
+ if (!TIFFGetField(tiff, TIFFTAG_IMAGEWIDTH, &width)
+ || !TIFFGetField(tiff, TIFFTAG_IMAGELENGTH, &height)
+ || !TIFFGetField(tiff, TIFFTAG_PHOTOMETRIC, &photometric)) {
+ TIFFClose(tiff);
+ return false;
+ }
+
+ // BitsPerSample defaults to 1 according to the TIFF spec.
+ uint16 bitPerSample;
+ if (!TIFFGetField(tiff, TIFFTAG_BITSPERSAMPLE, &bitPerSample))
+ bitPerSample = 1;
+ uint16 samplesPerPixel; // they may be e.g. grayscale with 2 samples per pixel
+ if (!TIFFGetField(tiff, TIFFTAG_SAMPLESPERPIXEL, &samplesPerPixel))
+ samplesPerPixel = 1;
+
+ bool grayscale = photometric == PHOTOMETRIC_MINISBLACK || photometric == PHOTOMETRIC_MINISWHITE;
+ if (grayscale && bitPerSample == 1 && samplesPerPixel == 1) {
+ if (image->size() != QSize(width, height) || image->format() != QImage::Format_Mono)
+ *image = QImage(width, height, QImage::Format_Mono);
+ QVector<QRgb> colortable(2);
+ if (photometric == PHOTOMETRIC_MINISBLACK) {
+ colortable[0] = 0xff000000;
+ colortable[1] = 0xffffffff;
+ } else {
+ colortable[0] = 0xffffffff;
+ colortable[1] = 0xff000000;
+ }
+ image->setColorTable(colortable);
+
+ if (!image->isNull()) {
+ for (uint32 y=0; y<height; ++y) {
+ if (TIFFReadScanline(tiff, image->scanLine(y), y, 0) < 0) {
+ TIFFClose(tiff);
+ return false;
+ }
+ }
+ }
+ } else {
+ if ((grayscale || photometric == PHOTOMETRIC_PALETTE) && bitPerSample == 8 && samplesPerPixel == 1) {
+ if (image->size() != QSize(width, height) || image->format() != QImage::Format_Indexed8)
+ *image = QImage(width, height, QImage::Format_Indexed8);
+ if (!image->isNull()) {
+ const uint16 tableSize = 256;
+ QVector<QRgb> qtColorTable(tableSize);
+ if (grayscale) {
+ for (int i = 0; i<tableSize; ++i) {
+ const int c = (photometric == PHOTOMETRIC_MINISBLACK) ? i : (255 - i);
+ qtColorTable[i] = qRgb(c, c, c);
+ }
+ } else {
+ // create the color table
+ uint16 *redTable = static_cast<uint16 *>(qMalloc(tableSize * sizeof(uint16)));
+ uint16 *greenTable = static_cast<uint16 *>(qMalloc(tableSize * sizeof(uint16)));
+ uint16 *blueTable = static_cast<uint16 *>(qMalloc(tableSize * sizeof(uint16)));
+ if (!redTable || !greenTable || !blueTable) {
+ TIFFClose(tiff);
+ return false;
+ }
+ if (!TIFFGetField(tiff, TIFFTAG_COLORMAP, &redTable, &greenTable, &blueTable)) {
+ TIFFClose(tiff);
+ return false;
+ }
+
+ for (int i = 0; i<tableSize ;++i) {
+ const int red = redTable[i] / 257;
+ const int green = greenTable[i] / 257;
+ const int blue = blueTable[i] / 257;
+ qtColorTable[i] = qRgb(red, green, blue);
+ }
+ }
+
+ image->setColorTable(qtColorTable);
+ for (uint32 y=0; y<height; ++y) {
+ if (TIFFReadScanline(tiff, image->scanLine(y), y, 0) < 0) {
+ TIFFClose(tiff);
+ return false;
+ }
+ }
+
+ // free redTable, greenTable and greenTable done by libtiff
+ }
+ } else {
+ if (image->size() != QSize(width, height) || image->format() != QImage::Format_ARGB32)
+ *image = QImage(width, height, QImage::Format_ARGB32);
+ if (!image->isNull()) {
+ const int stopOnError = 1;
+ if (TIFFReadRGBAImageOriented(tiff, width, height, reinterpret_cast<uint32 *>(image->bits()), ORIENTATION_TOPLEFT, stopOnError)) {
+ for (uint32 y=0; y<height; ++y)
+ convert32BitOrder(image->scanLine(y), width);
+ } else {
+ TIFFClose(tiff);
+ return false;
+ }
+ }
+ }
+ }
+
+ if (image->isNull()) {
+ TIFFClose(tiff);
+ return false;
+ }
+
+ float resX = 0;
+ float resY = 0;
+ uint16 resUnit = RESUNIT_NONE;
+ if (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;
+ }
+ }
+
+ // rotate the image if the orientation is defined in the file
+ uint16 orientationTag;
+ if (TIFFGetField(tiff, TIFFTAG_ORIENTATION, &orientationTag)) {
+ if (image->format() == QImage::Format_ARGB32) {
+ // TIFFReadRGBAImageOriented() flip the image but does not rotate them
+ switch (orientationTag) {
+ case 5:
+ rotate_right_mirror_horizontal(image);
+ break;
+ case 6:
+ rotate_right_mirror_vertical(image);
+ break;
+ case 7:
+ rotate_right_mirror_horizontal(image);
+ break;
+ case 8:
+ rotate_right_mirror_vertical(image);
+ break;
+ }
+ } else {
+ switch (orientationTag) {
+ case 1: // default orientation
+ break;
+ case 2: // mirror horizontal
+ *image = image->mirrored(true, false);
+ break;
+ case 3: // mirror both
+ *image = image->mirrored(true, true);
+ break;
+ case 4: // mirror vertical
+ *image = image->mirrored(false, true);
+ break;
+ case 5: // rotate right mirror horizontal
+ {
+ QMatrix transformation;
+ transformation.rotate(90);
+ *image = image->transformed(transformation);
+ *image = image->mirrored(true, false);
+ break;
+ }
+ case 6: // rotate right
+ {
+ QMatrix transformation;
+ transformation.rotate(90);
+ *image = image->transformed(transformation);
+ break;
+ }
+ case 7: // rotate right, mirror vertical
+ {
+ QMatrix transformation;
+ transformation.rotate(90);
+ *image = image->transformed(transformation);
+ *image = image->mirrored(false, true);
+ break;
+ }
+ case 8: // rotate left
+ {
+ QMatrix transformation;
+ transformation.rotate(270);
+ *image = image->transformed(transformation);
+ break;
+ }
+ }
+ }
+ }
+
+
+ TIFFClose(tiff);
+ return true;
+}
+
+static bool checkGrayscale(const QVector<QRgb> &colorTable)
+{
+ if (colorTable.size() != 256)
+ return false;
+
+ const bool increasing = (colorTable.at(0) == 0xff000000);
+ for (int i = 0; i < 256; ++i) {
+ if ((increasing && colorTable.at(i) != qRgb(i, i, i))
+ || (!increasing && colorTable.at(i) != qRgb(255 - i, 255 - i, 255 - i)))
+ return false;
+ }
+ return true;
+}
+
+bool QTiffHandler::write(const QImage &image)
+{
+ if (!device()->isWritable())
+ return false;
+
+ TIFF *const tiff = TIFFClientOpen("foo",
+ "w",
+ this,
+ qtiffReadProc,
+ qtiffWriteProc,
+ qtiffSeekProc,
+ qtiffCloseProc,
+ qtiffSizeProc,
+ qtiffMapProc,
+ qtiffUnmapProc);
+ if (!tiff)
+ return false;
+
+ const int width = image.width();
+ const int height = image.height();
+
+ if (!TIFFSetField(tiff, TIFFTAG_IMAGEWIDTH, width)
+ || !TIFFSetField(tiff, TIFFTAG_IMAGELENGTH, height)
+ || !TIFFSetField(tiff, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG)) {
+ TIFFClose(tiff);
+ return false;
+ }
+
+ // set the resolution
+ bool resolutionSet = false;
+ const int dotPerMeterX = image.dotsPerMeterX();
+ const int dotPerMeterY = image.dotsPerMeterY();
+ if ((dotPerMeterX % 100) == 0
+ && (dotPerMeterY % 100) == 0) {
+ resolutionSet = TIFFSetField(tiff, TIFFTAG_RESOLUTIONUNIT, RESUNIT_CENTIMETER)
+ && TIFFSetField(tiff, TIFFTAG_XRESOLUTION, dotPerMeterX/100.0)
+ && TIFFSetField(tiff, TIFFTAG_YRESOLUTION, dotPerMeterY/100.0);
+ } else {
+ resolutionSet = TIFFSetField(tiff, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH)
+ && TIFFSetField(tiff, TIFFTAG_XRESOLUTION, static_cast<float>(image.logicalDpiX()))
+ && TIFFSetField(tiff, TIFFTAG_YRESOLUTION, static_cast<float>(image.logicalDpiY()));
+ }
+ if (!resolutionSet) {
+ TIFFClose(tiff);
+ return false;
+ }
+
+ // configure image depth
+ const QImage::Format format = image.format();
+ if (format == QImage::Format_Mono || format == QImage::Format_MonoLSB) {
+ uint16 photometric = PHOTOMETRIC_MINISBLACK;
+ if (image.colorTable().at(0) == 0xffffffff)
+ photometric = PHOTOMETRIC_MINISWHITE;
+ if (!TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, photometric)
+ || !TIFFSetField(tiff, TIFFTAG_COMPRESSION, compression == NoCompression ? COMPRESSION_NONE : COMPRESSION_CCITTRLE)
+ || !TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, 1)) {
+ TIFFClose(tiff);
+ return false;
+ }
+
+ // try to do the conversion in chunks no greater than 16 MB
+ int chunks = (width * height / (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_Mono);
+
+ int chunkStart = y;
+ int chunkEnd = y + chunk.height();
+ while (y < chunkEnd) {
+ if (TIFFWriteScanline(tiff, reinterpret_cast<uint32 *>(chunk.scanLine(y - chunkStart)), y) != 1) {
+ TIFFClose(tiff);
+ return false;
+ }
+ ++y;
+ }
+ }
+ TIFFClose(tiff);
+ } else if (format == QImage::Format_Indexed8) {
+ const QVector<QRgb> colorTable = image.colorTable();
+ bool isGrayscale = checkGrayscale(colorTable);
+ if (isGrayscale) {
+ uint16 photometric = PHOTOMETRIC_MINISBLACK;
+ if (image.colorTable().at(0) == 0xffffffff)
+ photometric = PHOTOMETRIC_MINISWHITE;
+ if (!TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, photometric)
+ || !TIFFSetField(tiff, TIFFTAG_COMPRESSION, compression == NoCompression ? COMPRESSION_NONE : COMPRESSION_PACKBITS)
+ || !TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, 8)) {
+ TIFFClose(tiff);
+ return false;
+ }
+ } else {
+ if (!TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_PALETTE)
+ || !TIFFSetField(tiff, TIFFTAG_COMPRESSION, compression == NoCompression ? COMPRESSION_NONE : COMPRESSION_PACKBITS)
+ || !TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, 8)) {
+ TIFFClose(tiff);
+ return false;
+ }
+ //// write the color table
+ // allocate the color tables
+ uint16 *redTable = static_cast<uint16 *>(qMalloc(256 * sizeof(uint16)));
+ uint16 *greenTable = static_cast<uint16 *>(qMalloc(256 * sizeof(uint16)));
+ uint16 *blueTable = static_cast<uint16 *>(qMalloc(256 * sizeof(uint16)));
+ if (!redTable || !greenTable || !blueTable) {
+ TIFFClose(tiff);
+ return false;
+ }
+
+ // set the color table
+ const int tableSize = colorTable.size();
+ Q_ASSERT(tableSize <= 256);
+ for (int i = 0; i<tableSize; ++i) {
+ const QRgb color = colorTable.at(i);
+ redTable[i] = qRed(color) * 257;
+ greenTable[i] = qGreen(color) * 257;
+ blueTable[i] = qBlue(color) * 257;
+ }
+
+ const bool setColorTableSuccess = TIFFSetField(tiff, TIFFTAG_COLORMAP, redTable, greenTable, blueTable);
+
+ qFree(redTable);
+ qFree(greenTable);
+ qFree(blueTable);
+
+ if (!setColorTableSuccess) {
+ TIFFClose(tiff);
+ return false;
+ }
+ }
+
+ //// write the data
+ // try to do the conversion in chunks no greater than 16 MB
+ int chunks = (width * height/ (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));
+
+ int chunkStart = y;
+ int chunkEnd = y + chunk.height();
+ while (y < chunkEnd) {
+ if (TIFFWriteScanline(tiff, reinterpret_cast<uint32 *>(chunk.scanLine(y - chunkStart)), y) != 1) {
+ TIFFClose(tiff);
+ return false;
+ }
+ ++y;
+ }
+ }
+ TIFFClose(tiff);
+
+ } else {
+ if (!TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB)
+ || !TIFFSetField(tiff, TIFFTAG_COMPRESSION, compression == NoCompression ? COMPRESSION_NONE : COMPRESSION_LZW)
+ || !TIFFSetField(tiff, TIFFTAG_SAMPLESPERPIXEL, 4)
+ || !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);
+ }
+
+ 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);
+ TIFFClose(tiff);
+ }
+ 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/gui/image/qtiffhandler.pri b/src/gui/image/qtiffhandler.pri
new file mode 100644
index 0000000000..e1cc3ee2b7
--- /dev/null
+++ b/src/gui/image/qtiffhandler.pri
@@ -0,0 +1,10 @@
+# common to plugin and built-in forms
+INCLUDEPATH *= $$PWD
+HEADERS += $$PWD/qtiffhandler_p.h
+SOURCES += $$PWD/qtiffhandler.cpp
+contains(QT_CONFIG, system-tiff) {
+ if(unix|win32-g++*):LIBS += -ltiff
+ else:win32: LIBS += libtiff.lib
+} else {
+ include($$PWD/../../3rdparty/libtiff.pri)
+}
diff --git a/src/gui/image/qtiffhandler_p.h b/src/gui/image/qtiffhandler_p.h
new file mode 100644
index 0000000000..700ac37884
--- /dev/null
+++ b/src/gui/image/qtiffhandler_p.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTIFFHANDLER_P_H
+#define QTIFFHANDLER_P_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_P_H
diff --git a/src/gui/image/qvolatileimage.cpp b/src/gui/image/qvolatileimage.cpp
new file mode 100644
index 0000000000..098e9a1264
--- /dev/null
+++ b/src/gui/image/qvolatileimage.cpp
@@ -0,0 +1,318 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qvolatileimage_p.h"
+#include "qvolatileimagedata_p.h"
+#include <QtGui/private/qpaintengine_raster_p.h>
+#include <QtGui/private/qpixmapdata_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QVolatileImagePaintEnginePrivate : public QRasterPaintEnginePrivate
+{
+public:
+ QVolatileImagePaintEnginePrivate() { }
+ QVolatileImage *img;
+};
+
+class QVolatileImagePaintEngine : public QRasterPaintEngine
+{
+ Q_DECLARE_PRIVATE(QVolatileImagePaintEngine)
+
+public:
+ QVolatileImagePaintEngine(QPaintDevice *device, QVolatileImage *img);
+ bool begin(QPaintDevice *device);
+ bool end();
+ void drawPixmap(const QPointF &p, const QPixmap &pm);
+ void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr);
+};
+
+QVolatileImage::QVolatileImage()
+ : d(new QVolatileImageData)
+{
+}
+
+QVolatileImage::QVolatileImage(int w, int h, QImage::Format format)
+ : d(new QVolatileImageData(w, h, format))
+{
+}
+
+QVolatileImage::QVolatileImage(const QImage &sourceImage)
+ : d(new QVolatileImageData(sourceImage))
+{
+}
+
+QVolatileImage::QVolatileImage(void *nativeImage, void *nativeMask)
+ : d(new QVolatileImageData(nativeImage, nativeMask))
+{
+}
+
+// Need non-inline, non-autogenerated copy ctor, dtor, op= to keep the
+// fwd declared QSharedData working.
+
+QVolatileImage::QVolatileImage(const QVolatileImage &other)
+ : d(other.d)
+{
+}
+
+QVolatileImage::~QVolatileImage()
+{
+}
+
+QVolatileImage &QVolatileImage::operator=(const QVolatileImage &rhs)
+{
+ d = rhs.d;
+ return *this;
+}
+
+bool QVolatileImage::isNull() const
+{
+ return d->image.isNull();
+}
+
+QImage::Format QVolatileImage::format() const
+{
+ return d->image.format();
+}
+
+int QVolatileImage::width() const
+{
+ return d->image.width();
+}
+
+int QVolatileImage::height() const
+{
+ return d->image.height();
+}
+
+int QVolatileImage::bytesPerLine() const
+{
+ return d->image.bytesPerLine();
+}
+
+int QVolatileImage::byteCount() const
+{
+ return d->image.byteCount();
+}
+
+int QVolatileImage::depth() const
+{
+ return d->image.depth();
+}
+
+bool QVolatileImage::hasAlphaChannel() const
+{
+ return d->image.hasAlphaChannel();
+}
+
+void QVolatileImage::beginDataAccess() const
+{
+ d->beginDataAccess();
+}
+
+void QVolatileImage::endDataAccess(bool readOnly) const
+{
+ d->endDataAccess(readOnly);
+}
+
+/*!
+ Access to pixel data via bits() or constBits() should be guarded by
+ begin/endDataAccess().
+ */
+uchar *QVolatileImage::bits()
+{
+ return d->image.bits();
+}
+
+const uchar *QVolatileImage::constBits() const
+{
+ return d->image.constBits();
+}
+
+bool QVolatileImage::ensureFormat(QImage::Format format)
+{
+ return d->ensureFormat(format);
+}
+
+/*!
+ This will always perform a copy of the pixel data.
+ */
+QImage QVolatileImage::toImage() const
+{
+ d->beginDataAccess();
+ QImage newImage = d->image.copy(); // no sharing allowed
+ d->endDataAccess(true);
+ return newImage;
+}
+
+/*!
+ Returns a reference to the image that is potentially using some native
+ buffer internally. Access to the image's pixel data should be guarded by
+ begin/endDataAccess(). Use it when there is a need for QImage APIs not provided
+ by this class. The returned QImage must never be shared or assigned to.
+ */
+QImage &QVolatileImage::imageRef() // non-const, in order to cause a detach
+{
+ d->ensureImage();
+ return d->image;
+}
+
+void *QVolatileImage::duplicateNativeImage() const
+{
+ return d->duplicateNativeImage();
+}
+
+void QVolatileImage::setAlphaChannel(const QPixmap &alphaChannel)
+{
+ ensureFormat(QImage::Format_ARGB32_Premultiplied);
+ beginDataAccess();
+ imageRef().setAlphaChannel(alphaChannel.toImage());
+ endDataAccess();
+ d->ensureImage();
+}
+
+void QVolatileImage::fill(uint pixelValue)
+{
+ beginDataAccess();
+ imageRef().fill(pixelValue);
+ endDataAccess();
+ d->ensureImage();
+}
+
+void QVolatileImage::copyFrom(QVolatileImage *source, const QRect &rect)
+{
+ if (source->isNull()) {
+ return;
+ }
+ QRect r = rect;
+ if (rect.isNull()) {
+ r = QRect(0, 0, source->width(), source->height());
+ }
+ source->beginDataAccess();
+ QImage &srcImgRef(source->imageRef());
+ int srcbpl = srcImgRef.bytesPerLine();
+ int srcbpp = srcImgRef.depth() / 8;
+ const uchar *sptr = srcImgRef.constBits() + r.y() * srcbpl;
+ beginDataAccess();
+ QImage &dstImgRef(imageRef());
+ int dstbpl = dstImgRef.bytesPerLine();
+ uchar *dptr = dstImgRef.bits();
+ for (int y = 0; y < r.height(); ++y) {
+ qMemCopy(dptr, sptr + r.x() * srcbpp, r.width() * srcbpp);
+ sptr += srcbpl;
+ dptr += dstbpl;
+ }
+ endDataAccess();
+ source->endDataAccess(true);
+}
+
+/*!
+ To be called from the PixmapData's paintEngine().
+ */
+QPaintEngine *QVolatileImage::paintEngine()
+{
+ if (!d->pengine) {
+ d->pengine = new QVolatileImagePaintEngine(&imageRef(), this);
+ }
+ return d->pengine;
+}
+
+QVolatileImagePaintEngine::QVolatileImagePaintEngine(QPaintDevice *device,
+ QVolatileImage *img)
+ : QRasterPaintEngine(*(new QVolatileImagePaintEnginePrivate), device)
+{
+ Q_D(QVolatileImagePaintEngine);
+ d->img = img;
+}
+
+bool QVolatileImagePaintEngine::begin(QPaintDevice *device)
+{
+ Q_D(QVolatileImagePaintEngine);
+ d->img->beginDataAccess();
+ return QRasterPaintEngine::begin(device);
+}
+
+bool QVolatileImagePaintEngine::end()
+{
+ Q_D(QVolatileImagePaintEngine);
+ bool ret = QRasterPaintEngine::end();
+ d->img->endDataAccess();
+ return ret;
+}
+
+// For non-RasterClass pixmaps drawPixmap() would call toImage() which is slow in
+// our case. Therefore drawPixmap() is rerouted to drawImage().
+
+void QVolatileImagePaintEngine::drawPixmap(const QPointF &p, const QPixmap &pm)
+{
+#ifdef Q_OS_SYMBIAN
+ void *nativeData = pm.pixmapData()->toNativeType(QPixmapData::VolatileImage);
+ if (nativeData) {
+ QVolatileImage *img = static_cast<QVolatileImage *>(nativeData);
+ img->beginDataAccess();
+ QRasterPaintEngine::drawImage(p, img->imageRef());
+ img->endDataAccess(true);
+ } else {
+ QRasterPaintEngine::drawPixmap(p, pm);
+ }
+#else
+ QRasterPaintEngine::drawPixmap(p, pm);
+#endif
+}
+
+void QVolatileImagePaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
+{
+#ifdef Q_OS_SYMBIAN
+ void *nativeData = pm.pixmapData()->toNativeType(QPixmapData::VolatileImage);
+ if (nativeData) {
+ QVolatileImage *img = static_cast<QVolatileImage *>(nativeData);
+ img->beginDataAccess();
+ QRasterPaintEngine::drawImage(r, img->imageRef(), sr);
+ img->endDataAccess(true);
+ } else {
+ QRasterPaintEngine::drawPixmap(r, pm, sr);
+ }
+#else
+ QRasterPaintEngine::drawPixmap(r, pm, sr);
+#endif
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/image/qvolatileimage_p.h b/src/gui/image/qvolatileimage_p.h
new file mode 100644
index 0000000000..fc5d6b115c
--- /dev/null
+++ b/src/gui/image/qvolatileimage_p.h
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QVOLATILEIMAGE_P_H
+#define QVOLATILEIMAGE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/qimage.h>
+#include <QtCore/qshareddata.h>
+
+QT_BEGIN_NAMESPACE
+
+class QVolatileImageData;
+
+class Q_GUI_EXPORT QVolatileImage
+{
+public:
+ QVolatileImage();
+ QVolatileImage(int w, int h, QImage::Format format);
+ explicit QVolatileImage(const QImage &sourceImage);
+ explicit QVolatileImage(void *nativeImage, void *nativeMask = 0);
+ QVolatileImage(const QVolatileImage &other);
+ ~QVolatileImage();
+ QVolatileImage &operator=(const QVolatileImage &rhs);
+
+ bool isNull() const;
+ QImage::Format format() const;
+ int width() const;
+ int height() const;
+ int bytesPerLine() const;
+ int byteCount() const;
+ int depth() const;
+ bool hasAlphaChannel() const;
+ void beginDataAccess() const;
+ void endDataAccess(bool readOnly = false) const;
+ uchar *bits();
+ const uchar *constBits() const;
+ bool ensureFormat(QImage::Format format);
+ QImage toImage() const;
+ QImage &imageRef();
+ QPaintEngine *paintEngine();
+ void setAlphaChannel(const QPixmap &alphaChannel);
+ void fill(uint pixelValue);
+ void *duplicateNativeImage() const;
+ void copyFrom(QVolatileImage *source, const QRect &rect);
+
+private:
+ QSharedDataPointer<QVolatileImageData> d;
+};
+
+QT_END_NAMESPACE
+
+#endif // QVOLATILEIMAGE_P_H
diff --git a/src/gui/image/qvolatileimagedata.cpp b/src/gui/image/qvolatileimagedata.cpp
new file mode 100644
index 0000000000..d7b964c0a6
--- /dev/null
+++ b/src/gui/image/qvolatileimagedata.cpp
@@ -0,0 +1,114 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qvolatileimagedata_p.h"
+#include <QtGui/qpaintengine.h>
+
+QT_BEGIN_NAMESPACE
+
+QVolatileImageData::QVolatileImageData()
+ : pengine(0)
+{
+}
+
+QVolatileImageData::QVolatileImageData(int w, int h, QImage::Format format)
+ : pengine(0)
+{
+ image = QImage(w, h, format);
+}
+
+QVolatileImageData::QVolatileImageData(const QImage &sourceImage)
+ : pengine(0)
+{
+ image = sourceImage;
+}
+
+QVolatileImageData::QVolatileImageData(void *, void *)
+ : pengine(0)
+{
+ // Not supported.
+}
+
+QVolatileImageData::QVolatileImageData(const QVolatileImageData &other)
+ : QSharedData()
+{
+ image = other.image;
+ // The detach is not mandatory here but we do it nonetheless in order to
+ // keep the behavior consistent with other platforms.
+ image.detach();
+ pengine = 0;
+}
+
+QVolatileImageData::~QVolatileImageData()
+{
+ delete pengine;
+}
+
+void QVolatileImageData::beginDataAccess() const
+{
+ // nothing to do here
+}
+
+void QVolatileImageData::endDataAccess(bool readOnly) const
+{
+ Q_UNUSED(readOnly);
+ // nothing to do here
+}
+
+bool QVolatileImageData::ensureFormat(QImage::Format format)
+{
+ if (image.format() != format) {
+ image = image.convertToFormat(format);
+ }
+ return true;
+}
+
+void *QVolatileImageData::duplicateNativeImage() const
+{
+ return 0;
+}
+
+void QVolatileImageData::ensureImage()
+{
+ // nothing to do here
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/image/qvolatileimagedata_p.h b/src/gui/image/qvolatileimagedata_p.h
new file mode 100644
index 0000000000..dab1685347
--- /dev/null
+++ b/src/gui/image/qvolatileimagedata_p.h
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QVOLATILEIMAGEDATA_P_H
+#define QVOLATILEIMAGEDATA_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/qimage.h>
+#include <QtCore/qshareddata.h>
+
+#ifdef Q_OS_SYMBIAN
+class CFbsBitmap;
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QVolatileImageData : public QSharedData
+{
+public:
+ QVolatileImageData();
+ QVolatileImageData(int w, int h, QImage::Format format);
+ QVolatileImageData(const QImage &sourceImage);
+ QVolatileImageData(void *nativeImage, void *nativeMask);
+ QVolatileImageData(const QVolatileImageData &other);
+ ~QVolatileImageData();
+
+ void beginDataAccess() const;
+ void endDataAccess(bool readOnly = false) const;
+ bool ensureFormat(QImage::Format format);
+ void *duplicateNativeImage() const;
+ void ensureImage();
+
+#ifdef Q_OS_SYMBIAN
+ void updateImage();
+ void initWithBitmap(CFbsBitmap *source);
+ void applyMask(CFbsBitmap *mask);
+ void ensureBitmap();
+ void release();
+ QVolatileImageData *next;
+ QVolatileImageData *prev;
+ CFbsBitmap *bitmap;
+#endif
+ QImage image;
+ QPaintEngine *pengine;
+};
+
+QT_END_NAMESPACE
+
+#endif // QVOLATILEIMAGEDATA_P_H
diff --git a/src/gui/image/qvolatileimagedata_symbian.cpp b/src/gui/image/qvolatileimagedata_symbian.cpp
new file mode 100644
index 0000000000..6e2909bec3
--- /dev/null
+++ b/src/gui/image/qvolatileimagedata_symbian.cpp
@@ -0,0 +1,474 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qvolatileimagedata_p.h"
+#include <fbs.h>
+#include <QtGui/private/qt_s60_p.h>
+#include <QtGui/qpaintengine.h>
+#include <QtGui/private/qimage_p.h>
+
+QT_BEGIN_NAMESPACE
+
+static CFbsBitmap *rasterizeBitmap(CFbsBitmap *bitmap, TDisplayMode newMode)
+{
+ if (!bitmap) {
+ return 0;
+ }
+ QScopedPointer<CFbsBitmap> newBitmap(new CFbsBitmap);
+ if (newBitmap->Create(bitmap->SizeInPixels(), newMode) != KErrNone) {
+ qWarning("QVolatileImage: Failed to create new bitmap");
+ return 0;
+ }
+ CFbsBitmapDevice *bitmapDevice = 0;
+ CFbsBitGc *bitmapGc = 0;
+ QT_TRAP_THROWING(bitmapDevice = CFbsBitmapDevice::NewL(newBitmap.data()));
+ QScopedPointer<CFbsBitmapDevice> bitmapDevicePtr(bitmapDevice);
+ QT_TRAP_THROWING(bitmapGc = CFbsBitGc::NewL());
+ bitmapGc->Activate(bitmapDevice);
+ bitmapGc->BitBlt(TPoint(), bitmap);
+ delete bitmapGc;
+ return newBitmap.take();
+}
+
+static inline TDisplayMode format2TDisplayMode(QImage::Format format)
+{
+ TDisplayMode mode;
+ switch (format) {
+ case QImage::Format_MonoLSB:
+ mode = EGray2;
+ break;
+ case QImage::Format_Indexed8:
+ mode = EColor256;
+ break;
+ case QImage::Format_RGB444:
+ mode = EColor4K;
+ break;
+ case QImage::Format_RGB16:
+ mode = EColor64K;
+ break;
+ case QImage::Format_RGB888:
+ mode = EColor16M;
+ break;
+ case QImage::Format_RGB32:
+ mode = EColor16MU;
+ break;
+ case QImage::Format_ARGB32:
+ mode = EColor16MA;
+ break;
+ case QImage::Format_ARGB32_Premultiplied:
+ mode = Q_SYMBIAN_ECOLOR16MAP;
+ break;
+ default:
+ mode = ENone;
+ break;
+ }
+ return mode;
+}
+
+static CFbsBitmap *imageToBitmap(const QImage &image)
+{
+ if (image.isNull()) {
+ return 0;
+ }
+ CFbsBitmap *bitmap = new CFbsBitmap;
+ if (bitmap->Create(TSize(image.width(), image.height()),
+ format2TDisplayMode(image.format())) == KErrNone) {
+ bitmap->BeginDataAccess();
+ uchar *dptr = reinterpret_cast<uchar *>(bitmap->DataAddress());
+ int bmpLineLen = bitmap->DataStride();
+ int imgLineLen = image.bytesPerLine();
+ if (bmpLineLen == imgLineLen) {
+ qMemCopy(dptr, image.constBits(), image.byteCount());
+ } else {
+ int len = qMin(bmpLineLen, imgLineLen);
+ const uchar *sptr = image.constBits();
+ for (int y = 0; y < image.height(); ++y) {
+ qMemCopy(dptr, sptr, len);
+ dptr += bmpLineLen;
+ sptr += imgLineLen;
+ }
+ }
+ bitmap->EndDataAccess();
+ } else {
+ qWarning("QVolatileImage: Failed to create source bitmap");
+ delete bitmap;
+ bitmap = 0;
+ }
+ return bitmap;
+}
+
+static CFbsBitmap *copyData(const QVolatileImageData &source)
+{
+ source.beginDataAccess();
+ CFbsBitmap *bmp = imageToBitmap(source.image);
+ source.endDataAccess();
+ return bmp;
+}
+
+static CFbsBitmap *convertData(const QVolatileImageData &source, QImage::Format newFormat)
+{
+ source.beginDataAccess();
+ QImage img = source.image.convertToFormat(newFormat);
+ CFbsBitmap *bmp = imageToBitmap(img);
+ source.endDataAccess();
+ return bmp;
+}
+
+static CFbsBitmap *duplicateBitmap(const CFbsBitmap &sourceBitmap)
+{
+ CFbsBitmap *bitmap = new CFbsBitmap;
+ if (bitmap->Duplicate(sourceBitmap.Handle()) != KErrNone) {
+ qWarning("QVolatileImage: Failed to duplicate source bitmap");
+ delete bitmap;
+ bitmap = 0;
+ }
+ return bitmap;
+}
+
+static CFbsBitmap *createBitmap(int w, int h, QImage::Format format)
+{
+ CFbsBitmap *bitmap = new CFbsBitmap;
+ if (bitmap->Create(TSize(w, h), format2TDisplayMode(format)) != KErrNone) {
+ qWarning("QVolatileImage: Failed to create source bitmap %d,%d (%d)", w, h, format);
+ delete bitmap;
+ bitmap = 0;
+ }
+ return bitmap;
+}
+
+static inline bool bitmapNeedsCopy(CFbsBitmap *bitmap)
+{
+ bool needsCopy = bitmap->IsCompressedInRAM();
+#ifdef Q_SYMBIAN_HAS_EXTENDED_BITMAP_TYPE
+ needsCopy |= (bitmap->ExtendedBitmapType() != KNullUid);
+#endif
+ return needsCopy;
+}
+
+static bool cleanup_function_registered = false;
+static QVolatileImageData *firstImageData = 0;
+
+static void cleanup()
+{
+ if (RFbsSession::GetSession()) {
+ QVolatileImageData *imageData = firstImageData;
+ while (imageData) {
+ imageData->release();
+ imageData = imageData->next;
+ }
+ }
+}
+
+static void ensureCleanup()
+{
+ // Destroy all underlying bitmaps in a post routine to prevent panics.
+ // This is a must because CFbsBitmap destructor needs the fbs session,
+ // that was used to create the bitmap, to be open still.
+ if (!cleanup_function_registered) {
+ qAddPostRoutine(cleanup);
+ cleanup_function_registered = true;
+ }
+}
+
+static void registerImageData(QVolatileImageData *imageData)
+{
+ ensureCleanup();
+ imageData->next = firstImageData;
+ if (firstImageData) {
+ firstImageData->prev = imageData;
+ }
+ firstImageData = imageData;
+}
+
+static void unregisterImageData(QVolatileImageData *imageData)
+{
+ if (imageData->prev) {
+ imageData->prev->next = imageData->next;
+ } else {
+ firstImageData = imageData->next;
+ }
+ if (imageData->next) {
+ imageData->next->prev = imageData->prev;
+ }
+}
+
+QVolatileImageData::QVolatileImageData()
+ : next(0), prev(0), bitmap(0), pengine(0)
+{
+ registerImageData(this);
+}
+
+QVolatileImageData::QVolatileImageData(int w, int h, QImage::Format format)
+ : next(0), prev(0), bitmap(0), pengine(0)
+{
+ registerImageData(this);
+ bitmap = createBitmap(w, h, format);
+ updateImage();
+}
+
+QVolatileImageData::QVolatileImageData(const QImage &sourceImage)
+ : next(0), prev(0), bitmap(0), pengine(0)
+{
+ registerImageData(this);
+ image = sourceImage;
+ // The following is not mandatory, but we do it here to have a bitmap
+ // created always in order to reduce local heap usage.
+ ensureBitmap();
+}
+
+QVolatileImageData::QVolatileImageData(void *nativeImage, void *nativeMask)
+ : next(0), prev(0), bitmap(0), pengine(0)
+{
+ registerImageData(this);
+ if (nativeImage) {
+ CFbsBitmap *source = static_cast<CFbsBitmap *>(nativeImage);
+ CFbsBitmap *mask = static_cast<CFbsBitmap *>(nativeMask);
+ initWithBitmap(source);
+ if (mask) {
+ applyMask(mask);
+ }
+ }
+}
+
+QVolatileImageData::QVolatileImageData(const QVolatileImageData &other)
+{
+ bitmap = 0;
+ pengine = 0;
+ next = prev = 0;
+ registerImageData(this);
+ if (!other.image.isNull()) {
+ bitmap = copyData(other);
+ updateImage();
+ }
+}
+
+QVolatileImageData::~QVolatileImageData()
+{
+ release();
+ unregisterImageData(this);
+}
+
+void QVolatileImageData::release()
+{
+ delete bitmap;
+ bitmap = 0;
+ delete pengine;
+ pengine = 0;
+}
+
+void QVolatileImageData::beginDataAccess() const
+{
+ if (bitmap) {
+ bitmap->BeginDataAccess();
+ }
+}
+
+void QVolatileImageData::endDataAccess(bool readOnly) const
+{
+ if (bitmap) {
+ bitmap->EndDataAccess(readOnly);
+ }
+}
+
+bool QVolatileImageData::ensureFormat(QImage::Format format)
+{
+ if (image.isNull()) {
+ return false;
+ }
+ if (image.format() != format) {
+ CFbsBitmap *newBitmap = convertData(*this, format);
+ if (newBitmap && newBitmap != bitmap) {
+ delete bitmap;
+ bitmap = newBitmap;
+ updateImage();
+ } else {
+ return false;
+ }
+ }
+ return true;
+}
+
+void *QVolatileImageData::duplicateNativeImage() const
+{
+ const_cast<QVolatileImageData *>(this)->ensureBitmap();
+ if (bitmap) {
+ if (bitmap->DisplayMode() == EColor16M) {
+ // slow path: needs rgb swapping
+ beginDataAccess();
+ QImage tmp = image.rgbSwapped();
+ endDataAccess(true);
+ return imageToBitmap(tmp);
+ } else if (bitmap->DisplayMode() == EGray2) {
+ // slow path: needs inverting pixels
+ beginDataAccess();
+ QImage tmp = image.copy();
+ endDataAccess(true);
+ tmp.invertPixels();
+ return imageToBitmap(tmp);
+ } else {
+ // fast path: just duplicate the bitmap
+ return duplicateBitmap(*bitmap);
+ }
+ }
+ return 0;
+}
+
+void QVolatileImageData::updateImage()
+{
+ if (bitmap) {
+ TSize size = bitmap->SizeInPixels();
+ beginDataAccess();
+ // Use existing buffer, no copy. The data address never changes so it
+ // is enough to do this whenever we have a new CFbsBitmap. N.B. never
+ // use const uchar* here, that would create a read-only image data which
+ // would make a copy in detach() even when refcount is 1.
+ image = QImage(reinterpret_cast<uchar *>(bitmap->DataAddress()),
+ size.iWidth, size.iHeight, bitmap->DataStride(),
+ qt_TDisplayMode2Format(bitmap->DisplayMode()));
+ endDataAccess(true);
+ } else {
+ image = QImage();
+ }
+}
+
+void QVolatileImageData::initWithBitmap(CFbsBitmap *source)
+{
+ bool needsCopy = bitmapNeedsCopy(source);
+ if (source->DisplayMode() == EColor16M) {
+ // EColor16M is BGR
+ CFbsBitmap *unswappedBmp = source;
+ if (needsCopy) {
+ unswappedBmp = rasterizeBitmap(source, source->DisplayMode());
+ }
+ unswappedBmp->BeginDataAccess();
+ TSize sourceSize = unswappedBmp->SizeInPixels();
+ QImage img((uchar *) unswappedBmp->DataAddress(),
+ sourceSize.iWidth, sourceSize.iHeight, unswappedBmp->DataStride(),
+ qt_TDisplayMode2Format(unswappedBmp->DisplayMode()));
+ img = img.rgbSwapped();
+ unswappedBmp->EndDataAccess(true);
+ bitmap = imageToBitmap(img);
+ if (needsCopy) {
+ delete unswappedBmp;
+ }
+ } else if (needsCopy) {
+ // Rasterize extended and compressed bitmaps.
+ bitmap = rasterizeBitmap(source, EColor16MAP);
+ } else if (source->DisplayMode() == EGray2) {
+ // The pixels will be inverted, must make a copy.
+ bitmap = rasterizeBitmap(source, source->DisplayMode());
+ } else {
+ // Efficient path: no pixel data copying. Just duplicate. This of course
+ // means the original bitmap's data may get modified, but that's fine
+ // and is in accordance with the QPixmap::fromSymbianCFbsBitmap() docs.
+ bitmap = duplicateBitmap(*source);
+ }
+ updateImage();
+ if (bitmap && bitmap->DisplayMode() == EGray2) {
+ // Symbian thinks set pixels are white/transparent, Qt thinks they are
+ // foreground/solid. Invert mono bitmaps so that masks work correctly.
+ beginDataAccess();
+ image.invertPixels();
+ endDataAccess();
+ }
+}
+
+void QVolatileImageData::applyMask(CFbsBitmap *mask)
+{
+ ensureFormat(QImage::Format_ARGB32_Premultiplied);
+ bool destroyMask = false;
+ if (bitmapNeedsCopy(mask)) {
+ mask = rasterizeBitmap(mask, EColor16MU);
+ if (!mask) {
+ return;
+ }
+ destroyMask = true;
+ }
+ mask->BeginDataAccess();
+ TSize maskSize = mask->SizeInPixels();
+ QImage maskImg((const uchar *) mask->DataAddress(), maskSize.iWidth, maskSize.iHeight,
+ mask->DataStride(), qt_TDisplayMode2Format(mask->DisplayMode()));
+ if (mask->DisplayMode() == EGray2) {
+ maskImg = maskImg.copy();
+ maskImg.invertPixels();
+ }
+ beginDataAccess();
+ image.setAlphaChannel(maskImg);
+ endDataAccess();
+ mask->EndDataAccess(true);
+ ensureImage();
+ if (destroyMask) {
+ delete mask;
+ }
+}
+
+void QVolatileImageData::ensureImage()
+{
+ if (bitmap && !image.isNull()) {
+ QImageData *imaged = image.data_ptr();
+ if (imaged->ref != 1 || imaged->ro_data) {
+ // This is bad, the imagedata got shared somehow. Detach, in order to
+ // have the next check fail and thus have 'image' recreated.
+ beginDataAccess();
+ image.detach();
+ endDataAccess(true);
+ }
+ }
+ if (bitmap && image.constBits() != reinterpret_cast<const uchar *>(bitmap->DataAddress())) {
+ // Should not ever get here. If we do it means that either 'image' has
+ // been replaced with a copy (e.g. because some QImage API assigned a
+ // new, regular QImage to *this) or the bitmap's data address changed
+ // unexpectedly.
+ qWarning("QVolatileImageData: Ptr mismatch");
+ // Recover by recreating the image so that it uses the bitmap as its buffer.
+ updateImage();
+ }
+}
+
+void QVolatileImageData::ensureBitmap()
+{
+ if (!bitmap && !image.isNull()) {
+ bitmap = imageToBitmap(image);
+ updateImage();
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/image/qxbmhandler.cpp b/src/gui/image/qxbmhandler.cpp
new file mode 100644
index 0000000000..ae9e2047e5
--- /dev/null
+++ b/src/gui/image/qxbmhandler.cpp
@@ -0,0 +1,361 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qplatformdefs.h>
+#include "private/qxbmhandler_p.h"
+
+#ifndef QT_NO_IMAGEFORMAT_XBM
+
+#include <qimage.h>
+#include <qiodevice.h>
+#include <qvariant.h>
+
+#include <stdio.h>
+#include <ctype.h>
+
+QT_BEGIN_NAMESPACE
+
+/*****************************************************************************
+ X bitmap image read/write functions
+ *****************************************************************************/
+
+static inline int hex2byte(register char *p)
+{
+ return ((isdigit((uchar) *p) ? *p - '0' : toupper((uchar) *p) - 'A' + 10) << 4) |
+ (isdigit((uchar) *(p+1)) ? *(p+1) - '0' : toupper((uchar) *(p+1)) - 'A' + 10);
+}
+
+static bool read_xbm_header(QIODevice *device, int& w, int& h)
+{
+ const int buflen = 300;
+ const int maxlen = 4096;
+ char buf[buflen + 1];
+ QRegExp r1(QLatin1String("^#define[ \t]+[a-zA-Z0-9._]+[ \t]+"));
+ QRegExp r2(QLatin1String("[0-9]+"));
+
+ qint64 readBytes = 0;
+ qint64 totalReadBytes = 0;
+
+ buf[0] = '\0';
+
+ // skip initial comment, if any
+ while (buf[0] != '#') {
+ readBytes = device->readLine(buf, buflen);
+
+ // if readBytes >= buflen, it's very probably not a C file
+ if (readBytes <= 0 || readBytes >= buflen -1)
+ return false;
+
+ // limit xbm headers to the first 4k in the file to prevent
+ // excessive reads on non-xbm files
+ totalReadBytes += readBytes;
+ if (totalReadBytes >= maxlen)
+ return false;
+ }
+
+ buf[readBytes - 1] = '\0';
+ QString sbuf;
+ sbuf = QString::fromLatin1(buf);
+
+ // "#define .._width <num>"
+ if (r1.indexIn(sbuf) == 0 &&
+ r2.indexIn(sbuf, r1.matchedLength()) == r1.matchedLength())
+ w = QByteArray(&buf[r1.matchedLength()]).trimmed().toInt();
+
+ // "#define .._height <num>"
+ readBytes = device->readLine(buf, buflen);
+ if (readBytes <= 0)
+ return false;
+ buf[readBytes - 1] = '\0';
+
+ sbuf = QString::fromLatin1(buf);
+
+ if (r1.indexIn(sbuf) == 0 &&
+ r2.indexIn(sbuf, r1.matchedLength()) == r1.matchedLength())
+ h = QByteArray(&buf[r1.matchedLength()]).trimmed().toInt();
+
+ // format error
+ if (w <= 0 || w > 32767 || h <= 0 || h > 32767)
+ return false;
+
+ return true;
+}
+
+static bool read_xbm_body(QIODevice *device, int w, int h, QImage *outImage)
+{
+ const int buflen = 300;
+ char buf[buflen + 1];
+
+ qint64 readBytes = 0;
+
+ // scan for database
+ for (;;) {
+ if ((readBytes = device->readLine(buf, buflen)) <= 0) {
+ // end of file
+ return false;
+ }
+
+ buf[readBytes] = '\0';
+ if (QByteArray::fromRawData(buf, readBytes).contains("0x"))
+ break;
+ }
+
+ if (outImage->size() != QSize(w, h) || outImage->format() != QImage::Format_MonoLSB) {
+ *outImage = QImage(w, h, QImage::Format_MonoLSB);
+ if (outImage->isNull())
+ return false;
+ }
+
+ outImage->setColorCount(2);
+ outImage->setColor(0, qRgb(255,255,255)); // white
+ outImage->setColor(1, qRgb(0,0,0)); // black
+
+ int x = 0, y = 0;
+ uchar *b = outImage->scanLine(0);
+ char *p = buf + QByteArray::fromRawData(buf, readBytes).indexOf("0x");
+ w = (w+7)/8; // byte width
+
+ while (y < h) { // for all encoded bytes...
+ if (p) { // p = "0x.."
+ *b++ = hex2byte(p+2);
+ p += 2;
+ if (++x == w && ++y < h) {
+ b = outImage->scanLine(y);
+ x = 0;
+ }
+ p = strstr(p, "0x");
+ } else { // read another line
+ if ((readBytes = device->readLine(buf,buflen)) <= 0) // EOF ==> truncated image
+ break;
+ p = buf + QByteArray::fromRawData(buf, readBytes).indexOf("0x");
+ }
+ }
+
+ return true;
+}
+
+static bool read_xbm_image(QIODevice *device, QImage *outImage)
+{
+ int w = 0, h = 0;
+ if (!read_xbm_header(device, w, h))
+ return false;
+ return read_xbm_body(device, w, h, outImage);
+}
+
+static bool write_xbm_image(const QImage &sourceImage, QIODevice *device, const QString &fileName)
+{
+ QImage image = sourceImage;
+ int w = image.width();
+ int h = image.height();
+ int i;
+ QString s = fileName; // get file base name
+ int msize = s.length() + 100;
+ char *buf = new char[msize];
+
+ qsnprintf(buf, msize, "#define %s_width %d\n", s.toAscii().data(), w);
+ device->write(buf, qstrlen(buf));
+ qsnprintf(buf, msize, "#define %s_height %d\n", s.toAscii().data(), h);
+ device->write(buf, qstrlen(buf));
+ qsnprintf(buf, msize, "static char %s_bits[] = {\n ", s.toAscii().data());
+ device->write(buf, qstrlen(buf));
+
+ if (image.format() != QImage::Format_MonoLSB)
+ image = image.convertToFormat(QImage::Format_MonoLSB);
+
+ bool invert = qGray(image.color(0)) < qGray(image.color(1));
+ char hexrep[16];
+ for (i=0; i<10; i++)
+ hexrep[i] = '0' + i;
+ for (i=10; i<16; i++)
+ hexrep[i] = 'a' -10 + i;
+ if (invert) {
+ char t;
+ for (i=0; i<8; i++) {
+ t = hexrep[15-i];
+ hexrep[15-i] = hexrep[i];
+ hexrep[i] = t;
+ }
+ }
+ int bcnt = 0;
+ register char *p = buf;
+ int bpl = (w+7)/8;
+ for (int y = 0; y < h; ++y) {
+ uchar *b = image.scanLine(y);
+ for (i = 0; i < bpl; ++i) {
+ *p++ = '0'; *p++ = 'x';
+ *p++ = hexrep[*b >> 4];
+ *p++ = hexrep[*b++ & 0xf];
+
+ if (i < bpl - 1 || y < h - 1) {
+ *p++ = ',';
+ if (++bcnt > 14) {
+ *p++ = '\n';
+ *p++ = ' ';
+ *p = '\0';
+ if ((int)qstrlen(buf) != device->write(buf, qstrlen(buf))) {
+ delete [] buf;
+ return false;
+ }
+ p = buf;
+ bcnt = 0;
+ }
+ }
+ }
+ }
+#if defined(_MSC_VER) && _MSC_VER >= 1400
+ strcpy_s(p, sizeof(" };\n"), " };\n");
+#else
+ strcpy(p, " };\n");
+#endif
+ if ((int)qstrlen(buf) != device->write(buf, qstrlen(buf))) {
+ delete [] buf;
+ return false;
+ }
+
+ delete [] buf;
+ return true;
+}
+
+QXbmHandler::QXbmHandler()
+ : state(Ready)
+{
+}
+
+bool QXbmHandler::readHeader()
+{
+ state = Error;
+ if (!read_xbm_header(device(), width, height))
+ return false;
+ state = ReadHeader;
+ return true;
+}
+
+bool QXbmHandler::canRead() const
+{
+ if (state == Ready && !canRead(device()))
+ return false;
+
+ if (state != Error) {
+ setFormat("xbm");
+ return true;
+ }
+
+ return false;
+}
+
+bool QXbmHandler::canRead(QIODevice *device)
+{
+ QImage image;
+
+ // it's impossible to tell whether we can load an XBM or not when
+ // it's from a sequential device, as the only way to do it is to
+ // attempt to parse the whole image.
+ if (device->isSequential())
+ return false;
+
+ qint64 oldPos = device->pos();
+ bool success = read_xbm_image(device, &image);
+ device->seek(oldPos);
+
+ return success;
+}
+
+bool QXbmHandler::read(QImage *image)
+{
+ if (state == Error)
+ return false;
+
+ if (state == Ready && !readHeader()) {
+ state = Error;
+ return false;
+ }
+
+ if (!read_xbm_body(device(), width, height, image)) {
+ state = Error;
+ return false;
+ }
+
+ state = Ready;
+ return true;
+}
+
+bool QXbmHandler::write(const QImage &image)
+{
+ return write_xbm_image(image, device(), fileName);
+}
+
+bool QXbmHandler::supportsOption(ImageOption option) const
+{
+ return option == Name
+ || option == Size
+ || option == ImageFormat;
+}
+
+QVariant QXbmHandler::option(ImageOption option) const
+{
+ if (option == Name) {
+ return fileName;
+ } else if (option == Size) {
+ if (state == Error)
+ return QVariant();
+ if (state == Ready && !const_cast<QXbmHandler*>(this)->readHeader())
+ return QVariant();
+ return QSize(width, height);
+ } else if (option == ImageFormat) {
+ return QImage::Format_MonoLSB;
+ }
+ return QVariant();
+}
+
+void QXbmHandler::setOption(ImageOption option, const QVariant &value)
+{
+ if (option == Name)
+ fileName = value.toString();
+}
+
+QByteArray QXbmHandler::name() const
+{
+ return "xbm";
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_IMAGEFORMAT_XBM
diff --git a/src/gui/image/qxbmhandler_p.h b/src/gui/image/qxbmhandler_p.h
new file mode 100644
index 0000000000..6fdd3d0816
--- /dev/null
+++ b/src/gui/image/qxbmhandler_p.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QXBMHANDLER_P_H
+#define QXBMHANDLER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the QLibrary class. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtGui/qimageiohandler.h"
+
+#ifndef QT_NO_IMAGEFORMAT_XBM
+
+QT_BEGIN_NAMESPACE
+
+class QXbmHandler : public QImageIOHandler
+{
+public:
+ QXbmHandler();
+ 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:
+ bool readHeader();
+ enum State {
+ Ready,
+ ReadHeader,
+ Error
+ };
+ State state;
+ int width;
+ int height;
+ QString fileName;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_IMAGEFORMAT_XBM
+
+#endif // QXBMHANDLER_P_H
diff --git a/src/gui/image/qxpmhandler.cpp b/src/gui/image/qxpmhandler.cpp
new file mode 100644
index 0000000000..075d5dadc7
--- /dev/null
+++ b/src/gui/image/qxpmhandler.cpp
@@ -0,0 +1,1295 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "private/qxpmhandler_p.h"
+
+#ifndef QT_NO_IMAGEFORMAT_XPM
+
+#include <private/qcolor_p.h>
+#include <qimage.h>
+#include <qmap.h>
+#include <qtextstream.h>
+#include <qvariant.h>
+
+#if defined(Q_CC_BOR)
+// needed for qsort() because of a std namespace problem on Borland
+#include "qplatformdefs.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+static quint64 xpmHash(const QString &str)
+{
+ unsigned int hashValue = 0;
+ for (int i = 0; i < str.size(); ++i) {
+ hashValue <<= 8;
+ hashValue += (unsigned int)str.at(i).unicode();
+ }
+ return hashValue;
+}
+static quint64 xpmHash(char *str)
+{
+ unsigned int hashValue = 0;
+ while (*str != '\0') {
+ hashValue <<= 8;
+ hashValue += (unsigned int)*str;
+ ++str;
+ }
+ return hashValue;
+}
+
+#ifdef QRGB
+#undef QRGB
+#endif
+#define QRGB(r,g,b) (r*65536 + g*256 + b)
+
+static const int xpmRgbTblSize = 657;
+
+static const struct XPMRGBData {
+ uint value;
+ const char *name;
+} xpmRgbTbl[] = {
+ { QRGB(240,248,255), "aliceblue" },
+ { QRGB(250,235,215), "antiquewhite" },
+ { QRGB(255,239,219), "antiquewhite1" },
+ { QRGB(238,223,204), "antiquewhite2" },
+ { QRGB(205,192,176), "antiquewhite3" },
+ { QRGB(139,131,120), "antiquewhite4" },
+ { QRGB(127,255,212), "aquamarine" },
+ { QRGB(127,255,212), "aquamarine1" },
+ { QRGB(118,238,198), "aquamarine2" },
+ { QRGB(102,205,170), "aquamarine3" },
+ { QRGB( 69,139,116), "aquamarine4" },
+ { QRGB(240,255,255), "azure" },
+ { QRGB(240,255,255), "azure1" },
+ { QRGB(224,238,238), "azure2" },
+ { QRGB(193,205,205), "azure3" },
+ { QRGB(131,139,139), "azure4" },
+ { QRGB(245,245,220), "beige" },
+ { QRGB(255,228,196), "bisque" },
+ { QRGB(255,228,196), "bisque1" },
+ { QRGB(238,213,183), "bisque2" },
+ { QRGB(205,183,158), "bisque3" },
+ { QRGB(139,125,107), "bisque4" },
+ { QRGB( 0, 0, 0), "black" },
+ { QRGB(255,235,205), "blanchedalmond" },
+ { QRGB( 0, 0,255), "blue" },
+ { QRGB( 0, 0,255), "blue1" },
+ { QRGB( 0, 0,238), "blue2" },
+ { QRGB( 0, 0,205), "blue3" },
+ { QRGB( 0, 0,139), "blue4" },
+ { QRGB(138, 43,226), "blueviolet" },
+ { QRGB(165, 42, 42), "brown" },
+ { QRGB(255, 64, 64), "brown1" },
+ { QRGB(238, 59, 59), "brown2" },
+ { QRGB(205, 51, 51), "brown3" },
+ { QRGB(139, 35, 35), "brown4" },
+ { QRGB(222,184,135), "burlywood" },
+ { QRGB(255,211,155), "burlywood1" },
+ { QRGB(238,197,145), "burlywood2" },
+ { QRGB(205,170,125), "burlywood3" },
+ { QRGB(139,115, 85), "burlywood4" },
+ { QRGB( 95,158,160), "cadetblue" },
+ { QRGB(152,245,255), "cadetblue1" },
+ { QRGB(142,229,238), "cadetblue2" },
+ { QRGB(122,197,205), "cadetblue3" },
+ { QRGB( 83,134,139), "cadetblue4" },
+ { QRGB(127,255, 0), "chartreuse" },
+ { QRGB(127,255, 0), "chartreuse1" },
+ { QRGB(118,238, 0), "chartreuse2" },
+ { QRGB(102,205, 0), "chartreuse3" },
+ { QRGB( 69,139, 0), "chartreuse4" },
+ { QRGB(210,105, 30), "chocolate" },
+ { QRGB(255,127, 36), "chocolate1" },
+ { QRGB(238,118, 33), "chocolate2" },
+ { QRGB(205,102, 29), "chocolate3" },
+ { QRGB(139, 69, 19), "chocolate4" },
+ { QRGB(255,127, 80), "coral" },
+ { QRGB(255,114, 86), "coral1" },
+ { QRGB(238,106, 80), "coral2" },
+ { QRGB(205, 91, 69), "coral3" },
+ { QRGB(139, 62, 47), "coral4" },
+ { QRGB(100,149,237), "cornflowerblue" },
+ { QRGB(255,248,220), "cornsilk" },
+ { QRGB(255,248,220), "cornsilk1" },
+ { QRGB(238,232,205), "cornsilk2" },
+ { QRGB(205,200,177), "cornsilk3" },
+ { QRGB(139,136,120), "cornsilk4" },
+ { QRGB( 0,255,255), "cyan" },
+ { QRGB( 0,255,255), "cyan1" },
+ { QRGB( 0,238,238), "cyan2" },
+ { QRGB( 0,205,205), "cyan3" },
+ { QRGB( 0,139,139), "cyan4" },
+ { QRGB( 0, 0,139), "darkblue" },
+ { QRGB( 0,139,139), "darkcyan" },
+ { QRGB(184,134, 11), "darkgoldenrod" },
+ { QRGB(255,185, 15), "darkgoldenrod1" },
+ { QRGB(238,173, 14), "darkgoldenrod2" },
+ { QRGB(205,149, 12), "darkgoldenrod3" },
+ { QRGB(139,101, 8), "darkgoldenrod4" },
+ { QRGB(169,169,169), "darkgray" },
+ { QRGB( 0,100, 0), "darkgreen" },
+ { QRGB(169,169,169), "darkgrey" },
+ { QRGB(189,183,107), "darkkhaki" },
+ { QRGB(139, 0,139), "darkmagenta" },
+ { QRGB( 85,107, 47), "darkolivegreen" },
+ { QRGB(202,255,112), "darkolivegreen1" },
+ { QRGB(188,238,104), "darkolivegreen2" },
+ { QRGB(162,205, 90), "darkolivegreen3" },
+ { QRGB(110,139, 61), "darkolivegreen4" },
+ { QRGB(255,140, 0), "darkorange" },
+ { QRGB(255,127, 0), "darkorange1" },
+ { QRGB(238,118, 0), "darkorange2" },
+ { QRGB(205,102, 0), "darkorange3" },
+ { QRGB(139, 69, 0), "darkorange4" },
+ { QRGB(153, 50,204), "darkorchid" },
+ { QRGB(191, 62,255), "darkorchid1" },
+ { QRGB(178, 58,238), "darkorchid2" },
+ { QRGB(154, 50,205), "darkorchid3" },
+ { QRGB(104, 34,139), "darkorchid4" },
+ { QRGB(139, 0, 0), "darkred" },
+ { QRGB(233,150,122), "darksalmon" },
+ { QRGB(143,188,143), "darkseagreen" },
+ { QRGB(193,255,193), "darkseagreen1" },
+ { QRGB(180,238,180), "darkseagreen2" },
+ { QRGB(155,205,155), "darkseagreen3" },
+ { QRGB(105,139,105), "darkseagreen4" },
+ { QRGB( 72, 61,139), "darkslateblue" },
+ { QRGB( 47, 79, 79), "darkslategray" },
+ { QRGB(151,255,255), "darkslategray1" },
+ { QRGB(141,238,238), "darkslategray2" },
+ { QRGB(121,205,205), "darkslategray3" },
+ { QRGB( 82,139,139), "darkslategray4" },
+ { QRGB( 47, 79, 79), "darkslategrey" },
+ { QRGB( 0,206,209), "darkturquoise" },
+ { QRGB(148, 0,211), "darkviolet" },
+ { QRGB(255, 20,147), "deeppink" },
+ { QRGB(255, 20,147), "deeppink1" },
+ { QRGB(238, 18,137), "deeppink2" },
+ { QRGB(205, 16,118), "deeppink3" },
+ { QRGB(139, 10, 80), "deeppink4" },
+ { QRGB( 0,191,255), "deepskyblue" },
+ { QRGB( 0,191,255), "deepskyblue1" },
+ { QRGB( 0,178,238), "deepskyblue2" },
+ { QRGB( 0,154,205), "deepskyblue3" },
+ { QRGB( 0,104,139), "deepskyblue4" },
+ { QRGB(105,105,105), "dimgray" },
+ { QRGB(105,105,105), "dimgrey" },
+ { QRGB( 30,144,255), "dodgerblue" },
+ { QRGB( 30,144,255), "dodgerblue1" },
+ { QRGB( 28,134,238), "dodgerblue2" },
+ { QRGB( 24,116,205), "dodgerblue3" },
+ { QRGB( 16, 78,139), "dodgerblue4" },
+ { QRGB(178, 34, 34), "firebrick" },
+ { QRGB(255, 48, 48), "firebrick1" },
+ { QRGB(238, 44, 44), "firebrick2" },
+ { QRGB(205, 38, 38), "firebrick3" },
+ { QRGB(139, 26, 26), "firebrick4" },
+ { QRGB(255,250,240), "floralwhite" },
+ { QRGB( 34,139, 34), "forestgreen" },
+ { QRGB(220,220,220), "gainsboro" },
+ { QRGB(248,248,255), "ghostwhite" },
+ { QRGB(255,215, 0), "gold" },
+ { QRGB(255,215, 0), "gold1" },
+ { QRGB(238,201, 0), "gold2" },
+ { QRGB(205,173, 0), "gold3" },
+ { QRGB(139,117, 0), "gold4" },
+ { QRGB(218,165, 32), "goldenrod" },
+ { QRGB(255,193, 37), "goldenrod1" },
+ { QRGB(238,180, 34), "goldenrod2" },
+ { QRGB(205,155, 29), "goldenrod3" },
+ { QRGB(139,105, 20), "goldenrod4" },
+ { QRGB(190,190,190), "gray" },
+ { QRGB( 0, 0, 0), "gray0" },
+ { QRGB( 3, 3, 3), "gray1" },
+ { QRGB( 26, 26, 26), "gray10" },
+ { QRGB(255,255,255), "gray100" },
+ { QRGB( 28, 28, 28), "gray11" },
+ { QRGB( 31, 31, 31), "gray12" },
+ { QRGB( 33, 33, 33), "gray13" },
+ { QRGB( 36, 36, 36), "gray14" },
+ { QRGB( 38, 38, 38), "gray15" },
+ { QRGB( 41, 41, 41), "gray16" },
+ { QRGB( 43, 43, 43), "gray17" },
+ { QRGB( 46, 46, 46), "gray18" },
+ { QRGB( 48, 48, 48), "gray19" },
+ { QRGB( 5, 5, 5), "gray2" },
+ { QRGB( 51, 51, 51), "gray20" },
+ { QRGB( 54, 54, 54), "gray21" },
+ { QRGB( 56, 56, 56), "gray22" },
+ { QRGB( 59, 59, 59), "gray23" },
+ { QRGB( 61, 61, 61), "gray24" },
+ { QRGB( 64, 64, 64), "gray25" },
+ { QRGB( 66, 66, 66), "gray26" },
+ { QRGB( 69, 69, 69), "gray27" },
+ { QRGB( 71, 71, 71), "gray28" },
+ { QRGB( 74, 74, 74), "gray29" },
+ { QRGB( 8, 8, 8), "gray3" },
+ { QRGB( 77, 77, 77), "gray30" },
+ { QRGB( 79, 79, 79), "gray31" },
+ { QRGB( 82, 82, 82), "gray32" },
+ { QRGB( 84, 84, 84), "gray33" },
+ { QRGB( 87, 87, 87), "gray34" },
+ { QRGB( 89, 89, 89), "gray35" },
+ { QRGB( 92, 92, 92), "gray36" },
+ { QRGB( 94, 94, 94), "gray37" },
+ { QRGB( 97, 97, 97), "gray38" },
+ { QRGB( 99, 99, 99), "gray39" },
+ { QRGB( 10, 10, 10), "gray4" },
+ { QRGB(102,102,102), "gray40" },
+ { QRGB(105,105,105), "gray41" },
+ { QRGB(107,107,107), "gray42" },
+ { QRGB(110,110,110), "gray43" },
+ { QRGB(112,112,112), "gray44" },
+ { QRGB(115,115,115), "gray45" },
+ { QRGB(117,117,117), "gray46" },
+ { QRGB(120,120,120), "gray47" },
+ { QRGB(122,122,122), "gray48" },
+ { QRGB(125,125,125), "gray49" },
+ { QRGB( 13, 13, 13), "gray5" },
+ { QRGB(127,127,127), "gray50" },
+ { QRGB(130,130,130), "gray51" },
+ { QRGB(133,133,133), "gray52" },
+ { QRGB(135,135,135), "gray53" },
+ { QRGB(138,138,138), "gray54" },
+ { QRGB(140,140,140), "gray55" },
+ { QRGB(143,143,143), "gray56" },
+ { QRGB(145,145,145), "gray57" },
+ { QRGB(148,148,148), "gray58" },
+ { QRGB(150,150,150), "gray59" },
+ { QRGB( 15, 15, 15), "gray6" },
+ { QRGB(153,153,153), "gray60" },
+ { QRGB(156,156,156), "gray61" },
+ { QRGB(158,158,158), "gray62" },
+ { QRGB(161,161,161), "gray63" },
+ { QRGB(163,163,163), "gray64" },
+ { QRGB(166,166,166), "gray65" },
+ { QRGB(168,168,168), "gray66" },
+ { QRGB(171,171,171), "gray67" },
+ { QRGB(173,173,173), "gray68" },
+ { QRGB(176,176,176), "gray69" },
+ { QRGB( 18, 18, 18), "gray7" },
+ { QRGB(179,179,179), "gray70" },
+ { QRGB(181,181,181), "gray71" },
+ { QRGB(184,184,184), "gray72" },
+ { QRGB(186,186,186), "gray73" },
+ { QRGB(189,189,189), "gray74" },
+ { QRGB(191,191,191), "gray75" },
+ { QRGB(194,194,194), "gray76" },
+ { QRGB(196,196,196), "gray77" },
+ { QRGB(199,199,199), "gray78" },
+ { QRGB(201,201,201), "gray79" },
+ { QRGB( 20, 20, 20), "gray8" },
+ { QRGB(204,204,204), "gray80" },
+ { QRGB(207,207,207), "gray81" },
+ { QRGB(209,209,209), "gray82" },
+ { QRGB(212,212,212), "gray83" },
+ { QRGB(214,214,214), "gray84" },
+ { QRGB(217,217,217), "gray85" },
+ { QRGB(219,219,219), "gray86" },
+ { QRGB(222,222,222), "gray87" },
+ { QRGB(224,224,224), "gray88" },
+ { QRGB(227,227,227), "gray89" },
+ { QRGB( 23, 23, 23), "gray9" },
+ { QRGB(229,229,229), "gray90" },
+ { QRGB(232,232,232), "gray91" },
+ { QRGB(235,235,235), "gray92" },
+ { QRGB(237,237,237), "gray93" },
+ { QRGB(240,240,240), "gray94" },
+ { QRGB(242,242,242), "gray95" },
+ { QRGB(245,245,245), "gray96" },
+ { QRGB(247,247,247), "gray97" },
+ { QRGB(250,250,250), "gray98" },
+ { QRGB(252,252,252), "gray99" },
+ { QRGB( 0,255, 0), "green" },
+ { QRGB( 0,255, 0), "green1" },
+ { QRGB( 0,238, 0), "green2" },
+ { QRGB( 0,205, 0), "green3" },
+ { QRGB( 0,139, 0), "green4" },
+ { QRGB(173,255, 47), "greenyellow" },
+ { QRGB(190,190,190), "grey" },
+ { QRGB( 0, 0, 0), "grey0" },
+ { QRGB( 3, 3, 3), "grey1" },
+ { QRGB( 26, 26, 26), "grey10" },
+ { QRGB(255,255,255), "grey100" },
+ { QRGB( 28, 28, 28), "grey11" },
+ { QRGB( 31, 31, 31), "grey12" },
+ { QRGB( 33, 33, 33), "grey13" },
+ { QRGB( 36, 36, 36), "grey14" },
+ { QRGB( 38, 38, 38), "grey15" },
+ { QRGB( 41, 41, 41), "grey16" },
+ { QRGB( 43, 43, 43), "grey17" },
+ { QRGB( 46, 46, 46), "grey18" },
+ { QRGB( 48, 48, 48), "grey19" },
+ { QRGB( 5, 5, 5), "grey2" },
+ { QRGB( 51, 51, 51), "grey20" },
+ { QRGB( 54, 54, 54), "grey21" },
+ { QRGB( 56, 56, 56), "grey22" },
+ { QRGB( 59, 59, 59), "grey23" },
+ { QRGB( 61, 61, 61), "grey24" },
+ { QRGB( 64, 64, 64), "grey25" },
+ { QRGB( 66, 66, 66), "grey26" },
+ { QRGB( 69, 69, 69), "grey27" },
+ { QRGB( 71, 71, 71), "grey28" },
+ { QRGB( 74, 74, 74), "grey29" },
+ { QRGB( 8, 8, 8), "grey3" },
+ { QRGB( 77, 77, 77), "grey30" },
+ { QRGB( 79, 79, 79), "grey31" },
+ { QRGB( 82, 82, 82), "grey32" },
+ { QRGB( 84, 84, 84), "grey33" },
+ { QRGB( 87, 87, 87), "grey34" },
+ { QRGB( 89, 89, 89), "grey35" },
+ { QRGB( 92, 92, 92), "grey36" },
+ { QRGB( 94, 94, 94), "grey37" },
+ { QRGB( 97, 97, 97), "grey38" },
+ { QRGB( 99, 99, 99), "grey39" },
+ { QRGB( 10, 10, 10), "grey4" },
+ { QRGB(102,102,102), "grey40" },
+ { QRGB(105,105,105), "grey41" },
+ { QRGB(107,107,107), "grey42" },
+ { QRGB(110,110,110), "grey43" },
+ { QRGB(112,112,112), "grey44" },
+ { QRGB(115,115,115), "grey45" },
+ { QRGB(117,117,117), "grey46" },
+ { QRGB(120,120,120), "grey47" },
+ { QRGB(122,122,122), "grey48" },
+ { QRGB(125,125,125), "grey49" },
+ { QRGB( 13, 13, 13), "grey5" },
+ { QRGB(127,127,127), "grey50" },
+ { QRGB(130,130,130), "grey51" },
+ { QRGB(133,133,133), "grey52" },
+ { QRGB(135,135,135), "grey53" },
+ { QRGB(138,138,138), "grey54" },
+ { QRGB(140,140,140), "grey55" },
+ { QRGB(143,143,143), "grey56" },
+ { QRGB(145,145,145), "grey57" },
+ { QRGB(148,148,148), "grey58" },
+ { QRGB(150,150,150), "grey59" },
+ { QRGB( 15, 15, 15), "grey6" },
+ { QRGB(153,153,153), "grey60" },
+ { QRGB(156,156,156), "grey61" },
+ { QRGB(158,158,158), "grey62" },
+ { QRGB(161,161,161), "grey63" },
+ { QRGB(163,163,163), "grey64" },
+ { QRGB(166,166,166), "grey65" },
+ { QRGB(168,168,168), "grey66" },
+ { QRGB(171,171,171), "grey67" },
+ { QRGB(173,173,173), "grey68" },
+ { QRGB(176,176,176), "grey69" },
+ { QRGB( 18, 18, 18), "grey7" },
+ { QRGB(179,179,179), "grey70" },
+ { QRGB(181,181,181), "grey71" },
+ { QRGB(184,184,184), "grey72" },
+ { QRGB(186,186,186), "grey73" },
+ { QRGB(189,189,189), "grey74" },
+ { QRGB(191,191,191), "grey75" },
+ { QRGB(194,194,194), "grey76" },
+ { QRGB(196,196,196), "grey77" },
+ { QRGB(199,199,199), "grey78" },
+ { QRGB(201,201,201), "grey79" },
+ { QRGB( 20, 20, 20), "grey8" },
+ { QRGB(204,204,204), "grey80" },
+ { QRGB(207,207,207), "grey81" },
+ { QRGB(209,209,209), "grey82" },
+ { QRGB(212,212,212), "grey83" },
+ { QRGB(214,214,214), "grey84" },
+ { QRGB(217,217,217), "grey85" },
+ { QRGB(219,219,219), "grey86" },
+ { QRGB(222,222,222), "grey87" },
+ { QRGB(224,224,224), "grey88" },
+ { QRGB(227,227,227), "grey89" },
+ { QRGB( 23, 23, 23), "grey9" },
+ { QRGB(229,229,229), "grey90" },
+ { QRGB(232,232,232), "grey91" },
+ { QRGB(235,235,235), "grey92" },
+ { QRGB(237,237,237), "grey93" },
+ { QRGB(240,240,240), "grey94" },
+ { QRGB(242,242,242), "grey95" },
+ { QRGB(245,245,245), "grey96" },
+ { QRGB(247,247,247), "grey97" },
+ { QRGB(250,250,250), "grey98" },
+ { QRGB(252,252,252), "grey99" },
+ { QRGB(240,255,240), "honeydew" },
+ { QRGB(240,255,240), "honeydew1" },
+ { QRGB(224,238,224), "honeydew2" },
+ { QRGB(193,205,193), "honeydew3" },
+ { QRGB(131,139,131), "honeydew4" },
+ { QRGB(255,105,180), "hotpink" },
+ { QRGB(255,110,180), "hotpink1" },
+ { QRGB(238,106,167), "hotpink2" },
+ { QRGB(205, 96,144), "hotpink3" },
+ { QRGB(139, 58, 98), "hotpink4" },
+ { QRGB(205, 92, 92), "indianred" },
+ { QRGB(255,106,106), "indianred1" },
+ { QRGB(238, 99, 99), "indianred2" },
+ { QRGB(205, 85, 85), "indianred3" },
+ { QRGB(139, 58, 58), "indianred4" },
+ { QRGB(255,255,240), "ivory" },
+ { QRGB(255,255,240), "ivory1" },
+ { QRGB(238,238,224), "ivory2" },
+ { QRGB(205,205,193), "ivory3" },
+ { QRGB(139,139,131), "ivory4" },
+ { QRGB(240,230,140), "khaki" },
+ { QRGB(255,246,143), "khaki1" },
+ { QRGB(238,230,133), "khaki2" },
+ { QRGB(205,198,115), "khaki3" },
+ { QRGB(139,134, 78), "khaki4" },
+ { QRGB(230,230,250), "lavender" },
+ { QRGB(255,240,245), "lavenderblush" },
+ { QRGB(255,240,245), "lavenderblush1" },
+ { QRGB(238,224,229), "lavenderblush2" },
+ { QRGB(205,193,197), "lavenderblush3" },
+ { QRGB(139,131,134), "lavenderblush4" },
+ { QRGB(124,252, 0), "lawngreen" },
+ { QRGB(255,250,205), "lemonchiffon" },
+ { QRGB(255,250,205), "lemonchiffon1" },
+ { QRGB(238,233,191), "lemonchiffon2" },
+ { QRGB(205,201,165), "lemonchiffon3" },
+ { QRGB(139,137,112), "lemonchiffon4" },
+ { QRGB(173,216,230), "lightblue" },
+ { QRGB(191,239,255), "lightblue1" },
+ { QRGB(178,223,238), "lightblue2" },
+ { QRGB(154,192,205), "lightblue3" },
+ { QRGB(104,131,139), "lightblue4" },
+ { QRGB(240,128,128), "lightcoral" },
+ { QRGB(224,255,255), "lightcyan" },
+ { QRGB(224,255,255), "lightcyan1" },
+ { QRGB(209,238,238), "lightcyan2" },
+ { QRGB(180,205,205), "lightcyan3" },
+ { QRGB(122,139,139), "lightcyan4" },
+ { QRGB(238,221,130), "lightgoldenrod" },
+ { QRGB(255,236,139), "lightgoldenrod1" },
+ { QRGB(238,220,130), "lightgoldenrod2" },
+ { QRGB(205,190,112), "lightgoldenrod3" },
+ { QRGB(139,129, 76), "lightgoldenrod4" },
+ { QRGB(250,250,210), "lightgoldenrodyellow" },
+ { QRGB(211,211,211), "lightgray" },
+ { QRGB(144,238,144), "lightgreen" },
+ { QRGB(211,211,211), "lightgrey" },
+ { QRGB(255,182,193), "lightpink" },
+ { QRGB(255,174,185), "lightpink1" },
+ { QRGB(238,162,173), "lightpink2" },
+ { QRGB(205,140,149), "lightpink3" },
+ { QRGB(139, 95,101), "lightpink4" },
+ { QRGB(255,160,122), "lightsalmon" },
+ { QRGB(255,160,122), "lightsalmon1" },
+ { QRGB(238,149,114), "lightsalmon2" },
+ { QRGB(205,129, 98), "lightsalmon3" },
+ { QRGB(139, 87, 66), "lightsalmon4" },
+ { QRGB( 32,178,170), "lightseagreen" },
+ { QRGB(135,206,250), "lightskyblue" },
+ { QRGB(176,226,255), "lightskyblue1" },
+ { QRGB(164,211,238), "lightskyblue2" },
+ { QRGB(141,182,205), "lightskyblue3" },
+ { QRGB( 96,123,139), "lightskyblue4" },
+ { QRGB(132,112,255), "lightslateblue" },
+ { QRGB(119,136,153), "lightslategray" },
+ { QRGB(119,136,153), "lightslategrey" },
+ { QRGB(176,196,222), "lightsteelblue" },
+ { QRGB(202,225,255), "lightsteelblue1" },
+ { QRGB(188,210,238), "lightsteelblue2" },
+ { QRGB(162,181,205), "lightsteelblue3" },
+ { QRGB(110,123,139), "lightsteelblue4" },
+ { QRGB(255,255,224), "lightyellow" },
+ { QRGB(255,255,224), "lightyellow1" },
+ { QRGB(238,238,209), "lightyellow2" },
+ { QRGB(205,205,180), "lightyellow3" },
+ { QRGB(139,139,122), "lightyellow4" },
+ { QRGB( 50,205, 50), "limegreen" },
+ { QRGB(250,240,230), "linen" },
+ { QRGB(255, 0,255), "magenta" },
+ { QRGB(255, 0,255), "magenta1" },
+ { QRGB(238, 0,238), "magenta2" },
+ { QRGB(205, 0,205), "magenta3" },
+ { QRGB(139, 0,139), "magenta4" },
+ { QRGB(176, 48, 96), "maroon" },
+ { QRGB(255, 52,179), "maroon1" },
+ { QRGB(238, 48,167), "maroon2" },
+ { QRGB(205, 41,144), "maroon3" },
+ { QRGB(139, 28, 98), "maroon4" },
+ { QRGB(102,205,170), "mediumaquamarine" },
+ { QRGB( 0, 0,205), "mediumblue" },
+ { QRGB(186, 85,211), "mediumorchid" },
+ { QRGB(224,102,255), "mediumorchid1" },
+ { QRGB(209, 95,238), "mediumorchid2" },
+ { QRGB(180, 82,205), "mediumorchid3" },
+ { QRGB(122, 55,139), "mediumorchid4" },
+ { QRGB(147,112,219), "mediumpurple" },
+ { QRGB(171,130,255), "mediumpurple1" },
+ { QRGB(159,121,238), "mediumpurple2" },
+ { QRGB(137,104,205), "mediumpurple3" },
+ { QRGB( 93, 71,139), "mediumpurple4" },
+ { QRGB( 60,179,113), "mediumseagreen" },
+ { QRGB(123,104,238), "mediumslateblue" },
+ { QRGB( 0,250,154), "mediumspringgreen" },
+ { QRGB( 72,209,204), "mediumturquoise" },
+ { QRGB(199, 21,133), "mediumvioletred" },
+ { QRGB( 25, 25,112), "midnightblue" },
+ { QRGB(245,255,250), "mintcream" },
+ { QRGB(255,228,225), "mistyrose" },
+ { QRGB(255,228,225), "mistyrose1" },
+ { QRGB(238,213,210), "mistyrose2" },
+ { QRGB(205,183,181), "mistyrose3" },
+ { QRGB(139,125,123), "mistyrose4" },
+ { QRGB(255,228,181), "moccasin" },
+ { QRGB(255,222,173), "navajowhite" },
+ { QRGB(255,222,173), "navajowhite1" },
+ { QRGB(238,207,161), "navajowhite2" },
+ { QRGB(205,179,139), "navajowhite3" },
+ { QRGB(139,121, 94), "navajowhite4" },
+ { QRGB( 0, 0,128), "navy" },
+ { QRGB( 0, 0,128), "navyblue" },
+ { QRGB(253,245,230), "oldlace" },
+ { QRGB(107,142, 35), "olivedrab" },
+ { QRGB(192,255, 62), "olivedrab1" },
+ { QRGB(179,238, 58), "olivedrab2" },
+ { QRGB(154,205, 50), "olivedrab3" },
+ { QRGB(105,139, 34), "olivedrab4" },
+ { QRGB(255,165, 0), "orange" },
+ { QRGB(255,165, 0), "orange1" },
+ { QRGB(238,154, 0), "orange2" },
+ { QRGB(205,133, 0), "orange3" },
+ { QRGB(139, 90, 0), "orange4" },
+ { QRGB(255, 69, 0), "orangered" },
+ { QRGB(255, 69, 0), "orangered1" },
+ { QRGB(238, 64, 0), "orangered2" },
+ { QRGB(205, 55, 0), "orangered3" },
+ { QRGB(139, 37, 0), "orangered4" },
+ { QRGB(218,112,214), "orchid" },
+ { QRGB(255,131,250), "orchid1" },
+ { QRGB(238,122,233), "orchid2" },
+ { QRGB(205,105,201), "orchid3" },
+ { QRGB(139, 71,137), "orchid4" },
+ { QRGB(238,232,170), "palegoldenrod" },
+ { QRGB(152,251,152), "palegreen" },
+ { QRGB(154,255,154), "palegreen1" },
+ { QRGB(144,238,144), "palegreen2" },
+ { QRGB(124,205,124), "palegreen3" },
+ { QRGB( 84,139, 84), "palegreen4" },
+ { QRGB(175,238,238), "paleturquoise" },
+ { QRGB(187,255,255), "paleturquoise1" },
+ { QRGB(174,238,238), "paleturquoise2" },
+ { QRGB(150,205,205), "paleturquoise3" },
+ { QRGB(102,139,139), "paleturquoise4" },
+ { QRGB(219,112,147), "palevioletred" },
+ { QRGB(255,130,171), "palevioletred1" },
+ { QRGB(238,121,159), "palevioletred2" },
+ { QRGB(205,104,137), "palevioletred3" },
+ { QRGB(139, 71, 93), "palevioletred4" },
+ { QRGB(255,239,213), "papayawhip" },
+ { QRGB(255,218,185), "peachpuff" },
+ { QRGB(255,218,185), "peachpuff1" },
+ { QRGB(238,203,173), "peachpuff2" },
+ { QRGB(205,175,149), "peachpuff3" },
+ { QRGB(139,119,101), "peachpuff4" },
+ { QRGB(205,133, 63), "peru" },
+ { QRGB(255,192,203), "pink" },
+ { QRGB(255,181,197), "pink1" },
+ { QRGB(238,169,184), "pink2" },
+ { QRGB(205,145,158), "pink3" },
+ { QRGB(139, 99,108), "pink4" },
+ { QRGB(221,160,221), "plum" },
+ { QRGB(255,187,255), "plum1" },
+ { QRGB(238,174,238), "plum2" },
+ { QRGB(205,150,205), "plum3" },
+ { QRGB(139,102,139), "plum4" },
+ { QRGB(176,224,230), "powderblue" },
+ { QRGB(160, 32,240), "purple" },
+ { QRGB(155, 48,255), "purple1" },
+ { QRGB(145, 44,238), "purple2" },
+ { QRGB(125, 38,205), "purple3" },
+ { QRGB( 85, 26,139), "purple4" },
+ { QRGB(255, 0, 0), "red" },
+ { QRGB(255, 0, 0), "red1" },
+ { QRGB(238, 0, 0), "red2" },
+ { QRGB(205, 0, 0), "red3" },
+ { QRGB(139, 0, 0), "red4" },
+ { QRGB(188,143,143), "rosybrown" },
+ { QRGB(255,193,193), "rosybrown1" },
+ { QRGB(238,180,180), "rosybrown2" },
+ { QRGB(205,155,155), "rosybrown3" },
+ { QRGB(139,105,105), "rosybrown4" },
+ { QRGB( 65,105,225), "royalblue" },
+ { QRGB( 72,118,255), "royalblue1" },
+ { QRGB( 67,110,238), "royalblue2" },
+ { QRGB( 58, 95,205), "royalblue3" },
+ { QRGB( 39, 64,139), "royalblue4" },
+ { QRGB(139, 69, 19), "saddlebrown" },
+ { QRGB(250,128,114), "salmon" },
+ { QRGB(255,140,105), "salmon1" },
+ { QRGB(238,130, 98), "salmon2" },
+ { QRGB(205,112, 84), "salmon3" },
+ { QRGB(139, 76, 57), "salmon4" },
+ { QRGB(244,164, 96), "sandybrown" },
+ { QRGB( 46,139, 87), "seagreen" },
+ { QRGB( 84,255,159), "seagreen1" },
+ { QRGB( 78,238,148), "seagreen2" },
+ { QRGB( 67,205,128), "seagreen3" },
+ { QRGB( 46,139, 87), "seagreen4" },
+ { QRGB(255,245,238), "seashell" },
+ { QRGB(255,245,238), "seashell1" },
+ { QRGB(238,229,222), "seashell2" },
+ { QRGB(205,197,191), "seashell3" },
+ { QRGB(139,134,130), "seashell4" },
+ { QRGB(160, 82, 45), "sienna" },
+ { QRGB(255,130, 71), "sienna1" },
+ { QRGB(238,121, 66), "sienna2" },
+ { QRGB(205,104, 57), "sienna3" },
+ { QRGB(139, 71, 38), "sienna4" },
+ { QRGB(135,206,235), "skyblue" },
+ { QRGB(135,206,255), "skyblue1" },
+ { QRGB(126,192,238), "skyblue2" },
+ { QRGB(108,166,205), "skyblue3" },
+ { QRGB( 74,112,139), "skyblue4" },
+ { QRGB(106, 90,205), "slateblue" },
+ { QRGB(131,111,255), "slateblue1" },
+ { QRGB(122,103,238), "slateblue2" },
+ { QRGB(105, 89,205), "slateblue3" },
+ { QRGB( 71, 60,139), "slateblue4" },
+ { QRGB(112,128,144), "slategray" },
+ { QRGB(198,226,255), "slategray1" },
+ { QRGB(185,211,238), "slategray2" },
+ { QRGB(159,182,205), "slategray3" },
+ { QRGB(108,123,139), "slategray4" },
+ { QRGB(112,128,144), "slategrey" },
+ { QRGB(255,250,250), "snow" },
+ { QRGB(255,250,250), "snow1" },
+ { QRGB(238,233,233), "snow2" },
+ { QRGB(205,201,201), "snow3" },
+ { QRGB(139,137,137), "snow4" },
+ { QRGB( 0,255,127), "springgreen" },
+ { QRGB( 0,255,127), "springgreen1" },
+ { QRGB( 0,238,118), "springgreen2" },
+ { QRGB( 0,205,102), "springgreen3" },
+ { QRGB( 0,139, 69), "springgreen4" },
+ { QRGB( 70,130,180), "steelblue" },
+ { QRGB( 99,184,255), "steelblue1" },
+ { QRGB( 92,172,238), "steelblue2" },
+ { QRGB( 79,148,205), "steelblue3" },
+ { QRGB( 54,100,139), "steelblue4" },
+ { QRGB(210,180,140), "tan" },
+ { QRGB(255,165, 79), "tan1" },
+ { QRGB(238,154, 73), "tan2" },
+ { QRGB(205,133, 63), "tan3" },
+ { QRGB(139, 90, 43), "tan4" },
+ { QRGB(216,191,216), "thistle" },
+ { QRGB(255,225,255), "thistle1" },
+ { QRGB(238,210,238), "thistle2" },
+ { QRGB(205,181,205), "thistle3" },
+ { QRGB(139,123,139), "thistle4" },
+ { QRGB(255, 99, 71), "tomato" },
+ { QRGB(255, 99, 71), "tomato1" },
+ { QRGB(238, 92, 66), "tomato2" },
+ { QRGB(205, 79, 57), "tomato3" },
+ { QRGB(139, 54, 38), "tomato4" },
+ { QRGB( 64,224,208), "turquoise" },
+ { QRGB( 0,245,255), "turquoise1" },
+ { QRGB( 0,229,238), "turquoise2" },
+ { QRGB( 0,197,205), "turquoise3" },
+ { QRGB( 0,134,139), "turquoise4" },
+ { QRGB(238,130,238), "violet" },
+ { QRGB(208, 32,144), "violetred" },
+ { QRGB(255, 62,150), "violetred1" },
+ { QRGB(238, 58,140), "violetred2" },
+ { QRGB(205, 50,120), "violetred3" },
+ { QRGB(139, 34, 82), "violetred4" },
+ { QRGB(245,222,179), "wheat" },
+ { QRGB(255,231,186), "wheat1" },
+ { QRGB(238,216,174), "wheat2" },
+ { QRGB(205,186,150), "wheat3" },
+ { QRGB(139,126,102), "wheat4" },
+ { QRGB(255,255,255), "white" },
+ { QRGB(245,245,245), "whitesmoke" },
+ { QRGB(255,255, 0), "yellow" },
+ { QRGB(255,255, 0), "yellow1" },
+ { QRGB(238,238, 0), "yellow2" },
+ { QRGB(205,205, 0), "yellow3" },
+ { QRGB(139,139, 0), "yellow4" },
+ { QRGB(154,205, 50), "yellowgreen" } };
+
+inline bool operator<(const char *name, const XPMRGBData &data)
+{ return qstrcmp(name, data.name) < 0; }
+inline bool operator<(const XPMRGBData &data, const char *name)
+{ return qstrcmp(data.name, name) < 0; }
+
+static inline bool qt_get_named_xpm_rgb(const char *name_no_space, QRgb *rgb)
+{
+ const XPMRGBData *r = qBinaryFind(xpmRgbTbl, xpmRgbTbl + xpmRgbTblSize, name_no_space);
+ if (r != xpmRgbTbl + xpmRgbTblSize) {
+ *rgb = r->value;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+/*****************************************************************************
+ Misc. utility functions
+ *****************************************************************************/
+static QString fbname(const QString &fileName) // get file basename (sort of)
+{
+ QString s = fileName;
+ if (!s.isEmpty()) {
+ int i;
+ if ((i = s.lastIndexOf(QLatin1Char('/'))) >= 0)
+ s = s.mid(i);
+ if ((i = s.lastIndexOf(QLatin1Char('\\'))) >= 0)
+ s = s.mid(i);
+ QRegExp r(QLatin1String("[a-zA-Z][a-zA-Z0-9_]*"));
+ int p = r.indexIn(s);
+ if (p == -1)
+ s.clear();
+ else
+ s = s.mid(p, r.matchedLength());
+ }
+ if (s.isEmpty())
+ s = QString::fromLatin1("dummy");
+ return s;
+}
+
+// Skip until ", read until the next ", return the rest in *buf
+// Returns false on error, true on success
+
+static bool read_xpm_string(QByteArray &buf, QIODevice *d, const char * const *source, int &index,
+ QByteArray &state)
+{
+ if (source) {
+ buf = source[index++];
+ return true;
+ }
+
+ buf = "";
+ bool gotQuote = false;
+ int offset = 0;
+ forever {
+ if (offset == state.size() || state.isEmpty()) {
+ char buf[2048];
+ qint64 bytesRead = d->read(buf, sizeof(buf));
+ if (bytesRead <= 0)
+ return false;
+ state = QByteArray(buf, int(bytesRead));
+ offset = 0;
+ }
+
+ if (!gotQuote) {
+ if (state.at(offset++) == '"')
+ gotQuote = true;
+ } else {
+ char c = state.at(offset++);
+ if (c == '"')
+ break;
+ buf += c;
+ }
+ }
+ state.remove(0, offset);
+ return true;
+}
+
+// Tests if the given prefix can be the start of an XPM color specification
+
+static bool is_xpm_color_spec_prefix(const QByteArray& prefix)
+{
+ return prefix == "c" ||
+ prefix == "g" ||
+ prefix == "g4" ||
+ prefix == "m" ||
+ prefix == "s";
+}
+
+// Reads XPM header.
+
+static bool read_xpm_header(
+ QIODevice *device, const char * const * source, int& index, QByteArray &state,
+ int *cpp, int *ncols, int *w, int *h)
+{
+ QByteArray buf(200, 0);
+
+ if (!read_xpm_string(buf, device, source, index, state))
+ return false;
+
+#if defined(_MSC_VER) && _MSC_VER >= 1400 && !defined(Q_OS_WINCE)
+ if (sscanf_s(buf, "%d %d %d %d", w, h, ncols, cpp) < 4)
+#else
+ if (sscanf(buf, "%d %d %d %d", w, h, ncols, cpp) < 4)
+#endif
+ return false; // < 4 numbers parsed
+
+ return true;
+}
+
+// Reads XPM body (color information & pixels).
+
+static bool read_xpm_body(
+ QIODevice *device, const char * const * source, int& index, QByteArray& state,
+ int cpp, int ncols, int w, int h, QImage& image)
+{
+ QByteArray buf(200, 0);
+ int i;
+
+ if (cpp < 0 || cpp > 15)
+ return false;
+
+ // For > 256 colors, we delay creation of the image until
+ // after we have read the color specifications, so that we can
+ // create it in correct format (Format_RGB32 vs Format_ARGB32,
+ // depending on absence or presence of "c none", respectively)
+ if (ncols <= 256) {
+ if (image.size() != QSize(w, h) || image.format() != QImage::Format_Indexed8) {
+ image = QImage(w, h, QImage::Format_Indexed8);
+ if (image.isNull())
+ return false;
+ }
+ image.setColorCount(ncols);
+ }
+
+ QMap<quint64, int> colorMap;
+ int currentColor;
+ bool hasTransparency = false;
+
+ for(currentColor=0; currentColor < ncols; ++currentColor) {
+ if (!read_xpm_string(buf, device, source, index, state)) {
+ qWarning("QImage: XPM color specification missing");
+ return false;
+ }
+ QByteArray index;
+ index = buf.left(cpp);
+ buf = buf.mid(cpp).simplified().trimmed().toLower();
+ QList<QByteArray> tokens = buf.split(' ');
+ i = tokens.indexOf("c");
+ if (i < 0)
+ i = tokens.indexOf("g");
+ if (i < 0)
+ i = tokens.indexOf("g4");
+ if (i < 0)
+ i = tokens.indexOf("m");
+ if (i < 0) {
+ qWarning("QImage: XPM color specification is missing: %s", buf.constData());
+ return false; // no c/g/g4/m specification at all
+ }
+ QByteArray color;
+ while ((++i < tokens.size()) && !is_xpm_color_spec_prefix(tokens.at(i))) {
+ color.append(tokens.at(i));
+ }
+ if (color.isEmpty()) {
+ qWarning("QImage: XPM color value is missing from specification: %s", buf.constData());
+ return false; // no color value
+ }
+ buf = color;
+ if (buf == "none") {
+ hasTransparency = true;
+ int transparentColor = currentColor;
+ if (ncols <= 256) {
+ image.setColor(transparentColor, 0);
+ colorMap.insert(xpmHash(QLatin1String(index.constData())), transparentColor);
+ } else {
+ colorMap.insert(xpmHash(QLatin1String(index.constData())), 0);
+ }
+ } else {
+ QRgb c_rgb;
+ if (((buf.length()-1) % 3) && (buf[0] == '#')) {
+ buf.truncate(((buf.length()-1) / 4 * 3) + 1); // remove alpha channel left by imagemagick
+ }
+ if (buf[0] == '#') {
+ qt_get_hex_rgb(buf, &c_rgb);
+ } else {
+ qt_get_named_xpm_rgb(buf, &c_rgb);
+ }
+ if (ncols <= 256) {
+ image.setColor(currentColor, 0xff000000 | c_rgb);
+ colorMap.insert(xpmHash(QLatin1String(index.constData())), currentColor);
+ } else {
+ colorMap.insert(xpmHash(QLatin1String(index.constData())), 0xff000000 | c_rgb);
+ }
+ }
+ }
+
+ if (ncols > 256) {
+ // Now we can create 32-bit image of appropriate format
+ QImage::Format format = hasTransparency ?
+ QImage::Format_ARGB32 : QImage::Format_RGB32;
+ if (image.size() != QSize(w, h) || image.format() != format) {
+ image = QImage(w, h, format);
+ if (image.isNull())
+ return false;
+ }
+ }
+
+ // Read pixels
+ for(int y=0; y<h; y++) {
+ if (!read_xpm_string(buf, device, source, index, state)) {
+ qWarning("QImage: XPM pixels missing on image line %d", y);
+ return false;
+ }
+ if (image.depth() == 8) {
+ uchar *p = image.scanLine(y);
+ uchar *d = (uchar *)buf.data();
+ uchar *end = d + buf.length();
+ int x;
+ if (cpp == 1) {
+ char b[2];
+ b[1] = '\0';
+ for (x=0; x<w && d<end; x++) {
+ b[0] = *d++;
+ *p++ = (uchar)colorMap[xpmHash(b)];
+ }
+ } else {
+ char b[16];
+ b[cpp] = '\0';
+ for (x=0; x<w && d<end; x++) {
+ memcpy(b, (char *)d, cpp);
+ *p++ = (uchar)colorMap[xpmHash(b)];
+ d += cpp;
+ }
+ }
+ // avoid uninitialized memory for malformed xpms
+ if (x < w) {
+ qWarning("QImage: XPM pixels missing on image line %d (possibly a C++ trigraph).", y);
+ memset(p, 0, w - x);
+ }
+ } else {
+ QRgb *p = (QRgb*)image.scanLine(y);
+ uchar *d = (uchar *)buf.data();
+ uchar *end = d + buf.length();
+ int x;
+ char b[16];
+ b[cpp] = '\0';
+ for (x=0; x<w && d<end; x++) {
+ memcpy(b, (char *)d, cpp);
+ *p++ = (QRgb)colorMap[xpmHash(b)];
+ d += cpp;
+ }
+ // avoid uninitialized memory for malformed xpms
+ if (x < w) {
+ qWarning("QImage: XPM pixels missing on image line %d (possibly a C++ trigraph).", y);
+ memset(p, 0, (w - x)*4);
+ }
+ }
+ }
+
+ if (device) {
+ // Rewind unused characters, and skip to the end of the XPM struct.
+ for (int i = state.size() - 1; i >= 0; --i)
+ device->ungetChar(state[i]);
+ char c;
+ while (device->getChar(&c) && c != ';') {}
+ while (device->getChar(&c) && c != '\n') {}
+ }
+ return true;
+}
+
+//
+// INTERNAL
+//
+// Reads an .xpm from either the QImageIO or from the QString *.
+// One of the two HAS to be 0, the other one is used.
+//
+
+bool qt_read_xpm_image_or_array(QIODevice *device, const char * const * source, QImage &image)
+{
+ if (!source)
+ return true;
+
+ QByteArray buf(200, 0);
+ QByteArray state;
+
+ int cpp, ncols, w, h, index = 0;
+
+ if (device) {
+ // "/* XPM */"
+ int readBytes;
+ if ((readBytes = device->readLine(buf.data(), buf.size())) < 0)
+ return false;
+
+ if (buf.indexOf("/* XPM") != 0) {
+ while (readBytes > 0) {
+ device->ungetChar(buf.at(readBytes - 1));
+ --readBytes;
+ }
+ return false;
+ }// bad magic
+ }
+
+ if (!read_xpm_header(device, source, index, state, &cpp, &ncols, &w, &h))
+ return false;
+
+ return read_xpm_body(device, source, index, state, cpp, ncols, w, h, image);
+}
+
+static const char* xpm_color_name(int cpp, int index)
+{
+ static char returnable[5];
+ static const char code[] = ".#abcdefghijklmnopqrstuvwxyzABCD"
+ "EFGHIJKLMNOPQRSTUVWXYZ0123456789";
+ // cpp is limited to 4 and index is limited to 64^cpp
+ if (cpp > 1) {
+ if (cpp > 2) {
+ if (cpp > 3) {
+ returnable[3] = code[index % 64];
+ index /= 64;
+ } else
+ returnable[3] = '\0';
+ returnable[2] = code[index % 64];
+ index /= 64;
+ } else
+ returnable[2] = '\0';
+ // the following 4 lines are a joke!
+ if (index == 0)
+ index = 64*44+21;
+ else if (index == 64*44+21)
+ index = 0;
+ returnable[1] = code[index % 64];
+ index /= 64;
+ } else
+ returnable[1] = '\0';
+ returnable[0] = code[index];
+
+ return returnable;
+}
+
+
+// write XPM image data
+static bool write_xpm_image(const QImage &sourceImage, QIODevice *device, const QString &fileName)
+{
+ if (!device->isWritable())
+ return false;
+
+ QImage image;
+ if (sourceImage.depth() != 32)
+ image = sourceImage.convertToFormat(QImage::Format_RGB32);
+ else
+ image = sourceImage;
+
+ QMap<QRgb, int> colorMap;
+
+ int w = image.width(), h = image.height(), ncolors = 0;
+ int x, y;
+
+ // build color table
+ for(y=0; y<h; y++) {
+ QRgb * yp = (QRgb *)image.scanLine(y);
+ for(x=0; x<w; x++) {
+ QRgb color = *(yp + x);
+ if (!colorMap.contains(color))
+ colorMap.insert(color, ncolors++);
+ }
+ }
+
+ // number of 64-bit characters per pixel needed to encode all colors
+ int cpp = 1;
+ for (int k = 64; ncolors > k; k *= 64) {
+ ++cpp;
+ // limit to 4 characters per pixel
+ // 64^4 colors is enough for a 4096x4096 image
+ if (cpp > 4)
+ break;
+ }
+
+ QString line;
+
+ // write header
+ QTextStream s(device);
+ s << "/* XPM */" << endl
+ << "static char *" << fbname(fileName) << "[]={" << endl
+ << '\"' << w << ' ' << h << ' ' << ncolors << ' ' << cpp << '\"';
+
+ // write palette
+ QMap<QRgb, int>::Iterator c = colorMap.begin();
+ while (c != colorMap.end()) {
+ QRgb color = c.key();
+ if (image.format() != QImage::Format_RGB32 && !qAlpha(color))
+ line.sprintf("\"%s c None\"",
+ xpm_color_name(cpp, *c));
+ else
+ line.sprintf("\"%s c #%02x%02x%02x\"",
+ xpm_color_name(cpp, *c),
+ qRed(color),
+ qGreen(color),
+ qBlue(color));
+ ++c;
+ s << ',' << endl << line;
+ }
+
+ // write pixels, limit to 4 characters per pixel
+ line.truncate(cpp*w);
+ for(y=0; y<h; y++) {
+ QRgb * yp = (QRgb *) image.scanLine(y);
+ int cc = 0;
+ for(x=0; x<w; x++) {
+ int color = (int)(*(yp + x));
+ QByteArray chars(xpm_color_name(cpp, colorMap[color]));
+ line[cc++] = QLatin1Char(chars[0]);
+ if (cpp > 1) {
+ line[cc++] = QLatin1Char(chars[1]);
+ if (cpp > 2) {
+ line[cc++] = QLatin1Char(chars[2]);
+ if (cpp > 3) {
+ line[cc++] = QLatin1Char(chars[3]);
+ }
+ }
+ }
+ }
+ s << ',' << endl << '\"' << line << '\"';
+ }
+ s << "};" << endl;
+ return (s.status() == QTextStream::Ok);
+}
+
+QXpmHandler::QXpmHandler()
+ : state(Ready), index(0)
+{
+}
+
+bool QXpmHandler::readHeader()
+{
+ state = Error;
+ if (!read_xpm_header(device(), 0, index, buffer, &cpp, &ncols, &width, &height))
+ return false;
+ state = ReadHeader;
+ return true;
+}
+
+bool QXpmHandler::readImage(QImage *image)
+{
+ if (state == Error)
+ return false;
+
+ if (state == Ready && !readHeader()) {
+ state = Error;
+ return false;
+ }
+
+ if (!read_xpm_body(device(), 0, index, buffer, cpp, ncols, width, height, *image)) {
+ state = Error;
+ return false;
+ }
+
+ state = Ready;
+ return true;
+}
+
+bool QXpmHandler::canRead() const
+{
+ if (state == Ready && !canRead(device()))
+ return false;
+
+ if (state != Error) {
+ setFormat("xpm");
+ return true;
+ }
+
+ return false;
+}
+
+bool QXpmHandler::canRead(QIODevice *device)
+{
+ if (!device) {
+ qWarning("QXpmHandler::canRead() called with no device");
+ return false;
+ }
+
+ char head[6];
+ if (device->peek(head, sizeof(head)) != sizeof(head))
+ return false;
+
+ return qstrncmp(head, "/* XPM", 6) == 0;
+}
+
+bool QXpmHandler::read(QImage *image)
+{
+ if (!canRead())
+ return false;
+ return readImage(image);
+}
+
+bool QXpmHandler::write(const QImage &image)
+{
+ return write_xpm_image(image, device(), fileName);
+}
+
+bool QXpmHandler::supportsOption(ImageOption option) const
+{
+ return option == Name
+ || option == Size
+ || option == ImageFormat;
+}
+
+QVariant QXpmHandler::option(ImageOption option) const
+{
+ if (option == Name) {
+ return fileName;
+ } else if (option == Size) {
+ if (state == Error)
+ return QVariant();
+ if (state == Ready && !const_cast<QXpmHandler*>(this)->readHeader())
+ return QVariant();
+ return QSize(width, height);
+ } else if (option == ImageFormat) {
+ if (state == Error)
+ return QVariant();
+ if (state == Ready && !const_cast<QXpmHandler*>(this)->readHeader())
+ return QVariant();
+ // If we have more than 256 colors in the table, we need to
+ // figure out, if it contains transparency. That means reading
+ // the whole color table, which is too much work work pre-checking
+ // the image format
+ if (ncols <= 256)
+ return QImage::Format_Indexed8;
+ else
+ return QImage::Format_Invalid;
+ }
+
+ return QVariant();
+}
+
+void QXpmHandler::setOption(ImageOption option, const QVariant &value)
+{
+ if (option == Name)
+ fileName = value.toString();
+}
+
+QByteArray QXpmHandler::name() const
+{
+ return "xpm";
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_IMAGEFORMAT_XPM
diff --git a/src/gui/image/qxpmhandler_p.h b/src/gui/image/qxpmhandler_p.h
new file mode 100644
index 0000000000..fe2e9eab68
--- /dev/null
+++ b/src/gui/image/qxpmhandler_p.h
@@ -0,0 +1,100 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QXPMHANDLER_P_H
+#define QXPMHANDLER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the QLibrary class. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtGui/qimageiohandler.h"
+
+#ifndef QT_NO_IMAGEFORMAT_XPM
+
+QT_BEGIN_NAMESPACE
+
+class QXpmHandler : public QImageIOHandler
+{
+public:
+ QXpmHandler();
+ bool canRead() const;
+ bool read(QImage *image);
+ bool write(const QImage &image);
+
+ static bool canRead(QIODevice *device);
+
+ QByteArray name() const;
+
+ QVariant option(ImageOption option) const;
+ void setOption(ImageOption option, const QVariant &value);
+ bool supportsOption(ImageOption option) const;
+
+private:
+ bool readHeader();
+ bool readImage(QImage *image);
+ enum State {
+ Ready,
+ ReadHeader,
+ Error
+ };
+ State state;
+ int width;
+ int height;
+ int ncols;
+ int cpp;
+ QByteArray buffer;
+ int index;
+ QString fileName;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_IMAGEFORMAT_XPM
+
+#endif // QXPMHANDLER_P_H