diff options
author | Lars Knoll <lars.knoll@nokia.com> | 2009-03-23 09:34:13 (GMT) |
---|---|---|
committer | Simon Hausmann <simon.hausmann@nokia.com> | 2009-03-23 09:34:13 (GMT) |
commit | 67ad0519fd165acee4a4d2a94fa502e9e4847bd0 (patch) | |
tree | 1dbf50b3dff8d5ca7e9344733968c72704eb15ff /src/gui/image | |
download | Qt-67ad0519fd165acee4a4d2a94fa502e9e4847bd0.zip Qt-67ad0519fd165acee4a4d2a94fa502e9e4847bd0.tar.gz Qt-67ad0519fd165acee4a4d2a94fa502e9e4847bd0.tar.bz2 |
Long live Qt!
Diffstat (limited to 'src/gui/image')
57 files changed, 30847 insertions, 0 deletions
diff --git a/src/gui/image/image.pri b/src/gui/image/image.pri new file mode 100644 index 0000000..ca52974 --- /dev/null +++ b/src/gui/image/image.pri @@ -0,0 +1,108 @@ +# -*-mode:sh-*- +# Qt image handling + +# Qt kernel module + +HEADERS += \ + image/qbitmap.h \ + image/qicon.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/qpixmapcache.h \ + image/qpixmapdata_p.h \ + image/qpixmapdatafactory_p.h \ + image/qpixmapfilter_p.h + +SOURCES += \ + image/qbitmap.cpp \ + image/qicon.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/qnativeimage.cpp \ + +win32 { + SOURCES += image/qpixmap_win.cpp +} +embedded { + SOURCES += image/qpixmap_qws.cpp +} +x11 { + HEADERS += image/qpixmap_x11_p.h + SOURCES += image/qpixmap_x11.cpp +} +mac { + HEADERS += image/qpixmap_mac_p.h + SOURCES += image/qpixmap_mac.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 + +# 3rd party / system PNG support +!contains(QT_CONFIG, no-png) { + HEADERS += image/qpnghandler_p.h + SOURCES += image/qpnghandler.cpp + + contains(QT_CONFIG, system-png) { + unix:LIBS += -lpng + win32:LIBS += libpng.lib + } else { + !isEqual(QT_ARCH, i386):!isEqual(QT_ARCH, x86_64):DEFINES += PNG_NO_ASSEMBLER_CODE + INCLUDEPATH += ../3rdparty/libpng ../3rdparty/zlib + SOURCES += ../3rdparty/libpng/png.c \ + ../3rdparty/libpng/pngerror.c \ + ../3rdparty/libpng/pngget.c \ + ../3rdparty/libpng/pngmem.c \ + ../3rdparty/libpng/pngpread.c \ + ../3rdparty/libpng/pngread.c \ + ../3rdparty/libpng/pngrio.c \ + ../3rdparty/libpng/pngrtran.c \ + ../3rdparty/libpng/pngrutil.c \ + ../3rdparty/libpng/pngset.c \ + ../3rdparty/libpng/pngtrans.c \ + ../3rdparty/libpng/pngwio.c \ + ../3rdparty/libpng/pngwrite.c \ + ../3rdparty/libpng/pngwtran.c \ + ../3rdparty/libpng/pngwutil.c \ + ../3rdparty/libpng/pnggccrd.c + } +} else { + DEFINES *= QT_NO_IMAGEFORMAT_PNG +} diff --git a/src/gui/image/qbitmap.cpp b/src/gui/image/qbitmap.cpp new file mode 100644 index 0000000..3805b6e --- /dev/null +++ b/src/gui/image/qbitmap.cpp @@ -0,0 +1,403 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "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 multimedia + \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 QMatrix 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 {Implicit + Data Sharing} documentation. + + \sa QPixmap, QImage, QImageReader, QImageWriter +*/ + + +/*! + 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() +{ +} + +/*! + 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); + } + + QPixmapData *d; + QGraphicsSystem* gs = QApplicationPrivate::graphicsSystem(); + if (gs) + d = gs->createPixmapData(QPixmapData::BitmapType); + else + d = QGraphicsSystem::createDefaultPixmapData(QPixmapData::BitmapType); + + d->fromImage(img, flags | Qt::MonoOnly); + return QPixmap(d); +} + +/*! + 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 + + 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 0000000..b17e4ac --- /dev/null +++ b/src/gui/image/qbitmap.h @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef 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 &); + 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 +}; +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 0000000..6734e02 --- /dev/null +++ b/src/gui/image/qbmphandler.cpp @@ -0,0 +1,833 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "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->numColors() == 2) { + register uint *p = (uint *)image->bits(); + int nbytes = image->numBytes(); + 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 + +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; +} + +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 + + +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; +} + +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 & 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; + image.setNumColors(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.numColors() <= 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.numColors(); + bi.biClrImportant = image.numColors(); + 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.numColors()]; + uchar *rgb = color_table; + QVector<QRgb> c = image.colorTable(); + for (int i=0; i<image.numColors(); i++) { + *rgb++ = qBlue (c[i]); + *rgb++ = qGreen(c[i]); + *rgb++ = qRed (c[i]); + *rgb++ = 0; + } + if (d->write((char *)color_table, 4*image.numColors()) == -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) { + if (!canRead(device())) + return false; + setFormat("bmp"); + return true; + } + return state != Error; +} + +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.numColors() <= 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.numColors() * 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 0000000..6e953a5 --- /dev/null +++ b/src/gui/image/qbmphandler_p.h @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef 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/qicon.cpp b/src/gui/image/qicon.cpp new file mode 100644 index 0000000..3c71f15 --- /dev/null +++ b/src/gui/image/qicon.cpp @@ -0,0 +1,1128 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qicon.h" +#include "qiconengine.h" +#include "qiconengineplugin.h" +#include "private/qfactoryloader_p.h" +#include "qapplication.h" +#include "qstyleoption.h" +#include "qpainter.h" +#include "qfileinfo.h" +#include "qstyle.h" +#include "qpixmapcache.h" +#include "qvariant.h" +#include "qdebug.h" + +#ifdef Q_WS_MAC +#include <private/qt_mac_p.h> +#include <Carbon/Carbon.h> +#endif + +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); + +class QIconPrivate +{ +public: + QIconPrivate(): engine(0), ref(1), serialNum(serialNumCounter.fetchAndAddRelaxed(1)), detach_no(0), engine_version(2), v1RefCount(0) {} + + ~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); +}; + +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) && !defined(Q_WS_MAC64) + pixmapSize *= (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) ? HIGetScaleFactor() : 1; +#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 = QLatin1String("$qt_icon_") + + QString::number(pm.cacheKey()) + + QString::number(pe->mode) + + QString::number(actualSize.width()) + + QLatin1Char('_') + + QString::number(actualSize.height()) + + QLatin1Char('_'); + + + if (mode == QIcon::Active) { + if (QPixmapCache::find(key + QString::number(mode), pm)) + return pm; // horray + if (QPixmapCache::find(key + QString::number(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 + QString::number(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 + QString::number(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 + addPixmap(pm, QIcon::Mode(mode), QIcon::State(state)); + } + 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); + } +} + +#if !defined (QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS) +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 multimedia + \ingroup shared + \mainclass + + 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; +} + +/*! + 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. + + \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); +} + +/***************************************************************************** + 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 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); +#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 diff --git a/src/gui/image/qicon.h b/src/gui/image/qicon.h new file mode 100644 index 0000000..5a606d4 --- /dev/null +++ b/src/gui/image/qicon.h @@ -0,0 +1,144 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef 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); + 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; + + 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; + +#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/qiconengine.cpp b/src/gui/image/qiconengine.cpp new file mode 100644 index 0000000..866a82e --- /dev/null +++ b/src/gui/image/qiconengine.cpp @@ -0,0 +1,304 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qiconengine.h" +#include "qpainter.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QIconEngine + + \brief The QIconEngine class provides an abstract base class for QIcon renderers. + + \ingroup multimedia + + \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 multimedia + \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. + + \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; +} + +QT_END_NAMESPACE diff --git a/src/gui/image/qiconengine.h b/src/gui/image/qiconengine.h new file mode 100644 index 0000000..71c8927 --- /dev/null +++ b/src/gui/image/qiconengine.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef 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 }; + + 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_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 0000000..22f62cf --- /dev/null +++ b/src/gui/image/qiconengineplugin.cpp @@ -0,0 +1,171 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "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 0000000..5c7f8b5 --- /dev/null +++ b/src/gui/image/qiconengineplugin.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef 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/qimage.cpp b/src/gui/image/qimage.cpp new file mode 100644 index 0000000..558d574 --- /dev/null +++ b/src/gui/image/qimage.cpp @@ -0,0 +1,6119 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "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 <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 <qhash.h> + +#ifdef QT_RASTER_IMAGEENGINE +#include <private/qpaintengine_raster_p.h> +#else +#include <qpaintengine.h> +#endif + +#include <private/qimage_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(); \ + } + + +// ### Qt 5: remove +typedef void (*_qt_image_cleanup_hook)(int); +Q_GUI_EXPORT _qt_image_cleanup_hook qt_image_cleanup_hook = 0; + +// ### Qt 5: rename +typedef void (*_qt_image_cleanup_hook_64)(qint64); +Q_GUI_EXPORT _qt_image_cleanup_hook_64 qt_image_cleanup_hook_64 = 0; + +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; +} + +extern int qt_defaultDpiX(); +extern int qt_defaultDpiY(); + +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) +{ +} + +static int 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; +} + +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 = 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 8) + + // 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; + + 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) { + delete d; + return 0; + } + + d->ref.ref(); + return d; + +} + +QImageData::~QImageData() +{ + if (is_cached && qt_image_cleanup_hook_64) + qt_image_cleanup_hook_64((((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_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 multimedia + \ingroup shared + \mainclass + \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}. + + \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 numColors() 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 and 32 (for more information see the + \l {QImage#Image Formats}{Image Formats} section). + + The format(), bytesPerLine(), and numBytes() 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 + \row + \o \inlineimage qimage-32bit_scaled.png + \o + \snippet doc/src/snippets/code/src_gui_image_qimage.cpp 0 + \header + \o {2,1}32-bit + \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 + \row + \o \inlineimage qimage-8bit_scaled.png + \o + \snippet doc/src/snippets/code/src_gui_image_qimage.cpp 1 + \header + \o {2,1} 8-bit + \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. The monochrome (1-bit), 8-bit and 32-bit images are + available in all versions of Qt. In addition Qt for Embedded Linux + also supports 2-bit, 4-bit, and 16-bit images. For more information + about the Qt Extended specific formats, see the documentation of 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, the rgbSwapped() function + constructs a BGR image from a RGB image, and the alphaChannel() + function constructs an image from this image's alpha channel. + + 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 setAlphaChannel() + \o Sets the alpha channel of the image. + \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 setNumColors() + \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 all versions of Qt: + + \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). + + \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. + + \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. + + \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 = 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 + setNumColors() 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 + setNumColors() 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 + setNumColors() 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 + setNumColors() 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() +{ + 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 + setNumColors() 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 setNumColors() function. + image.setNumColors(numColors); + \endcode +*/ + +QImage::QImage(int w, int h, int depth, int numColors, Endian bitOrder) + : QPaintDevice() +{ + d = QImageData::create(QSize(w, h), formatFor(depth, bitOrder), numColors); +} + +/*! + 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 + setNumColors() 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 setNumColors() function. + image.setNumColors(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) { + setNumColors(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) { + setNumColors(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.d) + image.d->ref.ref(); + if (d && !d->ref.deref()) + delete d; + d = image.d; + return *this; +} + +/*! + \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 && qt_image_cleanup_hook_64 && d->ref == 1) + qt_image_cleanup_hook_64(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 encode a single + pixel, also called bits per pixel (bpp). + + The supported depths are 1, 8, 16, 24 and 32. + + \sa convertToFormat(), {QImage#Image Formats}{Image Formats}, + {QImage#Image Information}{Image Information} + +*/ +int QImage::depth() const +{ + return d ? d->depth : 0; +} + +/*! + \fn int QImage::numColors() const + + Returns the size of the color table for the image. + + Notice that numColors() returns 0 for 32-bpp images because these + images do not use color tables, but instead encode pixel values as + ARGB quadruplets. + + \sa setNumColors(), {QImage#Image Information}{Image Information} +*/ +int QImage::numColors() 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. + + 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 *)); + 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 *)); + 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) + d->has_alpha_clut |= (qAlpha(d->colortable.at(i)) != 255); +} + +/*! + 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(), numColors(), color() +*/ +QVector<QRgb> QImage::colorTable() const +{ + return d ? d->colortable : QVector<QRgb>(); +} + + +/*! + Returns the number of bytes occupied by the image data. + + \sa bytesPerLine(), bits(), {QImage#Image Information}{Image + Information} +*/ +int QImage::numBytes() const +{ + return d ? d->nbytes : 0; +} + +/*! + Returns the number of bytes per image scanline. + + This is equivalent to numBytes()/ 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 < numColors()); + 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 setNumColors(). + + \sa color(), numColors(), 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()) + setNumColors(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} +*/ +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 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(), numBytes() +*/ +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; +} + + + +/*! + \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); +} + +/*! + 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 + +/*! + Resizes the color table to contain \a numColors 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 numColors(), colorTable(), setColor(), {QImage#Image + Transformations}{Image Transformations} +*/ + +void QImage::setNumColors(int numColors) +{ + if (!d) { + qWarning("QImage::setNumColors: null image"); + return; + } + + detach(); + + // In case detach() ran out of memory + if (!d) + return; + + if (numColors == d->colortable.size()) + return; + if (numColors <= 0) { // use no color table + d->colortable = QVector<QRgb>(); + return; + } + int nc = d->colortable.size(); + d->colortable.resize(numColors); + for (int i = nc; i < numColors; ++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); + +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 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: { + int *line1 = new int[w]; + int *line2 = new int[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++; + } + } + delete [] line1; + delete [] line2; + } 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) +{ + QImageData *tmp = QImageData::create(QSize(src->width, src->height), QImage::Format_ARGB32); + convert_ARGB_PM_to_ARGB(tmp, src, flags); + dither_to_Mono(dst, tmp, flags, false); + delete tmp; +} + +// +// 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]; + line1[0] = new int[src->width]; + line2[0] = new int[src->width]; + line1[1] = new int[src->width]; + line2[1] = new int[src->width]; + line1[2] = new int[src->width]; + line2[2] = new int[src->width]; + pv[0] = new int[src->width]; + pv[1] = new int[src->width]; + pv[2] = new int[src->width]; + + 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; + } + delete [] line1[0]; + delete [] line2[0]; + delete [] line1[1]; + delete [] line2[1]; + delete [] line1[2]; + delete [] line2[2]; + delete [] pv[0]; + delete [] pv[1]; + delete [] pv[2]; + } 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; + QImageData *mask = QImageData::create(QSize(src->width, src->height), QImage::Format_Mono); + dither_to_Mono(mask, 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; + delete mask; + } + +#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) +{ + QImageData *tmp = QImageData::create(QSize(src->width, src->height), QImage::Format_ARGB32); + convert_ARGB_PM_to_ARGB(tmp, src, flags); + convert_RGB_to_Indexed8(dst, tmp, flags); + delete tmp; +} + +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); + + int w = src->width; + const uchar *src_data = src->data; + uchar *dest_data = dest->data; + 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(*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 const 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 +}; + +/*! + 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. + + \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 >= numColors() 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 < numColors(); 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 < numColors(); 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 copy(); + + QImage img; + QTransform wm; + wm.scale((qreal)newSize.width() / width(), (qreal)newSize.height() / height()); + 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(); + + QTransform wm; + qreal factor = (qreal) w / width(); + wm.scale(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(); + + QTransform wm; + qreal factor = (qreal) h / height(); + wm.scale(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); + 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); + m.setNumColors(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); + 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); + 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); + for (int i = 0; i < d->height; i++) { + uint *q = (uint*)res.scanLine(i); + uint *p = (uint*)scanLine(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); + for (int i = 0; i < d->height; i++) { + ushort *q = (ushort*)res.scanLine(i); + const ushort *p = (const ushort*)scanLine(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); + for (int i = 0; i < d->height; i++) { + quint8 *p = (quint8*)scanLine(i); + const quint8 *end = p + d->width * sizeof(qargb8565); + while (p < end) { + quint16 *q = reinterpret_cast<quint16*>(p + 1); + *q = ((*q << 11) & 0xf800) | ((*q >> 11) & 0x1f) | (*q & 0x07e0); + p += sizeof(qargb8565); + } + } + break; + case Format_RGB666: + res = QImage(d->width, d->height, d->format); + for (int i = 0; i < d->height; i++) { + qrgb666 *q = reinterpret_cast<qrgb666*>(res.scanLine(i)); + const qrgb666 *p = reinterpret_cast<const qrgb666*>(scanLine(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); + for (int i = 0; i < d->height; i++) { + qargb6666 *q = reinterpret_cast<qargb6666*>(res.scanLine(i)); + const qargb6666 *p = reinterpret_cast<const qargb6666*>(scanLine(i)); + const qargb6666 *end = p + d->width; + while (p < end) { + const QRgb rgb = quint32(*p++); + *q++ = qRgba(qBlue(rgb), qGreen(rgb), qRed(rgb), qAlpha(rgb)); + } + } + break; + case Format_RGB555: + res = QImage(d->width, d->height, d->format); + for (int i = 0; i < d->height; i++) { + ushort *q = (ushort*)res.scanLine(i); + const ushort *p = (const ushort*)scanLine(i); + const ushort *end = p + d->width; + while (p < end) { + *q = ((*p << 10) & 0x7800) | ((*p >> 10) & 0x1f) | (*p & 0x83e0); + p++; + q++; + } + } + break; + case Format_ARGB8555_Premultiplied: + res = QImage(d->width, d->height, d->format); + for (int i = 0; i < d->height; i++) { + quint8 *p = (quint8*)scanLine(i); + const quint8 *end = p + d->width * sizeof(qargb8555); + while (p < end) { + quint16 *q = reinterpret_cast<quint16*>(p + 1); + *q = ((*q << 10) & 0x7800) | ((*q >> 10) & 0x1f) | (*q & 0x83e0); + p += sizeof(qargb8555); + } + } + break; + case Format_RGB888: + res = QImage(d->width, d->height, d->format); + for (int i = 0; i < d->height; i++) { + quint8 *q = reinterpret_cast<quint8*>(res.scanLine(i)); + const quint8 *p = reinterpret_cast<const quint8*>(scanLine(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: + res = QImage(d->width, d->height, d->format); + for (int i = 0; i < d->height; i++) { + quint8 *q = reinterpret_cast<quint8*>(res.scanLine(i)); + const quint8 *p = reinterpret_cast<const quint8*>(scanLine(i)); + const quint8 *end = p + d->width * sizeof(qrgb444); + while (p < end) { + q[0] = (p[0] & 0xf0) | ((p[1] & 0x0f) << 8); + q[1] = ((p[0] & 0x0f) >> 8) | (p[1] & 0xf0); + q += sizeof(qrgb444); + p += sizeof(qrgb444); + } + } + break; + case Format_ARGB4444_Premultiplied: + res = QImage(d->width, d->height, d->format); + for (int i = 0; i < d->height; i++) { + quint8 *q = reinterpret_cast<quint8*>(res.scanLine(i)); + const quint8 *p = reinterpret_cast<const quint8*>(scanLine(i)); + const quint8 *end = p + d->width * sizeof(qargb4444); + while (p < end) { + q[0] = (p[0] & 0xf0) | ((p[1] & 0x0f) << 8); + q[1] = ((p[0] & 0x0f) >> 8) | (p[1] & 0xf0); + q += sizeof(qargb4444); + p += sizeof(qargb4444); + } + } + 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. + + If the loading of the image failed, this object is 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(), {Format of the QDataStream Operators} +*/ + +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(), {Format of the QDataStream Operators} +*/ + +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 + + +#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->colortable != i.d->colortable) + return false; + 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 { + int w = width(); + int h = height(); + for (int y=0; y<h; ++y) { + for (int x=0; x<w; ++x) { + if (pixelIndex(x, y) != 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. + + \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. + + \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 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; + +#ifdef QT_RASTER_IMAGEENGINE + if (!d->paintEngine) { + d->paintEngine = new QRasterPaintEngine(const_cast<QImage *>(this)); + } +#endif + return d->paintEngine; +} + + +/*! + \reimp + + 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 mods in QPainter::CompositionMode instead. + + \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; + } + + detach(); + + *this = convertToFormat(QImage::Format_ARGB32_Premultiplied); + + // 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; + } + } +} + + +/*! + 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. + + \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.setNumColors(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 alphaChannel(), {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))); +} + + +#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.numColors() > 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.numColors()) + 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.numColors() > 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.numColors()) + 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.numBytes()); + } else { + memset(dImage.bits(), 0, dImage.numBytes()); + } + break; + case 1: + case 16: + case 24: + case 32: + memset(dImage.bits(), 0x00, dImage.numBytes()); + 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()); +} + + +/*! + \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 0000000..08c8d7c --- /dev/null +++ b/src/gui/image/qimage.h @@ -0,0 +1,352 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef 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 &); + 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; + int numColors() const; + + QRgb color(int i) const; + void setColor(int i, QRgb c); + void setNumColors(int); + + bool allGray() const; + bool isGrayscale() const; + + uchar *bits(); + const uchar *bits() const; + int numBytes() const; + + uchar *scanLine(int); + const uchar *scanLine(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); + + 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 QDetachedPixmap; + 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_p.h b/src/gui/image/qimage_p.h new file mode 100644 index 0000000..9b47c5c --- /dev/null +++ b/src/gui/image/qimage_p.h @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef 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 + +struct 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; + + +#ifndef QT_NO_IMAGE_TEXT + QMap<QString, QString> text; +#endif + bool doImageIO(const QImage *image, QImageWriter* io, int quality) const; + + QPaintEngine *paintEngine; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/gui/image/qimageiohandler.cpp b/src/gui/image/qimageiohandler.cpp new file mode 100644 index 0000000..a47c69e --- /dev/null +++ b/src/gui/image/qimageiohandler.cpp @@ -0,0 +1,571 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \class 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() +{ + delete d_ptr; +} + +/*! + 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 0000000..3b654f3 --- /dev/null +++ b/src/gui/image/qimageiohandler.h @@ -0,0 +1,151 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QIMAGEIOHANDLER_H +#define QIMAGEIOHANDLER_H + +#include <QtCore/qplugin.h> +#include <QtCore/qfactoryinterface.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); + 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/qimagereader.cpp b/src/gui/image/qimagereader.cpp new file mode 100644 index 0000000..5de39d9 --- /dev/null +++ b/src/gui/image/qimagereader.cpp @@ -0,0 +1,1376 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//#define QIMAGEREADER_DEBUG + +/*! + \class QImageReader + \brief The QImageReader class provides a format independent interface + for reading images from files or other devices. + + \reentrant + \ingroup multimedia + \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 + +QT_BEGIN_NAMESPACE + +#if !defined (QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS) +Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, + (QImageIOHandlerFactoryInterface_iid, QLatin1String("/imageformats"))) +#endif + +enum _qt_BuiltInFormatType { +#ifndef QT_NO_IMAGEFORMAT_PNG + _qt_PngFormat, +#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 + {_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) +{ + if (!autoDetectImageFormat && format.isEmpty()) + return 0; + + QByteArray form = format.toLower(); + QImageIOHandler *handler = 0; + +#if !defined (QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS) + // 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 + +#if !defined (QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS) + int suffixPluginIndex = -1; + if (device && format.isEmpty() && autoDetectImageFormat) { + // 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 !defined (QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS) + 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() && autoDetectImageFormat) { + // check if any plugin supports the format (they are not allowed to + // read from the device yet). + 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, 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; + } + } + } + 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_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 + } + +#if !defined (QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS) + if (!handler && autoDetectImageFormat) { + // 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 + + if (!handler && autoDetectImageFormat) { + // 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_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; + 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) +{ + device = 0; + deleteDevice = false; + handler = 0; + quality = -1; + imageReaderError = QImageReader::UnknownError; + errorString = QLatin1String(QT_TRANSLATE_NOOP(QImageReader, "Unknown error")); + + q = qq; +} + +/*! + \internal +*/ +QImageReaderPrivate::~QImageReaderPrivate() +{ + if (deleteDevice) + delete device; + delete handler; +} + +/*! + \internal +*/ +bool QImageReaderPrivate::initHandler() +{ + // check some preconditions + if (!device || (!deleteDevice && !device->isOpen())) { + 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)) == 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; +} + +/*! + 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 qVariantValue<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. Otherwise, it + returns -1. + + \sa supportsAnimation(), QImageIOHandler::loopCount() +*/ +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. + + Certain animation formats do not support this feature, in which + case 0 is returned. + + \sa supportsAnimation(), QImageIOHandler::imageCount() +*/ +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. Otherwise, 0 is returned. + + \sa supportsAnimation(), QImageIOHandler::nextImageDelay() +*/ +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. Otherwise, -1 is + returned. + + \sa supportsAnimation(), QImageIOHandler::currentImageNumber() +*/ +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 +{ + 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); + 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 + \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. + + \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; + +#if !defined (QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS) + 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 0000000..95d4b9a --- /dev/null +++ b/src/gui/image/qimagereader.h @@ -0,0 +1,144 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef 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 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 0000000..c24bbda --- /dev/null +++ b/src/gui/image/qimagewriter.cpp @@ -0,0 +1,690 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \class QImageWriter + \brief The QImageWriter class provides a format independent interface + for writing images to files or other devices. + + \reentrant + \ingroup multimedia + \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 + +QT_BEGIN_NAMESPACE + +#if !defined (QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS) +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; + +#if !defined (QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS) + // 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()) { +#if !defined (QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS) + int index = keys.indexOf(QString::fromLatin1(suffix)); + if (index != -1) + suffixPluginIndex = index; +#endif + } + } + } + + QByteArray testFormat = !form.isEmpty() ? form : suffix; + +#if !defined (QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS) + 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 // Q_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_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 + } + } + +#if !defined (QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS) + 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)) { + handler = plugin->create(device, testFormat); + break; + } + } + } +#endif + + 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}. + + \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 + +#if !defined (QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS) + 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 0000000..5756618 --- /dev/null +++ b/src/gui/image/qimagewriter.h @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef 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/qmovie.cpp b/src/gui/image/qmovie.cpp new file mode 100644 index 0000000..ca69cab --- /dev/null +++ b/src/gui/image/qmovie.cpp @@ -0,0 +1,1081 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \class QMovie + + \brief The QMovie class is a convenience class for playing movies + with QImageReader. + + \ingroup multimedia + + 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); + 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 { + // We've read all frames now. Return an end marker + haveReadAll = true; + return QFrameInfo::endMarker(); + } + } + + // 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 0000000..c2c3abb --- /dev/null +++ b/src/gui/image/qmovie.h @@ -0,0 +1,177 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef 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 0000000..6b74323 --- /dev/null +++ b/src/gui/image/qnativeimage.cpp @@ -0,0 +1,279 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qdebug.h> +#include "qnativeimage_p.h" +#include "qcolormap.h" + +#include "private/qpaintengine_raster_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 + +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_OS_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_OS_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 = CreateCompatibleDC(qt_win_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_OS_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) +{ + if (!X11->use_mitshm) { + xshmimg = 0; + xshmpm = 0; + image = QImage(width, height, format); + 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, systemFormat()); + } + xshminfo.readOnly = false; + if (ok) + ok = XShmAttach(X11->display, &xshminfo); + 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; + } + 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 *) + : image(width, height, format) +{ + cgColorSpace = CGColorSpaceCreateDeviceRGB(); + uint cgflags = kCGImageAlphaNoneSkipFirst; + +#ifdef kCGBitmapByteOrder32Host //only needed because CGImage.h added symbols in the minor version + if(QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) + cgflags |= kCGBitmapByteOrder32Host; +#endif + + cg = CGBitmapContextCreate(image.bits(), width, height, 8, image.bytesPerLine(), cgColorSpace, 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); + CGColorSpaceRelease(cgColorSpace); +} + +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() +{ + return QImage::Format_RGB32; +} + +#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 0000000..860485a --- /dev/null +++ b/src/gui/image/qnativeimage_p.h @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef 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 Q_GUI_EXPORT 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; + CGColorSpaceRef cgColorSpace; +#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/qpaintengine_pic.cpp b/src/gui/image/qpaintengine_pic.cpp new file mode 100644 index 0000000..cba9827 --- /dev/null +++ b/src/gui/image/qpaintengine_pic.cpp @@ -0,0 +1,519 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "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 ®ion, 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); +} + +extern int qt_defaultDpi(); + +void QPicturePaintEngine::drawTextItem(const QPointF &p , const QTextItem &ti) +{ + Q_D(QPicturePaintEngine); +#ifdef QT_PICTURE_DEBUG + qDebug() << " -> drawTextItem():" << p << ti.text(); +#endif + + if (d->pic_d->formatMajor >= 9) { + const QTextItemInt &si = static_cast<const QTextItemInt &>(ti); + 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 0000000..3ae0845 --- /dev/null +++ b/src/gui/image/qpaintengine_pic_p.h @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef 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 ®ion, 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 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 0000000..d5d7cb0 --- /dev/null +++ b/src/gui/image/qpicture.cpp @@ -0,0 +1,1968 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qpicture.h" +#include <private/qpicture_p.h> + +#ifndef QT_NO_PICTURE + +#include <private/qfactoryloader_p.h> +#include <private/qpaintengine_pic_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 multimedia + \ingroup shared + \mainclass + + 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 # +extern int qt_defaultDpiX(); +extern int qt_defaultDpiY(); + +/*! + 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); + d_ptr->q_ptr = this; + d->paintEngine = 0; + + 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) +{ + d_func()->ref.ref(); +} + +/*! \internal */ +QPicture::QPicture(QPicturePrivate &dptr) + : QPaintDevice(), + d_ptr(&dptr) +{ + d_ptr->q_ptr = this; +} + +/*! + Destroys the picture. +*/ +QPicture::~QPicture() +{ + if (!d_func()->ref.deref()) { + delete d_func()->paintEngine; + delete d_func(); + } +} + +/*! + \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() +{ + if (d_func()->ref != 1) + detach_helper(); +} + +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->drawPixmap(p, QPixmap::fromImage(image)); + } else if (d->formatMajor <= 5){ + s >> ir >> image; + painter->drawPixmap(ir, QPixmap::fromImage(image), QRect(0, 0, ir.width(), ir.height())); + } else { + s >> r >> image; + painter->drawPixmap(r, QPixmap::fromImage(image), QRectF(0, 0, r.width(), r.height())); + } + } + 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 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 +*/ +void QPicture::detach_helper() +{ + Q_D(QPicture); + QPicturePrivate *x = new QPicturePrivate; + int pictsize = size(); + x->pictb.setData(data(), pictsize); + if (d->pictb.isOpen()) { + x->pictb.open(d->pictb.openMode()); + x->pictb.seek(d->pictb.pos()); + } + x->trecs = d->trecs; + x->formatOk = d->formatOk; + x->formatMinor = d->formatMinor; + x->brect = d->brect; + x->override_rect = d->override_rect; + if (!d->ref.deref()) + delete d; + d_ptr = x; +} + +/*! + Assigns picture \a p to this picture and returns a reference to + this picture. +*/ +QPicture& QPicture::operator=(const QPicture &p) +{ + qAtomicAssign<QPicturePrivate>(d_ptr, p.d_ptr); + return *this; +} + +/*! + \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 = new QPicturePaintEngine; + return d_func()->paintEngine; +} + +/***************************************************************************** + QPicture stream functions + *****************************************************************************/ + +/*! + \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; +} + + +#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 multimedia + \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 0000000..fe86e8d --- /dev/null +++ b/src/gui/image/qpicture.h @@ -0,0 +1,196 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPICTURE_H +#define QPICTURE_H + +#include <QtGui/qpaintdevice.h> +#include <QtCore/qstringlist.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); + 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(); + + QPicturePrivate *d_ptr; + friend class QPicturePaintEngine; + friend class Q3Picture; + friend class QAlphaPaintEngine; + friend class QPreviewPaintEngine; + +public: + typedef 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 + *****************************************************************************/ + +Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QPicture &); +Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QPicture &); + +#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 0000000..1da7f07 --- /dev/null +++ b/src/gui/image/qpicture_p.h @@ -0,0 +1,170 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef 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 Q_GUI_EXPORT QPicturePrivate +{ + Q_DECLARE_PUBLIC(QPicture) + 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 + }; + + inline QPicturePrivate() : in_memory_only(false), q_ptr(0) { ref = 1; } + QAtomicInt ref; + + bool checkFormat(); + void resetFormat(); + + QBuffer pictb; + int trecs; + bool formatOk; + int formatMajor; + int formatMinor; + QRect brect; + QRect override_rect; + QPaintEngine *paintEngine; + bool in_memory_only; + QList<QPixmap> pixmap_list; + QList<QBrush> brush_list; + QList<QPen> pen_list; + + QPicture *q_ptr; +}; + +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 0000000..33d10a4 --- /dev/null +++ b/src/gui/image/qpictureformatplugin.cpp @@ -0,0 +1,139 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "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 0000000..2eca024 --- /dev/null +++ b/src/gui/image/qpictureformatplugin.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef 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 0000000..e563fc9 --- /dev/null +++ b/src/gui/image/qpixmap.cpp @@ -0,0 +1,2053 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qglobal.h> + +#include "qpixmap.h" +#include "qpixmapdata_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 + +#if defined(Q_WS_X11) +# include "qx11info_x11.h" +# include <private/qt_x11_p.h> +# include <private/qpixmap_x11_p.h> +#endif + +#include "qpixmap_raster_p.h" + +QT_BEGIN_NAMESPACE + +// ### Qt 5: remove +typedef void (*_qt_pixmap_cleanup_hook)(int); +Q_GUI_EXPORT _qt_pixmap_cleanup_hook qt_pixmap_cleanup_hook = 0; + +// ### Qt 5: rename +typedef void (*_qt_pixmap_cleanup_hook_64)(qint64); +Q_GUI_EXPORT _qt_pixmap_cleanup_hook_64 qt_pixmap_cleanup_hook_64 = 0; + +// ### 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; + } +#ifndef Q_WS_WIN + if (qApp->thread() != QThread::currentThread()) { + qWarning("QPixmap: It is not safe to use pixmaps outside the GUI thread"); + return false; + } +#endif + return true; +} + +void QPixmap::init(int w, int h, Type type) +{ + init(w, h, int(type)); +} + +void QPixmap::init(int w, int h, int type) +{ + 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); + data->ref.ref(); +} + +/*! + \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) +{ + data->ref.ref(); +} + +/*! + 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 + data = 0; + operator=(pixmap.copy()); + } else { + data = pixmap.data; + data->ref.ref(); + } +} + +/*! + 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->pixelType() == QPixmapData::BitmapType) + *this = QBitmap::fromImage(image); + else + *this = fromImage(image); + } +} +#endif + + +/*! + Destroys the pixmap. +*/ + +QPixmap::~QPixmap() +{ + deref(); +} + +/*! + \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(); + + const QRect r = rect.isEmpty() ? QRect(0, 0, width(), height()) : rect; + + QPixmapData *d; + QGraphicsSystem* gs = QApplicationPrivate::graphicsSystem(); + if (gs) + d = gs->createPixmapData(data->pixelType()); + else + d = QGraphicsSystem::createDefaultPixmapData(data->pixelType()); + + d->copy(data, r); + return QPixmap(d); +} + +/*! + \fn QPixmap::scroll(int dx, int dy, int x, int y, int width, int height, QRegion *exposed) + + 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() +*/ + +/*! + 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 { + pixmap.data->ref.ref(); // avoid 'x = x' + deref(); + data = pixmap.data; + } + return *this; +} + +/*! + 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. 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 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->width() == 0; +} + +/*! + \fn int QPixmap::width() const + + Returns the width of the pixmap. + + \sa size(), {QPixmap#Pixmap Information}{Pixmap Information} +*/ +int QPixmap::width() const +{ + return data->width(); +} + +/*! + \fn int QPixmap::height() const + + Returns the height of the pixmap. + + \sa size(), {QPixmap#Pixmap Information}{Pixmap Information} +*/ +int QPixmap::height() const +{ + return data->height(); +} + +/*! + \fn QSize QPixmap::size() const + + Returns the size of the pixmap. + + \sa width(), height(), {QPixmap#Pixmap Information}{Pixmap + Information} +*/ +QSize QPixmap::size() const +{ + return QSize(data->width(), data->height()); +} + +/*! + \fn QRect QPixmap::rect() const + + Returns the pixmap's enclosing rectangle. + + \sa {QPixmap#Pixmap Information}{Pixmap Information} +*/ +QRect QPixmap::rect() const +{ + return QRect(0, 0, data->width(), data->height()); +} + +/*! + \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->depth(); +} + +/*! + \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; + + // Create new pixmap + QPixmap pm(QSize(w, h), data->type); + bool uninit = false; +#if defined(Q_WS_X11) + QX11PixmapData *x11Data = data->classId() == QPixmapData::X11Class ? static_cast<QX11PixmapData*>(data) : 0; + if (x11Data) { + pm.x11SetScreen(x11Data->xinfo.screen()); + uninit = x11Data->uninit; + } +#elif defined(Q_WS_MAC) + QMacPixmapData *macData = data->classId() == QPixmapData::MacClass ? static_cast<QMacPixmapData*>(data) : 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_MAC) +#ifndef QT_MAC_NO_QUICKDRAW + if(macData && macData->qd_alpha) + macData->macQDUpdateAlpha(); +#endif +#elif defined(Q_WS_X11) + if (x11Data && x11Data->x11_mask) { + QX11PixmapData *pmData = static_cast<QX11PixmapData*>(pm.data); + 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. + + 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 (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 opaque. If \a mode is Qt::MaskOutColor, all pixels + matching the maskColor will be transparent. + + 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 = QLatin1String("qt_pixmap_") + info.absoluteFilePath() + QLatin1Char('_') + QString::number(info.lastModified().toTime_t()) + QLatin1Char('_') + + QString::number(info.size()) + QLatin1Char('_') + QString::number(data->pixelType()); + + if (QPixmapCache::find(key, *this)) + return true; + + QImage image = QImageReader(fileName, format).read(); + if (image.isNull()) + return false; + QPixmap pm; + if (data->pixelType() == QPixmapData::BitmapType) + pm = QBitmap::fromImage(image, flags); + else + pm = fromImage(image, flags); + if (!pm.isNull()) { + *this = pm; + 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) +{ + QByteArray a = QByteArray::fromRawData(reinterpret_cast<const char *>(buf), len); + QBuffer b(&a); + b.open(QIODevice::ReadOnly); + + QImage image = QImageReader(&b, format).read(); + QPixmap pm; + if (data->pixelType() == QPixmapData::BitmapType) + pm = QBitmap::fromImage(image, flags); + else + pm = fromImage(image, flags); + if (!pm.isNull()) { + *this = pm; + return true; + } + return false; +} + +/*! + \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. + + \sa {QPixmap#Pixmap Transformations}{Pixmap Transformations} +*/ + +void QPixmap::fill(const QColor &color) +{ + if (isNull()) + return; + + detach(); + 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 +{ + int classKey = data->classId(); + if (classKey >= 1024) + classKey = -(classKey >> 10); + return ((((qint64) classKey) << 56) + | (((qint64) data->serialNumber()) << 32) + | ((qint64) (data->detach_no))); +} + +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); + + 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()); + widget->render(&res, QPoint(), r, + QWidget::DrawWindowBackground | QWidget::DrawChildren | QWidget::IgnoreMask); + 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. + + \sa detach() +*/ + +Qt::HANDLE QPixmap::handle() const +{ +#if defined(Q_WS_X11) + if (data->classId() == QPixmapData::X11Class) + return static_cast<QX11PixmapData*>(data)->handle(); +#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->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->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->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 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(), {Format of the QDataStream Operators} +*/ + +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(), {Format of the QDataStream Operators} +*/ + +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->ref == 1; +} + +void QPixmap::deref() +{ + if (data && !data->ref.deref()) { // Destroy image if last ref +#if !defined(QT_NO_DIRECT3D) && defined(Q_WS_WIN) + if (data->classId() == QPixmapData::RasterClass) { + QRasterPixmapData *rData = static_cast<QRasterPixmapData*>(data); + if (rData->texture) + rData->texture->Release(); + rData->texture = 0; + } +#endif + if (data->is_cached && qt_pixmap_cleanup_hook_64) + qt_pixmap_cleanup_hook_64(cacheKey()); + delete data; + data = 0; + } +} + +/*! + \fn QImage QPixmap::convertToImage() const + + Use the toImage() function instead. +*/ + +/*! + \fn bool QPixmap::convertFromImage(const QImage &image, Qt::ImageConversionFlags flags) + + Use the static fromImage() function instead. +*/ + +/*! + \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. + + \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; + + QPixmap pix; + QTransform wm; + wm.scale((qreal)newSize.width() / width(), (qreal)newSize.height() / height()); + 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(); + + QTransform wm; + qreal factor = (qreal) w / width(); + wm.scale(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(); + + QTransform wm; + qreal factor = (qreal) h / height(); + wm.scale(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 multimedia + \ingroup shared + \mainclass + + 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. And because QPixmap is a + QPaintDevice subclass, QPainter can be used to draw directly onto + pixmaps. + + 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, in it. + + Note that the pixel data in a pixmap is internal and is managed by + the underlying window system. Pixels can only be accessed through + QPainter functions or by converting the QPixmap to a QImage. + Depending on the system, QPixmap is stored using a RGB32 or a + premultiplied alpha format. If the image has an alpha channel, and + if the system allows, the preferred format is premultiplied alpha. + Note also that QPixmap, unlike QImage, may be hardware dependent. + On X11 and Mac, a QPixmap is stored on the server side while a + QImage is stored on the client side (on Windows, these two classes + have an equivalent internal representation, i.e. both QImage and + QPixmap are stored on the client side and don't use any GDI + resources). + + 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. + + 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. + + 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. + + \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, while the + hasAlpha() function returns true if the pixmap has an alpha + channel \e or a mask (otherwise false). + + The alphaChannel() function returns the alpha channel as a new + QPixmap object, while the mask() function returns the mask as a + QBitmap object. The alpha channel and mask can be set using the + setAlphaChannel() and setMask() functions, respectively. + + \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 to display the widget. 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. + + \section1 Pixmap Transformations + + QPixmap supports a number of functions for creating a new pixmap + that is a transformed version of the original: 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. + + + 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. + + There are also functions for changing attributes of a pixmap. + in-place: The fill() function fills the entire image with the + given color, the setMask() function sets a mask bitmap, and the + setAlphaChannel() function sets the pixmap's alpha channel. + + \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(), alphaChannel(), mask() +*/ +bool QPixmap::hasAlpha() const +{ + return (data->hasAlphaChannel() || !data->mask().isNull()); +} + +/*! + Returns true if the pixmap has a format that respects the alpha + channel, otherwise returns false. + + \sa alphaChannel(), hasAlpha() +*/ +bool QPixmap::hasAlphaChannel() const +{ + return data->hasAlphaChannel(); +} + +/*! + \reimp +*/ +int QPixmap::metric(PaintDeviceMetric metric) const +{ + return data->metric(metric); +} + +/*! + \fn void QPixmap::setAlphaChannel(const QPixmap &alphaChannel) + + 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. + + \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); +} + +/*! + 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 + + \sa setAlphaChannel(), {QPixmap#Pixmap Information}{Pixmap + Information} +*/ +QPixmap QPixmap::alphaChannel() const +{ + return data->alphaChannel(); +} + +/*! + \reimp +*/ +QPaintEngine *QPixmap::paintEngine() const +{ + return data->paintEngine(); +} + +/*! + \fn QBitmap QPixmap::mask() const + + Extracts a bitmap mask from the pixmap's alphachannel. + + This is potentially an expensive operation. + + \sa setMask(), {QPixmap#Pixmap Information}{Pixmap Information} +*/ +QBitmap QPixmap::mask() const +{ + return data->mask(); +} + +/*! + 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_OS_WINCE) + return QColormap::instance().depth(); +#elif defined(Q_WS_WIN) + return 32; // XXX +#elif defined(Q_WS_MAC) + return 32; +#endif +} + +typedef void (*_qt_pixmap_cleanup_hook_64)(qint64); +extern _qt_pixmap_cleanup_hook_64 qt_pixmap_cleanup_hook_64; + +/*! + 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() +{ + QPixmapData::ClassId id = data->classId(); + if (id == QPixmapData::RasterClass) { + QRasterPixmapData *rasterData = static_cast<QRasterPixmapData*>(data); + rasterData->image.detach(); +#if defined(Q_WS_WIN) && !defined(QT_NO_DIRECT3D) + if (rasterData->texture) { + rasterData->texture->Release(); + rasterData->texture = 0; + } +#endif + } + + if (data->is_cached && qt_pixmap_cleanup_hook_64 && data->ref == 1) + qt_pixmap_cleanup_hook_64(cacheKey()); + +#if defined(Q_WS_MAC) + QMacPixmapData *macData = id == QPixmapData::MacClass ? static_cast<QMacPixmapData*>(data) : 0; + if (macData) { + if (macData->cg_mask) { + CGImageRelease(macData->cg_mask); + macData->cg_mask = 0; + } + } +#endif + + if (data->ref != 1) { + *this = copy(); +#if defined(Q_WS_MAC) && !defined(QT_MAC_NO_QUICKDRAW) + if (id == QPixmapData::MacClass) { + macData->qd_alpha = 0; + } +#endif + } + ++data->detach_no; + +#if defined(Q_WS_X11) + if (data->classId() == QPixmapData::X11Class) { + QX11PixmapData *d = static_cast<QX11PixmapData*>(data); + d->uninit = false; + + // 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 toImage(), {QPixmap#Pixmap Conversion}{Pixmap Conversion} +*/ +QPixmap QPixmap::fromImage(const QImage &image, Qt::ImageConversionFlags flags) +{ + if (image.isNull()) + return QPixmap(); + + QPixmapData *data; + QGraphicsSystem* gs = QApplicationPrivate::graphicsSystem(); + if (gs) + data = gs->createPixmapData(QPixmapData::PixmapType); + else + data = QGraphicsSystem::createDefaultPixmapData(QPixmapData::PixmapType); + + data->fromImage(image, flags); + return QPixmap(data); +} + +/*! + \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 X11that 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. + + \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 +{ + return data; +} + +QT_END_NAMESPACE diff --git a/src/gui/image/qpixmap.h b/src/gui/image/qpixmap.h new file mode 100644 index 0000000..cd3b465 --- /dev/null +++ b/src/gui/image/qpixmap.h @@ -0,0 +1,312 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef 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 <QtGui/qimage.h> +#include <QtGui/qtransform.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QImageWriter; +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 &); + operator QVariant() const; + + bool isNull() const; + int devType() const; + + int width() const; + int height() const; + 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); + + 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; + +#if defined(Q_WS_WIN) + enum HBitmapFormat { + NoAlpha, + PremultipliedAlpha, + Alpha + }; + + HBITMAP toWinHBITMAP(HBitmapFormat format = NoAlpha) const; + static QPixmap fromWinHBITMAP(HBITMAP hbitmap, HBitmapFormat format = NoAlpha); +#endif + +#if defined(Q_WS_MAC) + CGImageRef toMacCGImageRef() const; + static QPixmap fromMacCGImageRef(CGImageRef image); +#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; + int numCols() 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); + QT3_SUPPORT bool convertFromImage(const QImage &img, Qt::ImageConversionFlags flags = Qt::AutoColor) + { (*this) = fromImage(img, flags); return !isNull(); } + 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: + 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 QBitmap; + friend class QPaintDevice; + friend class QPainter; + friend class QGLWidget; + friend class QX11PaintEngine; + friend class QCoreGraphicsPaintEngine; + friend class QWidgetPrivate; + friend class QRasterPaintEngine; + friend class QRasterBuffer; + friend class QDirect3DPaintEngine; + friend class QDirect3DPaintEnginePrivate; + friend class QDetachedPixmap; +#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 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_mac.cpp b/src/gui/image/qpixmap_mac.cpp new file mode 100644 index 0000000..b0075b2 --- /dev/null +++ b/src/gui/image/qpixmap_mac.cpp @@ -0,0 +1,1339 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ +//#define QT_RASTER_PAINTENGINE + +#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> +#ifdef QT_RASTER_PAINTENGINE +# include <private/qpaintengine_raster_p.h> +#endif +#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)->pixels; +} + +Q_GUI_EXPORT int qt_mac_pixmap_get_bytes_per_line(const QPixmap *pix) +{ + return static_cast<QMacPixmapData*>(pix->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); + +#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4) + uint cgflags = kCGImageAlphaPremultipliedFirst; +#ifdef kCGBitmapByteOrder32Host //only needed because CGImage.h added symbols in the minor version + if(QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) + cgflags |= kCGBitmapByteOrder32Host; +#endif +#else + CGImageAlphaInfo cgflags = kCGImageAlphaPremultipliedFirst; +#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), w(0), h(0), d(0), has_alpha(0), has_mask(0), + uninit(true), pixels(0), pixelsToFree(0), bytesPerRow(0), + cg_data(0), cg_dataBeingReleased(0), cg_mask(0), +#ifndef QT_MAC_NO_QUICKDRAW + qd_data(0), qd_alpha(0), +#endif + pengine(0) +{ +} + +#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; + d = (pixelType() == BitmapType ? 1 : 32); + bool make_null = w <= 0 || h <= 0; // create null pixmap + if (make_null || d == 0) { + w = 0; + h = 0; + 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(); + 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.numColors() == 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) = 0x00000000; + else + *(drow+x) = 0xFFFFFFFF; + } + break; + } + case QImage::Format_Indexed8: + for (int x = 0; x < w; ++x) { + *(drow+x) = PREMUL(image.color(*(srow + x))); + } + 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.numColors(); 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->numColors();loopc++) { + if(qi->color(loopc)==mycol) + return loopc; + } + qi->setNumColors(qi->numColors()+1); + qi->setColor(qi->numColors(),mycol); + return qi->numColors(); +} + +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.setNumColors(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; + } + } + macSetHasAlpha(fillColor.alpha() != 255); +} + +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); + 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); + 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); +#ifndef QT_MAC_NO_QUICKDRAW + macQDDisposeAlpha(); + if (qd_data) { + DisposeGWorld(qd_data); + qd_data = 0; + } +#endif + 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; +#ifndef QT_MAC_NO_QUICKDRAW + macQDDisposeAlpha(); //let it get created lazily +#endif + macReleaseCGImageRef(); +} + +#ifndef QT_MAC_NO_QUICKDRAW +void QMacPixmapData::macQDDisposeAlpha() +{ + if (qd_alpha) { + DisposeGWorld(qd_alpha); + qd_alpha = 0; + } +} + +void QMacPixmapData::macQDUpdateAlpha() +{ + macQDDisposeAlpha(); // get rid of alpha pixmap + if (!has_alpha && !has_mask) + return; + + //setup + Rect rect; + SetRect(&rect, 0, 0, w, h); + const int params = alignPix | stretchPix | newDepth; + NewGWorld(&qd_alpha, 32, &rect, 0, 0, params); + int *dptr = (int *)GetPixBaseAddr(GetGWorldPixMap(qd_alpha)), *drow; + unsigned short dbpr = GetPixRowBytes(GetGWorldPixMap(qd_alpha)); + const int *sptr = (int*)pixels, *srow; + const uint sbpr = bytesPerRow; + uchar clr; + for (int y = 0; y < h; ++y) { + drow = (int*)((char *)dptr + (y * dbpr)); + srow = (int*)((char *)sptr + (y * sbpr)); + for (int x=0; x < w; x++) { + clr = qAlpha(*(srow + x)); + *(drow + x) = qRgba(~clr, ~clr, ~clr, 0); + } + } +} +#endif + +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); +#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4) + uint cgflags = kCGImageAlphaPremultipliedFirst; +#ifdef kCGBitmapByteOrder32Host //only needed because CGImage.h added symbols in the minor version + if(QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) + cgflags |= kCGBitmapByteOrder32Host; +#endif +#else + CGImageAlphaInfo cgflags = kCGImageAlphaPremultipliedFirst; +#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, numBytes); + pixels = base_pixels; +} + +#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 +} + +static CGImageRef qt_mac_createImageFromBitmapContext(CGContextRef c) +{ + void *rasterData = CGBitmapContextGetData(c); + const int width = CGBitmapContextGetBytesPerRow(c), + height = CGBitmapContextGetHeight(c); + size_t imageDataSize = width*height; + + if(!rasterData) + return 0; + + // Create the data provider from the image data, using + // the image releaser function releaseBitmapContextImageData. + CGDataProviderRef dataProvider = CGDataProviderCreateWithData(0, rasterData, + imageDataSize, + qt_mac_cgimage_data_free); + + if(!dataProvider) + return 0; + + uint bitmapInfo = 0; +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4 + if(CGBitmapContextGetBitmapInfo) + bitmapInfo = CGBitmapContextGetBitmapInfo(c); + else +#endif + bitmapInfo = CGBitmapContextGetAlphaInfo(c); + return CGImageCreate(width, height, CGBitmapContextGetBitsPerComponent(c), + CGBitmapContextGetBitsPerPixel(c), CGBitmapContextGetBytesPerRow(c), + CGBitmapContextGetColorSpace(c), bitmapInfo, dataProvider, + 0, true, kCGRenderingIntentDefault); +} + +// 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); + +#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4) + if(QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) { + QCFType<CGImageRef> image = CGBitmapContextCreateImage(bitmap); + return QPixmap::fromMacCGImageRef(image); + } else +#endif + { + QCFType<CGImageRef> image = qt_mac_createImageFromBitmapContext(bitmap); + if (!image) + return QPixmap(); + 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. +*/ + +Qt::HANDLE QPixmap::macQDHandle() const +{ +#ifndef QT_MAC_NO_QUICKDRAW + QMacPixmapData *d = static_cast<QMacPixmapData*>(data); + if (!d->qd_data) { //create the qd data + Rect rect; + SetRect(&rect, 0, 0, d->w, d->h); + unsigned long qdformat = k32ARGBPixelFormat; + GWorldFlags qdflags = 0; +#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4) + if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) { + //we play such games so we can use the same buffer in CG as QD this + //makes our merge much simpler, at some point the hacks will go away + //because QD will be removed, but until that day this keeps them coexisting + if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) + qdformat = k32BGRAPixelFormat; +#if 0 + qdflags |= kNativeEndianPixMap; +#endif + } +#endif + if(NewGWorldFromPtr(&d->qd_data, qdformat, &rect, 0, 0, qdflags, + (char*)d->pixels, d->bytesPerRow) != noErr) + qWarning("Qt: internal: QPixmap::init error (%d %d %d %d)", rect.left, rect.top, rect.right, rect.bottom); + } + return d->qd_data; +#else + return 0; +#endif +} + +/*! \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. +*/ + +Qt::HANDLE QPixmap::macQDAlphaHandle() const +{ +#ifndef QT_MAC_NO_QUICKDRAW + QMacPixmapData *d = static_cast<QMacPixmapData*>(data); + if (d->has_alpha || d->has_mask) { + if (!d->qd_alpha) //lazily created + d->macQDUpdateAlpha(); + return d->qd_alpha; + } +#endif + 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 (data->classId() == QPixmapData::MacClass) { + QMacPixmapData *d = static_cast<QMacPixmapData *>(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)->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); + 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; + + QMacSavedPortInfo pi; //save the current state + //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 + +QPixmap qt_mac_convert_iconref(const IconRef icon, int width, int height) +{ + QPixmap ret(width, height); + ret.fill(QColor(0, 0, 0, 0)); + + CGRect rect = CGRectMake(0, 0, width, height); + + CGContextRef ctx = qt_mac_cg_context(&ret); + CGAffineTransform old_xform = CGContextGetCTM(ctx); + CGContextConcatCTM(ctx, CGAffineTransformInvert(old_xform)); + CGContextConcatCTM(ctx, CGAffineTransformIdentity); + + ::RGBColor b; + b.blue = b.green = b.red = 255*255; + PlotIconRefInContext(ctx, &rect, kAlignNone, kTransformNone, &b, kPlotIconRefNormalFlags, icon); + CGContextRelease(ctx); + return ret; +} + +/*! \internal */ +QPaintEngine* QMacPixmapData::paintEngine() const +{ + if (!pengine) { + QMacPixmapData *that = const_cast<QMacPixmapData*>(this); +#ifdef QT_RASTER_PAINTENGINE + if (qgetenv("QT_MAC_USE_COREGRAPHICS").isNull()) + that->pengine = new QRasterPaintEngine(); + else + that->pengine = new QCoreGraphicsPaintEngine(); +#else + that->pengine = new QCoreGraphicsPaintEngine(); +#endif + } + 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); + 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 0000000..2b22e6b --- /dev/null +++ b/src/gui/image/qpixmap_mac_p.h @@ -0,0 +1,137 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef 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(); + + 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: + int w, h, d; + + 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; + quint32 *pixelsToFree; + uint bytesPerRow; + QRectF cg_mask_rect; + CGImageRef cg_data, cg_dataBeingReleased, cg_mask; +#ifndef QT_MAC_NO_QUICKDRAW + GWorldPtr qd_data, qd_alpha; + void macQDDisposeAlpha(); + void macQDUpdateAlpha(); +#endif + 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_qws.cpp b/src/gui/image/qpixmap_qws.cpp new file mode 100644 index 0000000..6cc7981 --- /dev/null +++ b/src/gui/image/qpixmap_qws.cpp @@ -0,0 +1,164 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <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> + +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); +} + +/*! + \internal +*/ +QRgb* QPixmap::clut() const +{ + if (data->classId() == QPixmapData::RasterClass) { + const QRasterPixmapData *d = static_cast<const QRasterPixmapData*>(data); + return d->image.colorTable().data(); + } + + return 0; +} + +/*! + \internal +*/ +int QPixmap::numCols() const +{ + if (data->classId() == QPixmapData::RasterClass) { + const QRasterPixmapData *d = static_cast<const QRasterPixmapData*>(data); + return d->image.numColors(); + } + + return 0; +} + +/*! + \internal + \since 4.1 +*/ +const uchar* QPixmap::qwsBits() const +{ + if (data->classId() == QPixmapData::RasterClass) { + const QRasterPixmapData *d = static_cast<const QRasterPixmapData*>(data); + return d->image.bits(); + } + + return 0; +} + +/*! + \internal + \since 4.1 +*/ +int QPixmap::qwsBytesPerLine() const +{ + if (data->classId() == QPixmapData::RasterClass) { + const QRasterPixmapData *d = static_cast<const QRasterPixmapData*>(data); + return d->image.bytesPerLine(); + } + + return 0; +} diff --git a/src/gui/image/qpixmap_raster.cpp b/src/gui/image/qpixmap_raster.cpp new file mode 100644 index 0000000..145b02a --- /dev/null +++ b/src/gui/image/qpixmap_raster.cpp @@ -0,0 +1,360 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qpixmap.h" + +#include "qpixmap_raster_p.h" +#include "qnativeimage_p.h" +#include "qimage_p.h" + +#include "qbitmap.h" +#include "qimage.h" +#include <private/qwidget_p.h> +#include <private/qdrawhelper_p.h> + +#if !defined(QT_NO_DIRECT3D) && defined(Q_WS_WIN) +#include <private/qpaintengine_d3d_p.h> +#include <d3d9.h> +extern QDirect3DPaintEngine *qt_d3dEngine(); +#endif + +QT_BEGIN_NAMESPACE + +const uchar qt_pixmap_bit_mask[] = { 0x01, 0x02, 0x04, 0x08, + 0x10, 0x20, 0x40, 0x80 }; + +QRasterPixmapData::QRasterPixmapData(PixelType type) + : QPixmapData(type, RasterClass) +#if defined(Q_WS_WIN) && !defined(QT_NO_DIRECT3D) + , texture(0) +#endif +{ +} + +QRasterPixmapData::~QRasterPixmapData() +{ +} + +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); + + if (pixelType() == BitmapType && !image.isNull()) { + image.setNumColors(2); + image.setColor(0, QColor(Qt::color0).rgba()); + image.setColor(1, QColor(Qt::color1).rgba()); + } + + setSerialNumber(image.serialNumber()); +} + +void QRasterPixmapData::fromImage(const QImage &sourceImage, + Qt::ImageConversionFlags flags) +{ + Q_UNUSED(flags); + +#ifdef Q_WS_QWS + QImage::Format format; + 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; + } + + image = sourceImage.convertToFormat(format); +#else + if (pixelType() == BitmapType) { + image = sourceImage.convertToFormat(QImage::Format_MonoLSB); + } else { + if (sourceImage.depth() == 1) { + image = sourceImage.hasAlphaChannel() + ? sourceImage.convertToFormat(QImage::Format_ARGB32_Premultiplied) + : sourceImage.convertToFormat(QImage::Format_RGB32); + } else { + + QImage::Format opaqueFormat = QNativeImage::systemFormat(); + QImage::Format alphaFormat = QImage::Format_ARGB32_Premultiplied; + + switch (opaqueFormat) { + case QImage::Format_RGB16: + alphaFormat = QImage::Format_ARGB8565_Premultiplied; + break; + default: // We don't care about the others... + break; + } + + if (!sourceImage.hasAlphaChannel() + || ((flags & Qt::NoOpaqueDetection) == 0 + && !const_cast<QImage &>(sourceImage).data_ptr()->checkForAlphaPixels())) { + image = sourceImage.convertToFormat(opaqueFormat); + } else { + image = sourceImage.convertToFormat(alphaFormat); + } + } + } +#endif + + setSerialNumber(image.serialNumber()); +} + +// from qwindowsurface.cpp +extern void qt_scrollRectInImage(QImage &img, const QRect &rect, const QPoint &offset); + +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 (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 + toFormat = QImage::Format_ARGB32_Premultiplied; + 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 +{ + return image; +} + +void QRasterPixmapData::setAlphaChannel(const QPixmap &alphaChannel) +{ + image.setAlphaChannel(alphaChannel.toImage()); +} + +QPaintEngine* QRasterPixmapData::paintEngine() const +{ + return image.paintEngine(); +} + +extern int qt_defaultDpiX(); +extern int qt_defaultDpiY(); + +int QRasterPixmapData::metric(QPaintDevice::PaintDeviceMetric metric) const +{ + // override the image dpi with the screen dpi when rendering to a pixmap + const int dpmX = qRound(qt_defaultDpiX() * 100 / qreal(2.54)); + const int dpmY = qRound(qt_defaultDpiY() * 100 / qreal(2.54)); + switch (metric) { + case QPaintDevice::PdmWidthMM: + return qRound(image.width() * 1000 / dpmX); + case QPaintDevice::PdmHeightMM: + return qRound(image.height() * 1000 / dpmY); + case QPaintDevice::PdmDpiX: + return qRound(dpmX * qreal(0.0254)); + case QPaintDevice::PdmDpiY: + return qRound(dpmY * qreal(0.0254)); + case QPaintDevice::PdmPhysicalDpiX: + return qRound(dpmX * qreal(0.0254)); + case QPaintDevice::PdmPhysicalDpiY: + return qRound(dpmY * qreal(0.0254)); + default: + return image.metric(metric); + } +} + +QImage* QRasterPixmapData::buffer() +{ + return ℑ +} + +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 0000000..48d5cf8 --- /dev/null +++ b/src/gui/image/qpixmap_raster_p.h @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef 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" +# ifndef QT_NO_DIRECT3D +# include <d3d9.h> +# endif +#endif + +QT_BEGIN_NAMESPACE + +class Q_GUI_EXPORT QRasterPixmapData : public QPixmapData +{ +public: + QRasterPixmapData(PixelType type); + ~QRasterPixmapData(); + + void resize(int width, int height); + void fromFile(const QString &filename, Qt::ImageConversionFlags flags); + void fromImage(const QImage &image, Qt::ImageConversionFlags flags); + + 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; + QPaintEngine* paintEngine() const; + QImage* buffer(); + +protected: + int metric(QPaintDevice::PaintDeviceMetric metric) const; + +private: +#if defined(Q_WS_WIN) && !defined(QT_NO_DIRECT3D) + friend class QDirect3DPaintEnginePrivate; + IDirect3DTexture9 *texture; +#endif + friend class QPixmap; + friend class QBitmap; + friend class QDetachedPixmap; + friend class QRasterPaintEngine; + QImage image; +}; + +QT_END_NAMESPACE + +#endif // QPIXMAPDATA_RASTER_P_H + + diff --git a/src/gui/image/qpixmap_win.cpp b/src/gui/image/qpixmap_win.cpp new file mode 100644 index 0000000..3ec441b --- /dev/null +++ b/src/gui/image/qpixmap_win.cpp @@ -0,0 +1,481 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "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_OS_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_OS_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_OS_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; +} + + + +/*! + \enum QPixmap::HBitmapFormat + + 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() +*/ + +/*! + 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() +*/ +HBITMAP QPixmap::toWinHBITMAP(HBitmapFormat format) const +{ + HBITMAP bitmap = 0; + if (data->classId() == QPixmapData::RasterClass) { + QRasterPixmapData* d = static_cast<QRasterPixmapData*>(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; +} + +/*! + 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} + +*/ +QPixmap QPixmap::fromWinHBITMAP(HBITMAP bitmap, HBitmapFormat format) +{ + // Verify size + BITMAP bitmap_info; + memset(&bitmap_info, 0, sizeof(BITMAP)); + + int res; + QT_WA({ + res = GetObjectW(bitmap, sizeof(BITMAP), &bitmap_info); + } , { + res = GetObjectA(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); +} + +#ifdef Q_WS_WIN +#ifndef Q_OS_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"); + } + + return image; +} + +QPixmap convertHIconToPixmap( const 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("convertHIconToPixmap(), 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_OS_WINCE +QPixmap convertHIconToPixmap( const HICON icon, bool large) +{ + HDC screenDevice = GetDC(0); + HDC hdc = CreateCompatibleDC(screenDevice); + ReleaseDC(0, screenDevice); + + int size = large ? 64 : 32; + + BITMAPINFO bmi; + memset(&bmi, 0, sizeof(bmi)); + bmi.bmiHeader.biSize = sizeof(BITMAPINFO); + bmi.bmiHeader.biWidth = size; + bmi.bmiHeader.biHeight = -size; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 32; + bmi.bmiHeader.biCompression = BI_RGB; + bmi.bmiHeader.biSizeImage = size*size*4; + + uchar* bits; + + HBITMAP winBitmap = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, (void**) &bits, 0, 0); + if (winBitmap ) + memset(bits, 0xff, size*size*4); + if (!winBitmap) { + qWarning("convertHIconToPixmap(), failed to CreateDIBSection()"); + return QPixmap(); + } + + HGDIOBJ oldhdc = (HBITMAP)SelectObject(hdc, winBitmap); + if (!DrawIconEx( hdc, 0, 0, icon, size, size, 0, 0, DI_NORMAL)) + qWarning("convertHIconToPixmap(), failed to DrawIcon()"); + + uint mask = 0xff000000; + // Create image and copy data into image. + QImage image(size, size, QImage::Format_ARGB32); + + if (!image.isNull()) { // failed to alloc? + int bytes_per_line = size * sizeof(QRgb); + for (int y=0; y<size; ++y) { + QRgb *dest = (QRgb *) image.scanLine(y); + const QRgb *src = (const QRgb *) (bits + y * bytes_per_line); + for (int x=0; x<size; ++x) { + dest[x] = src[x]; + } + } + } + if (!DrawIconEx( hdc, 0, 0, icon, size, size, 0, 0, DI_MASK)) + qWarning("convertHIconToPixmap(), failed to DrawIcon()"); + if (!image.isNull()) { // failed to alloc? + int bytes_per_line = size * sizeof(QRgb); + for (int y=0; y<size; ++y) { + QRgb *dest = (QRgb *) image.scanLine(y); + const QRgb *src = (const QRgb *) (bits + y * bytes_per_line); + for (int x=0; x<size; ++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_OS_WINCE + +QPixmap loadIconFromShell32( int resourceId, int size ) +{ +#ifdef Q_OS_WINCE + HMODULE hmod = LoadLibrary((const wchar_t *) QString::fromLatin1("ceshell.dll").utf16()); +#else + HMODULE hmod = LoadLibraryA("shell32.dll"); +#endif + if( hmod ) { + HICON iconHandle = (HICON)LoadImage(hmod, MAKEINTRESOURCE(resourceId), IMAGE_ICON, size, size, 0); + if( iconHandle ) { + QPixmap iconpixmap = convertHIconToPixmap( iconHandle ); + DestroyIcon(iconHandle); + return iconpixmap; + } + } + return QPixmap(); +} + +#endif + +QT_END_NAMESPACE diff --git a/src/gui/image/qpixmap_x11.cpp b/src/gui/image/qpixmap_x11.cpp new file mode 100644 index 0000000..9d99d2e --- /dev/null +++ b/src/gui/image/qpixmap_x11.cpp @@ -0,0 +1,2301 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// 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 <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 + *****************************************************************************/ + +static int qt_pixmap_serial = 0; +int Q_GUI_EXPORT qt_x11_preferred_pixmap_depth = 0; + +QX11PixmapData::QX11PixmapData(PixelType type) + : QPixmapData(type, X11Class), hd(0), w(0), h(0), d(0), + uninit(true), read_only(false), x11_mask(0), picture(0), mask_picture(0), hd2(0), + share_mode(QPixmap::ImplicitlyShared), pengine(0) +{ +} + +void QX11PixmapData::resize(int width, int height) +{ + setSerialNumber(++qt_pixmap_serial); + + w = width; + h = height; + + 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; + 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 +} + +void QX11PixmapData::fromImage(const QImage &img, + Qt::ImageConversionFlags flags) +{ + setSerialNumber(++qt_pixmap_serial); + + w = img.width(); + h = img.height(); + d = img.depth(); + + 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; + return; + } + + int dd = X11->use_xrender && img.hasAlphaChannel() ? 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.numColors() == 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.numBytes(); + uchar *newbits= 0; + +#ifndef QT_NO_XRENDER + if (X11->use_xrender && image.hasAlphaChannel()) { + 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.numColors(); 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.numColors() == 0) + image.setNumColors(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.numColors(); i++) { // compute number of colors + if (pop[i] > 0) + ncols++; + } + for (int i = cimage.numColors(); 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 (image.hasAlphaChannel()) { + QBitmap m = QBitmap::fromImage(image.createAlphaMask(flags)); + setMask(m); + } +} + +void QX11PixmapData::bitmapFromImage(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; + w = img.width(); + h = img.height(); + d = 1; + 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; + } + hd = (Qt::HANDLE)XCreateBitmapFromData(xinfo.display(), + RootWindow(xinfo.display(), xinfo.screen()), + bits, w, h); + +#ifndef QT_NO_XRENDER + if (X11->use_xrender) + picture = XRenderCreatePicture(X11->display, hd, + XRenderFindStandardFormat(X11->display, PictStandardA1), 0, 0); +#endif // QT_NO_XRENDER + + if (tmp_bits) // Avoid purify complaint + delete [] tmp_bits; +} + +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() +{ + release(); +} + +void QX11PixmapData::release() +{ + delete pengine; + pengine = 0; + + if (!X11) + 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 (!read_only) + XFreePixmap(xinfo.display(), hd); + hd = 0; + } +} + +QPixmap QX11PixmapData::alphaChannel() const +{ + if (!hasAlphaChannel()) + return QPixmap(); + 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; + 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; + } +} + +/*! + 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 +{ + 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; + } + + XImage *xi = XGetImage(X11->display, hd, 0, 0, w, h, AllPlanes, + (d == 1) ? XYPixmap : ZPixmap); + + Q_CHECK_PTR(xi); + if (!xi) + return QImage(); + + if (picture && depth() == 32) { + QImage image(w, h, QImage::Format_ARGB32_Premultiplied); + memcpy(image.bits(), xi->data, xi->bytes_per_line * xi->height); + + // we may have to swap the byte order + if ((QSysInfo::ByteOrder == QSysInfo::LittleEndian && xi->byte_order == MSBFirst) + || (QSysInfo::ByteOrder == QSysInfo::BigEndian)) + { + for (int i=0; i < image.height(); i++) { + uint *p = (uint*)image.scanLine(i); + uint *end = p + image.width(); + if ((xi->byte_order == LSBFirst && QSysInfo::ByteOrder == QSysInfo::BigEndian) + || (xi->byte_order == MSBFirst && QSysInfo::ByteOrder == QSysInfo::LittleEndian)) { + while (p < end) { + *p = ((*p << 24) & 0xff000000) | ((*p << 8) & 0x00ff0000) + | ((*p >> 8) & 0x0000ff00) | ((*p >> 24) & 0x000000ff); + p++; + } + } else if (xi->byte_order == MSBFirst && QSysInfo::ByteOrder == QSysInfo::BigEndian) { + while (p < end) { + *p = ((*p << 16) & 0x00ff0000) | ((*p >> 16) & 0x000000ff) + | ((*p ) & 0xff00ff00); + p++; + } + } + } + } + + // throw away image data + qSafeXDestroyImage(xi); + + return image; + } + + 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(w, h, format); + if (image.isNull()) // could not create image + return image; + + QImage alpha; + if (x11_mask) { + alpha = mask().toImage(); + } + 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 < h; ++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 < w; 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 = w; // leave loop + y = h; + 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<h; 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.setNumColors(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 < h; i++) { + uchar* asrc = alpha.scanLine(i); + p = image.scanLine(i); + if (ale) { + for (int x = 0; x < w; x++) { + if (asrc[x >> 3] & (1 << (x & 7))) + use[*p] = 1; + ++p; + } + } else { + for (int x = 0; x < w; x++) { + if (asrc[x >> 3] & (0x80 >> (x & 7))) + use[*p] = 1; + ++p; + } + } + } + } else { + for (int i = 0; i < h; 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 < h; 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.setNumColors(ncols); // create color table + image.setColor(trans, 0x00000000); + } else { + image.setNumColors(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 < h; i++) { + uchar* asrc = alpha.scanLine(i); + p = image.scanLine(i); + if (ale) { + for (int x = 0; x < w; x++) { + if (!(asrc[x >> 3] & (1 << (x & 7)))) + *p = trans; + ++p; + } + } else { + for (int x = 0; x < w; x++) { + if (!(asrc[x >> 3] & (1 << (7 -(x & 7))))) + *p = trans; + ++p; + } + } + } + } else { + image.setNumColors(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()); + } + } + + qSafeXDestroyImage(xi); + + 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 + QPixmap pm; + QX11PixmapData *x11Data = static_cast<QX11PixmapData*>(pm.data); + x11Data->uninit = false; + x11Data->xinfo = xinfo; + x11Data->d = d; + x11Data->w = w; + x11Data->h = h; + x11Data->hd = (Qt::HANDLE)XCreatePixmap(X11->display, + RootWindow(X11->display, xinfo.screen()), + w, h, d); +#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; + } +} + +/*! + \internal +*/ +int QPixmap::x11SetDefaultScreen(int screen) +{ + int old = defaultScreen; + defaultScreen = screen; + return old; +} + +/*! + \internal +*/ +void QPixmap::x11SetScreen(int screen) +{ + if (paintingActive()) { + qWarning("QPixmap::x11SetScreen(): Cannot change screens during painting"); + return; + } + + if (data->classId() != QPixmapData::X11Class) + return; + + if (screen < 0) + screen = QX11Info::appScreen(); + + QX11PixmapData *x11Data = static_cast<QX11PixmapData*>(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->uninit = false; + 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; +} + +/*! + Returns information about the configuration of the X display used to display + the widget. + + \warning This function is only available on X11. + + \sa {QPixmap#Pixmap Information}{Pixmap Information} +*/ +const QX11Info &QPixmap::x11Info() const +{ + if (data->classId() == QPixmapData::X11Class) + return static_cast<QX11PixmapData*>(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 (read_only && 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->read_only = false; + } + + if (!that->pengine) + that->pengine = new QX11PaintEngine; + return that->pengine; +} + +/*! + 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} +*/ + +Qt::HANDLE QPixmap::x11PictureHandle() const +{ +#ifndef QT_NO_XRENDER + if (data->classId() == QPixmapData::X11Class) + return static_cast<QX11PixmapData*>(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); + + uninit = false; + xinfo = x11Data->xinfo; + d = x11Data->d; + w = rect.width(); + h = rect.height(); + 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 (read_only && 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 (!read_only) + XRenderFreePicture(X11->display, picture); + } + if (hd && !read_only) + 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); + data->read_only = true; + data->share_mode = mode; + data->uninit = false; + data->w = width; + data->h = height; + 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 0000000..c526402 --- /dev/null +++ b/src/gui/image/qpixmap_x11_p.h @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef 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; + +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(); + + 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; + QPaintEngine* paintEngine() const; + + Qt::HANDLE handle() const { return hd; } + Qt::HANDLE x11ConvertToDefaultDepth(); + +protected: + int metric(QPaintDevice::PaintDeviceMetric metric) const; + +private: + friend class QPixmap; + friend class QBitmap; + friend class QX11PaintEngine; + friend class QX11WindowSurface; + friend class QRasterWindowSurface; + + void release(); + + QBitmap mask_to_bitmap(int screen) const; + static Qt::HANDLE bitmap_to_mask(const QBitmap &, int screen); + void bitmapFromImage(const QImage &image); + + Qt::HANDLE hd; + + int w, h; + short d; + uint uninit : 1; + uint read_only : 1; + + QX11Info xinfo; + Qt::HANDLE x11_mask; + Qt::HANDLE picture; + Qt::HANDLE mask_picture; + Qt::HANDLE hd2; // sorted in the default display depth +#ifndef QT_NO_XRENDER + void convertToARGB32(bool preserveContents = true); +#endif + 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 0000000..4253f8d --- /dev/null +++ b/src/gui/image/qpixmapcache.cpp @@ -0,0 +1,320 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qpixmapcache.h" +#include "qcache.h" +#include "qobject.h" +#include "qdebug.h" + +#include "qpaintengine.h" +#include <private/qimage_p.h> +#include <private/qpixmap_raster_p.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QPixmapCache + + \brief The QPixmapCache class provides an application-wide cache for pixmaps. + + \ingroup environment + \ingroup multimedia + + 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 string (key). If two pixmaps + are inserted into the cache using equal keys, then the last pixmap + will hide the first pixmap. The QHash and QCache classes do + exactly the same. + + The cache becomes full when the total size of all pixmaps in the + cache exceeds cacheLimit(). The initial cache limit is 1024 KB (1 + MB); it is changed with setCacheLimit(). A pixmap takes roughly + (\e{width} * \e{height} * \e{depth})/8 bytes of memory. + + The \e{Qt Quarterly} article + \l{http://doc.trolltech.com/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_OS_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 + +// XXX: hw: is this a general concept we need to abstract? +class QDetachedPixmap : public QPixmap +{ +public: + QDetachedPixmap(const QPixmap &pix) : QPixmap(pix) + { + if (data && data->classId() == QPixmapData::RasterClass) { + QRasterPixmapData *d = static_cast<QRasterPixmapData*>(data); + if (!d->image.isNull() && d->image.d->paintEngine + && !d->image.d->paintEngine->isActive()) + { + delete d->image.d->paintEngine; + d->image.d->paintEngine = 0; + } + } + } +}; + +class QPMCache : public QObject, public QCache<qint64, QDetachedPixmap> +{ + Q_OBJECT +public: + QPMCache() + : QObject(0), + QCache<qint64, QDetachedPixmap>(cache_limit * 1024), + theid(0), ps(0), t(false) { } + ~QPMCache() { } + + void timerEvent(QTimerEvent *); + bool insert(const QString& key, const QPixmap &pixmap, int cost); + bool remove(const QString &key); + + QPixmap *object(const QString &key) const; + +private: + QHash<QString, qint64> cacheKeys; + int theid; + int ps; + bool t; +}; +QT_BEGIN_INCLUDE_NAMESPACE +#include "qpixmapcache.moc" +QT_END_INCLUDE_NAMESPACE + +/* + This is supposed to cut the cache size down by about 80-90% 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 pixmap has been deleted from the cache, kill the + timer so Qt won't keep the CPU from going into sleep mode. +*/ + +void QPMCache::timerEvent(QTimerEvent *) +{ + int mc = maxCost(); + bool nt = totalCost() == ps; + setMaxCost(nt ? totalCost() * 3 / 4 : totalCost() -1); + setMaxCost(mc); + ps = totalCost(); + + QHash<QString, qint64>::iterator it = cacheKeys.begin(); + while (it != cacheKeys.end()) { + if (!contains(it.value())) { + it = cacheKeys.erase(it); + } else { + ++it; + } + } + + if (!size()) { + killTimer(theid); + theid = 0; + } else if (nt != t) { + killTimer(theid); + theid = startTimer(nt ? 10000 : 30000); + t = nt; + } +} + +QPixmap *QPMCache::object(const QString &key) const +{ + return QCache<qint64, QDetachedPixmap>::object(cacheKeys.value(key, -1)); +} + + +bool QPMCache::insert(const QString& key, const QPixmap &pixmap, int cost) +{ + qint64 cacheKey = pixmap.cacheKey(); + if (QCache<qint64, QDetachedPixmap>::object(cacheKey)) { + cacheKeys.insert(key, cacheKey); + return true; + } + bool success = QCache<qint64, QDetachedPixmap>::insert(cacheKey, new QDetachedPixmap(pixmap), cost); + if (success) { + cacheKeys.insert(key, cacheKey); + if (!theid) { + theid = startTimer(30000); + t = false; + } + } + return success; +} + +bool QPMCache::remove(const QString &key) +{ + qint64 cacheKey = cacheKeys.value(key, -1); + cacheKeys.remove(key); + return QCache<qint64, QDetachedPixmap>::remove(cacheKey); +} + +Q_GLOBAL_STATIC(QPMCache, pm_cache) + +/*! + \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 + 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); +} + + +/*! + Looks for a cached pixmap associated with the \a key in the cache. + If the pixmap is found, the function sets \a pm to that pixmap and + returns true; otherwise it leaves \a pm alone and returns false. + + Example: + \snippet doc/src/snippets/code/src_gui_image_qpixmapcache.cpp 1 +*/ + +bool QPixmapCache::find(const QString &key, QPixmap& pm) +{ + QPixmap *ptr = pm_cache()->object(key); + if (ptr) + pm = *ptr; + return ptr != 0; +} + + +/*! + Inserts a copy of the pixmap \a pm 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 &pm) +{ + return pm_cache()->insert(key, pm, pm.width() * pm.height() * pm.depth() / 8); +} + +/*! + Returns the cache limit (in kilobytes). + + The default cache limit is 2048 KB for Embedded, 10240 KB for Desktops. + + \sa setCacheLimit() +*/ + +int QPixmapCache::cacheLimit() +{ + return cache_limit; +} + +/*! + Sets the cache limit to \a n kilobytes. + + The default setting is 1024 kilobytes. + + \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 all pixmaps from the cache. +*/ + +void QPixmapCache::clear() +{ + pm_cache()->clear(); +} + +QT_END_NAMESPACE diff --git a/src/gui/image/qpixmapcache.h b/src/gui/image/qpixmapcache.h new file mode 100644 index 0000000..2750a88 --- /dev/null +++ b/src/gui/image/qpixmapcache.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPIXMAPCACHE_H +#define QPIXMAPCACHE_H + +#include <QtGui/qpixmap.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class Q_GUI_EXPORT QPixmapCache +{ +public: + static int cacheLimit(); + static void setCacheLimit(int); + static QPixmap *find(const QString &key); + static bool find(const QString &key, QPixmap&); + static bool insert(const QString &key, const QPixmap&); + static void remove(const QString &key); + static void clear(); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QPIXMAPCACHE_H diff --git a/src/gui/image/qpixmapdata.cpp b/src/gui/image/qpixmapdata.cpp new file mode 100644 index 0000000..245a866 --- /dev/null +++ b/src/gui/image/qpixmapdata.cpp @@ -0,0 +1,187 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qpixmapdata_p.h" +#include <QtGui/qbitmap.h> +#include <QtGui/qimagereader.h> + +QT_BEGIN_NAMESPACE + +const uchar qt_pixmap_bit_mask[] = { 0x01, 0x02, 0x04, 0x08, + 0x10, 0x20, 0x40, 0x80 }; + +QPixmapData::QPixmapData(PixelType pixelType, int objectId) + : ref(0), detach_no(0), type(pixelType), id(objectId), ser_no(0), is_cached(false) +{ + +} + +QPixmapData::~QPixmapData() +{ +} + +void QPixmapData::fromFile(const QString &fileName, const char *format, + Qt::ImageConversionFlags flags) +{ + const QImage image = QImageReader(fileName, format).read(); + if (image.isNull()) + return; + + fromImage(image, flags); +} + +void QPixmapData::copy(const QPixmapData *data, const QRect &rect) +{ + fromImage(data->toImage().copy(rect), Qt::AutoColor); +} + +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.setNumColors(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::buffer() +{ + return 0; +} + +QT_END_NAMESPACE diff --git a/src/gui/image/qpixmapdata_p.h b/src/gui/image/qpixmapdata_p.h new file mode 100644 index 0000000..df26c42 --- /dev/null +++ b/src/gui/image/qpixmapdata_p.h @@ -0,0 +1,141 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef 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 Q_GUI_EXPORT QPixmapData +{ +public: + enum PixelType { + // WARNING: Do not change the first two + // Must match QPixmap::Type + PixmapType, BitmapType + }; + enum ClassId { RasterClass, X11Class, MacClass, DirectFBClass, + OpenGLClass, OpenVGClass, CustomClass = 1024 }; + + QPixmapData(PixelType pixelpType, int classId); + virtual ~QPixmapData(); + + virtual void resize(int width, int height) = 0; + virtual void fromImage(const QImage &image, + Qt::ImageConversionFlags flags) = 0; + virtual void fromFile(const QString &filename, 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 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(); + + int width() const { return metric(QPaintDevice::PdmWidth); } + int height() const { return metric(QPaintDevice::PdmHeight); } + int numColors() const { return metric(QPaintDevice::PdmNumColors); } + int depth() const { return metric(QPaintDevice::PdmDepth); } + +protected: + void setSerialNumber(int serNo); + +private: + friend class QPixmap; + friend class QGLContextPrivate; + + QAtomicInt ref; + int detach_no; + + PixelType type; + int id; + int ser_no; + uint is_cached; +}; + +#ifdef Q_WS_WIN +#ifndef Q_OS_WINCE +QPixmap convertHIconToPixmap( const HICON icon); +#else +QPixmap convertHIconToPixmap( const HICON icon, bool large = false); +#endif +QPixmap loadIconFromShell32( int resourceId, int size ); +#endif + +# 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 0000000..699489d --- /dev/null +++ b/src/gui/image/qpixmapdatafactory.cpp @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qpixmapdatafactory_p.h" + +#ifdef Q_WS_QWS +# include <QtGui/qscreen_qws.h> +#endif +#ifdef Q_WS_X11 +# include <private/qpixmap_x11_p.h> +#endif +#ifdef Q_WS_WIN +# include <private/qpixmap_raster_p.h> +#endif +#ifdef Q_WS_MAC +# include <private/qpixmap_mac_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); +#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 0000000..65e3f11 --- /dev/null +++ b/src/gui/image/qpixmapdatafactory_p.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef 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 Q_GUI_EXPORT 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 0000000..8631c8c --- /dev/null +++ b/src/gui/image/qpixmapfilter.cpp @@ -0,0 +1,849 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <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" + +QT_BEGIN_NAMESPACE + +class QPixmapFilterPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QPixmapFilter) +public: + QPixmapFilter::FilterType type; +}; + +/*! + \class QPixmapFilter + \since 4.5 + \ingroup multimedia + + \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 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 multimedia + + \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; +} + + +/*! + \reimp + + \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++; + } +} + +/*! + \reimp + + \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; + + QPixmapFilter *filter = painter->paintEngine() && painter->paintEngine()->isExtended() ? + static_cast<QPaintEngineEx *>(painter->paintEngine())->createPixmapFilter(type()) : 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); + delete convolutionFilter; + 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); + } +} + +// 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 multimedia + + \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; +}; + +/*! + 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) +{ + d_func()->color = QColor(0, 0, 192); +} + +/*! + 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; +} + +/*! + \reimp + + \internal +*/ +void QPixmapColorizeFilter::draw(QPainter *painter, const QPointF &dest, const QPixmap &src, const QRectF &srcRect) const +{ + Q_D(const QPixmapColorizeFilter); + QPixmapFilter *filter = painter->paintEngine() && painter->paintEngine()->isExtended() ? + static_cast<QPaintEngineEx *>(painter->paintEngine())->createPixmapFilter(type()) : 0; + QPixmapColorizeFilter *colorizeFilter = static_cast<QPixmapColorizeFilter*>(filter); + if (colorizeFilter) { + colorizeFilter->setColor(d->color); + colorizeFilter->draw(painter, dest, src, srcRect); + delete colorizeFilter; + return; + } + + // falling back to raster implementation + + 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 (srcImage.hasAlphaChannel()) + destImage.setAlphaChannel(srcImage.alphaChannel()); + + painter->drawImage(dest, destImage); +} + +class QPixmapDropShadowFilterPrivate : public QPixmapFilterPrivate +{ +public: + QPixmapDropShadowFilterPrivate() + : offset(8, 8), + radius(1), + color(63, 63, 63, 255) { + } + + QPointF offset; + qreal radius; + QColor color; + + QPixmapConvolutionFilter *convolution; +}; + +/*! + \class QPixmapDropShadowFilter + \since 4.5 + \ingroup multimedia + + \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) +{ + Q_D(QPixmapDropShadowFilter); + d->convolution = new QPixmapConvolutionFilter; + + setBlurRadius(1); +} + +/*! + Destroys drop shadow filter. + + \internal +*/ +QPixmapDropShadowFilter::~QPixmapDropShadowFilter() +{ + Q_D(QPixmapDropShadowFilter); + delete d->convolution; +} + +/*! + 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; + + int dim = 2 * qRound(radius) + 1; + QVarLengthArray<qreal> arr(dim * dim); + qreal f = 1 / qreal(dim * dim); + for (int i = 0; i < dim * dim; ++i) + arr[i] = f; + d->convolution->setConvolutionKernel(arr.data(), dim, dim); +} + +/*! + 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 +*/ + +/*! + \reimp + + \internal + */ +QRectF QPixmapDropShadowFilter::boundingRectFor(const QRectF &rect) const +{ + Q_D(const QPixmapDropShadowFilter); + + qreal x1 = qMin(rect.left(), rect.left() + d->offset.x() - d->radius); + qreal y1 = qMin(rect.top(), rect.top() + d->offset.y() - d->radius); + qreal x2 = qMax(rect.right(), rect.right() + d->offset.x() + d->radius); + qreal y2 = qMax(rect.bottom(), rect.bottom() + d->offset.y() + d->radius); + + return QRectF(x1, y1, x2 - x1, y2 - y1); +} + +/*! + \reimp + + \internal + */ +void QPixmapDropShadowFilter::draw(QPainter *p, + const QPointF &pos, + const QPixmap &px, + const QRectF &src) const +{ + Q_D(const QPixmapDropShadowFilter); + + QPixmap tmp = src.isNull() ? px : px.copy(src.toRect()); + QPainter tmpPainter(&tmp); + + // blacken the image... + tmpPainter.setCompositionMode(QPainter::CompositionMode_SourceIn); + tmpPainter.fillRect(0, 0, tmp.width(), tmp.height(), d->color); + tmpPainter.end(); + + // draw the blurred drop shadow... + d->convolution->draw(p, pos + d->offset, tmp); + + // Draw the actual pixmap... + p->drawPixmap(pos, px, src); +} +QT_END_NAMESPACE diff --git a/src/gui/image/qpixmapfilter_p.h b/src/gui/image/qpixmapfilter_p.h new file mode 100644 index 0000000..d494c98 --- /dev/null +++ b/src/gui/image/qpixmapfilter_p.h @@ -0,0 +1,165 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef 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> + +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, + + 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 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 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 // QPIXMAPFILTER_H diff --git a/src/gui/image/qpnghandler.cpp b/src/gui/image/qpnghandler.cpp new file mode 100644 index 0000000..0a55910 --- /dev/null +++ b/src/gui/image/qpnghandler.cpp @@ -0,0 +1,973 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "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> + +#include <png.h> +#include <pngconf.h> + +#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 + +/* + All PNG files load to the minimal QImage equivalent. + + All QImage formats output to reasonably efficient PNG equivalents. + Never to grayscale. +*/ + +#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) +{ + QIODevice *in = (QIODevice *)png_get_io_ptr(png_ptr); + + 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_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 && info_ptr->channels == 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.setNumColors(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.setNumColors(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_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { + const int g = info_ptr->trans_values.gray; + if (g < ncols) { + image.setColor(g, 0); + } + } + } + } else if (color_type == PNG_COLOR_TYPE_PALETTE + && png_get_valid(png_ptr, info_ptr, PNG_INFO_PLTE) + && info_ptr->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; + } + image.setNumColors(info_ptr->num_palette); + int i = 0; + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { + while (i < info_ptr->num_trans) { + image.setColor(i, qRgba( + info_ptr->palette[i].red, + info_ptr->palette[i].green, + info_ptr->palette[i].blue, + info_ptr->trans[i] + ) + ); + i++; + } + } + while (i < info_ptr->num_palette) { + image.setColor(i, qRgba( + info_ptr->palette[i].red, + info_ptr->palette[i].green, + info_ptr->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 + +class QPngHandlerPrivate +{ +public: + enum State { + Ready, + ReadHeader, + 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; + + png_struct *png_ptr; + png_info *info_ptr; + png_info *end_info; + png_byte **row_pointers; + + bool readPngHeader(); + bool readPngImage(QImage *image); + + QImage::Format readImageFormat(); + + State state; + + QPngHandler *q; +}; + +/*! + \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, q->device(), iod_read_fn); + png_read_info(png_ptr, info_ptr); + +#ifndef QT_NO_IMAGE_TEXT + png_textp text_ptr; + int num_text=0; + png_get_text(png_ptr,info_ptr,&text_ptr,&num_text); + + while (num_text--) { + QString key, value; +#if defined(PNG_iTXt_SUPPORTED) + if (text_ptr->lang) { + QTextCodec *codec = QTextCodec::codecForName(text_ptr->lang); + if (codec) { + key = codec->toUnicode(text_ptr->lang_key); + value = codec->toUnicode(QByteArray(text_ptr->text, text_ptr->itxt_length)); + } else { + key = QString::fromLatin1(text_ptr->key); + value = QString::fromLatin1(QByteArray(text_ptr->text, int(text_ptr->text_length))); + } + } else +#endif + { + key = QString::fromLatin1(text_ptr->key); + value = QString::fromLatin1(QByteArray(text_ptr->text, int(text_ptr->text_length))); + } + if (!description.isEmpty()) + description += QLatin1String("\n\n"); + description += key + QLatin1String(": ") + value.simplified(); + text_ptr++; + } +#endif + + 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)); + +#ifndef QT_NO_IMAGE_TEXT + png_textp text_ptr; + int num_text=0; + png_get_text(png_ptr,info_ptr,&text_ptr,&num_text); + while (num_text--) { + outImage->setText(text_ptr->key,0,QString::fromAscii(text_ptr->text)); + text_ptr++; + } + + foreach (const QString &pair, description.split(QLatin1String("\n\n"))) { + int index = pair.indexOf(QLatin1Char(':')); + if (index >= 0 && pair.indexOf(QLatin1Char(' ')) < index) { + outImage->setText(QLatin1String("Description"), pair.simplified()); + } else { + QString key = pair.left(index); + outImage->setText(key, pair.mid(index + 2).simplified()); + } + } +#endif + + png_read_end(png_ptr, end_info); + 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->numColors(); + for (int y=0; y<(int)height; ++y) { + uchar *p = outImage->scanLine(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; + if (info_ptr->color_type == PNG_COLOR_TYPE_GRAY) { + // Black & White or 8-bit grayscale + if (info_ptr->bit_depth == 1 && info_ptr->channels == 1) { + format = QImage::Format_Mono; + } else if (info_ptr->bit_depth == 16 && png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { + format = QImage::Format_ARGB32; + } else { + format = QImage::Format_Indexed8; + } + } else if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE + && png_get_valid(png_ptr, info_ptr, PNG_INFO_PLTE) + && info_ptr->num_palette <= 256) + { + // 1-bit and 8-bit color + if (info_ptr->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 (info_ptr->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 (!(info_ptr->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()]; + + QMap<QString, QString>::ConstIterator it = text.constBegin(); + int i = 0; + while (it != text.constEnd()) { + QString t = it.value(); + if (t.length() < 40) + text_ptr[i].compression = PNG_TEXT_COMPRESSION_NONE; + else + text_ptr[i].compression = PNG_TEXT_COMPRESSION_zTXt; + text_ptr[i].key = qstrdup(it.key().left(79).toLatin1().constData()); + +#ifndef PNG_iTXt_SUPPORTED + QByteArray value = it.value().toLatin1(); + text_ptr[i].text = qstrdup(value.constData()); + text_ptr[i].text_length = value.size(); +#else + QByteArray value = it.value().toUtf8(); + text_ptr[i].text = qstrdup(value.constData()); + text_ptr[i].text_length = 0; + text_ptr[i].itxt_length = value.size(); + text_ptr[i].lang = "UTF-8"; + text_ptr[i].lang_key = qstrdup(it.key().toUtf8().constData()); +#endif + ++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_in, int quality_in, const QString &description, + int off_x_in, int off_y_in) +{ +#ifdef QT_NO_IMAGE_TEXT + Q_UNUSED(description); +#endif + + QImage image; + switch (image_in.format()) { + case QImage::Format_ARGB32_Premultiplied: + case QImage::Format_ARGB4444_Premultiplied: + case QImage::Format_ARGB8555_Premultiplied: + case QImage::Format_ARGB8565_Premultiplied: + case QImage::Format_ARGB6666_Premultiplied: + image = image_in.convertToFormat(QImage::Format_ARGB32); + break; + case QImage::Format_RGB16: + case QImage::Format_RGB444: + case QImage::Format_RGB555: + case QImage::Format_RGB666: + case QImage::Format_RGB888: + image = image_in.convertToFormat(QImage::Format_RGB32); + break; + default: + image = image_in; + break; + } + + 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_bytep* row_pointers; + + 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); + } + + if (gamma != 0.0) { + png_set_gAMA(png_ptr, info_ptr, 1.0/gamma); + } + + png_set_write_fn(png_ptr, (void*)this, qpiw_write_fn, qpiw_flush_fn); + + info_ptr->channels = + (image.depth() == 32) + ? (image.format() == QImage::Format_RGB32 ? 3 : 4) + : 1; + + png_set_IHDR(png_ptr, info_ptr, image.width(), image.height(), + image.depth() == 1 ? 1 : 8 /* per channel */, + image.depth() == 32 + ? image.format() == QImage::Format_RGB32 + ? PNG_COLOR_TYPE_RGB + : PNG_COLOR_TYPE_RGB_ALPHA + : PNG_COLOR_TYPE_PALETTE, 0, 0, 0); + + + //png_set_sBIT(png_ptr, info_ptr, 8); + info_ptr->sig_bit.red = 8; + info_ptr->sig_bit.green = 8; + info_ptr->sig_bit.blue = 8; + + if (image.format() == QImage::Format_MonoLSB) + png_set_packswap(png_ptr); + + png_colorp palette = 0; + png_bytep copy_trans = 0; + if (image.numColors()) { + // Paletted + int num_palette = image.numColors(); + palette = new png_color[num_palette]; + png_set_PLTE(png_ptr, info_ptr, palette, num_palette); + int* trans = new int[num_palette]; + int num_trans = 0; + for (int i=0; i<num_palette; i++) { + QRgb rgb=image.color(i); + info_ptr->palette[i].red = qRed(rgb); + info_ptr->palette[i].green = qGreen(rgb); + info_ptr->palette[i].blue = qBlue(rgb); + trans[i] = rgb >> 24; + if (trans[i] < 255) { + num_trans = i+1; + } + } + if (num_trans) { + copy_trans = new png_byte[num_trans]; + for (int i=0; i<num_trans; i++) + copy_trans[i] = trans[i]; + png_set_tRNS(png_ptr, info_ptr, copy_trans, num_trans, 0); + } + delete [] trans; + } + + if (image.format() != QImage::Format_RGB32) { + info_ptr->sig_bit.alpha = 8; + } + + // 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) + if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) { + 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 (image.format() == QImage::Format_RGB32) + 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); + } + + 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); + + const uchar *data = image.bits(); + int bpl = image.bytesPerLine(); + row_pointers = new png_bytep[height]; + uint y; + for (y=0; y<height; y++) { + row_pointers[y] = (png_bytep)(data + y * bpl); + } + png_write_image(png_ptr, row_pointers); + delete [] row_pointers; + + png_write_end(png_ptr, info_ptr); + frames_written++; + + if (palette) + delete [] palette; + if (copy_trans) + delete [] copy_trans; + + 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) { + if (!canRead(device())) + return false; + setFormat("png"); + return true; + } + return d->state != QPngHandlerPrivate::Error; +} + +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(d->info_ptr->width, d->info_ptr->height); + else if (option == ImageFormat) + return d->readImageFormat(); + return 0; +} + +void QPngHandler::setOption(ImageOption option, const QVariant &value) +{ + if (option == Gamma) + d->gamma = value.toDouble(); + 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_p.h b/src/gui/image/qpnghandler_p.h new file mode 100644 index 0000000..543fa0f --- /dev/null +++ b/src/gui/image/qpnghandler_p.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef 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 0000000..902f764 --- /dev/null +++ b/src/gui/image/qppmhandler.cpp @@ -0,0 +1,531 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "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 == '#') + 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->setNumColors(2); + outImage->setColor(0, qRgb(255,255,255)); // white + outImage->setColor(1, qRgb(0,0,0)); // black + } else if (nbits == 8) { // graymap + outImage->setNumColors(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_MonoLSB); + } 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.numColors() == 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) { + if (!canRead(device(), &subType)) + return false; + setFormat(subType); + return true; + } + return state != Error; +} + +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 0000000..c40c51d --- /dev/null +++ b/src/gui/image/qppmhandler_p.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef 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/qxbmhandler.cpp b/src/gui/image/qxbmhandler.cpp new file mode 100644 index 0000000..351bc25 --- /dev/null +++ b/src/gui/image/qxbmhandler.cpp @@ -0,0 +1,350 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <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; + char buf[buflen + 1]; + QRegExp r1(QLatin1String("^#define[ \t]+[a-zA-Z0-9._]+[ \t]+")); + QRegExp r2(QLatin1String("[0-9]+")); + + qint64 readBytes = 0; + + // "#define .._width <num>" + readBytes = device->readLine(buf, buflen); + if (readBytes <= 0) + return false; + buf[readBytes - 1] = '\0'; + + // skip initial comment, if any + while (buf[0] != '#' && (readBytes = device->readLine( buf, buflen )) > 0) {} + + if (readBytes <= 0) + return false; + buf[readBytes - 1] = '\0'; + QString sbuf; + sbuf = QString::fromLatin1(buf); + + 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->setNumColors(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) { + if (!canRead(device())) + return false; + setFormat("xbm"); + return true; + } + return state != Error; +} + +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 0000000..ffbbfe4 --- /dev/null +++ b/src/gui/image/qxbmhandler_p.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef 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 0000000..90bd2ab --- /dev/null +++ b/src/gui/image/qxpmhandler.cpp @@ -0,0 +1,1309 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "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 + +#include <stdlib.h> + +#if defined(Q_OS_WINCE) +#include "qguifunctions_wince.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" } }; + +#if defined(Q_C_CALLBACKS) +extern "C" { +#endif +static int rgb_cmp(const void *d1, const void *d2) +{ + return qstricmp(((XPMRGBData *)d1)->name, ((XPMRGBData *)d2)->name); +} +#if defined(Q_C_CALLBACKS) +} +#endif + +static bool qt_get_named_xpm_rgb(const char *name_no_space, QRgb *rgb) +{ + XPMRGBData x; + x.name = name_no_space; + // Funtion bsearch() is supposed to be + // void *bsearch(const void *key, const void *base, ... + // So why (char*)? Are there broken bsearch() declarations out there? + XPMRGBData *r = (XPMRGBData *)bsearch((char *)&x, (char *)xpmRgbTbl, xpmRgbTblSize, + sizeof(XPMRGBData), rgb_cmp); + if (r) { + *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 > 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.setNumColors(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())) { + setFormat("xpm"); + return true; + } + return state != Error; +} + +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 0000000..5c2f809 --- /dev/null +++ b/src/gui/image/qxpmhandler_p.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef 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 |