From bde2292247bf4849852355c0dcc71f97c9daace9 Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Mon, 14 Oct 2024 15:38:50 +0200 Subject: [PATCH] Add primary RGB color points getter [ChangeLog][QtGui][QColorSpace] Added primary points getter and setter Pick-to: 6.9 Change-Id: Ia4e412b3e63584cf88fa632e5c1d47618d07ebf1 Reviewed-by: Marc Mutz --- src/gui/image/qpnghandler.cpp | 4 +- src/gui/painting/qcolorspace.cpp | 177 ++++++++++++------ src/gui/painting/qcolorspace.h | 16 +- src/gui/painting/qcolorspace_p.h | 30 +-- .../painting/qcolorspace/tst_qcolorspace.cpp | 2 +- 5 files changed, 143 insertions(+), 86 deletions(-) diff --git a/src/gui/image/qpnghandler.cpp b/src/gui/image/qpnghandler.cpp index 9ecf8d30532..1092f651acf 100644 --- a/src/gui/image/qpnghandler.cpp +++ b/src/gui/image/qpnghandler.cpp @@ -466,7 +466,7 @@ bool QPngHandlerPrivate::readPngHeader() png_get_gAMA(png_ptr, info_ptr, &file_gamma); fileGamma = file_gamma; if (fileGamma > 0.0f) { - QColorSpacePrimaries primaries; + QColorSpace::PrimaryPoints primaries; if (png_get_valid(png_ptr, info_ptr, PNG_INFO_cHRM)) { double white_x, white_y, red_x, red_y; double green_x, green_y, blue_x, blue_y; @@ -478,7 +478,7 @@ bool QPngHandlerPrivate::readPngHeader() primaries.greenPoint = QPointF(green_x, green_y); primaries.bluePoint = QPointF(blue_x, blue_y); } - if (primaries.areValid()) { + if (primaries.isValid()) { colorSpace = QColorSpace(primaries.whitePoint, primaries.redPoint, primaries.greenPoint, primaries.bluePoint, QColorSpace::TransferFunction::Gamma, 1.0f / fileGamma); } else { diff --git a/src/gui/painting/qcolorspace.cpp b/src/gui/painting/qcolorspace.cpp index e93cf0caebf..19b8f6bb49e 100644 --- a/src/gui/painting/qcolorspace.cpp +++ b/src/gui/painting/qcolorspace.cpp @@ -33,45 +33,47 @@ static void cleanupPredefinedColorspaces() Q_DESTRUCTOR_FUNCTION(cleanupPredefinedColorspaces) -QColorSpacePrimaries::QColorSpacePrimaries(QColorSpace::Primaries primaries) +QColorSpace::PrimaryPoints QColorSpace::PrimaryPoints::fromPrimaries(Primaries primaries) { + PrimaryPoints out; switch (primaries) { - case QColorSpace::Primaries::SRgb: - redPoint = QPointF(0.640, 0.330); - greenPoint = QPointF(0.300, 0.600); - bluePoint = QPointF(0.150, 0.060); - whitePoint = QColorVector::D65Chromaticity(); + case Primaries::SRgb: + out.redPoint = QPointF(0.640, 0.330); + out.greenPoint = QPointF(0.300, 0.600); + out.bluePoint = QPointF(0.150, 0.060); + out.whitePoint = QColorVector::D65Chromaticity(); break; - case QColorSpace::Primaries::DciP3D65: - redPoint = QPointF(0.680, 0.320); - greenPoint = QPointF(0.265, 0.690); - bluePoint = QPointF(0.150, 0.060); - whitePoint = QColorVector::D65Chromaticity(); + case Primaries::DciP3D65: + out.redPoint = QPointF(0.680, 0.320); + out.greenPoint = QPointF(0.265, 0.690); + out.bluePoint = QPointF(0.150, 0.060); + out.whitePoint = QColorVector::D65Chromaticity(); break; - case QColorSpace::Primaries::AdobeRgb: - redPoint = QPointF(0.640, 0.330); - greenPoint = QPointF(0.210, 0.710); - bluePoint = QPointF(0.150, 0.060); - whitePoint = QColorVector::D65Chromaticity(); + case Primaries::AdobeRgb: + out.redPoint = QPointF(0.640, 0.330); + out.greenPoint = QPointF(0.210, 0.710); + out.bluePoint = QPointF(0.150, 0.060); + out.whitePoint = QColorVector::D65Chromaticity(); break; - case QColorSpace::Primaries::ProPhotoRgb: - redPoint = QPointF(0.7347, 0.2653); - greenPoint = QPointF(0.1596, 0.8404); - bluePoint = QPointF(0.0366, 0.0001); - whitePoint = QColorVector::D50Chromaticity(); + case Primaries::ProPhotoRgb: + out.redPoint = QPointF(0.7347, 0.2653); + out.greenPoint = QPointF(0.1596, 0.8404); + out.bluePoint = QPointF(0.0366, 0.0001); + out.whitePoint = QColorVector::D50Chromaticity(); break; - case QColorSpace::Primaries::Bt2020: - redPoint = QPointF(0.708, 0.292); - greenPoint = QPointF(0.170, 0.797); - bluePoint = QPointF(0.131, 0.046); - whitePoint = QColorVector::D65Chromaticity(); + case Primaries::Bt2020: + out.redPoint = QPointF(0.708, 0.292); + out.greenPoint = QPointF(0.170, 0.797); + out.bluePoint = QPointF(0.131, 0.046); + out.whitePoint = QColorVector::D65Chromaticity(); break; default: Q_UNREACHABLE(); } + return out; } -bool QColorSpacePrimaries::areValid() const +bool QColorSpace::PrimaryPoints::isValid() const noexcept { if (!QColorVector::isValidChromaticity(redPoint)) return false; @@ -84,17 +86,19 @@ bool QColorSpacePrimaries::areValid() const return true; } -QColorMatrix QColorSpacePrimaries::toXyzMatrix() const +QColorMatrix qColorSpacePrimaryPointsToXyzMatrix(const QColorSpace::PrimaryPoints &primaries) { // This converts to XYZ in some undefined scale. - QColorMatrix toXyz = { QColorVector::fromXYChromaticity(redPoint), - QColorVector::fromXYChromaticity(greenPoint), - QColorVector::fromXYChromaticity(bluePoint) }; + QColorMatrix toXyz = { + QColorVector::fromXYChromaticity(primaries.redPoint), + QColorVector::fromXYChromaticity(primaries.greenPoint), + QColorVector::fromXYChromaticity(primaries.bluePoint) + }; // Since the white point should be (1.0, 1.0, 1.0) in the // input, we can figure out the scale by using the // inverse conversion on the white point. - const auto wXyz = QColorVector::fromXYChromaticity(whitePoint); + const auto wXyz = QColorVector::fromXYChromaticity(primaries.whitePoint); QColorVector whiteScale = toXyz.inverted().map(wXyz); // Now we have scaled conversion to XYZ relative to the given whitepoint @@ -169,7 +173,7 @@ QColorSpacePrivate::QColorSpacePrivate(QColorSpace::Primaries primaries, QColorS initialize(); } -QColorSpacePrivate::QColorSpacePrivate(const QColorSpacePrimaries &primaries, +QColorSpacePrivate::QColorSpacePrivate(const QColorSpace::PrimaryPoints &primaries, QColorSpace::TransferFunction transferFunction, float gamma) : primaries(QColorSpace::Primaries::Custom) @@ -178,8 +182,8 @@ QColorSpacePrivate::QColorSpacePrivate(const QColorSpacePrimaries &primaries, , gamma(gamma) , whitePoint(QColorVector::fromXYChromaticity(primaries.whitePoint)) { - Q_ASSERT(primaries.areValid()); - toXyz = primaries.toXyzMatrix(); + Q_ASSERT(primaries.isValid()); + toXyz = qColorSpacePrimaryPointsToXyzMatrix(primaries); chad = QColorMatrix::chromaticAdaptation(whitePoint); toXyz = chad * toXyz; @@ -225,15 +229,15 @@ QColorSpacePrivate::QColorSpacePrivate(QColorSpace::Primaries primaries, const Q initialize(); } -QColorSpacePrivate::QColorSpacePrivate(const QColorSpacePrimaries &primaries, const QList &transferFunctionTable) +QColorSpacePrivate::QColorSpacePrivate(const QColorSpace::PrimaryPoints &primaries, const QList &transferFunctionTable) : primaries(QColorSpace::Primaries::Custom) , transferFunction(QColorSpace::TransferFunction::Custom) , colorModel(QColorSpace::ColorModel::Rgb) , gamma(0) , whitePoint(QColorVector::fromXYChromaticity(primaries.whitePoint)) { - Q_ASSERT(primaries.areValid()); - toXyz = primaries.toXyzMatrix(); + Q_ASSERT(primaries.isValid()); + toXyz = qColorSpacePrimaryPointsToXyzMatrix(primaries); chad = QColorMatrix::chromaticAdaptation(whitePoint); toXyz = chad * toXyz; setTransferFunctionTable(transferFunctionTable); @@ -241,7 +245,7 @@ QColorSpacePrivate::QColorSpacePrivate(const QColorSpacePrimaries &primaries, co initialize(); } -QColorSpacePrivate::QColorSpacePrivate(const QColorSpacePrimaries &primaries, +QColorSpacePrivate::QColorSpacePrivate(const QColorSpace::PrimaryPoints &primaries, const QList &redTransferFunctionTable, const QList &greenTransferFunctionTable, const QList &blueTransferFunctionTable) @@ -250,8 +254,8 @@ QColorSpacePrivate::QColorSpacePrivate(const QColorSpacePrimaries &primaries, , colorModel(QColorSpace::ColorModel::Rgb) , gamma(0) { - Q_ASSERT(primaries.areValid()); - toXyz = primaries.toXyzMatrix(); + Q_ASSERT(primaries.isValid()); + toXyz = qColorSpacePrimaryPointsToXyzMatrix(primaries); whitePoint = QColorVector::fromXYChromaticity(primaries.whitePoint); chad = QColorMatrix::chromaticAdaptation(whitePoint); toXyz = chad * toXyz; @@ -353,8 +357,8 @@ void QColorSpacePrivate::setToXyzMatrix() whitePoint = QColorVector::D50(); return; } - QColorSpacePrimaries colorSpacePrimaries(primaries); - toXyz = colorSpacePrimaries.toXyzMatrix(); + auto colorSpacePrimaries = QColorSpace::PrimaryPoints::fromPrimaries(primaries); + toXyz = qColorSpacePrimaryPointsToXyzMatrix(colorSpacePrimaries); whitePoint = QColorVector::fromXYChromaticity(colorSpacePrimaries.whitePoint); chad = QColorMatrix::chromaticAdaptation(whitePoint); toXyz = chad * toXyz; @@ -643,6 +647,29 @@ void QColorSpacePrivate::clearElementListProcessingForEdit() In effect only QImage::Format_CMYK32. Note Cmyk color spaces will be TransformModel::ElementListProcessing. */ +/*! + \class QColorSpace::PrimaryPoints + \brief The PrimaryPoints struct contains four primary color space points. + \inmodule QtGui + \since 6.9 + + The four CIE XY color space points describing the gamut of an RGB color space; red, green, blue, and white. + + \sa QColorSpace::Primaries +*/ + +/*! + \fn QColorSpace::PrimaryPoints QColorSpace::PrimaryPoints::fromPrimaries(QColorspace::Primaries primaries); + + Returns the four primary points making up \a primaries. +*/ + +/*! + \fn QColorSpace::PrimaryPoints::isValid() const noexcept + + Returns \c true if the primary points passes the sanity check used by methods consuming QColorSpace::PrimaryPoints. +*/ + /*! \fn QColorSpace::QColorSpace() @@ -728,19 +755,30 @@ QColorSpace::QColorSpace(QPointF whitePoint, const QList &transferFunc } /*! - Creates a custom colorspace with a primaries based on the chromaticities of the primary colors \a whitePoint, + Creates a custom colorspace with primaries based on the chromaticities of the primary colors \a whitePoint, \a redPoint, \a greenPoint and \a bluePoint, and using the transfer function \a transferFunction and optionally \a gamma. */ QColorSpace::QColorSpace(const QPointF &whitePoint, const QPointF &redPoint, const QPointF &greenPoint, const QPointF &bluePoint, QColorSpace::TransferFunction transferFunction, float gamma) + : QColorSpace({whitePoint, redPoint, greenPoint, bluePoint}, transferFunction, gamma) { - QColorSpacePrimaries primaries(whitePoint, redPoint, greenPoint, bluePoint); - if (!primaries.areValid()) { - qWarning() << "QColorSpace attempted constructed from invalid primaries:" << whitePoint << redPoint << greenPoint << bluePoint; +} + +/*! + Creates a custom colorspace with primaries based on the chromaticities of the primary colors \a primaryPoints, + and using the transfer function \a transferFunction and optionally \a gamma. + + \since 6.9 + */ +QColorSpace::QColorSpace(const PrimaryPoints &primaryPoints, TransferFunction transferFunction, float gamma) +{ + if (!primaryPoints.isValid()) { + qWarning() << "QColorSpace attempted constructed from invalid primaries:" + << primaryPoints.whitePoint << primaryPoints.redPoint << primaryPoints.greenPoint << primaryPoints.bluePoint; return; } - d_ptr = new QColorSpacePrivate(primaries, transferFunction, gamma); + d_ptr = new QColorSpacePrivate(primaryPoints, transferFunction, gamma); } /*! @@ -987,22 +1025,32 @@ void QColorSpace::setPrimaries(QColorSpace::Primaries primariesId) Set primaries to the chromaticities of \a whitePoint, \a redPoint, \a greenPoint and \a bluePoint. - \sa primaries() + \sa primaries(), setPrimaryPoints() */ void QColorSpace::setPrimaries(const QPointF &whitePoint, const QPointF &redPoint, const QPointF &greenPoint, const QPointF &bluePoint) { - QColorSpacePrimaries primaries(whitePoint, redPoint, greenPoint, bluePoint); - if (!primaries.areValid()) + setPrimaryPoints({whitePoint, redPoint, greenPoint, bluePoint}); +} + +/*! + Set all primaries to the chromaticities of \a primaryPoints. + + \since 6.9 + \sa primaries(), primaryPoints() +*/ +void QColorSpace::setPrimaryPoints(const QColorSpace::PrimaryPoints &primaryPoints) +{ + if (!primaryPoints.isValid()) return; if (!d_ptr) { - d_ptr = new QColorSpacePrivate(primaries, TransferFunction::Custom, 0.0f); + d_ptr = new QColorSpacePrivate(primaryPoints, TransferFunction::Custom, 0.0f); return; } - QColorMatrix toXyz = primaries.toXyzMatrix(); - QColorMatrix chad = QColorMatrix::chromaticAdaptation(QColorVector::fromXYChromaticity(whitePoint)); + QColorMatrix toXyz = qColorSpacePrimaryPointsToXyzMatrix(primaryPoints); + QColorMatrix chad = QColorMatrix::chromaticAdaptation(QColorVector::fromXYChromaticity(primaryPoints.whitePoint)); toXyz = chad * toXyz; - if (QColorVector::fromXYChromaticity(primaries.whitePoint) == d_ptr->whitePoint + if (QColorVector::fromXYChromaticity(primaryPoints.whitePoint) == d_ptr->whitePoint && toXyz == d_ptr->toXyz && chad == d_ptr->chad) return; detach(); @@ -1014,10 +1062,27 @@ void QColorSpace::setPrimaries(const QPointF &whitePoint, const QPointF &redPoin d_ptr->colorModel = QColorSpace::ColorModel::Rgb; d_ptr->toXyz = toXyz; d_ptr->chad = chad; - d_ptr->whitePoint = QColorVector::fromXYChromaticity(primaries.whitePoint); + d_ptr->whitePoint = QColorVector::fromXYChromaticity(primaryPoints.whitePoint); d_ptr->identifyColorSpace(); } +/*! + Returns the primary chromaticities, if not defined, returns null points. + + \since 6.9 + \sa primaries(), setPrimaryPoints() +*/ +QColorSpace::PrimaryPoints QColorSpace::primaryPoints() const +{ + if (Q_UNLIKELY(!d_ptr)) + return {}; + QColorMatrix rawToXyz = d_ptr->chad.inverted() * d_ptr->toXyz; + return PrimaryPoints{rawToXyz.r.toChromaticity(), + rawToXyz.g.toChromaticity(), + rawToXyz.b.toChromaticity(), + d_ptr->whitePoint.toChromaticity()}; +} + /*! Returns the white point used for this color space. Returns a null QPointF if not defined. diff --git a/src/gui/painting/qcolorspace.h b/src/gui/painting/qcolorspace.h index e23e995d672..607d08d4136 100644 --- a/src/gui/painting/qcolorspace.h +++ b/src/gui/painting/qcolorspace.h @@ -7,13 +7,13 @@ #include #include #include +#include #include #include QT_BEGIN_NAMESPACE class QColorSpacePrivate; -class QPointF; QT_DECLARE_QESDP_SPECIALIZATION_DTOR_WITH_EXPORT(QColorSpacePrivate, Q_GUI_EXPORT) @@ -65,6 +65,16 @@ public: }; Q_ENUM(ColorModel) + struct PrimaryPoints + { + Q_GUI_EXPORT static PrimaryPoints fromPrimaries(Primaries primaries); + Q_GUI_EXPORT bool isValid() const noexcept; + QPointF whitePoint; + QPointF redPoint; + QPointF greenPoint; + QPointF bluePoint; + }; + QColorSpace() noexcept = default; QColorSpace(NamedColorSpace namedColorSpace); explicit QColorSpace(QPointF whitePoint, TransferFunction transferFunction, float gamma = 0.0f); @@ -75,6 +85,8 @@ public: QColorSpace(const QPointF &whitePoint, const QPointF &redPoint, const QPointF &greenPoint, const QPointF &bluePoint, TransferFunction transferFunction, float gamma = 0.0f); + QColorSpace(const PrimaryPoints &primaryPoints, + TransferFunction transferFunction, float gamma = 0.0f); QColorSpace(const QPointF &whitePoint, const QPointF &redPoint, const QPointF &greenPoint, const QPointF &bluePoint, const QList &transferFunctionTable); @@ -122,6 +134,8 @@ public: const QPointF &greenPoint, const QPointF &bluePoint); void setWhitePoint(QPointF whitePoint); QPointF whitePoint() const; + void setPrimaryPoints(const PrimaryPoints &primaryPoints); + PrimaryPoints primaryPoints() const; TransformModel transformModel() const noexcept; ColorModel colorModel() const noexcept; diff --git a/src/gui/painting/qcolorspace_p.h b/src/gui/painting/qcolorspace_p.h index 0f5e846d316..f5050d09b72 100644 --- a/src/gui/painting/qcolorspace_p.h +++ b/src/gui/painting/qcolorspace_p.h @@ -29,29 +29,7 @@ QT_BEGIN_NAMESPACE -class Q_AUTOTEST_EXPORT QColorSpacePrimaries -{ -public: - QColorSpacePrimaries() = default; - QColorSpacePrimaries(QColorSpace::Primaries primaries); - QColorSpacePrimaries(QPointF whitePoint, - QPointF redPoint, - QPointF greenPoint, - QPointF bluePoint) - : whitePoint(whitePoint) - , redPoint(redPoint) - , greenPoint(greenPoint) - , bluePoint(bluePoint) - { } - - QColorMatrix toXyzMatrix() const; - bool areValid() const; - - QPointF whitePoint; - QPointF redPoint; - QPointF greenPoint; - QPointF bluePoint; -}; +bool qColorSpacePrimaryPointsAreValid(const QColorSpace::PrimaryPoints &primaries); class QColorSpacePrivate : public QSharedData { @@ -60,9 +38,9 @@ public: QColorSpacePrivate(QColorSpace::NamedColorSpace namedColorSpace); QColorSpacePrivate(QColorSpace::Primaries primaries, QColorSpace::TransferFunction transferFunction, float gamma); QColorSpacePrivate(QColorSpace::Primaries primaries, const QList &transferFunctionTable); - QColorSpacePrivate(const QColorSpacePrimaries &primaries, QColorSpace::TransferFunction transferFunction, float gamma); - QColorSpacePrivate(const QColorSpacePrimaries &primaries, const QList &transferFunctionTable); - QColorSpacePrivate(const QColorSpacePrimaries &primaries, + QColorSpacePrivate(const QColorSpace::PrimaryPoints &primaries, QColorSpace::TransferFunction transferFunction, float gamma); + QColorSpacePrivate(const QColorSpace::PrimaryPoints &primaries, const QList &transferFunctionTable); + QColorSpacePrivate(const QColorSpace::PrimaryPoints &primaries, const QList &redTransferFunctionTable, const QList &greenTransferFunctionTable, const QList &blueRransferFunctionTable); diff --git a/tests/auto/gui/painting/qcolorspace/tst_qcolorspace.cpp b/tests/auto/gui/painting/qcolorspace/tst_qcolorspace.cpp index e23b3d21a11..786c3cc491a 100644 --- a/tests/auto/gui/painting/qcolorspace/tst_qcolorspace.cpp +++ b/tests/auto/gui/painting/qcolorspace/tst_qcolorspace.cpp @@ -823,7 +823,7 @@ void tst_QColorSpace::primaries2_data() void tst_QColorSpace::primaries2() { QFETCH(QColorSpace::Primaries, primariesId); - QColorSpacePrimaries primaries(primariesId); + QColorSpace::PrimaryPoints primaries = QColorSpace::PrimaryPoints::fromPrimaries(primariesId); QColorSpace original(primariesId, QColorSpace::TransferFunction::Linear); QColorSpace custom1(primaries.whitePoint, primaries.redPoint,