[graphics/krita] /: Make default PQ profile the 203 nits one, and implement search for reference white.

Wolthera van Hövell <[email protected]>
Newsgroups gmane.comp.kde.cvs
Message-ID <[email protected]>
Git commit 086abdb2be85f0e09bf161d46393b2be69b0fb75 by Wolthera van Hövell, on behalf of Wolthera van Hövell tot Westerflier.
Committed on 16/07/2026 at 16:26.
Pushed by woltherav into branch 'master'.

Make default PQ profile the 203 nits one, and implement search for reference white.

M  +6    -1    libs/pigment/KoColorProfileStorage.cpp
M  +12   -5    libs/pigment/KoColorSpaceRegistry.cpp
M  +4    -0    libs/surfacecolormanagementapi/surfacecolormanagement/KisSurfaceColorimetryIccUtils.cpp
M  +1    -2    libs/widgets/KisVisualColorModel.cpp
M  +33   -10   plugins/color/lcms2engine/tests/TestIccFromColorimetryConversion.cpp
M  +22   -2    plugins/color/lcms2engine/tests/TestProfileGeneration.cpp
M  +3    -0    plugins/color/lcms2engine/tests/TestProfileGeneration.h
M  +2    -3    plugins/dockers/smallcolorselector/kis_small_color_widget.cc

https://invent.kde.org/graphics/krita/-/commit/086abdb2be85f0e09bf161d46393b2be69b0fb75

