summaryrefslogtreecommitdiffstats
path: root/src/gui/image
diff options
context:
space:
mode:
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