[graphics/krita] /: Fix generation of Display P3 PQ profile
Wolthera van Hövell <[email protected]>
| Newsgroups | gmane.comp.kde.cvs |
|---|---|
| Message-ID | <[email protected]> |
Git commit 347ce8bc050f19ffefe440d24c1f1df3805ccb0c by Wolthera van Hövell, on behalf of Dmitry Kazakov.
Committed on 16/07/2026 at 16:26.
Pushed by woltherav into branch 'master'.
Fix generation of Display P3 PQ profile
The patch implements a concept of "connection
profiles" for KoColorSpaceFactories. This avoids
recursion (and hence a deadlock) when trying
to connect a PQ profile to its corresponding
linear profile in Rgb*ColorSpaceFactory::
colorConversionLinksFromProfile().
The patch also adds a unittests for verifying that.
M +42 -0 libs/pigment/KoColorConversionSystem.cpp
M +59 -4 libs/pigment/KoColorConversionSystem.h
M +13 -0 libs/pigment/KoColorProfileQuery.h
M +7 -2 libs/pigment/KoColorProfileStorage.cpp
M +6 -20 libs/pigment/KoColorSpaceFactory.cpp
M +4 -14 libs/pigment/KoColorSpaceFactory.h
M +55 -23 libs/pigment/KoColorSpaceRegistry.cpp
M +8 -0 libs/pigment/KoColorSpaceRegistry.h
M +15 -2 plugins/color/lcms2engine/colorspaces/rgb_f16/RgbF16ColorSpace.cpp
M +1 -0 plugins/color/lcms2engine/colorspaces/rgb_f16/RgbF16ColorSpace.h
M +14 -2 plugins/color/lcms2engine/colorspaces/rgb_f32/RgbF32ColorSpace.cpp
M +1 -0 plugins/color/lcms2engine/colorspaces/rgb_f32/RgbF32ColorSpace.h
M +15 -2 plugins/color/lcms2engine/colorspaces/rgb_u16/RgbU16ColorSpace.cpp
M +1 -0 plugins/color/lcms2engine/colorspaces/rgb_u16/RgbU16ColorSpace.h
M +15 -2 plugins/color/lcms2engine/colorspaces/rgb_u8/RgbU8ColorSpace.cpp
M +1 -0 plugins/color/lcms2engine/colorspaces/rgb_u8/RgbU8ColorSpace.h
M +46 -10 plugins/color/lcms2engine/tests/TestProfileGeneration.cpp
https://invent.kde.org/graphics/krita/-/commit/347ce8bc050f19ffefe440d24c1f1df3805ccb0c
diff --git a/libs/pigment/KoColorConversionSystem.cpp b/libs/pigment/KoColorConversionSystem.cpp
index 65afed2596e..a4a2b22dbda 100644
--- a/libs/pigment/KoColorConversionSystem.cpp
+++ b/libs/pigment/KoColorConversionSystem.cpp
@@ -13,6 +13,7 @@
#include "KoColorConversionAlphaTransformation.h"
#include "KoColorConversionTransformation.h"
#include "KoColorProfile.h"
+#include "KoColorProfileQuery.h"
#include "KoColorSpace.h"
#include "KoCopyColorConversionTransformation.h"
#include "KoMultipleColorConversionTransformation.h"
@@ -52,6 +53,26 @@ KoColorConversionSystem::Node* KoColorConversionSystem::insertEngine(const KoCol
return n;
}
+QList<KoColorProfileQuery> KoColorConversionSystem::requiredConnectionProfilesFor(const KoColorSpaceFactory* csf)
+{
+ QList<KoColorProfileQuery> profileQueries;
+
+ const QList<const KoColorProfile*> existingProfiles = d->registryInterface->profilesFor(csf);
+ Q_FOREACH (const KoColorProfile* profile, existingProfiles) {
+ profileQueries.append(csf->requiredConnectionProfiles(profile));
+ }
+
+ // TODO: make a better 'unique' function without sorting
+ QList<KoColorProfileQuery> uniqueProfileQueries;
+ std::copy_if(profileQueries.begin(),
+ profileQueries.end(),
+ std::back_inserter(uniqueProfileQueries),
+ [&](const KoColorProfileQuery &query) {
+ return !uniqueProfileQueries.contains(query);
+ });
+
+ return uniqueProfileQueries;
+}
void KoColorConversionSystem::insertColorSpace(const KoColorSpaceFactory* csf)
{
@@ -115,6 +136,27 @@ void KoColorConversionSystem::insertColorSpace(const KoColorSpaceFactory* csf)
}
}
+QList<KoColorProfileQuery> KoColorConversionSystem::requiredConnectionProfilesFor(const KoColorProfile* profile)
+{
+ QList<KoColorProfileQuery> profileQueries;
+
+ const QList< const KoColorSpaceFactory* >& factories = d->registryInterface->colorSpacesFor(profile);
+ Q_FOREACH (const KoColorSpaceFactory* factory, factories) {
+ profileQueries.append(factory->requiredConnectionProfiles(profile));
+ }
+
+ // TODO: make a better 'unique' function without sorting
+ QList<KoColorProfileQuery> uniqueProfileQueries;
+ std::copy_if(profileQueries.begin(),
+ profileQueries.end(),
+ std::back_inserter(uniqueProfileQueries),
+ [&](const KoColorProfileQuery &query) {
+ return !uniqueProfileQueries.contains(query);
+ });
+
+ return uniqueProfileQueries;
+}
+
void KoColorConversionSystem::insertColorProfile(const KoColorProfile* _profile)
{
dbgPigmentCCS << _profile->name();
diff --git a/libs/pigment/KoColorConversionSystem.h b/libs/pigment/KoColorConversionSystem.h
index a343534526f..bd4609d03eb 100644
--- a/libs/pigment/KoColorConversionSystem.h
+++ b/libs/pigment/KoColorConversionSystem.h
@@ -8,6 +8,7 @@
#define _KO_COLOR_CONVERSION_SYSTEM_H_
class KoColorProfile;
+class KoColorProfileQuery;
class KoColorSpace;
class KoColorSpaceFactory;
class KoColorSpaceEngine;
@@ -53,13 +54,67 @@ public:
*/
KoColorConversionSystem(RegistryInterface *registryInterface);
~KoColorConversionSystem();
+
+ /**
+ * Add a color space to a graph of transformation. The new node will be
+ * added for each known compatible profile.
+ *
+ * Make sure you call `requiredConnectionProfilesFor(csf)` and add all
+ * the required connection profiles into the registry **before** inserting
+ * the actual color space \p csf. Otherwise the created node will not be
+ * able to connect itself into the graph (due to a missing connection
+ * point).
+ *
+ * \see requiredConnectionProfilesFor()
+ */
+ void insertColorSpace(const KoColorSpaceFactory *csf);
+
+ /**
+ * Add a profile to the graph of transformation. The new node will be added
+ * for each known color space compatible with this profile.
+ *
+ * Make sure you call `requiredConnectionProfilesFor(profile)` and add all
+ * the required connection profiles into the registry **before** inserting
+ * the actual profile \p profile. Otherwise the created node will not be
+ * able to connect itself into the graph (due to a missing connection
+ * point).
+ *
+ * \see requiredConnectionProfilesFor()
+ */
+ void insertColorProfile(const KoColorProfile *profile);
+
+ /**
+ * \return a list of connection profiles required for this color space
+ *
+ * Some color space nodes may require custom profiles to be connected
+ * to the color conversion system. I.e. Display P3 PQ space will require
+ * a Display P3 Linear profile to connect itself to the color conversion
+ * system through.
+ *
+ * This function returns a list of such connection profiles.
+ *
+ * Connection profiles should be requested and added to the registry
+ * **before** adding the color space \p csf into the color conversion system
+ * with insertColorSpace(csf).
+ */
+ QList<KoColorProfileQuery> requiredConnectionProfilesFor(const KoColorSpaceFactory* csf);
+
/**
- * This function is called by the KoColorSpaceRegistry to add a new color space
- * to the graph of transformation.
+ * \return a list of connection profiles required for this profile
+ *
+ * Some color space nodes may require custom profiles to be connected
+ * to the color conversion system. I.e. Display P3 PQ space will require
+ * a Display P3 Linear profile to connect itself to the color conversion
+ * system through.
+ *
+ * This function returns a list of such connection profiles.
+ *
+ * Connection profiles should be requested and added to the registry
+ * **before** adding the profile \p profile into the color conversion system
+ * with insertColorProfile(csf).
*/
- void insertColorSpace(const KoColorSpaceFactory*);
+ QList<KoColorProfileQuery> requiredConnectionProfilesFor(const KoColorProfile* profile);
- void insertColorProfile(const KoColorProfile*);
/**
* This function is called by the color space to create a color conversion
* between two color space. This function search in the graph of transformations
diff --git a/libs/pigment/KoColorProfileQuery.h b/libs/pigment/KoColorProfileQuery.h
index 36b740e2aef..80acacd61b7 100644
--- a/libs/pigment/KoColorProfileQuery.h
+++ b/libs/pigment/KoColorProfileQuery.h
@@ -46,6 +46,19 @@ struct KoColorProfileQuery {
&& (primaries != PRIMARIES_UNSPECIFIED || !rgbColorants.isEmpty())
&& !(whitePoint == KoColorimetryUtils::xy());
}
+
+ inline bool operator==(const KoColorProfileQuery &rhs) const {
+ return whitePoint == rhs.whitePoint
+ && rgbColorants == rhs.rgbColorants
+ && primaries == rhs.primaries
+ && transfer == rhs.transfer
+ && hdrReferenceWhite == rhs.hdrReferenceWhite;
+ }
+
+ inline bool operator!=(const KoColorProfileQuery &rhs) const {
+ return !(*this == rhs);
+ }
+
};
inline QDebug operator<<(QDebug debug, const KoColorProfileQuery &value) {
diff --git a/libs/pigment/KoColorProfileStorage.cpp b/libs/pigment/KoColorProfileStorage.cpp
index d4dabfa4575..14966e76cb9 100644
--- a/libs/pigment/KoColorProfileStorage.cpp
+++ b/libs/pigment/KoColorProfileStorage.cpp
@@ -225,8 +225,13 @@ QList<const KoColorProfile *> KoColorProfileStorage::profilesFor(const KoColorPr
}
}
- if (query.hdrReferenceWhite && query.transfer == TRC_ITU_R_BT_2100_0_PQ) {
- luminanceMatch = (profile->hdrReferenceWhite() && qFuzzyCompare(*profile->hdrReferenceWhite(), *query.hdrReferenceWhite));
+ if (query.transfer == TRC_ITU_R_BT_2100_0_PQ) {
+ // by default we expect HDR profiles to have HDR Reference White point
+ // to set to 203 nits
+ // TODO: should we spit a warning if the reference white is not set
+ // for a pq space?
+ const qreal requestedHdrReferenceWhite = query.hdrReferenceWhite.value_or(203.0);
+ luminanceMatch = (profile->hdrReferenceWhite() && qFuzzyCompare(*profile->hdrReferenceWhite(), requestedHdrReferenceWhite));
}
if (transferMatch && colorantMatch && colorantTypeMatch && luminanceMatch) {
diff --git a/libs/pigment/KoColorSpaceFactory.cpp b/libs/pigment/KoColorSpaceFactory.cpp
index 423b220f2a7..63a459cb786 100644
--- a/libs/pigment/KoColorSpaceFactory.cpp
+++ b/libs/pigment/KoColorSpaceFactory.cpp
@@ -12,13 +12,13 @@
#include <QMutexLocker>
#include "KoColorProfile.h"
+#include "KoColorProfileQuery.h"
#include "KoColorSpace.h"
#include "KoColorSpaceRegistry.h"
#include "kis_assert.h"
struct Q_DECL_HIDDEN KoColorSpaceFactory::Private {
- QList<KoColorProfile*> colorprofiles;
QHash<QString, KoColorSpace* > availableColorspaces;
QMutex mutex;
#ifndef NDEBUG
@@ -45,28 +45,9 @@ KoColorSpaceFactory::~KoColorSpaceFactory()
errorPigment << it.value();
}
#endif
- Q_FOREACH (KoColorProfile* profile, d->colorprofiles) {
- KoColorSpaceRegistry::instance()->removeProfile(profile);
- delete profile;
- }
delete d;
}
-const KoColorProfile *KoColorSpaceFactory::colorProfile(const QByteArray& rawData, ProfileRegistrationInterface *registrationInterface, const CustomProfileNameAlias &customProfileNameAlias) const
-{
- KoColorProfile* colorProfile = createColorProfile(rawData);
- if (colorProfile && colorProfile->valid()) {
- const QString effectiveProfileName = customProfileNameAlias.value(colorProfile->name(), colorProfile->name());
- if (const KoColorProfile* existingProfile = registrationInterface->profileByName(effectiveProfileName)) {
- delete colorProfile;
- return existingProfile;
- }
- registrationInterface->registerNewProfile(colorProfile);
- d->colorprofiles.append(colorProfile);
- }
- return colorProfile;
-}
-
const KoColorSpace *KoColorSpaceFactory::grabColorSpace(const KoColorProfile * profile)
{
QMutexLocker l(&d->mutex);
@@ -88,3 +69,8 @@ const KoColorSpace *KoColorSpaceFactory::grabColorSpace(const KoColorProfile * p
return cs;
}
+QList<KoColorProfileQuery> KoColorSpaceFactory::requiredConnectionProfiles(const KoColorProfile *profile) const
+{
+ Q_UNUSED(profile)
+ return {};
+}
diff --git a/libs/pigment/KoColorSpaceFactory.h b/libs/pigment/KoColorSpaceFactory.h
index 84789c02fa8..9394d4c7794 100644
--- a/libs/pigment/KoColorSpaceFactory.h
+++ b/libs/pigment/KoColorSpaceFactory.h
@@ -13,6 +13,7 @@
#include "kritapigment_export.h"
class KoColorProfile;
+class KoColorProfileQuery;
class KoColorConversionTransformationFactory;
/**
@@ -98,22 +99,10 @@ public:
*/
virtual QString defaultProfile() const = 0;
- struct ProfileRegistrationInterface
- {
- virtual ~ProfileRegistrationInterface() {}
- virtual const KoColorProfile* profileByName(const QString &profileName) const = 0;
- virtual void registerNewProfile(KoColorProfile *profile) = 0;
- };
-
/**
* Create a color profile from a memory array, if possible, otherwise return 0.
- * If there is an existing profile with the same name, it will be used instead,
- * and the binary data from \p rawData will be ignored
- *
- * This will call the descendant's createColorProfile()
*/
- using CustomProfileNameAlias = QHash<QString, QString>;
- const KoColorProfile* colorProfile(const QByteArray& rawData, ProfileRegistrationInterface *registrationInterface, const CustomProfileNameAlias &customProfileNameAlias) const;
+ virtual KoColorProfile* createColorProfile(const QByteArray& rawData) const = 0;
/**
* Create or reuse the existing colorspace for the given profile.
@@ -134,12 +123,13 @@ public:
return QList<KoColorConversionTransformationFactory*>();
}
+ virtual QList<KoColorProfileQuery> requiredConnectionProfiles(const KoColorProfile *profile) const;
+
protected:
/**
* creates a color space using the given profile.
*/
virtual KoColorSpace *createColorSpace(const KoColorProfile *) const = 0;
- virtual KoColorProfile* createColorProfile(const QByteArray& rawData) const = 0;
private:
struct Private;
Private* const d;
diff --git a/libs/pigment/KoColorSpaceRegistry.cpp b/libs/pigment/KoColorSpaceRegistry.cpp
index 5f58930f1be..09093a5c3f7 100644
--- a/libs/pigment/KoColorSpaceRegistry.cpp
+++ b/libs/pigment/KoColorSpaceRegistry.cpp
@@ -42,8 +42,6 @@ Q_GLOBAL_STATIC(KoColorSpaceRegistry, s_instance)
struct Q_DECL_HIDDEN KoColorSpaceRegistry::Private {
- // interface for KoColorSpaceFactory
- struct ProfileRegistrationInterface;
// interface for KoColorConversionSystem
struct ConversionSystemInterface;
@@ -233,6 +231,19 @@ KoColorSpaceRegistry::~KoColorSpaceRegistry()
void KoColorSpaceRegistry::add(KoColorSpaceFactory* item)
{
+ {
+ QReadLocker l(&d->registrylock);
+ auto connectionProfileRequests = d->colorConversionSystem->requiredConnectionProfilesFor(item);
+
+ // the actual connection profiles should be added without any lock held
+ l.unlock();
+
+ for (auto it = connectionProfileRequests.begin(); it != connectionProfileRequests.end(); ++it) {
+ const KoColorProfile *profile = this->profileFor(*it, true); // auto-generate all the required profiles
+ KIS_SAFE_ASSERT_RECOVER_NOOP(profile);
+ }
+ }
+
QWriteLocker l(&d->registrylock);
d->colorSpaceFactoryRegistry.add(item);
d->colorConversionSystem->insertColorSpace(item);
@@ -320,6 +331,20 @@ void KoColorSpaceRegistry::addProfile(KoColorProfile *p)
{
if (!p->valid()) return;
+ {
+ QReadLocker l(&d->registrylock);
+ auto connectionProfileRequests = d->colorConversionSystem->requiredConnectionProfilesFor(p);
+
+ // the actual connection profiles should be added without any lock held
+ l.unlock();
+
+ for (auto it = connectionProfileRequests.begin(); it != connectionProfileRequests.end(); ++it) {
+ const KoColorProfile *profile = this->profileFor(*it, true); // auto-generate all the required profiles
+ KIS_SAFE_ASSERT_RECOVER_NOOP(profile);
+ }
+
+ }
+
QWriteLocker locker(&d->registrylock);
if (p->valid()) {
addProfileToMap(p);
@@ -701,6 +726,14 @@ const KoColorProfile *KoColorSpaceRegistry::p709SRGBProfile() const
}
const KoColorProfile *KoColorSpaceRegistry::profileFor(const KoColorProfileQuery &query, const bool generate) const
+{
+ // We're currently disabling on-the-fly generation of PQ profiles, because it can cause
+ // some issues with inconsistent hdr-reference-white value, i.e. the user of this
+ // code may get nits value he/she didn't expect
+ return profileForInternal(query, generate && query.transfer != TRC_ITU_R_BT_2100_0_PQ);
+}
+
+const KoColorProfile *KoColorSpaceRegistry::profileForInternal(const KoColorProfileQuery &query, const bool generate) const
{
if (query.primaries == PRIMARIES_ITU_R_BT_709_5) {
if (query.transfer == TRC_IEC_61966_2_1) {
@@ -726,8 +759,8 @@ const KoColorProfile *KoColorSpaceRegistry::profileFor(const KoColorProfileQuery
}
KoColorSpaceEngine *engine = KoColorSpaceEngineRegistry::instance()->get("icc");
- // We're disabling custom generation of PQ profiles for now.
- if (engine && generate && !(query.hdrReferenceWhite && query.transfer == TRC_ITU_R_BT_2100_0_PQ)) {
+
+ if (engine && generate) {
return engine->getProfile(query);
}
@@ -967,22 +1000,6 @@ QList<KoID> KoColorSpaceRegistry::listKeys() const
return answer;
}
-struct KoColorSpaceRegistry::Private::ProfileRegistrationInterface : public KoColorSpaceFactory::ProfileRegistrationInterface
-{
- ProfileRegistrationInterface(KoColorSpaceRegistry::Private *_d) : d(_d) {}
-
- const KoColorProfile* profileByName(const QString &profileName) const override {
- return d->profileStorage.profileByName(profileName);
- }
-
- void registerNewProfile(KoColorProfile *profile) override {
- d->profileStorage.addProfile(profile);
- d->colorConversionSystem->insertColorProfile(profile);
- }
-
- KoColorSpaceRegistry::Private *d {nullptr};
-};
-
const KoColorProfile* KoColorSpaceRegistry::createColorProfile(const QString& colorModelId, const QString& colorDepthId, const QByteArray& rawData)
{
return createColorProfile(colorModelId, colorDepthId, rawData, {});
@@ -990,11 +1007,26 @@ const KoColorProfile* KoColorSpaceRegistry::createColorProfile(const QString& co
const KoColorProfile* KoColorSpaceRegistry::createColorProfile(const QString & colorModelId, const QString & colorDepthId, const QByteArray& rawData, CustomProfileNameAlias customProfileNameAlias)
{
- QWriteLocker l(&d->registrylock);
+ QReadLocker l(&d->registrylock);
+
KoColorSpaceFactory* factory_ = d->colorSpaceFactoryRegistry.get(d->colorSpaceIdImpl(colorModelId, colorDepthId));
+ std::unique_ptr<KoColorProfile> profile(factory_->createColorProfile(rawData));
+
+ l.unlock();
+
+ if (profile && profile->valid()) {
+ const QString effectiveProfileName = customProfileNameAlias.value(profile->name(), profile->name());
+ if (const KoColorProfile* existingProfile = profileByName(effectiveProfileName)) {
+ return existingProfile;
+ }
- Private::ProfileRegistrationInterface interface(d);
- return factory_->colorProfile(rawData, &interface, customProfileNameAlias);
+ const KoColorProfile *constProfile = profile.get();
+ this->addProfile(profile.release());
+
+ return constProfile;
+ }
+
+ return nullptr;
}
QList<const KoColorSpace*> KoColorSpaceRegistry::allColorSpaces(ColorSpaceListVisibility visibility, ColorSpaceListProfilesSelection pSelection)
diff --git a/libs/pigment/KoColorSpaceRegistry.h b/libs/pigment/KoColorSpaceRegistry.h
index bcf54837d0d..86341990df0 100644
--- a/libs/pigment/KoColorSpaceRegistry.h
+++ b/libs/pigment/KoColorSpaceRegistry.h
@@ -127,6 +127,11 @@ public:
*/
const KoColorProfile *createColorProfile(const QString & colorModelId, const QString & colorDepthId, const QByteArray& rawData);
+ /**
+ * Create a color profile from a memory array, if possible, otherwise return 0.
+ * If there is an existing profile with the same name, it will be used instead,
+ * and the binary data from \p rawData will be ignored
+ */
using CustomProfileNameAlias = QHash<QString, QString>;
const KoColorProfile *createColorProfile(const QString & colorModelId, const QString & colorDepthId, const QByteArray& rawData, CustomProfileNameAlias customProfileNameAlias);
@@ -419,6 +424,7 @@ private:
friend class TestKoColorSpaceSanity;
friend class TestColorConversionSystem;
friend struct FriendOfColorSpaceRegistry;
+ friend class TestProfileGeneration;
/**
* @return a list with an instance of all color space with their default profile.
@@ -434,6 +440,8 @@ private:
*/
const KoColorConversionSystem* colorConversionSystem() const;
+ const KoColorProfile *profileForInternal(const KoColorProfileQuery &query, const bool generate) const;
+
private:
KoColorSpaceRegistry(const KoColorSpaceRegistry&);
KoColorSpaceRegistry operator=(const KoColorSpaceRegistry&);
diff --git a/plugins/color/lcms2engine/colorspaces/rgb_f16/RgbF16ColorSpace.cpp b/plugins/color/lcms2engine/colorspaces/rgb_f16/RgbF16ColorSpace.cpp
index d60204790d2..8bf3fd7936b 100644
--- a/plugins/color/lcms2engine/colorspaces/rgb_f16/RgbF16ColorSpace.cpp
+++ b/plugins/color/lcms2engine/colorspaces/rgb_f16/RgbF16ColorSpace.cpp
@@ -118,6 +118,15 @@ void RgbF16ColorSpace::modulateLightnessByGrayBrush(quint8 *dst, const QRgb *bru
modulateLightnessByGrayBrushRGB<KoRgbF16Traits>(dst, brush, strength, nPixels);
}
+QList<KoColorProfileQuery> RgbF16ColorSpaceFactory::requiredConnectionProfiles(const KoColorProfile *profile) const
+{
+ if (profile->getTransferCharacteristics() == TRC_ITU_R_BT_2100_0_PQ
+ && profile->getColorPrimaries() != PRIMARIES_UNSPECIFIED) {
+ return {KoColorProfileQuery(profile->getColorPrimaries(), TRC_LINEAR)};
+ }
+ return {};
+}
+
QList<KoColorConversionTransformationFactory *> RgbF16ColorSpaceFactory::colorConversionLinksFromProfile(const KoColorProfile *profile) const
{
if (profile->getTransferCharacteristics() == TRC_ITU_R_BT_2100_0_PQ
@@ -125,9 +134,13 @@ QList<KoColorConversionTransformationFactory *> RgbF16ColorSpaceFactory::colorCo
KoColorSpaceRegistry *registry = KoColorSpaceRegistry::instance();
KoColorProfileQuery query(profile->getColorPrimaries(), TRC_LINEAR);
- QString linear = registry->profileFor(query)->name();
- LcmsRGBP2020PQColorSpaceFactoryWrapper<RgbF16ColorSpaceFactory> factory(profile->name(), linear);
+ /// we cannot generate a profile at this stage, it should have been generated
+ /// by the registry thanks to the call to requiredConnectionProfiles()
+ const KoColorProfile *connectionProfile = registry->profileFor(query, false);
+ KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(connectionProfile, QList<KoColorConversionTransformationFactory *>());
+
+ LcmsRGBP2020PQColorSpaceFactoryWrapper<RgbF16ColorSpaceFactory> factory(profile->name(), connectionProfile->name());
return factory.colorConversionLinks();
} else if (profile->name() == "High Dynamic Range UHDTV Wide Color Gamut Display (Rec. 2020) - SMPTE ST 2084 PQ EOTF") {
KoColorSpaceRegistry *registry = KoColorSpaceRegistry::instance();
diff --git a/plugins/color/lcms2engine/colorspaces/rgb_f16/RgbF16ColorSpace.h b/plugins/color/lcms2engine/colorspaces/rgb_f16/RgbF16ColorSpace.h
index 50a08005f25..c0c69ca2cd9 100644
--- a/plugins/color/lcms2engine/colorspaces/rgb_f16/RgbF16ColorSpace.h
+++ b/plugins/color/lcms2engine/colorspaces/rgb_f16/RgbF16ColorSpace.h
@@ -107,6 +107,7 @@ public:
return true;
}
+ QList<KoColorProfileQuery> requiredConnectionProfiles(const KoColorProfile *profile) const override;
QList<KoColorConversionTransformationFactory*> colorConversionLinksFromProfile(const KoColorProfile *profile) const override;
};
diff --git a/plugins/color/lcms2engine/colorspaces/rgb_f32/RgbF32ColorSpace.cpp b/plugins/color/lcms2engine/colorspaces/rgb_f32/RgbF32ColorSpace.cpp
index 9859cebb9cd..11edcc2f39d 100644
--- a/plugins/color/lcms2engine/colorspaces/rgb_f32/RgbF32ColorSpace.cpp
+++ b/plugins/color/lcms2engine/colorspaces/rgb_f32/RgbF32ColorSpace.cpp
@@ -122,6 +122,14 @@ void RgbF32ColorSpace::modulateLightnessByGrayBrush(quint8 *dst, const QRgb *bru
modulateLightnessByGrayBrushRGB<KoRgbF32Traits>(dst, brush, strength, nPixels);
}
+QList<KoColorProfileQuery> RgbF32ColorSpaceFactory::requiredConnectionProfiles(const KoColorProfile *profile) const
+{
+ if (profile->getTransferCharacteristics() == TRC_ITU_R_BT_2100_0_PQ
+ && profile->getColorPrimaries() != PRIMARIES_UNSPECIFIED) {
+ return {KoColorProfileQuery(profile->getColorPrimaries(), TRC_LINEAR)};
+ }
+ return {};
+}
QList<KoColorConversionTransformationFactory *> RgbF32ColorSpaceFactory::colorConversionLinksFromProfile(const KoColorProfile *profile) const
{
@@ -130,9 +138,13 @@ QList<KoColorConversionTransformationFactory *> RgbF32ColorSpaceFactory::colorCo
KoColorSpaceRegistry *registry = KoColorSpaceRegistry::instance();
KoColorProfileQuery query(profile->getColorPrimaries(), TRC_LINEAR);
- QString linear = registry->profileFor(query)->name();
- LcmsRGBP2020PQColorSpaceFactoryWrapper<RgbF32ColorSpaceFactory> factory(profile->name(), linear);
+ /// we cannot generate a profile at this stage, it should have been generated
+ /// by the registry thanks to the call to requiredConnectionProfiles()
+ const KoColorProfile *connectionProfile = registry->profileFor(query, false);
+ KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(connectionProfile, QList<KoColorConversionTransformationFactory *>());
+
+ LcmsRGBP2020PQColorSpaceFactoryWrapper<RgbF32ColorSpaceFactory> factory(profile->name(), connectionProfile->name());
return factory.colorConversionLinks();
} else if (profile->name() == "High Dynamic Range UHDTV Wide Color Gamut Display (Rec. 2020) - SMPTE ST 2084 PQ EOTF") {
KoColorSpaceRegistry *registry = KoColorSpaceRegistry::instance();
diff --git a/plugins/color/lcms2engine/colorspaces/rgb_f32/RgbF32ColorSpace.h b/plugins/color/lcms2engine/colorspaces/rgb_f32/RgbF32ColorSpace.h
index d43301b21b8..b215de1c52a 100644
--- a/plugins/color/lcms2engine/colorspaces/rgb_f32/RgbF32ColorSpace.h
+++ b/plugins/color/lcms2engine/colorspaces/rgb_f32/RgbF32ColorSpace.h
@@ -107,6 +107,7 @@ public:
return true;
}
+ QList<KoColorProfileQuery> requiredConnectionProfiles(const KoColorProfile *profile) const override;
QList<KoColorConversionTransformationFactory*> colorConversionLinksFromProfile(const KoColorProfile *profile) const override;
};
diff --git a/plugins/color/lcms2engine/colorspaces/rgb_u16/RgbU16ColorSpace.cpp b/plugins/color/lcms2engine/colorspaces/rgb_u16/RgbU16ColorSpace.cpp
index ff94971bd27..922e8edab5c 100644
--- a/plugins/color/lcms2engine/colorspaces/rgb_u16/RgbU16ColorSpace.cpp
+++ b/plugins/color/lcms2engine/colorspaces/rgb_u16/RgbU16ColorSpace.cpp
@@ -115,6 +115,15 @@ void RgbU16ColorSpace::modulateLightnessByGrayBrush(quint8 *dst, const QRgb *bru
modulateLightnessByGrayBrushRGB<KoBgrU16Traits>(dst, brush, strength, nPixels);
}
+QList<KoColorProfileQuery> RgbU16ColorSpaceFactory::requiredConnectionProfiles(const KoColorProfile *profile) const
+{
+ if (profile->getTransferCharacteristics() == TRC_ITU_R_BT_2100_0_PQ
+ && profile->getColorPrimaries() != PRIMARIES_UNSPECIFIED) {
+ return {KoColorProfileQuery(profile->getColorPrimaries(), TRC_LINEAR)};
+ }
+ return {};
+}
+
QList<KoColorConversionTransformationFactory *> RgbU16ColorSpaceFactory::colorConversionLinksFromProfile(const KoColorProfile *profile) const
{
if (profile->getTransferCharacteristics() == TRC_ITU_R_BT_2100_0_PQ
@@ -122,9 +131,13 @@ QList<KoColorConversionTransformationFactory *> RgbU16ColorSpaceFactory::colorCo
KoColorSpaceRegistry *registry = KoColorSpaceRegistry::instance();
KoColorProfileQuery query(profile->getColorPrimaries(), TRC_LINEAR);
- QString linear = registry->profileFor(query)->name();
- LcmsRGBP2020PQColorSpaceFactoryWrapper<RgbU16ColorSpaceFactory> factory(profile->name(), linear);
+ /// we cannot generate a profile at this stage, it should have been generated
+ /// by the registry thanks to the call to requiredConnectionProfiles()
+ const KoColorProfile *connectionProfile = registry->profileFor(query, false);
+ KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(connectionProfile, QList<KoColorConversionTransformationFactory *>());
+
+ LcmsRGBP2020PQColorSpaceFactoryWrapper<RgbU16ColorSpaceFactory> factory(profile->name(), connectionProfile->name());
return factory.colorConversionLinks();
} else if (profile->name() == "High Dynamic Range UHDTV Wide Color Gamut Display (Rec. 2020) - SMPTE ST 2084 PQ EOTF") {
KoColorSpaceRegistry *registry = KoColorSpaceRegistry::instance();
diff --git a/plugins/color/lcms2engine/colorspaces/rgb_u16/RgbU16ColorSpace.h b/plugins/color/lcms2engine/colorspaces/rgb_u16/RgbU16ColorSpace.h
index 83e4a848ff4..1d05cbf66db 100644
--- a/plugins/color/lcms2engine/colorspaces/rgb_u16/RgbU16ColorSpace.h
+++ b/plugins/color/lcms2engine/colorspaces/rgb_u16/RgbU16ColorSpace.h
@@ -91,6 +91,7 @@ public:
return "sRGB-elle-V2-g10.icc";//this is a linear space, because 16bit is enough to only enjoy advantages of linear space
}
+ QList<KoColorProfileQuery> requiredConnectionProfiles(const KoColorProfile *profile) const override;
QList<KoColorConversionTransformationFactory*> colorConversionLinksFromProfile(const KoColorProfile *profile) const override;
};
diff --git a/plugins/color/lcms2engine/colorspaces/rgb_u8/RgbU8ColorSpace.cpp b/plugins/color/lcms2engine/colorspaces/rgb_u8/RgbU8ColorSpace.cpp
index e38472296ee..3b55083a22a 100644
--- a/plugins/color/lcms2engine/colorspaces/rgb_u8/RgbU8ColorSpace.cpp
+++ b/plugins/color/lcms2engine/colorspaces/rgb_u8/RgbU8ColorSpace.cpp
@@ -130,6 +130,15 @@ void RgbU8ColorSpace::modulateLightnessByGrayBrush(quint8 *dst, const QRgb *brus
modulateLightnessByGrayBrushRGB<KoBgrU8Traits>(dst, brush, strength, nPixels);
}
+QList<KoColorProfileQuery> RgbU8ColorSpaceFactory::requiredConnectionProfiles(const KoColorProfile *profile) const
+{
+ if (profile->getTransferCharacteristics() == TRC_ITU_R_BT_2100_0_PQ
+ && profile->getColorPrimaries() != PRIMARIES_UNSPECIFIED) {
+ return {KoColorProfileQuery(profile->getColorPrimaries(), TRC_LINEAR)};
+ }
+ return {};
+}
+
QList<KoColorConversionTransformationFactory*> RgbU8ColorSpaceFactory::colorConversionLinksFromProfile(const KoColorProfile *profile) const
{
if (profile->getTransferCharacteristics() == TRC_ITU_R_BT_2100_0_PQ
@@ -137,9 +146,13 @@ QList<KoColorConversionTransformationFactory*> RgbU8ColorSpaceFactory::colorConv
KoColorSpaceRegistry *registry = KoColorSpaceRegistry::instance();
KoColorProfileQuery query(profile->getColorPrimaries(), TRC_LINEAR);
- QString linear = registry->profileFor(query)->name();
- LcmsRGBP2020PQColorSpaceFactoryWrapper<RgbU8ColorSpaceFactory> factory(profile->name(), linear);
+ /// we cannot generate a profile at this stage, it should have been generated
+ /// by the registry thanks to the call to requiredConnectionProfiles()
+ const KoColorProfile *connectionProfile = registry->profileFor(query, false);
+ KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(connectionProfile, QList<KoColorConversionTransformationFactory *>());
+
+ LcmsRGBP2020PQColorSpaceFactoryWrapper<RgbU8ColorSpaceFactory> factory(profile->name(), connectionProfile->name());
return factory.colorConversionLinks();
} else if (profile->name() == "High Dynamic Range UHDTV Wide Color Gamut Display (Rec. 2020) - SMPTE ST 2084 PQ EOTF") {
KoColorSpaceRegistry *registry = KoColorSpaceRegistry::instance();
diff --git a/plugins/color/lcms2engine/colorspaces/rgb_u8/RgbU8ColorSpace.h b/plugins/color/lcms2engine/colorspaces/rgb_u8/RgbU8ColorSpace.h
index e1b327051fe..6542f3c1e8a 100644
--- a/plugins/color/lcms2engine/colorspaces/rgb_u8/RgbU8ColorSpace.h
+++ b/plugins/color/lcms2engine/colorspaces/rgb_u8/RgbU8ColorSpace.h
@@ -105,6 +105,7 @@ public:
return "sRGB-elle-V2-srgbtrc.icc";
}
+ QList<KoColorProfileQuery> requiredConnectionProfiles(const KoColorProfile *profile) const override;
QList<KoColorConversionTransformationFactory*> colorConversionLinksFromProfile(const KoColorProfile *profile) const override;
};
diff --git a/plugins/color/lcms2engine/tests/TestProfileGeneration.cpp b/plugins/color/lcms2engine/tests/TestProfileGeneration.cpp
index 007945f039f..7fa9c508845 100644
--- a/plugins/color/lcms2engine/tests/TestProfileGeneration.cpp
+++ b/plugins/color/lcms2engine/tests/TestProfileGeneration.cpp
@@ -415,21 +415,57 @@ void TestProfileGeneration::testCICPwriting()
void TestProfileGeneration::testRetrieveNits_data()
{
- QTest::addColumn<double>("expectedReferenceWhite");
- QTest::addRow("rec2100PQ 80nits") << 80.0;
- QTest::addRow("rec2100PQ 203nits") << 203.0;
+ QTest::addColumn<ColorPrimaries>("primaries");
+ QTest::addColumn<TransferCharacteristics>("transfer");
+ QTest::addColumn<qreal>("requestedReferenceWhite");
+ QTest::addColumn<qreal>("expectedReferenceWhite");
+
+ // SDR profiles should have no hdr reference white value
+ QTest::addRow("rec709-srgbtrc") << PRIMARIES_ITU_R_BT_709_5 << TRC_IEC_61966_2_1 << -1.0 << -1.0;
+
+ // Scene-linear profiles should also have **no** reference white value, since it
+ // is meaningless for them, their value of 1.0 is pinned to the reference white point
+ QTest::addRow("rec2020-g10") << PRIMARIES_ITU_R_BT_2020_2_AND_2100_0 << TRC_LINEAR << -1.0 << -1.0;
+ QTest::addRow("rec709-g10") << PRIMARIES_ITU_R_BT_709_5 << TRC_LINEAR << -1.0 << -1.0;
+
+ // HDR profiles should have one
+
+ QTest::addRow("rec2100 PQ default nits") << PRIMARIES_ITU_R_BT_2020_2_AND_2100_0 << TRC_ITU_R_BT_2100_0_PQ << -1.0 << 203.0;
+ QTest::addRow("rec2100 PQ 80 nits") << PRIMARIES_ITU_R_BT_2020_2_AND_2100_0 << TRC_ITU_R_BT_2100_0_PQ << 80.0 << 80.0;
+ QTest::addRow("rec2100 PQ 203 nits") << PRIMARIES_ITU_R_BT_2020_2_AND_2100_0 << TRC_ITU_R_BT_2100_0_PQ << 203.0 << 203.0;
+
+ // Display P3 profile is generated on the fly
+ QTest::addRow("display-p3 srgbtrc") << PRIMARIES_SMPTE_EG_432_1 << TRC_IEC_61966_2_1 << -1.0 << -1.0;
+
+ // Display P3 PQ is also generated on the fly and should not cause any deadlocks
+ // when connection linear profiles are added to the registry
+ QTest::addRow("display-p3 PQ default nits") << PRIMARIES_SMPTE_EG_432_1 << TRC_ITU_R_BT_2100_0_PQ << -1.0 << 203.0;
+ QTest::addRow("display-p3 PQ 80 nits") << PRIMARIES_SMPTE_EG_432_1 << TRC_ITU_R_BT_2100_0_PQ << 80.0 << 80.0;
+ QTest::addRow("display-p3 PQ 203 nits") << PRIMARIES_SMPTE_EG_432_1 << TRC_ITU_R_BT_2100_0_PQ << 203.0 << 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);
-
+ QFETCH(ColorPrimaries, primaries);
+ QFETCH(TransferCharacteristics, transfer);
+ QFETCH(qreal, requestedReferenceWhite);
+ QFETCH(qreal, expectedReferenceWhite);
+ KoColorProfileQuery query(primaries, transfer);
+ if (requestedReferenceWhite > 0) {
+ query.hdrReferenceWhite = requestedReferenceWhite;
+ }
+ const KoColorProfile *profile = KoColorSpaceRegistry::instance()->profileForInternal(query, true);
QVERIFY(profile);
- QVERIFY(profile->hdrReferenceWhite());
- QVERIFY(qFuzzyCompare(*profile->hdrReferenceWhite(), expectedReferenceWhite));
+
+ QCOMPARE(profile->getColorPrimaries(), primaries);
+ QCOMPARE(profile->getTransferCharacteristics(), transfer);
+
+ if (expectedReferenceWhite > 0) {
+ QVERIFY(profile->hdrReferenceWhite().has_value());
+ QCOMPARE(*profile->hdrReferenceWhite(), expectedReferenceWhite);
+ } else {
+ QVERIFY(!profile->hdrReferenceWhite().has_value());
+ }
}
KISTEST_MAIN(TestProfileGeneration)