diff --git a/libs/pigment/KoColorProfileStorage.cpp b/libs/pigment/KoColorProfileStorage.cpp
index b9bafc12fe9..d4dabfa4575 100644
--- a/libs/pigment/KoColorProfileStorage.cpp
+++ b/libs/pigment/KoColorProfileStorage.cpp
@@ -186,6 +186,7 @@ QList<const KoColorProfile *> KoColorProfileStorage::profilesFor(const KoColorPr
         bool colorantMatch = (!query.rgbColorants.isEmpty() || query.primaries != PRIMARIES_UNSPECIFIED);
         bool colorantTypeMatch = (query.primaries == PRIMARIES_UNSPECIFIED);
         bool transferMatch = (query.transfer == TRC_UNSPECIFIED);
+        bool luminanceMatch = true;
         if (query.primaries != PRIMARIES_UNSPECIFIED) {
             if (int(profile->getColorPrimaries()) == query.primaries) {
                 colorantTypeMatch = true;
@@ -224,7 +225,11 @@ QList<const KoColorProfile *> KoColorProfileStorage::profilesFor(const KoColorPr
             }
         }
 
-        if (transferMatch && colorantMatch && colorantTypeMatch) {
+        if (query.hdrReferenceWhite && query.transfer == TRC_ITU_R_BT_2100_0_PQ) {
+            luminanceMatch = (profile->hdrReferenceWhite() && qFuzzyCompare(*profile->hdrReferenceWhite(), *query.hdrReferenceWhite));
+        }
+
+        if (transferMatch && colorantMatch && colorantTypeMatch && luminanceMatch) {
             profiles.push_back(profile);
         }
     }
diff --git a/libs/pigment/KoColorSpaceRegistry.cpp b/libs/pigment/KoColorSpaceRegistry.cpp
index 9ad6af7bb40..5f58930f1be 100644
--- a/libs/pigment/KoColorSpaceRegistry.cpp
+++ b/libs/pigment/KoColorSpaceRegistry.cpp
@@ -687,7 +687,7 @@ const KoColorProfile *KoColorSpaceRegistry::p2020G10Profile() const
 
 const KoColorProfile *KoColorSpaceRegistry::p2020PQProfile() const
 {
-    return profileByName("Krita Rec. 2100 Perceptual Quantizer (80cd/m²)");
+    return profileByName("Krita Rec. 2100 Perceptual Quantizer (203cd/m²)");
 }
 
 const KoColorProfile *KoColorSpaceRegistry::p709G10Profile() const
@@ -711,20 +711,23 @@ const KoColorProfile *KoColorSpaceRegistry::profileFor(const KoColorProfileQuery
     }
 
     if (query.primaries == PRIMARIES_ITU_R_BT_2020_2_AND_2100_0) {
-        if (query.transfer == TRC_ITU_R_BT_2100_0_PQ) {
+        if (query.transfer == TRC_ITU_R_BT_2100_0_PQ
+            && query.hdrReferenceWhite
+            && qFuzzyCompare(*query.hdrReferenceWhite, 203.0)) {
             return p2020PQProfile();
         } else if (query.transfer == TRC_LINEAR) {
             return p2020G10Profile();
         }
     }
 
-    QList<const KoColorProfile*> list = d->profileStorage.profilesFor(query.primaries, query.transfer);
+    QList<const KoColorProfile*> list = d->profileStorage.profilesFor(query);
     if (!list.empty()) {
         return list.first();
     }
 
     KoColorSpaceEngine *engine = KoColorSpaceEngineRegistry::instance()->get("icc");
-    if (engine && generate) {
+    // We're disabling custom generation of PQ profiles for now.
+    if (engine && generate && !(query.hdrReferenceWhite && query.transfer == TRC_ITU_R_BT_2100_0_PQ)) {
         return engine->getProfile(query);
     }
 
@@ -829,9 +832,13 @@ QColorSpace KoColorSpaceRegistry::QColorSpaceForProfile(const KoColorProfile *pr
         return QColorSpace(QColorSpace::SRgb);
     }
 #if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
-    if (profile == p2020PQProfile()) {
+    if (profile == p2020PQProfile()
+        || (profile->getColorPrimaries() == PRIMARIES_ITU_R_BT_2020_2_AND_2100_0
+            && profile->getTransferCharacteristics() == TRC_ITU_R_BT_2100_0_PQ)) {
         return QColorSpace(QColorSpace::Bt2100Pq);
     }
+    // TODO: we'll need to fine tune the conversion between non "rec 2100" pq profiles and qcolorspace.
+    // Though, arguably, these probably don't show up in a QColorSpace way, and should roundtrip in the icc cicp values.
 #endif
     return QColorSpace::fromIccProfile(profile->rawData());
 }
diff --git a/libs/surfacecolormanagementapi/surfacecolormanagement/KisSurfaceColorimetryIccUtils.cpp b/libs/surfacecolormanagementapi/surfacecolormanagement/KisSurfaceColorimetryIccUtils.cpp
index 0c7996b1738..f8974868b8c 100644
--- a/libs/surfacecolormanagementapi/surfacecolormanagement/KisSurfaceColorimetryIccUtils.cpp
+++ b/libs/surfacecolormanagementapi/surfacecolormanagement/KisSurfaceColorimetryIccUtils.cpp
@@ -92,6 +92,10 @@ KoColorProfileQuery colorSpaceToRequest(ColorSpace cs)
         request.rgbColorants.clear();
     }
 
+    if (request.transfer == TRC_ITU_R_BT_2100_0_PQ) {
+        request.hdrReferenceWhite = std::make_optional(double(cs.luminance->referenceLuminance));
+    }
+
     return request;
 }
 
diff --git a/libs/widgets/KisVisualColorModel.cpp b/libs/widgets/KisVisualColorModel.cpp
index 6098aafaf1f..3404d8df888 100644
--- a/libs/widgets/KisVisualColorModel.cpp
+++ b/libs/widgets/KisVisualColorModel.cpp
@@ -83,8 +83,7 @@ void KisVisualColorModel::slotSetColorSpace(const KoColorSpace *cs)
 
         // PQ color space is not very suitable for color picking, substitute with linear one
         if (cs->colorModelId() == RGBAColorModelID
-                && KoColorSpaceRegistry::instance()->p2020PQProfile()
-                && cs->profile()->uniqueId() == KoColorSpaceRegistry::instance()->p2020PQProfile()->uniqueId()) {
+                && cs->profile()->getTransferCharacteristics() == TRC_ITU_R_BT_2100_0_PQ) {
 
             csNew = KoColorSpaceRegistry::instance()->
                     colorSpace(RGBAColorModelID.id(), Float32BitsColorDepthID.id(),
diff --git a/plugins/color/lcms2engine/tests/TestIccFromColorimetryConversion.cpp b/plugins/color/lcms2engine/tests/TestIccFromColorimetryConversion.cpp
index a7fd50d9774..36791528f02 100644
--- a/plugins/color/lcms2engine/tests/TestIccFromColorimetryConversion.cpp
+++ b/plugins/color/lcms2engine/tests/TestIccFromColorimetryConversion.cpp
@@ -26,6 +26,7 @@ void TestIccFromColorimetryConversion::testRequestConstruction_data()
     QTest::addColumn<bool>("isValid");
     QTest::addColumn<ColorPrimaries>("expectedPrimaries");
     QTest::addColumn<TransferCharacteristics>("expectedTransferFunction");
+    QTest::addColumn<double>("expectedReferenceWhite");
 
     using KisSurfaceColorimetry::ColorSpace;
     using KisSurfaceColorimetry::NamedPrimaries;
@@ -35,6 +36,7 @@ void TestIccFromColorimetryConversion::testRequestConstruction_data()
         ColorSpace cs;
         cs.primaries = p;
         cs.transferFunction = tf;
+        cs.luminance->referenceLuminance = 80;
         return cs;
     };
 
@@ -42,49 +44,57 @@ void TestIccFromColorimetryConversion::testRequestConstruction_data()
         << makeCS(NamedPrimaries::primaries_srgb, NamedTransferFunction::transfer_function_srgb)
         << true
         << PRIMARIES_ITU_R_BT_709_5
-        << TRC_IEC_61966_2_1;
+        << TRC_IEC_61966_2_1
+        << 0.0;
 
     QTest::newRow("srgb-linear")
         << makeCS(NamedPrimaries::primaries_srgb, NamedTransferFunction::transfer_function_ext_linear)
         << true
         << PRIMARIES_ITU_R_BT_709_5
-        << TRC_LINEAR;
+        << TRC_LINEAR
+        << 0.0;
 
     QTest::newRow("srgb-2.2")
         << makeCS(NamedPrimaries::primaries_srgb, NamedTransferFunction::transfer_function_gamma22)
         << true
         << PRIMARIES_ITU_R_BT_709_5
-        << TRC_ITU_R_BT_470_6_SYSTEM_M;
+        << TRC_ITU_R_BT_470_6_SYSTEM_M
+        << 0.0;
 
     QTest::newRow("srgb-2.8")
         << makeCS(NamedPrimaries::primaries_srgb, NamedTransferFunction::transfer_function_gamma28)
         << true
         << PRIMARIES_ITU_R_BT_709_5
-        << TRC_ITU_R_BT_470_6_SYSTEM_B_G;
+        << TRC_ITU_R_BT_470_6_SYSTEM_B_G
+        << 0.0;
 
     QTest::newRow("bt2020-linear")
         << makeCS(NamedPrimaries::primaries_bt2020, NamedTransferFunction::transfer_function_ext_linear)
         << true
         << PRIMARIES_ITU_R_BT_2020_2_AND_2100_0
-        << TRC_LINEAR;
+        << TRC_LINEAR
+        << 0.0;
 
     QTest::newRow("bt2020-pq")
         << makeCS(NamedPrimaries::primaries_bt2020, NamedTransferFunction::transfer_function_st2084_pq)
         << true
         << PRIMARIES_ITU_R_BT_2020_2_AND_2100_0
-        << TRC_ITU_R_BT_2100_0_PQ;
+        << TRC_ITU_R_BT_2100_0_PQ
+        << 80.0;
 
     QTest::newRow("unknown-srgb")
         << makeCS(NamedPrimaries::primaries_unknown, NamedTransferFunction::transfer_function_srgb)
         << false
         << PRIMARIES_UNSPECIFIED
-        << TRC_IEC_61966_2_1;
+        << TRC_IEC_61966_2_1
+        << 0.0;
 
     QTest::newRow("srgb-unknown")
         << makeCS(NamedPrimaries::primaries_srgb, NamedTransferFunction::transfer_function_unknown)
         << false
         << PRIMARIES_ITU_R_BT_709_5
-        << TRC_UNSPECIFIED;
+        << TRC_UNSPECIFIED
+        << 0.0;
 
     // any pq-space that is not bt2020pq is considered unsupported
 
@@ -92,13 +102,15 @@ void TestIccFromColorimetryConversion::testRequestConstruction_data()
         << makeCS(NamedPrimaries::primaries_srgb, NamedTransferFunction::transfer_function_st2084_pq)
         << false
         << PRIMARIES_ITU_R_BT_709_5
-        << TRC_UNSPECIFIED;
+        << TRC_UNSPECIFIED
+        << 0.0;
 
     QTest::newRow("adobergb-pq")
         << makeCS(NamedPrimaries::primaries_adobe_rgb, NamedTransferFunction::transfer_function_st2084_pq)
         << false
         << PRIMARIES_ADOBE_RGB_1998
-        << TRC_UNSPECIFIED;
+        << TRC_UNSPECIFIED
+        << 0.0;
 }
 
 void TestIccFromColorimetryConversion::testRequestConstruction()
@@ -107,12 +119,17 @@ void TestIccFromColorimetryConversion::testRequestConstruction()
     QFETCH(bool, isValid);
     QFETCH(ColorPrimaries, expectedPrimaries);
     QFETCH(TransferCharacteristics, expectedTransferFunction);
+    QFETCH(double, expectedReferenceWhite);
 
     auto request = KisSurfaceColorimetry::colorSpaceToRequest(colorSpace);
 
     QCOMPARE(request.isRgb(), isValid);
     QCOMPARE(request.primaries, expectedPrimaries);
     QCOMPARE(request.transfer, expectedTransferFunction);
+    if (expectedReferenceWhite > 0.0) {
+        QVERIFY(request.hdrReferenceWhite);
+        QVERIFY(qFuzzyCompare(*request.hdrReferenceWhite, expectedReferenceWhite));
+    }
     QVERIFY(request.rgbColorants.isEmpty());
 }
 
@@ -213,6 +230,7 @@ void TestIccFromColorimetryConversion::testProfileConstruction()
     QFETCH(bool, isValid);
     QFETCH(ColorPrimaries, expectedPrimaries);
     QFETCH(TransferCharacteristics, expectedTransferFunction);
+    QFETCH(double, expectedReferenceWhite);
 
     // skip inherited invalid requests
     if (!isValid) return;
@@ -227,6 +245,11 @@ void TestIccFromColorimetryConversion::testProfileConstruction()
     QCOMPARE(profile->getColorPrimaries(), expectedPrimaries);
     QCOMPARE(profile->getTransferCharacteristics(), expectedTransferFunction);
 
+    if (expectedReferenceWhite > 0.0) {
+        QVERIFY(profile->hdrReferenceWhite());
+        QVERIFY(qFuzzyCompare(*profile->hdrReferenceWhite(), expectedReferenceWhite));
+    }
+
     qDebug() << ppVar(profile->name()) << ppVar(profile->fileName());
 }
 
