summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2022-03-16 12:21:40 +0100
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2022-05-26 09:06:45 +0200
commit734c9f2df26b12b89c8a4de4ae43a15726ff1886 (patch)
tree4be65958c6b790c63cd35371485c7338cec02ae0
parent34c21d040766d54d959ed835bdf5464f657b7693 (diff)
Expand QColorTransform
Add comparison operators and an isIdentity() method to detect (1-1) transforms. [ChangeLog][QtGui] Added QColorTransform::isIdentity() method. Added QImage::colorTransformed() transitive method. Change-Id: I5fbcd14e75f2179e43e94e8c5f42cd0a5600790b Reviewed-by: Eirik Aavitsland <eirik.aavitsland@qt.io>
-rw-r--r--src/gui/image/qimage.cpp45
-rw-r--r--src/gui/image/qimage.h2
-rw-r--r--src/gui/image/qimage_conversions.cpp8
-rw-r--r--src/gui/painting/qcolormatrix_p.h4
-rw-r--r--src/gui/painting/qcolorspace.cpp5
-rw-r--r--src/gui/painting/qcolortransform.cpp74
-rw-r--r--src/gui/painting/qcolortransform.h9
-rw-r--r--src/gui/painting/qcolortransform_p.h1
-rw-r--r--tests/auto/gui/painting/qcolortransform/tst_qcolortransform.cpp48
9 files changed, 166 insertions, 30 deletions
diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp
index 5027a09a09..57900d4379 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"
@@ -4979,6 +4979,8 @@ QImage QImage::convertedToColorSpace(const QColorSpace &colorSpace) const
{
if (!d || !d->colorSpace.isValid() || !colorSpace.isValid())
return QImage();
+ if (d->colorSpace == colorSpace)
+ return *this;
QImage image = copy();
image.convertToColorSpace(colorSpace);
return image;
@@ -5003,6 +5005,8 @@ QColorSpace QImage::colorSpace() const
*/
void QImage::applyColorTransform(const QColorTransform &transform)
{
+ if (transform.isIdentity())
+ return;
detach();
if (!d)
return;
@@ -5054,21 +5058,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);
}
};
}
@@ -5097,6 +5101,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)
{
diff --git a/src/gui/image/qimage.h b/src/gui/image/qimage.h
index fd42813115..9e4abd006e 100644
--- a/src/gui/image/qimage.h
+++ b/src/gui/image/qimage.h
@@ -234,6 +234,8 @@ public:
void convertToColorSpace(const QColorSpace &);
void setColorSpace(const QColorSpace &);
+ QImage colorTransformed(const QColorTransform &transform) const &;
+ QImage colorTransformed(const QColorTransform &transform) &&;
void applyColorTransform(const QColorTransform &transform);
bool load(QIODevice *device, const char *format);
diff --git a/src/gui/image/qimage_conversions.cpp b/src/gui/image/qimage_conversions.cpp
index 433134ff7e..fa75bed3c8 100644
--- a/src/gui/image/qimage_conversions.cpp
+++ b/src/gui/image/qimage_conversions.cpp
@@ -1418,7 +1418,7 @@ static void convert_ARGB_to_gray8(QImageData *dest, const QImageData *src, Qt::I
QColorSpace fromCS = src->colorSpace.isValid() ? src->colorSpace : QColorSpace::SRgb;
QColorTransform tf = QColorSpacePrivate::get(fromCS)->transformationToXYZ();
- QColorTransformPrivate *tfd = QColorTransformPrivate::get(tf);
+ const QColorTransformPrivate *tfd = QColorTransformPrivate::get(tf);
QColorTransformPrivate::TransformFlags flags = Premultiplied
? QColorTransformPrivate::InputPremultiplied
: QColorTransformPrivate::Unpremultiplied;
@@ -1448,7 +1448,7 @@ static void convert_ARGB_to_gray16(QImageData *dest, const QImageData *src, Qt::
QColorSpace fromCS = src->colorSpace.isValid() ? src->colorSpace : QColorSpace::SRgb;
QColorTransform tf = QColorSpacePrivate::get(fromCS)->transformationToXYZ();
- QColorTransformPrivate *tfd = QColorTransformPrivate::get(tf);
+ const QColorTransformPrivate *tfd = QColorTransformPrivate::get(tf);
QColorTransformPrivate::TransformFlags flags = Premultiplied
? QColorTransformPrivate::InputPremultiplied
: QColorTransformPrivate::Unpremultiplied;
@@ -1487,7 +1487,7 @@ static void convert_RGBA64_to_gray8(QImageData *dest, const QImageData *src, Qt:
QColorSpace fromCS = src->colorSpace.isValid() ? src->colorSpace : QColorSpace::SRgb;
QColorTransform tf = QColorSpacePrivate::get(fromCS)->transformationToXYZ();
- QColorTransformPrivate *tfd = QColorTransformPrivate::get(tf);
+ const QColorTransformPrivate *tfd = QColorTransformPrivate::get(tf);
QColorTransformPrivate::TransformFlags flags = Premultiplied
? QColorTransformPrivate::InputPremultiplied
: QColorTransformPrivate::Unpremultiplied;
@@ -1526,7 +1526,7 @@ static void convert_RGBA64_to_gray16(QImageData *dest, const QImageData *src, Qt
QColorSpace fromCS = src->colorSpace.isValid() ? src->colorSpace : QColorSpace::SRgb;
QColorTransform tf = QColorSpacePrivate::get(fromCS)->transformationToXYZ();
- QColorTransformPrivate *tfd = QColorTransformPrivate::get(tf);
+ const QColorTransformPrivate *tfd = QColorTransformPrivate::get(tf);
QColorTransformPrivate::TransformFlags flags = Premultiplied
? QColorTransformPrivate::InputPremultiplied
: QColorTransformPrivate::Unpremultiplied;
diff --git a/src/gui/painting/qcolormatrix_p.h b/src/gui/painting/qcolormatrix_p.h
index cda0d8229a..1ce5df6264 100644
--- a/src/gui/painting/qcolormatrix_p.h
+++ b/src/gui/painting/qcolormatrix_p.h
@@ -101,6 +101,10 @@ public:
r.z * (b.y * g.x - g.y * b.x);
return !qFuzzyIsNull(det);
}
+ bool isIdentity() const noexcept
+ {
+ return *this == identity();
+ }
QColorMatrix inverted() const
{
diff --git a/src/gui/painting/qcolorspace.cpp b/src/gui/painting/qcolorspace.cpp
index e8ced30727..7fdfe34872 100644
--- a/src/gui/painting/qcolorspace.cpp
+++ b/src/gui/painting/qcolorspace.cpp
@@ -419,6 +419,8 @@ QColorTransform QColorSpacePrivate::transformationToColorSpace(const QColorSpace
ptr->colorSpaceIn = this;
ptr->colorSpaceOut = out;
ptr->colorMatrix = out->toXyz.inverted() * toXyz;
+ if (ptr->isIdentity())
+ return QColorTransform();
return combined;
}
@@ -981,6 +983,9 @@ QColorTransform QColorSpace::transformationToColorSpace(const QColorSpace &color
if (!isValid() || !colorspace.isValid())
return QColorTransform();
+ if (*this == colorspace)
+ return QColorTransform();
+
return d_ptr->transformationToColorSpace(colorspace.d_ptr.get());
}
diff --git a/src/gui/painting/qcolortransform.cpp b/src/gui/painting/qcolortransform.cpp
index 71b07f30a5..c89909cff4 100644
--- a/src/gui/painting/qcolortransform.cpp
+++ b/src/gui/painting/qcolortransform.cpp
@@ -1,7 +1,6 @@
-// Copyright (C) 2018 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 "qcolortransform.h"
#include "qcolortransform_p.h"
@@ -13,6 +12,7 @@
#include <QtCore/qatomic.h>
#include <QtCore/qmath.h>
#include <QtGui/qcolor.h>
+#include <QtGui/qimage.h>
#include <QtGui/qtransform.h>
#include <QtCore/private/qsimd_p.h>
@@ -105,6 +105,50 @@ QColorTransform::~QColorTransform() = default;
QT_DEFINE_QESDP_SPECIALIZATION_DTOR(QColorTransformPrivate)
/*!
+ \since 6.4
+ Returns true if the color transform is the identity transform.
+*/
+bool QColorTransform::isIdentity() const noexcept
+{
+ return !d || d->isIdentity();
+}
+
+/*!
+ \fn bool QColorTransform::operator==(const QColorTransform &ct1, const QColorTransform &ct2)
+ \since 6.4
+ Returns true if \a ct1 defines the same color transformation as \a ct2.
+*/
+
+/*!
+ \fn bool QColorTransform::operator!=(const QColorTransform &ct1, const QColorTransform &ct2)
+ \since 6.4
+ Returns true if \a ct1 does not define the same transformation as \a ct2.
+*/
+
+/*! \internal
+*/
+bool QColorTransform::compare(const QColorTransform &other) const
+{
+ if (d == other.d)
+ return true;
+ if (bool(d) != bool(other.d))
+ return d ? d->isIdentity() : other.d->isIdentity();
+ if (d->colorMatrix != other.d->colorMatrix)
+ return false;
+ if (bool(d->colorSpaceIn) != bool(other.d->colorSpaceIn))
+ return false;
+ if (bool(d->colorSpaceOut) != bool(other.d->colorSpaceOut))
+ return false;
+ for (int i = 0; i < 3; ++i) {
+ if (d->colorSpaceIn && d->colorSpaceIn->trc[i] != other.d->colorSpaceIn->trc[i])
+ return false;
+ if (d->colorSpaceOut && d->colorSpaceOut->trc[i] != other.d->colorSpaceOut->trc[i])
+ return false;
+ }
+ return true;
+}
+
+/*!
Applies the color transformation on the QRgb value \a argb.
The input should be opaque or unpremultiplied.
@@ -1129,7 +1173,7 @@ void QColorTransformPrivate::apply(T *dst, const T *src, qsizetype count, Transf
updateLutsIn();
updateLutsOut();
- bool doApplyMatrix = (colorMatrix != QColorMatrix::identity());
+ bool doApplyMatrix = !colorMatrix.isIdentity();
constexpr bool DoClip = !std::is_same_v<T, QRgbaFloat16> && !std::is_same_v<T, QRgbaFloat32>;
QUninitialized<QColorVector, WorkBlockSize> buffer;
@@ -1281,4 +1325,28 @@ void QColorTransformPrivate::apply(quint16 *dst, const QRgba64 *src, qsizetype c
}
+/*!
+ \internal
+*/
+bool QColorTransformPrivate::isIdentity() const
+{
+ if (!colorMatrix.isIdentity())
+ return false;
+ if (colorSpaceIn && colorSpaceOut) {
+ if (colorSpaceIn->transferFunction != colorSpaceOut->transferFunction)
+ return false;
+ if (colorSpaceIn->transferFunction == QColorSpace::TransferFunction::Custom) {
+ return colorSpaceIn->trc[0] == colorSpaceOut->trc[0]
+ && colorSpaceIn->trc[1] == colorSpaceOut->trc[1]
+ && colorSpaceIn->trc[2] == colorSpaceOut->trc[2];
+ }
+ } else {
+ if (colorSpaceIn && colorSpaceIn->transferFunction != QColorSpace::TransferFunction::Linear)
+ return false;
+ if (colorSpaceOut && colorSpaceOut->transferFunction != QColorSpace::TransferFunction::Linear)
+ return false;
+ }
+ return true;
+}
+
QT_END_NAMESPACE
diff --git a/src/gui/painting/qcolortransform.h b/src/gui/painting/qcolortransform.h
index 3df5be7c8b..14673fe924 100644
--- a/src/gui/painting/qcolortransform.h
+++ b/src/gui/painting/qcolortransform.h
@@ -31,16 +31,21 @@ public:
QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QColorTransform)
void swap(QColorTransform &other) noexcept { d.swap(other.d); }
+ Q_GUI_EXPORT bool isIdentity() const noexcept;
Q_GUI_EXPORT QRgb map(QRgb argb) const;
Q_GUI_EXPORT QRgba64 map(QRgba64 rgba64) const;
Q_GUI_EXPORT QColor map(const QColor &color) const;
+ friend bool operator==(const QColorTransform &ct1, const QColorTransform &ct2)
+ { return ct1.compare(ct2); }
+ friend bool operator!=(const QColorTransform &ct1, const QColorTransform &ct2)
+ { return !ct1.compare(ct2); }
+
private:
- friend class QColorSpace;
friend class QColorSpacePrivate;
friend class QColorTransformPrivate;
- friend class QImage;
+ Q_GUI_EXPORT bool compare(const QColorTransform &other) const;
QExplicitlySharedDataPointer<QColorTransformPrivate> d;
};
diff --git a/src/gui/painting/qcolortransform_p.h b/src/gui/painting/qcolortransform_p.h
index 36420dcfdc..c3063c0fc6 100644
--- a/src/gui/painting/qcolortransform_p.h
+++ b/src/gui/painting/qcolortransform_p.h
@@ -36,6 +36,7 @@ public:
void updateLutsIn() const;
void updateLutsOut() const;
bool simpleGammaCorrection() const;
+ bool isIdentity() const;
void prepare();
enum TransformFlag {
diff --git a/tests/auto/gui/painting/qcolortransform/tst_qcolortransform.cpp b/tests/auto/gui/painting/qcolortransform/tst_qcolortransform.cpp
index c09d3fc11e..c2205a02d1 100644
--- a/tests/auto/gui/painting/qcolortransform/tst_qcolortransform.cpp
+++ b/tests/auto/gui/painting/qcolortransform/tst_qcolortransform.cpp
@@ -46,6 +46,8 @@ private slots:
void mapRGB64();
void mapQColor_data();
void mapQColor();
+
+ void transformIsIdentity();
};
tst_QColorTransform::tst_QColorTransform()
@@ -55,32 +57,30 @@ tst_QColorTransform::tst_QColorTransform()
void tst_QColorTransform::mapRGB32_data()
{
QTest::addColumn<QColorTransform>("transform");
- QTest::addColumn<bool>("isIdentity");
QTest::addColumn<bool>("sharesRed");
- QTest::newRow("default") << QColorTransform() << true << true;
- QTest::newRow("sRGB to Linear") << QColorSpace(QColorSpace::SRgb).transformationToColorSpace(QColorSpace::SRgbLinear) << false << true;
- QTest::newRow("AdobeRGB to sRGB") << QColorSpace(QColorSpace::AdobeRgb).transformationToColorSpace(QColorSpace::SRgb) << false << true;
+ QTest::newRow("default") << QColorTransform() << true;
+ QTest::newRow("sRGB to Linear") << QColorSpace(QColorSpace::SRgb).transformationToColorSpace(QColorSpace::SRgbLinear) << true;
+ QTest::newRow("AdobeRGB to sRGB") << QColorSpace(QColorSpace::AdobeRgb).transformationToColorSpace(QColorSpace::SRgb) << true;
QTest::newRow("Linear AdobeRGB to Linear sRGB")
<< QColorSpace(QColorSpace::AdobeRgb).withTransferFunction(QColorSpace::TransferFunction::Linear).transformationToColorSpace(
QColorSpace::SRgb)
- << false << true;
- QTest::newRow("sRgb to AdobeRGB") << QColorSpace(QColorSpace::SRgb).transformationToColorSpace(QColorSpace::AdobeRgb) << false << true;
- QTest::newRow("DP3 to sRGB") << QColorSpace(QColorSpace::DisplayP3).transformationToColorSpace(QColorSpace::SRgb) << false << false;
+ << true;
+ QTest::newRow("sRgb to AdobeRGB") << QColorSpace(QColorSpace::SRgb).transformationToColorSpace(QColorSpace::AdobeRgb) << true;
+ QTest::newRow("DP3 to sRGB") << QColorSpace(QColorSpace::DisplayP3).transformationToColorSpace(QColorSpace::SRgb) << false;
QTest::newRow("DP3 to Linear DP3")
<< QColorSpace(QColorSpace::DisplayP3).transformationToColorSpace(
QColorSpace(QColorSpace::DisplayP3).withTransferFunction(QColorSpace::TransferFunction::Linear))
- << false << false;
+ << false;
QTest::newRow("Linear DP3 to Linear sRGB")
<< QColorSpace(QColorSpace::DisplayP3).withTransferFunction(QColorSpace::TransferFunction::Linear).transformationToColorSpace(
QColorSpace::SRgb)
- << false << false;
+ << false;
}
void tst_QColorTransform::mapRGB32()
{
QFETCH(QColorTransform, transform);
- QFETCH(bool, isIdentity);
QFETCH(bool, sharesRed);
// Do basic sanity tests of conversions between similar sane color spaces
@@ -89,7 +89,7 @@ void tst_QColorTransform::mapRGB32()
QVERIFY(qRed(result) < qGreen(result));
QVERIFY(qGreen(result) < qBlue(result));
QCOMPARE(qAlpha(result), 255);
- if (isIdentity)
+ if (transform.isIdentity())
QVERIFY(result == testColor);
else
QVERIFY(result != testColor);
@@ -99,7 +99,7 @@ void tst_QColorTransform::mapRGB32()
QVERIFY(qRed(result) > qGreen(result));
QVERIFY(qGreen(result) > qBlue(result));
QCOMPARE(qAlpha(result), 255);
- if (isIdentity)
+ if (transform.isIdentity())
QVERIFY(result == testColor);
else
QVERIFY(result != testColor);
@@ -109,7 +109,7 @@ void tst_QColorTransform::mapRGB32()
QVERIFY(qRed(result) < qGreen(result));
QVERIFY(qGreen(result) < qBlue(result));
QCOMPARE(qAlpha(result), 128);
- if (isIdentity)
+ if (transform.isIdentity())
QVERIFY(result == testColor);
else
QVERIFY(result != testColor);
@@ -148,7 +148,6 @@ void tst_QColorTransform::mapRGB64_data()
void tst_QColorTransform::mapRGB64()
{
QFETCH(QColorTransform, transform);
- QFETCH(bool, isIdentity);
QFETCH(bool, sharesRed);
QRgba64 testColor = QRgba64::fromRgba(128, 64, 32, 255);
@@ -156,7 +155,7 @@ void tst_QColorTransform::mapRGB64()
QVERIFY(result.red() > result.green());
QVERIFY(result.green() > result.blue());
QCOMPARE(result.alpha(), 0xffff);
- if (isIdentity)
+ if (transform.isIdentity())
QVERIFY(result == testColor);
else
QVERIFY(result != testColor);
@@ -189,7 +188,6 @@ void tst_QColorTransform::mapQColor_data()
void tst_QColorTransform::mapQColor()
{
QFETCH(QColorTransform, transform);
- QFETCH(bool, isIdentity);
QFETCH(bool, sharesRed);
QColor testColor(32, 64, 128);
@@ -197,7 +195,7 @@ void tst_QColorTransform::mapQColor()
QVERIFY(result.redF() < result.greenF());
QVERIFY(result.greenF() < result.blueF());
QCOMPARE(result.alphaF(), 1.0f);
- if (isIdentity)
+ if (transform.isIdentity())
QVERIFY(result == testColor);
else
QVERIFY(result != testColor);
@@ -222,5 +220,21 @@ void tst_QColorTransform::mapQColor()
QVERIFY(result.blueF() >= 1.0f);
}
+void tst_QColorTransform::transformIsIdentity()
+{
+ QColorTransform ct;
+ QVERIFY(ct.isIdentity());
+
+ QColorSpace cs = QColorSpace::SRgb;
+ ct = cs.transformationToColorSpace(QColorSpace::SRgb);
+ QVERIFY(ct.isIdentity());
+
+ ct = cs.transformationToColorSpace(QColorSpace::SRgbLinear);
+ QVERIFY(!ct.isIdentity());
+
+ ct = cs.withTransferFunction(QColorSpace::TransferFunction::Linear).transformationToColorSpace(QColorSpace::SRgbLinear);
+ QVERIFY(ct.isIdentity());
+}
+
QTEST_MAIN(tst_QColorTransform)
#include "tst_qcolortransform.moc"