diff --git a/src/gui/image/qpnghandler.cpp b/src/gui/image/qpnghandler.cpp index 3bf4e9db15..16d6c25b8b 100644 --- a/src/gui/image/qpnghandler.cpp +++ b/src/gui/image/qpnghandler.cpp @@ -53,7 +53,6 @@ #include #include -#include #include #include @@ -681,10 +680,7 @@ bool QPngHandlerPrivate::readPngImage(QImage *outImage) // This configuration forces gamma correction and // thus changes the output colorspace png_set_gamma(png_ptr, 1.0f / gamma, fileGamma); - QColorSpacePrivate *csPrivate = QColorSpacePrivate::getWritable(colorSpace); - csPrivate->transferFunction = QColorSpace::TransferFunction::Gamma; - csPrivate->gamma = 1.0f / gamma; - csPrivate->setTransferFunction(); + colorSpace = colorSpace.withTransferFunction(QColorSpace::TransferFunction::Gamma, 1.0f / gamma); colorSpaceState = GammaChrm; } @@ -984,14 +980,8 @@ bool QPNGImageWriter::writeImage(const QImage& image, volatile int compression_i if (image.colorSpace().isValid()) { QColorSpace cs = image.colorSpace(); // Support the old gamma making it override transferfunction. - if (gamma != 0.0 && !qFuzzyCompare(cs.gamma(), 1.0f / gamma)) { - QColorSpacePrivate *csPrivate = QColorSpacePrivate::getWritable(cs); - csPrivate->transferFunction = QColorSpace::TransferFunction::Gamma; - csPrivate->gamma = 1.0f / gamma; - csPrivate->setTransferFunction(); - csPrivate->iccProfile.clear(); - csPrivate->description.clear(); - } + if (gamma != 0.0 && !qFuzzyCompare(cs.gamma(), 1.0f / gamma)) + cs = cs.withTransferFunction(QColorSpace::TransferFunction::Gamma, 1.0f / gamma); QByteArray iccProfileName = QColorSpacePrivate::get(cs)->description.toLatin1(); if (iccProfileName.isEmpty()) iccProfileName = QByteArrayLiteral("Custom"); diff --git a/src/gui/painting/qcolorspace.cpp b/src/gui/painting/qcolorspace.cpp index a14d630814..043a951521 100644 --- a/src/gui/painting/qcolorspace.cpp +++ b/src/gui/painting/qcolorspace.cpp @@ -163,24 +163,22 @@ QColorSpacePrivate::QColorSpacePrivate() QColorSpacePrivate::QColorSpacePrivate(QColorSpace::ColorSpaceId colorSpaceId) : id(colorSpaceId) + , gamma(0.0f) { switch (colorSpaceId) { case QColorSpace::Undefined: primaries = QColorSpace::Primaries::Custom; transferFunction = QColorSpace::TransferFunction::Custom; - gamma = 0.0f; description = QStringLiteral("Undefined"); break; case QColorSpace::SRgb: primaries = QColorSpace::Primaries::SRgb; transferFunction = QColorSpace::TransferFunction::SRgb; - gamma = 2.31f; // ? description = QStringLiteral("sRGB"); break; case QColorSpace::SRgbLinear: primaries = QColorSpace::Primaries::SRgb; transferFunction = QColorSpace::TransferFunction::Linear; - gamma = 1.0f; description = QStringLiteral("Linear sRGB"); break; case QColorSpace::AdobeRgb: @@ -192,19 +190,16 @@ QColorSpacePrivate::QColorSpacePrivate(QColorSpace::ColorSpaceId colorSpaceId) case QColorSpace::DisplayP3: primaries = QColorSpace::Primaries::DciP3D65; transferFunction = QColorSpace::TransferFunction::SRgb; - gamma = 2.31f; // ? description = QStringLiteral("Display P3"); break; case QColorSpace::ProPhotoRgb: primaries = QColorSpace::Primaries::ProPhotoRgb; transferFunction = QColorSpace::TransferFunction::ProPhotoRgb; - gamma = 1.8f; description = QStringLiteral("ProPhoto RGB"); break; case QColorSpace::Bt2020: primaries = QColorSpace::Primaries::Bt2020; transferFunction = QColorSpace::TransferFunction::Bt2020; - gamma = 2.1f; // ? description = QStringLiteral("BT.2020"); break; case QColorSpace::Unknown: @@ -236,8 +231,7 @@ QColorSpacePrivate::QColorSpacePrivate(const QColorSpacePrimaries &primaries, Q_ASSERT(primaries.areValid()); toXyz = primaries.toXyzMatrix(); whitePoint = QColorVector(primaries.whitePoint); - if (!identifyColorSpace()) - id = QColorSpace::Unknown; + identifyColorSpace(); setTransferFunction(); } @@ -304,6 +298,7 @@ bool QColorSpacePrivate::identifyColorSpace() default: break; } + id = QColorSpace::Unknown; return false; } @@ -331,6 +326,8 @@ void QColorSpacePrivate::setTransferFunction() case QColorSpace::TransferFunction::Linear: trc[0].m_type = QColorTrc::Type::Function; trc[0].m_fun = QColorTransferFunction(); + if (qFuzzyIsNull(gamma)) + gamma = 1.0f; break; case QColorSpace::TransferFunction::Gamma: trc[0].m_type = QColorTrc::Type::Function; @@ -339,14 +336,20 @@ void QColorSpacePrivate::setTransferFunction() case QColorSpace::TransferFunction::SRgb: trc[0].m_type = QColorTrc::Type::Function; trc[0].m_fun = QColorTransferFunction::fromSRgb(); + if (qFuzzyIsNull(gamma)) + gamma = 2.31f; break; case QColorSpace::TransferFunction::ProPhotoRgb: trc[0].m_type = QColorTrc::Type::Function; trc[0].m_fun = QColorTransferFunction::fromProPhotoRgb(); + if (qFuzzyIsNull(gamma)) + gamma = 1.8f; break; case QColorSpace::TransferFunction::Bt2020: trc[0].m_type = QColorTrc::Type::Function; trc[0].m_fun = QColorTransferFunction::fromBt2020(); + if (qFuzzyIsNull(gamma)) + gamma = 1.961f; break; case QColorSpace::TransferFunction::Custom: break; @@ -564,7 +567,7 @@ QColorSpace::Primaries QColorSpace::primaries() const noexcept Returns the predefined transfer function of the color space or \c TransferFunction::Custom if it doesn't match any of them. - \sa gamma() + \sa gamma(), setTransferFunction(), withTransferFunction() */ QColorSpace::TransferFunction QColorSpace::transferFunction() const noexcept { @@ -583,6 +586,91 @@ float QColorSpace::gamma() const noexcept return d_ptr->gamma; } +/*! + Sets the transfer function to \a transferFunction and \a gamma. + + \note This also changes colorSpaceId(). + + \sa transferFunction(), gamma(), withTransferFunction() +*/ +void QColorSpace::setTransferFunction(QColorSpace::TransferFunction transferFunction, float gamma) +{ + if (!isValid() || transferFunction == QColorSpace::TransferFunction::Custom) + return; + if (d_ptr->transferFunction == transferFunction && d_ptr->gamma == gamma) + return; + d_ptr.detach(); + d_ptr->description.clear(); + d_ptr->transferFunction = transferFunction; + d_ptr->gamma = gamma; + d_ptr->identifyColorSpace(); + d_ptr->setTransferFunction(); +} + +/*! + Returns a copy of this color space, except using the transfer function + \a transferFunction and \a gamma. + + \sa transferFunction(), gamma(), setTransferFunction() +*/ +QColorSpace QColorSpace::withTransferFunction(QColorSpace::TransferFunction transferFunction, float gamma) const +{ + if (!isValid() || transferFunction == QColorSpace::TransferFunction::Custom) + return *this; + if (d_ptr->transferFunction == transferFunction && d_ptr->gamma == gamma) + return *this; + QColorSpace out(*this); + out.setTransferFunction(transferFunction, gamma); + return out; +} + +/*! + Sets the primaries to those of the \a primariesId set. + + \note This also changes colorSpaceId(). + + \sa primaries() +*/ +void QColorSpace::setPrimaries(QColorSpace::Primaries primariesId) +{ + if (!isValid() || primariesId == QColorSpace::Primaries::Custom) + return; + if (d_ptr->primaries == primariesId) + return; + d_ptr.detach(); + d_ptr->description.clear(); + d_ptr->primaries = primariesId; + d_ptr->identifyColorSpace(); + d_ptr->setToXyzMatrix(); +} + +/*! + Set primaries to the chromaticities of \a whitePoint, \a redPoint, \a greenPoint + and \a bluePoint. + + \note This also changes colorSpaceId(). + + \sa primaries() +*/ +void QColorSpace::setPrimaries(const QPointF &whitePoint, const QPointF &redPoint, + const QPointF &greenPoint, const QPointF &bluePoint) +{ + if (!isValid()) + return; + QColorSpacePrimaries primaries(whitePoint, redPoint, greenPoint, bluePoint); + if (!primaries.areValid()) + return; + QColorMatrix toXyz = primaries.toXyzMatrix(); + if (QColorVector(primaries.whitePoint) == d_ptr->whitePoint && toXyz == d_ptr->toXyz) + return; + d_ptr.detach(); + d_ptr->description.clear(); + d_ptr->primaries = QColorSpace::Primaries::Custom; + d_ptr->toXyz = toXyz; + d_ptr->whitePoint = QColorVector(primaries.whitePoint); + d_ptr->identifyColorSpace(); +} + /*! Returns an ICC profile representing the color space. diff --git a/src/gui/painting/qcolorspace.h b/src/gui/painting/qcolorspace.h index a9871fc277..a7c1091911 100644 --- a/src/gui/painting/qcolorspace.h +++ b/src/gui/painting/qcolorspace.h @@ -103,6 +103,13 @@ public: TransferFunction transferFunction() const noexcept; float gamma() const noexcept; + void setTransferFunction(TransferFunction transferFunction, float gamma = 0.0f); + QColorSpace withTransferFunction(TransferFunction transferFunction, float gamma = 0.0f) const; + + void setPrimaries(Primaries primariesId); + void setPrimaries(const QPointF &whitePoint, const QPointF &redPoint, + const QPointF &greenPoint, const QPointF &bluePoint); + bool isValid() const noexcept; friend Q_GUI_EXPORT bool operator==(const QColorSpace &colorSpace1, const QColorSpace &colorSpace2); diff --git a/tests/auto/gui/painting/qcolorspace/tst_qcolorspace.cpp b/tests/auto/gui/painting/qcolorspace/tst_qcolorspace.cpp index c0f3559f57..bc1a45013c 100644 --- a/tests/auto/gui/painting/qcolorspace/tst_qcolorspace.cpp +++ b/tests/auto/gui/painting/qcolorspace/tst_qcolorspace.cpp @@ -66,6 +66,9 @@ private slots: void primaries2_data(); void primaries2(); void invalidPrimaries(); + + void changeTransferFunction(); + void changePrimaries(); }; tst_QColorSpace::tst_QColorSpace() @@ -375,5 +378,49 @@ void tst_QColorSpace::invalidPrimaries() QCOMPARE(custom.colorSpaceId(), QColorSpace::Undefined); } +void tst_QColorSpace::changeTransferFunction() +{ + QColorSpace sRgb = QColorSpace::SRgb; + + QColorSpace sRgbLinear = sRgb.withTransferFunction(QColorSpace::TransferFunction::Linear); + QCOMPARE(sRgbLinear.transferFunction(), QColorSpace::TransferFunction::Linear); + QCOMPARE(sRgbLinear.gamma(), 1.0f); + QCOMPARE(sRgbLinear.primaries(), QColorSpace::Primaries::SRgb); + QCOMPARE(sRgbLinear.colorSpaceId(), QColorSpace::SRgbLinear); + QCOMPARE(sRgbLinear, QColorSpace(QColorSpace::SRgbLinear)); + QVERIFY(sRgbLinear != sRgb); + QCOMPARE(sRgbLinear.withTransferFunction(QColorSpace::TransferFunction::SRgb), sRgb); + + QColorSpace aRgb = QColorSpace::AdobeRgb; + aRgb.setTransferFunction(QColorSpace::TransferFunction::SRgb); + QCOMPARE(aRgb.transferFunction(), QColorSpace::TransferFunction::SRgb); + QCOMPARE(aRgb.primaries(), QColorSpace::Primaries::AdobeRgb); + QCOMPARE(aRgb.colorSpaceId(), QColorSpace::Unknown); + QVERIFY(aRgb != QColorSpace(QColorSpace::AdobeRgb)); + QVERIFY(aRgb != sRgb); + QCOMPARE(aRgb.withTransferFunction(QColorSpace::TransferFunction::Gamma, 2.2f), + QColorSpace(QColorSpace::AdobeRgb)); + QVERIFY(aRgb != QColorSpace(QColorSpace::AdobeRgb)); + aRgb.setTransferFunction(QColorSpace::TransferFunction::Gamma, 2.2f); + QVERIFY(aRgb == QColorSpace(QColorSpace::AdobeRgb)); + + QColorSpace undefined; + QCOMPARE(undefined.withTransferFunction(QColorSpace::TransferFunction::Linear), undefined); + undefined.setTransferFunction(QColorSpace::TransferFunction::SRgb); + QCOMPARE(undefined, QColorSpace()); +} + +void tst_QColorSpace::changePrimaries() +{ + QColorSpace cs = QColorSpace::SRgb; + cs.setPrimaries(QColorSpace::Primaries::DciP3D65); + QVERIFY(cs.isValid()); + QCOMPARE(cs, QColorSpace(QColorSpace::DisplayP3)); + cs.setTransferFunction(QColorSpace::TransferFunction::Linear); + cs.setPrimaries(QPointF(0.3127, 0.3290), QPointF(0.640, 0.330), + QPointF(0.3000, 0.6000), QPointF(0.150, 0.060)); + QCOMPARE(cs, QColorSpace(QColorSpace::SRgbLinear)); +} + QTEST_MAIN(tst_QColorSpace) #include "tst_qcolorspace.moc"