diff options
Diffstat (limited to 'src/gui/image')
39 files changed, 886 insertions, 465 deletions
diff --git a/src/gui/image/qbitmap.cpp b/src/gui/image/qbitmap.cpp index 2453242fa8..455045eb57 100644 --- a/src/gui/image/qbitmap.cpp +++ b/src/gui/image/qbitmap.cpp @@ -310,6 +310,7 @@ QBitmap QBitmap::transformed(const QTransform &matrix) const return bm; } +#if QT_DEPRECATED_SINCE(5, 13) /*! \overload \obsolete @@ -321,5 +322,6 @@ QBitmap QBitmap::transformed(const QMatrix &matrix) const { return transformed(QTransform(matrix)); } +#endif QT_END_NAMESPACE diff --git a/src/gui/image/qbitmap.h b/src/gui/image/qbitmap.h index 188064fccf..3cc360f670 100644 --- a/src/gui/image/qbitmap.h +++ b/src/gui/image/qbitmap.h @@ -61,7 +61,7 @@ public: QBitmap(const QBitmap &other) : QPixmap(other) {} // QBitmap(QBitmap &&other) : QPixmap(std::move(other)) {} // QPixmap doesn't, yet, have a move ctor QBitmap &operator=(const QBitmap &other) { QPixmap::operator=(other); return *this; } - QBitmap &operator=(QBitmap &&other) Q_DECL_NOTHROW { QPixmap::operator=(std::move(other)); return *this; } + QBitmap &operator=(QBitmap &&other) noexcept { QPixmap::operator=(std::move(other)); return *this; } ~QBitmap(); #endif @@ -76,7 +76,10 @@ public: static QBitmap fromData(const QSize &size, const uchar *bits, QImage::Format monoFormat = QImage::Format_MonoLSB); +#if QT_DEPRECATED_SINCE(5, 13) + QT_DEPRECATED_X("Use QBitmap::transformed(QTransform) instead") QBitmap transformed(const QMatrix &) const; +#endif QBitmap transformed(const QTransform &matrix) const; typedef QExplicitlySharedDataPointer<QPlatformPixmap> DataPtr; diff --git a/src/gui/image/qbmphandler.cpp b/src/gui/image/qbmphandler.cpp index 5dff4ab0ac..7f8e072322 100644 --- a/src/gui/image/qbmphandler.cpp +++ b/src/gui/image/qbmphandler.cpp @@ -188,7 +188,7 @@ static bool read_dib_infoheader(QDataStream &s, BMP_INFOHDR &bi) 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 - if (bi.biWidth < 0 || quint64(bi.biWidth) * qAbs(bi.biHeight) > 16384 * 16384) + if (bi.biWidth <= 0 || !bi.biHeight || quint64(bi.biWidth) * qAbs(bi.biHeight) > 16384 * 16384) return false; return true; @@ -866,10 +866,12 @@ void QBmpHandler::setOption(ImageOption option, const QVariant &value) Q_UNUSED(value); } +#if QT_DEPRECATED_SINCE(5, 13) QByteArray QBmpHandler::name() const { return formatName(); } +#endif QT_END_NAMESPACE diff --git a/src/gui/image/qbmphandler_p.h b/src/gui/image/qbmphandler_p.h index 56b39dd0f0..33b5b9c501 100644 --- a/src/gui/image/qbmphandler_p.h +++ b/src/gui/image/qbmphandler_p.h @@ -113,8 +113,9 @@ public: bool read(QImage *image) override; bool write(const QImage &image) override; +#if QT_DEPRECATED_SINCE(5, 13) QByteArray name() const override; - +#endif static bool canRead(QIODevice *device); QVariant option(ImageOption option) const override; diff --git a/src/gui/image/qicon.cpp b/src/gui/image/qicon.cpp index 5907d69f87..892a686c89 100644 --- a/src/gui/image/qicon.cpp +++ b/src/gui/image/qicon.cpp @@ -47,8 +47,10 @@ #include "private/qiconloader_p.h" #include "qpainter.h" #include "qfileinfo.h" +#if QT_CONFIG(mimetype) #include <qmimedatabase.h> #include <qmimetype.h> +#endif #include "qpixmapcache.h" #include "qvariant.h" #include "qcache.h" @@ -313,9 +315,9 @@ QPixmap QPixmapIconEngine::pixmap(const QSize &size, QIcon::Mode mode, QIcon::St % HexString<uint>(actualSize.height()); if (mode == QIcon::Active) { - if (QPixmapCache::find(key % HexString<uint>(mode), pm)) + if (QPixmapCache::find(key % HexString<uint>(mode), &pm)) return pm; // horray - if (QPixmapCache::find(key % HexString<uint>(QIcon::Normal), pm)) { + if (QPixmapCache::find(key % HexString<uint>(QIcon::Normal), &pm)) { QPixmap active = pm; if (QGuiApplication *guiApp = qobject_cast<QGuiApplication *>(qApp)) active = static_cast<QGuiApplicationPrivate*>(QObjectPrivate::get(guiApp))->applyQIconStyleHelper(QIcon::Active, pm); @@ -324,7 +326,7 @@ QPixmap QPixmapIconEngine::pixmap(const QSize &size, QIcon::Mode mode, QIcon::St } } - if (!QPixmapCache::find(key % HexString<uint>(mode), pm)) { + if (!QPixmapCache::find(key % HexString<uint>(mode), &pm)) { if (pm.size() != actualSize) pm = pm.scaled(actualSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); if (pe->mode != mode && mode != QIcon::Normal) { @@ -661,7 +663,7 @@ QFactoryLoader *qt_iconEngineFactoryLoader() /*! Constructs a null icon. */ -QIcon::QIcon() Q_DECL_NOEXCEPT +QIcon::QIcon() noexcept : d(0) { } @@ -1079,10 +1081,10 @@ void QIcon::addFile(const QString &fileName, const QSize &size, Mode mode, State QFileInfo info(fileName); QString suffix = info.suffix(); -#ifndef QT_NO_MIMETYPE +#if QT_CONFIG(mimetype) if (suffix.isEmpty()) suffix = QMimeDatabase().mimeTypeForFile(info).preferredSuffix(); // determination from contents -#endif // !QT_NO_MIMETYPE +#endif // mimetype QIconEngine *engine = iconEngineFromSuffix(fileName, suffix); d = new QIconPrivate(engine ? engine : new QPixmapIconEngine); } diff --git a/src/gui/image/qicon.h b/src/gui/image/qicon.h index 6a4fc8927a..0f834fc2cb 100644 --- a/src/gui/image/qicon.h +++ b/src/gui/image/qicon.h @@ -50,6 +50,7 @@ QT_BEGIN_NAMESPACE class QIconPrivate; class QIconEngine; +class QPainter; class Q_GUI_EXPORT QIcon { @@ -57,11 +58,11 @@ public: enum Mode { Normal, Disabled, Active, Selected }; enum State { On, Off }; - QIcon() Q_DECL_NOEXCEPT; + QIcon() noexcept; QIcon(const QPixmap &pixmap); QIcon(const QIcon &other); #ifdef Q_COMPILER_RVALUE_REFS - QIcon(QIcon &&other) Q_DECL_NOEXCEPT + QIcon(QIcon &&other) noexcept : d(other.d) { other.d = nullptr; } #endif @@ -70,10 +71,10 @@ public: ~QIcon(); QIcon &operator=(const QIcon &other); #ifdef Q_COMPILER_RVALUE_REFS - inline QIcon &operator=(QIcon &&other) Q_DECL_NOEXCEPT + inline QIcon &operator=(QIcon &&other) noexcept { swap(other); return *this; } #endif - inline void swap(QIcon &other) Q_DECL_NOEXCEPT + inline void swap(QIcon &other) noexcept { qSwap(d, other.d); } operator QVariant() const; diff --git a/src/gui/image/qiconengine.h b/src/gui/image/qiconengine.h index 0c67ef2686..0c5b51dc71 100644 --- a/src/gui/image/qiconengine.h +++ b/src/gui/image/qiconengine.h @@ -90,6 +90,7 @@ public: QPixmap pixmap; }; + // ### Qt6: move content to proper virtual functions virtual void virtual_hook(int id, void *data); private: diff --git a/src/gui/image/qiconloader.cpp b/src/gui/image/qiconloader.cpp index 228de3adc3..1d0c93f26f 100644 --- a/src/gui/image/qiconloader.cpp +++ b/src/gui/image/qiconloader.cpp @@ -50,7 +50,9 @@ #include <QtCore/qmath.h> #include <QtCore/QList> #include <QtCore/QDir> +#if QT_CONFIG(settings) #include <QtCore/QSettings> +#endif #include <QtGui/QPainter> #include <private/qhexstring_p.h> @@ -348,7 +350,7 @@ QIconTheme::QIconTheme(const QString &themeName) m_valid = true; } } -#ifndef QT_NO_SETTINGS +#if QT_CONFIG(settings) if (themeIndex.exists()) { const QSettings indexReader(themeIndex.fileName(), QSettings::IniFormat); const QStringList keys = indexReader.allKeys(); @@ -407,7 +409,7 @@ QIconTheme::QIconTheme(const QString &themeName) if (!m_parents.contains(QLatin1String("hicolor"))) m_parents.append(QLatin1String("hicolor")); } -#endif //QT_NO_SETTINGS +#endif // settings } QThemeIconInfo QIconLoader::findIconHelper(const QString &themeName, @@ -627,7 +629,10 @@ void QIconLoaderEngine::ensureLoaded() void QIconLoaderEngine::paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state) { - QSize pixmapSize = rect.size(); + const qreal dpr = !qApp->testAttribute(Qt::AA_UseHighDpiPixmaps) ? + qreal(1.0) : painter->device()->devicePixelRatioF(); + + QSize pixmapSize = rect.size() * dpr; painter->drawPixmap(rect, pixmap(pixmapSize, mode, state)); } diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp index 636cacfb9c..917dde3b0f 100644 --- a/src/gui/image/qimage.cpp +++ b/src/gui/image/qimage.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtGui module of the Qt Toolkit. @@ -38,8 +38,10 @@ ****************************************************************************/ #include "qimage.h" -#include "qdatastream.h" + #include "qbuffer.h" +#include "qdatastream.h" +#include "qcolortransform.h" #include "qmap.h" #include "qmatrix.h" #include "qtransform.h" @@ -54,6 +56,7 @@ #include <stdlib.h> #include <limits.h> #include <qpa/qplatformpixmap.h> +#include <private/qcolortransform_p.h> #include <private/qdrawhelper_p.h> #include <private/qmemrotate_p.h> #include <private/qimagescale_p.h> @@ -113,26 +116,19 @@ QImageData::QImageData() \internal Creates a new image data. - Returns 0 if invalid parameters are give or anything else failed. + Returns \nullptr if invalid parameters are give or anything else failed. */ QImageData * QImageData::create(const QSize &size, QImage::Format format) { - if (!size.isValid() || format == QImage::Format_Invalid) - return 0; // invalid parameter(s) + if (size.isEmpty() || format == QImage::Format_Invalid) + return nullptr; // invalid parameter(s) - uint width = size.width(); - uint height = size.height(); - uint depth = qt_depthForFormat(format); - - const int bytes_per_line = ((width * depth + 31) >> 5) << 2; // bytes per scanline (must be multiple of 4) - - // sanity check for potential overflows - if (std::numeric_limits<int>::max()/depth < width - || bytes_per_line <= 0 - || height <= 0 - || std::numeric_limits<qsizetype>::max()/uint(bytes_per_line) < height - || std::numeric_limits<int>::max()/sizeof(uchar *) < uint(height)) - return 0; + int width = size.width(); + int height = size.height(); + int depth = qt_depthForFormat(format); + auto params = calculateImageParameters(width, height, depth); + if (!params.isValid()) + return nullptr; QScopedPointer<QImageData> d(new QImageData); @@ -154,18 +150,15 @@ QImageData * QImageData::create(const QSize &size, QImage::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->bytes_per_line = params.bytesPerLine; + d->nbytes = params.totalSize; d->data = (uchar *)malloc(d->nbytes); - if (!d->data) { - return 0; - } + if (!d->data) + return nullptr; d->ref.ref(); return d.take(); - } QImageData::~QImageData() @@ -298,6 +291,7 @@ bool QImageData::checkForAlphaPixels() const case QImage::Format_BGR30: case QImage::Format_RGB30: case QImage::Format_Grayscale8: + case QImage::Format_Grayscale16: case QImage::Format_RGBX64: break; case QImage::Format_Invalid: @@ -720,6 +714,7 @@ bool QImageData::checkForAlphaPixels() const \value Format_A2RGB30_Premultiplied The image is stored using a 32-bit premultiplied ARGB format (2-10-10-10). (added in Qt 5.4) \value Format_Alpha8 The image is stored using an 8-bit alpha only format. (added in Qt 5.5) \value Format_Grayscale8 The image is stored using an 8-bit grayscale format. (added in Qt 5.5) + \value Format_Grayscale16 The image is stored using an 16-bit grayscale format. (added in Qt 5.13) \value Format_RGBX64 The image is stored using a 64-bit halfword-ordered RGB(x) format (16-16-16-16). This is the same as the Format_RGBX64 except alpha must always be 65535. (added in Qt 5.12) \value Format_RGBA64 The image is stored using a 64-bit halfword-ordered RGBA format (16-16-16-16). (added in Qt 5.12) @@ -746,7 +741,7 @@ bool QImageData::checkForAlphaPixels() const \sa isNull() */ -QImage::QImage() Q_DECL_NOEXCEPT +QImage::QImage() noexcept : QPaintDevice() { d = 0; @@ -786,27 +781,27 @@ QImage::QImage(const QSize &size, Format format) QImageData *QImageData::create(uchar *data, int width, int height, int bpl, QImage::Format format, bool readOnly, QImageCleanupFunction cleanupFunction, void *cleanupInfo) { - QImageData *d = 0; - - if (format == QImage::Format_Invalid) - return d; + if (width <= 0 || height <= 0 || !data || format == QImage::Format_Invalid) + return nullptr; const int depth = qt_depthForFormat(format); - const int calc_bytes_per_line = ((width * depth + 31)/32) * 4; - const int min_bytes_per_line = (width * depth + 7)/8; - - if (bpl <= 0) - bpl = calc_bytes_per_line; - - if (width <= 0 || height <= 0 || !data - || INT_MAX/sizeof(uchar *) < uint(height) - || INT_MAX/uint(depth) < uint(width) - || bpl <= 0 - || bpl < min_bytes_per_line - || INT_MAX/uint(bpl) < uint(height)) - return d; // invalid parameter(s) + auto params = calculateImageParameters(width, height, depth); + if (!params.isValid()) + return nullptr; + + if (bpl > 0) { + // can't overflow, because has calculateImageParameters already done this multiplication + const int min_bytes_per_line = (width * depth + 7)/8; + if (bpl < min_bytes_per_line) + return nullptr; + + // recalculate the total with this value + params.bytesPerLine = bpl; + if (mul_overflow<qsizetype>(bpl, height, ¶ms.totalSize)) + return nullptr; + } - d = new QImageData; + QImageData *d = new QImageData; d->ref.ref(); d->own_data = false; @@ -817,8 +812,8 @@ QImageData *QImageData::create(uchar *data, int width, int height, int bpl, QIm d->depth = depth; d->format = format; - d->bytes_per_line = bpl; - d->nbytes = d->bytes_per_line * height; + d->bytes_per_line = params.bytesPerLine; + d->nbytes = params.totalSize; d->cleanupFunction = cleanupFunction; d->cleanupInfo = cleanupInfo; @@ -1099,13 +1094,30 @@ void QImage::detach() } -static void copyMetadata(QImageData *dst, const QImageData *src) +static void copyPhysicalMetadata(QImageData *dst, const QImageData *src) { - // Doesn't copy colortable and alpha_clut, or offset. dst->dpmx = src->dpmx; dst->dpmy = src->dpmy; dst->devicePixelRatio = src->devicePixelRatio; +} + +static void copyMetadata(QImageData *dst, const QImageData *src) +{ + // Doesn't copy colortable and alpha_clut, or offset. + copyPhysicalMetadata(dst, src); dst->text = src->text; + dst->colorSpace = src->colorSpace; +} + +static void copyMetadata(QImage *dst, const QImage &src) +{ + dst->setDotsPerMeterX(src.dotsPerMeterX()); + dst->setDotsPerMeterY(src.dotsPerMeterY()); + dst->setDevicePixelRatio(src.devicePixelRatio()); + const auto textKeys = src.textKeys(); + for (const auto &key: textKeys) + dst->setText(key, src.text(key)); + } /*! @@ -1319,7 +1331,7 @@ QRect QImage::rect() const The image depth is the number of bits used to store a single pixel, also called bits per pixel (bpp). - The supported depths are 1, 8, 16, 24 and 32. + The supported depths are 1, 8, 16, 24, 32 and 64. \sa bitPlaneCount(), convertToFormat(), {QImage#Image Formats}{Image Formats}, {QImage#Image Information}{Image Information} @@ -1384,7 +1396,7 @@ void QImage::setColorTable(const QVector<QRgb> colors) #if QT_VERSION >= QT_VERSION_CHECK(6,0,0) d->colortable = colors; #else - d->colortable = qMove(const_cast<QVector<QRgb>&>(colors)); + d->colortable = std::move(const_cast<QVector<QRgb>&>(colors)); #endif d->has_alpha_clut = false; for (int i = 0; i < d->colortable.size(); ++i) { @@ -1458,6 +1470,7 @@ void QImage::setDevicePixelRatio(qreal scaleFactor) d->devicePixelRatio = scaleFactor; } +#if QT_DEPRECATED_SINCE(5, 10) /*! \since 4.6 \obsolete @@ -1474,6 +1487,7 @@ int QImage::byteCount() const Q_ASSERT(!d || d->nbytes < std::numeric_limits<int>::max()); return d ? int(d->nbytes) : 0; } +#endif /*! \since 5.10 @@ -1494,10 +1508,17 @@ qsizetype QImage::sizeInBytes() const \sa scanLine() */ +#if QT_VERSION >= QT_VERSION_CHECK(6,0,0) +qsizetype QImage::bytesPerLine() const +{ + return d ? d->bytes_per_line : 0; +} +#else int QImage::bytesPerLine() const { return d ? d->bytes_per_line : 0; } +#endif /*! @@ -1553,7 +1574,9 @@ void QImage::setColor(int i, QRgb c) 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. + The scanline data is as minimum 32-bit aligned. For 64-bit formats + it follows the native alignment of 64-bit integers (64-bit for most + platforms, but notably 32-bit on i386). \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 @@ -2048,6 +2071,7 @@ static bool highColorPrecision(QImage::Format format) case QImage::Format_RGBX64: case QImage::Format_RGBA64: case QImage::Format_RGBA64_Premultiplied: + case QImage::Format_Grayscale16: return true; default: break; @@ -2069,13 +2093,7 @@ QImage QImage::convertToFormat_helper(Format format, Qt::ImageConversionFlags fl Image_Converter converter = qimage_converter_map[d->format][format]; if (!converter && format > QImage::Format_Indexed8 && d->format > QImage::Format_Indexed8) { if (highColorPrecision(format) && highColorPrecision(d->format)) { - // Convert over RGBA64_Premultiplied - if (format == QImage::Format_RGBA64_Premultiplied) - converter = convert_generic_to_rgb64; - else { - Q_ASSERT(d->format != QImage::Format_RGBA64_Premultiplied); - return convertToFormat(Format_RGBA64_Premultiplied, flags).convertToFormat(format, flags); - } + converter = convert_generic_to_rgb64; } else converter = convert_generic; } @@ -2256,6 +2274,29 @@ bool QImage::reinterpretAsFormat(Format format) } /*! + \since 5.13 + + Detach and convert the image to the given \a format in place. + + The specified image conversion \a flags control how the image data + is handled during the conversion process. + + \sa convertToFormat() +*/ + +void QImage::convertTo(Format format, Qt::ImageConversionFlags flags) +{ + if (!d || format == QImage::Format_Invalid) + return; + + detach(); + if (convertToFormat_inplace(format, flags)) + return; + + *this = convertToFormat_helper(format, flags); +} + +/*! \fn bool QImage::valid(const QPoint &pos) const Returns \c true if \a pos is a valid coordinate pair within the @@ -2541,6 +2582,10 @@ QColor QImage::pixelColor(int x, int y) const case Format_RGBA64_Premultiplied: c = reinterpret_cast<const QRgba64 *>(s)[x]; break; + case Format_Grayscale16: { + quint16 v = reinterpret_cast<const quint16 *>(s)[x]; + return QColor(qRgba64(v, v, v, 0xffff)); + } default: c = QRgba64::fromArgb32(pixel(x, y)); break; @@ -2651,6 +2696,7 @@ bool QImage::allGray() const case Format_Alpha8: return false; case Format_Grayscale8: + case Format_Grayscale16: return true; case Format_RGB32: case Format_ARGB32: @@ -2717,7 +2763,7 @@ bool QImage::isGrayscale() const if (d->format == QImage::Format_Alpha8) return false; - if (d->format == QImage::Format_Grayscale8) + if (d->format == QImage::Format_Grayscale8 || d->format == QImage::Format_Grayscale16) return true; switch (depth()) { @@ -2927,8 +2973,10 @@ QImage QImage::createAlphaMask(Qt::ImageConversionFlags flags) const } QImage mask(d->width, d->height, Format_MonoLSB); - if (!mask.isNull()) + if (!mask.isNull()) { dither_to_Mono(mask.d, d, flags, true); + copyPhysicalMetadata(mask.d, d); + } return mask; } @@ -3046,6 +3094,7 @@ QImage QImage::createHeuristicMask(bool clipTight) const #undef PIX + copyPhysicalMetadata(m.d, d); return m; } #endif //QT_NO_IMAGE_HEURISTIC_MASK @@ -3089,6 +3138,8 @@ QImage QImage::createMaskFromColor(QRgb color, Qt::MaskMode mode) const } if (mode == Qt::MaskOutColor) maskImage.invertPixels(); + + copyPhysicalMetadata(maskImage.d, d); return maskImage; } @@ -3351,6 +3402,7 @@ QImage QImage::rgbSwapped_helper() const break; case Format_Alpha8: case Format_Grayscale8: + case Format_Grayscale16: return *this; case Format_Mono: case Format_MonoLSB: @@ -3462,6 +3514,7 @@ void QImage::rgbSwapped_inplace() break; case Format_Alpha8: case Format_Grayscale8: + case Format_Grayscale16: return; case Format_Mono: case Format_MonoLSB: @@ -3553,7 +3606,7 @@ void QImage::rgbSwapped_inplace() 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), it is auto-detected based on the file's suffix and header. For details, see - {QImageReader::setAutoDetectImageFormat()}{QImageReader}. + QImageReader::setAutoDetectImageFormat(). The file name can either refer to an actual file on disk or to one of the application's embedded resources. See the @@ -3750,7 +3803,9 @@ QDataStream &operator>>(QDataStream &s, QImage &image) return s; } } - image = QImageReader(s.device(), 0).read(); + image = QImageReader(s.device(), s.version() == 1 ? "bmp" : "png").read(); + if (image.isNull() && s.version() >= 5) + s.setStatus(QDataStream::ReadPastEnd); return s; } #endif // QT_NO_DATASTREAM @@ -4635,12 +4690,14 @@ QImage QImage::smoothScaled(int w, int h) const { case QImage::Format_RGBX8888: #endif case QImage::Format_RGBA8888_Premultiplied: +#if QT_CONFIG(raster_64bit) case QImage::Format_RGBX64: case QImage::Format_RGBA64_Premultiplied: break; case QImage::Format_RGBA64: src = src.convertToFormat(QImage::Format_RGBA64_Premultiplied); break; +#endif default: if (src.hasAlphaChannel()) src = src.convertToFormat(QImage::Format_ARGB32_Premultiplied); @@ -4656,8 +4713,7 @@ QImage QImage::smoothScaled(int w, int h) const { static QImage rotated90(const QImage &image) { QImage out(image.height(), image.width(), image.format()); - out.setDotsPerMeterX(image.dotsPerMeterY()); - out.setDotsPerMeterY(image.dotsPerMeterX()); + copyMetadata(&out, image); if (image.colorCount() > 0) out.setColorTable(image.colorTable()); int w = image.width(); @@ -4685,8 +4741,7 @@ 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()); + copyMetadata(&out, image); if (image.colorCount() > 0) out.setColorTable(image.colorTable()); int w = image.width(); @@ -4698,8 +4753,7 @@ static QImage rotated180(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()); + copyMetadata(&out, image); if (image.colorCount() > 0) out.setColorTable(image.colorTable()); int w = image.width(); @@ -4901,6 +4955,132 @@ QTransform QImage::trueMatrix(const QTransform &matrix, int w, int h) return matrix * QTransform().translate(-delta.x(), -delta.y()); } +/*! + \since 5.14 + + Sets the image color space to \a colorSpace without performing any conversions on image data. + + \sa colorSpace() +*/ +void QImage::setColorSpace(const QColorSpace &colorSpace) +{ + if (!d) + return; + if (d->colorSpace == colorSpace) + return; + if (!isDetached()) // Detach only if shared, not for read-only data. + detach(); + d->colorSpace = colorSpace; +} + +/*! + \since 5.14 + + Converts the image to \a colorSpace. + + If the image has no valid color space, the method does nothing. + + \sa convertedToColorSpace(), setColorSpace() +*/ +void QImage::convertToColorSpace(const QColorSpace &colorSpace) +{ + if (!d) + return; + if (!d->colorSpace.isValid()) + return; + if (!colorSpace.isValid()) { + qWarning() << "QImage::convertToColorSpace: Output colorspace is not valid"; + return; + } + detach(); + applyColorTransform(d->colorSpace.transformationToColorSpace(colorSpace)); + d->colorSpace = colorSpace; +} + +/*! + \since 5.14 + + Returns the image converted to \a colorSpace. + + If the image has no valid color space, a null QImage is returned. + + \sa convertToColorSpace() +*/ +QImage QImage::convertedToColorSpace(const QColorSpace &colorSpace) const +{ + if (!d || !d->colorSpace.isValid() || !colorSpace.isValid()) + return QImage(); + QImage image = copy(); + image.convertToColorSpace(colorSpace); + return image; +} + +/*! + \since 5.14 + + Returns the color space of the image if a color space is defined. +*/ +QColorSpace QImage::colorSpace() const +{ + if (!d) + return QColorSpace::Undefined; + return d->colorSpace; +} + +/*! + \since 5.14 + + Applies the color transformation \a transform to all pixels in the image. +*/ +void QImage::applyColorTransform(const QColorTransform &transform) +{ + QImage::Format oldFormat = format(); + if (depth() > 32) { + if (format() != QImage::Format_RGBX64 && format() != QImage::Format_RGBA64 + && format() != QImage::Format_RGBA64_Premultiplied) + *this = std::move(*this).convertToFormat(QImage::Format_RGBA64); + } else if (format() != QImage::Format_ARGB32 && format() != QImage::Format_RGB32 + && format() != QImage::Format_ARGB32_Premultiplied) { + if (hasAlphaChannel()) + *this = std::move(*this).convertToFormat(QImage::Format_ARGB32); + else + *this = std::move(*this).convertToFormat(QImage::Format_RGB32); + } + + QColorTransformPrivate::TransformFlags flags = QColorTransformPrivate::Unpremultiplied; + switch (format()) { + case Format_ARGB32_Premultiplied: + case Format_RGBA64_Premultiplied: + flags = QColorTransformPrivate::Premultiplied; + break; + case Format_RGB32: + case Format_RGBX64: + flags = QColorTransformPrivate::InputOpaque; + break; + case Format_ARGB32: + case Format_RGBA64: + break; + default: + Q_UNREACHABLE(); + } + + if (depth() > 32) { + for (int i = 0; i < height(); ++i) { + QRgba64 *scanline = reinterpret_cast<QRgba64 *>(scanLine(i)); + transform.d_func()->apply(scanline, scanline, width(), flags); + } + } else { + for (int i = 0; i < height(); ++i) { + QRgb *scanline = reinterpret_cast<QRgb *>(scanLine(i)); + transform.d_func()->apply(scanline, scanline, width(), flags); + } + } + + if (oldFormat != format()) + *this = std::move(*this).convertToFormat(oldFormat); +} + + bool QImageData::convertInPlace(QImage::Format newFormat, Qt::ImageConversionFlags flags) { if (format == newFormat) @@ -5357,13 +5537,26 @@ static Q_CONSTEXPR QPixelFormat pixelformats[] = { /*PREMULTIPLIED*/ QPixelFormat::Premultiplied, /*INTERPRETATION*/ QPixelFormat::UnsignedShort, /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian), + //QImage::Format_Grayscale16: + QPixelFormat(QPixelFormat::Grayscale, + /*GRAY*/ 16, + /*SECOND*/ 0, + /*THIRD*/ 0, + /*FOURTH*/ 0, + /*FIFTH*/ 0, + /*ALPHA*/ 0, + /*ALPHA USAGE*/ QPixelFormat::IgnoresAlpha, + /*ALPHA POSITION*/ QPixelFormat::AtBeginning, + /*PREMULTIPLIED*/ QPixelFormat::NotPremultiplied, + /*INTERPRETATION*/ QPixelFormat::UnsignedShort, + /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian), }; Q_STATIC_ASSERT(sizeof(pixelformats) / sizeof(*pixelformats) == QImage::NImageFormats); /*! Returns the QImage::Format as a QPixelFormat */ -QPixelFormat QImage::pixelFormat() const Q_DECL_NOTHROW +QPixelFormat QImage::pixelFormat() const noexcept { return toPixelFormat(format()); } @@ -5371,7 +5564,7 @@ QPixelFormat QImage::pixelFormat() const Q_DECL_NOTHROW /*! Converts \a format into a QPixelFormat */ -QPixelFormat QImage::toPixelFormat(QImage::Format format) Q_DECL_NOTHROW +QPixelFormat QImage::toPixelFormat(QImage::Format format) noexcept { Q_ASSERT(static_cast<int>(format) < NImageFormats); return pixelformats[format]; @@ -5380,7 +5573,7 @@ QPixelFormat QImage::toPixelFormat(QImage::Format format) Q_DECL_NOTHROW /*! Converts \a format into a QImage::Format */ -QImage::Format QImage::toImageFormat(QPixelFormat format) Q_DECL_NOTHROW +QImage::Format QImage::toImageFormat(QPixelFormat format) noexcept { for (int i = 0; i < NImageFormats; i++) { if (format == pixelformats[i]) @@ -5396,7 +5589,7 @@ Q_GUI_EXPORT void qt_imageTransform(QImage &src, QImageIOHandler::Transformation if (orient == QImageIOHandler::TransformationRotate270) { src = rotated270(src); } else { - src = qMove(src).mirrored(orient & QImageIOHandler::TransformationMirror, + src = std::move(src).mirrored(orient & QImageIOHandler::TransformationMirror, orient & QImageIOHandler::TransformationFlip); if (orient & QImageIOHandler::TransformationRotate90) src = rotated90(src); diff --git a/src/gui/image/qimage.h b/src/gui/image/qimage.h index 4b7a3b1ead..9d177142d9 100644 --- a/src/gui/image/qimage.h +++ b/src/gui/image/qimage.h @@ -61,9 +61,11 @@ Q_FORWARD_DECLARE_MUTABLE_CG_TYPE(CGImage); QT_BEGIN_NAMESPACE +class QColorSpace; +class QColorTransform; class QIODevice; -class QStringList; class QMatrix; +class QStringList; class QTransform; class QVariant; template <class T> class QList; @@ -128,17 +130,14 @@ public: Format_RGBX64, Format_RGBA64, Format_RGBA64_Premultiplied, -#if 0 - // reserved for future use Format_Grayscale16, -#endif #ifndef Q_QDOC NImageFormats #endif }; Q_ENUM(Format) - QImage() Q_DECL_NOEXCEPT; + QImage() noexcept; QImage(const QSize &size, Format format); QImage(int width, int height, Format format); QImage(uchar *data, int width, int height, Format format, QImageCleanupFunction cleanupFunction = nullptr, void *cleanupInfo = nullptr); @@ -153,7 +152,7 @@ public: QImage(const QImage &); #ifdef Q_COMPILER_RVALUE_REFS - inline QImage(QImage &&other) Q_DECL_NOEXCEPT + inline QImage(QImage &&other) noexcept : QPaintDevice(), d(nullptr) { qSwap(d, other.d); } #endif @@ -161,10 +160,10 @@ public: QImage &operator=(const QImage &); #ifdef Q_COMPILER_RVALUE_REFS - inline QImage &operator=(QImage &&other) Q_DECL_NOEXCEPT + inline QImage &operator=(QImage &&other) noexcept { qSwap(d, other.d); return *this; } #endif - inline void swap(QImage &other) Q_DECL_NOEXCEPT + inline void swap(QImage &other) noexcept { qSwap(d, other.d); } bool isNull() const; @@ -199,6 +198,8 @@ public: Q_REQUIRED_RESULT QImage convertToFormat(Format f, const QVector<QRgb> &colorTable, Qt::ImageConversionFlags flags = Qt::AutoColor) const; bool reinterpretAsFormat(Format f); + void convertTo(Format f, Qt::ImageConversionFlags flags = Qt::AutoColor); + int width() const; int height() const; QSize size() const; @@ -227,7 +228,11 @@ public: uchar *scanLine(int); const uchar *scanLine(int) const; const uchar *constScanLine(int) const; +#if QT_VERSION >= QT_VERSION_CHECK(6,0,0) + qsizetype bytesPerLine() const; +#else int bytesPerLine() const; +#endif bool valid(int x, int y) const; bool valid(const QPoint &pt) const; @@ -286,17 +291,23 @@ public: QImage mirrored(bool horizontally = false, bool vertically = true) const & { return mirrored_helper(horizontally, vertically); } QImage &&mirrored(bool horizontally = false, bool vertically = true) && - { mirrored_inplace(horizontally, vertically); return qMove(*this); } + { mirrored_inplace(horizontally, vertically); return std::move(*this); } QImage rgbSwapped() const & { return rgbSwapped_helper(); } QImage &&rgbSwapped() && - { rgbSwapped_inplace(); return qMove(*this); } + { rgbSwapped_inplace(); return std::move(*this); } #else QImage mirrored(bool horizontally = false, bool vertically = true) const; QImage rgbSwapped() const; #endif void invertPixels(InvertMode = InvertRgb); + QColorSpace colorSpace() const; + QImage convertedToColorSpace(const QColorSpace &) const; + void convertToColorSpace(const QColorSpace &); + void setColorSpace(const QColorSpace &); + + void applyColorTransform(const QColorTransform &transform); bool load(QIODevice *device, const char* format); bool load(const QString &fileName, const char *format = nullptr); @@ -330,9 +341,9 @@ public: QString text(const QString &key = QString()) const; void setText(const QString &key, const QString &value); - QPixelFormat pixelFormat() const Q_DECL_NOTHROW; - static QPixelFormat toPixelFormat(QImage::Format format) Q_DECL_NOTHROW; - static QImage::Format toImageFormat(QPixelFormat format) Q_DECL_NOTHROW; + QPixelFormat pixelFormat() const noexcept; + static QPixelFormat toPixelFormat(QImage::Format format) noexcept; + static QImage::Format toImageFormat(QPixelFormat format) noexcept; // Platform specific conversion functions #if defined(Q_OS_DARWIN) || defined(Q_QDOC) diff --git a/src/gui/image/qimage_conversions.cpp b/src/gui/image/qimage_conversions.cpp index 964dc0d5c6..837ac88470 100644 --- a/src/gui/image/qimage_conversions.cpp +++ b/src/gui/image/qimage_conversions.cpp @@ -39,7 +39,7 @@ #include <private/qdrawhelper_p.h> #include <private/qguiapplication_p.h> -#include <private/qcolorprofile_p.h> +#include <private/qcolortrclut_p.h> #include <private/qendian_p.h> #include <private/qsimd_p.h> #include <private/qimage_p.h> @@ -100,7 +100,7 @@ const uchar *qt_get_bitflip_array() void qGamma_correct_back_to_linear_cs(QImage *image) { - const QColorProfile *cp = QGuiApplicationPrivate::instance()->colorProfileForA32Text(); + const QColorTrcLut *cp = QGuiApplicationPrivate::instance()->colorProfileForA32Text(); if (!cp) return; // gamma correct the pixels back to linear color space... @@ -223,18 +223,29 @@ void convert_generic(QImageData *dest, const QImageData *src, Qt::ImageConversio void convert_generic_to_rgb64(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) { - Q_ASSERT(dest->format == QImage::Format_RGBA64_Premultiplied); + Q_ASSERT(dest->format > QImage::Format_Indexed8); Q_ASSERT(src->format > QImage::Format_Indexed8); + QRgba64 buf[BufferSize]; + QRgba64 *buffer = buf; const QPixelLayout *srcLayout = &qPixelLayouts[src->format]; + const QPixelLayout *destLayout = &qPixelLayouts[dest->format]; const uchar *srcData = src->data; uchar *destData = dest->data; const FetchAndConvertPixelsFunc64 fetch = srcLayout->fetchToRGBA64PM; + const ConvertAndStorePixelsFunc64 store = qStoreFromRGBA64PM[dest->format]; for (int y = 0; y < src->height; ++y) { - const QRgba64 *ptr = fetch((QRgba64*)destData, srcData, 0, src->width, nullptr, nullptr); - if (ptr != (const QRgba64*)destData) { - memcpy(destData, ptr, dest->bytes_per_line); + int x = 0; + while (x < src->width) { + int l = src->width - x; + if (destLayout->bpp == QPixelLayout::BPP64) + buffer = reinterpret_cast<QRgba64 *>(destData) + x; + else + l = qMin(l, BufferSize); + const QRgba64 *ptr = fetch(buffer, srcData, x, l, nullptr, nullptr); + store(destData, ptr, x, l, nullptr, nullptr); + x += l; } srcData += src->bytes_per_line; destData += dest->bytes_per_line; @@ -817,10 +828,10 @@ static bool convert_indexed8_to_ARGB_PM_inplace(QImageData *data, Qt::ImageConve Q_ASSERT(data->own_data); const int depth = 32; - - const qsizetype dst_bytes_per_line = ((data->width * depth + 31) >> 5) << 2; - const qsizetype nbytes = dst_bytes_per_line * data->height; - uchar *const newData = (uchar *)realloc(data->data, nbytes); + auto params = QImageData::calculateImageParameters(data->width, data->height, depth); + if (params.bytesPerLine < 0) + return false; + uchar *const newData = (uchar *)realloc(data->data, params.totalSize); if (!newData) return false; @@ -828,10 +839,10 @@ static bool convert_indexed8_to_ARGB_PM_inplace(QImageData *data, Qt::ImageConve // start converting from the end because the end image is bigger than the source uchar *src_data = newData + data->nbytes; // end of src - quint32 *dest_data = (quint32 *) (newData + nbytes); // end of dest > end of src + quint32 *dest_data = (quint32 *) (newData + params.totalSize); // end of dest > end of src const int width = data->width; const int src_pad = data->bytes_per_line - width; - const int dest_pad = (dst_bytes_per_line >> 2) - width; + const int dest_pad = (params.bytesPerLine >> 2) - width; if (data->colortable.size() == 0) { data->colortable.resize(256); for (int i = 0; i < 256; ++i) @@ -858,9 +869,9 @@ static bool convert_indexed8_to_ARGB_PM_inplace(QImageData *data, Qt::ImageConve data->colortable = QVector<QRgb>(); data->format = QImage::Format_ARGB32_Premultiplied; - data->bytes_per_line = dst_bytes_per_line; + data->bytes_per_line = params.bytesPerLine; data->depth = depth; - data->nbytes = nbytes; + data->nbytes = params.totalSize; return true; } @@ -871,10 +882,10 @@ static bool convert_indexed8_to_ARGB_inplace(QImageData *data, Qt::ImageConversi Q_ASSERT(data->own_data); const int depth = 32; - - const qsizetype dst_bytes_per_line = ((data->width * depth + 31) >> 5) << 2; - const qsizetype nbytes = dst_bytes_per_line * data->height; - uchar *const newData = (uchar *)realloc(data->data, nbytes); + auto params = QImageData::calculateImageParameters(data->width, data->height, depth); + if (params.bytesPerLine < 0) + return false; + uchar *const newData = (uchar *)realloc(data->data, params.totalSize); if (!newData) return false; @@ -882,10 +893,10 @@ static bool convert_indexed8_to_ARGB_inplace(QImageData *data, Qt::ImageConversi // start converting from the end because the end image is bigger than the source uchar *src_data = newData + data->nbytes; - quint32 *dest_data = (quint32 *) (newData + nbytes); + quint32 *dest_data = (quint32 *) (newData + params.totalSize); const int width = data->width; const int src_pad = data->bytes_per_line - width; - const int dest_pad = (dst_bytes_per_line >> 2) - width; + const int dest_pad = (params.bytesPerLine >> 2) - width; if (data->colortable.size() == 0) { data->colortable.resize(256); for (int i = 0; i < 256; ++i) @@ -909,9 +920,9 @@ static bool convert_indexed8_to_ARGB_inplace(QImageData *data, Qt::ImageConversi data->colortable = QVector<QRgb>(); data->format = QImage::Format_ARGB32; - data->bytes_per_line = dst_bytes_per_line; + data->bytes_per_line = params.bytesPerLine; data->depth = depth; - data->nbytes = nbytes; + data->nbytes = params.totalSize; return true; } @@ -939,10 +950,10 @@ static bool convert_indexed8_to_RGB16_inplace(QImageData *data, Qt::ImageConvers Q_ASSERT(data->own_data); const int depth = 16; - - const qsizetype dst_bytes_per_line = ((data->width * depth + 31) >> 5) << 2; - const qsizetype nbytes = dst_bytes_per_line * data->height; - uchar *const newData = (uchar *)realloc(data->data, nbytes); + auto params = QImageData::calculateImageParameters(data->width, data->height, depth); + if (params.bytesPerLine < 0) + return false; + uchar *const newData = (uchar *)realloc(data->data, params.totalSize); if (!newData) return false; @@ -950,10 +961,10 @@ static bool convert_indexed8_to_RGB16_inplace(QImageData *data, Qt::ImageConvers // start converting from the end because the end image is bigger than the source uchar *src_data = newData + data->nbytes; - quint16 *dest_data = (quint16 *) (newData + nbytes); + quint16 *dest_data = (quint16 *) (newData + params.totalSize); const int width = data->width; const int src_pad = data->bytes_per_line - width; - const int dest_pad = (dst_bytes_per_line >> 1) - width; + const int dest_pad = (params.bytesPerLine >> 1) - width; quint16 colorTableRGB16[256]; const int tableSize = data->colortable.size(); @@ -983,9 +994,9 @@ static bool convert_indexed8_to_RGB16_inplace(QImageData *data, Qt::ImageConvers } data->format = QImage::Format_RGB16; - data->bytes_per_line = dst_bytes_per_line; + data->bytes_per_line = params.bytesPerLine; data->depth = depth; - data->nbytes = nbytes; + data->nbytes = params.totalSize; return true; } @@ -997,6 +1008,7 @@ static bool convert_RGB_to_RGB16_inplace(QImageData *data, Qt::ImageConversionFl const int depth = 16; + // cannot overflow, since we're shrinking the buffer const qsizetype dst_bytes_per_line = ((data->width * depth + 31) >> 5) << 2; const qsizetype src_bytes_per_line = data->bytes_per_line; quint32 *src_data = (quint32 *) data->data; @@ -1013,12 +1025,11 @@ static bool convert_RGB_to_RGB16_inplace(QImageData *data, Qt::ImageConversionFl data->depth = depth; data->nbytes = dst_bytes_per_line * data->height; uchar *const newData = (uchar *)realloc(data->data, data->nbytes); - if (newData) { + if (newData) data->data = newData; - return true; - } else { - return false; - } + + // can't fail, since we're shrinking + return true; } static void convert_ARGB_PM_to_ARGB(QImageData *dest, const QImageData *src) @@ -1205,33 +1216,6 @@ static void convert_RGBA64_to_ARGB32(QImageData *dest, const QImageData *src, Qt } template<bool RGBA> -static void convert_RGBA64PM_to_ARGB32(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) -{ - Q_ASSERT(src->format == QImage::Format_RGBA64_Premultiplied); - Q_ASSERT(RGBA || dest->format == QImage::Format_ARGB32); - Q_ASSERT(!RGBA || dest->format == QImage::Format_RGBA8888); - Q_ASSERT(src->width == dest->width); - Q_ASSERT(src->height == dest->height); - - const int src_pad = (src->bytes_per_line >> 3) - src->width; - const int dest_pad = (dest->bytes_per_line >> 2) - dest->width; - const QRgba64 *src_data = reinterpret_cast<const QRgba64 *>(src->data); - uint *dest_data = reinterpret_cast<uint *>(dest->data); - - for (int i = 0; i < src->height; ++i) { - const QRgba64 *end = src_data + src->width; - while (src_data < end) { - QRgba64 s = src_data->unpremultiplied(); - *dest_data = RGBA ? ARGB2RGBA(s.toArgb32()) : s.toArgb32(); - ++src_data; - ++dest_data; - } - src_data += src_pad; - dest_data += dest_pad; - } -} - -template<bool RGBA> static void convert_ARGB32_to_RGBA64(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) { Q_ASSERT(RGBA || src->format == QImage::Format_ARGB32); @@ -1240,74 +1224,14 @@ static void convert_ARGB32_to_RGBA64(QImageData *dest, const QImageData *src, Qt 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 >> 3) - dest->width; - const uint *src_data = reinterpret_cast<const uint *>(src->data); - QRgba64 *dest_data = reinterpret_cast<QRgba64 *>(dest->data); - - for (int i = 0; i < src->height; ++i) { - const uint *end = src_data + src->width; - while (src_data < end) { - if (RGBA) - *dest_data = QRgba64::fromArgb32(RGBA2ARGB(*src_data)); - else - *dest_data = QRgba64::fromArgb32(*src_data); - ++src_data; - ++dest_data; - } - src_data += src_pad; - dest_data += dest_pad; - } -} - -template<QtPixelOrder PixelOrder> -static void convert_RGBA64PM_to_RGB30(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) -{ - Q_ASSERT(src->format == QImage::Format_RGBA64_Premultiplied); - Q_ASSERT(dest->format == QImage::Format_RGB30 || dest->format == QImage::Format_BGR30); - Q_ASSERT(src->width == dest->width); - Q_ASSERT(src->height == dest->height); - - const int src_pad = (src->bytes_per_line >> 3) - src->width; - const int dest_pad = (dest->bytes_per_line >> 2) - dest->width; - const QRgba64 *src_data = reinterpret_cast<const QRgba64 *>(src->data); - uint *dest_data = reinterpret_cast<uint *>(dest->data); - - for (int i = 0; i < src->height; ++i) { - const QRgba64 *end = src_data + src->width; - while (src_data < end) { - *dest_data = 0xc0000000 | qConvertRgb64ToRgb30<PixelOrder>(src_data->unpremultiplied()); - ++src_data; - ++dest_data; - } - src_data += src_pad; - dest_data += dest_pad; - } -} - -template<QtPixelOrder PixelOrder> -static void convert_RGBA64PM_to_A2RGB30(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) -{ - Q_ASSERT(src->format == QImage::Format_RGBA64_Premultiplied); - Q_ASSERT(dest->format == QImage::Format_A2RGB30_Premultiplied - || dest->format == QImage::Format_A2BGR30_Premultiplied); - Q_ASSERT(src->width == dest->width); - Q_ASSERT(src->height == dest->height); - - const int src_pad = (src->bytes_per_line >> 3) - src->width; - const int dest_pad = (dest->bytes_per_line >> 2) - dest->width; - const QRgba64 *src_data = reinterpret_cast<const QRgba64 *>(src->data); - uint *dest_data = reinterpret_cast<uint *>(dest->data); + const uchar *src_data = src->data; + uchar *dest_data = dest->data; + const FetchAndConvertPixelsFunc64 fetch = qPixelLayouts[src->format + 1].fetchToRGBA64PM; for (int i = 0; i < src->height; ++i) { - const QRgba64 *end = src_data + src->width; - while (src_data < end) { - *dest_data = qConvertRgb64ToRgb30<PixelOrder>(*src_data); - ++src_data; - ++dest_data; - } - src_data += src_pad; - dest_data += dest_pad; + fetch(reinterpret_cast<QRgba64 *>(dest_data), src_data, 0, src->width, nullptr, nullptr); + src_data += src->bytes_per_line;; + dest_data += dest->bytes_per_line; } } @@ -1447,6 +1371,56 @@ static bool convert_RGBA64PM_to_RGBA64_inplace(QImageData *data, Qt::ImageConver return true; } +static void convert_gray16_to_RGBA64(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) +{ + Q_ASSERT(src->format == QImage::Format_Grayscale16); + Q_ASSERT(dest->format == QImage::Format_RGBA64 || dest->format == QImage::Format_RGBX64 || + dest->format == QImage::Format_RGBA64_Premultiplied); + Q_ASSERT(src->width == dest->width); + Q_ASSERT(src->height == dest->height); + + const qsizetype sbpl = src->bytes_per_line; + const qsizetype dbpl = dest->bytes_per_line; + const uchar *src_data = src->data; + uchar *dest_data = dest->data; + + for (int i = 0; i < src->height; ++i) { + const quint16 *src_line = reinterpret_cast<const quint16 *>(src_data); + QRgba64 *dest_line = reinterpret_cast<QRgba64 *>(dest_data); + for (int j = 0; j < src->width; ++j) { + quint16 s = src_line[j]; + dest_line[j] = qRgba64(s, s, s, 0xFFFF); + } + src_data += sbpl; + dest_data += dbpl; + } +} + +static void convert_RGBA64_to_gray16(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) +{ + Q_ASSERT(dest->format == QImage::Format_Grayscale16); + Q_ASSERT(src->format == QImage::Format_RGBX64 || + src->format == QImage::Format_RGBA64_Premultiplied); + Q_ASSERT(src->width == dest->width); + Q_ASSERT(src->height == dest->height); + + const qsizetype sbpl = src->bytes_per_line; + const qsizetype dbpl = dest->bytes_per_line; + const uchar *src_data = src->data; + uchar *dest_data = dest->data; + + for (int i = 0; i < src->height; ++i) { + const QRgba64 *src_line = reinterpret_cast<const QRgba64 *>(src_data); + quint16 *dest_line = reinterpret_cast<quint16 *>(dest_data); + for (int j = 0; j < src->width; ++j) { + QRgba64 s = src_line[j].unpremultiplied(); + dest_line[j] = qGray(s.red(), s.green(), s.blue()); + } + src_data += sbpl; + dest_data += dbpl; + } +} + static QVector<QRgb> fix_color_table(const QVector<QRgb> &ctbl, QImage::Format format) { QVector<QRgb> colorTable = ctbl; @@ -2291,7 +2265,7 @@ static bool convert_Grayscale8_to_Indexed8_inplace(QImageData *data, Qt::ImageCo Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormats] = { { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, @@ -2312,7 +2286,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_Mono { @@ -2334,7 +2308,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_MonoLSB { @@ -2359,7 +2333,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, 0, 0, convert_Indexed8_to_Alpha8, convert_Indexed8_to_Grayscale8, - 0, 0, 0 + 0, 0, 0, 0 }, // Format_Indexed8 { @@ -2387,7 +2361,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat convert_RGB_to_RGB30<PixelOrderRGB, false>, 0, 0, 0, - 0, 0, 0 + 0, 0, 0, 0 }, // Format_RGB32 { @@ -2417,7 +2391,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, convert_ARGB32_to_RGBA64<false>, - 0 + 0, 0 }, // Format_ARGB32 { @@ -2442,7 +2416,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat convert_ARGB_to_RGBA, 0, 0, 0, 0, 0, 0, - 0, 0, 0 + 0, 0, 0, 0 }, // Format_ARGB32_Premultiplied { @@ -2464,7 +2438,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_RGB16 { @@ -2486,7 +2460,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_ARGB8565_Premultiplied { @@ -2508,7 +2482,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_RGB666 { @@ -2530,7 +2504,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_ARGB6666_Premultiplied { @@ -2552,7 +2526,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_RGB555 { @@ -2574,7 +2548,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_ARGB8555_Premultiplied { @@ -2597,7 +2571,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat convert_RGB888_to_RGB<true>, convert_RGB888_to_RGB<true>, convert_RGB888_to_RGB<true>, - 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_RGB888 { @@ -2619,7 +2593,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_RGB444 { @@ -2640,7 +2614,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_ARGB4444_Premultiplied { 0, @@ -2667,7 +2641,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat convert_RGB_to_RGB30<PixelOrderRGB, true>, 0, 0, 0, - 0, 0, 0 + 0, 0, 0, 0 }, // Format_RGBX8888 { 0, @@ -2696,7 +2670,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, convert_ARGB32_to_RGBA64<true>, - 0 + 0, 0 }, // Format_RGBA8888 { @@ -2718,7 +2692,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_RGBA8888_Premultiplied { @@ -2746,7 +2720,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat convert_BGR30_to_RGB30, convert_BGR30_to_RGB30, 0, 0, - 0, 0, 0 + 0, 0, 0, 0 }, // Format_BGR30 { 0, @@ -2773,8 +2747,8 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat convert_A2RGB30_PM_to_RGB30<true>, convert_BGR30_to_RGB30, 0, 0, - 0, 0, 0 - }, // Format_BGR30A2_Premultiplied + 0, 0, 0, 0 + }, // Format_A2BGR30_Premultiplied { 0, 0, @@ -2799,7 +2773,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat convert_BGR30_to_RGB30, 0, convert_passthrough, - 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0 }, // Format_RGB30 { 0, @@ -2826,8 +2800,8 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat convert_A2RGB30_PM_to_RGB30<false>, 0, 0, 0, - 0, 0, 0 - }, // Format_RGB30A2_Premultiplied + 0, 0, 0, 0 + }, // Format_A2RGB30_Premultiplied { 0, 0, @@ -2846,7 +2820,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_Alpha8 { 0, @@ -2872,7 +2846,8 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // self convert_passthrough, - convert_passthrough + convert_passthrough, + convert_RGBA64_to_gray16 }, // Format_RGBX64 { 0, @@ -2898,7 +2873,8 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, convert_RGBA64_to_RGBx64, 0, // self - convert_RGBA64_to_RGBA64PM + convert_RGBA64_to_RGBA64PM, + 0 }, // Format_RGBA64 { 0, @@ -2906,7 +2882,6 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - convert_RGBA64PM_to_ARGB32<false>, 0, 0, 0, @@ -2918,29 +2893,58 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - convert_RGBA64PM_to_ARGB32<true>, 0, - convert_RGBA64PM_to_RGB30<PixelOrderBGR>, - convert_RGBA64PM_to_A2RGB30<PixelOrderBGR>, - convert_RGBA64PM_to_RGB30<PixelOrderRGB>, - convert_RGBA64PM_to_A2RGB30<PixelOrderRGB>, + 0, + 0, + 0, 0, 0, 0, 0, 0, convert_RGBA64PM_to_RGBA64<true>, convert_RGBA64PM_to_RGBA64<false>, - 0 // self - } // Format_RGBA64_Premultiplied + 0, // self + convert_RGBA64_to_gray16 + }, // Format_RGBA64_Premultiplied + { + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, 0, + convert_gray16_to_RGBA64, + convert_gray16_to_RGBA64, + convert_gray16_to_RGBA64, + 0 // self + }, // Format_Grayscale16 }; InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QImage::NImageFormats] = { { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_Mono { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_MonoLSB { 0, @@ -2964,7 +2968,7 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma 0, 0, 0, 0, 0, convert_Indexed8_to_Alpha8_inplace, convert_Indexed8_to_Grayscale8_inplace, - 0, 0, 0 + 0, 0, 0, 0 }, // Format_Indexed8 { 0, @@ -2991,7 +2995,7 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma convert_RGB_to_RGB30_inplace<PixelOrderRGB, false>, 0, 0, 0, - 0, 0, 0 + 0, 0, 0, 0 }, // Format_RGB32 { 0, @@ -3018,7 +3022,7 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma convert_RGB_to_RGB30_inplace<PixelOrderRGB, false>, 0, 0, 0, - 0, 0, 0 + 0, 0, 0, 0 }, // Format_ARGB32 { 0, @@ -3042,34 +3046,34 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma convert_ARGB_to_RGBA_inplace<QImage::Format_RGBA8888_Premultiplied>, 0, 0, 0, 0, 0, 0, - 0, 0, 0 + 0, 0, 0, 0 }, // Format_ARGB32_Premultiplied { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_RGB16 { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_ARGB8565_Premultiplied { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_RGB666 { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_ARGB6666_Premultiplied { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_RGB555 { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_ARGB8555_Premultiplied { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_RGB888 { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_RGB444 { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_ARGB4444_Premultiplied { 0, @@ -3096,7 +3100,7 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma convert_RGB_to_RGB30_inplace<PixelOrderRGB, true>, 0, 0, 0, - 0, 0, 0 + 0, 0, 0, 0 }, // Format_RGBX8888 { 0, @@ -3123,7 +3127,7 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma convert_RGB_to_RGB30_inplace<PixelOrderRGB, true>, 0, 0, 0, - 0, 0, 0 + 0, 0, 0, 0 }, // Format_RGBA8888 { 0, @@ -3145,7 +3149,7 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_RGBA8888_Premultiplied { 0, @@ -3172,7 +3176,7 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma convert_BGR30_to_RGB30_inplace, convert_BGR30_to_A2RGB30_inplace, 0, 0, - 0, 0, 0 + 0, 0, 0, 0 }, // Format_BGR30 { 0, @@ -3198,8 +3202,8 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma 0, // self convert_A2RGB30_PM_to_RGB30_inplace<true>, convert_BGR30_to_RGB30_inplace, - 0, 0, 0, 0, 0 - }, // Format_BGR30A2_Premultiplied + 0, 0, 0, 0, 0, 0 + }, // Format_A2BGR30_Premultiplied { 0, 0, @@ -3224,7 +3228,7 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma convert_BGR30_to_A2RGB30_inplace, 0, // self convert_passthrough_inplace<QImage::Format_A2RGB30_Premultiplied>, - 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0 }, // Format_RGB30 { 0, @@ -3251,8 +3255,8 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma convert_A2RGB30_PM_to_RGB30_inplace<false>, 0, // self 0, 0, - 0, 0, 0 - }, // Format_RGB30A2_Premultiplied + 0, 0, 0, 0 + }, // Format_A2RGB30_Premultiplied { 0, 0, @@ -3276,7 +3280,7 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma 0, 0, 0, 0, 0, // self 0, - 0, 0, 0 + 0, 0, 0, 0 }, // Format_Alpha8 { 0, @@ -3301,26 +3305,32 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma 0, 0, 0, 0, 0, 0, // self - 0, 0, 0 + 0, 0, 0, 0 }, // Format_Grayscale8 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // self convert_passthrough_inplace<QImage::Format_RGBA64>, convert_passthrough_inplace<QImage::Format_RGBA64_Premultiplied>, + 0 }, // Format_RGBX64 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, convert_RGBA64_to_RGBx64_inplace, 0, // self - convert_RGBA64_to_RGBA64PM_inplace + convert_RGBA64_to_RGBA64PM_inplace, + 0 }, // Format_RGBA64 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, convert_RGBA64PM_to_RGBA64_inplace<true>, convert_RGBA64PM_to_RGBA64_inplace<false>, - 0 // self - } // Format_RGBA64_Premultiplied + 0, // self + 0 + }, // Format_RGBA64_Premultiplied + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, // Format_Grayscale16 }; static void qInitImageConversions() diff --git a/src/gui/image/qimage_darwin.mm b/src/gui/image/qimage_darwin.mm index a5c391ad21..7ca1a0869a 100644 --- a/src/gui/image/qimage_darwin.mm +++ b/src/gui/image/qimage_darwin.mm @@ -40,6 +40,7 @@ #include "qimage.h" #include <private/qcore_mac_p.h> +#include <private/qcoregraphics_p.h> #import <Foundation/Foundation.h> #import <CoreGraphics/CoreGraphics.h> @@ -98,32 +99,10 @@ CGImageRef QImage::toCGImage() const if (isNull()) return nil; - // Determine the target native format - uint cgflags = kCGImageAlphaNone; - switch (format()) { - case QImage::Format_ARGB32: - cgflags = kCGImageAlphaFirst | kCGBitmapByteOrder32Host; - break; - case QImage::Format_RGB32: - cgflags = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host; - break; - case QImage::Format_RGBA8888_Premultiplied: - cgflags = kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big; - break; - case QImage::Format_RGBA8888: - cgflags = kCGImageAlphaLast | kCGBitmapByteOrder32Big; - break; - case QImage::Format_RGBX8888: - cgflags = kCGImageAlphaNoneSkipLast | kCGBitmapByteOrder32Big; - break; - case QImage::Format_ARGB32_Premultiplied: - cgflags = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host; - break; - default: break; - } + CGBitmapInfo bitmapInfo = qt_mac_bitmapInfoForImage(*this); // Format not supported: return nil CGImageRef - if (cgflags == kCGImageAlphaNone) + if (bitmapInfo == kCGImageAlphaNone) return nil; // Create a data provider that owns a copy of the QImage and references the image data. @@ -140,7 +119,7 @@ CGImageRef QImage::toCGImage() const const bool shouldInterpolate = false; return CGImageCreate(width(), height(), bitsPerComponent, bitsPerPixel, - this->bytesPerLine(), colorSpace, cgflags, dataProvider, + this->bytesPerLine(), colorSpace, bitmapInfo, dataProvider, decode, shouldInterpolate, kCGRenderingIntentDefault); } diff --git a/src/gui/image/qimage_neon.cpp b/src/gui/image/qimage_neon.cpp index 57a24edeca..9dbcb11db5 100644 --- a/src/gui/image/qimage_neon.cpp +++ b/src/gui/image/qimage_neon.cpp @@ -52,65 +52,41 @@ Q_GUI_EXPORT void QT_FASTCALL qt_convert_rgb888_to_rgb32_neon(quint32 *dst, cons const quint32 *const end = dst + len; - // align dst on 64 bits - const int offsetToAlignOn8Bytes = (reinterpret_cast<quintptr>(dst) >> 2) & 0x1; - for (int i = 0; i < offsetToAlignOn8Bytes; ++i) { + // align dst on 128 bits + const int offsetToAlignOn16Bytes = (reinterpret_cast<quintptr>(dst) >> 2) & 0x3; + for (int i = 0; i < offsetToAlignOn16Bytes; ++i) { *dst++ = qRgb(src[0], src[1], src[2]); src += 3; } - if ((len - offsetToAlignOn8Bytes) >= 8) { - const quint32 *const simdEnd = end - 7; -#if !defined(Q_PROCESSOR_ARM_64) - register uint8x8_t fullVector asm ("d3") = vdup_n_u8(0xff); - do { + if ((len - offsetToAlignOn16Bytes) >= 16) { + const quint32 *const simdEnd = end - 15; + uint8x16x4_t dstVector; #if Q_BYTE_ORDER == Q_BIG_ENDIAN - asm volatile ( - "vld3.8 { d4, d5, d6 }, [%[SRC]] !\n\t" - "vst4.8 { d3, d4, d5, d6 }, [%[DST],:64] !\n\t" - : [DST]"+r" (dst), [SRC]"+r" (src) - : "w"(fullVector) - : "memory", "d4", "d5", "d6" - ); + dstVector.val[0] = vdupq_n_u8(0xff); #else - asm volatile ( - "vld3.8 { d0, d1, d2 }, [%[SRC]] !\n\t" - "vswp d0, d2\n\t" - "vst4.8 { d0, d1, d2, d3 }, [%[DST],:64] !\n\t" - : [DST]"+r" (dst), [SRC]"+r" (src) - : "w"(fullVector) - : "memory", "d0", "d1", "d2" - ); + dstVector.val[3] = vdupq_n_u8(0xff); #endif - } while (dst < simdEnd); -#else - register uint8x8_t fullVector asm ("v3") = vdup_n_u8(0xff); do { + uint8x16x3_t srcVector = vld3q_u8(src); + src += 3 * 16; #if Q_BYTE_ORDER == Q_BIG_ENDIAN - asm volatile ( - "ld3 { v4.8b, v5.8b, v6.8b }, [%[SRC]], #24 \n\t" - "st4 { v3.8b, v4.8b, v5.8b, v6.8b }, [%[DST]], #32 \n\t" - : [DST]"+r" (dst), [SRC]"+r" (src) - : "w"(fullVector) - : "memory", "v4", "v5", "v6" - ); + dstVector.val[1] = srcVector.val[0]; + dstVector.val[2] = srcVector.val[1]; + dstVector.val[3] = srcVector.val[2]; #else - asm volatile ( - "ld3 { v0.8b, v1.8b, v2.8b }, [%[SRC]], #24 \n\t" - "mov v4.8b, v2.8b\n\t" - "mov v2.8b, v0.8b\n\t" - "mov v0.8b, v4.8b\n\t" - "st4 { v0.8b, v1.8b, v2.8b, v3.8b }, [%[DST]], #32 \n\t" - : [DST]"+r" (dst), [SRC]"+r" (src) - : "w"(fullVector) - : "memory", "v0", "v1", "v2", "v4" - ); + dstVector.val[0] = srcVector.val[2]; + dstVector.val[1] = srcVector.val[1]; + dstVector.val[2] = srcVector.val[0]; #endif + vst4q_u8(reinterpret_cast<uint8_t*>(dst), dstVector); + dst += 16; } while (dst < simdEnd); -#endif } - while (dst != end) { + int i = 0; + int length = end - dst; + SIMD_EPILOGUE(i, length, 15) { *dst++ = qRgb(src[0], src[1], src[2]); src += 3; } diff --git a/src/gui/image/qimage_p.h b/src/gui/image/qimage_p.h index 2fe29a88d3..9da6acd0a7 100644 --- a/src/gui/image/qimage_p.h +++ b/src/gui/image/qimage_p.h @@ -51,7 +51,10 @@ // We mean it. // +#include <QtGui/qcolorspace.h> #include <QtGui/private/qtguiglobal_p.h> +#include <QtGui/qimage.h> +#include <QtCore/private/qnumeric_p.h> #include <QMap> #include <QVector> @@ -64,7 +67,7 @@ struct Q_GUI_EXPORT QImageData { // internal image data QImageData(); ~QImageData(); static QImageData *create(const QSize &size, QImage::Format format); - static QImageData *create(uchar *data, int w, int h, int bpl, QImage::Format format, bool readOnly, QImageCleanupFunction cleanupFunction = 0, void *cleanupInfo = 0); + static QImageData *create(uchar *data, int w, int h, int bpl, QImage::Format format, bool readOnly, QImageCleanupFunction cleanupFunction = nullptr, void *cleanupInfo = nullptr); QAtomicInt ref; @@ -104,8 +107,48 @@ struct Q_GUI_EXPORT QImageData { // internal image data bool doImageIO(const QImage *image, QImageWriter* io, int quality) const; QPaintEngine *paintEngine; + + QColorSpace colorSpace; + + struct ImageSizeParameters { + qsizetype bytesPerLine; + qsizetype totalSize; + bool isValid() const { return bytesPerLine > 0 && totalSize > 0; } + }; + static ImageSizeParameters calculateImageParameters(qsizetype width, qsizetype height, qsizetype depth); }; +inline QImageData::ImageSizeParameters +QImageData::calculateImageParameters(qsizetype width, qsizetype height, qsizetype depth) +{ + ImageSizeParameters invalid = { -1, -1 }; + if (height <= 0) + return invalid; + + // calculate the size, taking care of overflows + qsizetype bytes_per_line; + if (mul_overflow(width, depth, &bytes_per_line)) + return invalid; + if (add_overflow(bytes_per_line, qsizetype(31), &bytes_per_line)) + return invalid; + // bytes per scanline (must be multiple of 4) + bytes_per_line = (bytes_per_line >> 5) << 2; // can't overflow + + qsizetype total_size; + if (mul_overflow(height, bytes_per_line, &total_size)) + return invalid; + qsizetype dummy; + if (mul_overflow(height, qsizetype(sizeof(uchar *)), &dummy)) + return invalid; // why is this here? +#if QT_VERSION < QT_VERSION_CHECK(6,0,0) + // Disallow images where width * depth calculations might overflow + if (width > (INT_MAX - 31) / depth) + return invalid; +#endif + + return { bytes_per_line, total_size }; +} + typedef void (*Image_Converter)(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags); typedef bool (*InPlace_Image_Converter)(QImageData *data, Qt::ImageConversionFlags); @@ -156,6 +199,7 @@ inline int qt_depthForFormat(QImage::Format format) case QImage::Format_RGB16: case QImage::Format_RGB444: case QImage::Format_ARGB4444_Premultiplied: + case QImage::Format_Grayscale16: depth = 16; break; case QImage::Format_RGB666: diff --git a/src/gui/image/qimageiohandler.cpp b/src/gui/image/qimageiohandler.cpp index 5d1f8fa91f..0e7b541cf2 100644 --- a/src/gui/image/qimageiohandler.cpp +++ b/src/gui/image/qimageiohandler.cpp @@ -340,7 +340,7 @@ void QImageIOHandler::setDevice(QIODevice *device) /*! Returns the device currently assigned to the QImageIOHandler. If - not device has been assigned, 0 is returned. + not device has been assigned, \nullptr is returned. */ QIODevice *QImageIOHandler::device() const { @@ -416,6 +416,7 @@ QByteArray QImageIOHandler::format() const \sa read(), QIODevice::peek() */ +#if QT_DEPRECATED_SINCE(5, 13) /*! \obsolete @@ -426,6 +427,7 @@ QByteArray QImageIOHandler::name() const { return format(); } +#endif /*! Writes the image \a image to the assigned device. Returns \c true on diff --git a/src/gui/image/qimageiohandler.h b/src/gui/image/qimageiohandler.h index 990df96534..c20b84afbb 100644 --- a/src/gui/image/qimageiohandler.h +++ b/src/gui/image/qimageiohandler.h @@ -69,7 +69,10 @@ public: void setFormat(const QByteArray &format) const; QByteArray format() const; +#if QT_DEPRECATED_SINCE(5, 13) + QT_DEPRECATED_X("Use QImageIOHandler::format() instead") virtual QByteArray name() const; +#endif virtual bool canRead() const = 0; virtual bool read(QImage *image) = 0; diff --git a/src/gui/image/qimagereader.cpp b/src/gui/image/qimagereader.cpp index 6d358984d6..61f20e0c65 100644 --- a/src/gui/image/qimagereader.cpp +++ b/src/gui/image/qimagereader.cpp @@ -165,6 +165,7 @@ #endif #include <private/qimagereaderwriterhelpers_p.h> +#include <qtgui_tracepoints_p.h> #include <algorithm> @@ -525,7 +526,7 @@ bool QImageReaderPrivate::initHandler() // Try the most probable extension first int currentFormatIndex = extensions.indexOf(format.toLower()); if (currentFormatIndex > 0) - extensions.swap(0, currentFormatIndex); + extensions.swapItemsAt(0, currentFormatIndex); } int currentExtension = 0; @@ -755,13 +756,13 @@ void QImageReader::setDevice(QIODevice *device) d->device = device; d->deleteDevice = false; delete d->handler; - d->handler = 0; + d->handler = nullptr; d->text.clear(); } /*! - Returns the device currently assigned to QImageReader, or 0 if no - device has been assigned. + Returns the device currently assigned to QImageReader, or \nullptr + if no device has been assigned. */ QIODevice *QImageReader::device() const { @@ -1250,7 +1251,18 @@ bool QImageReader::read(QImage *image) d->handler->setOption(QImageIOHandler::Quality, d->quality); // read the image - if (!d->handler->read(image)) { + if (Q_TRACE_ENABLED(QImageReader_read_before_reading)) { + QString fileName = QStringLiteral("unknown"); + if (QFile *file = qobject_cast<QFile *>(d->device)) + fileName = file->fileName(); + Q_TRACE(QImageReader_read_before_reading, this, fileName); + } + + const bool result = d->handler->read(image); + + Q_TRACE(QImageReader_read_after_reading, this, result); + + if (!result) { d->imageReaderError = InvalidDataError; d->errorString = QImageReader::tr("Unable to read image data"); return false; diff --git a/src/gui/image/qimagewriter.cpp b/src/gui/image/qimagewriter.cpp index 5ce7e309bb..ec66588ddf 100644 --- a/src/gui/image/qimagewriter.cpp +++ b/src/gui/image/qimagewriter.cpp @@ -71,6 +71,16 @@ formats, in addition to any image format plugins that support writing. + \note QImageWriter assumes exclusive control over the file or + device that is assigned. Any attempts to modify the assigned file + or device during the lifetime of the QImageWriter object will + yield undefined results. If immediate access to a resource is + desired, the use of a scope is the recommended method. + + For example: + + \snippet qimagewriter/main.cpp 0 + \sa QImageReader, QImageIOHandler, QImageIOPlugin */ @@ -397,8 +407,8 @@ void QImageWriter::setDevice(QIODevice *device) } /*! - Returns the device currently assigned to QImageWriter, or 0 if no - device has been assigned. + Returns the device currently assigned to QImageWriter, or \nullptr + if no device has been assigned. */ QIODevice *QImageWriter::device() const { @@ -638,6 +648,7 @@ QImageIOHandler::Transformations QImageWriter::transformation() const return d->transformation; } +#if QT_DEPRECATED_SINCE(5, 13) /*! \obsolete @@ -669,6 +680,7 @@ QString QImageWriter::description() const { return d->description; } +#endif /*! \since 4.1 diff --git a/src/gui/image/qimagewriter.h b/src/gui/image/qimagewriter.h index 29c06ccdd2..ef84a59b7c 100644 --- a/src/gui/image/qimagewriter.h +++ b/src/gui/image/qimagewriter.h @@ -100,9 +100,12 @@ public: QImageIOHandler::Transformations transformation() const; void setTransformation(QImageIOHandler::Transformations orientation); - // Obsolete as of 4.1 +#if QT_DEPRECATED_SINCE(5, 13) + QT_DEPRECATED_X("Use QImageWriter::setText() instead") void setDescription(const QString &description); + QT_DEPRECATED_X("Use QImageReader::text() instead") QString description() const; +#endif void setText(const QString &key, const QString &text); diff --git a/src/gui/image/qmovie.cpp b/src/gui/image/qmovie.cpp index 010760de4c..79203c7b98 100644 --- a/src/gui/image/qmovie.cpp +++ b/src/gui/image/qmovie.cpp @@ -379,7 +379,7 @@ QFrameInfo QMoviePrivate::infoForFrame(int frameNumber) } if (frameNumber > greatestFrameNumber) greatestFrameNumber = frameNumber; - QPixmap aPixmap = QPixmap::fromImage(anImage); + QPixmap aPixmap = QPixmap::fromImage(std::move(anImage)); int aDelay = reader->nextImageDelay(); return QFrameInfo(aPixmap, aDelay); } else if (frameNumber != 0) { @@ -405,7 +405,7 @@ QFrameInfo QMoviePrivate::infoForFrame(int frameNumber) return QFrameInfo(); // Invalid } greatestFrameNumber = i; - QPixmap aPixmap = QPixmap::fromImage(anImage); + QPixmap aPixmap = QPixmap::fromImage(std::move(anImage)); int aDelay = reader->nextImageDelay(); QFrameInfo info(aPixmap, aDelay); // Cache it! @@ -659,7 +659,7 @@ void QMovie::setDevice(QIODevice *device) /*! Returns the device QMovie reads image data from. If no device has - currently been assigned, 0 is returned. + currently been assigned, \nullptr is returned. \sa setDevice(), fileName() */ diff --git a/src/gui/image/qpaintengine_pic_p.h b/src/gui/image/qpaintengine_pic_p.h index c3044796ad..c9e4b43197 100644 --- a/src/gui/image/qpaintengine_pic_p.h +++ b/src/gui/image/qpaintengine_pic_p.h @@ -103,7 +103,7 @@ protected: QPicturePaintEngine(QPaintEnginePrivate &dptr); private: - Q_DISABLE_COPY(QPicturePaintEngine) + Q_DISABLE_COPY_MOVE(QPicturePaintEngine) void writeCmdLength(int pos, const QRectF &r, bool corr); }; diff --git a/src/gui/image/qpicture.cpp b/src/gui/image/qpicture.cpp index 7aa221948e..56b82abcfa 100644 --- a/src/gui/image/qpicture.cpp +++ b/src/gui/image/qpicture.cpp @@ -636,7 +636,7 @@ bool QPicture::exec(QPainter *painter, QDataStream &s, int nrecords) if (d->formatMajor <= 5) { s >> ia >> i_8; painter->drawPolygon(ia, i_8 ? Qt::WindingFill : Qt::OddEvenFill); - a.clear(); + ia.clear(); } else { s >> a >> i_8; painter->drawPolygon(a, i_8 ? Qt::WindingFill : Qt::OddEvenFill); @@ -647,10 +647,10 @@ bool QPicture::exec(QPainter *painter, QDataStream &s, int nrecords) 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)); + path.moveTo(ia.value(0)); + path.cubicTo(ia.value(1), ia.value(2), ia.value(3)); painter->strokePath(path, painter->pen()); - a.clear(); + ia.clear(); } break; case QPicturePrivate::PdcDrawText: @@ -730,7 +730,7 @@ bool QPicture::exec(QPainter *painter, QDataStream &s, int nrecords) int index; s >> r >> index >> sr; Q_ASSERT(index < d->pixmap_list.size()); - pixmap = d->pixmap_list.at(index); + pixmap = d->pixmap_list.value(index); } else { s >> r >> pixmap >> sr; } @@ -744,7 +744,7 @@ bool QPicture::exec(QPainter *painter, QDataStream &s, int nrecords) int index; s >> r >> index >> p; Q_ASSERT(index < d->pixmap_list.size()); - pixmap = d->pixmap_list.at(index); + pixmap = d->pixmap_list.value(index); } else { s >> r >> pixmap >> p; } @@ -765,7 +765,7 @@ bool QPicture::exec(QPainter *painter, QDataStream &s, int nrecords) int index; s >> r >> index >> sr >> ul; Q_ASSERT(index < d->image_list.size()); - image = d->image_list.at(index); + image = d->image_list.value(index); } else { s >> r >> image >> sr >> ul; } @@ -817,7 +817,7 @@ bool QPicture::exec(QPainter *painter, QDataStream &s, int nrecords) int index; s >> index; Q_ASSERT(index < d->pen_list.size()); - pen = d->pen_list.at(index); + pen = d->pen_list.value(index); } else { s >> pen; } @@ -828,7 +828,7 @@ bool QPicture::exec(QPainter *painter, QDataStream &s, int nrecords) int index; s >> index; Q_ASSERT(index < d->brush_list.size()); - brush = d->brush_list.at(index); + brush = d->brush_list.value(index); } else { s >> brush; } @@ -858,7 +858,7 @@ bool QPicture::exec(QPainter *painter, QDataStream &s, int nrecords) break; case QPicturePrivate::PdcSetWXform: s >> i_8; - painter->setMatrixEnabled(i_8); + painter->setWorldMatrixEnabled(i_8); break; case QPicturePrivate::PdcSetWMatrix: if (d->formatMajor >= 8) { @@ -910,7 +910,7 @@ bool QPicture::exec(QPainter *painter, QDataStream &s, int nrecords) break; default: qWarning("QPicture::play: Invalid command %d", c); - if (len) // skip unknown command + if (len > 0) // skip unknown command s.device()->seek(s.device()->pos()+len); } #if defined(QT_DEBUG) @@ -1075,7 +1075,8 @@ bool QPicturePrivate::checkFormat() 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 + int bufSize = pictb.buffer().size(); + if (memcmp(mf_id, qt_mfhdr_tag, 4) != 0 || bufSize < 12) { // wrong header id or size qWarning("QPicturePaintEngine::checkFormat: Incorrect header"); pictb.close(); return false; @@ -1199,8 +1200,8 @@ 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. + fileName, or \nullptr if the file cannot be read or if the format + is not recognized. \sa load(), save() */ @@ -1542,7 +1543,7 @@ const QPicture &QPictureIO::picture() const { return d->pi; } int QPictureIO::status() const { return d->iostat; } /*! - Returns the picture format string or 0 if no format has been + Returns the picture format string or \nullptr if no format has been explicitly set. */ const char *QPictureIO::format() const { return d->frmt; } @@ -1865,7 +1866,7 @@ QList<QByteArray> QPictureIO::outputFormats() bool QPictureIO::read() { QFile file; - const char *picture_format; + QByteArray picture_format; QPictureHandler *h; if (d->iodev) { // read from io device @@ -1881,7 +1882,7 @@ bool QPictureIO::read() if (d->frmt.isEmpty()) { // Try to guess format picture_format = pictureFormat(d->iodev); // get picture format - if (!picture_format) { + if (picture_format.isEmpty()) { if (file.isOpen()) { // unknown format file.close(); d->iodev = 0; diff --git a/src/gui/image/qpicture.h b/src/gui/image/qpicture.h index ec7b4bd7e3..cac2ef5dfc 100644 --- a/src/gui/image/qpicture.h +++ b/src/gui/image/qpicture.h @@ -79,10 +79,10 @@ public: QPicture& operator=(const QPicture &p); #ifdef Q_COMPILER_RVALUE_REFS - inline QPicture &operator=(QPicture &&other) Q_DECL_NOEXCEPT + inline QPicture &operator=(QPicture &&other) noexcept { qSwap(d_ptr, other.d_ptr); return *this; } #endif - inline void swap(QPicture &other) Q_DECL_NOEXCEPT + inline void swap(QPicture &other) noexcept { d_ptr.swap(other.d_ptr); } void detach(); bool isDetached() const; diff --git a/src/gui/image/qpixmap.cpp b/src/gui/image/qpixmap.cpp index 4b2334ae52..5b4d218603 100644 --- a/src/gui/image/qpixmap.cpp +++ b/src/gui/image/qpixmap.cpp @@ -714,8 +714,8 @@ QBitmap QPixmap::createMaskFromColor(const QColor &maskColor, Qt::MaskMode mode) 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. + when loaded from a file in main thread; the key used is internal + and cannot be acquired. \sa loadFromData(), {QPixmap#Reading and Writing Image Files}{Reading and Writing Image Files} @@ -729,6 +729,7 @@ bool QPixmap::load(const QString &fileName, const char *format, Qt::ImageConvers // Note: If no extension is provided, we try to match the // file against known plugin extensions if (info.completeSuffix().isEmpty() || info.exists()) { + const bool inGuiThread = qApp->thread() == QThread::currentThread(); QString key = QLatin1String("qt_pixmap") % info.absoluteFilePath() @@ -736,13 +737,14 @@ bool QPixmap::load(const QString &fileName, const char *format, Qt::ImageConvers % HexString<quint64>(info.size()) % HexString<uint>(data ? data->pixelType() : QPlatformPixmap::PixmapType); - if (QPixmapCache::find(key, this)) + if (inGuiThread && QPixmapCache::find(key, this)) return true; data = QPlatformPixmap::create(0, 0, data ? data->pixelType() : QPlatformPixmap::PixmapType); if (data->fromFile(fileName, format, flags)) { - QPixmapCache::insert(key, *this); + if (inGuiThread) + QPixmapCache::insert(key, *this); return true; } } @@ -856,6 +858,7 @@ bool QPixmap::doImageIO(QImageWriter *writer, int quality) const } +#if QT_DEPRECATED_SINCE(5, 13) /*! \obsolete @@ -876,6 +879,14 @@ void QPixmap::fill(const QPaintDevice *device, const QPoint &p) Use QPainter or the fill(QColor) overload instead. */ +void QPixmap::fill(const QPaintDevice *device, int xofs, int yofs) +{ + Q_UNUSED(device) + Q_UNUSED(xofs) + Q_UNUSED(yofs) + qWarning("this function is deprecated, ignored"); +} +#endif /*! @@ -959,6 +970,7 @@ static void sendResizeEvents(QWidget *target) } #endif +#if QT_DEPRECATED_SINCE(5, 13) /*! \obsolete @@ -982,6 +994,14 @@ QPixmap QPixmap::grabWidget(QObject *widget, const QRect &rectangle) Use QWidget::grab() instead. */ +QPixmap QPixmap::grabWidget(QObject *widget, int x, int y, int w, int h) +{ +QT_WARNING_PUSH +QT_WARNING_DISABLE_DEPRECATED + return grabWidget(widget, QRect(x, y, w, h)); +QT_WARNING_POP +} +#endif /***************************************************************************** QPixmap stream functions @@ -1356,12 +1376,6 @@ QPixmap QPixmap::transformed(const QMatrix &matrix, Qt::TransformationMode mode) The cacheKey() function returns a number that uniquely identifies the contents of the QPixmap object. - The x11Info() function returns information about the configuration - of the X display used by the screen to which the pixmap currently - belongs. The x11PictureHandle() function returns the X11 Picture - handle of the pixmap for XRender support. Note that the two latter - functions are only available on x11. - \endtable \section1 Pixmap Conversion @@ -1392,9 +1406,6 @@ QPixmap QPixmap::transformed(const QMatrix &matrix, Qt::TransformationMode mode) function returns the actual matrix used for transforming the pixmap. - \note When using the native X11 graphics system, the pixmap - becomes invalid when the QApplication instance is destroyed. - \sa QBitmap, QImage, QImageReader, QImageWriter */ @@ -1541,6 +1552,11 @@ QPixmap QPixmap::fromImage(const QImage &image, Qt::ImageConversionFlags flags) if (image.isNull()) return QPixmap(); + if (Q_UNLIKELY(!qobject_cast<QGuiApplication *>(QCoreApplication::instance()))) { + qWarning("QPixmap::fromImage: QPixmap cannot be created without a QGuiApplication"); + return QPixmap(); + } + QScopedPointer<QPlatformPixmap> data(QGuiApplicationPrivate::platformIntegration()->createPlatformPixmap(QPlatformPixmap::PixmapType)); data->fromImage(image, flags); return QPixmap(data.take()); @@ -1563,6 +1579,11 @@ QPixmap QPixmap::fromImageInPlace(QImage &image, Qt::ImageConversionFlags flags) if (image.isNull()) return QPixmap(); + if (Q_UNLIKELY(!qobject_cast<QGuiApplication *>(QCoreApplication::instance()))) { + qWarning("QPixmap::fromImageInPlace: QPixmap cannot be created without a QGuiApplication"); + return QPixmap(); + } + QScopedPointer<QPlatformPixmap> data(QGuiApplicationPrivate::platformIntegration()->createPlatformPixmap(QPlatformPixmap::PixmapType)); data->fromImageInPlace(image, flags); return QPixmap(data.take()); @@ -1582,11 +1603,17 @@ QPixmap QPixmap::fromImageInPlace(QImage &image, Qt::ImageConversionFlags flags) */ QPixmap QPixmap::fromImageReader(QImageReader *imageReader, Qt::ImageConversionFlags flags) { + if (Q_UNLIKELY(!qobject_cast<QGuiApplication *>(QCoreApplication::instance()))) { + qWarning("QPixmap::fromImageReader: QPixmap cannot be created without a QGuiApplication"); + return QPixmap(); + } + QScopedPointer<QPlatformPixmap> data(QGuiApplicationPrivate::platformIntegration()->createPlatformPixmap(QPlatformPixmap::PixmapType)); data->fromImageReader(imageReader, flags); return QPixmap(data.take()); } +#if QT_DEPRECATED_SINCE(5, 13) /*! \fn QPixmap QPixmap::grabWindow(WId window, int x, int y, int width, int height) @@ -1640,6 +1667,7 @@ QPixmap QPixmap::grabWindow(WId window, int x, int y, int w, int h) " Defaulting to primary screen."); return QGuiApplication::primaryScreen()->grabWindow(window, x, y, w, h); } +#endif /*! \internal diff --git a/src/gui/image/qpixmap.h b/src/gui/image/qpixmap.h index 55cca7a766..2103fcc58c 100644 --- a/src/gui/image/qpixmap.h +++ b/src/gui/image/qpixmap.h @@ -74,10 +74,10 @@ public: QPixmap &operator=(const QPixmap &); #ifdef Q_COMPILER_RVALUE_REFS - inline QPixmap &operator=(QPixmap &&other) Q_DECL_NOEXCEPT + inline QPixmap &operator=(QPixmap &&other) noexcept { qSwap(data, other.data); return *this; } #endif - inline void swap(QPixmap &other) Q_DECL_NOEXCEPT + inline void swap(QPixmap &other) noexcept { qSwap(data, other.data); } operator QVariant() const; @@ -94,8 +94,12 @@ public: static int defaultDepth(); void fill(const QColor &fillColor = Qt::white); +#if QT_DEPRECATED_SINCE(5, 13) + QT_DEPRECATED_X("Use QPainter or fill(QColor)") void fill(const QPaintDevice *device, const QPoint &ofs); - inline void fill(const QPaintDevice *device, int xofs, int yofs) { fill(device, QPoint(xofs, yofs)); } + QT_DEPRECATED_X("Use QPainter or fill(QColor)") + void fill(const QPaintDevice *device, int xofs, int yofs); +#endif QBitmap mask() const; void setMask(const QBitmap &); @@ -111,10 +115,14 @@ public: #endif QBitmap createMaskFromColor(const QColor &maskColor, Qt::MaskMode mode = Qt::MaskInColor) const; - static QPixmap grabWindow(WId, int x=0, int y=0, int w=-1, int h=-1); +#if QT_DEPRECATED_SINCE(5, 13) + QT_DEPRECATED_X("Use QScreen::grabWindow() instead") + static QPixmap grabWindow(WId, int x = 0, int y = 0, int w = -1, int h = -1); + QT_DEPRECATED_X("Use QWidget::grab() instead") static QPixmap grabWidget(QObject *widget, const QRect &rect); - static inline QPixmap grabWidget(QObject *widget, int x=0, int y=0, int w=-1, int h=-1) - { return grabWidget(widget, QRect(x, y, w, h)); } + QT_DEPRECATED_X("Use QWidget::grab() instead") + static QPixmap grabWidget(QObject *widget, int x = 0, int y = 0, int w = -1, int h = -1); +#endif inline QPixmap scaled(int w, int h, Qt::AspectRatioMode aspectMode = Qt::IgnoreAspectRatio, Qt::TransformationMode mode = Qt::FastTransformation) const diff --git a/src/gui/image/qpixmap_raster.cpp b/src/gui/image/qpixmap_raster.cpp index 13c1c29d5b..2732bbd197 100644 --- a/src/gui/image/qpixmap_raster.cpp +++ b/src/gui/image/qpixmap_raster.cpp @@ -203,6 +203,9 @@ void QRasterPlatformPixmap::fill(const QColor &color) pixel = qAlpha(color.rgba()); } else if (image.format() == QImage::Format_Grayscale8) { pixel = qGray(color.rgba()); + } else if (image.format() == QImage::Format_Grayscale16) { + QRgba64 c = color.rgba64(); + pixel = qGray(c.red(), c.green(), c.blue()); } else { pixel = 0; diff --git a/src/gui/image/qpixmap_win.cpp b/src/gui/image/qpixmap_win.cpp index b8d13ac092..3a2db74098 100644 --- a/src/gui/image/qpixmap_win.cpp +++ b/src/gui/image/qpixmap_win.cpp @@ -210,7 +210,7 @@ static QImage copyImageData(const BITMAPINFOHEADER &header, const RGBQUAD *color class DisplayHdc { - Q_DISABLE_COPY(DisplayHdc) + Q_DISABLE_COPY_MOVE(DisplayHdc) public: DisplayHdc() : m_displayDc(GetDC(nullptr)) {} ~DisplayHdc() { ReleaseDC(nullptr, m_displayDc); } @@ -422,8 +422,11 @@ static QImage imageFromWinHBITMAP_GetDiBits(HBITMAP bitmap, bool forceQuads, int if (info.bmiHeader.biHeight > 0) // Force top-down info.bmiHeader.biHeight = -info.bmiHeader.biHeight; info.bmiHeader.biCompression = BI_RGB; // Extract using no compression (can be BI_BITFIELD) - if (forceQuads) + size_t allocSize = info.bmiHeader.biSizeImage; + if (forceQuads) { info.bmiHeader.biBitCount = 32; + allocSize = info.bmiHeader.biWidth * qAbs(info.bmiHeader.biHeight) * 4; + } const QImage::Format imageFormat = imageFromWinHBITMAP_Format(info.bmiHeader, hbitmapFormat); if (imageFormat == QImage::Format_Invalid) { @@ -431,7 +434,7 @@ static QImage imageFromWinHBITMAP_GetDiBits(HBITMAP bitmap, bool forceQuads, int return QImage(); } - QScopedPointer<uchar> data(new uchar[info.bmiHeader.biSizeImage]); + QScopedArrayPointer<uchar> data(new uchar[allocSize]); if (!GetDIBits(displayDc, bitmap, 0, qAbs(info.bmiHeader.biHeight), data.data(), &info, DIB_RGB_COLORS)) { qErrnoWarning("%s: GetDIBits() failed to get data.", __FUNCTION__); return QImage(); diff --git a/src/gui/image/qpixmapcache.cpp b/src/gui/image/qpixmapcache.cpp index 3d1652f68b..a41ec8f35c 100644 --- a/src/gui/image/qpixmapcache.cpp +++ b/src/gui/image/qpixmapcache.cpp @@ -176,7 +176,7 @@ bool QPixmapCache::Key::operator ==(const Key &key) const Otherwise, if pixmap was flushed, the key is no longer valid. \since 5.7 */ -bool QPixmapCache::Key::isValid() const Q_DECL_NOTHROW +bool QPixmapCache::Key::isValid() const noexcept { return d && d->isValid; } @@ -469,10 +469,13 @@ QPixmapCacheEntry::~QPixmapCacheEntry() pm_cache()->releaseKey(key); } +#if QT_DEPRECATED_SINCE(5, 13) /*! \obsolete \overload + Use bool find(const QString &, QPixmap *) instead. + Returns the pixmap associated with the \a key in the cache, or null if there is no such pixmap. @@ -494,13 +497,14 @@ QPixmap *QPixmapCache::find(const QString &key) /*! \obsolete - Use bool find(const QString&, QPixmap*) instead. + Use bool find(const QString &, QPixmap *) instead. */ -bool QPixmapCache::find(const QString &key, QPixmap& pixmap) +bool QPixmapCache::find(const QString &key, QPixmap &pixmap) { return find(key, &pixmap); } +#endif /*! Looks for a cached pixmap associated with the given \a key in the cache. @@ -513,7 +517,7 @@ bool QPixmapCache::find(const QString &key, QPixmap& pixmap) \snippet code/src_gui_image_qpixmapcache.cpp 1 */ -bool QPixmapCache::find(const QString &key, QPixmap* pixmap) +bool QPixmapCache::find(const QString &key, QPixmap *pixmap) { QPixmap *ptr = pm_cache()->object(key); if (ptr && pixmap) @@ -530,7 +534,7 @@ bool QPixmapCache::find(const QString &key, QPixmap* pixmap) \since 4.6 */ -bool QPixmapCache::find(const Key &key, QPixmap* pixmap) +bool QPixmapCache::find(const Key &key, QPixmap *pixmap) { //The key is not valid anymore, a flush happened before probably if (!key.d || !key.d->isValid) diff --git a/src/gui/image/qpixmapcache.h b/src/gui/image/qpixmapcache.h index ea10ab1b76..c5bedb27ab 100644 --- a/src/gui/image/qpixmapcache.h +++ b/src/gui/image/qpixmapcache.h @@ -56,8 +56,8 @@ public: Key(); Key(const Key &other); #ifdef Q_COMPILER_RVALUE_REFS - Key(Key &&other) Q_DECL_NOTHROW : d(other.d) { other.d = nullptr; } - Key &operator =(Key &&other) Q_DECL_NOTHROW { swap(other); return *this; } + Key(Key &&other) noexcept : d(other.d) { other.d = nullptr; } + Key &operator =(Key &&other) noexcept { swap(other); return *this; } #endif ~Key(); bool operator ==(const Key &key) const; @@ -65,8 +65,8 @@ public: { return !operator==(key); } Key &operator =(const Key &other); - void swap(Key &other) Q_DECL_NOTHROW { qSwap(d, other.d); } - bool isValid() const Q_DECL_NOTHROW; + void swap(Key &other) noexcept { qSwap(d, other.d); } + bool isValid() const noexcept; private: KeyData *d; @@ -76,8 +76,12 @@ public: static int cacheLimit(); static void setCacheLimit(int); +#if QT_DEPRECATED_SINCE(5, 13) + QT_DEPRECATED_X("Use bool find(const QString &, QPixmap *) instead") static QPixmap *find(const QString &key); + QT_DEPRECATED_X("Use bool find(const QString &, QPixmap *) instead") static bool find(const QString &key, QPixmap &pixmap); +#endif static bool find(const QString &key, QPixmap *pixmap); static bool find(const Key &key, QPixmap *pixmap); static bool insert(const QString &key, const QPixmap &pixmap); diff --git a/src/gui/image/qpixmapcache_p.h b/src/gui/image/qpixmapcache_p.h index 3c57367514..ab8e2b7558 100644 --- a/src/gui/image/qpixmapcache_p.h +++ b/src/gui/image/qpixmapcache_p.h @@ -87,7 +87,7 @@ public: && !d->image.d->paintEngine->isActive()) { delete d->image.d->paintEngine; - d->image.d->paintEngine = 0; + d->image.d->paintEngine = nullptr; } } } diff --git a/src/gui/image/qplatformpixmap.cpp b/src/gui/image/qplatformpixmap.cpp index 2209c3de4d..a2e01147c4 100644 --- a/src/gui/image/qplatformpixmap.cpp +++ b/src/gui/image/qplatformpixmap.cpp @@ -178,6 +178,7 @@ QBitmap QPlatformPixmap::mask() const if (mask.isNull()) // allocation failed return QBitmap(); + mask.setDevicePixelRatio(devicePixelRatio()); mask.setColorCount(2); mask.setColor(0, QColor(Qt::color0).rgba()); mask.setColor(1, QColor(Qt::color1).rgba()); diff --git a/src/gui/image/qpnghandler.cpp b/src/gui/image/qpnghandler.cpp index 242d8cd63b..39d0807606 100644 --- a/src/gui/image/qpnghandler.cpp +++ b/src/gui/image/qpnghandler.cpp @@ -42,15 +42,19 @@ #ifndef QT_NO_IMAGEFORMAT_PNG #include <qcoreapplication.h> +#include <qdebug.h> #include <qiodevice.h> #include <qimage.h> #include <qlist.h> -#include <qtextcodec.h> #include <qvariant.h> #include <qvector.h> #include <private/qimage_p.h> // for qt_getImageText +#include <qcolorspace.h> +#include <private/qcolorspace_p.h> +#include <private/qicc_p.h> + #include <png.h> #include <pngconf.h> @@ -97,17 +101,27 @@ public: ReadingEnd, Error }; + // Defines the order of how the various ways of setting colorspace overrides eachother: + enum ColorSpaceState { + Undefined = 0, + GammaChrm = 1, // gAMA+cHRM chunks + Srgb = 2, // sRGB chunk + Icc = 3 // iCCP chunk + }; QPngHandlerPrivate(QPngHandler *qq) - : gamma(0.0), fileGamma(0.0), quality(2), png_ptr(0), info_ptr(0), end_info(0), state(Ready), q(qq) + : gamma(0.0), fileGamma(0.0), quality(50), compression(50), colorSpaceState(Undefined), png_ptr(0), info_ptr(0), end_info(0), state(Ready), q(qq) { } float gamma; float fileGamma; - int quality; + int quality; // quality is used for backward compatibility, maps to compression + int compression; QString description; QSize scaledSize; QStringList readTexts; + QColorSpace colorSpace; + ColorSpaceState colorSpaceState; png_struct *png_ptr; png_info *info_ptr; @@ -161,11 +175,11 @@ public: void setGamma(float); bool writeImage(const QImage& img, int x, int y); - bool writeImage(const QImage& img, volatile int quality, const QString &description, int x, int y); + bool writeImage(const QImage& img, volatile int compression_in, 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); } + bool writeImage(const QImage& img, int compression, const QString &description) + { return writeImage(img, compression, description, 0, 0); } QIODevice* device() { return dev; } @@ -226,21 +240,18 @@ void qpiw_flush_fn(png_structp /* png_ptr */) } static -void setup_qt(QImage& image, png_structp png_ptr, png_infop info_ptr, QSize scaledSize, bool *doScaledRead, float screen_gamma=0.0, float file_gamma=0.0) +void setup_qt(QImage& image, png_structp png_ptr, png_infop info_ptr, QSize scaledSize, bool *doScaledRead) { - if (screen_gamma != 0.0 && file_gamma != 0.0) - png_set_gamma(png_ptr, 1.0f / screen_gamma, file_gamma); - - png_uint_32 width; - png_uint_32 height; - int bit_depth; - int color_type; + png_uint_32 width = 0; + png_uint_32 height = 0; + int bit_depth = 0; + int color_type = 0; png_bytep trans_alpha = 0; png_color_16p trans_color_p = 0; int num_trans; png_colorp palette = 0; int num_palette; - int interlace_method; + int interlace_method = PNG_INTERLACE_LAST; png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_method, 0, 0); png_set_interlace_handling(png_ptr); @@ -266,6 +277,18 @@ void setup_qt(QImage& image, png_structp png_ptr, png_infop info_ptr, QSize scal else if (g == 1) image.setColor(0, qRgba(255, 255, 255, 0)); } + } else if (bit_depth == 16 + && png_get_channels(png_ptr, info_ptr) == 1 + && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { + if (image.size() != QSize(width, height) || image.format() != QImage::Format_Grayscale16) { + image = QImage(width, height, QImage::Format_Grayscale16); + if (image.isNull()) + return; + } + + png_read_update_info(png_ptr, info_ptr); + if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) + png_set_swap(png_ptr); } else if (bit_depth == 16) { bool hasMask = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS); if (!hasMask) @@ -573,10 +596,45 @@ bool QPngHandlerPrivate::readPngHeader() readPngTexts(info_ptr); +#ifdef PNG_iCCP_SUPPORTED + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_iCCP)) { + png_charp name = nullptr; + int compressionType = 0; +#if (PNG_LIBPNG_VER < 10500) + png_charp profileData = nullptr; +#else + png_bytep profileData = nullptr; +#endif + png_uint_32 profLen; + png_get_iCCP(png_ptr, info_ptr, &name, &compressionType, &profileData, &profLen); + if (!QIcc::fromIccProfile(QByteArray::fromRawData((const char *)profileData, profLen), &colorSpace)) { + qWarning() << "QPngHandler: Failed to parse ICC profile"; + } else { + colorSpaceState = Icc; + } + } +#endif + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_sRGB)) { + int rendering_intent = -1; + png_get_sRGB(png_ptr, info_ptr, &rendering_intent); + // We don't actually care about the rendering_intent, just that it is valid + if (rendering_intent >= 0 && rendering_intent <= 3 && colorSpaceState <= Srgb) { + colorSpace = QColorSpace::SRgb; + colorSpaceState = Srgb; + } + } if (png_get_valid(png_ptr, info_ptr, PNG_INFO_gAMA)) { double file_gamma = 0.0; png_get_gAMA(png_ptr, info_ptr, &file_gamma); fileGamma = file_gamma; + if (fileGamma > 0.0f && colorSpaceState <= GammaChrm) { + QColorSpacePrivate *csPrivate = colorSpace.d_func(); + csPrivate->gamut = QColorSpace::Gamut::SRgb; + csPrivate->transferFunction = QColorSpace::TransferFunction::Gamma; + csPrivate->gamma = fileGamma; + csPrivate->initialize(); + colorSpaceState = GammaChrm; + } } state = ReadHeader; @@ -601,8 +659,19 @@ bool QPngHandlerPrivate::readPngImage(QImage *outImage) return false; } + if (gamma != 0.0 && fileGamma != 0.0) { + // This configuration forces gamma correction and + // thus changes the output colorspace + png_set_gamma(png_ptr, 1.0f / gamma, fileGamma); + QColorSpacePrivate *csPrivate = colorSpace.d_func(); + csPrivate->transferFunction = QColorSpace::TransferFunction::Gamma; + csPrivate->gamma = gamma; + csPrivate->initialize(); + colorSpaceState = GammaChrm; + } + bool doScaledRead = false; - setup_qt(*outImage, png_ptr, info_ptr, scaledSize, &doScaledRead, gamma, fileGamma); + setup_qt(*outImage, png_ptr, info_ptr, scaledSize, &doScaledRead); if (outImage->isNull()) { png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); @@ -671,14 +740,17 @@ bool QPngHandlerPrivate::readPngImage(QImage *outImage) if (scaledSize.isValid() && outImage->size() != scaledSize) *outImage = outImage->scaled(scaledSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + if (colorSpaceState > Undefined && colorSpace.isValid()) + outImage->setColorSpace(colorSpace); + return true; } QImage::Format QPngHandlerPrivate::readImageFormat() { QImage::Format format = QImage::Format_Invalid; - png_uint_32 width, height; - int bit_depth, color_type; + png_uint_32 width = 0, height = 0; + int bit_depth = 0, color_type = 0; png_colorp palette; int num_palette; png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, 0, 0, 0); @@ -687,7 +759,7 @@ QImage::Format QPngHandlerPrivate::readImageFormat() if (bit_depth == 1 && png_get_channels(png_ptr, info_ptr) == 1) { format = QImage::Format_Mono; } else if (bit_depth == 16) { - format = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) ? QImage::Format_RGBA64 : QImage::Format_RGBX64; + format = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) ? QImage::Format_RGBA64 : QImage::Format_Grayscale16; } else if (bit_depth == 8 && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { format = QImage::Format_Grayscale8; } else { @@ -814,7 +886,7 @@ bool QPNGImageWriter::writeImage(const QImage& image, int off_x, int off_y) return writeImage(image, -1, QString(), off_x, off_y); } -bool QPNGImageWriter::writeImage(const QImage& image, volatile int quality_in, const QString &description, +bool QPNGImageWriter::writeImage(const QImage& image, volatile int compression_in, const QString &description, int off_x_in, int off_y_in) { QPoint offset = image.offset(); @@ -842,13 +914,13 @@ bool QPNGImageWriter::writeImage(const QImage& image, volatile int quality_in, c return false; } - int quality = quality_in; - if (quality >= 0) { - if (quality > 9) { - qWarning("PNG: Quality %d out of range", quality); - quality = 9; + int compression = compression_in; + if (compression >= 0) { + if (compression > 9) { + qWarning("PNG: Compression %d out of range", compression); + compression = 9; } - png_set_compression_level(png_ptr, quality); + png_set_compression_level(png_ptr, compression); } png_set_write_fn(png_ptr, (void*)this, qpiw_write_fn, qpiw_flush_fn); @@ -861,7 +933,8 @@ bool QPNGImageWriter::writeImage(const QImage& image, volatile int quality_in, c else color_type = PNG_COLOR_TYPE_PALETTE; } - else if (image.format() == QImage::Format_Grayscale8) + else if (image.format() == QImage::Format_Grayscale8 + || image.format() == QImage::Format_Grayscale16) color_type = PNG_COLOR_TYPE_GRAY; else if (image.hasAlphaChannel()) color_type = PNG_COLOR_TYPE_RGB_ALPHA; @@ -877,6 +950,7 @@ bool QPNGImageWriter::writeImage(const QImage& image, volatile int quality_in, c case QImage::Format_RGBX64: case QImage::Format_RGBA64: case QImage::Format_RGBA64_Premultiplied: + case QImage::Format_Grayscale16: bpc = 16; break; default: @@ -988,6 +1062,7 @@ bool QPNGImageWriter::writeImage(const QImage& image, volatile int quality_in, c case QImage::Format_RGBX64: case QImage::Format_RGBA64: case QImage::Format_RGBA64_Premultiplied: + case QImage::Format_Grayscale16: png_set_swap(png_ptr); break; default: @@ -1018,6 +1093,7 @@ bool QPNGImageWriter::writeImage(const QImage& image, volatile int quality_in, c case QImage::Format_MonoLSB: case QImage::Format_Indexed8: case QImage::Format_Grayscale8: + case QImage::Format_Grayscale16: case QImage::Format_RGB32: case QImage::Format_ARGB32: case QImage::Format_RGB888: @@ -1067,15 +1143,21 @@ bool QPNGImageWriter::writeImage(const QImage& image, volatile int quality_in, c } static bool write_png_image(const QImage &image, QIODevice *device, - int quality, float gamma, const QString &description) + int compression, int quality, float gamma, const QString &description) { + // quality is used for backward compatibility, maps to compression + QPNGImageWriter writer(device); - if (quality >= 0) { - quality = qMin(quality, 100); - quality = (100-quality) * 9 / 91; // map [0,100] -> [9,0] - } + if (compression >= 0) + compression = qMin(compression, 100); + else if (quality >= 0) + compression = 100 - qMin(quality, 100); + + if (compression >= 0) + compression = (compression * 9) / 91; // map [0,100] -> [0,9] + writer.setGamma(gamma); - return writer.writeImage(image, quality, description); + return writer.writeImage(image, compression, description); } QPngHandler::QPngHandler() @@ -1122,7 +1204,7 @@ bool QPngHandler::read(QImage *image) bool QPngHandler::write(const QImage &image) { - return write_png_image(image, device(), d->quality, d->gamma, d->description); + return write_png_image(image, device(), d->compression, d->quality, d->gamma, d->description); } bool QPngHandler::supportsOption(ImageOption option) const @@ -1131,6 +1213,7 @@ bool QPngHandler::supportsOption(ImageOption option) const || option == Description || option == ImageFormat || option == Quality + || option == CompressionRatio || option == Size || option == ScaledSize; } @@ -1146,6 +1229,8 @@ QVariant QPngHandler::option(ImageOption option) const return d->gamma == 0.0 ? d->fileGamma : d->gamma; else if (option == Quality) return d->quality; + else if (option == CompressionRatio) + return d->compression; else if (option == Description) return d->description; else if (option == Size) @@ -1164,16 +1249,20 @@ void QPngHandler::setOption(ImageOption option, const QVariant &value) d->gamma = value.toFloat(); else if (option == Quality) d->quality = value.toInt(); + else if (option == CompressionRatio) + d->compression = value.toInt(); else if (option == Description) d->description = value.toString(); else if (option == ScaledSize) d->scaledSize = value.toSize(); } +#if QT_DEPRECATED_SINCE(5, 13) QByteArray QPngHandler::name() const { return "png"; } +#endif QT_END_NAMESPACE diff --git a/src/gui/image/qpnghandler_p.h b/src/gui/image/qpnghandler_p.h index 4ca716e7c2..5d4da97395 100644 --- a/src/gui/image/qpnghandler_p.h +++ b/src/gui/image/qpnghandler_p.h @@ -69,7 +69,9 @@ public: bool read(QImage *image) override; bool write(const QImage &image) override; +#if QT_DEPRECATED_SINCE(5, 13) QByteArray name() const override; +#endif QVariant option(ImageOption option) const override; void setOption(ImageOption option, const QVariant &value) override; diff --git a/src/gui/image/qppmhandler.cpp b/src/gui/image/qppmhandler.cpp index bfde0aa76e..13ee2eadd2 100644 --- a/src/gui/image/qppmhandler.cpp +++ b/src/gui/image/qppmhandler.cpp @@ -576,10 +576,12 @@ void QPpmHandler::setOption(ImageOption option, const QVariant &value) subType = value.toByteArray().toLower(); } +#if QT_DEPRECATED_SINCE(5, 13) QByteArray QPpmHandler::name() const { return subType.isEmpty() ? QByteArray("ppm") : subType; } +#endif QT_END_NAMESPACE diff --git a/src/gui/image/qppmhandler_p.h b/src/gui/image/qppmhandler_p.h index 1c6fbd6869..2f3811b759 100644 --- a/src/gui/image/qppmhandler_p.h +++ b/src/gui/image/qppmhandler_p.h @@ -67,9 +67,11 @@ public: bool read(QImage *image) override; bool write(const QImage &image) override; +#if QT_DEPRECATED_SINCE(5, 13) QByteArray name() const override; +#endif - static bool canRead(QIODevice *device, QByteArray *subType = 0); + static bool canRead(QIODevice *device, QByteArray *subType = nullptr); QVariant option(ImageOption option) const override; void setOption(ImageOption option, const QVariant &value) override; diff --git a/src/gui/image/qxbmhandler.cpp b/src/gui/image/qxbmhandler.cpp index 24d86e116d..65a5b63bc7 100644 --- a/src/gui/image/qxbmhandler.cpp +++ b/src/gui/image/qxbmhandler.cpp @@ -97,6 +97,8 @@ static bool read_xbm_header(QIODevice *device, int& w, int& h) if (r1.indexIn(sbuf) == 0 && r2.indexIn(sbuf, r1.matchedLength()) == r1.matchedLength()) w = QByteArray(&buf[r1.matchedLength()]).trimmed().toInt(); + else + return false; // "#define .._height <num>" readBytes = device->readLine(buf, buflen); @@ -109,6 +111,8 @@ static bool read_xbm_header(QIODevice *device, int& w, int& h) if (r1.indexIn(sbuf) == 0 && r2.indexIn(sbuf, r1.matchedLength()) == r1.matchedLength()) h = QByteArray(&buf[r1.matchedLength()]).trimmed().toInt(); + else + return false; // format error if (w <= 0 || w > 32767 || h <= 0 || h > 32767) @@ -352,10 +356,12 @@ void QXbmHandler::setOption(ImageOption option, const QVariant &value) fileName = value.toString(); } +#if QT_DEPRECATED_SINCE(5, 13) QByteArray QXbmHandler::name() const { return "xbm"; } +#endif QT_END_NAMESPACE diff --git a/src/gui/image/qxbmhandler_p.h b/src/gui/image/qxbmhandler_p.h index 26439af527..ae590a1944 100644 --- a/src/gui/image/qxbmhandler_p.h +++ b/src/gui/image/qxbmhandler_p.h @@ -66,7 +66,9 @@ public: bool read(QImage *image) override; bool write(const QImage &image) override; +#if QT_DEPRECATED_SINCE(5, 13) QByteArray name() const override; +#endif static bool canRead(QIODevice *device); diff --git a/src/gui/image/qxpmhandler.cpp b/src/gui/image/qxpmhandler.cpp index 17272ffe69..a32dfda96d 100644 --- a/src/gui/image/qxpmhandler.cpp +++ b/src/gui/image/qxpmhandler.cpp @@ -1287,10 +1287,12 @@ void QXpmHandler::setOption(ImageOption option, const QVariant &value) fileName = value.toString(); } +#if QT_DEPRECATED_SINCE(5, 13) QByteArray QXpmHandler::name() const { return "xpm"; } +#endif QT_END_NAMESPACE diff --git a/src/gui/image/qxpmhandler_p.h b/src/gui/image/qxpmhandler_p.h index f118bf2309..a4dd88cd17 100644 --- a/src/gui/image/qxpmhandler_p.h +++ b/src/gui/image/qxpmhandler_p.h @@ -68,7 +68,9 @@ public: static bool canRead(QIODevice *device); +#if QT_DEPRECATED_SINCE(5, 13) QByteArray name() const override; +#endif QVariant option(ImageOption option) const override; void setOption(ImageOption option, const QVariant &value) override; |