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.cpp226
1 files changed, 165 insertions, 61 deletions
diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp
index 5027a09a09..4f91fbb8b9 100644
--- a/src/gui/image/qimage.cpp
+++ b/src/gui/image/qimage.cpp
@@ -1,4 +1,4 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// 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"
@@ -35,8 +35,9 @@
#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>
@@ -63,6 +64,17 @@ QT_WARNING_DISABLE_MSVC(4723)
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);
@@ -70,7 +82,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);
}
@@ -94,7 +106,7 @@ 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 || format >= QImage::NImageFormats)
return nullptr; // invalid parameter(s)
@@ -604,8 +616,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}
*/
/*!
@@ -807,7 +819,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;
}
@@ -1104,6 +1116,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;
@@ -1160,7 +1194,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)
@@ -1385,7 +1419,7 @@ void QImage::setColorTable(const QList<QRgb> &colors)
{
if (!d)
return;
- detach();
+ detachMetadata(true);
// In case detach() ran out of memory
if (!d)
@@ -1459,18 +1493,20 @@ void QImage::setDevicePixelRatio(qreal scaleFactor)
if (scaleFactor == d->devicePixelRatio)
return;
- detach();
+ detachMetadata();
if (d)
d->devicePixelRatio = scaleFactor;
}
/*!
- Returns the size of the pixmap in device independent pixels.
+ Returns the size of the image in device independent pixels.
- This value should be used when using the pixmap size in user interface
+ This value should be used when using the image size in user interface
size calculations.
- The return value is equivalent to pixmap.size() / pixmap.devicePixelRatio(),
+ The return value is equivalent to image.size() / image.devicePixelRatio().
+
+ \since 6.2
*/
QSizeF QImage::deviceIndependentSize() const
{
@@ -1542,7 +1578,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)
@@ -1888,7 +1924,7 @@ void QImage::fill(const QColor &color)
if (!hasAlphaChannel())
a = 1.0f;
if (depth() == 64) {
- QRgbaFloat16 c16{r, g, b, a};
+ 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,
@@ -1974,11 +2010,11 @@ void QImage::invertPixels(InvertMode mode)
qfloat16 *p = reinterpret_cast<qfloat16 *>(d->data);
qfloat16 *end = reinterpret_cast<qfloat16 *>(d->data + d->nbytes);
while (p < end) {
- p[0] = 1.0f - p[0];
- p[1] = 1.0f - p[1];
- p[2] = 1.0f - p[2];
+ p[0] = qfloat16(1) - p[0];
+ p[1] = qfloat16(1) - p[1];
+ p[2] = qfloat16(1) - p[2];
if (mode == InvertRgba)
- p[3] = 1.0f - p[3];
+ p[3] = qfloat16(1) - p[3];
p += 4;
}
} else if (format() >= QImage::Format_RGBX32FPx4 && format() <= QImage::Format_RGBA32FPx4_Premultiplied) {
@@ -2084,7 +2120,7 @@ void QImage::setColorCount(int colorCount)
return;
}
- detach();
+ detachMetadata(true);
// In case detach() ran out of memory
if (!d)
@@ -2781,7 +2817,7 @@ void QImage::setPixelColor(int x, int y, const QColor &color)
color.getRgbF(&r, &g, &b, &a);
if (d->format == Format_RGBX16FPx4)
a = 1.0f;
- QRgbaFloat16 c16f{r, g, b, a};
+ QRgbaFloat16 c16f{qfloat16(r), qfloat16(g), qfloat16(b), qfloat16(a)};
if (d->format == Format_RGBA16FPx4_Premultiplied)
c16f = c16f.premultiplied();
((QRgbaFloat16 *)s)[x] = c16f;
@@ -2956,7 +2992,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");
@@ -2993,7 +3029,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");
@@ -3023,7 +3059,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");
@@ -3056,7 +3092,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();
@@ -3508,7 +3544,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;
@@ -3864,10 +3900,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;
}
/*****************************************************************************
@@ -4056,9 +4097,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;
@@ -4078,9 +4119,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;
@@ -4110,9 +4151,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;
@@ -4182,7 +4223,7 @@ void QImage::setText(const QString &key, const QString &value)
{
if (!d)
return;
- detach();
+ detachMetadata();
if (d)
d->text.insert(key, value);
@@ -4650,6 +4691,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());
@@ -4678,6 +4721,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());
@@ -4690,6 +4735,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());
@@ -4735,12 +4782,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();
@@ -4761,13 +4811,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.
@@ -4817,14 +4862,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());
}
}
}
@@ -4866,7 +4921,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());
@@ -4936,9 +4998,9 @@ 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;
+ detachMetadata(false);
+ if (d)
+ d->colorSpace = colorSpace;
}
/*!
@@ -4956,7 +5018,7 @@ void QImage::convertToColorSpace(const QColorSpace &colorSpace)
return;
if (!d->colorSpace.isValid())
return;
- if (!colorSpace.isValid()) {
+ if (!colorSpace.isValidTarget()) {
qWarning() << "QImage::convertToColorSpace: Output colorspace is not valid";
return;
}
@@ -4977,8 +5039,16 @@ void QImage::convertToColorSpace(const QColorSpace &colorSpace)
*/
QImage QImage::convertedToColorSpace(const QColorSpace &colorSpace) const
{
- if (!d || !d->colorSpace.isValid() || !colorSpace.isValid())
+ if (!d)
+ return QImage();
+ if (!d->colorSpace.isValid())
return QImage();
+ if (!colorSpace.isValidTarget()) {
+ qWarning() << "QImage::convertedToColorSpace: Output colorspace is not valid";
+ return QImage();
+ }
+ if (d->colorSpace == colorSpace)
+ return *this;
QImage image = copy();
image.convertToColorSpace(colorSpace);
return image;
@@ -5003,6 +5073,8 @@ QColorSpace QImage::colorSpace() const
*/
void QImage::applyColorTransform(const QColorTransform &transform)
{
+ if (transform.isIdentity())
+ return;
detach();
if (!d)
return;
@@ -5054,21 +5126,21 @@ void QImage::applyColorTransform(const QColorTransform &transform)
transformSegment = [&](int yStart, int yEnd) {
for (int y = yStart; y < yEnd; ++y) {
QRgbaFloat32 *scanline = reinterpret_cast<QRgbaFloat32 *>(d->data + y * d->bytes_per_line);
- transform.d->apply(scanline, scanline, width(), flags);
+ 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);
- transform.d->apply(scanline, scanline, width(), flags);
+ 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 *>(d->data + y * d->bytes_per_line);
- transform.d->apply(scanline, scanline, width(), flags);
+ QColorTransformPrivate::get(transform)->apply(scanline, scanline, width(), flags);
}
};
}
@@ -5076,7 +5148,7 @@ void QImage::applyColorTransform(const QColorTransform &transform)
#if QT_CONFIG(thread) && !defined(Q_OS_WASM)
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;
@@ -5097,6 +5169,39 @@ void QImage::applyColorTransform(const QColorTransform &transform)
*this = std::move(*this).convertToFormat(oldFormat);
}
+/*!
+ \since 6.4
+
+ Returns the image color transformed using \a transform on all pixels in the image.
+
+ \sa applyColorTransform()
+*/
+QImage QImage::colorTransformed(const QColorTransform &transform) const &
+{
+ if (!d || !d->colorSpace.isValid())
+ return QImage();
+ if (transform.isIdentity())
+ return *this;
+ QImage image = copy();
+ image.applyColorTransform(transform);
+ return image;
+}
+
+/*!
+ \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 || !d->colorSpace.isValid())
+ return QImage();
+ applyColorTransform(transform);
+ return std::move(*this);
+}
bool QImageData::convertInPlace(QImage::Format newFormat, Qt::ImageConversionFlags flags)
{
@@ -5682,8 +5787,7 @@ 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) {
+ 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())