File indexing completed on 2024-05-19 04:27:25

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 = nullptr;
0205     d->colorConversionCache = nullptr;
0206 }
0207 
0208 KoColorSpaceRegistry::~KoColorSpaceRegistry()
0209 {
0210     delete d->colorConversionSystem;
0211     d->colorConversionSystem = nullptr;
0212 
0213     Q_FOREACH (const KoColorSpace * cs, d->csMap) {
0214         cs->d->deletability = OwnedByRegistryRegistryDeletes;
0215         delete cs;
0216     }
0217     d->csMap.clear();
0218 
0219     // deleting colorspaces calls a function in the cache
0220     delete d->colorConversionCache;
0221     d->colorConversionCache = nullptr;
0222 
0223     // Delete the colorspace factories
0224     Q_FOREACH(KoColorSpaceFactory *f, d->colorSpaceFactoryRegistry.values()) {
0225         d->colorSpaceFactoryRegistry.remove(f->id());
0226         delete f;
0227     }
0228     Q_FOREACH(KoColorSpaceFactory *f, d->colorSpaceFactoryRegistry.doubleEntries()) {
0229         delete f;
0230     }
0231 
0232     delete d;
0233 }
0234 
0235 void KoColorSpaceRegistry::add(KoColorSpaceFactory* item)
0236 {
0237     QWriteLocker l(&d->registrylock);
0238     if (d->colorSpaceFactoryRegistry.contains(item->id())) {
0239         const KoColorSpaceFactory *original =
0240             d->colorSpaceFactoryRegistry.get(item->id());
0241         warnPigment << "Replacing color space factory"
0242                     << original->id() << original->name()
0243                     << "with"
0244                     << item->id() << item->name();
0245     }
0246     d->colorSpaceFactoryRegistry.add(item);
0247     d->colorConversionSystem->insertColorSpace(item);
0248 }
0249 
0250 void KoColorSpaceRegistry::remove(KoColorSpaceFactory* item)
0251 {
0252     QWriteLocker l(&d->registrylock);
0253 
0254     QList<QString> toremove;
0255     Q_FOREACH (const KoColorSpace * cs, d->csMap) {
0256         if (cs->id() == item->id()) {
0257             toremove.push_back(d->idsToCacheName(cs->id(), cs->profile()->name()));
0258             cs->d->deletability = OwnedByRegistryRegistryDeletes;
0259         }
0260     }
0261 
0262     Q_FOREACH (const QString& id, toremove) {
0263         d->csMap.remove(id);
0264         // TODO: should not it delete the color space when removing it from the map ?
0265     }
0266     d->colorSpaceFactoryRegistry.remove(item->id());
0267 }
0268 
0269 void KoColorSpaceRegistry::addProfileAlias(const QString& name, const QString& to)
0270 {
0271     d->profileStorage.addProfileAlias(name, to);
0272 }
0273 
0274 QString KoColorSpaceRegistry::profileAlias(const QString& name) const
0275 {
0276     return d->profileStorage.profileAlias(name);
0277 }
0278 
0279 const KoColorProfile*  KoColorSpaceRegistry::profileByName(const QString &name) const
0280 {
0281     return d->profileStorage.profileByName(name);
0282 }
0283 
0284 const KoColorProfile *  KoColorSpaceRegistry::profileByUniqueId(const QByteArray &id) const
0285 {
0286     return d->profileStorage.profileByUniqueId(id);
0287 }
0288 
0289 QList<const KoColorProfile *>  KoColorSpaceRegistry::profilesFor(const QString &csID) const
0290 {
0291     QReadLocker l(&d->registrylock);
0292     return d->profileStorage.profilesFor(d->colorSpaceFactoryRegistry.value(csID));
0293 }
0294 
0295 const KoColorSpace * KoColorSpaceRegistry::colorSpace(const QString & colorModelId, const QString & colorDepthId, const KoColorProfile *profile)
0296 {
0297     return d->colorSpace1(colorSpaceId(colorModelId, colorDepthId), profile);
0298 }
0299 
0300 const KoColorSpace * KoColorSpaceRegistry::colorSpace(const QString & colorModelId, const QString & colorDepthId, const QString &profileName)
0301 {
0302     return d->colorSpace1(colorSpaceId(colorModelId, colorDepthId), profileName);
0303 }
0304 
0305 const KoColorSpace * KoColorSpaceRegistry::colorSpace(const QString & colorModelId, const QString & colorDepthId)
0306 {
0307     return d->colorSpace1(colorSpaceId(colorModelId, colorDepthId));
0308 }
0309 
0310 bool KoColorSpaceRegistry::profileIsCompatible(const KoColorProfile *profile, const QString &colorSpaceId)
0311 {
0312     QReadLocker l(&d->registrylock);
0313     KoColorSpaceFactory *csf = d->colorSpaceFactoryRegistry.value(colorSpaceId);
0314 
0315     return csf ? csf->profileIsCompatible(profile) : false;
0316 }
0317 
0318 void KoColorSpaceRegistry::addProfileToMap(KoColorProfile *p)
0319 {
0320     d->profileStorage.addProfile(p);
0321 }
0322 
0323 void KoColorSpaceRegistry::addProfile(KoColorProfile *p)
0324 {
0325     if (!p->valid()) return;
0326 
0327     QWriteLocker locker(&d->registrylock);
0328     if (p->valid()) {
0329         addProfileToMap(p);
0330         d->colorConversionSystem->insertColorProfile(p);
0331     }
0332 }
0333 
0334 void KoColorSpaceRegistry::addProfile(const KoColorProfile* profile)
0335 {
0336     addProfile(profile->clone());
0337 }
0338 
0339 void KoColorSpaceRegistry::removeProfile(KoColorProfile* profile)
0340 {
0341     d->profileStorage.removeProfile(profile);
0342     // FIXME: how about removing it from conversion system?
0343 }
0344 
0345 const KoColorSpace* KoColorSpaceRegistry::Private::getCachedColorSpaceImpl(const QString & csID, const QString & profileName) const
0346 {
0347     auto it = csMap.find(idsToCacheName(csID, profileName));
0348 
0349     if (it != csMap.end()) {
0350         return it.value();
0351     }
0352 
0353     return 0;
0354 }
0355 
0356 QString KoColorSpaceRegistry::Private::idsToCacheName(const QString & csID, const QString & profileName) const
0357 {
0358     return csID + "<comb>" + profileName;
0359 }
0360 
0361 QString KoColorSpaceRegistry::defaultProfileForColorSpace(const QString &colorSpaceId) const
0362 {
0363     QReadLocker l(&d->registrylock);
0364     return d->defaultProfileForCsIdImpl(colorSpaceId);
0365 }
0366 
0367 KoColorConversionTransformation *KoColorSpaceRegistry::createColorConverter(const KoColorSpace *srcColorSpace, const KoColorSpace *dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const
0368 {
0369     QWriteLocker l(&d->registrylock);
0370     return d->colorConversionSystem->createColorConverter(srcColorSpace, dstColorSpace, renderingIntent, conversionFlags);
0371 }
0372 
0373 void KoColorSpaceRegistry::createColorConverters(const KoColorSpace *colorSpace, const QList<QPair<KoID, KoID> > &possibilities, KoColorConversionTransformation *&fromCS, KoColorConversionTransformation *&toCS) const
0374 {
0375     QWriteLocker l(&d->registrylock);
0376     d->colorConversionSystem->createColorConverters(colorSpace, possibilities, fromCS, toCS);
0377 }
0378 
0379 QString KoColorSpaceRegistry::Private::defaultProfileForCsIdImpl(const QString &csID)
0380 {
0381     QString defaultProfileName;
0382 
0383     KoColorSpaceFactory *csf = colorSpaceFactoryRegistry.value(csID);
0384     if (csf) {
0385         defaultProfileName = csf->defaultProfile();
0386     } else {
0387         dbgPigmentCSRegistry << "Unknown color space type : " << csID;
0388     }
0389 
0390     return defaultProfileName;
0391 }
0392 
0393 const KoColorProfile *KoColorSpaceRegistry::Private::profileForCsIdWithFallbackImpl(const QString &csID, const QString &profileName)
0394 {
0395     const KoColorProfile *profile = 0;
0396 
0397     // last attempt at getting a profile, sometimes the default profile, like adobe cmyk isn't available.
0398     profile = profileStorage.profileByName(profileName);
0399     if (!profile) {
0400         dbgPigmentCSRegistry << "Profile not found :" << profileName;
0401 
0402         // first try: default
0403         profile = profileStorage.profileByName(defaultProfileForCsIdImpl(csID));
0404 
0405         if (!profile) {
0406             // second try: first profile in the list
0407             QList<const KoColorProfile *> profiles = profileStorage.profilesFor(colorSpaceFactoryRegistry.value(csID));
0408             if (profiles.isEmpty() || !profiles.first()) {
0409                 dbgPigmentCSRegistry << "Couldn't fetch a fallback profile:" << profileName;
0410                 qWarning() << "profileForCsIdWithFallbackImpl couldn't fetch a fallback profile for " << qUtf8Printable(profileName);
0411                 return 0;
0412             }
0413 
0414             profile = profiles.first();
0415         }
0416     }
0417 
0418     return profile;
0419 }
0420 
0421 const KoColorSpace *KoColorSpaceRegistry::Private::lazyCreateColorSpaceImpl(const QString &csID, const KoColorProfile *profile)
0422 {
0423     const KoColorSpace *cs = 0;
0424 
0425     /*
0426      * We need to check again here, a thread requesting the same colorspace could've added it
0427      * already, in between the read unlock and write lock.
0428      * TODO: We also potentially changed profileName content, which means we maybe are going to
0429      * create a colorspace that's actually in the space registry cache, but currently this might
0430      * not be an issue because the colorspace should be cached also by the factory, so it won't
0431      * create a new instance. That being said, having two caches with the same stuff doesn't make
0432      * much sense.
0433      */
0434     cs = getCachedColorSpaceImpl(csID, profile->name());
0435     if (!cs) {
0436         KoColorSpaceFactory *csf = colorSpaceFactoryRegistry.value(csID);
0437         if (!csf) {
0438             qWarning() << "Unable to create color space factory for" << csID;
0439             return 0;
0440         }
0441         cs = csf->grabColorSpace(profile);
0442         if (!cs) {
0443             dbgPigmentCSRegistry << "Unable to create color space";
0444             qWarning() << "lazyCreateColorSpaceImpl was unable to create a color space for " << csID;
0445             return 0;
0446         }
0447 
0448         dbgPigmentCSRegistry << "colorspace count: " << csMap.count()
0449                              << ", adding name: " << idsToCacheName(cs->id(), cs->profile()->name())
0450                              << "\n\tcsID" << csID
0451                              << "\n\tcs->id()" << cs->id()
0452                              << "\n\tcs->profile()->name()" << cs->profile()->name()
0453                              << "\n\tprofile->name()" << profile->name();
0454         Q_ASSERT(cs->id() == csID);
0455         Q_ASSERT(cs->profile()->name() == profile->name());
0456         csMap[idsToCacheName(cs->id(), cs->profile()->name())] = cs;
0457         cs->d->deletability = OwnedByRegistryDoNotDelete;
0458     }
0459 
0460     return cs;
0461 }
0462 
0463 template<class LockPolicy>
0464 const KoColorSpace * KoColorSpaceRegistry::Private::colorSpace1(const QString &csID, const QString &pName)
0465 {
0466     QString profileName = pName;
0467 
0468     const KoColorSpace *cs = 0;
0469 
0470     {
0471         typename LockPolicy::ReadLocker l(&registrylock);
0472 
0473         if (profileName.isEmpty()) {
0474             profileName = defaultProfileForCsIdImpl(csID);
0475         }
0476 
0477         if (!profileName.isEmpty()) {
0478             // quick attempt to fetch a cached color space
0479             cs = getCachedColorSpaceImpl(csID, profileName);
0480         }
0481     }
0482 
0483     if (!cs) {
0484         // slow attempt to create a color space
0485         typename LockPolicy::WriteLocker l(&registrylock);
0486 
0487         const KoColorProfile *profile =
0488             profileForCsIdWithFallbackImpl(csID, profileName);
0489 
0490         if (!profile) return 0;
0491 
0492         cs = lazyCreateColorSpaceImpl(csID, profile);
0493     }
0494     else {
0495         KIS_SAFE_ASSERT_RECOVER_NOOP(cs->id() == csID);
0496         KIS_SAFE_ASSERT_RECOVER_NOOP(cs->profile()->name() == profileName);
0497     }
0498 
0499     return cs;
0500 }
0501 
0502 
0503 const KoColorSpace * KoColorSpaceRegistry::Private::colorSpace1(const QString &csID, const KoColorProfile *profile)
0504 {
0505     if (csID.isEmpty()) {
0506         return 0;
0507     } else if (!profile) {
0508         return colorSpace1(csID);
0509     }
0510 
0511     const KoColorSpace *cs = 0;
0512 
0513     {
0514         QReadLocker l(&registrylock);
0515         cs = getCachedColorSpaceImpl(csID, profile->name());
0516     }
0517 
0518     // the profile should have already been added to the registry by createColorProfile() method
0519     KIS_SAFE_ASSERT_RECOVER(profileStorage.containsProfile(profile)) {
0520         // warning! locking happens inside addProfile!
0521         q->addProfile(profile);
0522     }
0523 
0524     if (!cs) {
0525         // The profile was not stored and thus not the combination either
0526         QWriteLocker l(&registrylock);
0527         KoColorSpaceFactory *csf = colorSpaceFactoryRegistry.value(csID);
0528 
0529         if (!csf) {
0530             dbgPigmentCSRegistry << "Unknown color space type :" << csf;
0531             return 0;
0532         }
0533 
0534         if (!csf->profileIsCompatible(profile)) {
0535             dbgPigmentCSRegistry << "Profile is not compatible:" << csf << profile->name();
0536             return 0;
0537         }
0538 
0539         cs = lazyCreateColorSpaceImpl(csID, profile);
0540     }
0541 
0542     return cs;
0543 }
0544 
0545 const KoColorSpace * KoColorSpaceRegistry::alpha8()
0546 {
0547     if (!d->alphaCs) {
0548         d->alphaCs = d->colorSpace1(KoAlphaColorSpace::colorSpaceId());
0549     }
0550     Q_ASSERT(d->alphaCs);
0551     return d->alphaCs;
0552 }
0553 
0554 const KoColorSpace * KoColorSpaceRegistry::alpha16()
0555 {
0556     if (!d->alphaU16Cs) {
0557         d->alphaU16Cs = d->colorSpace1(KoAlphaU16ColorSpace::colorSpaceId());
0558     }
0559     Q_ASSERT(d->alphaU16Cs);
0560     return d->alphaU16Cs;
0561 }
0562 
0563 #ifdef HAVE_OPENEXR
0564 const KoColorSpace * KoColorSpaceRegistry::alpha16f()
0565 {
0566     if (!d->alphaF16Cs) {
0567         d->alphaF16Cs = d->colorSpace1(KoAlphaF16ColorSpace::colorSpaceId());
0568     }
0569     Q_ASSERT(d->alphaF16Cs);
0570     return d->alphaF16Cs;
0571 }
0572 #endif
0573 
0574 const KoColorSpace * KoColorSpaceRegistry::alpha32f()
0575 {
0576     if (!d->alphaF32Cs) {
0577         d->alphaF32Cs = d->colorSpace1(KoAlphaF32ColorSpace::colorSpaceId());
0578     }
0579     Q_ASSERT(d->alphaF32Cs);
0580     return d->alphaF32Cs;
0581 }
0582 
0583 const KoColorSpace *KoColorSpaceRegistry::graya8(const QString &profile)
0584 {
0585 
0586     if (profile.isEmpty()) {
0587         KoColorSpaceFactory* factory = d->colorSpaceFactoryRegistry.get(GrayAColorModelID.id());
0588         return KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Integer8BitsColorDepthID.id(), factory->defaultProfile());
0589     }
0590     else {
0591         return KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Integer8BitsColorDepthID.id(), profile);
0592     }
0593 
0594 }
0595 
0596 const KoColorSpace *KoColorSpaceRegistry::graya8(const KoColorProfile *profile)
0597 {
0598     if (!profile) {
0599         return graya8();
0600     }
0601     else {
0602         return KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Integer8BitsColorDepthID.id(), profile);
0603     }
0604 
0605 }
0606 
0607 const KoColorSpace *KoColorSpaceRegistry::graya16(const QString &profile)
0608 {
0609     if (profile.isEmpty()) {
0610         KoColorSpaceFactory* factory = d->colorSpaceFactoryRegistry.get(GrayAColorModelID.id());
0611         return KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Integer16BitsColorDepthID.id(), factory->defaultProfile());
0612     }
0613     else {
0614         return KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Integer16BitsColorDepthID.id(), profile);
0615     }
0616 
0617 }
0618 
0619 const KoColorSpace *KoColorSpaceRegistry::graya16(const KoColorProfile *profile)
0620 {
0621     if (!profile) {
0622         return graya16();
0623     }
0624     else {
0625         return KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Integer16BitsColorDepthID.id(), profile);
0626     }
0627 }
0628 
0629 
0630 const KoColorSpace * KoColorSpaceRegistry::rgb8(const QString &profileName)
0631 {
0632     if (profileName.isEmpty()) {
0633         if (!d->rgbU8sRGB) {
0634             d->rgbU8sRGB = d->colorSpace1(KoRgbU8ColorSpace::colorSpaceId());
0635         }
0636         Q_ASSERT(d->rgbU8sRGB);
0637         return d->rgbU8sRGB;
0638     }
0639     return d->colorSpace1(KoRgbU8ColorSpace::colorSpaceId(), profileName);
0640 }
0641 
0642 const KoColorSpace * KoColorSpaceRegistry::rgb8(const KoColorProfile * profile)
0643 {
0644     if (profile == 0) {
0645         if (!d->rgbU8sRGB) {
0646             d->rgbU8sRGB = d->colorSpace1(KoRgbU8ColorSpace::colorSpaceId());
0647         }
0648         Q_ASSERT(d->rgbU8sRGB);
0649         return d->rgbU8sRGB;
0650     }
0651     return d->colorSpace1(KoRgbU8ColorSpace::colorSpaceId(), profile);
0652 }
0653 
0654 const KoColorSpace * KoColorSpaceRegistry::rgb16(const QString &profileName)
0655 {
0656     return d->colorSpace1(KoRgbU16ColorSpace::colorSpaceId(), profileName);
0657 }
0658 
0659 const KoColorSpace * KoColorSpaceRegistry::rgb16(const KoColorProfile * profile)
0660 {
0661     return d->colorSpace1(KoRgbU16ColorSpace::colorSpaceId(), profile);
0662 }
0663 
0664 const KoColorSpace * KoColorSpaceRegistry::lab16(const QString &profileName)
0665 {
0666     if (profileName.isEmpty()) {
0667         if (!d->lab16sLAB) {
0668             d->lab16sLAB = d->colorSpace1(KoLabColorSpace::colorSpaceId());
0669         }
0670         return d->lab16sLAB;
0671     }
0672     return d->colorSpace1(KoLabColorSpace::colorSpaceId(), profileName);
0673 }
0674 
0675 const KoColorSpace * KoColorSpaceRegistry::lab16(const KoColorProfile * profile)
0676 {
0677     if (profile == 0) {
0678         if (!d->lab16sLAB) {
0679             d->lab16sLAB = d->colorSpace1(KoLabColorSpace::colorSpaceId());
0680         }
0681         Q_ASSERT(d->lab16sLAB);
0682         return d->lab16sLAB;
0683     }
0684     return d->colorSpace1(KoLabColorSpace::colorSpaceId(), profile);
0685 }
0686 
0687 const KoColorProfile *KoColorSpaceRegistry::p2020G10Profile() const
0688 {
0689     return profileByName("Rec2020-elle-V4-g10.icc");
0690 }
0691 
0692 const KoColorProfile *KoColorSpaceRegistry::p2020PQProfile() const
0693 {
0694     return profileByName("High Dynamic Range UHDTV Wide Color Gamut Display (Rec. 2020) - SMPTE ST 2084 PQ EOTF");
0695 }
0696 
0697 const KoColorProfile *KoColorSpaceRegistry::p709G10Profile() const
0698 {
0699     return profileByName("sRGB-elle-V2-g10.icc");
0700 }
0701 
0702 const KoColorProfile *KoColorSpaceRegistry::p709SRGBProfile() const
0703 {
0704     return profileByName("sRGB-elle-V2-srgbtrc.icc");
0705 }
0706 
0707 const KoColorProfile *KoColorSpaceRegistry::profileFor(const QVector<double> &colorants, ColorPrimaries colorPrimaries, TransferCharacteristics transferFunction) const
0708 {
0709     if (colorPrimaries == PRIMARIES_ITU_R_BT_709_5) {
0710         if (transferFunction == TRC_IEC_61966_2_1) {
0711             return p709SRGBProfile();
0712         } else if (transferFunction == TRC_LINEAR) {
0713             return p709G10Profile();
0714         }
0715     }
0716 
0717     if (colorPrimaries == PRIMARIES_ITU_R_BT_2020_2_AND_2100_0) {
0718         if (transferFunction == TRC_ITU_R_BT_2100_0_PQ) {
0719             return p2020PQProfile();
0720         } else if (transferFunction == TRC_LINEAR) {
0721             return p2020G10Profile();
0722         }
0723     }
0724 
0725     QList<const KoColorProfile*> list = d->profileStorage.profilesFor(colorants, colorPrimaries, transferFunction);
0726     if (!list.empty()) {
0727         return list.first();
0728     }
0729 
0730     KoColorSpaceEngine *engine = KoColorSpaceEngineRegistry::instance()->get("icc");
0731     if (engine) {
0732         return engine->getProfile(colorants, colorPrimaries, transferFunction);
0733     }
0734 
0735     return nullptr;
0736 }
0737 
0738 QList<KoID> KoColorSpaceRegistry::colorModelsList(ColorSpaceListVisibility option) const
0739 {
0740     QReadLocker l(&d->registrylock);
0741 
0742     QList<KoID> ids;
0743     QList<KoColorSpaceFactory*> factories = d->colorSpaceFactoryRegistry.values();
0744     Q_FOREACH (KoColorSpaceFactory* factory, factories) {
0745         if (!ids.contains(factory->colorModelId())
0746                 && (option == AllColorSpaces || factory->userVisible())) {
0747             ids << factory->colorModelId();
0748         }
0749     }
0750     return ids;
0751 }
0752 QList<KoID> KoColorSpaceRegistry::colorDepthList(const KoID& colorModelId, ColorSpaceListVisibility option) const
0753 {
0754     return colorDepthList(colorModelId.id(), option);
0755 }
0756 
0757 
0758 QList<KoID> KoColorSpaceRegistry::colorDepthList(const QString & colorModelId, ColorSpaceListVisibility option) const
0759 {
0760     QReadLocker l(&d->registrylock);
0761 
0762     QList<KoID> ids;
0763     QList<KoColorSpaceFactory*> factories = d->colorSpaceFactoryRegistry.values();
0764     Q_FOREACH (KoColorSpaceFactory* factory, factories) {
0765         if (!ids.contains(KoID(factory->colorDepthId()))
0766                 && factory->colorModelId().id() == colorModelId
0767                 && (option == AllColorSpaces || factory->userVisible())) {
0768             ids << factory->colorDepthId();
0769         }
0770     }
0771     QList<KoID> r;
0772 
0773     if (ids.contains(Integer8BitsColorDepthID)) r << Integer8BitsColorDepthID;
0774     if (ids.contains(Integer16BitsColorDepthID)) r << Integer16BitsColorDepthID;
0775     if (ids.contains(Float16BitsColorDepthID)) r << Float16BitsColorDepthID;
0776     if (ids.contains(Float32BitsColorDepthID)) r << Float32BitsColorDepthID;
0777     if (ids.contains(Float64BitsColorDepthID)) r << Float64BitsColorDepthID;
0778 
0779     return r;
0780 }
0781 
0782 QString KoColorSpaceRegistry::Private::colorSpaceIdImpl(const QString & colorModelId, const QString & colorDepthId) const
0783 {
0784     for (auto it = colorSpaceFactoryRegistry.constBegin(); it != colorSpaceFactoryRegistry.constEnd(); ++it) {
0785         if (it.value()->colorModelId().id() == colorModelId && it.value()->colorDepthId().id() == colorDepthId) {
0786             return it.value()->id();
0787         }
0788     }
0789     return "";
0790 }
0791 
0792 QString KoColorSpaceRegistry::colorSpaceId(const QString & colorModelId, const QString & colorDepthId) const
0793 {
0794     QReadLocker l(&d->registrylock);
0795     return d->colorSpaceIdImpl(colorModelId, colorDepthId);
0796 }
0797 
0798 QString KoColorSpaceRegistry::colorSpaceId(const KoID& colorModelId, const KoID& colorDepthId) const
0799 {
0800     return colorSpaceId(colorModelId.id(), colorDepthId.id());
0801 }
0802 
0803 KoID KoColorSpaceRegistry::colorSpaceColorModelId(const QString & _colorSpaceId) const
0804 {
0805     QReadLocker l(&d->registrylock);
0806 
0807     KoColorSpaceFactory* factory = d->colorSpaceFactoryRegistry.get(_colorSpaceId);
0808     if (factory) {
0809         return factory->colorModelId();
0810     } else {
0811         return KoID();
0812     }
0813 }
0814 
0815 KoID KoColorSpaceRegistry::colorSpaceColorDepthId(const QString & _colorSpaceId) const
0816 {
0817     QReadLocker l(&d->registrylock);
0818 
0819     KoColorSpaceFactory* factory = d->colorSpaceFactoryRegistry.get(_colorSpaceId);
0820     if (factory) {
0821         return factory->colorDepthId();
0822     } else {
0823         return KoID();
0824     }
0825 }
0826 
0827 const KoColorConversionSystem* KoColorSpaceRegistry::colorConversionSystem() const
0828 {
0829     return d->colorConversionSystem;
0830 }
0831 
0832 KoColorConversionCache* KoColorSpaceRegistry::colorConversionCache() const
0833 {
0834     return d->colorConversionCache;
0835 }
0836 
0837 const KoColorSpace* KoColorSpaceRegistry::permanentColorspace(const KoColorSpace* _colorSpace)
0838 {
0839     if (_colorSpace->d->deletability != NotOwnedByRegistry) {
0840         return _colorSpace;
0841     } else if (*_colorSpace == *d->alphaCs) {
0842         return d->alphaCs;
0843     } else {
0844         const KoColorSpace* cs = d->colorSpace1(_colorSpace->id(), _colorSpace->profile());
0845         Q_ASSERT(cs);
0846         Q_ASSERT(*cs == *_colorSpace);
0847         return cs;
0848     }
0849 }
0850 
0851 QList<KoID> KoColorSpaceRegistry::listKeys() const
0852 {
0853     QReadLocker l(&d->registrylock);
0854     QList<KoID> answer;
0855     Q_FOREACH (const QString& key, d->colorSpaceFactoryRegistry.keys()) {
0856         answer.append(KoID(key, d->colorSpaceFactoryRegistry.get(key)->name()));
0857     }
0858 
0859     return answer;
0860 }
0861 
0862 struct KoColorSpaceRegistry::Private::ProfileRegistrationInterface : public KoColorSpaceFactory::ProfileRegistrationInterface
0863 {
0864     ProfileRegistrationInterface(KoColorSpaceRegistry::Private *_d) : d(_d) {}
0865 
0866     const KoColorProfile* profileByName(const QString &profileName) const override {
0867         return d->profileStorage.profileByName(profileName);
0868     }
0869 
0870     void registerNewProfile(KoColorProfile *profile) override {
0871         d->profileStorage.addProfile(profile);
0872         d->colorConversionSystem->insertColorProfile(profile);
0873     }
0874 
0875     KoColorSpaceRegistry::Private *d {nullptr};
0876 };
0877 
0878 const KoColorProfile* KoColorSpaceRegistry::createColorProfile(const QString& colorModelId, const QString& colorDepthId, const QByteArray& rawData)
0879 {
0880     QWriteLocker l(&d->registrylock);
0881     KoColorSpaceFactory* factory_ = d->colorSpaceFactoryRegistry.get(d->colorSpaceIdImpl(colorModelId, colorDepthId));
0882 
0883     Private::ProfileRegistrationInterface interface(d);
0884     return factory_->colorProfile(rawData, &interface);
0885 }
0886 
0887 QList<const KoColorSpace*> KoColorSpaceRegistry::allColorSpaces(ColorSpaceListVisibility visibility, ColorSpaceListProfilesSelection pSelection)
0888 {
0889     QList<const KoColorSpace*> colorSpaces;
0890 
0891     // TODO: thread-unsafe code: the factories might change right after the lock in released
0892     // HINT: used in a unittest only!
0893 
0894     d->registrylock.lockForRead();
0895     QList<KoColorSpaceFactory*> factories = d->colorSpaceFactoryRegistry.values();
0896     d->registrylock.unlock();
0897 
0898     Q_FOREACH (KoColorSpaceFactory* factory, factories) {
0899         // Don't test with ycbcr for now, since we don't have a default profile for it.
0900         if (factory->colorModelId().id().startsWith("Y")) continue;
0901         if (visibility == AllColorSpaces || factory->userVisible()) {
0902             if (pSelection == OnlyDefaultProfile) {
0903                 const KoColorSpace *cs = d->colorSpace1(factory->id());
0904                 if (cs) {
0905                     colorSpaces.append(cs);
0906                 }
0907                 else {
0908                     warnPigment << "Could not create colorspace for id" << factory->id() << "since there is no working default profile";
0909                 }
0910             } else {
0911                 QList<const KoColorProfile*> profiles = KoColorSpaceRegistry::instance()->profilesFor(factory->id());
0912                 Q_FOREACH (const KoColorProfile * profile, profiles) {
0913                     const KoColorSpace *cs = d->colorSpace1(factory->id(), profile);
0914                     if (cs) {
0915                         colorSpaces.append(cs);
0916                     }
0917                     else {
0918                         warnPigment << "Could not create colorspace for id" << factory->id() << "and profile" << profile->name();
0919                     }
0920                 }
0921             }
0922         }
0923     }
0924 
0925     return colorSpaces;
0926 }