diff --git a/plugins/color/lcms2engine/tests/TestProfileGeneration.cpp b/plugins/color/lcms2engine/tests/TestProfileGeneration.cpp
index 062cd783e7c..007945f039f 100644
--- a/plugins/color/lcms2engine/tests/TestProfileGeneration.cpp
+++ b/plugins/color/lcms2engine/tests/TestProfileGeneration.cpp
@@ -393,12 +393,13 @@ void TestProfileGeneration::testCICPwriting()
     QFETCH(ColorPrimaries, primaries);
     QFETCH(TransferCharacteristics, transfer);
     KoColorProfileQuery query(primaries, transfer);
+    if (transfer == TRC_ITU_R_BT_2100_0_PQ) {
+        query.hdrReferenceWhite = std::make_optional(203.0);
+    }
     const KoColorProfile *profile = KoColorSpaceRegistry::instance()->profileFor(query);
 
     QVERIFY(profile);
 
-    // TODO: write and reload the profile.
-
     QVERIFY(profile->getColorPrimaries() == primaries);
     QVERIFY(profile->getTransferCharacteristics() == transfer);
 
@@ -412,4 +413,23 @@ void TestProfileGeneration::testCICPwriting()
     QVERIFY(lcms->cicpTransfer() == transfer);
 }
 
