summaryrefslogtreecommitdiffstats
path: root/src/gui/image/qimage.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/image/qimage.cpp')
-rw-r--r--src/gui/image/qimage.cpp1531
1 files changed, 1296 insertions, 235 deletions
diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp
index d05a72acc6..3bbf21320e 100644
--- a/src/gui/image/qimage.cpp
+++ b/src/gui/image/qimage.cpp
@@ -1,51 +1,17 @@
-/****************************************************************************
-**
-** 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.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qimage.h"
#include "qbuffer.h"
#include "qdatastream.h"
#include "qcolortransform.h"
+#include "qfloat16.h"
#include "qmap.h"
#include "qtransform.h"
#include "qimagereader.h"
#include "qimagewriter.h"
+#include "qrgbafloat.h"
#include "qstringlist.h"
#include "qvariant.h"
#include "qimagepixmapcleanuphooks_p.h"
@@ -55,6 +21,7 @@
#include <stdlib.h>
#include <limits.h>
#include <qpa/qplatformpixmap.h>
+#include <private/qcolorspace_p.h>
#include <private/qcolortransform_p.h>
#include <private/qmemrotate_p.h>
#include <private/qimagescale_p.h>
@@ -69,18 +36,24 @@
#include <private/qfont_p.h>
#if QT_CONFIG(thread)
-#include "qsemaphore.h"
-#include "qthreadpool.h"
+#include <qsemaphore.h>
+#include <qthreadpool.h>
+#include <private/qthreadpool_p.h>
#endif
#include <qtgui_tracepoints_p.h>
+#include <memory>
+
QT_BEGIN_NAMESPACE
+class QCmyk32;
-static inline bool isLocked(QImageData *data)
-{
- return data != nullptr && data->is_locked;
-}
+using namespace Qt::StringLiterals;
+
+// MSVC 19.28 does show spurious warning "C4723: potential divide by 0" for code that divides
+// by height() in release builds. Anyhow, all the code paths in this file are only executed
+// for valid QImage's, where height() cannot be 0. Therefore disable the warning.
+QT_WARNING_DISABLE_MSVC(4723)
#if defined(Q_CC_DEC) && defined(__alpha) && (__DECCXX_VER-0 >= 50190001)
#pragma message disable narrowptr
@@ -93,6 +66,17 @@ static inline bool isLocked(QImageData *data)
return QImage(); \
}
+Q_TRACE_PREFIX(qtgui,
+ "#include <qimagereader.h>"
+);
+
+Q_TRACE_METADATA(qtgui,
+"ENUM { } QImage::Format;" \
+"FLAGS { } Qt::ImageConversionFlags;"
+);
+
+Q_TRACE_PARAM_REPLACE(Qt::AspectRatioMode, int);
+Q_TRACE_PARAM_REPLACE(Qt::TransformationMode, int);
static QImage rotated90(const QImage &src);
static QImage rotated180(const QImage &src);
@@ -100,7 +84,7 @@ static QImage rotated270(const QImage &src);
static int next_qimage_serial_number()
{
- static QBasicAtomicInt serial = Q_BASIC_ATOMIC_INITIALIZER(0);
+ Q_CONSTINIT static QBasicAtomicInt serial = Q_BASIC_ATOMIC_INITIALIZER(0);
return 1 + serial.fetchAndAddRelaxed(1);
}
@@ -112,7 +96,7 @@ QImageData::QImageData()
dpmx(qt_defaultDpiX() * 100 / qreal(2.54)),
dpmy(qt_defaultDpiY() * 100 / qreal(2.54)),
offset(0, 0), own_data(true), ro_data(false), has_alpha_clut(false),
- is_cached(false), is_locked(false), cleanupFunction(nullptr), cleanupInfo(nullptr),
+ is_cached(false), cleanupFunction(nullptr), cleanupInfo(nullptr),
paintEngine(nullptr)
{
}
@@ -124,9 +108,9 @@ QImageData::QImageData()
Creates a new image data.
Returns \nullptr if invalid parameters are give or anything else failed.
*/
-QImageData * QImageData::create(const QSize &size, QImage::Format format)
+QImageData * Q_TRACE_INSTRUMENT(qtgui) QImageData::create(const QSize &size, QImage::Format format)
{
- if (size.isEmpty() || format == QImage::Format_Invalid)
+ if (size.isEmpty() || format <= QImage::Format_Invalid || format >= QImage::NImageFormats)
return nullptr; // invalid parameter(s)
Q_TRACE_SCOPE(QImageData_create, size, format);
@@ -138,7 +122,7 @@ QImageData * QImageData::create(const QSize &size, QImage::Format format)
if (!params.isValid())
return nullptr;
- QScopedPointer<QImageData> d(new QImageData);
+ auto d = std::make_unique<QImageData>();
switch (format) {
case QImage::Format_Mono:
@@ -166,7 +150,7 @@ QImageData * QImageData::create(const QSize &size, QImage::Format format)
return nullptr;
d->ref.ref();
- return d.take();
+ return d.release();
}
QImageData::~QImageData()
@@ -288,6 +272,24 @@ bool QImageData::checkForAlphaPixels() const
bits += bytes_per_line;
}
} break;
+ case QImage::Format_RGBA16FPx4:
+ case QImage::Format_RGBA16FPx4_Premultiplied: {
+ uchar *bits = data;
+ for (int y = 0; y < height && !has_alpha_pixels; ++y) {
+ for (int x = 0; x < width; ++x)
+ has_alpha_pixels |= ((qfloat16 *)bits)[x * 4 + 3] < 1.0f;
+ bits += bytes_per_line;
+ }
+ } break;
+ case QImage::Format_RGBA32FPx4:
+ case QImage::Format_RGBA32FPx4_Premultiplied: {
+ uchar *bits = data;
+ for (int y = 0; y < height && !has_alpha_pixels; ++y) {
+ for (int x = 0; x < width; ++x)
+ has_alpha_pixels |= ((float *)bits)[x * 4 + 3] < 1.0f;
+ bits += bytes_per_line;
+ }
+ } break;
case QImage::Format_RGB32:
case QImage::Format_RGB16:
@@ -302,6 +304,9 @@ bool QImageData::checkForAlphaPixels() const
case QImage::Format_Grayscale8:
case QImage::Format_Grayscale16:
case QImage::Format_RGBX64:
+ case QImage::Format_RGBX16FPx4:
+ case QImage::Format_RGBX32FPx4:
+ case QImage::Format_CMYK8888:
break;
case QImage::Format_Invalid:
case QImage::NImageFormats:
@@ -358,7 +363,7 @@ bool QImageData::checkForAlphaPixels() const
refer to the \l{How to Create Qt Plugins}{Plugin HowTo}.
\warning Painting on a QImage with the format
- QImage::Format_Indexed8 is not supported.
+ QImage::Format_Indexed8 or QImage::Format_CMYK8888 is not supported.
\tableofcontents
@@ -426,7 +431,7 @@ bool QImageData::checkForAlphaPixels() const
The color of a pixel can be retrieved by passing its coordinates
to the pixel() function. The pixel() function returns the color
- as a QRgb value indepedent of the image's format.
+ as a QRgb value independent of the image's format.
In case of monochrome and 8-bit images, the colorCount() and
colorTable() functions provide information about the color
@@ -614,8 +619,8 @@ bool QImageData::checkForAlphaPixels() const
\endtable
- \sa QImageReader, QImageWriter, QPixmap, QSvgRenderer, {Image Composition Example},
- {Image Viewer Example}, {Scribble Example}, {Pixelator Example}
+ \sa QImageReader, QImageWriter, QPixmap, QSvgRenderer,
+ {Image Composition Example}, {Scribble Example}
*/
/*!
@@ -708,30 +713,62 @@ bool QImageData::checkForAlphaPixels() const
The unused bits are always zero.
\value Format_ARGB4444_Premultiplied The image is stored using a
premultiplied 16-bit ARGB format (4-4-4-4).
- \value Format_RGBX8888 The image is stored using a 32-bit byte-ordered RGB(x) format (8-8-8-8).
- This is the same as the Format_RGBA8888 except alpha must always be 255. (added in Qt 5.2)
- \value Format_RGBA8888 The image is stored using a 32-bit byte-ordered RGBA format (8-8-8-8).
+ \value [since 5.2]
+ Format_RGBX8888 The image is stored using a 32-bit byte-ordered RGB(x) format (8-8-8-8).
+ This is the same as the Format_RGBA8888 except alpha must always be 255.
+ \value [since 5.2]
+ Format_RGBA8888 The image is stored using a 32-bit byte-ordered RGBA format (8-8-8-8).
Unlike ARGB32 this is a byte-ordered format, which means the 32bit
encoding differs between big endian and little endian architectures,
being respectively (0xRRGGBBAA) and (0xAABBGGRR). The order of the colors
- is the same on any architecture if read as bytes 0xRR,0xGG,0xBB,0xAA. (added in Qt 5.2)
- \value Format_RGBA8888_Premultiplied The image is stored using a
- premultiplied 32-bit byte-ordered RGBA format (8-8-8-8). (added in Qt 5.2)
- \value Format_BGR30 The image is stored using a 32-bit BGR format (x-10-10-10). (added in Qt 5.4)
- \value Format_A2BGR30_Premultiplied The image is stored using a 32-bit premultiplied ABGR format (2-10-10-10). (added in Qt 5.4)
- \value Format_RGB30 The image is stored using a 32-bit RGB format (x-10-10-10). (added in Qt 5.4)
- \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_RGBA64 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)
- \value Format_RGBA64_Premultiplied The image is stored using a premultiplied 64-bit halfword-ordered
- RGBA format (16-16-16-16). (added in Qt 5.12)
- \value Format_BGR888 The image is stored using a 24-bit BGR format. (added in Qt 5.14)
-
- \note Drawing into a QImage with QImage::Format_Indexed8 is not
+ is the same on any architecture if read as bytes 0xRR,0xGG,0xBB,0xAA.
+ \value [since 5.2]
+ Format_RGBA8888_Premultiplied The image is stored using a
+ premultiplied 32-bit byte-ordered RGBA format (8-8-8-8).
+ \value [since 5.4]
+ Format_BGR30 The image is stored using a 32-bit BGR format (x-10-10-10).
+ \value [since 5.4]
+ Format_A2BGR30_Premultiplied The image is stored using a 32-bit premultiplied ABGR format (2-10-10-10).
+ \value [since 5.4]
+ Format_RGB30 The image is stored using a 32-bit RGB format (x-10-10-10).
+ \value [since 5.4]
+ Format_A2RGB30_Premultiplied The image is stored using a 32-bit premultiplied ARGB format (2-10-10-10).
+ \value [since 5.5]
+ Format_Alpha8 The image is stored using an 8-bit alpha only format.
+ \value [since 5.5]
+ Format_Grayscale8 The image is stored using an 8-bit grayscale format.
+ \value [since 5.13]
+ Format_Grayscale16 The image is stored using an 16-bit grayscale format.
+ \value [since 5.12]
+ 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_RGBA64 except alpha must always be 65535.
+ \value [since 5.12]
+ Format_RGBA64 The image is stored using a 64-bit halfword-ordered RGBA format (16-16-16-16).
+ \value [since 5.12]
+ Format_RGBA64_Premultiplied The image is stored using a premultiplied 64-bit halfword-ordered
+ RGBA format (16-16-16-16).
+ \value [since 5.14]
+ Format_BGR888 The image is stored using a 24-bit BGR format.
+ \value [since 6.2]
+ Format_RGBX16FPx4 The image is stored using a four 16-bit halfword floating point RGBx format (16FP-16FP-16FP-16FP).
+ This is the same as the Format_RGBA16FPx4 except alpha must always be 1.0.
+ \value [since 6.2]
+ Format_RGBA16FPx4 The image is stored using a four 16-bit halfword floating point RGBA format (16FP-16FP-16FP-16FP).
+ \value [since 6.2]
+ Format_RGBA16FPx4_Premultiplied The image is stored using a premultiplied four 16-bit halfword floating point
+ RGBA format (16FP-16FP-16FP-16FP).
+ \value [since 6.2]
+ Format_RGBX32FPx4 The image is stored using a four 32-bit floating point RGBx format (32FP-32FP-32FP-32FP).
+ This is the same as the Format_RGBA32FPx4 except alpha must always be 1.0.
+ \value [since 6.2]
+ Format_RGBA32FPx4 The image is stored using a four 32-bit floating point RGBA format (32FP-32FP-32FP-32FP).
+ \value [since 6.2]
+ Format_RGBA32FPx4_Premultiplied The image is stored using a premultiplied four 32-bit floating point
+ RGBA format (32FP-32FP-32FP-32FP).
+ \value [since 6.8]
+ Format_CMYK8888 The image is stored using a 32-bit byte-ordered CMYK format.
+
+ \note Drawing into a QImage with format QImage::Format_Indexed8 or QImage::Format_CMYK8888 is not
supported.
\note Avoid most rendering directly to most of these formats using QPainter. Rendering
@@ -791,7 +828,7 @@ QImage::QImage(const QSize &size, Format format)
QImageData *QImageData::create(uchar *data, int width, int height, qsizetype bpl, QImage::Format format, bool readOnly, QImageCleanupFunction cleanupFunction, void *cleanupInfo)
{
- if (width <= 0 || height <= 0 || !data || format == QImage::Format_Invalid)
+ if (width <= 0 || height <= 0 || !data || format <= QImage::Format_Invalid || format >= QImage::NImageFormats)
return nullptr;
const int depth = qt_depthForFormat(format);
@@ -807,7 +844,7 @@ QImageData *QImageData::create(uchar *data, int width, int height, qsizetype bp
// recalculate the total with this value
params.bytesPerLine = bpl;
- if (mul_overflow<qsizetype>(bpl, height, &params.totalSize))
+ if (qMulOverflow<qsizetype>(bpl, height, &params.totalSize))
return nullptr;
}
@@ -993,7 +1030,7 @@ QImage::QImage(const char * const xpm[])
if (!xpm)
return;
if (!qt_read_xpm_image_or_array(nullptr, xpm, *this))
- // Issue: Warning because the constructor may be ambigious
+ // Issue: Warning because the constructor may be ambiguous
qWarning("QImage::QImage(), XPM is not supported");
}
#endif // QT_NO_IMAGEFORMAT_XPM
@@ -1010,7 +1047,7 @@ QImage::QImage(const char * const xpm[])
QImage::QImage(const QImage &image)
: QPaintDevice()
{
- if (image.paintingActive() || isLocked(image.d)) {
+ if (image.paintingActive()) {
d = nullptr;
image.copy().swap(*this);
} else {
@@ -1042,7 +1079,7 @@ QImage::~QImage()
QImage &QImage::operator=(const QImage &image)
{
- if (image.paintingActive() || isLocked(image.d)) {
+ if (image.paintingActive()) {
operator=(image.copy());
} else {
if (image.d)
@@ -1104,6 +1141,28 @@ void QImage::detach()
}
+/*!
+ \internal
+
+ A variant for metadata-only detach, which will not detach readonly image data,
+ and only invalidate caches of the image data if asked to.
+
+ \sa detach(), isDetached()
+*/
+void QImage::detachMetadata(bool invalidateCache)
+{
+ if (d) {
+ if (d->is_cached && d->ref.loadRelaxed() == 1)
+ QImagePixmapCleanupHooks::executeImageHooks(cacheKey());
+
+ if (d->ref.loadRelaxed() != 1)
+ *this = copy();
+
+ if (d && invalidateCache)
+ ++d->detach_no;
+ }
+}
+
static void copyPhysicalMetadata(QImageData *dst, const QImageData *src)
{
dst->dpmx = src->dpmx;
@@ -1113,9 +1172,10 @@ static void copyPhysicalMetadata(QImageData *dst, const QImageData *src)
static void copyMetadata(QImageData *dst, const QImageData *src)
{
- // Doesn't copy colortable and alpha_clut, or offset.
+ // Doesn't copy colortable and alpha_clut.
copyPhysicalMetadata(dst, src);
dst->text = src->text;
+ dst->offset = src->offset;
dst->colorSpace = src->colorSpace;
}
@@ -1160,7 +1220,7 @@ static void copyMetadata(QImage *dst, const QImage &src)
\sa QImage()
*/
-QImage QImage::copy(const QRect& r) const
+QImage Q_TRACE_INSTRUMENT(qtgui) QImage::copy(const QRect& r) const
{
Q_TRACE_SCOPE(QImage_copy, r);
if (!d)
@@ -1180,7 +1240,6 @@ QImage QImage::copy(const QRect& r) const
} else
memcpy(image.bits(), bits(), d->nbytes);
image.d->colortable = d->colortable;
- image.d->offset = d->offset;
image.d->has_alpha_clut = d->has_alpha_clut;
copyMetadata(image.d, d);
return image;
@@ -1269,7 +1328,6 @@ QImage QImage::copy(const QRect& r) const
}
copyMetadata(image.d, d);
- image.d->offset = offset();
image.d->has_alpha_clut = d->has_alpha_clut;
return image;
}
@@ -1316,7 +1374,7 @@ int QImage::height() const
Returns the size of the image, i.e. its width() and height().
- \sa {QImage#Image Information}{Image Information}
+ \sa {QImage#Image Information}{Image Information}, deviceIndependentSize()
*/
QSize QImage::size() const
{
@@ -1385,7 +1443,7 @@ void QImage::setColorTable(const QList<QRgb> &colors)
{
if (!d)
return;
- detach();
+ detachMetadata(true);
// In case detach() ran out of memory
if (!d)
@@ -1449,7 +1507,7 @@ qreal QImage::devicePixelRatio() const
high-DPI image rather than a large image
(see \l{Drawing High Resolution Versions of Pixmaps and Images}).
- \sa devicePixelRatio()
+ \sa devicePixelRatio(), deviceIndependentSize()
*/
void QImage::setDevicePixelRatio(qreal scaleFactor)
{
@@ -1459,12 +1517,30 @@ void QImage::setDevicePixelRatio(qreal scaleFactor)
if (scaleFactor == d->devicePixelRatio)
return;
- detach();
+ detachMetadata();
if (d)
d->devicePixelRatio = scaleFactor;
}
/*!
+ Returns the size of the image in device independent pixels.
+
+ This value should be used when using the image size in user interface
+ size calculations.
+
+ The return value is equivalent to image.size() / image.devicePixelRatio().
+
+ \since 6.2
+*/
+QSizeF QImage::deviceIndependentSize() const
+{
+ if (!d)
+ return QSizeF(0, 0);
+ return QSizeF(d->width, d->height) / d->devicePixelRatio;
+}
+
+
+/*!
\since 5.10
Returns the image data size in bytes.
@@ -1526,7 +1602,7 @@ void QImage::setColor(int i, QRgb c)
qWarning("QImage::setColor: Index out of bound %d", i);
return;
}
- detach();
+ detachMetadata(true);
// In case detach() run out of memory
if (!d)
@@ -1674,11 +1750,11 @@ const uchar *QImage::constBits() const
the depth is 8, the lowest 8 bits are used and if the depth is 16
the lowest 16 bits are used.
- Note: QImage::pixel() returns the color of the pixel at the given
- coordinates while QColor::pixel() returns the pixel value of the
- underlying window system (essentially an index value), so normally
- you will want to use QImage::pixel() to use a color from an
- existing image or QColor::rgb() to use a specific color.
+ If the image depth is higher than 32bit the result is undefined.
+
+ \note There are no corresponding value getter, though QImage::pixelIndex()
+ will return the same value for indexed formats, and QImage::pixel() for
+ RGB32, ARGB32, and ARGB32PM formats.
\sa depth(), {QImage#Image Transformations}{Image Transformations}
*/
@@ -1709,18 +1785,40 @@ void QImage::fill(uint pixel)
w, d->height, d->bytes_per_line);
return;
} else if (d->depth == 16) {
+ if (d->format == Format_RGB444)
+ pixel |= 0xf000;
qt_rectfill<quint16>(reinterpret_cast<quint16*>(d->data), pixel,
0, 0, d->width, d->height, d->bytes_per_line);
return;
} else if (d->depth == 24) {
+ if (d->format == Format_RGB666)
+ pixel |= 0xfc0000;
qt_rectfill<quint24>(reinterpret_cast<quint24*>(d->data), pixel,
0, 0, d->width, d->height, d->bytes_per_line);
return;
- } else if (d->depth == 64) {
+ } else if (d->format >= QImage::Format_RGBX64 && d->format <= QImage::Format_RGBA64_Premultiplied) {
qt_rectfill<quint64>(reinterpret_cast<quint64*>(d->data), QRgba64::fromArgb32(pixel),
0, 0, d->width, d->height, d->bytes_per_line);
return;
+ } else if (d->format >= QImage::Format_RGBX16FPx4 && d->format <= QImage::Format_RGBA16FPx4_Premultiplied) {
+ quint64 cu;
+ QRgbaFloat16 cf = QRgbaFloat16::fromArgb32(pixel);
+ ::memcpy(&cu, &cf, sizeof(quint64));
+ qt_rectfill<quint64>(reinterpret_cast<quint64*>(d->data), cu,
+ 0, 0, d->width, d->height, d->bytes_per_line);
+ return;
+ } else if (d->format >= QImage::Format_RGBX32FPx4 && d->format <= QImage::Format_RGBA32FPx4_Premultiplied) {
+ QRgbaFloat32 cf = QRgbaFloat32::fromArgb32(pixel);
+ uchar *data = d->data;
+ for (int y = 0; y < d->height; ++y) {
+ QRgbaFloat32 *line = reinterpret_cast<QRgbaFloat32 *>(data);
+ for (int x = 0; x < d->width; ++x)
+ line[x] = cf;
+ data += d->bytes_per_line;
+ }
+ return;
}
+ Q_ASSERT(d->depth == 32);
if (d->format == Format_RGB32)
pixel |= 0xff000000;
@@ -1781,6 +1879,8 @@ void QImage::fill(const QColor &color)
if (!d)
return;
+ QRgba64 opaque = color.rgba64();
+ opaque.setAlpha(65535);
switch (d->format) {
case QImage::Format_RGB32:
case QImage::Format_ARGB32:
@@ -1799,12 +1899,10 @@ void QImage::fill(const QColor &color)
fill(ARGB2RGBA(qPremultiply(color.rgba())));
break;
case QImage::Format_BGR30:
- case QImage::Format_A2BGR30_Premultiplied:
- fill(qConvertRgb64ToRgb30<PixelOrderBGR>(color.rgba64()));
+ fill(qConvertRgb64ToRgb30<PixelOrderBGR>(opaque));
break;
case QImage::Format_RGB30:
- case QImage::Format_A2RGB30_Premultiplied:
- fill(qConvertRgb64ToRgb30<PixelOrderRGB>(color.rgba64()));
+ fill(qConvertRgb64ToRgb30<PixelOrderRGB>(opaque));
break;
case QImage::Format_RGB16:
fill((uint) qConvertRgb32To16(color.rgba()));
@@ -1827,19 +1925,43 @@ void QImage::fill(const QColor &color)
else
fill((uint) 0);
break;
- case QImage::Format_RGBX64: {
- QRgba64 c = color.rgba64();
- c.setAlpha(65535);
- qt_rectfill<quint64>(reinterpret_cast<quint64*>(d->data), c,
+ case QImage::Format_RGBX64:
+ qt_rectfill<quint64>(reinterpret_cast<quint64*>(d->data), opaque,
0, 0, d->width, d->height, d->bytes_per_line);
break;
-
- }
case QImage::Format_RGBA64:
- case QImage::Format_RGBA64_Premultiplied:
qt_rectfill<quint64>(reinterpret_cast<quint64*>(d->data), color.rgba64(),
0, 0, d->width, d->height, d->bytes_per_line);
break;
+ case QImage::Format_RGBA64_Premultiplied:
+ qt_rectfill<quint64>(reinterpret_cast<quint64 *>(d->data), color.rgba64().premultiplied(),
+ 0, 0, d->width, d->height, d->bytes_per_line);
+ break;
+ case QImage::Format_RGBX16FPx4:
+ case QImage::Format_RGBA16FPx4:
+ case QImage::Format_RGBA16FPx4_Premultiplied:
+ case QImage::Format_RGBX32FPx4:
+ case QImage::Format_RGBA32FPx4:
+ case QImage::Format_RGBA32FPx4_Premultiplied:{
+ float r, g, b, a;
+ color.getRgbF(&r, &g, &b, &a);
+ if (!hasAlphaChannel())
+ a = 1.0f;
+ if (depth() == 64) {
+ QRgbaFloat16 c16{qfloat16(r), qfloat16(g), qfloat16(b), qfloat16(a)};
+ if (d->format == Format_RGBA16FPx4_Premultiplied)
+ c16 = c16.premultiplied();
+ qt_rectfill<QRgbaFloat16>(reinterpret_cast<QRgbaFloat16 *>(d->data), c16,
+ 0, 0, d->width, d->height, d->bytes_per_line);
+ } else {
+ QRgbaFloat32 c32{r, g, b, a};
+ if (d->format == Format_RGBA32FPx4_Premultiplied)
+ c32 = c32.premultiplied();
+ qt_rectfill<QRgbaFloat32>(reinterpret_cast<QRgbaFloat32 *>(d->data), c32,
+ 0, 0, d->width, d->height, d->bytes_per_line);
+ }
+ break;
+ }
default: {
QPainter p(this);
p.setCompositionMode(QPainter::CompositionMode_Source);
@@ -1883,7 +2005,13 @@ void QImage::invertPixels(InvertMode mode)
QImage::Format originalFormat = d->format;
// Inverting premultiplied pixels would produce invalid image data.
if (hasAlphaChannel() && qPixelLayouts[d->format].premultiplied) {
- if (depth() > 32) {
+ if (d->format == QImage::Format_RGBA16FPx4_Premultiplied) {
+ if (!d->convertInPlace(QImage::Format_RGBA16FPx4, { }))
+ *this = convertToFormat(QImage::Format_RGBA16FPx4);
+ } else if (d->format == QImage::Format_RGBA32FPx4_Premultiplied) {
+ if (!d->convertInPlace(QImage::Format_RGBA32FPx4, { }))
+ *this = convertToFormat(QImage::Format_RGBA32FPx4);
+ } else if (depth() > 32) {
if (!d->convertInPlace(QImage::Format_RGBA64, { }))
*this = convertToFormat(QImage::Format_RGBA64);
} else {
@@ -1902,8 +2030,32 @@ void QImage::invertPixels(InvertMode mode)
*sl++ ^= 0xff;
sl += pad;
}
- }
- else if (depth() == 64) {
+ } else if (format() >= QImage::Format_RGBX16FPx4 && format() <= QImage::Format_RGBA16FPx4_Premultiplied) {
+ qfloat16 *p = reinterpret_cast<qfloat16 *>(d->data);
+ qfloat16 *end = reinterpret_cast<qfloat16 *>(d->data + d->nbytes);
+ while (p < end) {
+ p[0] = qfloat16(1) - p[0];
+ p[1] = qfloat16(1) - p[1];
+ p[2] = qfloat16(1) - p[2];
+ if (mode == InvertRgba)
+ p[3] = qfloat16(1) - p[3];
+ p += 4;
+ }
+ } else if (format() >= QImage::Format_RGBX32FPx4 && format() <= QImage::Format_RGBA32FPx4_Premultiplied) {
+ uchar *data = d->data;
+ for (int y = 0; y < d->height; ++y) {
+ float *p = reinterpret_cast<float *>(data);
+ for (int x = 0; x < d->width; ++x) {
+ p[0] = 1.0f - p[0];
+ p[1] = 1.0f - p[1];
+ p[2] = 1.0f - p[2];
+ if (mode == InvertRgba)
+ p[3] = 1.0f - p[3];
+ p += 4;
+ }
+ data += d->bytes_per_line;
+ }
+ } else if (depth() == 64) {
quint16 *p = (quint16*)d->data;
quint16 *end = (quint16*)(d->data + d->nbytes);
quint16 xorbits = 0xffff;
@@ -1992,7 +2144,7 @@ void QImage::setColorCount(int colorCount)
return;
}
- detach();
+ detachMetadata(true);
// In case detach() ran out of memory
if (!d)
@@ -2053,7 +2205,7 @@ QImage QImage::convertToFormat_helper(Format format, Qt::ImageConversionFlags fl
if (!d || d->format == format)
return *this;
- if (format == Format_Invalid || d->format == Format_Invalid)
+ if (d->format == Format_Invalid || format <= Format_Invalid || format >= NImageFormats)
return QImage();
const QPixelLayout *destLayout = &qPixelLayouts[format];
@@ -2061,7 +2213,12 @@ QImage QImage::convertToFormat_helper(Format format, Qt::ImageConversionFlags fl
if (!converter && format > QImage::Format_Indexed8 && d->format > QImage::Format_Indexed8) {
if (qt_highColorPrecision(d->format, !destLayout->hasAlphaChannel)
&& qt_highColorPrecision(format, !hasAlphaChannel())) {
- converter = convert_generic_over_rgb64;
+#if QT_CONFIG(raster_fp)
+ if (qt_fpColorPrecision(d->format) && qt_fpColorPrecision(format))
+ converter = convert_generic_over_rgba32f;
+ else
+#endif
+ converter = convert_generic_over_rgb64;
} else
converter = convert_generic;
}
@@ -2070,7 +2227,6 @@ QImage QImage::convertToFormat_helper(Format format, Qt::ImageConversionFlags fl
QIMAGE_SANITYCHECK_MEMORY(image);
- image.d->offset = offset();
copyMetadata(image.d, d);
converter(image.d, d, flags);
@@ -2127,7 +2283,7 @@ static QImage convertWithPalette(const QImage &src, QImage::Format format,
QImage dest(src.size(), format);
dest.setColorTable(clut);
- QImageData::get(dest)->text = QImageData::get(src)->text;
+ copyMetadata(QImageData::get(dest), QImageData::get(src));
int h = src.height();
int w = src.width();
@@ -2183,7 +2339,7 @@ QImage QImage::convertToFormat(Format format, const QList<QRgb> &colorTable, Qt:
if (!d || d->format == format)
return *this;
- if (format == QImage::Format_Invalid)
+ if (format <= QImage::Format_Invalid || format >= QImage::NImageFormats)
return QImage();
if (format <= QImage::Format_Indexed8)
return convertWithPalette(convertToFormat(QImage::Format_ARGB32, flags), format, colorTable);
@@ -2228,6 +2384,7 @@ bool QImage::reinterpretAsFormat(Format format)
// In case detach() ran out of memory
if (!d) {
d = oldD;
+ d->ref.ref();
return false;
}
}
@@ -2239,7 +2396,7 @@ bool QImage::reinterpretAsFormat(Format format)
/*!
\since 5.13
- Detach and convert the image to the given \a format in place.
+ Converts the image to the given \a format in place, detaching if necessary.
The specified image conversion \a flags control how the image data
is handled during the conversion process.
@@ -2249,7 +2406,10 @@ bool QImage::reinterpretAsFormat(Format format)
void QImage::convertTo(Format format, Qt::ImageConversionFlags flags)
{
- if (!d || format == QImage::Format_Invalid)
+ if (!d || format <= QImage::Format_Invalid || format >= QImage::NImageFormats)
+ return;
+
+ if (d->format == format)
return;
detach();
@@ -2391,6 +2551,14 @@ QRgb QImage::pixel(int x, int y) const
case Format_RGBA64: // Match ARGB32 behavior.
case Format_RGBA64_Premultiplied:
return reinterpret_cast<const QRgba64 *>(s)[x].toArgb32();
+ case Format_RGBX16FPx4:
+ case Format_RGBA16FPx4: // Match ARGB32 behavior.
+ case Format_RGBA16FPx4_Premultiplied:
+ return reinterpret_cast<const QRgbaFloat16 *>(s)[x].toArgb32();
+ case Format_RGBX32FPx4:
+ case Format_RGBA32FPx4: // Match ARGB32 behavior.
+ case Format_RGBA32FPx4_Premultiplied:
+ return reinterpret_cast<const QRgbaFloat32 *>(s)[x].toArgb32();
default:
break;
}
@@ -2467,7 +2635,7 @@ void QImage::setPixel(int x, int y, uint index_or_rgb)
((uint *)s)[x] = index_or_rgb;
return;
case Format_RGB16:
- ((quint16 *)s)[x] = qConvertRgb32To16(qUnpremultiply(index_or_rgb));
+ ((quint16 *)s)[x] = qConvertRgb32To16(index_or_rgb);
return;
case Format_RGBX8888:
((uint *)s)[x] = ARGB2RGBA(0xff000000 | index_or_rgb);
@@ -2488,6 +2656,27 @@ void QImage::setPixel(int x, int y, uint index_or_rgb)
case Format_A2RGB30_Premultiplied:
((uint *)s)[x] = qConvertArgb32ToA2rgb30<PixelOrderRGB>(index_or_rgb);
return;
+ case Format_RGBX64:
+ ((QRgba64 *)s)[x] = QRgba64::fromArgb32(index_or_rgb | 0xff000000);
+ return;
+ case Format_RGBA64:
+ case Format_RGBA64_Premultiplied:
+ ((QRgba64 *)s)[x] = QRgba64::fromArgb32(index_or_rgb);
+ return;
+ case Format_RGBX16FPx4:
+ ((QRgbaFloat16 *)s)[x] = QRgbaFloat16::fromArgb32(index_or_rgb | 0xff000000);
+ return;
+ case Format_RGBA16FPx4:
+ case Format_RGBA16FPx4_Premultiplied:
+ ((QRgbaFloat16 *)s)[x] = QRgbaFloat16::fromArgb32(index_or_rgb);
+ return;
+ case Format_RGBX32FPx4:
+ ((QRgbaFloat32 *)s)[x] = QRgbaFloat32::fromArgb32(index_or_rgb | 0xff000000);
+ return;
+ case Format_RGBA32FPx4:
+ case Format_RGBA32FPx4_Premultiplied:
+ ((QRgbaFloat32 *)s)[x] = QRgbaFloat32::fromArgb32(index_or_rgb);
+ return;
case Format_Invalid:
case NImageFormats:
Q_ASSERT(false);
@@ -2497,7 +2686,10 @@ void QImage::setPixel(int x, int y, uint index_or_rgb)
}
const QPixelLayout *layout = &qPixelLayouts[d->format];
- layout->storeFromARGB32PM(s, &index_or_rgb, x, 1, nullptr, nullptr);
+ if (!hasAlphaChannel())
+ layout->storeFromRGB32(s, &index_or_rgb, x, 1, nullptr, nullptr);
+ else
+ layout->storeFromARGB32PM(s, &index_or_rgb, x, 1, nullptr, nullptr);
}
/*!
@@ -2549,6 +2741,26 @@ QColor QImage::pixelColor(int x, int y) const
quint16 v = reinterpret_cast<const quint16 *>(s)[x];
return QColor(qRgba64(v, v, v, 0xffff));
}
+ case Format_RGBX16FPx4:
+ case Format_RGBA16FPx4:
+ case Format_RGBA16FPx4_Premultiplied: {
+ QRgbaFloat16 p = reinterpret_cast<const QRgbaFloat16 *>(s)[x];
+ if (d->format == Format_RGBA16FPx4_Premultiplied)
+ p = p.unpremultiplied();
+ QColor color;
+ color.setRgbF(p.red(), p.green(), p.blue(), p.alpha());
+ return color;
+ }
+ case Format_RGBX32FPx4:
+ case Format_RGBA32FPx4:
+ case Format_RGBA32FPx4_Premultiplied: {
+ QRgbaFloat32 p = reinterpret_cast<const QRgbaFloat32 *>(s)[x];
+ if (d->format == Format_RGBA32FPx4_Premultiplied)
+ p = p.unpremultiplied();
+ QColor color;
+ color.setRgbF(p.red(), p.green(), p.blue(), p.alpha());
+ return color;
+ }
default:
c = QRgba64::fromArgb32(pixel(x, y));
break;
@@ -2620,13 +2832,36 @@ void QImage::setPixelColor(int x, int y, const QColor &color)
((uint *)s)[x] = qConvertRgb64ToRgb30<PixelOrderRGB>(c);
return;
case Format_RGBX64:
- ((QRgba64 *)s)[x] = color.rgba64();
- ((QRgba64 *)s)[x].setAlpha(65535);
- return;
case Format_RGBA64:
case Format_RGBA64_Premultiplied:
- ((QRgba64 *)s)[x] = color.rgba64();
+ ((QRgba64 *)s)[x] = c;
+ return;
+ case Format_RGBX16FPx4:
+ case Format_RGBA16FPx4:
+ case Format_RGBA16FPx4_Premultiplied: {
+ float r, g, b, a;
+ color.getRgbF(&r, &g, &b, &a);
+ if (d->format == Format_RGBX16FPx4)
+ a = 1.0f;
+ QRgbaFloat16 c16f{qfloat16(r), qfloat16(g), qfloat16(b), qfloat16(a)};
+ if (d->format == Format_RGBA16FPx4_Premultiplied)
+ c16f = c16f.premultiplied();
+ ((QRgbaFloat16 *)s)[x] = c16f;
+ return;
+ }
+ case Format_RGBX32FPx4:
+ case Format_RGBA32FPx4:
+ case Format_RGBA32FPx4_Premultiplied: {
+ float r, g, b, a;
+ color.getRgbF(&r, &g, &b, &a);
+ if (d->format == Format_RGBX32FPx4)
+ a = 1.0f;
+ QRgbaFloat32 c32f{r, g, b, a};
+ if (d->format == Format_RGBA32FPx4_Premultiplied)
+ c32f = c32f.premultiplied();
+ ((QRgbaFloat32 *)s)[x] = c32f;
return;
+ }
default:
setPixel(x, y, c.toArgb32());
return;
@@ -2783,7 +3018,7 @@ bool QImage::isGrayscale() const
\sa isNull(), {QImage#Image Transformations}{Image
Transformations}
*/
-QImage QImage::scaled(const QSize& s, Qt::AspectRatioMode aspectMode, Qt::TransformationMode mode) const
+QImage Q_TRACE_INSTRUMENT(qtgui) QImage::scaled(const QSize& s, Qt::AspectRatioMode aspectMode, Qt::TransformationMode mode) const
{
if (!d) {
qWarning("QImage::scaled: Image is a null image");
@@ -2820,7 +3055,7 @@ QImage QImage::scaled(const QSize& s, Qt::AspectRatioMode aspectMode, Qt::Transf
\sa {QImage#Image Transformations}{Image Transformations}
*/
-QImage QImage::scaledToWidth(int w, Qt::TransformationMode mode) const
+QImage Q_TRACE_INSTRUMENT(qtgui) QImage::scaledToWidth(int w, Qt::TransformationMode mode) const
{
if (!d) {
qWarning("QImage::scaleWidth: Image is a null image");
@@ -2850,7 +3085,7 @@ QImage QImage::scaledToWidth(int w, Qt::TransformationMode mode) const
\sa {QImage#Image Transformations}{Image Transformations}
*/
-QImage QImage::scaledToHeight(int h, Qt::TransformationMode mode) const
+QImage Q_TRACE_INSTRUMENT(qtgui) QImage::scaledToHeight(int h, Qt::TransformationMode mode) const
{
if (!d) {
qWarning("QImage::scaleHeight: Image is a null image");
@@ -2883,7 +3118,7 @@ QImage QImage::scaledToHeight(int h, Qt::TransformationMode mode) const
\sa createHeuristicMask(), {QImage#Image Transformations}{Image
Transformations}
*/
-QImage QImage::createAlphaMask(Qt::ImageConversionFlags flags) const
+QImage Q_TRACE_INSTRUMENT(qtgui) QImage::createAlphaMask(Qt::ImageConversionFlags flags) const
{
if (!d || d->format == QImage::Format_RGB32)
return QImage();
@@ -3039,6 +3274,8 @@ QImage QImage::createMaskFromColor(QRgb color, Qt::MaskMode mode) const
QIMAGE_SANITYCHECK_MEMORY(maskImage);
maskImage.fill(0);
uchar *s = maskImage.bits();
+ if (!s)
+ return QImage();
if (depth() == 32) {
for (int h = 0; h < d->height; h++) {
@@ -3185,6 +3422,9 @@ inline void do_mirror(QImageData *dst, QImageData *src, bool horizontal, bool ve
}
switch (depth) {
+ case 128:
+ do_mirror_data<QRgbaFloat32>(dst, src, dstX0, dstY0, dstXIncr, dstYIncr, w, h);
+ break;
case 64:
do_mirror_data<quint64>(dst, src, dstX0, dstY0, dstXIncr, dstYIncr, w, h);
break;
@@ -3330,7 +3570,7 @@ static inline void rgbSwapped_generic(int width, int height, const QImage *src,
/*!
\internal
*/
-QImage QImage::rgbSwapped_helper() const
+QImage Q_TRACE_INSTRUMENT(qtgui) QImage::rgbSwapped_helper() const
{
if (isNull())
return *this;
@@ -3411,25 +3651,9 @@ QImage QImage::rgbSwapped_helper() const
}
}
break;
- case Format_RGBX64:
- case Format_RGBA64:
- case Format_RGBA64_Premultiplied:
- res = QImage(d->width, d->height, d->format);
- QIMAGE_SANITYCHECK_MEMORY(res);
- for (int i = 0; i < d->height; i++) {
- QRgba64 *q = reinterpret_cast<QRgba64 *>(res.scanLine(i));
- const QRgba64 *p = reinterpret_cast<const QRgba64 *>(constScanLine(i));
- const QRgba64 *end = p + d->width;
- while (p < end) {
- QRgba64 c = *p;
- *q = QRgba64::fromRgba64(c.blue(), c.green(), c.red(), c.alpha());
- p++;
- q++;
- }
- }
- break;
default:
res = QImage(d->width, d->height, d->format);
+ QIMAGE_SANITYCHECK_MEMORY(res);
rgbSwapped_generic(d->width, d->height, this, &res, &qPixelLayouts[d->format]);
break;
}
@@ -3523,19 +3747,6 @@ void QImage::rgbSwapped_inplace()
}
}
break;
- case Format_RGBX64:
- case Format_RGBA64:
- case Format_RGBA64_Premultiplied:
- for (int i = 0; i < d->height; i++) {
- QRgba64 *p = reinterpret_cast<QRgba64 *>(scanLine(i));
- QRgba64 *end = p + d->width;
- while (p < end) {
- QRgba64 c = *p;
- *p = QRgba64::fromRgba64(c.blue(), c.green(), c.red(), c.alpha());
- p++;
- }
- }
- break;
default:
rgbSwapped_generic(d->width, d->height, this, this, &qPixelLayouts[d->format]);
break;
@@ -3581,11 +3792,10 @@ bool QImage::load(QIODevice* device, const char* format)
}
/*!
- \fn bool QImage::loadFromData(const uchar *data, int len, const char *format)
+ \since 6.2
- Loads an image from the first \a len bytes of the given binary \a
- data. Returns \c true if the image was successfully loaded; otherwise
- invalidates the image and returns \c false.
+ Loads an image from the given QByteArrayView \a data. Returns \c true if the image was
+ successfully loaded; otherwise invalidates the image and returns \c false.
The loader attempts to read the image using the specified \a format, e.g.,
PNG or JPG. If \a format is not specified (which is the default), the
@@ -3594,13 +3804,26 @@ bool QImage::load(QIODevice* device, const char* format)
\sa {QImage#Reading and Writing Image Files}{Reading and Writing Image Files}
*/
-bool QImage::loadFromData(const uchar *data, int len, const char *format)
+bool QImage::loadFromData(QByteArrayView data, const char *format)
{
- *this = fromData(data, len, format);
+ *this = fromData(data, format);
return !isNull();
}
/*!
+ \fn bool QImage::loadFromData(const uchar *data, int len, const char *format)
+
+ \overload
+
+ Loads an image from the first \a len bytes of the given binary \a data.
+*/
+
+bool QImage::loadFromData(const uchar *buf, int len, const char *format)
+{
+ return loadFromData(QByteArrayView(buf, len), format);
+}
+
+/*!
\fn bool QImage::loadFromData(const QByteArray &data, const char *format)
\overload
@@ -3609,12 +3832,11 @@ bool QImage::loadFromData(const uchar *data, int len, const char *format)
*/
/*!
- \fn QImage QImage::fromData(const uchar *data, int size, const char *format)
+ \since 6.2
- Constructs a QImage from the first \a size bytes of the given
- binary \a data. The loader attempts to read the image using the
- specified \a format. If \a format is not specified (which is the default),
- the loader probes the data for a header to guess the file format.
+ Constructs an image from the given QByteArrayView \a data. The loader attempts to read the image
+ using the specified \a format. If \a format is not specified (which is the default), the loader
+ probes the data for a header to guess the file format.
If \a format is specified, it must be one of the values returned by
QImageReader::supportedImageFormats().
@@ -3624,9 +3846,9 @@ bool QImage::loadFromData(const uchar *data, int len, const char *format)
\sa load(), save(), {QImage#Reading and Writing Image Files}{Reading and Writing Image Files}
*/
-QImage QImage::fromData(const uchar *data, int size, const char *format)
+QImage QImage::fromData(QByteArrayView data, const char *format)
{
- QByteArray a = QByteArray::fromRawData(reinterpret_cast<const char *>(data), size);
+ QByteArray a = QByteArray::fromRawData(data.constData(), data.size());
QBuffer b;
b.setData(a);
b.open(QIODevice::ReadOnly);
@@ -3634,11 +3856,25 @@ QImage QImage::fromData(const uchar *data, int size, const char *format)
}
/*!
+ \fn QImage QImage::fromData(const uchar *data, int size, const char *format)
+
+ \overload
+
+ Constructs a QImage from the first \a size bytes of the given binary \a data.
+*/
+
+QImage QImage::fromData(const uchar *data, int size, const char *format)
+{
+ return fromData(QByteArrayView(data, size), format);
+}
+
+/*!
\fn QImage QImage::fromData(const QByteArray &data, const char *format)
\overload
- Loads an image from the given QByteArray \a data.
+ Constructs a QImage from the given QByteArray \a data.
+
*/
/*!
@@ -3690,10 +3926,15 @@ bool QImage::save(QIODevice* device, const char* format, int quality) const
bool QImageData::doImageIO(const QImage *image, QImageWriter *writer, int quality) const
{
if (quality > 100 || quality < -1)
- qWarning("QPixmap::save: Quality out of range [-1, 100]");
+ qWarning("QImage::save: Quality out of range [-1, 100]");
if (quality >= 0)
writer->setQuality(qMin(quality,100));
- return writer->write(*image);
+ const bool result = writer->write(*image);
+#ifdef QT_DEBUG
+ if (!result)
+ qWarning("QImage::save: failed to write image - %s", qPrintable(writer->errorString()));
+#endif
+ return result;
}
/*****************************************************************************
@@ -3778,7 +4019,7 @@ bool QImage::operator==(const QImage & i) const
return false;
// obviously different stuff?
- if (i.d->height != d->height || i.d->width != d->width || i.d->format != d->format)
+ if (i.d->height != d->height || i.d->width != d->width || i.d->format != d->format || i.d->colorSpace != d->colorSpace)
return false;
if (d->format != Format_RGB32) {
@@ -3882,9 +4123,9 @@ int QImage::dotsPerMeterY() const
*/
void QImage::setDotsPerMeterX(int x)
{
- if (!d || !x)
+ if (!d || !x || d->dpmx == x)
return;
- detach();
+ detachMetadata();
if (d)
d->dpmx = x;
@@ -3904,9 +4145,9 @@ void QImage::setDotsPerMeterX(int x)
*/
void QImage::setDotsPerMeterY(int y)
{
- if (!d || !y)
+ if (!d || !y || d->dpmy == y)
return;
- detach();
+ detachMetadata();
if (d)
d->dpmy = y;
@@ -3936,9 +4177,9 @@ QPoint QImage::offset() const
*/
void QImage::setOffset(const QPoint& p)
{
- if (!d)
+ if (!d || d->offset == p)
return;
- detach();
+ detachMetadata();
if (d)
d->offset = p;
@@ -3974,7 +4215,7 @@ QString QImage::text(const QString &key) const
QString tmp;
for (auto it = d->text.begin(), end = d->text.end(); it != end; ++it)
- tmp += it.key() + QLatin1String(": ") + it.value().simplified() + QLatin1String("\n\n");
+ tmp += it.key() + ": "_L1 + it.value().simplified() + "\n\n"_L1;
if (!tmp.isEmpty())
tmp.chop(2); // remove final \n\n
return tmp;
@@ -4008,7 +4249,7 @@ void QImage::setText(const QString &key, const QString &value)
{
if (!d)
return;
- detach();
+ detachMetadata();
if (d)
d->text.insert(key, value);
@@ -4405,8 +4646,12 @@ int QImage::bitPlaneCount() const
bpc = 12;
break;
case QImage::Format_RGBX64:
+ case QImage::Format_RGBX16FPx4:
bpc = 48;
break;
+ case QImage::Format_RGBX32FPx4:
+ bpc = 96;
+ break;
default:
bpc = qt_depthForFormat(d->format);
break;
@@ -4425,7 +4670,8 @@ int QImage::bitPlaneCount() const
if necessary. To avoid unnecessary conversion the result is returned in the format
internally used, and not in the original format.
*/
-QImage QImage::smoothScaled(int w, int h) const {
+QImage QImage::smoothScaled(int w, int h) const
+{
QImage src = *this;
switch (src.format()) {
case QImage::Format_RGB32:
@@ -4439,14 +4685,28 @@ QImage QImage::smoothScaled(int w, int h) const {
case QImage::Format_RGBA64_Premultiplied:
break;
case QImage::Format_RGBA64:
- src = src.convertToFormat(QImage::Format_RGBA64_Premultiplied);
+ case QImage::Format_Grayscale16:
+ src.convertTo(QImage::Format_RGBA64_Premultiplied);
+ break;
+#endif
+#if QT_CONFIG(raster_fp)
+ case QImage::Format_RGBX32FPx4:
+ case QImage::Format_RGBA32FPx4_Premultiplied:
+ break;
+ case QImage::Format_RGBX16FPx4:
+ src.convertTo(QImage::Format_RGBX32FPx4);
+ break;
+ case QImage::Format_RGBA16FPx4:
+ case QImage::Format_RGBA16FPx4_Premultiplied:
+ case QImage::Format_RGBA32FPx4:
+ src.convertTo(QImage::Format_RGBA32FPx4_Premultiplied);
break;
#endif
default:
if (src.hasAlphaChannel())
- src = src.convertToFormat(QImage::Format_ARGB32_Premultiplied);
+ src.convertTo(QImage::Format_ARGB32_Premultiplied);
else
- src = src.convertToFormat(QImage::Format_RGB32);
+ src.convertTo(QImage::Format_RGB32);
}
src = qSmoothScaleImage(src, w, h);
if (!src.isNull())
@@ -4457,6 +4717,8 @@ QImage QImage::smoothScaled(int w, int h) const {
static QImage rotated90(const QImage &image)
{
QImage out(image.height(), image.width(), image.format());
+ if (out.isNull())
+ return out;
copyMetadata(&out, image);
if (image.colorCount() > 0)
out.setColorTable(image.colorTable());
@@ -4485,6 +4747,8 @@ static QImage rotated180(const QImage &image)
return image.mirrored(true, true);
QImage out(image.width(), image.height(), image.format());
+ if (out.isNull())
+ return out;
copyMetadata(&out, image);
if (image.colorCount() > 0)
out.setColorTable(image.colorTable());
@@ -4497,6 +4761,8 @@ static QImage rotated180(const QImage &image)
static QImage rotated270(const QImage &image)
{
QImage out(image.height(), image.width(), image.format());
+ if (out.isNull())
+ return out;
copyMetadata(&out, image);
if (image.colorCount() > 0)
out.setColorTable(image.colorTable());
@@ -4527,7 +4793,7 @@ static QImage rotated270(const QImage &image)
image where not all pixels are covered by the transformed pixels of the
original image. In such cases, those background pixels will be assigned a
transparent color value, and the transformed image will be given a format
- with an alpha channel, even if the orginal image did not have that.
+ with an alpha channel, even if the original image did not have that.
The transformation \a matrix is internally adjusted to compensate
for unwanted translation; i.e. the image produced is the smallest
@@ -4542,12 +4808,15 @@ static QImage rotated270(const QImage &image)
Transformations}
*/
-QImage QImage::transformed(const QTransform &matrix, Qt::TransformationMode mode ) const
+QImage Q_TRACE_INSTRUMENT(qtgui) QImage::transformed(const QTransform &matrix, Qt::TransformationMode mode ) const
{
if (!d)
return QImage();
- Q_TRACE_SCOPE(QImage_transformed, matrix, mode);
+ Q_TRACE_PARAM_REPLACE(const QTransform &, double[9]);
+ Q_TRACE_SCOPE(QImage_transformed, QList<double>({matrix.m11(), matrix.m12(), matrix.m13(),
+ matrix.m21(), matrix.m22(), matrix.m23(),
+ matrix.m31(), matrix.m32(), matrix.m33()}).data(), mode);
// source image data
const int ws = width();
@@ -4568,13 +4837,8 @@ QImage QImage::transformed(const QTransform &matrix, Qt::TransformationMode mode
else if (mat.m11() == -1. && mat.m22() == -1.)
return rotated180(*this);
- if (mode == Qt::FastTransformation) {
- hd = qRound(qAbs(mat.m22()) * hs);
- wd = qRound(qAbs(mat.m11()) * ws);
- } else {
- hd = int(qAbs(mat.m22()) * hs + 0.9999);
- wd = int(qAbs(mat.m11()) * ws + 0.9999);
- }
+ hd = qRound(qAbs(mat.m22()) * hs);
+ wd = qRound(qAbs(mat.m11()) * ws);
scale_xform = true;
// The paint-based scaling is only bilinear, and has problems
// with scaling smoothly more than 2x down.
@@ -4624,14 +4888,24 @@ QImage QImage::transformed(const QTransform &matrix, Qt::TransformationMode mode
|| (ws * hs) >= (1<<20)
#endif
) {
+ QImage scaledImage;
if (mat.m11() < 0.0F && mat.m22() < 0.0F) { // horizontal/vertical flip
- return smoothScaled(wd, hd).mirrored(true, true).convertToFormat(format());
+ scaledImage = smoothScaled(wd, hd).mirrored(true, true);
} else if (mat.m11() < 0.0F) { // horizontal flip
- return smoothScaled(wd, hd).mirrored(true, false).convertToFormat(format());
+ scaledImage = smoothScaled(wd, hd).mirrored(true, false);
} else if (mat.m22() < 0.0F) { // vertical flip
- return smoothScaled(wd, hd).mirrored(false, true).convertToFormat(format());
+ scaledImage = smoothScaled(wd, hd).mirrored(false, true);
} else { // no flipping
- return smoothScaled(wd, hd).convertToFormat(format());
+ scaledImage = smoothScaled(wd, hd);
+ }
+
+ switch (format()) {
+ case QImage::Format_Mono:
+ case QImage::Format_MonoLSB:
+ case QImage::Format_Indexed8:
+ return scaledImage;
+ default:
+ return scaledImage.convertToFormat(format());
}
}
}
@@ -4673,7 +4947,14 @@ QImage QImage::transformed(const QTransform &matrix, Qt::TransformationMode mode
if (target_format >= QImage::Format_RGB32) {
// Prevent QPainter from applying devicePixelRatio corrections
- const QImage sImage = (devicePixelRatio() != 1) ? QImage(constBits(), width(), height(), format()) : *this;
+ QImage sImage = (devicePixelRatio() != 1) ? QImage(constBits(), width(), height(), format()) : *this;
+ if (sImage.d != d
+ && (d->format == QImage::Format_MonoLSB
+ || d->format == QImage::Format_Mono
+ || d->format == QImage::Format_Indexed8)) {
+ sImage.d->colortable = d->colortable;
+ sImage.d->has_alpha_clut = d->has_alpha_clut;
+ }
Q_ASSERT(sImage.devicePixelRatio() == 1);
Q_ASSERT(sImage.devicePixelRatio() == dImage.devicePixelRatio());
@@ -4743,9 +5024,12 @@ void QImage::setColorSpace(const QColorSpace &colorSpace)
return;
if (d->colorSpace == colorSpace)
return;
- if (!isDetached()) // Detach only if shared, not for read-only data.
- detach();
- d->colorSpace = colorSpace;
+ if (colorSpace.isValid() && !qt_compatibleColorModel(pixelFormat().colorModel(), colorSpace.colorModel()))
+ return;
+
+ detachMetadata(false);
+ if (d)
+ d->colorSpace = colorSpace;
}
/*!
@@ -4755,20 +5039,60 @@ void QImage::setColorSpace(const QColorSpace &colorSpace)
If the image has no valid color space, the method does nothing.
+ \note If \a colorSpace is not compatible with the current format, the image
+ will be converted to one that is.
+
\sa convertedToColorSpace(), setColorSpace()
*/
void QImage::convertToColorSpace(const QColorSpace &colorSpace)
{
- if (!d)
- return;
- if (!d->colorSpace.isValid())
+ if (!d || !d->colorSpace.isValid())
return;
- if (!colorSpace.isValid()) {
+ if (!colorSpace.isValidTarget()) {
qWarning() << "QImage::convertToColorSpace: Output colorspace is not valid";
return;
}
- detach();
+ if (d->colorSpace == colorSpace)
+ return;
+ if (!qt_compatibleColorModel(pixelFormat().colorModel(), colorSpace.colorModel())) {
+ *this = convertedToColorSpace(colorSpace);
+ return;
+ }
applyColorTransform(d->colorSpace.transformationToColorSpace(colorSpace));
+ if (d->ref.loadRelaxed() != 1)
+ detachMetadata(false);
+ d->colorSpace = colorSpace;
+}
+
+/*!
+ \since 6.8
+
+ Converts the image to \a colorSpace and \a format.
+
+ If the image has no valid color space, the method does nothing,
+ nor if the color space is not compatible with with the format.
+
+ The specified image conversion \a flags control how the image data
+ is handled during the format conversion process.
+
+ \sa convertedToColorSpace(), setColorSpace()
+*/
+void QImage::convertToColorSpace(const QColorSpace &colorSpace, QImage::Format format, Qt::ImageConversionFlags flags)
+{
+ if (!d || !d->colorSpace.isValid())
+ return;
+ if (!colorSpace.isValidTarget()) {
+ qWarning() << "QImage::convertToColorSpace: Output colorspace is not valid";
+ return;
+ }
+ if (!qt_compatibleColorModel(toPixelFormat(format).colorModel(), colorSpace.colorModel())) {
+ qWarning() << "QImage::convertToColorSpace: Color space is not compatible with format";
+ return;
+ }
+
+ if (d->colorSpace == colorSpace)
+ return convertTo(format, flags);
+ applyColorTransform(d->colorSpace.transformationToColorSpace(colorSpace), format, flags);
d->colorSpace = colorSpace;
}
@@ -4779,14 +5103,56 @@ void QImage::convertToColorSpace(const QColorSpace &colorSpace)
If the image has no valid color space, a null QImage is returned.
- \sa convertToColorSpace()
+ \note If \a colorSpace is not compatible with the current format,
+ the returned image will also be converted to a format this is.
+ For more control over returned image format, see the three argument
+ overload of this method.
+
+ \sa convertToColorSpace(), colorTransformed()
*/
QImage QImage::convertedToColorSpace(const QColorSpace &colorSpace) const
{
- if (!d || !d->colorSpace.isValid() || !colorSpace.isValid())
+ if (!d || !d->colorSpace.isValid())
return QImage();
- QImage image = copy();
- image.convertToColorSpace(colorSpace);
+ if (!colorSpace.isValidTarget()) {
+ qWarning() << "QImage::convertedToColorSpace: Output colorspace is not valid";
+ return QImage();
+ }
+ if (d->colorSpace == colorSpace)
+ return *this;
+ QImage image = colorTransformed(d->colorSpace.transformationToColorSpace(colorSpace));
+ image.setColorSpace(colorSpace);
+ return image;
+}
+
+/*!
+ \since 6.8
+
+ Returns the image converted to \a colorSpace and \a format.
+
+ If the image has no valid color space, a null QImage is returned.
+
+ The specified image conversion \a flags control how the image data
+ is handled during the format conversion process.
+
+ \sa colorTransformed()
+*/
+QImage QImage::convertedToColorSpace(const QColorSpace &colorSpace, QImage::Format format, Qt::ImageConversionFlags flags) const
+{
+ if (!d || !d->colorSpace.isValid())
+ return QImage();
+ if (!colorSpace.isValidTarget()) {
+ qWarning() << "QImage::convertedToColorSpace: Output colorspace is not valid";
+ return QImage();
+ }
+ if (!qt_compatibleColorModel(toPixelFormat(format).colorModel(), colorSpace.colorModel())) {
+ qWarning() << "QImage::convertedToColorSpace: Color space is not compatible with format";
+ return QImage();
+ }
+ if (d->colorSpace == colorSpace)
+ return convertedTo(format, flags);
+ QImage image = colorTransformed(d->colorSpace.transformationToColorSpace(colorSpace), format, flags);
+ image.setColorSpace(colorSpace);
return image;
}
@@ -4809,31 +5175,59 @@ QColorSpace QImage::colorSpace() const
*/
void QImage::applyColorTransform(const QColorTransform &transform)
{
+ if (transform.isIdentity())
+ return;
+
+ if (!qt_compatibleColorModel(pixelFormat().colorModel(), QColorTransformPrivate::get(transform)->colorSpaceIn->colorModel) ||
+ !qt_compatibleColorModel(pixelFormat().colorModel(), QColorTransformPrivate::get(transform)->colorSpaceOut->colorModel)) {
+ qWarning() << "QImage::applyColorTransform can not apply format switching transform without switching format";
+ return;
+ }
+
+ detach();
+ if (!d)
+ return;
+ if (pixelFormat().colorModel() == QPixelFormat::Indexed) {
+ for (int i = 0; i < d->colortable.size(); ++i)
+ d->colortable[i] = transform.map(d->colortable[i]);
+ return;
+ }
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 (qt_fpColorPrecision(oldFormat)) {
+ if (oldFormat != QImage::Format_RGBX32FPx4 && oldFormat != QImage::Format_RGBA32FPx4
+ && oldFormat != QImage::Format_RGBA32FPx4_Premultiplied)
+ convertTo(QImage::Format_RGBA32FPx4);
+ } else if (depth() > 32) {
+ if (oldFormat != QImage::Format_RGBX64 && oldFormat != QImage::Format_RGBA64
+ && oldFormat != QImage::Format_RGBA64_Premultiplied)
+ convertTo(QImage::Format_RGBA64);
+ } else if (oldFormat != QImage::Format_ARGB32 && oldFormat != QImage::Format_RGB32
+ && oldFormat != QImage::Format_ARGB32_Premultiplied && oldFormat != QImage::Format_CMYK8888
+ && oldFormat != QImage::Format_Grayscale8 && oldFormat != QImage::Format_Grayscale16) {
if (hasAlphaChannel())
- *this = std::move(*this).convertToFormat(QImage::Format_ARGB32);
+ convertTo(QImage::Format_ARGB32);
else
- *this = std::move(*this).convertToFormat(QImage::Format_RGB32);
+ convertTo(QImage::Format_RGB32);
}
QColorTransformPrivate::TransformFlags flags = QColorTransformPrivate::Unpremultiplied;
switch (format()) {
case Format_ARGB32_Premultiplied:
case Format_RGBA64_Premultiplied:
+ case Format_RGBA32FPx4_Premultiplied:
flags = QColorTransformPrivate::Premultiplied;
break;
+ case Format_Grayscale8:
+ case Format_Grayscale16:
case Format_RGB32:
+ case Format_CMYK8888:
case Format_RGBX64:
+ case Format_RGBX32FPx4:
flags = QColorTransformPrivate::InputOpaque;
break;
case Format_ARGB32:
case Format_RGBA64:
+ case Format_RGBA32FPx4:
break;
default:
Q_UNREACHABLE();
@@ -4841,26 +5235,54 @@ void QImage::applyColorTransform(const QColorTransform &transform)
std::function<void(int,int)> transformSegment;
- if (depth() > 32) {
+ if (format() == Format_Grayscale8) {
transformSegment = [&](int yStart, int yEnd) {
for (int y = yStart; y < yEnd; ++y) {
- QRgba64 *scanline = reinterpret_cast<QRgba64 *>(scanLine(y));
- transform.d->apply(scanline, scanline, width(), flags);
+ uint8_t *scanline = reinterpret_cast<uint8_t *>(d->data + y * d->bytes_per_line);
+ QColorTransformPrivate::get(transform)->applyGray(scanline, scanline, width(), flags);
+ }
+ };
+ } else if (format() == Format_Grayscale16) {
+ transformSegment = [&](int yStart, int yEnd) {
+ for (int y = yStart; y < yEnd; ++y) {
+ uint16_t *scanline = reinterpret_cast<uint16_t *>(d->data + y * d->bytes_per_line);
+ QColorTransformPrivate::get(transform)->applyGray(scanline, scanline, width(), flags);
+ }
+ };
+ } else if (qt_fpColorPrecision(format())) {
+ transformSegment = [&](int yStart, int yEnd) {
+ for (int y = yStart; y < yEnd; ++y) {
+ QRgbaFloat32 *scanline = reinterpret_cast<QRgbaFloat32 *>(d->data + y * d->bytes_per_line);
+ QColorTransformPrivate::get(transform)->apply(scanline, scanline, width(), flags);
+ }
+ };
+ } else if (depth() > 32) {
+ transformSegment = [&](int yStart, int yEnd) {
+ for (int y = yStart; y < yEnd; ++y) {
+ QRgba64 *scanline = reinterpret_cast<QRgba64 *>(d->data + y * d->bytes_per_line);
+ QColorTransformPrivate::get(transform)->apply(scanline, scanline, width(), flags);
+ }
+ };
+ } else if (oldFormat == QImage::Format_CMYK8888) {
+ transformSegment = [&](int yStart, int yEnd) {
+ for (int y = yStart; y < yEnd; ++y) {
+ QCmyk32 *scanline = reinterpret_cast<QCmyk32 *>(d->data + y * d->bytes_per_line);
+ QColorTransformPrivate::get(transform)->apply(scanline, scanline, width(), flags);
}
};
} else {
transformSegment = [&](int yStart, int yEnd) {
for (int y = yStart; y < yEnd; ++y) {
- QRgb *scanline = reinterpret_cast<QRgb *>(scanLine(y));
- transform.d->apply(scanline, scanline, width(), flags);
+ QRgb *scanline = reinterpret_cast<QRgb *>(d->data + y * d->bytes_per_line);
+ QColorTransformPrivate::get(transform)->apply(scanline, scanline, width(), flags);
}
};
}
#if QT_CONFIG(thread) && !defined(Q_OS_WASM)
- int segments = sizeInBytes() / (1<<16);
+ int segments = (qsizetype(width()) * height()) >> 16;
segments = std::min(segments, height());
- QThreadPool *threadPool = QThreadPool::globalInstance();
+ QThreadPool *threadPool = QThreadPoolPrivate::qtGuiInstance();
if (segments > 1 && threadPool && !threadPool->contains(QThread::currentThread())) {
QSemaphore semaphore;
int y = 0;
@@ -4881,6 +5303,549 @@ void QImage::applyColorTransform(const QColorTransform &transform)
*this = std::move(*this).convertToFormat(oldFormat);
}
+/*!
+ \since 6.8
+
+ Applies the color transformation \a transform to all pixels in the image, and converts the format of the image to \a toFormat.
+
+ The specified image conversion \a flags control how the image data
+ is handled during the format conversion process.
+*/
+void QImage::applyColorTransform(const QColorTransform &transform, QImage::Format toFormat, Qt::ImageConversionFlags flags)
+{
+ if (!d)
+ return;
+ if (transform.isIdentity())
+ return convertTo(toFormat, flags);
+
+ *this = colorTransformed(transform, toFormat, flags);
+}
+
+/*!
+ \since 6.4
+
+ Returns the image color transformed using \a transform on all pixels in the image.
+
+ \note If \a transform has a source color space which is incompatible with the format of this image,
+ returns a null QImage. If \a transform has a target color space which is incompatible with the format
+ of this image, the image will also be converted to a compatible format. For more control about the
+ choice of the target pixel format, see the three argument overload of this method.
+
+ \sa applyColorTransform()
+*/
+QImage QImage::colorTransformed(const QColorTransform &transform) const &
+{
+ if (!d)
+ return QImage();
+ if (transform.isIdentity())
+ return *this;
+
+ QColorSpace::ColorModel inColorModel = QColorTransformPrivate::get(transform)->colorSpaceIn->colorModel;
+ QColorSpace::ColorModel outColorModel = QColorTransformPrivate::get(transform)->colorSpaceOut->colorModel;
+ if (!qt_compatibleColorModel(pixelFormat().colorModel(), inColorModel)) {
+ qWarning() << "QImage::colorTransformed: Invalid input color space for transform";
+ return QImage();
+ }
+ if (!qt_compatibleColorModel(pixelFormat().colorModel(), outColorModel)) {
+ // All model switching transforms are opaque in at least one end.
+ switch (outColorModel) {
+ case QColorSpace::ColorModel::Rgb:
+ return colorTransformed(transform, qt_highColorPrecision(format(), true) ? QImage::Format_RGBX64 : QImage::Format_RGB32);
+ case QColorSpace::ColorModel::Gray:
+ return colorTransformed(transform, qt_highColorPrecision(format(), true) ? QImage::Format_Grayscale16 : QImage::Format_Grayscale8);
+ case QColorSpace::ColorModel::Cmyk:
+ return colorTransformed(transform, QImage::Format_CMYK8888);
+ case QColorSpace::ColorModel::Undefined:
+ break;
+ }
+ return QImage();
+ }
+
+ QImage image = copy();
+ image.applyColorTransform(transform);
+ return image;
+}
+
+static bool isRgb32Data(QImage::Format f)
+{
+ switch (f) {
+ case QImage::Format_RGB32:
+ case QImage::Format_ARGB32:
+ case QImage::Format_ARGB32_Premultiplied:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+static bool isRgb64Data(QImage::Format f)
+{
+ switch (f) {
+ case QImage::Format_RGBX64:
+ case QImage::Format_RGBA64:
+ case QImage::Format_RGBA64_Premultiplied:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+static bool isRgb32fpx4Data(QImage::Format f)
+{
+ switch (f) {
+ case QImage::Format_RGBX32FPx4:
+ case QImage::Format_RGBA32FPx4:
+ case QImage::Format_RGBA32FPx4_Premultiplied:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+/*!
+ \since 6.8
+
+ Returns the image color transformed using \a transform on all pixels in the image, returning an image of format \a toFormat.
+
+ The specified image conversion \a flags control how the image data
+ is handled during the format conversion process.
+
+ \note If \a transform has a source color space which is incompatible with the format of this image,
+ or a target color space that is incompatible with \a toFormat, returns a null QImage.
+
+ \sa applyColorTransform()
+*/
+QImage QImage::colorTransformed(const QColorTransform &transform, QImage::Format toFormat, Qt::ImageConversionFlags flags) const &
+{
+ if (!d)
+ return QImage();
+ if (toFormat == QImage::Format_Invalid)
+ toFormat = format();
+ if (transform.isIdentity())
+ return convertedTo(toFormat, flags);
+
+ QColorSpace::ColorModel inColorModel = QColorTransformPrivate::get(transform)->colorSpaceIn->colorModel;
+ QColorSpace::ColorModel outColorModel = QColorTransformPrivate::get(transform)->colorSpaceOut->colorModel;
+ if (!qt_compatibleColorModel(pixelFormat().colorModel(), inColorModel)) {
+ qWarning() << "QImage::colorTransformed: Invalid input color space for transform";
+ return QImage();
+ }
+ if (!qt_compatibleColorModel(toPixelFormat(toFormat).colorModel(), outColorModel)) {
+ qWarning() << "QImage::colorTransformed: Invalid output color space for transform";
+ return QImage();
+ }
+
+ QImage fromImage = *this;
+
+ QImage::Format tmpFormat = toFormat;
+ switch (toFormat) {
+ case QImage::Format_RGB32:
+ case QImage::Format_ARGB32:
+ case QImage::Format_ARGB32_Premultiplied:
+ case QImage::Format_RGBX32FPx4:
+ case QImage::Format_RGBA32FPx4:
+ case QImage::Format_RGBA32FPx4_Premultiplied:
+ case QImage::Format_RGBX64:
+ case QImage::Format_RGBA64:
+ case QImage::Format_RGBA64_Premultiplied:
+ case QImage::Format_Grayscale8:
+ case QImage::Format_Grayscale16:
+ case QImage::Format_CMYK8888:
+ // can be output natively
+ break;
+ case QImage::Format_RGB16:
+ case QImage::Format_RGB444:
+ case QImage::Format_RGB555:
+ case QImage::Format_RGB666:
+ case QImage::Format_RGB888:
+ case QImage::Format_BGR888:
+ case QImage::Format_RGBX8888:
+ tmpFormat = QImage::Format_RGB32;
+ break;
+ case QImage::Format_Mono:
+ case QImage::Format_MonoLSB:
+ case QImage::Format_Indexed8:
+ case QImage::Format_ARGB8565_Premultiplied:
+ case QImage::Format_ARGB6666_Premultiplied:
+ case QImage::Format_ARGB8555_Premultiplied:
+ case QImage::Format_ARGB4444_Premultiplied:
+ case QImage::Format_RGBA8888:
+ case QImage::Format_RGBA8888_Premultiplied:
+ tmpFormat = QImage::Format_ARGB32;
+ break;
+ case QImage::Format_BGR30:
+ case QImage::Format_RGB30:
+ tmpFormat = QImage::Format_RGBX64;
+ break;
+ case QImage::Format_A2BGR30_Premultiplied:
+ case QImage::Format_A2RGB30_Premultiplied:
+ tmpFormat = QImage::Format_RGBA64;
+ break;
+ case QImage::Format_RGBX16FPx4:
+ case QImage::Format_RGBA16FPx4:
+ case QImage::Format_RGBA16FPx4_Premultiplied:
+ tmpFormat = QImage::Format_RGBA32FPx4;
+ break;
+ case QImage::Format_Alpha8:
+ return convertedTo(QImage::Format_Alpha8);
+ case QImage::Format_Invalid:
+ case QImage::NImageFormats:
+ Q_UNREACHABLE();
+ break;
+ }
+ QColorSpace::ColorModel inColorData = qt_csColorData(pixelFormat().colorModel());
+ QColorSpace::ColorModel outColorData = qt_csColorData(toPixelFormat(toFormat).colorModel());
+ // Ensure only precision increasing transforms
+ if (inColorData != outColorData) {
+ if (fromImage.format() == QImage::Format_Grayscale8 && outColorData == QColorSpace::ColorModel::Rgb)
+ tmpFormat = QImage::Format_RGB32;
+ else if (tmpFormat == QImage::Format_Grayscale8 && qt_highColorPrecision(fromImage.format()))
+ tmpFormat = QImage::Format_Grayscale16;
+ else if (fromImage.format() == QImage::Format_Grayscale16 && outColorData == QColorSpace::ColorModel::Rgb)
+ tmpFormat = QImage::Format_RGBX64;
+ } else {
+ if (tmpFormat == QImage::Format_Grayscale8 && fromImage.format() == QImage::Format_Grayscale16)
+ tmpFormat = QImage::Format_Grayscale16;
+ else if (qt_fpColorPrecision(fromImage.format()) && !qt_fpColorPrecision(tmpFormat))
+ tmpFormat = QImage::Format_RGBA32FPx4;
+ else if (isRgb32Data(tmpFormat) && qt_highColorPrecision(fromImage.format(), true))
+ tmpFormat = QImage::Format_RGBA64;
+ }
+
+ QImage toImage(size(), tmpFormat);
+ copyMetadata(&toImage, *this);
+
+ std::function<void(int, int)> transformSegment;
+ QColorTransformPrivate::TransformFlags transFlags = QColorTransformPrivate::Unpremultiplied;
+
+ if (inColorData != outColorData) {
+ // Needs color model switching transform
+ if (inColorData == QColorSpace::ColorModel::Gray && outColorData == QColorSpace::ColorModel::Rgb) {
+ // Gray -> RGB
+ if (format() == QImage::Format_Grayscale8) {
+ transformSegment = [&](int yStart, int yEnd) {
+ for (int y = yStart; y < yEnd; ++y) {
+ const quint8 *in_scanline = reinterpret_cast<const quint8 *>(d->data + y * d->bytes_per_line);
+ QRgb *out_scanline = reinterpret_cast<QRgb *>(toImage.d->data + y * toImage.bytesPerLine());
+ QColorTransformPrivate::get(transform)->applyGray(out_scanline, in_scanline, width(), QColorTransformPrivate::InputOpaque);
+ }
+ };
+ } else {
+ transformSegment = [&](int yStart, int yEnd) {
+ for (int y = yStart; y < yEnd; ++y) {
+ const quint16 *in_scanline = reinterpret_cast<const quint16 *>(d->data + y * d->bytes_per_line);
+ QRgba64 *out_scanline = reinterpret_cast<QRgba64 *>(toImage.d->data + y * toImage.bytesPerLine());
+ QColorTransformPrivate::get(transform)->applyGray(out_scanline, in_scanline, width(), QColorTransformPrivate::InputOpaque);
+ }
+ };
+ }
+ } else if (inColorData == QColorSpace::ColorModel::Gray && outColorData == QColorSpace::ColorModel::Cmyk) {
+ // Gray -> CMYK
+ if (format() == QImage::Format_Grayscale8) {
+ transformSegment = [&](int yStart, int yEnd) {
+ for (int y = yStart; y < yEnd; ++y) {
+ const quint8 *in_scanline = reinterpret_cast<const quint8 *>(d->data + y * d->bytes_per_line);
+ QCmyk32 *out_scanline = reinterpret_cast<QCmyk32 *>(toImage.d->data + y * toImage.bytesPerLine());
+ QColorTransformPrivate::get(transform)->applyGray(out_scanline, in_scanline, width(), QColorTransformPrivate::InputOpaque);
+ }
+ };
+ } else {
+ transformSegment = [&](int yStart, int yEnd) {
+ for (int y = yStart; y < yEnd; ++y) {
+ const quint16 *in_scanline = reinterpret_cast<const quint16 *>(d->data + y * d->bytes_per_line);
+ QCmyk32 *out_scanline = reinterpret_cast<QCmyk32 *>(toImage.d->data + y * toImage.bytesPerLine());
+ QColorTransformPrivate::get(transform)->applyGray(out_scanline, in_scanline, width(), QColorTransformPrivate::InputOpaque);
+ }
+ };
+ }
+ } else if (inColorData == QColorSpace::ColorModel::Rgb && outColorData == QColorSpace::ColorModel::Gray) {
+ // RGB -> Gray
+ if (tmpFormat == QImage::Format_Grayscale8) {
+ fromImage.convertTo(QImage::Format_RGB32);
+ transformSegment = [&](int yStart, int yEnd) {
+ for (int y = yStart; y < yEnd; ++y) {
+ const QRgb *in_scanline = reinterpret_cast<const QRgb *>(fromImage.constBits() + y * fromImage.bytesPerLine());
+ quint8 *out_scanline = reinterpret_cast<quint8 *>(toImage.d->data + y * toImage.bytesPerLine());
+ QColorTransformPrivate::get(transform)->applyReturnGray(out_scanline, in_scanline, width(), QColorTransformPrivate::InputOpaque);
+ }
+ };
+ } else {
+ fromImage.convertTo(QImage::Format_RGBX64);
+ transformSegment = [&](int yStart, int yEnd) {
+ for (int y = yStart; y < yEnd; ++y) {
+ const QRgba64 *in_scanline = reinterpret_cast<const QRgba64 *>(fromImage.constBits() + y * fromImage.bytesPerLine());
+ quint16 *out_scanline = reinterpret_cast<quint16 *>(toImage.d->data + y * toImage.bytesPerLine());
+ QColorTransformPrivate::get(transform)->applyReturnGray(out_scanline, in_scanline, width(), QColorTransformPrivate::InputOpaque);
+ }
+ };
+ }
+ } else if (inColorData == QColorSpace::ColorModel::Cmyk && outColorData == QColorSpace::ColorModel::Gray) {
+ // CMYK -> Gray
+ if (tmpFormat == QImage::Format_Grayscale8) {
+ transformSegment = [&](int yStart, int yEnd) {
+ for (int y = yStart; y < yEnd; ++y) {
+ const QCmyk32 *in_scanline = reinterpret_cast<const QCmyk32 *>(fromImage.constBits() + y * fromImage.bytesPerLine());
+ quint8 *out_scanline = reinterpret_cast<quint8 *>(toImage.d->data + y * toImage.bytesPerLine());
+ QColorTransformPrivate::get(transform)->applyReturnGray(out_scanline, in_scanline, width(), QColorTransformPrivate::InputOpaque);
+ }
+ };
+ } else {
+ transformSegment = [&](int yStart, int yEnd) {
+ for (int y = yStart; y < yEnd; ++y) {
+ const QCmyk32 *in_scanline = reinterpret_cast<const QCmyk32 *>(fromImage.constBits() + y * fromImage.bytesPerLine());
+ quint16 *out_scanline = reinterpret_cast<quint16 *>(toImage.d->data + y * toImage.bytesPerLine());
+ QColorTransformPrivate::get(transform)->applyReturnGray(out_scanline, in_scanline, width(), QColorTransformPrivate::InputOpaque);
+ }
+ };
+ }
+ } else if (inColorData == QColorSpace::ColorModel::Cmyk && outColorData == QColorSpace::ColorModel::Rgb) {
+ // CMYK -> RGB
+ if (isRgb32Data(tmpFormat) ) {
+ transformSegment = [&](int yStart, int yEnd) {
+ for (int y = yStart; y < yEnd; ++y) {
+ const QCmyk32 *in_scanline = reinterpret_cast<const QCmyk32 *>(fromImage.constBits() + y * fromImage.bytesPerLine());
+ QRgb *out_scanline = reinterpret_cast<QRgb *>(toImage.d->data + y * toImage.bytesPerLine());
+ QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), QColorTransformPrivate::InputOpaque);
+ }
+ };
+ } else if (isRgb64Data(tmpFormat)) {
+ transformSegment = [&](int yStart, int yEnd) {
+ for (int y = yStart; y < yEnd; ++y) {
+ const QCmyk32 *in_scanline = reinterpret_cast<const QCmyk32 *>(fromImage.constBits() + y * fromImage.bytesPerLine());
+ QRgba64 *out_scanline = reinterpret_cast<QRgba64 *>(toImage.d->data + y * toImage.bytesPerLine());
+ QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), QColorTransformPrivate::InputOpaque);
+ }
+ };
+ } else {
+ Q_ASSERT(isRgb32fpx4Data(tmpFormat));
+ transformSegment = [&](int yStart, int yEnd) {
+ for (int y = yStart; y < yEnd; ++y) {
+ const QCmyk32 *in_scanline = reinterpret_cast<const QCmyk32 *>(fromImage.constBits() + y * fromImage.bytesPerLine());
+ QRgbaFloat32 *out_scanline = reinterpret_cast<QRgbaFloat32 *>(toImage.d->data + y * toImage.bytesPerLine());
+ QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), QColorTransformPrivate::InputOpaque);
+ }
+ };
+ }
+ } else if (inColorData == QColorSpace::ColorModel::Rgb && outColorData == QColorSpace::ColorModel::Cmyk) {
+ // RGB -> CMYK
+ if (!fromImage.hasAlphaChannel())
+ transFlags = QColorTransformPrivate::InputOpaque;
+ else if (qPixelLayouts[fromImage.format()].premultiplied)
+ transFlags = QColorTransformPrivate::Premultiplied;
+ if (isRgb32Data(fromImage.format()) ) {
+ transformSegment = [&](int yStart, int yEnd) {
+ for (int y = yStart; y < yEnd; ++y) {
+ const QRgb *in_scanline = reinterpret_cast<const QRgb *>(fromImage.constBits() + y * fromImage.bytesPerLine());
+ QCmyk32 *out_scanline = reinterpret_cast<QCmyk32 *>(toImage.d->data + y * toImage.bytesPerLine());
+ QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), transFlags);
+ }
+ };
+ } else if (isRgb64Data(fromImage.format())) {
+ transformSegment = [&](int yStart, int yEnd) {
+ for (int y = yStart; y < yEnd; ++y) {
+ const QRgba64 *in_scanline = reinterpret_cast<const QRgba64 *>(fromImage.constBits() + y * fromImage.bytesPerLine());
+ QCmyk32 *out_scanline = reinterpret_cast<QCmyk32 *>(toImage.d->data + y * toImage.bytesPerLine());
+ QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), transFlags);
+ }
+ };
+ } else {
+ Q_ASSERT(isRgb32fpx4Data(fromImage.format()));
+ transformSegment = [&](int yStart, int yEnd) {
+ for (int y = yStart; y < yEnd; ++y) {
+ const QRgbaFloat32 *in_scanline = reinterpret_cast<const QRgbaFloat32 *>(fromImage.constBits() + y * fromImage.bytesPerLine());
+ QCmyk32 *out_scanline = reinterpret_cast<QCmyk32 *>(toImage.d->data + y * toImage.bytesPerLine());
+ QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), transFlags);
+ }
+ };
+ }
+ } else {
+ Q_UNREACHABLE();
+ }
+ } else {
+ // Conversion on same color model
+ if (pixelFormat().colorModel() == QPixelFormat::Indexed) {
+ for (int i = 0; i < d->colortable.size(); ++i)
+ fromImage.d->colortable[i] = transform.map(d->colortable[i]);
+ return fromImage.convertedTo(toFormat, flags);
+ }
+
+ QImage::Format oldFormat = format();
+ if (qt_fpColorPrecision(oldFormat)) {
+ if (oldFormat != QImage::Format_RGBX32FPx4 && oldFormat != QImage::Format_RGBA32FPx4
+ && oldFormat != QImage::Format_RGBA32FPx4_Premultiplied)
+ fromImage.convertTo(QImage::Format_RGBA32FPx4);
+ } else if (qt_highColorPrecision(oldFormat, true)) {
+ if (oldFormat != QImage::Format_RGBX64 && oldFormat != QImage::Format_RGBA64
+ && oldFormat != QImage::Format_RGBA64_Premultiplied && oldFormat != QImage::Format_Grayscale16)
+ fromImage.convertTo(QImage::Format_RGBA64);
+ } else if (oldFormat != QImage::Format_ARGB32 && oldFormat != QImage::Format_RGB32
+ && oldFormat != QImage::Format_ARGB32_Premultiplied && oldFormat != QImage::Format_CMYK8888
+ && oldFormat != QImage::Format_Grayscale8 && oldFormat != QImage::Format_Grayscale16) {
+ if (hasAlphaChannel())
+ fromImage.convertTo(QImage::Format_ARGB32);
+ else
+ fromImage.convertTo(QImage::Format_RGB32);
+ }
+
+ if (!fromImage.hasAlphaChannel())
+ transFlags = QColorTransformPrivate::InputOpaque;
+ else if (qPixelLayouts[fromImage.format()].premultiplied)
+ transFlags = QColorTransformPrivate::Premultiplied;
+
+ if (fromImage.format() == Format_Grayscale8) {
+ transformSegment = [&](int yStart, int yEnd) {
+ for (int y = yStart; y < yEnd; ++y) {
+ const quint8 *in_scanline = reinterpret_cast<const quint8 *>(fromImage.constBits() + y * fromImage.bytesPerLine());
+ if (tmpFormat == Format_Grayscale8) {
+ quint8 *out_scanline = reinterpret_cast<quint8 *>(toImage.d->data + y * toImage.bytesPerLine());
+ QColorTransformPrivate::get(transform)->applyGray(out_scanline, in_scanline, width(), transFlags);
+ } else {
+ Q_ASSERT(tmpFormat == Format_Grayscale16);
+ quint16 *out_scanline = reinterpret_cast<quint16 *>(toImage.d->data + y * toImage.bytesPerLine());
+ QColorTransformPrivate::get(transform)->applyGray(out_scanline, in_scanline, width(), transFlags);
+ }
+ }
+ };
+ } else if (fromImage.format() == Format_Grayscale16) {
+ transformSegment = [&](int yStart, int yEnd) {
+ for (int y = yStart; y < yEnd; ++y) {
+ const quint16 *in_scanline = reinterpret_cast<const quint16 *>(fromImage.constBits() + y * fromImage.bytesPerLine());
+ quint16 *out_scanline = reinterpret_cast<quint16 *>(toImage.d->data + y * toImage.bytesPerLine());
+ QColorTransformPrivate::get(transform)->applyGray(out_scanline, in_scanline, width(), transFlags);
+ }
+ };
+ } else if (fromImage.format() == Format_CMYK8888) {
+ Q_ASSERT(tmpFormat == Format_CMYK8888);
+ transformSegment = [&](int yStart, int yEnd) {
+ for (int y = yStart; y < yEnd; ++y) {
+ const QCmyk32 *in_scanline = reinterpret_cast<const QCmyk32 *>(fromImage.constBits() + y * fromImage.bytesPerLine());
+ QCmyk32 *out_scanline = reinterpret_cast<QCmyk32 *>(toImage.d->data + y * toImage.bytesPerLine());
+ QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), transFlags);
+ }
+ };
+ } else if (isRgb32fpx4Data(fromImage.format())) {
+ Q_ASSERT(isRgb32fpx4Data(tmpFormat));
+ transformSegment = [&](int yStart, int yEnd) {
+ for (int y = yStart; y < yEnd; ++y) {
+ const QRgbaFloat32 *in_scanline = reinterpret_cast<const QRgbaFloat32 *>(fromImage.constBits() + y * fromImage.bytesPerLine());
+ QRgbaFloat32 *out_scanline = reinterpret_cast<QRgbaFloat32 *>(toImage.d->data + y * toImage.bytesPerLine());
+ QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), transFlags);
+ }
+ };
+ } else if (isRgb64Data(fromImage.format())) {
+ transformSegment = [&](int yStart, int yEnd) {
+ for (int y = yStart; y < yEnd; ++y) {
+ const QRgba64 *in_scanline = reinterpret_cast<const QRgba64 *>(fromImage.constBits() + y * fromImage.bytesPerLine());
+ if (isRgb32fpx4Data(tmpFormat)) {
+ QRgbaFloat32 *out_scanline = reinterpret_cast<QRgbaFloat32 *>(toImage.d->data + y * toImage.bytesPerLine());
+ QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), transFlags);
+ } else {
+ Q_ASSERT(isRgb64Data(tmpFormat));
+ QRgba64 *out_scanline = reinterpret_cast<QRgba64 *>(toImage.d->data + y * toImage.bytesPerLine());
+ QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), transFlags);
+ }
+ }
+ };
+ } else {
+ transformSegment = [&](int yStart, int yEnd) {
+ for (int y = yStart; y < yEnd; ++y) {
+ const QRgb *in_scanline = reinterpret_cast<const QRgb *>(fromImage.constBits() + y * fromImage.bytesPerLine());
+ if (isRgb32fpx4Data(tmpFormat)) {
+ QRgbaFloat32 *out_scanline = reinterpret_cast<QRgbaFloat32 *>(toImage.d->data + y * toImage.bytesPerLine());
+ QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), transFlags);
+ } else if (isRgb64Data(tmpFormat)) {
+ QRgba64 *out_scanline = reinterpret_cast<QRgba64 *>(toImage.d->data + y * toImage.bytesPerLine());
+ QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), transFlags);
+ } else {
+ Q_ASSERT(isRgb32Data(tmpFormat));
+ QRgb *out_scanline = reinterpret_cast<QRgb *>(toImage.d->data + y * toImage.bytesPerLine());
+ QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), transFlags);
+ }
+ }
+ };
+ }
+ }
+
+#if QT_CONFIG(thread) && !defined(Q_OS_WASM)
+ int segments = (qsizetype(width()) * height()) >> 16;
+ segments = std::min(segments, height());
+ QThreadPool *threadPool = QThreadPoolPrivate::qtGuiInstance();
+ if (segments > 1 && threadPool && !threadPool->contains(QThread::currentThread())) {
+ QSemaphore semaphore;
+ int y = 0;
+ for (int i = 0; i < segments; ++i) {
+ int yn = (height() - y) / (segments - i);
+ threadPool->start([&, y, yn]() {
+ transformSegment(y, y + yn);
+ semaphore.release(1);
+ });
+ y += yn;
+ }
+ semaphore.acquire(segments);
+ } else
+#endif
+ transformSegment(0, height());
+
+ if (tmpFormat != toFormat)
+ toImage.convertTo(toFormat);
+
+ return toImage;
+}
+
+/*!
+ \since 6.4
+ \overload
+
+ Returns the image color transformed using \a transform on all pixels in the image.
+
+ \sa applyColorTransform()
+*/
+QImage QImage::colorTransformed(const QColorTransform &transform) &&
+{
+ if (!d)
+ return QImage();
+
+ QColorSpace::ColorModel inColorModel = QColorTransformPrivate::get(transform)->colorSpaceIn->colorModel;
+ QColorSpace::ColorModel outColorModel = QColorTransformPrivate::get(transform)->colorSpaceOut->colorModel;
+ if (!qt_compatibleColorModel(pixelFormat().colorModel(), inColorModel)) {
+ qWarning() << "QImage::colorTransformed: Invalid input color space for transform";
+ return QImage();
+ }
+ if (!qt_compatibleColorModel(pixelFormat().colorModel(), outColorModel)) {
+ // There is currently no inplace conversion of both colorspace and format, so just use the normal version.
+ switch (outColorModel) {
+ case QColorSpace::ColorModel::Rgb:
+ return colorTransformed(transform, qt_highColorPrecision(format(), true) ? QImage::Format_RGBX64 : QImage::Format_RGB32);
+ case QColorSpace::ColorModel::Gray:
+ return colorTransformed(transform, qt_highColorPrecision(format(), true) ? QImage::Format_Grayscale16 : QImage::Format_Grayscale8);
+ case QColorSpace::ColorModel::Cmyk:
+ return colorTransformed(transform, QImage::Format_CMYK8888);
+ case QColorSpace::ColorModel::Undefined:
+ break;
+ }
+ return QImage();
+ }
+
+ applyColorTransform(transform);
+ return std::move(*this);
+}
+
+/*!
+ \since 6.8
+ \overload
+
+ Returns the image color transformed using \a transform on all pixels in the image.
+
+ \sa applyColorTransform()
+*/
+QImage QImage::colorTransformed(const QColorTransform &transform, QImage::Format format, Qt::ImageConversionFlags flags) &&
+{
+ // There is currently no inplace conversion of both colorspace and format, so just use the normal version.
+ return colorTransformed(transform, format, flags);
+}
bool QImageData::convertInPlace(QImage::Format newFormat, Qt::ImageConversionFlags flags)
{
@@ -4899,6 +5864,10 @@ bool QImageData::convertInPlace(QImage::Format newFormat, Qt::ImageConversionFla
// any direct ones are probably better even if not inplace.
if (qt_highColorPrecision(newFormat, !qPixelLayouts[newFormat].hasAlphaChannel)
&& qt_highColorPrecision(format, !qPixelLayouts[format].hasAlphaChannel)) {
+#if QT_CONFIG(raster_fp)
+ if (qt_fpColorPrecision(format) && qt_fpColorPrecision(newFormat))
+ return convert_generic_inplace_over_rgba32f(this, newFormat, flags);
+#endif
return convert_generic_inplace_over_rgb64(this, newFormat, flags);
}
return convert_generic_inplace(this, newFormat, flags);
@@ -5324,6 +6293,97 @@ static constexpr QPixelFormat pixelformats[] = {
/*PREMULTIPLIED*/ QPixelFormat::NotPremultiplied,
/*INTERPRETATION*/ QPixelFormat::UnsignedByte,
/*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian),
+ //QImage::Format_RGBX16FPx4:
+ QPixelFormat(QPixelFormat::RGB,
+ /*RED*/ 16,
+ /*GREEN*/ 16,
+ /*BLUE*/ 16,
+ /*FOURTH*/ 0,
+ /*FIFTH*/ 0,
+ /*ALPHA*/ 16,
+ /*ALPHA USAGE*/ QPixelFormat::IgnoresAlpha,
+ /*ALPHA POSITION*/ QPixelFormat::AtEnd,
+ /*PREMULTIPLIED*/ QPixelFormat::NotPremultiplied,
+ /*INTERPRETATION*/ QPixelFormat::FloatingPoint,
+ /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian),
+ //QImage::Format_RGBA16FPx4:
+ QPixelFormat(QPixelFormat::RGB,
+ /*RED*/ 16,
+ /*GREEN*/ 16,
+ /*BLUE*/ 16,
+ /*FOURTH*/ 0,
+ /*FIFTH*/ 0,
+ /*ALPHA*/ 16,
+ /*ALPHA USAGE*/ QPixelFormat::UsesAlpha,
+ /*ALPHA POSITION*/ QPixelFormat::AtEnd,
+ /*PREMULTIPLIED*/ QPixelFormat::NotPremultiplied,
+ /*INTERPRETATION*/ QPixelFormat::FloatingPoint,
+ /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian),
+ //QImage::Format_RGBA16FPx4_Premultiplied:
+ QPixelFormat(QPixelFormat::RGB,
+ /*RED*/ 16,
+ /*GREEN*/ 16,
+ /*BLUE*/ 16,
+ /*FOURTH*/ 0,
+ /*FIFTH*/ 0,
+ /*ALPHA*/ 16,
+ /*ALPHA USAGE*/ QPixelFormat::UsesAlpha,
+ /*ALPHA POSITION*/ QPixelFormat::AtEnd,
+ /*PREMULTIPLIED*/ QPixelFormat::Premultiplied,
+ /*INTERPRETATION*/ QPixelFormat::FloatingPoint,
+ /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian),
+ //QImage::Format_RGBX32FPx4:
+ QPixelFormat(QPixelFormat::RGB,
+ /*RED*/ 32,
+ /*GREEN*/ 32,
+ /*BLUE*/ 32,
+ /*FOURTH*/ 0,
+ /*FIFTH*/ 0,
+ /*ALPHA*/ 32,
+ /*ALPHA USAGE*/ QPixelFormat::IgnoresAlpha,
+ /*ALPHA POSITION*/ QPixelFormat::AtEnd,
+ /*PREMULTIPLIED*/ QPixelFormat::NotPremultiplied,
+ /*INTERPRETATION*/ QPixelFormat::FloatingPoint,
+ /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian),
+ //QImage::Format_RGBA32FPx4:
+ QPixelFormat(QPixelFormat::RGB,
+ /*RED*/ 32,
+ /*GREEN*/ 32,
+ /*BLUE*/ 32,
+ /*FOURTH*/ 0,
+ /*FIFTH*/ 0,
+ /*ALPHA*/ 32,
+ /*ALPHA USAGE*/ QPixelFormat::UsesAlpha,
+ /*ALPHA POSITION*/ QPixelFormat::AtEnd,
+ /*PREMULTIPLIED*/ QPixelFormat::NotPremultiplied,
+ /*INTERPRETATION*/ QPixelFormat::FloatingPoint,
+ /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian),
+ //QImage::Format_RGBA32FPx4_Premultiplied:
+ QPixelFormat(QPixelFormat::RGB,
+ /*RED*/ 32,
+ /*GREEN*/ 32,
+ /*BLUE*/ 32,
+ /*FOURTH*/ 0,
+ /*FIFTH*/ 0,
+ /*ALPHA*/ 32,
+ /*ALPHA USAGE*/ QPixelFormat::UsesAlpha,
+ /*ALPHA POSITION*/ QPixelFormat::AtEnd,
+ /*PREMULTIPLIED*/ QPixelFormat::Premultiplied,
+ /*INTERPRETATION*/ QPixelFormat::FloatingPoint,
+ /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian),
+ //QImage::Format_CMYK8888:
+ QPixelFormat(QPixelFormat::CMYK,
+ /*RED*/ 8,
+ /*GREEN*/ 8,
+ /*BLUE*/ 8,
+ /*FOURTH*/ 8,
+ /*FIFTH*/ 0,
+ /*ALPHA*/ 0,
+ /*ALPHA USAGE*/ QPixelFormat::IgnoresAlpha,
+ /*ALPHA POSITION*/ QPixelFormat::AtBeginning,
+ /*PREMULTIPLIED*/ QPixelFormat::NotPremultiplied,
+ /*INTERPRETATION*/ QPixelFormat::UnsignedInteger,
+ /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian),
};
static_assert(sizeof(pixelformats) / sizeof(*pixelformats) == QImage::NImageFormats);
@@ -5340,7 +6400,7 @@ QPixelFormat QImage::pixelFormat() const noexcept
*/
QPixelFormat QImage::toPixelFormat(QImage::Format format) noexcept
{
- Q_ASSERT(static_cast<int>(format) < NImageFormats);
+ Q_ASSERT(static_cast<int>(format) < NImageFormats && static_cast<int>(format) >= 0);
return pixelformats[format];
}
@@ -5384,12 +6444,11 @@ QMap<QString, QString> qt_getImageText(const QImage &image, const QString &descr
QMap<QString, QString> qt_getImageTextFromDescription(const QString &description)
{
QMap<QString, QString> text;
- const auto pairs = QStringView{description}.split(u"\n\n");
- for (const auto &pair : pairs) {
- int index = pair.indexOf(QLatin1Char(':'));
- if (index >= 0 && pair.indexOf(QLatin1Char(' ')) < index) {
+ for (const auto &pair : QStringView{description}.tokenize(u"\n\n")) {
+ int index = pair.indexOf(u':');
+ if (index >= 0 && pair.indexOf(u' ') < index) {
if (!pair.trimmed().isEmpty())
- text.insert(QLatin1String("Description"), pair.toString().simplified());
+ text.insert("Description"_L1, pair.toString().simplified());
} else {
const auto key = pair.left(index);
if (!key.trimmed().isEmpty())
@@ -5400,3 +6459,5 @@ QMap<QString, QString> qt_getImageTextFromDescription(const QString &description
}
QT_END_NAMESPACE
+
+#include "moc_qimage.cpp"