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(®istrylock); 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(®istrylock); 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(®istrylock); 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(®istrylock); 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 }