+void TestProfileGeneration::testRetrieveNits_data()
+{
+    QTest::addColumn<double>("expectedReferenceWhite");
+    QTest::addRow("rec2100PQ 80nits") << 80.0;
+    QTest::addRow("rec2100PQ 203nits") << 203.0;
+}
+
+void TestProfileGeneration::testRetrieveNits()
+{
+    QFETCH(double, expectedReferenceWhite);
+    KoColorProfileQuery query(PRIMARIES_ITU_R_BT_2020_2_AND_2100_0, TRC_ITU_R_BT_2100_0_PQ);
+    query.hdrReferenceWhite = std::make_optional(expectedReferenceWhite);
+    const KoColorProfile *profile = KoColorSpaceRegistry::instance()->profileFor(query);
+
+    QVERIFY(profile);
+    QVERIFY(profile->hdrReferenceWhite());
+    QVERIFY(qFuzzyCompare(*profile->hdrReferenceWhite(), expectedReferenceWhite));
+}
+
 KISTEST_MAIN(TestProfileGeneration)
diff --git a/plugins/color/lcms2engine/tests/TestProfileGeneration.h b/plugins/color/lcms2engine/tests/TestProfileGeneration.h
index fca9d8e942f..bfb4d1ea01f 100644
--- a/plugins/color/lcms2engine/tests/TestProfileGeneration.h
+++ b/plugins/color/lcms2engine/tests/TestProfileGeneration.h
@@ -17,6 +17,9 @@ private Q_SLOTS:
     void testCICPwriting_data();
     void testCICPwriting();
 
