diff options
Diffstat (limited to 'src/gui/image')
-rw-r--r-- | src/gui/image/qicon.cpp | 106 | ||||
-rw-r--r-- | src/gui/image/qicon_p.h | 2 | ||||
-rw-r--r-- | src/gui/image/qiconengine.cpp | 102 | ||||
-rw-r--r-- | src/gui/image/qiconengine.h | 12 | ||||
-rw-r--r-- | src/gui/image/qiconloader.cpp | 48 | ||||
-rw-r--r-- | src/gui/image/qiconloader_p.h | 5 | ||||
-rw-r--r-- | src/gui/image/qimage.cpp | 165 | ||||
-rw-r--r-- | src/gui/image/qimage.h | 1 | ||||
-rw-r--r-- | src/gui/image/qimage_conversions.cpp | 56 | ||||
-rw-r--r-- | src/gui/image/qimage_darwin.mm | 34 | ||||
-rw-r--r-- | src/gui/image/qimagereader.cpp | 37 | ||||
-rw-r--r-- | src/gui/image/qimagewriter.cpp | 14 | ||||
-rw-r--r-- | src/gui/image/qpixmap_raster.cpp | 25 | ||||
-rw-r--r-- | src/gui/image/qpixmap_raster_p.h | 2 | ||||
-rw-r--r-- | src/gui/image/qpnghandler.cpp | 2 | ||||
-rw-r--r-- | src/gui/image/qppmhandler.cpp | 59 | ||||
-rw-r--r-- | src/gui/image/qxbmhandler.cpp | 2 | ||||
-rw-r--r-- | src/gui/image/qxpmhandler.cpp | 3 |
18 files changed, 431 insertions, 244 deletions
diff --git a/src/gui/image/qicon.cpp b/src/gui/image/qicon.cpp index 0d2f55b1c2..07dcf8b726 100644 --- a/src/gui/image/qicon.cpp +++ b/src/gui/image/qicon.cpp @@ -97,7 +97,11 @@ QT_BEGIN_NAMESPACE \value On Display the pixmap when the widget is in an "on" state */ -static QBasicAtomicInt serialNumCounter = Q_BASIC_ATOMIC_INITIALIZER(1); +static int nextSerialNumCounter() +{ + static QBasicAtomicInt serial = Q_BASIC_ATOMIC_INITIALIZER(0); + return 1 + serial.fetchAndAddRelaxed(1); +} static void qt_cleanup_icon_cache(); namespace { @@ -137,9 +141,9 @@ static qreal qt_effective_device_pixel_ratio(QWindow *window = 0) return qApp->devicePixelRatio(); // Don't know which window to target. } -QIconPrivate::QIconPrivate() - : engine(0), ref(1), - serialNum(serialNumCounter.fetchAndAddRelaxed(1)), +QIconPrivate::QIconPrivate(QIconEngine *e) + : engine(e), ref(1), + serialNum(nextSerialNumCounter()), detach_no(0), is_mask(false) { @@ -605,6 +609,51 @@ QFactoryLoader *qt_iconEngineFactoryLoader() \note QIcon needs a QGuiApplication instance before the icon is created. + \section1 High DPI Icons + + There are two ways that QIcon supports \l {High DPI Displays}{high DPI} + icons: via \l addFile() and \l fromTheme(). + + \l addFile() is useful if you have your own custom directory structure and do + not need to use the \l {Icon Theme Specification}{freedesktop.org Icon Theme + Specification}. Icons created via this approach use Qt's \l {High Resolution + Versions of Images}{"@nx" high DPI syntax}. + + Using \l fromTheme() is necessary if you plan on following the Icon Theme + Specification. To make QIcon use the high DPI version of an image, add an + additional entry to the appropriate \c index.theme file: + + \badcode + [Icon Theme] + Name=Test + Comment=Test Theme + + Directories=32x32/actions,32x32@2/actions + + [32x32/actions] + Size=32 + Context=Actions + Type=Fixed + + # High DPI version of the entry above. + [32x32@2/actions] + Size=32 + Scale=2 + Type=Fixed + \endcode + + Your icon theme directory would then look something like this: + + \badcode + ├── 32x32 + │ └── actions + │ └── appointment-new.png + ├── 32x32@2 + │ └── actions + │ └── appointment-new.png + └── index.theme + \endcode + \sa {fowler}{GUI Design Handbook: Iconic Label}, {Icons Example} */ @@ -651,7 +700,7 @@ QIcon::QIcon(const QIcon &other) 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 + The file name can 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 @@ -673,9 +722,8 @@ QIcon::QIcon(const QString &fileName) ownership of the engine. */ QIcon::QIcon(QIconEngine *engine) - :d(new QIconPrivate) + :d(new QIconPrivate(engine)) { - d->engine = engine; } /*! @@ -848,9 +896,10 @@ QPixmap QIcon::pixmap(QWindow *window, const QSize &size, Mode mode, State state } // Try get a pixmap that is big enough to be displayed at device pixel resolution. - QPixmap pixmap = d->engine->pixmap(size * devicePixelRatio, mode, state); - pixmap.setDevicePixelRatio(d->pixmapDevicePixelRatio(devicePixelRatio, size, pixmap.size())); - return pixmap; + QIconEngine::ScaledPixmapArgument scalePixmapArg = { size * devicePixelRatio, mode, state, devicePixelRatio, QPixmap() }; + d->engine->virtual_hook(QIconEngine::ScaledPixmapHook, reinterpret_cast<void*>(&scalePixmapArg)); + scalePixmapArg.pixmap.setDevicePixelRatio(d->pixmapDevicePixelRatio(devicePixelRatio, size, scalePixmapArg.pixmap.size())); + return scalePixmapArg.pixmap; } /*! @@ -950,8 +999,7 @@ void QIcon::detach() d = 0; return; } else if (d->ref.load() != 1) { - QIconPrivate *x = new QIconPrivate; - x->engine = d->engine->clone(); + QIconPrivate *x = new QIconPrivate(d->engine->clone()); if (!d->ref.deref()) delete d; d = x; @@ -974,10 +1022,8 @@ void QIcon::addPixmap(const QPixmap &pixmap, Mode mode, State state) if (pixmap.isNull()) return; detach(); - if (!d) { - d = new QIconPrivate; - d->engine = new QPixmapIconEngine; - } + if (!d) + d = new QIconPrivate(new QPixmapIconEngine); d->engine->addPixmap(pixmap, mode, state); } @@ -1003,7 +1049,7 @@ static QIconEngine *iconEngineFromSuffix(const QString &fileName, const QString 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 + The file name can 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 @@ -1037,8 +1083,7 @@ void QIcon::addFile(const QString &fileName, const QSize &size, Mode mode, State if (!engine) engine = iconEngineFromSuffix(fileName, QMimeDatabase().mimeTypeForFile(info).preferredSuffix()); #endif // !QT_NO_MIMETYPE - d = new QIconPrivate; - d->engine = engine ? engine : new QPixmapIconEngine; + d = new QIconPrivate(engine ? engine : new QPixmapIconEngine); } d->engine->addFile(fileName, size, mode, state); @@ -1240,12 +1285,10 @@ bool QIcon::hasThemeIcon(const QString &name) */ void QIcon::setIsMask(bool isMask) { - if (!d) { - d = new QIconPrivate; - d->engine = new QPixmapIconEngine; - } else { + if (!d) + d = new QIconPrivate(new QPixmapIconEngine); + else detach(); - } d->is_mask = isMask; } @@ -1326,22 +1369,17 @@ QDataStream &operator>>(QDataStream &s, QIcon &icon) QString key; s >> key; if (key == QLatin1String("QPixmapIconEngine")) { - icon.d = new QIconPrivate; - QIconEngine *engine = new QPixmapIconEngine; - icon.d->engine = engine; - engine->read(s); + icon.d = new QIconPrivate(new QPixmapIconEngine); + icon.d->engine->read(s); } else if (key == QLatin1String("QIconLoaderEngine")) { - icon.d = new QIconPrivate; - QIconEngine *engine = new QIconLoaderEngine(); - icon.d->engine = engine; - engine->read(s); + icon.d = new QIconPrivate(new QIconLoaderEngine()); + icon.d->engine->read(s); } else { const int index = loader()->indexOf(key); if (index != -1) { if (QIconEnginePlugin *factory = qobject_cast<QIconEnginePlugin*>(loader()->instance(index))) { if (QIconEngine *engine= factory->create()) { - icon.d = new QIconPrivate; - icon.d->engine = engine; + icon.d = new QIconPrivate(engine); engine->read(s); } // factory } // instance diff --git a/src/gui/image/qicon_p.h b/src/gui/image/qicon_p.h index a978a72192..aa358e88af 100644 --- a/src/gui/image/qicon_p.h +++ b/src/gui/image/qicon_p.h @@ -64,7 +64,7 @@ QT_BEGIN_NAMESPACE class QIconPrivate { public: - QIconPrivate(); + explicit QIconPrivate(QIconEngine *e); ~QIconPrivate() { delete engine; diff --git a/src/gui/image/qiconengine.cpp b/src/gui/image/qiconengine.cpp index 0ba9844f7a..1f8e5f321a 100644 --- a/src/gui/image/qiconengine.cpp +++ b/src/gui/image/qiconengine.cpp @@ -169,6 +169,12 @@ void QIconEngine::addFile(const QString &/*fileName*/, const QSize &/*size*/, QI bool that can be set to true if the icon is null. This enum value was added in Qt 5.7. + \value ScaledPixmapHook Provides a way to get a pixmap that is scaled + according to the given scale (typically equal to the \l {Glossary Of High + DPI Terms}{device pixel ratio}). The \a data argument of the virtual_hook() + function is a \l ScaledPixmapArgument pointer that contains both the input and + output arguments. This enum value was added in Qt 5.9. + \sa virtual_hook() */ @@ -207,6 +213,60 @@ void QIconEngine::addFile(const QString &/*fileName*/, const QSize &/*size*/, QI vectorial format normally return an empty list. */ +/*! + \class QIconEngine::ScaledPixmapArgument + \since 5.9 + + \inmodule QtGui + + This struct represents arguments to the virtual_hook() function when + the \a id parameter is QIconEngine::ScaledPixmapHook. + + The struct provides a way for icons created via \l QIcon::fromTheme() + to return pixmaps that are designed for the current \l {Glossary Of High + DPI Terms}{device pixel ratio}. The scale for such an icon is specified + using the \l {Icon Theme Specification - Directory Layout}{Scale directory key} + in the appropriate \c index.theme file. + + Icons created via other approaches will return the same result as a call to + \l pixmap() would, and continue to benefit from Qt's \l {High Resolution + Versions of Images}{"@nx" high DPI syntax}. + + \sa virtual_hook(), QIconEngine::IconEngineHook, {High DPI Icons} + */ + +/*! + \variable QIconEngine::ScaledPixmapArgument::size + \brief The requested size of the pixmap. +*/ + +/*! + \variable QIconEngine::ScaledPixmapArgument::mode + \brief The requested mode of the pixmap. + + \sa QIcon::Mode +*/ + +/*! + \variable QIconEngine::ScaledPixmapArgument::state + \brief The requested state of the pixmap. + + \sa QIcon::State +*/ + +/*! + \variable QIconEngine::ScaledPixmapArgument::scale + \brief The requested scale of the pixmap. +*/ + +/*! + \variable QIconEngine::ScaledPixmapArgument::pixmap + + \brief The pixmap that is the best match for the given \l size, \l mode, \l + \state, and \l scale. This is an output parameter that is set after calling + \l virtual_hook(). +*/ + /*! Returns a key that identifies this icon engine. @@ -262,6 +322,13 @@ void QIconEngine::virtual_hook(int id, void *data) arg.sizes.clear(); break; } + case QIconEngine::ScaledPixmapHook: { + // We don't have any notion of scale besides "@nx", so just call pixmap() here. + QIconEngine::ScaledPixmapArgument &arg = + *reinterpret_cast<QIconEngine::ScaledPixmapArgument*>(data); + arg.pixmap = pixmap(arg.size, arg.mode, arg.state); + break; + } default: break; } @@ -273,9 +340,7 @@ void QIconEngine::virtual_hook(int id, void *data) 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. + \include qiconengine-virtualhookhelper.qdocinc */ QList<QSize> QIconEngine::availableSizes(QIcon::Mode mode, QIcon::State state) const { @@ -291,9 +356,7 @@ QList<QSize> QIconEngine::availableSizes(QIcon::Mode mode, QIcon::State state) c Returns the name used to create the engine, if available. - \note This is a helper method and the actual work is done by - virtual_hook() method, hence this method depends on icon engine support - and may not work with all icon engines. + \include qiconengine-virtualhookhelper.qdocinc */ QString QIconEngine::iconName() const { @@ -306,6 +369,8 @@ QString QIconEngine::iconName() const \since 5.7 Returns true if this icon engine represent a null QIcon. + + \include qiconengine-virtualhookhelper.qdocinc */ bool QIconEngine::isNull() const { @@ -314,4 +379,29 @@ bool QIconEngine::isNull() const return isNull; } +/*! + \since 5.9 + + Returns a pixmap for the given \a size, \a mode, \a state and \a scale. + + The \a scale argument is typically equal to the \l {Glossary Of High DPI + Terms}{device pixel ratio} of the display. + + \include qiconengine-virtualhookhelper.qdocinc + + \note Some engines may cast \a scale to an integer. + + \sa ScaledPixmapArgument +*/ +QPixmap QIconEngine::scaledPixmap(const QSize &size, QIcon::Mode mode, QIcon::State state, qreal scale) +{ + ScaledPixmapArgument arg; + arg.size = size; + arg.mode = mode; + arg.state = state; + arg.scale = scale; + const_cast<QIconEngine *>(this)->virtual_hook(QIconEngine::ScaledPixmapHook, reinterpret_cast<void*>(&arg)); + return arg.pixmap; +} + QT_END_NAMESPACE diff --git a/src/gui/image/qiconengine.h b/src/gui/image/qiconengine.h index 783770cd30..0c67ef2686 100644 --- a/src/gui/image/qiconengine.h +++ b/src/gui/image/qiconengine.h @@ -65,7 +65,7 @@ public: virtual bool read(QDataStream &in); virtual bool write(QDataStream &out) const; - enum IconEngineHook { AvailableSizesHook = 1, IconNameHook, IsNullHook }; + enum IconEngineHook { AvailableSizesHook = 1, IconNameHook, IsNullHook, ScaledPixmapHook }; struct AvailableSizesArgument { @@ -79,6 +79,16 @@ public: virtual QString iconName() const; bool isNull() const; // ### Qt6 make virtual + QPixmap scaledPixmap(const QSize &size, QIcon::Mode mode, QIcon::State state, qreal scale); // ### Qt6 make virtual + + struct ScaledPixmapArgument + { + QSize size; + QIcon::Mode mode; + QIcon::State state; + qreal scale; + QPixmap pixmap; + }; virtual void virtual_hook(int id, void *data); diff --git a/src/gui/image/qiconloader.cpp b/src/gui/image/qiconloader.cpp index 324f13a17b..d72c05a3c5 100644 --- a/src/gui/image/qiconloader.cpp +++ b/src/gui/image/qiconloader.cpp @@ -47,6 +47,7 @@ #include <qpa/qplatformtheme.h> #include <QtGui/QIconEngine> #include <QtGui/QPalette> +#include <QtCore/qmath.h> #include <QtCore/QList> #include <QtCore/QDir> #include <QtCore/QSettings> @@ -347,6 +348,10 @@ QIconTheme::QIconTheme(const QString &themeName) dirInfo.maxSize = indexReader.value(directoryKey + QLatin1String("/MaxSize"), size).toInt(); + + dirInfo.scale = indexReader.value(directoryKey + + QLatin1String("/Scale"), + 1).toInt(); m_keyList.append(dirInfo); } } @@ -553,8 +558,11 @@ void QIconLoaderEngine::paint(QPainter *painter, const QRect &rect, * This algorithm is defined by the freedesktop spec: * http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html */ -static bool directoryMatchesSize(const QIconDirInfo &dir, int iconsize) +static bool directoryMatchesSize(const QIconDirInfo &dir, int iconsize, int iconscale) { + if (dir.scale != iconscale) + return false; + if (dir.type == QIconDirInfo::Fixed) { return dir.size == iconsize; @@ -575,24 +583,25 @@ static bool directoryMatchesSize(const QIconDirInfo &dir, int iconsize) * This algorithm is defined by the freedesktop spec: * http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html */ -static int directorySizeDistance(const QIconDirInfo &dir, int iconsize) +static int directorySizeDistance(const QIconDirInfo &dir, int iconsize, int iconscale) { + const int scaledIconSize = iconsize * iconscale; if (dir.type == QIconDirInfo::Fixed) { - return qAbs(dir.size - iconsize); + return qAbs(dir.size * dir.scale - scaledIconSize); } else if (dir.type == QIconDirInfo::Scalable) { - if (iconsize < dir.minSize) - return dir.minSize - iconsize; - else if (iconsize > dir.maxSize) - return iconsize - dir.maxSize; + if (scaledIconSize < dir.minSize * dir.scale) + return dir.minSize * dir.scale - scaledIconSize; + else if (scaledIconSize > dir.maxSize * dir.scale) + return scaledIconSize - dir.maxSize * dir.scale; else return 0; } else if (dir.type == QIconDirInfo::Threshold) { - if (iconsize < dir.size - dir.threshold) - return dir.minSize - iconsize; - else if (iconsize > dir.size + dir.threshold) - return iconsize - dir.maxSize; + if (scaledIconSize < (dir.size - dir.threshold) * dir.scale) + return dir.minSize * dir.scale - scaledIconSize; + else if (scaledIconSize > (dir.size + dir.threshold) * dir.scale) + return scaledIconSize - dir.maxSize * dir.scale; else return 0; } @@ -600,7 +609,7 @@ static int directorySizeDistance(const QIconDirInfo &dir, int iconsize) return INT_MAX; } -QIconLoaderEngineEntry *QIconLoaderEngine::entryForSize(const QSize &size) +QIconLoaderEngineEntry *QIconLoaderEngine::entryForSize(const QSize &size, int scale) { int iconsize = qMin(size.width(), size.height()); @@ -612,7 +621,7 @@ QIconLoaderEngineEntry *QIconLoaderEngine::entryForSize(const QSize &size) // Search for exact matches first for (int i = 0; i < numEntries; ++i) { QIconLoaderEngineEntry *entry = m_info.entries.at(i); - if (directoryMatchesSize(entry->dir, iconsize)) { + if (directoryMatchesSize(entry->dir, iconsize, scale)) { return entry; } } @@ -622,7 +631,7 @@ QIconLoaderEngineEntry *QIconLoaderEngine::entryForSize(const QSize &size) QIconLoaderEngineEntry *closestMatch = 0; for (int i = 0; i < numEntries; ++i) { QIconLoaderEngineEntry *entry = m_info.entries.at(i); - int distance = directorySizeDistance(entry->dir, iconsize); + int distance = directorySizeDistance(entry->dir, iconsize, scale); if (distance < minimalSize) { minimalSize = distance; closestMatch = entry; @@ -665,6 +674,8 @@ QPixmap PixmapEntry::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State st basePixmap.load(filename); QSize actualSize = basePixmap.size(); + // If the size of the best match we have (basePixmap) is larger than the + // requested size, we downscale it to match. if (!actualSize.isNull() && (actualSize.width() > size.width() || actualSize.height() > size.height())) actualSize.scale(size, Qt::KeepAspectRatio); @@ -748,6 +759,15 @@ void QIconLoaderEngine::virtual_hook(int id, void *data) *reinterpret_cast<bool*>(data) = m_info.entries.isEmpty(); } break; + case QIconEngine::ScaledPixmapHook: + { + QIconEngine::ScaledPixmapArgument &arg = *reinterpret_cast<QIconEngine::ScaledPixmapArgument*>(data); + // QIcon::pixmap() multiplies size by the device pixel ratio. + const int integerScale = qCeil(arg.scale); + QIconLoaderEngineEntry *entry = entryForSize(arg.size / integerScale, integerScale); + arg.pixmap = entry ? entry->pixmap(arg.size, arg.mode, arg.state) : QPixmap(); + } + break; default: QIconEngine::virtual_hook(id, data); } diff --git a/src/gui/image/qiconloader_p.h b/src/gui/image/qiconloader_p.h index ed7b7ff7ae..5f3a3ef948 100644 --- a/src/gui/image/qiconloader_p.h +++ b/src/gui/image/qiconloader_p.h @@ -76,12 +76,14 @@ struct QIconDirInfo maxSize(0), minSize(0), threshold(0), + scale(1), type(Threshold) {} QString path; short size; short maxSize; short minSize; short threshold; + short scale; Type type; }; Q_DECLARE_TYPEINFO(QIconDirInfo, Q_MOVABLE_TYPE); @@ -135,7 +137,8 @@ private: bool hasIcon() const; void ensureLoaded(); void virtual_hook(int id, void *data) Q_DECL_OVERRIDE; - QIconLoaderEngineEntry *entryForSize(const QSize &size); + QIconLoaderEngineEntry *entryForSize(const QSize &size, int scale = 1); + QIconLoaderEngine(const QIconLoaderEngine &other); QThemeIconInfo m_info; QString m_iconName; diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp index 9e9f01a46a..c1ea053204 100644 --- a/src/gui/image/qimage.cpp +++ b/src/gui/image/qimage.cpp @@ -89,12 +89,16 @@ static QImage rotated90(const QImage &src); static QImage rotated180(const QImage &src); static QImage rotated270(const QImage &src); -QBasicAtomicInt qimage_serial_number = Q_BASIC_ATOMIC_INITIALIZER(1); +static int next_qimage_serial_number() +{ + static QBasicAtomicInt serial = Q_BASIC_ATOMIC_INITIALIZER(0); + return 1 + serial.fetchAndAddRelaxed(1); +} QImageData::QImageData() : ref(0), width(0), height(0), depth(0), nbytes(0), devicePixelRatio(1.0), data(0), format(QImage::Format_ARGB32), bytes_per_line(0), - ser_no(qimage_serial_number.fetchAndAddRelaxed(1)), + ser_no(next_qimage_serial_number()), detach_no(0), dpmx(qt_defaultDpiX() * 100 / qreal(2.54)), dpmy(qt_defaultDpiY() * 100 / qreal(2.54)), @@ -2121,6 +2125,44 @@ QImage QImage::convertToFormat(Format format, const QVector<QRgb> &colorTable, Q } /*! + \since 5.9 + + Changes the format of the image without changing the data. Only + works between formats of the same depth. + + Returns \c true if successful. + + This function can be used to change images with alpha-channels to + their corresponding opaque formats if the data is known to be opaque-only, + or to change the format of a given image buffer before overwriting + it with new data. + + \warning The function does not check if the image data is valid in the + new format and will still return \c true if the depths are compatible. + Operations on an image with invalid data are undefined. + + \warning If the image is not detached, this will cause the data to be + copied. + + \sa isDetached(), hasAlphaChannel(), convertToFormat() +*/ + +bool QImage::reinterpretAsFormat(Format format) +{ + if (!d) + return false; + if (d->format == format) + return true; + if (qt_depthForFormat(format) != qt_depthForFormat(d->format)) + return false; + if (!isDetached()) // Detach only if shared, not for read-only data. + detach(); + + d->format = format; + return true; +} + +/*! \fn bool QImage::valid(const QPoint &pos) const Returns \c true if \a pos is a valid coordinate pair within the @@ -4458,7 +4500,8 @@ QImage QImage::smoothScaled(int w, int h) const { return src; } -static QImage rotated90(const QImage &image) { +static QImage rotated90(const QImage &image) +{ QImage out(image.height(), image.width(), image.format()); out.setDotsPerMeterX(image.dotsPerMeterY()); out.setDotsPerMeterY(image.dotsPerMeterX()); @@ -4466,49 +4509,10 @@ static QImage rotated90(const QImage &image) { 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: - case QImage::Format_RGBX8888: - case QImage::Format_RGBA8888: - case QImage::Format_RGBA8888_Premultiplied: - case QImage::Format_BGR30: - case QImage::Format_A2BGR30_Premultiplied: - case QImage::Format_RGB30: - case QImage::Format_A2RGB30_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_Alpha8: - case QImage::Format_Grayscale8: - 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: + const MemRotateFunc memrotate = qMemRotateFunctions[qPixelLayouts[image.format()].bpp][2]; + if (memrotate) { + memrotate(image.constBits(), w, h, image.bytesPerLine(), out.bits(), out.bytesPerLine()); + } else { for (int y=0; y<h; ++y) { if (image.colorCount()) for (int x=0; x<w; ++x) @@ -4517,18 +4521,29 @@ static QImage rotated90(const QImage &image) { 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) +{ + const MemRotateFunc memrotate = qMemRotateFunctions[qPixelLayouts[image.format()].bpp][1]; + if (!memrotate) + return image.mirrored(true, true); -static QImage rotated180(const QImage &image) { - return image.mirrored(true, true); + QImage out(image.width(), image.height(), image.format()); + out.setDotsPerMeterX(image.dotsPerMeterY()); + out.setDotsPerMeterY(image.dotsPerMeterX()); + if (image.colorCount() > 0) + out.setColorTable(image.colorTable()); + int w = image.width(); + int h = image.height(); + memrotate(image.constBits(), w, h, image.bytesPerLine(), out.bits(), out.bytesPerLine()); + return out; } - -static QImage rotated270(const QImage &image) { +static QImage rotated270(const QImage &image) +{ QImage out(image.height(), image.width(), image.format()); out.setDotsPerMeterX(image.dotsPerMeterY()); out.setDotsPerMeterY(image.dotsPerMeterX()); @@ -4536,49 +4551,10 @@ static QImage rotated270(const QImage &image) { 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: - case QImage::Format_RGBX8888: - case QImage::Format_RGBA8888: - case QImage::Format_RGBA8888_Premultiplied: - case QImage::Format_BGR30: - case QImage::Format_A2BGR30_Premultiplied: - case QImage::Format_RGB30: - case QImage::Format_A2RGB30_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_Alpha8: - case QImage::Format_Grayscale8: - 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: + const MemRotateFunc memrotate = qMemRotateFunctions[qPixelLayouts[image.format()].bpp][0]; + if (memrotate) { + memrotate(image.constBits(), w, h, image.bytesPerLine(), out.bits(), out.bytesPerLine()); + } else { for (int y=0; y<h; ++y) { if (image.colorCount()) for (int x=0; x<w; ++x) @@ -4587,7 +4563,6 @@ static QImage rotated270(const QImage &image) { for (int x=0; x<w; ++x) out.setPixel(y, w-x-1, image.pixel(x, y)); } - break; } return out; } diff --git a/src/gui/image/qimage.h b/src/gui/image/qimage.h index fd2298561e..204e7a54b2 100644 --- a/src/gui/image/qimage.h +++ b/src/gui/image/qimage.h @@ -192,6 +192,7 @@ public: QImage convertToFormat(Format f, Qt::ImageConversionFlags flags = Qt::AutoColor) const Q_REQUIRED_RESULT; #endif QImage convertToFormat(Format f, const QVector<QRgb> &colorTable, Qt::ImageConversionFlags flags = Qt::AutoColor) const Q_REQUIRED_RESULT; + bool reinterpretAsFormat(Format f); int width() const; int height() const; diff --git a/src/gui/image/qimage_conversions.cpp b/src/gui/image/qimage_conversions.cpp index 9c9db9a245..50fad1566c 100644 --- a/src/gui/image/qimage_conversions.cpp +++ b/src/gui/image/qimage_conversions.cpp @@ -39,12 +39,29 @@ #include <private/qdrawhelper_p.h> #include <private/qguiapplication_p.h> +#include <private/qcolorprofile_p.h> #include <private/qsimd_p.h> #include <private/qimage_p.h> #include <qendian.h> QT_BEGIN_NAMESPACE +struct QDefaultColorTables +{ + QDefaultColorTables() + : gray(256), alpha(256) + { + for (int i = 0; i < 256; ++i) { + gray[i] = qRgb(i, i, i); + alpha[i] = qRgba(0, 0, 0, i); + } + } + + QVector<QRgb> gray, alpha; +}; + +Q_GLOBAL_STATIC(QDefaultColorTables, defaultColorTables); + // table to flip bits static const uchar bitflip[256] = { /* @@ -82,23 +99,17 @@ const uchar *qt_get_bitflip_array() void qGamma_correct_back_to_linear_cs(QImage *image) { - const QDrawHelperGammaTables *tables = QGuiApplicationPrivate::instance()->gammaTables(); - if (!tables) + const QColorProfile *cp = QGuiApplicationPrivate::instance()->colorProfileForA32Text(); + if (!cp) return; - const uchar *gamma = tables->qt_pow_rgb_gamma; // gamma correct the pixels back to linear color space... int h = image->height(); int w = image->width(); for (int y=0; y<h; ++y) { - uint *pixels = (uint *) image->scanLine(y); - for (int x=0; x<w; ++x) { - uint p = pixels[x]; - uint r = gamma[qRed(p)]; - uint g = gamma[qGreen(p)]; - uint b = gamma[qBlue(p)]; - pixels[x] = (r << 16) | (g << 8) | b | 0xff000000; - } + QRgb *pixels = reinterpret_cast<QRgb *>(image->scanLine(y)); + for (int x=0; x<w; ++x) + pixels[x] = cp->toLinear(pixels[x]); } } @@ -1957,11 +1968,7 @@ static void convert_Alpha8_to_Indexed8(QImageData *dest, const QImageData *src, memcpy(dest->data, src->data, src->bytes_per_line * src->height); - QVector<QRgb> colors(256); - for (int i=0; i<256; ++i) - colors[i] = qRgba(0, 0, 0, i); - - dest->colortable = colors; + dest->colortable = defaultColorTables->alpha; } static void convert_Grayscale8_to_Indexed8(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) @@ -1971,22 +1978,15 @@ static void convert_Grayscale8_to_Indexed8(QImageData *dest, const QImageData *s memcpy(dest->data, src->data, src->bytes_per_line * src->height); - QVector<QRgb> colors(256); - for (int i=0; i<256; ++i) - colors[i] = qRgb(i, i, i); - dest->colortable = colors; + dest->colortable = defaultColorTables->gray; } static bool convert_Alpha8_to_Indexed8_inplace(QImageData *data, Qt::ImageConversionFlags) { Q_ASSERT(data->format == QImage::Format_Alpha8); - QVector<QRgb> colors(256); - for (int i=0; i<256; ++i) - colors[i] = qRgba(0, 0, 0, i); - - data->colortable = colors; + data->colortable = defaultColorTables->alpha; data->format = QImage::Format_Indexed8; return true; @@ -1996,11 +1996,7 @@ static bool convert_Grayscale8_to_Indexed8_inplace(QImageData *data, Qt::ImageCo { Q_ASSERT(data->format == QImage::Format_Grayscale8); - QVector<QRgb> colors(256); - for (int i=0; i<256; ++i) - colors[i] = qRgb(i, i, i); - - data->colortable = colors; + data->colortable = defaultColorTables->gray; data->format = QImage::Format_Indexed8; return true; diff --git a/src/gui/image/qimage_darwin.mm b/src/gui/image/qimage_darwin.mm index 2d38468cc5..3764bef06b 100644 --- a/src/gui/image/qimage_darwin.mm +++ b/src/gui/image/qimage_darwin.mm @@ -1,31 +1,37 @@ /**************************************************************************** ** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtGui module of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL21$ +** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, 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 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** diff --git a/src/gui/image/qimagereader.cpp b/src/gui/image/qimagereader.cpp index 4390e46fde..d01f0d640b 100644 --- a/src/gui/image/qimagereader.cpp +++ b/src/gui/image/qimagereader.cpp @@ -190,32 +190,42 @@ enum _qt_BuiltInFormatType { _qt_NoFormat = -1 }; +#if !defined(QT_NO_IMAGEFORMAT_PPM) +# define MAX_MT_SIZE 20 +#elif !defined(QT_NO_IMAGEFORMAT_XBM) || !defined(QT_NO_IMAGEFORMAT_XPM) +# define MAX_MT_SIZE 10 +#else +# define MAX_MT_SIZE 4 +#endif + struct _qt_BuiltInFormatStruct { - const char *extension; - const char *mimeType; + char extension[4]; + char mimeType[MAX_MT_SIZE]; }; +#undef MAX_MT_SIZE + static const _qt_BuiltInFormatStruct _qt_BuiltInFormats[] = { #ifndef QT_NO_IMAGEFORMAT_PNG - {"png", "image/png"}, + {"png", "png"}, #endif #ifndef QT_NO_IMAGEFORMAT_BMP - {"bmp", "image/bmp"}, + {"bmp", "bmp"}, #endif #ifndef QT_NO_IMAGEFORMAT_PPM - {"ppm", "image/x-portable-pixmap"}, - {"pgm", "image/x-portable-graymap"}, - {"pbm", "image/x-portable-bitmap"}, + {"ppm", "x-portable-pixmap"}, + {"pgm", "x-portable-graymap"}, + {"pbm", "x-portable-bitmap"}, #endif #ifndef QT_NO_IMAGEFORMAT_XBM - {"xbm", "image/x-xbitmap"}, + {"xbm", "x-xbitmap"}, #endif #ifndef QT_NO_IMAGEFORMAT_XPM - {"xpm", "image/x-xpixmap"}, + {"xpm", "x-xpixmap"}, #endif - {"", ""} }; +Q_STATIC_ASSERT(_qt_NumFormats == sizeof _qt_BuiltInFormats / sizeof *_qt_BuiltInFormats); static QImageIOHandler *createReadHandlerHelper(QIODevice *device, const QByteArray &format, @@ -461,7 +471,8 @@ static QImageIOHandler *createReadHandlerHelper(QIODevice *device, --numFormats; ++currentFormat; - currentFormat %= _qt_NumFormats; + if (currentFormat >= _qt_NumFormats) + currentFormat = 0; } } @@ -1604,8 +1615,8 @@ QList<QByteArray> QImageReader::supportedMimeTypes() { QList<QByteArray> mimeTypes; mimeTypes.reserve(_qt_NumFormats); - for (int i = 0; i < _qt_NumFormats; ++i) - mimeTypes << _qt_BuiltInFormats[i].mimeType; + for (const auto &fmt : _qt_BuiltInFormats) + mimeTypes.append(QByteArrayLiteral("image/") + fmt.mimeType); #ifndef QT_NO_IMAGEFORMATPLUGIN supportedImageHandlerMimeTypes(loader(), QImageIOPlugin::CanRead, &mimeTypes); diff --git a/src/gui/image/qimagewriter.cpp b/src/gui/image/qimagewriter.cpp index f3af2738af..ab15d8ee29 100644 --- a/src/gui/image/qimagewriter.cpp +++ b/src/gui/image/qimagewriter.cpp @@ -283,8 +283,13 @@ bool QImageWriterPrivate::canWriteHelper() errorString = QImageWriter::tr("Device is not set"); return false; } - if (!device->isOpen()) - device->open(QIODevice::WriteOnly); + if (!device->isOpen()) { + if (!device->open(QIODevice::WriteOnly)) { + imageWriterError = QImageWriter::DeviceError; + errorString = QImageWriter::tr("Cannot open device for writing: %1").arg(device->errorString()); + return false; + } + } if (!device->isWritable()) { imageWriterError = QImageWriter::DeviceError; errorString = QImageWriter::tr("Device not writable"); @@ -705,6 +710,11 @@ bool QImageWriter::canWrite() const if (QFile *file = qobject_cast<QFile *>(d->device)) { const bool remove = !file->isOpen() && !file->exists(); const bool result = d->canWriteHelper(); + + // This looks strange (why remove if it doesn't exist?) but the issue + // here is that canWriteHelper will create the file in the process of + // checking if the write can succeed. If it subsequently fails, we + // should remove that empty file. if (!result && remove) file->remove(); return result; diff --git a/src/gui/image/qpixmap_raster.cpp b/src/gui/image/qpixmap_raster.cpp index 741f7713da..7eb61adb2d 100644 --- a/src/gui/image/qpixmap_raster.cpp +++ b/src/gui/image/qpixmap_raster.cpp @@ -51,7 +51,6 @@ #include <QImageReader> #include <QGuiApplication> #include <QScreen> -#include <private/qimage_p.h> #include <private/qsimd_p.h> #include <private/qdrawhelper_p.h> #include <qpa/qplatformscreen.h> @@ -135,7 +134,7 @@ bool QRasterPlatformPixmap::fromData(const uchar *buffer, uint len, const char * if (image.isNull()) return false; - createPixmapForImage(image, flags, /* inplace = */true); + createPixmapForImage(std::move(image), flags); return !isNull(); } @@ -143,13 +142,13 @@ void QRasterPlatformPixmap::fromImage(const QImage &sourceImage, Qt::ImageConversionFlags flags) { QImage image = sourceImage; - createPixmapForImage(image, flags, /* inplace = */false); + createPixmapForImage(std::move(image), flags); } void QRasterPlatformPixmap::fromImageInPlace(QImage &sourceImage, Qt::ImageConversionFlags flags) { - createPixmapForImage(sourceImage, flags, /* inplace = */true); + createPixmapForImage(std::move(sourceImage), flags); } void QRasterPlatformPixmap::fromImageReader(QImageReader *imageReader, @@ -160,7 +159,7 @@ void QRasterPlatformPixmap::fromImageReader(QImageReader *imageReader, if (image.isNull()) return; - createPixmapForImage(image, flags, /* inplace = */true); + createPixmapForImage(std::move(image), flags); } // from qbackingstore.cpp @@ -301,7 +300,7 @@ int QRasterPlatformPixmap::metric(QPaintDevice::PaintDeviceMetric metric) const return 0; } -void QRasterPlatformPixmap::createPixmapForImage(QImage &sourceImage, Qt::ImageConversionFlags flags, bool inPlace) +void QRasterPlatformPixmap::createPixmapForImage(QImage sourceImage, Qt::ImageConversionFlags flags) { QImage::Format format; if (flags & Qt::NoFormatConversion) @@ -335,16 +334,10 @@ void QRasterPlatformPixmap::createPixmapForImage(QImage &sourceImage, Qt::ImageC if (format == QImage::Format_RGB32 && (sourceImage.format() == QImage::Format_ARGB32 || sourceImage.format() == QImage::Format_ARGB32_Premultiplied)) { - inPlace = inPlace && sourceImage.isDetached(); - image = sourceImage; - if (!inPlace) - image.detach(); - if (image.d) - image.d->format = QImage::Format_RGB32; - } else if (inPlace && sourceImage.d->convertInPlace(format, flags)) { - image = sourceImage; + image = std::move(sourceImage); + image.reinterpretAsFormat(QImage::Format_RGB32); } else { - image = sourceImage.convertToFormat(format, flags); + image = std::move(sourceImage).convertToFormat(format, flags); } if (image.d) { @@ -356,8 +349,6 @@ void QRasterPlatformPixmap::createPixmapForImage(QImage &sourceImage, Qt::ImageC } is_null = (w <= 0 || h <= 0); - if (image.d) - image.d->devicePixelRatio = sourceImage.devicePixelRatio(); //ensure the pixmap and the image resulting from toImage() have the same cacheKey(); setSerialNumber(image.cacheKey() >> 32); if (image.d) diff --git a/src/gui/image/qpixmap_raster_p.h b/src/gui/image/qpixmap_raster_p.h index 95e018eb35..6ea965a324 100644 --- a/src/gui/image/qpixmap_raster_p.h +++ b/src/gui/image/qpixmap_raster_p.h @@ -85,7 +85,7 @@ public: protected: int metric(QPaintDevice::PaintDeviceMetric metric) const Q_DECL_OVERRIDE; - void createPixmapForImage(QImage &sourceImage, Qt::ImageConversionFlags flags, bool inPlace); + void createPixmapForImage(QImage sourceImage, Qt::ImageConversionFlags flags); void setImage(const QImage &image); QImage image; static QImage::Format systemOpaqueFormat(); diff --git a/src/gui/image/qpnghandler.cpp b/src/gui/image/qpnghandler.cpp index d55a26b2a4..1d19c165fc 100644 --- a/src/gui/image/qpnghandler.cpp +++ b/src/gui/image/qpnghandler.cpp @@ -745,7 +745,7 @@ static void set_text(const QImage &image, png_structp png_ptr, png_infop info_pt #ifdef PNG_iTXt_SUPPORTED bool needsItxt = false; - foreach(const QChar c, it.value()) { + for (const QChar c : it.value()) { uchar ch = c.cell(); if (c.row() || (ch < 0x20 && ch != '\n') || (ch > 0x7e && ch < 0xa0)) { needsItxt = true; diff --git a/src/gui/image/qppmhandler.cpp b/src/gui/image/qppmhandler.cpp index 42d3684aea..e9f5a905f0 100644 --- a/src/gui/image/qppmhandler.cpp +++ b/src/gui/image/qppmhandler.cpp @@ -45,6 +45,7 @@ #include <qvariant.h> #include <qvector.h> #include <ctype.h> +#include <qrgba64.h> QT_BEGIN_NAMESPACE @@ -120,6 +121,11 @@ static bool read_pbm_header(QIODevice *device, char& type, int& w, int& h, int& return true; } +static inline QRgb scale_pbm_color(quint16 mx, quint16 rv, quint16 gv, quint16 bv) +{ + return QRgba64::fromRgba64((rv * 0xffff) / mx, (gv * 0xffff) / mx, (bv * 0xffff) / mx, 0xffff).toArgb32(); +} + static bool read_pbm_body(QIODevice *device, char type, int w, int h, int mcc, QImage *outImage) { int nbits, y; @@ -148,9 +154,6 @@ static bool read_pbm_body(QIODevice *device, char type, int w, int h, int mcc, Q } 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()) @@ -175,22 +178,50 @@ static bool read_pbm_body(QIODevice *device, char type, int w, int h, int mcc, Q b = buf24; while (p < end) { if (mcc < 256) { - *p++ = qRgb(b[0],b[1],b[2]); + if (mcc == 255) + *p++ = qRgb(b[0],b[1],b[2]); + else + *p++ = scale_pbm_color(mcc, 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); + quint16 rv = b[0] << 8 | b[1]; + quint16 gv = b[2] << 8 | b[3]; + quint16 bv = b[4] << 8 | b[5]; + if (mcc == 0xffff) + *p++ = QRgba64::fromRgba64(rv, gv, bv, 0xffff).toArgb32(); + else + *p++ = scale_pbm_color(mcc, rv, gv, bv); b += 6; } } } delete[] buf24; + } else if (nbits == 8 && mcc > 255) { // type 5 16bit + pbm_bpl = 2*w; + uchar *buf16 = new uchar[pbm_bpl]; + for (y=0; y<h; y++) { + if (device->read((char *)buf16, pbm_bpl) != pbm_bpl) { + delete[] buf16; + return false; + } + uchar *p = outImage->scanLine(y); + uchar *end = p + w; + uchar *b = buf16; + while (p < end) { + *p++ = (b[0] << 8 | b[1]) * 255 / mcc; + b += 2; + } + } + delete[] buf16; } else { // type 4,5 for (y=0; y<h; y++) { - if (device->read((char *)outImage->scanLine(y), pbm_bpl) - != pbm_bpl) + uchar *p = outImage->scanLine(y); + if (device->read((char *)p, pbm_bpl) != pbm_bpl) return false; + if (nbits == 8 && mcc < 255) { + for (int i = 0; i < pbm_bpl; i++) + p[i] = (p[i] * 255) / mcc; + } } } } else { // read ascii data @@ -227,7 +258,7 @@ static bool read_pbm_body(QIODevice *device, char type, int w, int h, int mcc, Q } else { // 32 bits n /= 4; int r, g, b; - if (mcc == maxc) { + if (mcc == 255) { while (n--) { r = read_pbm_int(device); g = read_pbm_int(device); @@ -237,10 +268,10 @@ static bool read_pbm_body(QIODevice *device, char type, int w, int h, int mcc, Q } } 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); + r = read_pbm_int(device); + g = read_pbm_int(device); + b = read_pbm_int(device); + *((QRgb*)p) = scale_pbm_color(mcc, r, g, b); p += 4; } } diff --git a/src/gui/image/qxbmhandler.cpp b/src/gui/image/qxbmhandler.cpp index 19015c5dcd..155a4f88b4 100644 --- a/src/gui/image/qxbmhandler.cpp +++ b/src/gui/image/qxbmhandler.cpp @@ -143,6 +143,8 @@ static bool read_xbm_body(QIODevice *device, int w, int h, QImage *outImage) return false; } + outImage->fill(Qt::color0); // in case the image data does not cover the full image + outImage->setColorCount(2); outImage->setColor(0, qRgb(255,255,255)); // white outImage->setColor(1, qRgb(0,0,0)); // black diff --git a/src/gui/image/qxpmhandler.cpp b/src/gui/image/qxpmhandler.cpp index 1f1f6b388f..ce7f7b8a0f 100644 --- a/src/gui/image/qxpmhandler.cpp +++ b/src/gui/image/qxpmhandler.cpp @@ -852,6 +852,9 @@ static bool read_xpm_header( #endif return false; // < 4 numbers parsed + if (*w <= 0 || *w > 32767 || *h <= 0 || *h > 32767 || *ncols <= 0 || *ncols > (64 * 64 * 64 * 64) || *cpp <= 0 || *cpp > 15) + return false; // failed sanity check + return true; } |