Krita Source Code Documentation
Loading...
Searching...
No Matches
KoColorSpaceRegistry.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2003 Patrick Julien <freak@codepimps.org>
3 * SPDX-FileCopyrightText: 2004, 2010 Cyrille Berger <cberger@cberger.net>
4 *
5 * SPDX-License-Identifier: LGPL-2.1-or-later
6*/
7
9
10#include <QHash>
11
12#include <QReadWriteLock>
13#include <QDir>
14#include <QGlobalStatic>
15#include <QColorSpace>
16
17#include "KoPluginLoader.h"
18#include "KoGenericRegistry.h"
19#include "DebugPigment.h"
21#include "KoColorSpace.h"
22#include "KoColorProfile.h"
25
31#include "KoColorSpace_p.h"
32
33#include "kis_assert.h"
36
38
40
41
42struct Q_DECL_HIDDEN KoColorSpaceRegistry::Private {
43
44 // interface for KoColorSpaceFactory
45 struct ProfileRegistrationInterface;
46 // interface for KoColorConversionSystem
47 struct ConversionSystemInterface;
48
49
51
53
56 QHash<QString, const KoColorSpace *> csMap;
57 QScopedPointer<ConversionSystemInterface> conversionSystemInterface;
58 KoColorConversionSystem *colorConversionSystem {nullptr};
59 KoColorConversionCache* colorConversionCache {nullptr};
60 const KoColorSpace *rgbU8sRGB {nullptr};
61 const KoColorSpace *lab16sLAB {nullptr};
62 const KoColorSpace *alphaCs {nullptr};
63 const KoColorSpace *alphaU16Cs {nullptr};
64#ifdef HAVE_OPENEXR
65 const KoColorSpace *alphaF16Cs {nullptr};
66#endif
67 const KoColorSpace *alphaF32Cs {nullptr};
68 QReadWriteLock registrylock;
69
79 const KoColorSpace* getCachedColorSpaceImpl(const QString & csId, const QString & profileName) const;
80
81 QString idsToCacheName(const QString & csId, const QString & profileName) const;
82 QString defaultProfileForCsIdImpl(const QString &csID);
83 const KoColorProfile * profileForCsIdWithFallbackImpl(const QString &csID, const QString &profileName);
84 QString colorSpaceIdImpl(const QString & colorModelId, const QString & colorDepthId) const;
85
86 const KoColorSpace *lazyCreateColorSpaceImpl(const QString &csID, const KoColorProfile *profile);
87
93 template<class LockPolicy = NormalLockPolicy>
94 const KoColorSpace * colorSpace1(const QString &colorSpaceId, const QString &pName = QString());
95
102 const KoColorSpace * colorSpace1(const QString &colorSpaceId, const KoColorProfile *profile);
103};
104
106{
108 : q(parentRegistry)
109 {
110 }
111
112 const KoColorSpace * colorSpace(const QString & colorModelId, const QString & colorDepthId, const QString &profileName) override {
113 return q->d->colorSpace1<NoLockPolicy>(q->d->colorSpaceIdImpl(colorModelId, colorDepthId), profileName);
114 }
115
116 const KoColorSpaceFactory* colorSpaceFactory(const QString &colorModelId, const QString &colorDepthId) const override {
117 return q->d->colorSpaceFactoryRegistry.get(q->d->colorSpaceIdImpl(colorModelId, colorDepthId));
118 }
119
121 return q->d->profileStorage.profilesFor(csf);
122 }
123
126 Q_FOREACH (KoColorSpaceFactory* csf, q->d->colorSpaceFactoryRegistry.values()) {
127 if (csf->profileIsCompatible(profile)) {
128 csfs.push_back(csf);
129 }
130 }
131 return csfs;
132 }
133
134private:
136};
137
139{
140 if (!s_instance.exists()) {
141 s_instance->init();
142 }
143 return s_instance;
144}
145
146
148{
149 d->rgbU8sRGB = 0;
150 d->lab16sLAB = 0;
151 d->alphaCs = 0;
152 d->alphaU16Cs = 0;
153#ifdef HAVE_OPENEXR
154 d->alphaF16Cs = 0;
155#endif
156 d->alphaF32Cs = 0;
157
158 d->conversionSystemInterface.reset(new Private::ConversionSystemInterface(this));
159 d->colorConversionSystem = new KoColorConversionSystem(d->conversionSystemInterface.data());
160 d->colorConversionCache = new KoColorConversionCache;
161
163
165
166 // Create the built-in colorspaces
167 QList<KoColorSpaceFactory *> localFactories;
168 localFactories
171 #ifdef HAVE_OPENEXR
172 << new KoAlphaF16ColorSpaceFactory()
173 #endif
178
179 Q_FOREACH (KoColorSpaceFactory *factory, localFactories) {
180 add(factory);
181 }
182
184 config.blacklist = "ColorSpacePluginsDisabled";
185 config.group = "krita";
186 KoPluginLoader::instance()->load("Krita/ColorSpace", config);
187
188 KoPluginLoader::PluginsConfig configExtensions;
189 configExtensions.blacklist = "ColorSpaceExtensionsPluginsDisabled";
190 configExtensions.group = "krita";
191 KoPluginLoader::instance()->load("Krita/ColorSpaceExtension", configExtensions);
192
193
194 dbgPigment << "Loaded the following colorspaces:";
195 Q_FOREACH (const KoID& id, listKeys()) {
196 dbgPigment << "\t" << id.id() << "," << id.name();
197 }
198}
199
201{
202 d->colorConversionSystem = nullptr;
203 d->colorConversionCache = nullptr;
204}
205
207{
208 delete d->colorConversionSystem;
209 d->colorConversionSystem = nullptr;
210
211 Q_FOREACH (const KoColorSpace * cs, d->csMap) {
212 cs->d->deletability = OwnedByRegistryRegistryDeletes;
213 delete cs;
214 }
215 d->csMap.clear();
216
217 // deleting colorspaces calls a function in the cache
218 delete d->colorConversionCache;
219 d->colorConversionCache = nullptr;
220
221 // Delete the colorspace factories
222 Q_FOREACH(KoColorSpaceFactory *f, d->colorSpaceFactoryRegistry.values()) {
223 d->colorSpaceFactoryRegistry.remove(f->id());
224 delete f;
225 }
226 Q_FOREACH(KoColorSpaceFactory *f, d->colorSpaceFactoryRegistry.doubleEntries()) {
227 delete f;
228 }
229
230 delete d;
231}
232
234{
235 QWriteLocker l(&d->registrylock);
236 d->colorSpaceFactoryRegistry.add(item);
237 d->colorConversionSystem->insertColorSpace(item);
238}
239
241{
242 QWriteLocker l(&d->registrylock);
243
244 QList<QString> toremove;
245 Q_FOREACH (const KoColorSpace * cs, d->csMap) {
246 if (cs->id() == item->id()) {
247 toremove.push_back(d->idsToCacheName(cs->id(), cs->profile()->name()));
248 cs->d->deletability = OwnedByRegistryRegistryDeletes;
249 }
250 }
251
252 Q_FOREACH (const QString& id, toremove) {
253 d->csMap.remove(id);
254 // TODO: should not it delete the color space when removing it from the map ?
255 }
256 d->colorSpaceFactoryRegistry.remove(item->id());
257}
258
259void KoColorSpaceRegistry::addProfileAlias(const QString& name, const QString& to)
260{
261 d->profileStorage.addProfileAlias(name, to);
262}
263
264QString KoColorSpaceRegistry::profileAlias(const QString& name) const
265{
266 return d->profileStorage.profileAlias(name);
267}
268
270{
271 return d->profileStorage.profileByName(name);
272}
273
275{
276 return d->profileStorage.profileByUniqueId(id);
277}
278
280{
281 QReadLocker l(&d->registrylock);
282 return d->profileStorage.profilesFor(d->colorSpaceFactoryRegistry.value(csID));
283}
284
285const KoColorSpace * KoColorSpaceRegistry::colorSpace(const QString & colorModelId, const QString & colorDepthId, const KoColorProfile *profile)
286{
287 return d->colorSpace1(colorSpaceId(colorModelId, colorDepthId), profile);
288}
289
290const KoColorSpace * KoColorSpaceRegistry::colorSpace(const QString & colorModelId, const QString & colorDepthId, const QString &profileName)
291{
292 return d->colorSpace1(colorSpaceId(colorModelId, colorDepthId), profileName);
293}
294
295const KoColorSpace * KoColorSpaceRegistry::colorSpace(const QString & colorModelId, const QString & colorDepthId)
296{
297 return d->colorSpace1(colorSpaceId(colorModelId, colorDepthId));
298}
299
300bool KoColorSpaceRegistry::profileIsCompatible(const KoColorProfile *profile, const QString &colorSpaceId)
301{
302 QReadLocker l(&d->registrylock);
303 KoColorSpaceFactory *csf = d->colorSpaceFactoryRegistry.value(colorSpaceId);
304
305 return csf ? csf->profileIsCompatible(profile) : false;
306}
307
309{
310 d->profileStorage.addProfile(p);
311}
312
314{
315 if (!p->valid()) return;
316
317 QWriteLocker locker(&d->registrylock);
318 if (p->valid()) {
320 d->colorConversionSystem->insertColorProfile(p);
321 }
322}
323
325{
326 addProfile(profile->clone());
327}
328
330{
331 d->profileStorage.removeProfile(profile);
332 // FIXME: how about removing it from conversion system?
333}
334
335const KoColorSpace* KoColorSpaceRegistry::Private::getCachedColorSpaceImpl(const QString & csID, const QString & profileName) const
336{
337 auto it = csMap.find(idsToCacheName(csID, profileName));
338
339 if (it != csMap.end()) {
340 return it.value();
341 }
342
343 return 0;
344}
345
346QString KoColorSpaceRegistry::Private::idsToCacheName(const QString & csID, const QString & profileName) const
347{
348 return csID + "<comb>" + profileName;
349}
350
351QString KoColorSpaceRegistry::defaultProfileForColorSpace(const QString &colorSpaceId) const
352{
353 QReadLocker l(&d->registrylock);
354 return d->defaultProfileForCsIdImpl(colorSpaceId);
355}
356
357KoColorConversionTransformation *KoColorSpaceRegistry::createColorConverter(const KoColorSpace *srcColorSpace, const KoColorSpace *dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const
358{
359 QWriteLocker l(&d->registrylock);
360 return d->colorConversionSystem->createColorConverter(srcColorSpace, dstColorSpace, renderingIntent, conversionFlags);
361}
362
363void KoColorSpaceRegistry::createColorConverters(const KoColorSpace *colorSpace, const QList<QPair<KoID, KoID> > &possibilities, KoColorConversionTransformation *&fromCS, KoColorConversionTransformation *&toCS) const
364{
365 QWriteLocker l(&d->registrylock);
366 d->colorConversionSystem->createColorConverters(colorSpace, possibilities, fromCS, toCS);
367}
368
369QString KoColorSpaceRegistry::Private::defaultProfileForCsIdImpl(const QString &csID)
370{
371 QString defaultProfileName;
372
373 KoColorSpaceFactory *csf = colorSpaceFactoryRegistry.value(csID);
374 if (csf) {
375 defaultProfileName = csf->defaultProfile();
376 } else {
377 dbgPigmentCSRegistry << "Unknown color space type : " << csID;
378 }
379
380 return defaultProfileName;
381}
382
383const KoColorProfile *KoColorSpaceRegistry::Private::profileForCsIdWithFallbackImpl(const QString &csID, const QString &profileName)
384{
385 const KoColorProfile *profile = 0;
386
387 // last attempt at getting a profile, sometimes the default profile, like adobe cmyk isn't available.
388 profile = profileStorage.profileByName(profileName);
389 if (!profile) {
390 dbgPigmentCSRegistry << "Profile not found :" << profileName;
391
392 // first try: default
393 profile = profileStorage.profileByName(defaultProfileForCsIdImpl(csID));
394
395 if (!profile) {
396 // second try: first profile in the list
397 QList<const KoColorProfile *> profiles = profileStorage.profilesFor(colorSpaceFactoryRegistry.value(csID));
398 if (profiles.isEmpty() || !profiles.first()) {
399 dbgPigmentCSRegistry << "Couldn't fetch a fallback profile:" << profileName;
400 qWarning() << "profileForCsIdWithFallbackImpl couldn't fetch a fallback profile for " << qUtf8Printable(profileName);
401 return 0;
402 }
403
404 profile = profiles.first();
405 }
406 }
407
408 return profile;
409}
410
411const KoColorSpace *KoColorSpaceRegistry::Private::lazyCreateColorSpaceImpl(const QString &csID, const KoColorProfile *profile)
412{
413 const KoColorSpace *cs = 0;
414
415 /*
416 * We need to check again here, a thread requesting the same colorspace could've added it
417 * already, in between the read unlock and write lock.
418 * TODO: We also potentially changed profileName content, which means we maybe are going to
419 * create a colorspace that's actually in the space registry cache, but currently this might
420 * not be an issue because the colorspace should be cached also by the factory, so it won't
421 * create a new instance. That being said, having two caches with the same stuff doesn't make
422 * much sense.
423 */
424 cs = getCachedColorSpaceImpl(csID, profile->name());
425 if (!cs) {
426 KoColorSpaceFactory *csf = colorSpaceFactoryRegistry.value(csID);
427 if (!csf) {
428 qWarning() << "Unable to create color space factory for" << csID;
429 return 0;
430 }
431 cs = csf->grabColorSpace(profile);
432 if (!cs) {
433 dbgPigmentCSRegistry << "Unable to create color space";
434 qWarning() << "lazyCreateColorSpaceImpl was unable to create a color space for " << csID;
435 return 0;
436 }
437
438 dbgPigmentCSRegistry << "colorspace count: " << csMap.count()
439 << ", adding name: " << idsToCacheName(cs->id(), cs->profile()->name())
440 << "\n\tcsID" << csID
441 << "\n\tcs->id()" << cs->id()
442 << "\n\tcs->profile()->name()" << cs->profile()->name()
443 << "\n\tprofile->name()" << profile->name();
444 Q_ASSERT(cs->id() == csID);
445 Q_ASSERT(cs->profile()->name() == profile->name());
446 csMap[idsToCacheName(cs->id(), cs->profile()->name())] = cs;
447 cs->d->deletability = OwnedByRegistryDoNotDelete;
448 }
449
450 return cs;
451}
452
453template<class LockPolicy>
454const KoColorSpace * KoColorSpaceRegistry::Private::colorSpace1(const QString &csID, const QString &pName)
455{
456 QString profileName = pName;
457
458 const KoColorSpace *cs = 0;
459
460 {
461 typename LockPolicy::ReadLocker l(&registrylock);
462
463 if (profileName.isEmpty()) {
464 profileName = defaultProfileForCsIdImpl(csID);
465 }
466
467 if (!profileName.isEmpty()) {
468 // quick attempt to fetch a cached color space
469 cs = getCachedColorSpaceImpl(csID, profileName);
470 }
471 }
472
473 if (!cs) {
474 // slow attempt to create a color space
475 typename LockPolicy::WriteLocker l(&registrylock);
476
477 const KoColorProfile *profile =
478 profileForCsIdWithFallbackImpl(csID, profileName);
479
480 if (!profile) return 0;
481
482 cs = lazyCreateColorSpaceImpl(csID, profile);
483 }
484 else {
485 KIS_SAFE_ASSERT_RECOVER_NOOP(cs->id() == csID);
486 KIS_SAFE_ASSERT_RECOVER_NOOP(cs->profile()->name() == profileName);
487 }
488
489 return cs;
490}
491
492
493const KoColorSpace * KoColorSpaceRegistry::Private::colorSpace1(const QString &csID, const KoColorProfile *profile)
494{
495 if (csID.isEmpty()) {
496 return 0;
497 } else if (!profile) {
498 return colorSpace1(csID);
499 }
500
501 const KoColorSpace *cs = 0;
502
503 {
504 QReadLocker l(&registrylock);
505 cs = getCachedColorSpaceImpl(csID, profile->name());
506 }
507
508 // the profile should have already been added to the registry by createColorProfile() method
509 KIS_SAFE_ASSERT_RECOVER(profileStorage.containsProfile(profile)) {
510 // warning! locking happens inside addProfile!
511 q->addProfile(profile);
512 }
513
514 if (!cs) {
515 // The profile was not stored and thus not the combination either
516 QWriteLocker l(&registrylock);
517 KoColorSpaceFactory *csf = colorSpaceFactoryRegistry.value(csID);
518
519 if (!csf) {
520 dbgPigmentCSRegistry << "Unknown color space type :" << csf;
521 return 0;
522 }
523
524 if (!csf->profileIsCompatible(profile)) {
525 dbgPigmentCSRegistry << "Profile is not compatible:" << csf << profile->name();
526 return 0;
527 }
528
529 cs = lazyCreateColorSpaceImpl(csID, profile);
530 }
531
532 return cs;
533}
534
536{
537 if (!d->alphaCs) {
538 d->alphaCs = d->colorSpace1(KoAlphaColorSpace::colorSpaceId());
539 }
540 Q_ASSERT(d->alphaCs);
541 return d->alphaCs;
542}
543
545{
546 if (!d->alphaU16Cs) {
547 d->alphaU16Cs = d->colorSpace1(KoAlphaU16ColorSpace::colorSpaceId());
548 }
549 Q_ASSERT(d->alphaU16Cs);
550 return d->alphaU16Cs;
551}
552
553#ifdef HAVE_OPENEXR
554const KoColorSpace * KoColorSpaceRegistry::alpha16f()
555{
556 if (!d->alphaF16Cs) {
557 d->alphaF16Cs = d->colorSpace1(KoAlphaF16ColorSpace::colorSpaceId());
558 }
559 Q_ASSERT(d->alphaF16Cs);
560 return d->alphaF16Cs;
561}
562#endif
563
565{
566 if (!d->alphaF32Cs) {
567 d->alphaF32Cs = d->colorSpace1(KoAlphaF32ColorSpace::colorSpaceId());
568 }
569 Q_ASSERT(d->alphaF32Cs);
570 return d->alphaF32Cs;
571}
572
573const KoColorSpace *KoColorSpaceRegistry::graya8(const QString &profile)
574{
575
576 if (profile.isEmpty()) {
577 KoColorSpaceFactory* factory = d->colorSpaceFactoryRegistry.get(GrayAColorModelID.id());
579 }
580 else {
582 }
583
584}
585
587{
588 if (!profile) {
589 return graya8();
590 }
591 else {
593 }
594
595}
596
597const KoColorSpace *KoColorSpaceRegistry::graya16(const QString &profile)
598{
599 if (profile.isEmpty()) {
600 KoColorSpaceFactory* factory = d->colorSpaceFactoryRegistry.get(GrayAColorModelID.id());
602 }
603 else {
605 }
606
607}
608
610{
611 if (!profile) {
612 return graya16();
613 }
614 else {
616 }
617}
618
619
620const KoColorSpace * KoColorSpaceRegistry::rgb8(const QString &profileName)
621{
622 if (profileName.isEmpty()) {
623 if (!d->rgbU8sRGB) {
624 d->rgbU8sRGB = d->colorSpace1(KoRgbU8ColorSpace::colorSpaceId());
625 }
626 Q_ASSERT(d->rgbU8sRGB);
627 return d->rgbU8sRGB;
628 }
629 return d->colorSpace1(KoRgbU8ColorSpace::colorSpaceId(), profileName);
630}
631
633{
634 if (profile == 0) {
635 if (!d->rgbU8sRGB) {
636 d->rgbU8sRGB = d->colorSpace1(KoRgbU8ColorSpace::colorSpaceId());
637 }
638 Q_ASSERT(d->rgbU8sRGB);
639 return d->rgbU8sRGB;
640 }
641 return d->colorSpace1(KoRgbU8ColorSpace::colorSpaceId(), profile);
642}
643
644const KoColorSpace * KoColorSpaceRegistry::rgb16(const QString &profileName)
645{
646 return d->colorSpace1(KoRgbU16ColorSpace::colorSpaceId(), profileName);
647}
648
650{
651 return d->colorSpace1(KoRgbU16ColorSpace::colorSpaceId(), profile);
652}
653
654const KoColorSpace * KoColorSpaceRegistry::lab16(const QString &profileName)
655{
656 if (profileName.isEmpty()) {
657 if (!d->lab16sLAB) {
658 d->lab16sLAB = d->colorSpace1(KoLabColorSpace::colorSpaceId());
659 }
660 return d->lab16sLAB;
661 }
662 return d->colorSpace1(KoLabColorSpace::colorSpaceId(), profileName);
663}
664
666{
667 if (profile == 0) {
668 if (!d->lab16sLAB) {
669 d->lab16sLAB = d->colorSpace1(KoLabColorSpace::colorSpaceId());
670 }
671 Q_ASSERT(d->lab16sLAB);
672 return d->lab16sLAB;
673 }
674 return d->colorSpace1(KoLabColorSpace::colorSpaceId(), profile);
675}
676
678{
679 return profileByName("Rec2020-elle-V4-g10.icc");
680}
681
683{
684 return profileByName("High Dynamic Range UHDTV Wide Color Gamut Display (Rec. 2020) - SMPTE ST 2084 PQ EOTF");
685}
686
688{
689 return profileByName("sRGB-elle-V2-g10.icc");
690}
691
693{
694 return profileByName("sRGB-elle-V2-srgbtrc.icc");
695}
696
697const KoColorProfile *KoColorSpaceRegistry::profileFor(const QVector<double> &colorants, ColorPrimaries colorPrimaries, TransferCharacteristics transferFunction) const
698{
699 if (colorPrimaries == PRIMARIES_ITU_R_BT_709_5) {
700 if (transferFunction == TRC_IEC_61966_2_1) {
701 return p709SRGBProfile();
702 } else if (transferFunction == TRC_LINEAR) {
703 return p709G10Profile();
704 }
705 }
706
707 if (colorPrimaries == PRIMARIES_ITU_R_BT_2020_2_AND_2100_0) {
708 if (transferFunction == TRC_ITU_R_BT_2100_0_PQ) {
709 return p2020PQProfile();
710 } else if (transferFunction == TRC_LINEAR) {
711 return p2020G10Profile();
712 }
713 }
714
715 QList<const KoColorProfile*> list = d->profileStorage.profilesFor(colorants, colorPrimaries, transferFunction);
716 if (!list.empty()) {
717 return list.first();
718 }
719
721 if (engine) {
722 return engine->getProfile(colorants, colorPrimaries, transferFunction);
723 }
724
725 return nullptr;
726}
727
728static QMap<QColorSpace::Primaries, ColorPrimaries> mapQColorSpaceColorPrimaries {
729 {QColorSpace::Primaries::SRgb, PRIMARIES_ITU_R_BT_709_5},
730 {QColorSpace::Primaries::AdobeRgb, PRIMARIES_ADOBE_RGB_1998},
731 {QColorSpace::Primaries::DciP3D65, PRIMARIES_SMPTE_EG_432_1},
732 {QColorSpace::Primaries::ProPhotoRgb, PRIMARIES_PROPHOTO},
733#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
734 {QColorSpace::Primaries::Bt2020, PRIMARIES_ITU_R_BT_2020_2_AND_2100_0},
735#endif
736 {QColorSpace::Primaries::Custom, PRIMARIES_UNSPECIFIED}
737};
738
739static QMap<QColorSpace::TransferFunction, TransferCharacteristics> mapQColorSpaceColorTransfer {
740 {QColorSpace::TransferFunction::Linear, TRC_LINEAR},
741 {QColorSpace::TransferFunction::SRgb, TRC_IEC_61966_2_1},
742 {QColorSpace::TransferFunction::ProPhotoRgb, TRC_PROPHOTO},
743#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
744 {QColorSpace::TransferFunction::Bt2020, TRC_ITU_R_BT_2020_2_12bit},
745 {QColorSpace::TransferFunction::St2084, TRC_ITU_R_BT_2100_0_PQ},
746 {QColorSpace::TransferFunction::Hlg, TRC_ITU_R_BT_2100_0_HLG},
747#endif
748 {QColorSpace::TransferFunction::Custom, TRC_UNSPECIFIED}
749};
750
752{
753 const KoColorProfile *profile = nullptr;
754#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
755 ColorPrimaries primaries = mapQColorSpaceColorPrimaries.value(space.primaries(), PRIMARIES_UNSPECIFIED);
756 TransferCharacteristics transfer = mapQColorSpaceColorTransfer.value(space.transferFunction(), TRC_UNSPECIFIED);
757 if (space.transferFunction() == QColorSpace::TransferFunction::Gamma || (transfer == TRC_UNSPECIFIED && primaries == PRIMARIES_UNSPECIFIED)) {
758 // load icc profile.
759 QByteArray profileData = space.iccProfile();
761 } else {
762 QVector<double> colorants;
763 profile = profileFor(colorants, primaries, transfer);
764 }
765#else
766 // We want to test RGB before testing wether we are dealing with a LUT, because we want to funnel rec2020 pq into loading our profile.
767
768 if (space.colorModel() == QColorSpace::ColorModel::Rgb) {
769 if (space.transferFunction() == QColorSpace::TransferFunction::Gamma) {
770 // load icc profile.
771 QByteArray profileData = space.iccProfile();
773 }
774 ColorPrimaries primaries = mapQColorSpaceColorPrimaries.value(space.primaries(), PRIMARIES_UNSPECIFIED);
775 TransferCharacteristics transfer = mapQColorSpaceColorTransfer.value(space.transferFunction(), TRC_UNSPECIFIED);
776 // In qt6.9 we can get the 'primary points'.
777 QVector<double> colorants;
778 if (!profile && (!colorants.isEmpty() || primaries != PRIMARIES_UNSPECIFIED) && transfer != TRC_UNSPECIFIED) {
779 profile = profileFor(colorants, primaries, transfer);
780 }
781 } else if (space.colorModel() == QColorSpace::ColorModel::Gray) {
782 TransferCharacteristics transfer = mapQColorSpaceColorTransfer.value(space.transferFunction(), TRC_UNSPECIFIED);
783 if (space.transferFunction() == QColorSpace::TransferFunction::Gamma) {
784 QByteArray profileData = space.iccProfile();
786
787 }
788 QVector<double> colorants;
789 colorants << space.whitePoint().x() << space.whitePoint().y();
790 if (!profile && !colorants.isEmpty() && transfer != TRC_UNSPECIFIED) {
791 profile = profileFor(colorants, PRIMARIES_UNSPECIFIED, transfer);
792 }
793 }
794#endif
795 if (!profile) {
796 // We might now have a non-qt supported colormodel. Let's load the raw data directly.
798 if (engine) {
799 QByteArray profileData = space.iccProfile();
800 profile = engine->addProfile(profileData);
801 }
802 }
803 if (profile) {
804 const KoColorProfile *preExisting = profileByUniqueId(profile->uniqueId());
805 if (!preExisting) {
806 preExisting = profileByName(profile->name());
807 }
808 if (preExisting) {
809 return preExisting;
810 } else {
811 addProfile(profile);
812 }
813 }
814
815 return profile;
816}
817
819{
820 if (!profile) return QColorSpace(QColorSpace::SRgb);
821 if (profile == p709SRGBProfile()) {
822 return QColorSpace(QColorSpace::SRgb);
823 }
824#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
825 if (profile == p2020PQProfile()) {
826 return QColorSpace(QColorSpace::Bt2100Pq);
827 }
828#endif
829 return QColorSpace::fromIccProfile(profile->rawData());
830}
831
833{
834 QReadLocker l(&d->registrylock);
835
836 QList<KoID> ids;
837 QList<KoColorSpaceFactory*> factories = d->colorSpaceFactoryRegistry.values();
838 Q_FOREACH (KoColorSpaceFactory* factory, factories) {
839 if (!ids.contains(factory->colorModelId())
840 && (option == AllColorSpaces || factory->userVisible())) {
841 ids << factory->colorModelId();
842 }
843 }
844 return ids;
845}
847{
848 return colorDepthList(colorModelId.id(), option);
849}
850
851
853{
854 QReadLocker l(&d->registrylock);
855
856 QList<KoID> ids;
857 QList<KoColorSpaceFactory*> factories = d->colorSpaceFactoryRegistry.values();
858 Q_FOREACH (KoColorSpaceFactory* factory, factories) {
859 if (!ids.contains(KoID(factory->colorDepthId()))
860 && factory->colorModelId().id() == colorModelId
861 && (option == AllColorSpaces || factory->userVisible())) {
862 ids << factory->colorDepthId();
863 }
864 }
865 QList<KoID> r;
866
869 if (ids.contains(Float16BitsColorDepthID)) r << Float16BitsColorDepthID;
870 if (ids.contains(Float32BitsColorDepthID)) r << Float32BitsColorDepthID;
871 if (ids.contains(Float64BitsColorDepthID)) r << Float64BitsColorDepthID;
872
873 return r;
874}
875
876QString KoColorSpaceRegistry::Private::colorSpaceIdImpl(const QString & colorModelId, const QString & colorDepthId) const
877{
878 for (auto it = colorSpaceFactoryRegistry.constBegin(); it != colorSpaceFactoryRegistry.constEnd(); ++it) {
879 if (it.value()->colorModelId().id() == colorModelId && it.value()->colorDepthId().id() == colorDepthId) {
880 return it.value()->id();
881 }
882 }
883 return "";
884}
885
886QString KoColorSpaceRegistry::colorSpaceId(const QString & colorModelId, const QString & colorDepthId) const
887{
888 QReadLocker l(&d->registrylock);
889 return d->colorSpaceIdImpl(colorModelId, colorDepthId);
890}
891
892QString KoColorSpaceRegistry::colorSpaceId(const KoID& colorModelId, const KoID& colorDepthId) const
893{
894 return colorSpaceId(colorModelId.id(), colorDepthId.id());
895}
896
897KoID KoColorSpaceRegistry::colorSpaceColorModelId(const QString & _colorSpaceId) const
898{
899 QReadLocker l(&d->registrylock);
900
901 KoColorSpaceFactory* factory = d->colorSpaceFactoryRegistry.get(_colorSpaceId);
902 if (factory) {
903 return factory->colorModelId();
904 } else {
905 return KoID();
906 }
907}
908
909KoID KoColorSpaceRegistry::colorSpaceColorDepthId(const QString & _colorSpaceId) const
910{
911 QReadLocker l(&d->registrylock);
912
913 KoColorSpaceFactory* factory = d->colorSpaceFactoryRegistry.get(_colorSpaceId);
914 if (factory) {
915 return factory->colorDepthId();
916 } else {
917 return KoID();
918 }
919}
920
922{
923 return d->colorConversionSystem;
924}
925
927{
928 return d->colorConversionCache;
929}
930
932{
933 if (_colorSpace->d->deletability != NotOwnedByRegistry) {
934 return _colorSpace;
935 } else if (*_colorSpace == *d->alphaCs) {
936 return d->alphaCs;
937 } else {
938 const KoColorSpace* cs = d->colorSpace1(_colorSpace->id(), _colorSpace->profile());
939 Q_ASSERT(cs);
940 Q_ASSERT(*cs == *_colorSpace);
941 return cs;
942 }
943}
944
946{
947 QReadLocker l(&d->registrylock);
948 QList<KoID> answer;
949 Q_FOREACH (const QString& key, d->colorSpaceFactoryRegistry.keys()) {
950 answer.append(KoID(key, d->colorSpaceFactoryRegistry.get(key)->name()));
951 }
952
953 return answer;
954}
955
957{
958 ProfileRegistrationInterface(KoColorSpaceRegistry::Private *_d) : d(_d) {}
959
960 const KoColorProfile* profileByName(const QString &profileName) const override {
961 return d->profileStorage.profileByName(profileName);
962 }
963
964 void registerNewProfile(KoColorProfile *profile) override {
965 d->profileStorage.addProfile(profile);
966 d->colorConversionSystem->insertColorProfile(profile);
967 }
968
970};
971
972const KoColorProfile* KoColorSpaceRegistry::createColorProfile(const QString& colorModelId, const QString& colorDepthId, const QByteArray& rawData)
973{
974 QWriteLocker l(&d->registrylock);
975 KoColorSpaceFactory* factory_ = d->colorSpaceFactoryRegistry.get(d->colorSpaceIdImpl(colorModelId, colorDepthId));
976
977 Private::ProfileRegistrationInterface interface(d);
978 return factory_->colorProfile(rawData, &interface);
979}
980
982{
983 QList<const KoColorSpace*> colorSpaces;
984
985 // TODO: thread-unsafe code: the factories might change right after the lock in released
986 // HINT: used in a unittest only!
987
988 d->registrylock.lockForRead();
989 QList<KoColorSpaceFactory*> factories = d->colorSpaceFactoryRegistry.values();
990 d->registrylock.unlock();
991
992 Q_FOREACH (KoColorSpaceFactory* factory, factories) {
993 // Don't test with ycbcr for now, since we don't have a default profile for it.
994 if (factory->colorModelId().id().startsWith("Y")) continue;
995 if (visibility == AllColorSpaces || factory->userVisible()) {
996 if (pSelection == OnlyDefaultProfile) {
997 const KoColorSpace *cs = d->colorSpace1(factory->id());
998 if (cs) {
999 colorSpaces.append(cs);
1000 }
1001 else {
1002 warnPigment << "Could not create colorspace for id" << factory->id() << "since there is no working default profile";
1003 }
1004 } else {
1006 Q_FOREACH (const KoColorProfile * profile, profiles) {
1007 const KoColorSpace *cs = d->colorSpace1(factory->id(), profile);
1008 if (cs) {
1009 colorSpaces.append(cs);
1010 }
1011 else {
1012 warnPigment << "Could not create colorspace for id" << factory->id() << "and profile" << profile->name();
1013 }
1014 }
1015 }
1016 }
1017 }
1018
1019 return colorSpaces;
1020}
#define dbgPigment
#define warnPigment
#define dbgPigmentCSRegistry
const Params2D p
Q_GLOBAL_STATIC(KisStoragePluginRegistry, s_instance)
KoAlphaColorSpaceFactoryImpl< AlphaU16Traits > KoAlphaU16ColorSpaceFactory
KoAlphaColorSpaceFactoryImpl< AlphaU8Traits > KoAlphaColorSpaceFactory
KoAlphaColorSpaceFactoryImpl< AlphaF32Traits > KoAlphaF32ColorSpaceFactory
const KoID Float32BitsColorDepthID("F32", ki18n("32-bit float/channel"))
const KoID Float64BitsColorDepthID("F64", ki18n("64-bit float/channel"))
const KoID GrayAColorModelID("GRAYA", ki18n("Grayscale/Alpha"))
const KoID Float16BitsColorDepthID("F16", ki18n("16-bit float/channel"))
const KoID Integer8BitsColorDepthID("U8", ki18n("8-bit integer/channel"))
const KoID Integer16BitsColorDepthID("U16", ki18n("16-bit integer/channel"))
const KoID RGBAColorModelID("RGBA", ki18n("RGB/Alpha"))
ColorPrimaries
The colorPrimaries enum Enum of colorants, follows ITU H.273 for values 0 to 255, and has extra known...
@ PRIMARIES_ITU_R_BT_2020_2_AND_2100_0
@ PRIMARIES_UNSPECIFIED
@ PRIMARIES_PROPHOTO
@ PRIMARIES_ADOBE_RGB_1998
@ PRIMARIES_SMPTE_EG_432_1
@ PRIMARIES_ITU_R_BT_709_5
TransferCharacteristics
The transferCharacteristics enum Enum of transfer characteristics, follows ITU H.273 for values 0 to ...
@ TRC_ITU_R_BT_2100_0_HLG
@ TRC_ITU_R_BT_2100_0_PQ
@ TRC_IEC_61966_2_1
@ TRC_ITU_R_BT_2020_2_12bit
static QMap< QColorSpace::Primaries, ColorPrimaries > mapQColorSpaceColorPrimaries
static QMap< QColorSpace::TransferFunction, TransferCharacteristics > mapQColorSpaceColorTransfer
@ NotOwnedByRegistry
@ OwnedByRegistryDoNotDelete
@ OwnedByRegistryRegistryDeletes
static QString colorSpaceId()
The KoColorProfileStorage class is a "composite subclass" of KoColorSpaceRegistry that ensures that t...
const KoColorProfile * profileByName(const QString &name) const
QList< const KoColorProfile * > profilesFor(const KoColorSpaceFactory *csf) const
bool containsProfile(const KoColorProfile *profile)
containsProfile shows if a profile is registered in the storage
static KoColorSpaceEngineRegistry * instance()
Private *const d
virtual const KoColorProfile * profile() const =0
const T value(const QString &id) const
T get(const QString &id) const
QHash< QString, T >::const_iterator constBegin() const
QHash< QString, T >::const_iterator constEnd() const
Definition KoID.h:30
QString id() const
Definition KoID.cpp:63
static QString colorSpaceId()
void load(const QString &serviceType, const PluginsConfig &config=PluginsConfig(), QObject *owner=0, bool cache=true)
static KoPluginLoader * instance()
static QString colorSpaceId()
static QString colorSpaceId()
#define KIS_SAFE_ASSERT_RECOVER(cond)
Definition kis_assert.h:126
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130
virtual QByteArray rawData() const
virtual QByteArray uniqueId() const =0
virtual KoColorProfile * clone() const =0
virtual const KoColorProfile * getProfile(const QVector< double > &colorants, ColorPrimaries colorPrimaries, TransferCharacteristics transferFunction)=0
getProfile This tries to generate a profile with the given characteristics and add it to the registry...
virtual const KoColorProfile * addProfile(const QString &filename)=0
virtual KoID colorDepthId() const =0
virtual QString defaultProfile() const =0
virtual KoID colorModelId() const =0
virtual QString id() const =0
virtual bool userVisible() const =0
const KoColorProfile * colorProfile(const QByteArray &rawData, ProfileRegistrationInterface *registrationInterface) const
virtual bool profileIsCompatible(const KoColorProfile *profile) const =0
const KoColorSpace * grabColorSpace(const KoColorProfile *profile)
QList< const KoColorProfile * > profilesFor(const KoColorSpaceFactory *csf) const override
const KoColorSpace * colorSpace(const QString &colorModelId, const QString &colorDepthId, const QString &profileName) override
const KoColorSpaceFactory * colorSpaceFactory(const QString &colorModelId, const QString &colorDepthId) const override
QList< const KoColorSpaceFactory * > colorSpacesFor(const KoColorProfile *profile) const override
ConversionSystemInterface(KoColorSpaceRegistry *parentRegistry)
const KoColorProfile * profileByName(const QString &profileName) const override
const KoColorProfile * profileByName(const QString &name) const
const KoColorSpace * colorSpace1(const QString &colorSpaceId, const QString &pName=QString())
QString colorSpaceId(const QString &colorModelId, const QString &colorDepthId) const
const KoColorSpace * lab16(const QString &profileName=QString())
const KoColorSpace * alpha32f()
KoColorConversionCache * colorConversionCache
const KoColorSpace * lazyCreateColorSpaceImpl(const QString &csID, const KoColorProfile *profile)
QList< KoID > colorDepthList(const KoID &colorModelId, ColorSpaceListVisibility option) const
void addProfileAlias(const QString &name, const QString &to)
const KoColorSpace * permanentColorspace(const KoColorSpace *_colorSpace)
@ OnlyDefaultProfile
Only add the default profile.
QString idsToCacheName(const QString &csId, const QString &profileName) const
const KoColorProfile * profileFor(const QVector< double > &colorants, ColorPrimaries colorPrimaries, TransferCharacteristics transferFunction) const
profileFor tries to find the profile that matches these characteristics, if no such profile is found,...
QString colorSpaceIdImpl(const QString &colorModelId, const QString &colorDepthId) const
QString defaultProfileForCsIdImpl(const QString &csID)
QList< const KoColorSpace * > allColorSpaces(ColorSpaceListVisibility visibility, ColorSpaceListProfilesSelection pSelection)
const KoColorSpace * colorSpace(const QString &colorModelId, const QString &colorDepthId, const KoColorProfile *profile)
const KoColorSpace * colorSpace1(const QString &colorSpaceId, const KoColorProfile *profile)
KoColorConversionTransformation * createColorConverter(const KoColorSpace *srcColorSpace, const KoColorSpace *dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const
static KoColorSpaceRegistry * instance()
Private(KoColorSpaceRegistry *_q)
const KoColorSpace * graya8(const QString &profile=QString())
const KoColorSpace * graya16(const QString &profile=QString())
KoID colorSpaceColorDepthId(const QString &_colorSpaceId) const
const KoColorProfile * p709G10Profile() const
const KoColorProfile * p709SRGBProfile() const
void remove(KoColorSpaceFactory *item)
KoGenericRegistry< KoColorSpaceFactory * > colorSpaceFactoryRegistry
const KoColorSpace * rgb8(const QString &profileName=QString())
const KoColorProfile * profileForQColorSpace(const QColorSpace &space)
profileForQColorSpace Find a KoColorProfile that matches a given QColorSpace. This will use the QColo...
bool profileIsCompatible(const KoColorProfile *profile, const QString &colorSpaceId)
QString defaultProfileForColorSpace(const QString &colorSpaceId) const
const KoColorProfile * profileByUniqueId(const QByteArray &id) const
const KoColorProfile * p2020PQProfile() const
const KoColorSpace * getCachedColorSpaceImpl(const QString &csId, const QString &profileName) const
const KoColorSpace * rgb16(const QString &profileName=QString())
const KoColorSpace * alpha8()
const KoColorProfile * profileForCsIdWithFallbackImpl(const QString &csID, const QString &profileName)
QList< KoID > colorModelsList(ColorSpaceListVisibility option) const
KoID colorSpaceColorModelId(const QString &_colorSpaceId) const
void add(KoColorSpaceFactory *item)
QList< const KoColorProfile * > profilesFor(const QString &csID) const
QColorSpace QColorSpaceForProfile(const KoColorProfile *profile) const
QColorSpaceForProfile Generate a QColorSpace for the given KoColorProfile. Will return sRGB when the ...
void addProfileToMap(KoColorProfile *p)
QString profileAlias(const QString &name) const
const KoColorSpace * alpha16()
void createColorConverters(const KoColorSpace *colorSpace, const QList< QPair< KoID, KoID > > &possibilities, KoColorConversionTransformation *&fromCS, KoColorConversionTransformation *&toCS) const
const KoColorProfile * p2020G10Profile() const
KoColorConversionSystem * colorConversionSystem
void removeProfile(KoColorProfile *profile)
@ AllColorSpaces
All color space even those not visible to the user.
const KoColorProfile * createColorProfile(const QString &colorModelId, const QString &colorDepthId, const QByteArray &rawData)
QScopedPointer< ConversionSystemInterface > conversionSystemInterface
void addProfile(KoColorProfile *profile)
QHash< QString, const KoColorSpace * > csMap
KoColorProfileStorage profileStorage
QList< KoID > listKeys() const
const char * blacklist
This contains the variable name for the list of plugins (by library name) that will not be loaded.