+    void testRetrieveNits_data();
+    void testRetrieveNits();
+
 };
 
 #endif // TESTPROFILEGENERATION_H
diff --git a/plugins/dockers/smallcolorselector/kis_small_color_widget.cc b/plugins/dockers/smallcolorselector/kis_small_color_widget.cc
index cee7e70c8dc..05235630f53 100644
--- a/plugins/dockers/smallcolorselector/kis_small_color_widget.cc
+++ b/plugins/dockers/smallcolorselector/kis_small_color_widget.cc
@@ -107,8 +107,7 @@ struct KisSmallColorWidget::Private {
         if (result
                 && result->colorModelId() == RGBAColorModelID
                 && result->profile()
-                && KoColorSpaceRegistry::instance()->p2020PQProfile()
-                && result->profile()->uniqueId() == KoColorSpaceRegistry::instance()->p2020PQProfile()->uniqueId()) {
+                && result->profile()->getTransferCharacteristics() == TRC_ITU_R_BT_2100_0_PQ) {
 
             result = KoColorSpaceRegistry::instance()->
                     colorSpace(RGBAColorModelID.id(), Float32BitsColorDepthID.id(),
@@ -490,7 +489,7 @@ void KisSmallColorWidget::slotDisplayConfigurationChanged()
                 (cs->colorDepthId() == Float16BitsColorDepthID ||
                  cs->colorDepthId() == Float32BitsColorDepthID ||
                  cs->colorDepthId() == Float64BitsColorDepthID ||
-                 cs->profile()->uniqueId() == KoColorSpaceRegistry::instance()->p2020PQProfile()->uniqueId());
+                 cs->profile()->getTransferCharacteristics() == TRC_ITU_R_BT_2100_0_PQ);
     }
 
     if (d->dynamicRange) {
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.