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