diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2022-03-16 12:21:40 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2022-05-26 09:06:45 +0200 |
commit | 734c9f2df26b12b89c8a4de4ae43a15726ff1886 (patch) | |
tree | 4be65958c6b790c63cd35371485c7338cec02ae0 | |
parent | 34c21d040766d54d959ed835bdf5464f657b7693 (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.cpp | 45 | ||||
-rw-r--r-- | src/gui/image/qimage.h | 2 | ||||
-rw-r--r-- | src/gui/image/qimage_conversions.cpp | 8 | ||||
-rw-r--r-- | src/gui/painting/qcolormatrix_p.h | 4 | ||||
-rw-r--r-- | src/gui/painting/qcolorspace.cpp | 5 | ||||
-rw-r--r-- | src/gui/painting/qcolortransform.cpp | 74 | ||||
-rw-r--r-- | src/gui/painting/qcolortransform.h | 9 | ||||
-rw-r--r-- | src/gui/painting/qcolortransform_p.h | 1 | ||||
-rw-r--r-- | tests/auto/gui/painting/qcolortransform/tst_qcolortransform.cpp | 48 |
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" |