File indexing completed on 2024-05-12 15:59:35

0001 /*
0002  *  SPDX-FileCopyrightText: 2003 Patrick Julien <freak@codepimps.org>
0003  *  SPDX-FileCopyrightText: 2004, 2010 Cyrille Berger <cberger@cberger.net>
0004  *
0005  * SPDX-License-Identifier: LGPL-2.1-or-later
0006 */
0007 
0008 #include "KoColorSpaceRegistry.h"
0009 
0010 #include <QHash>
0011 
0012 #include <QReadWriteLock>
0013 #include <QStringList>
0014 #include <QDir>
0015 #include <QGlobalStatic>
0016 
0017 #include "KoPluginLoader.h"
0018 #include "KoGenericRegistry.h"
0019 #include "DebugPigment.h"
0020 #include "KoBasicHistogramProducers.h"
0021 #include "KoColorSpace.h"
0022 #include "KoColorProfile.h"
0023 #include "KoColorConversionCache.h"
0024 #include "KoColorConversionSystem.h"
0025 
0026 #include "colorspaces/KoAlphaColorSpace.h"
0027 #include "colorspaces/KoLabColorSpace.h"
0028 #include "colorspaces/KoRgbU16ColorSpace.h"
0029 #include "colorspaces/KoRgbU8ColorSpace.h"
0030 #include "colorspaces/KoSimpleColorSpaceEngine.h"
0031 #include "KoColorSpace_p.h"
0032 
0033 #include "kis_assert.h"
0034 #include "KoColorProfileStorage.h"
0035 #include <KisReadWriteLockPolicy.h>
0036 
0037 #include <KoColorModelStandardIds.h>
0038 
0039 Q_GLOBAL_STATIC(KoColorSpaceRegistry, s_instance)
0040 
0041 
0042 struct Q_DECL_HIDDEN KoColorSpaceRegistry::Private {
0043 
0044     // interface for KoColorSpaceFactory
0045     struct ProfileRegistrationInterface;
0046     // interface for KoColorConversionSystem
0047     struct ConversionSystemInterface;
0048 
0049 
0050     Private(KoColorSpaceRegistry *_q) : q(_q) {}
0051 
0052     KoColorSpaceRegistry *q {nullptr};
0053 
0054     KoGenericRegistry<KoColorSpaceFactory *> colorSpaceFactoryRegistry;
0055     KoColorProfileStorage profileStorage;
0056     QHash<QString, const KoColorSpace *> csMap;
0057     QScopedPointer<ConversionSystemInterface> conversionSystemInterface;
0058     KoColorConversionSystem *colorConversionSystem {nullptr};
0059     KoColorConversionCache* colorConversionCache {nullptr};
0060     const KoColorSpace *rgbU8sRGB {nullptr};
0061     const KoColorSpace *lab16sLAB {nullptr};
0062     const KoColorSpace *alphaCs {nullptr};
0063     const KoColorSpace *alphaU16Cs {nullptr};
0064 #ifdef HAVE_OPENEXR
0065     const KoColorSpace *alphaF16Cs {nullptr};
0066 #endif
0067     const KoColorSpace *alphaF32Cs {nullptr};
0068     QReadWriteLock registrylock;
0069 
0070     /**
0071      * The function checks if a colorspace with a certain id and profile name can be found in the cache
0072      * NOTE: the function doesn't take any lock but it needs to be called inside a d->registryLock
0073      * locked either in read or write.
0074      * @param csId The colorspace id
0075      * @param profileName The colorspace profile name
0076      * @retval KoColorSpace The matching colorspace
0077      * @retval 0 Null pointer if not match
0078      */
0079     const KoColorSpace* getCachedColorSpaceImpl(const QString & csId, const QString & profileName) const;
0080 
0081     QString idsToCacheName(const QString & csId, const QString & profileName) const;
0082     QString defaultProfileForCsIdImpl(const QString &csID);
0083     const KoColorProfile * profileForCsIdWithFallbackImpl(const QString &csID, const QString &profileName);
0084     QString colorSpaceIdImpl(const QString & colorModelId, const QString & colorDepthId) const;
0085 
0086     const KoColorSpace *lazyCreateColorSpaceImpl(const QString &csID, const KoColorProfile *profile);
0087 
0088     /**
0089      * Return a colorspace that works with the parameter profile.
0090      * @param profileName the name of the KoColorProfile to be combined with the colorspace
0091      * @return the wanted colorspace, or 0 when the cs and profile can not be combined.
0092      */
0093     template<class LockPolicy = NormalLockPolicy>
0094     const KoColorSpace * colorSpace1(const QString &colorSpaceId, const QString &pName = QString());
0095 
0096     /**
0097      * Return a colorspace that works with the parameter profile.
0098      * @param colorSpaceId the ID string of the colorspace that you want to have returned
0099      * @param profile the profile be combined with the colorspace
0100      * @return the wanted colorspace, or 0 when the cs and profile can not be combined.
0101      */
0102     const KoColorSpace * colorSpace1(const QString &colorSpaceId, const KoColorProfile *profile);
0103 };
0104 
0105 struct KoColorSpaceRegistry::Private::ConversionSystemInterface : public KoColorConversionSystem::RegistryInterface
0106 {
0107     ConversionSystemInterface(KoColorSpaceRegistry *parentRegistry)
0108         : q(parentRegistry)
0109     {
0110     }
0111 
0112     const KoColorSpace * colorSpace(const QString & colorModelId, const QString & colorDepthId, const QString &profileName) override {
0113         return q->d->colorSpace1<NoLockPolicy>(q->d->colorSpaceIdImpl(colorModelId, colorDepthId), profileName);
0114     }
0115 
0116     const KoColorSpaceFactory* colorSpaceFactory(const QString &colorModelId, const QString &colorDepthId) const override {
0117         return q->d->colorSpaceFactoryRegistry.get(q->d->colorSpaceIdImpl(colorModelId, colorDepthId));
0118     }
0119 
0120     QList<const KoColorProfile *>  profilesFor(const KoColorSpaceFactory * csf) const override {
0121         return q->d->profileStorage.profilesFor(csf);
0122     }
0123 
0124     QList<const KoColorSpaceFactory*> colorSpacesFor(const KoColorProfile* profile) const override {
0125         QList<const KoColorSpaceFactory*> csfs;
0126         Q_FOREACH (KoColorSpaceFactory* csf, q->d->colorSpaceFactoryRegistry.values()) {
0127             if (csf->profileIsCompatible(profile)) {
0128                 csfs.push_back(csf);
0129             }
0130         }
0131         return csfs;
0132     }
0133 
0134 private:
0135     KoColorSpaceRegistry *q {nullptr};
0136 };
0137 
0138 KoColorSpaceRegistry* KoColorSpaceRegistry::instance()
0139 {
0140     if (!s_instance.exists()) {
0141         s_instance->init();
0142     }
0143     return s_instance;
0144 }
0145 
0146 
0147 void KoColorSpaceRegistry::init()
0148 {
0149     d->rgbU8sRGB = 0;
0150     d->lab16sLAB = 0;
0151     d->alphaCs = 0;
0152     d->alphaU16Cs = 0;
0153 #ifdef HAVE_OPENEXR
0154     d->alphaF16Cs = 0;
0155 #endif
0156     d->alphaF32Cs = 0;
0157 
0158     d->conversionSystemInterface.reset(new Private::ConversionSystemInterface(this));
0159     d->colorConversionSystem = new KoColorConversionSystem(d->conversionSystemInterface.data());
0160     d->colorConversionCache = new KoColorConversionCache;
0161 
0162     KoColorSpaceEngineRegistry::instance()->add(new KoSimpleColorSpaceEngine());
0163 
0164     addProfile(new KoDummyColorProfile);
0165 
0166     // Create the built-in colorspaces
0167     QList<KoColorSpaceFactory *> localFactories;
0168     localFactories
0169             << new KoAlphaColorSpaceFactory()
0170             << new KoAlphaU16ColorSpaceFactory()
0171            #ifdef HAVE_OPENEXR
0172             << new KoAlphaF16ColorSpaceFactory()
0173            #endif
0174             << new KoAlphaF32ColorSpaceFactory()
0175             << new KoLabColorSpaceFactory()
0176             << new KoRgbU8ColorSpaceFactory()
0177             << new KoRgbU16ColorSpaceFactory();
0178 
0179     Q_FOREACH (KoColorSpaceFactory *factory, localFactories) {
0180         add(factory);
0181     }
0182 
0183     KoPluginLoader::PluginsConfig config;
0184     config.whiteList = "ColorSpacePlugins";
0185     config.blacklist = "ColorSpacePluginsDisabled";
0186     config.group = "krita";
0187     KoPluginLoader::instance()->load("Krita/ColorSpace", "[X-Pigment-PluginVersion] == 28", config);
0188 
0189     KoPluginLoader::PluginsConfig configExtensions;
0190     configExtensions.whiteList = "ColorSpaceExtensionsPlugins";
0191     configExtensions.blacklist = "ColorSpaceExtensionsPluginsDisabled";
0192     configExtensions.group = "krita";
0193     KoPluginLoader::instance()->load("Krita/ColorSpaceExtension", "[X-Pigment-PluginVersion] == 28", configExtensions);
0194 
0195 
0196     dbgPigment << "Loaded the following colorspaces:";
0197     Q_FOREACH (const KoID& id, listKeys()) {
0198         dbgPigment << "\t" << id.id() << "," << id.name();
0199     }
0200 }
0201 
0202 KoColorSpaceRegistry::KoColorSpaceRegistry() : d(new Private(this))
0203 {
0204     d->colorConversionSystem = 0;
0205     d->colorConversionCache = 0;
0206 }
0207 
0208 KoColorSpaceRegistry::~KoColorSpaceRegistry()
0209 {
0210     // Just leak on exit... It's faster.
0211 //    delete d->colorConversionSystem;
0212 //    Q_FOREACH (KoColorProfile* profile, d->profileMap) {
0213 //        delete profile;
0214 //    }
0215 //    d->profileMap.clear();
0216 
0217 //    Q_FOREACH (const KoColorSpace * cs, d->csMap) {
0218 //        cs->d->deletability = OwnedByRegistryRegistryDeletes;
0219 //    }
0220 //    d->csMap.clear();
0221 
0222 //    // deleting colorspaces calls a function in the cache
0223 //    delete d->colorConversionCache;
0224 //    d->colorConversionCache = 0;
0225 
0226 //    // Delete the colorspace factories
0227 //    qDeleteAll(d->localFactories);
0228 
0229     delete d;
0230 }
0231 
0232 void KoColorSpaceRegistry::add(KoColorSpaceFactory* item)
0233 {
0234     QWriteLocker l(&d->registrylock);
0235     d->colorSpaceFactoryRegistry.add(item);
0236     d->colorConversionSystem->insertColorSpace(item);
0237 }
0238 
0239 void KoColorSpaceRegistry::remove(KoColorSpaceFactory* item)
0240 {
0241     QWriteLocker l(&d->registrylock);
0242 
0243     QList<QString> toremove;
0244     Q_FOREACH (const KoColorSpace * cs, d->csMap) {
0245         if (cs->id() == item->id()) {
0246             toremove.push_back(d->idsToCacheName(cs->id(), cs->profile()->name()));
0247             cs->d->deletability = OwnedByRegistryRegistryDeletes;
0248         }
0249     }
0250 
0251     Q_FOREACH (const QString& id, toremove) {
0252         d->csMap.remove(id);
0253         // TODO: should not it delete the color space when removing it from the map ?
0254     }
0255     d->colorSpaceFactoryRegistry.remove(item->id());
0256 }
0257 
0258 void KoColorSpaceRegistry::addProfileAlias(const QString& name, const QString& to)
0259 {
0260     d->profileStorage.addProfileAlias(name, to);
0261 }
0262 
0263 QString KoColorSpaceRegistry::profileAlias(const QString& name) const
0264 {
0265     return d->profileStorage.profileAlias(name);
0266 }
0267 
0268 const KoColorProfile*  KoColorSpaceRegistry::profileByName(const QString &name) const
0269 {
0270     return d->profileStorage.profileByName(name);
0271 }
0272 
0273 const KoColorProfile *  KoColorSpaceRegistry::profileByUniqueId(const QByteArray &id) const
0274 {
0275     return d->profileStorage.profileByUniqueId(id);
0276 }
0277 
0278 QList<const KoColorProfile *>  KoColorSpaceRegistry::profilesFor(const QString &csID) const
0279 {
0280     QReadLocker l(&d->registrylock);
0281     return d->profileStorage.profilesFor(d->colorSpaceFactoryRegistry.value(csID));
0282 }
0283 
0284 const KoColorSpace * KoColorSpaceRegistry::colorSpace(const QString & colorModelId, const QString & colorDepthId, const KoColorProfile *profile)
0285 {
0286     return d->colorSpace1(colorSpaceId(colorModelId, colorDepthId), profile);
0287 }
0288 
0289 const KoColorSpace * KoColorSpaceRegistry::colorSpace(const QString & colorModelId, const QString & colorDepthId, const QString &profileName)
0290 {
0291     return d->colorSpace1(colorSpaceId(colorModelId, colorDepthId), profileName);
0292 }
0293 
0294 const KoColorSpace * KoColorSpaceRegistry::colorSpace(const QString & colorModelId, const QString & colorDepthId)
0295 {
0296     return d->colorSpace1(colorSpaceId(colorModelId, colorDepthId));
0297 }
0298 
0299 bool KoColorSpaceRegistry::profileIsCompatible(const KoColorProfile *profile, const QString &colorSpaceId)
0300 {
0301     QReadLocker l(&d->registrylock);
0302     KoColorSpaceFactory *csf = d->colorSpaceFactoryRegistry.value(colorSpaceId);
0303 
0304     return csf ? csf->profileIsCompatible(profile) : false;
0305 }
0306 
0307 void KoColorSpaceRegistry::addProfileToMap(KoColorProfile *p)
0308 {
0309     d->profileStorage.addProfile(p);
0310 }
0311 
0312 void KoColorSpaceRegistry::addProfile(KoColorProfile *p)
0313 {
0314     if (!p->valid()) return;
0315 
0316     QWriteLocker locker(&d->registrylock);
0317     if (p->valid()) {
0318         addProfileToMap(p);
0319         d->colorConversionSystem->insertColorProfile(p);
0320     }
0321 }
0322 
0323 void KoColorSpaceRegistry::addProfile(const KoColorProfile* profile)
0324 {
0325     addProfile(profile->clone());
0326 }
0327 
0328 void KoColorSpaceRegistry::removeProfile(KoColorProfile* profile)
0329 {
0330     d->profileStorage.removeProfile(profile);
0331     // FIXME: how about removing it from conversion system?
0332 }
0333 
0334 const KoColorSpace* KoColorSpaceRegistry::Private::getCachedColorSpaceImpl(const QString & csID, const QString & profileName) const
0335 {
0336     auto it = csMap.find(idsToCacheName(csID, profileName));
0337 
0338     if (it != csMap.end()) {
0339         return it.value();
0340     }
0341 
0342     return 0;
0343 }
0344 
0345 QString KoColorSpaceRegistry::Private::idsToCacheName(const QString & csID, const QString & profileName) const
0346 {
0347     return csID + "<comb>" + profileName;
0348 }
0349 
0350 QString KoColorSpaceRegistry::defaultProfileForColorSpace(const QString &colorSpaceId) const
0351 {
0352     QReadLocker l(&d->registrylock);
0353     return d->defaultProfileForCsIdImpl(colorSpaceId);
0354 }
0355 
0356 KoColorConversionTransformation *KoColorSpaceRegistry::createColorConverter(const KoColorSpace *srcColorSpace, const KoColorSpace *dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const
0357 {
0358     QWriteLocker l(&d->registrylock);
0359     return d->colorConversionSystem->createColorConverter(srcColorSpace, dstColorSpace, renderingIntent, conversionFlags);
0360 }
0361 
0362 void KoColorSpaceRegistry::createColorConverters(const KoColorSpace *colorSpace, const QList<QPair<KoID, KoID> > &possibilities, KoColorConversionTransformation *&fromCS, KoColorConversionTransformation *&toCS) const
0363 {
0364     QWriteLocker l(&d->registrylock);
0365     d->colorConversionSystem->createColorConverters(colorSpace, possibilities, fromCS, toCS);
0366 }
0367 
0368 QString KoColorSpaceRegistry::Private::defaultProfileForCsIdImpl(const QString &csID)
0369 {
0370     QString defaultProfileName;
0371 
0372     KoColorSpaceFactory *csf = colorSpaceFactoryRegistry.value(csID);
0373     if (csf) {
0374         defaultProfileName = csf->defaultProfile();
0375     } else {
0376         dbgPigmentCSRegistry << "Unknown color space type : " << csID;
0377     }
0378 
0379     return defaultProfileName;
0380 }
0381 
0382 const KoColorProfile *KoColorSpaceRegistry::Private::profileForCsIdWithFallbackImpl(const QString &csID, const QString &profileName)
0383 {
0384     const KoColorProfile *profile = 0;
0385 
0386     // last attempt at getting a profile, sometimes the default profile, like adobe cmyk isn't available.
0387     profile = profileStorage.profileByName(profileName);
0388     if (!profile) {
0389         dbgPigmentCSRegistry << "Profile not found :" << profileName;
0390 
0391         // first try: default
0392         profile = profileStorage.profileByName(defaultProfileForCsIdImpl(csID));
0393 
0394         if (!profile) {
0395             // second try: first profile in the list
0396             QList<const KoColorProfile *> profiles = profileStorage.profilesFor(colorSpaceFactoryRegistry.value(csID));
0397             if (profiles.isEmpty() || !profiles.first()) {
0398                 dbgPigmentCSRegistry << "Couldn't fetch a fallback profile:" << profileName;
0399                 qWarning() << "profileForCsIdWithFallbackImpl couldn't fetch a fallback profile for " << qUtf8Printable(profileName);
0400                 return 0;
0401             }
0402 
0403             profile = profiles.first();
0404         }
0405     }
0406 
0407     return profile;
0408 }
0409 
0410 const KoColorSpace *KoColorSpaceRegistry::Private::lazyCreateColorSpaceImpl(const QString &csID, const KoColorProfile *profile)
0411 {
0412     const KoColorSpace *cs = 0;
0413 
0414     /*
0415      * We need to check again here, a thread requesting the same colorspace could've added it
0416      * already, in between the read unlock and write lock.
0417      * TODO: We also potentially changed profileName content, which means we maybe are going to
0418      * create a colorspace that's actually in the space registry cache, but currently this might
0419      * not be an issue because the colorspace should be cached also by the factory, so it won't
0420      * create a new instance. That being said, having two caches with the same stuff doesn't make
0421      * much sense.
0422      */
0423     cs = getCachedColorSpaceImpl(csID, profile->name());
0424     if (!cs) {
0425         KoColorSpaceFactory *csf = colorSpaceFactoryRegistry.value(csID);
0426         if (!csf) {
0427             qWarning() << "Unable to create color space factory for" << csID;
0428             return 0;
0429         }
0430         cs = csf->grabColorSpace(profile);
0431         if (!cs) {
0432             dbgPigmentCSRegistry << "Unable to create color space";
0433             qWarning() << "lazyCreateColorSpaceImpl was unable to create a color space for " << csID;
0434             return 0;
0435         }
0436 
0437         dbgPigmentCSRegistry << "colorspace count: " << csMap.count()
0438                              << ", adding name: " << idsToCacheName(cs->id(), cs->profile()->name())
0439                              << "\n\tcsID" << csID
0440                              << "\n\tcs->id()" << cs->id()
0441                              << "\n\tcs->profile()->name()" << cs->profile()->name()
0442                              << "\n\tprofile->name()" << profile->name();
0443         Q_ASSERT(cs->id() == csID);
0444         Q_ASSERT(cs->profile()->name() == profile->name());
0445         csMap[idsToCacheName(cs->id(), cs->profile()->name())] = cs;
0446         cs->d->deletability = OwnedByRegistryDoNotDelete;
0447     }
0448 
0449     return cs;
0450 }
0451 
0452 template<class LockPolicy>
0453 const KoColorSpace * KoColorSpaceRegistry::Private::colorSpace1(const QString &csID, const QString &pName)
0454 {
0455     QString profileName = pName;
0456 
0457     const KoColorSpace *cs = 0;
0458 
0459     {
0460         typename LockPolicy::ReadLocker l(&registrylock);
0461 
0462         if (profileName.isEmpty()) {
0463             profileName = defaultProfileForCsIdImpl(csID);
0464         }
0465 
0466         if (!profileName.isEmpty()) {
0467             // quick attempt to fetch a cached color space
0468             cs = getCachedColorSpaceImpl(csID, profileName);
0469         }
0470     }
0471 
0472     if (!cs) {
0473         // slow attempt to create a color space
0474         typename LockPolicy::WriteLocker l(&registrylock);
0475 
0476         const KoColorProfile *profile =
0477             profileForCsIdWithFallbackImpl(csID, profileName);
0478 
0479         if (!profile) return 0;
0480 
0481         cs = lazyCreateColorSpaceImpl(csID, profile);
0482     }
0483     else {
0484         KIS_SAFE_ASSERT_RECOVER_NOOP(cs->id() == csID);
0485         KIS_SAFE_ASSERT_RECOVER_NOOP(cs->profile()->name() == profileName);
0486     }
0487 
0488     return cs;
0489 }
0490 
0491 
0492 const KoColorSpace * KoColorSpaceRegistry::Private::colorSpace1(const QString &csID, const KoColorProfile *profile)
0493 {
0494     if (csID.isEmpty()) {
0495         return 0;
0496     } else if (!profile) {
0497         return colorSpace1(csID);
0498     }
0499 
0500     const KoColorSpace *cs = 0;
0501 
0502     {
0503         QReadLocker l(&registrylock);
0504         cs = getCachedColorSpaceImpl(csID, profile->name());
0505     }
0506 
0507     // the profile should have already been added to the registry by createColorProfile() method
0508     KIS_SAFE_ASSERT_RECOVER(profileStorage.containsProfile(profile)) {
0509         // warning! locking happens inside addProfile!
0510         q->addProfile(profile);
0511     }
0512 
0513     if (!cs) {
0514         // The profile was not stored and thus not the combination either
0515         QWriteLocker l(&registrylock);
0516         KoColorSpaceFactory *csf = colorSpaceFactoryRegistry.value(csID);
0517 
0518         if (!csf) {
0519             dbgPigmentCSRegistry << "Unknown color space type :" << csf;
0520             return 0;
0521         }
0522 
0523         if (!csf->profileIsCompatible(profile)) {
0524             dbgPigmentCSRegistry << "Profile is not compatible:" << csf << profile->name();
0525             return 0;
0526         }
0527 
0528         cs = lazyCreateColorSpaceImpl(csID, profile);
0529     }
0530 
0531     return cs;
0532 }
0533 
0534 const KoColorSpace * KoColorSpaceRegistry::alpha8()
0535 {
0536     if (!d->alphaCs) {
0537         d->alphaCs = d->colorSpace1(KoAlphaColorSpace::colorSpaceId());
0538     }
0539     Q_ASSERT(d->alphaCs);
0540     return d->alphaCs;
0541 }
0542 
0543 const KoColorSpace * KoColorSpaceRegistry::alpha16()
0544 {
0545     if (!d->alphaU16Cs) {
0546         d->alphaU16Cs = d->colorSpace1(KoAlphaU16ColorSpace::colorSpaceId());
0547     }
0548     Q_ASSERT(d->alphaU16Cs);
0549     return d->alphaU16Cs;
0550 }
0551 
0552 #ifdef HAVE_OPENEXR
0553 const KoColorSpace * KoColorSpaceRegistry::alpha16f()
0554 {
0555     if (!d->alphaF16Cs) {
0556         d->alphaF16Cs = d->colorSpace1(KoAlphaF16ColorSpace::colorSpaceId());
0557     }
0558     Q_ASSERT(d->alphaF16Cs);
0559     return d->alphaF16Cs;
0560 }
0561 #endif
0562 
0563 const KoColorSpace * KoColorSpaceRegistry::alpha32f()
0564 {
0565     if (!d->alphaF32Cs) {
0566         d->alphaF32Cs = d->colorSpace1(KoAlphaF32ColorSpace::colorSpaceId());
0567     }
0568     Q_ASSERT(d->alphaF32Cs);
0569     return d->alphaF32Cs;
0570 }
0571 
0572 const KoColorSpace *KoColorSpaceRegistry::graya8(const QString &profile)
0573 {
0574 
0575     if (profile.isEmpty()) {
0576         KoColorSpaceFactory* factory = d->colorSpaceFactoryRegistry.get(GrayAColorModelID.id());
0577         return KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Integer8BitsColorDepthID.id(), factory->defaultProfile());
0578     }
0579     else {
0580         return KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Integer8BitsColorDepthID.id(), profile);
0581     }
0582 
0583 }
0584 
0585 const KoColorSpace *KoColorSpaceRegistry::graya8(const KoColorProfile *profile)
0586 {
0587     if (!profile) {
0588         return graya8();
0589     }
0590     else {
0591         return KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Integer8BitsColorDepthID.id(), profile);
0592     }
0593 
0594 }
0595 
0596 const KoColorSpace *KoColorSpaceRegistry::graya16(const QString &profile)
0597 {
0598     if (profile.isEmpty()) {
0599         KoColorSpaceFactory* factory = d->colorSpaceFactoryRegistry.get(GrayAColorModelID.id());
0600         return KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Integer16BitsColorDepthID.id(), factory->defaultProfile());
0601     }
0602     else {
0603         return KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Integer16BitsColorDepthID.id(), profile);
0604     }
0605 
0606 }
0607 
0608 const KoColorSpace *KoColorSpaceRegistry::graya16(const KoColorProfile *profile)
0609 {
0610     if (!profile) {
0611         return graya8();
0612     }
0613     else {
0614         return KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Integer8BitsColorDepthID.id(), profile);
0615     }
0616 }
0617 
0618 
0619 const KoColorSpace * KoColorSpaceRegistry::rgb8(const QString &profileName)
0620 {
0621     if (profileName.isEmpty()) {
0622         if (!d->rgbU8sRGB) {
0623             d->rgbU8sRGB = d->colorSpace1(KoRgbU8ColorSpace::colorSpaceId());
0624         }
0625         Q_ASSERT(d->rgbU8sRGB);
0626         return d->rgbU8sRGB;
0627     }
0628     return d->colorSpace1(KoRgbU8ColorSpace::colorSpaceId(), profileName);
0629 }
0630 
0631 const KoColorSpace * KoColorSpaceRegistry::rgb8(const KoColorProfile * profile)
0632 {
0633     if (profile == 0) {
0634         if (!d->rgbU8sRGB) {
0635             d->rgbU8sRGB = d->colorSpace1(KoRgbU8ColorSpace::colorSpaceId());
0636         }
0637         Q_ASSERT(d->rgbU8sRGB);
0638         return d->rgbU8sRGB;
0639     }
0640     return d->colorSpace1(KoRgbU8ColorSpace::colorSpaceId(), profile);
0641 }
0642 
0643 const KoColorSpace * KoColorSpaceRegistry::rgb16(const QString &profileName)
0644 {
0645     return d->colorSpace1(KoRgbU16ColorSpace::colorSpaceId(), profileName);
0646 }
0647 
0648 const KoColorSpace * KoColorSpaceRegistry::rgb16(const KoColorProfile * profile)
0649 {
0650     return d->colorSpace1(KoRgbU16ColorSpace::colorSpaceId(), profile);
0651 }
0652 
0653 const KoColorSpace * KoColorSpaceRegistry::lab16(const QString &profileName)
0654 {
0655     if (profileName.isEmpty()) {
0656         if (!d->lab16sLAB) {
0657             d->lab16sLAB = d->colorSpace1(KoLabColorSpace::colorSpaceId());
0658         }
0659         return d->lab16sLAB;
0660     }
0661     return d->colorSpace1(KoLabColorSpace::colorSpaceId(), profileName);
0662 }
0663 
0664 const KoColorSpace * KoColorSpaceRegistry::lab16(const KoColorProfile * profile)
0665 {
0666     if (profile == 0) {
0667         if (!d->lab16sLAB) {
0668             d->lab16sLAB = d->colorSpace1(KoLabColorSpace::colorSpaceId());
0669         }
0670         Q_ASSERT(d->lab16sLAB);
0671         return d->lab16sLAB;
0672     }
0673     return d->colorSpace1(KoLabColorSpace::colorSpaceId(), profile);
0674 }
0675 
0676 const KoColorProfile *KoColorSpaceRegistry::p2020G10Profile() const
0677 {
0678     return profileByName("Rec2020-elle-V4-g10.icc");
0679 }
0680 
0681 const KoColorProfile *KoColorSpaceRegistry::p2020PQProfile() const
0682 {
0683     return profileByName("High Dynamic Range UHDTV Wide Color Gamut Display (Rec. 2020) - SMPTE ST 2084 PQ EOTF");
0684 }
0685 
0686 const KoColorProfile *KoColorSpaceRegistry::p709G10Profile() const
0687 {
0688     return profileByName("sRGB-elle-V2-g10.icc");
0689 }
0690 
0691 const KoColorProfile *KoColorSpaceRegistry::p709SRGBProfile() const
0692 {
0693     return profileByName("sRGB-elle-V2-srgbtrc.icc");
0694 }
0695 
0696 const KoColorProfile *KoColorSpaceRegistry::profileFor(const QVector<double> &colorants, ColorPrimaries colorPrimaries, TransferCharacteristics transferFunction) const
0697 {
0698     if (colorPrimaries == PRIMARIES_ITU_R_BT_709_5) {
0699         if (transferFunction == TRC_IEC_61966_2_1) {
0700             return p709SRGBProfile();
0701         } else if (transferFunction == TRC_LINEAR) {
0702             return p709G10Profile();
0703         }
0704     }
0705 
0706     if (colorPrimaries == PRIMARIES_ITU_R_BT_2020_2_AND_2100_0) {
0707         if (transferFunction == TRC_ITU_R_BT_2100_0_PQ) {
0708             return p2020PQProfile();
0709         } else if (transferFunction == TRC_LINEAR) {
0710             return p2020G10Profile();
0711         }
0712     }
0713 
0714     QList<const KoColorProfile*> list = d->profileStorage.profilesFor(colorants, colorPrimaries, transferFunction);
0715     if (!list.empty()) {
0716         return list.first();
0717     }
0718 
0719     KoColorSpaceEngine *engine = KoColorSpaceEngineRegistry::instance()->get("icc");
0720     if (engine) {
0721         return engine->getProfile(colorants, colorPrimaries, transferFunction);
0722     }
0723 
0724     return nullptr;
0725 }
0726 
0727 QList<KoID> KoColorSpaceRegistry::colorModelsList(ColorSpaceListVisibility option) const
0728 {
0729     QReadLocker l(&d->registrylock);
0730 
0731     QList<KoID> ids;
0732     QList<KoColorSpaceFactory*> factories = d->colorSpaceFactoryRegistry.values();
0733     Q_FOREACH (KoColorSpaceFactory* factory, factories) {
0734         if (!ids.contains(factory->colorModelId())
0735                 && (option == AllColorSpaces || factory->userVisible())) {
0736             ids << factory->colorModelId();
0737         }
0738     }
0739     return ids;
0740 }
0741 QList<KoID> KoColorSpaceRegistry::colorDepthList(const KoID& colorModelId, ColorSpaceListVisibility option) const
0742 {
0743     return colorDepthList(colorModelId.id(), option);
0744 }
0745 
0746 
0747 QList<KoID> KoColorSpaceRegistry::colorDepthList(const QString & colorModelId, ColorSpaceListVisibility option) const
0748 {
0749     QReadLocker l(&d->registrylock);
0750 
0751     QList<KoID> ids;
0752     QList<KoColorSpaceFactory*> factories = d->colorSpaceFactoryRegistry.values();
0753     Q_FOREACH (KoColorSpaceFactory* factory, factories) {
0754         if (!ids.contains(KoID(factory->colorDepthId()))
0755                 && factory->colorModelId().id() == colorModelId
0756                 && (option == AllColorSpaces || factory->userVisible())) {
0757             ids << factory->colorDepthId();
0758         }
0759     }
0760     QList<KoID> r;
0761 
0762     if (ids.contains(Integer8BitsColorDepthID)) r << Integer8BitsColorDepthID;
0763     if (ids.contains(Integer16BitsColorDepthID)) r << Integer16BitsColorDepthID;
0764     if (ids.contains(Float16BitsColorDepthID)) r << Float16BitsColorDepthID;
0765     if (ids.contains(Float32BitsColorDepthID)) r << Float32BitsColorDepthID;
0766     if (ids.contains(Float64BitsColorDepthID)) r << Float64BitsColorDepthID;
0767 
0768     return r;
0769 }
0770 
0771 QString KoColorSpaceRegistry::Private::colorSpaceIdImpl(const QString & colorModelId, const QString & colorDepthId) const
0772 {
0773     for (auto it = colorSpaceFactoryRegistry.constBegin(); it != colorSpaceFactoryRegistry.constEnd(); ++it) {
0774         if (it.value()->colorModelId().id() == colorModelId && it.value()->colorDepthId().id() == colorDepthId) {
0775             return it.value()->id();
0776         }
0777     }
0778     return "";
0779 }
0780 
0781 QString KoColorSpaceRegistry::colorSpaceId(const QString & colorModelId, const QString & colorDepthId) const
0782 {
0783     QReadLocker l(&d->registrylock);
0784     return d->colorSpaceIdImpl(colorModelId, colorDepthId);
0785 }
0786 
0787 QString KoColorSpaceRegistry::colorSpaceId(const KoID& colorModelId, const KoID& colorDepthId) const
0788 {
0789     return colorSpaceId(colorModelId.id(), colorDepthId.id());
0790 }
0791 
0792 KoID KoColorSpaceRegistry::colorSpaceColorModelId(const QString & _colorSpaceId) const
0793 {
0794     QReadLocker l(&d->registrylock);
0795 
0796     KoColorSpaceFactory* factory = d->colorSpaceFactoryRegistry.get(_colorSpaceId);
0797     if (factory) {
0798         return factory->colorModelId();
0799     } else {
0800         return KoID();
0801     }
0802 }
0803 
0804 KoID KoColorSpaceRegistry::colorSpaceColorDepthId(const QString & _colorSpaceId) const
0805 {
0806     QReadLocker l(&d->registrylock);
0807 
0808     KoColorSpaceFactory* factory = d->colorSpaceFactoryRegistry.get(_colorSpaceId);
0809     if (factory) {
0810         return factory->colorDepthId();
0811     } else {
0812         return KoID();
0813     }
0814 }
0815 
0816 const KoColorConversionSystem* KoColorSpaceRegistry::colorConversionSystem() const
0817 {
0818     return d->colorConversionSystem;
0819 }
0820 
0821 KoColorConversionCache* KoColorSpaceRegistry::colorConversionCache() const
0822 {
0823     return d->colorConversionCache;
0824 }
0825 
0826 const KoColorSpace* KoColorSpaceRegistry::permanentColorspace(const KoColorSpace* _colorSpace)
0827 {
0828     if (_colorSpace->d->deletability != NotOwnedByRegistry) {
0829         return _colorSpace;
0830     } else if (*_colorSpace == *d->alphaCs) {
0831         return d->alphaCs;
0832     } else {
0833         const KoColorSpace* cs = d->colorSpace1(_colorSpace->id(), _colorSpace->profile());
0834         Q_ASSERT(cs);
0835         Q_ASSERT(*cs == *_colorSpace);
0836         return cs;
0837     }
0838 }
0839 
0840 QList<KoID> KoColorSpaceRegistry::listKeys() const
0841 {
0842     QReadLocker l(&d->registrylock);
0843     QList<KoID> answer;
0844     Q_FOREACH (const QString& key, d->colorSpaceFactoryRegistry.keys()) {
0845         answer.append(KoID(key, d->colorSpaceFactoryRegistry.get(key)->name()));
0846     }
0847 
0848     return answer;
0849 }
0850 
0851 struct KoColorSpaceRegistry::Private::ProfileRegistrationInterface : public KoColorSpaceFactory::ProfileRegistrationInterface
0852 {
0853     ProfileRegistrationInterface(KoColorSpaceRegistry::Private *_d) : d(_d) {}
0854 
0855     const KoColorProfile* profileByName(const QString &profileName) const override {
0856         return d->profileStorage.profileByName(profileName);
0857     }
0858 
0859     void registerNewProfile(KoColorProfile *profile) override {
0860         d->profileStorage.addProfile(profile);
0861         d->colorConversionSystem->insertColorProfile(profile);
0862     }
0863 
0864     KoColorSpaceRegistry::Private *d {nullptr};
0865 };
0866 
0867 const KoColorProfile* KoColorSpaceRegistry::createColorProfile(const QString& colorModelId, const QString& colorDepthId, const QByteArray& rawData)
0868 {
0869     QWriteLocker l(&d->registrylock);
0870     KoColorSpaceFactory* factory_ = d->colorSpaceFactoryRegistry.get(d->colorSpaceIdImpl(colorModelId, colorDepthId));
0871 
0872     Private::ProfileRegistrationInterface interface(d);
0873     return factory_->colorProfile(rawData, &interface);
0874 }
0875 
0876 QList<const KoColorSpace*> KoColorSpaceRegistry::allColorSpaces(ColorSpaceListVisibility visibility, ColorSpaceListProfilesSelection pSelection)
0877 {
0878     QList<const KoColorSpace*> colorSpaces;
0879 
0880     // TODO: thread-unsafe code: the factories might change right after the lock in released
0881     // HINT: used in a unittest only!
0882 
0883     d->registrylock.lockForRead();
0884     QList<KoColorSpaceFactory*> factories = d->colorSpaceFactoryRegistry.values();
0885     d->registrylock.unlock();
0886 
0887     Q_FOREACH (KoColorSpaceFactory* factory, factories) {
0888         // Don't test with ycbcr for now, since we don't have a default profile for it.
0889         if (factory->colorModelId().id().startsWith("Y")) continue;
0890         if (visibility == AllColorSpaces || factory->userVisible()) {
0891             if (pSelection == OnlyDefaultProfile) {
0892                 const KoColorSpace *cs = d->colorSpace1(factory->id());
0893                 if (cs) {
0894                     colorSpaces.append(cs);
0895                 }
0896                 else {
0897                     warnPigment << "Could not create colorspace for id" << factory->id() << "since there is no working default profile";
0898                 }
0899             } else {
0900                 QList<const KoColorProfile*> profiles = KoColorSpaceRegistry::instance()->profilesFor(factory->id());
0901                 Q_FOREACH (const KoColorProfile * profile, profiles) {
0902                     const KoColorSpace *cs = d->colorSpace1(factory->id(), profile);
0903                     if (cs) {
0904                         colorSpaces.append(cs);
0905                     }
0906                     else {
0907                         warnPigment << "Could not create colorspace for id" << factory->id() << "and profile" << profile->name();
0908                     }
0909                 }
0910             }
0911         }
0912     }
0913 
0914     return colorSpaces;
0915 }