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#include "KoColorProfileQuery.h"
26
32#include "KoColorSpace_p.h"
33
34#include "kis_assert.h"
37
39
41
42
43struct Q_DECL_HIDDEN KoColorSpaceRegistry::Private {
44
45 // interface for KoColorSpaceFactory
46 struct ProfileRegistrationInterface;
47 // interface for KoColorConversionSystem
48 struct ConversionSystemInterface;
49
50
52
54
57 QHash<QString, const KoColorSpace *> csMap;
58 QScopedPointer<ConversionSystemInterface> conversionSystemInterface;
59 KoColorConversionSystem *colorConversionSystem {nullptr};
60 KoColorConversionCache* colorConversionCache {nullptr};
61 const KoColorSpace *rgbU8sRGB {nullptr};
62 const KoColorSpace *lab16sLAB {nullptr};
63 const KoColorSpace *alphaCs {nullptr};
64 const KoColorSpace *alphaU16Cs {nullptr};
65#ifdef HAVE_OPENEXR
66 const KoColorSpace *alphaF16Cs {nullptr};
67#endif
68 const KoColorSpace *alphaF32Cs {nullptr};
69 QReadWriteLock registrylock;
70
80 const KoColorSpace* getCachedColorSpaceImpl(const QString & csId, const QString & profileName) const;
81
82 QString idsToCacheName(const QString & csId, const QString & profileName) const;
83 QString defaultProfileForCsIdImpl(const QString &csID);
84 const KoColorProfile * profileForCsIdWithFallbackImpl(const QString &csID, const QString &profileName);
85 QString colorSpaceIdImpl(const QString & colorModelId, const QString & colorDepthId) const;
86
87 const KoColorSpace *lazyCreateColorSpaceImpl(const QString &csID, const KoColorProfile *profile);
88
94 template<class LockPolicy = NormalLockPolicy>
95 const KoColorSpace * colorSpace1(const QString &colorSpaceId, const QString &pName = QString());
96
103 const KoColorSpace * colorSpace1(const QString &colorSpaceId, const KoColorProfile *profile);
104};
105
107{
109 : q(parentRegistry)
110 {
111 }
112
113 const KoColorSpace * colorSpace(const QString & colorModelId, const QString & colorDepthId, const QString &profileName) override {
114 return q->d->colorSpace1<NoLockPolicy>(q->d->colorSpaceIdImpl(colorModelId, colorDepthId), profileName);
115 }
116
117 const KoColorSpaceFactory* colorSpaceFactory(const QString &colorModelId, const QString &colorDepthId) const override {
118 return q->d->colorSpaceFactoryRegistry.get(q->d->colorSpaceIdImpl(colorModelId, colorDepthId));
119 }
120
122 return q->d->profileStorage.profilesFor(csf);
123 }
124
127 Q_FOREACH (KoColorSpaceFactory* csf, q->d->colorSpaceFactoryRegistry.values()) {
128 if (csf->profileIsCompatible(profile)) {
129 csfs.push_back(csf);
130 }
131 }
132 return csfs;
133 }
134
135private:
137};
138
140{
141 if (!s_instance.exists()) {
142 s_instance->init();
143 }
144 return s_instance;
145}
146
147
149{
150 d->rgbU8sRGB = 0;
151 d->lab16sLAB = 0;
152 d->alphaCs = 0;
153 d->alphaU16Cs = 0;
154#ifdef HAVE_OPENEXR
155 d->alphaF16Cs = 0;
156#endif
157 d->alphaF32Cs = 0;
158
159 d->conversionSystemInterface.reset(new Private::ConversionSystemInterface(this));
160 d->colorConversionSystem = new KoColorConversionSystem(d->conversionSystemInterface.data());
161 d->colorConversionCache = new KoColorConversionCache;
162
164
166
167 // Create the built-in colorspaces
168 QList<KoColorSpaceFactory *> localFactories;
169 localFactories
172 #ifdef HAVE_OPENEXR
173 << new KoAlphaF16ColorSpaceFactory()
174 #endif
179
180 Q_FOREACH (KoColorSpaceFactory *factory, localFactories) {
181 add(factory);
182 }
183
185 config.blacklist = "ColorSpacePluginsDisabled";
186 config.group = "krita";
187 KoPluginLoader::instance()->load("Krita/ColorSpace", config);
188
189 KoPluginLoader::PluginsConfig configExtensions;
190 configExtensions.blacklist = "ColorSpaceExtensionsPluginsDisabled";
191 configExtensions.group = "krita";
192 KoPluginLoader::instance()->load("Krita/ColorSpaceExtension", configExtensions);
193
194
195 dbgPigment << "Loaded the following colorspaces:";
196 Q_FOREACH (const KoID& id, listKeys()) {
197 dbgPigment << "\t" << id.id() << "," << id.name();
198 }
199}
200
202{
203 d->colorConversionSystem = nullptr;
204 d->colorConversionCache = nullptr;
205}
206
208{
209 delete d->colorConversionSystem;
210 d->colorConversionSystem = nullptr;
211
212 Q_FOREACH (const KoColorSpace * cs, d->csMap) {
213 cs->d->deletability = OwnedByRegistryRegistryDeletes;
214 delete cs;
215 }
216 d->csMap.clear();
217
218 // deleting colorspaces calls a function in the cache
219 delete d->colorConversionCache;
220 d->colorConversionCache = nullptr;
221
222 // Delete the colorspace factories
223 Q_FOREACH(KoColorSpaceFactory *f, d->colorSpaceFactoryRegistry.values()) {
224 d->colorSpaceFactoryRegistry.remove(f->id());
225 delete f;
226 }
227 Q_FOREACH(KoColorSpaceFactory *f, d->colorSpaceFactoryRegistry.doubleEntries()) {
228 delete f;
229 }
230
231 delete d;
232}
233
235{
236 QWriteLocker l(&d->registrylock);
237 d->colorSpaceFactoryRegistry.add(item);
238 d->colorConversionSystem->insertColorSpace(item);
239}
240
242{
243 QWriteLocker l(&d->registrylock);
244
245 QList<QString> toremove;
246 Q_FOREACH (const KoColorSpace * cs, d->csMap) {
247 if (cs->id() == item->id()) {
248 toremove.push_back(d->idsToCacheName(cs->id(), cs->profile()->name()));
249 cs->d->deletability = OwnedByRegistryRegistryDeletes;
250 }
251 }
252
253 Q_FOREACH (const QString& id, toremove) {
254 d->csMap.remove(id);
255 // TODO: should not it delete the color space when removing it from the map ?
256 }
257 d->colorSpaceFactoryRegistry.remove(item->id());
258}
259
260void KoColorSpaceRegistry::addProfileAlias(const QString& name, const QString& to)
261{
262 d->profileStorage.addProfileAlias(name, to);
263}
264
266{
267 d->profileStorage.removeProfileAlias(name);
268}
269
270QString KoColorSpaceRegistry::profileAlias(const QString& name) const
271{
272 return d->profileStorage.profileAlias(name);
273}
274
276{
277 return d->profileStorage.profileByName(name);
278}
279
281{
282 return d->profileStorage.profileByUniqueId(id);
283}
284
286{
287 QReadLocker l(&d->registrylock);
288 return d->profileStorage.profilesFor(d->colorSpaceFactoryRegistry.value(csID));
289}
290
291const KoColorSpace * KoColorSpaceRegistry::colorSpace(const QString & colorModelId, const QString & colorDepthId, const KoColorProfile *profile)
292{
293 return d->colorSpace1(colorSpaceId(colorModelId, colorDepthId), profile);
294}
295
296const KoColorSpace * KoColorSpaceRegistry::colorSpace(const QString & colorModelId, const QString & colorDepthId, const QString &profileName)
297{
298 return d->colorSpace1(colorSpaceId(colorModelId, colorDepthId), profileName);
299}
300
301const KoColorSpace * KoColorSpaceRegistry::colorSpace(const QString & colorModelId, const QString & colorDepthId)
302{
303 return d->colorSpace1(colorSpaceId(colorModelId, colorDepthId));
304}
305
306bool KoColorSpaceRegistry::profileIsCompatible(const KoColorProfile *profile, const QString &colorSpaceId)
307{
308 QReadLocker l(&d->registrylock);
309 KoColorSpaceFactory *csf = d->colorSpaceFactoryRegistry.value(colorSpaceId);
310
311 return csf ? csf->profileIsCompatible(profile) : false;
312}
313
315{
316 d->profileStorage.addProfile(p);
317}
318
320{
321 if (!p->valid()) return;
322
323 QWriteLocker locker(&d->registrylock);
324 if (p->valid()) {
326 d->colorConversionSystem->insertColorProfile(p);
327 }
328}
329
331{
332 addProfile(profile->clone());
333}
334
336{
337 d->profileStorage.removeProfile(profile);
338 // FIXME: how about removing it from conversion system?
339}
340
341const KoColorSpace* KoColorSpaceRegistry::Private::getCachedColorSpaceImpl(const QString & csID, const QString & profileName) const
342{
343 auto it = csMap.find(idsToCacheName(csID, profileName));
344
345 if (it != csMap.end()) {
346 return it.value();
347 }
348
349 return 0;
350}
351
352QString KoColorSpaceRegistry::Private::idsToCacheName(const QString & csID, const QString & profileName) const
353{
354 return csID + "<comb>" + profileName;
355}
356
357QString KoColorSpaceRegistry::defaultProfileForColorSpace(const QString &colorSpaceId) const
358{
359 QReadLocker l(&d->registrylock);
360 return d->defaultProfileForCsIdImpl(colorSpaceId);
361}
362
363KoColorConversionTransformation *KoColorSpaceRegistry::createColorConverter(const KoColorSpace *srcColorSpace, const KoColorSpace *dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const
364{
365 QWriteLocker l(&d->registrylock);
366 return d->colorConversionSystem->createColorConverter(srcColorSpace, dstColorSpace, renderingIntent, conversionFlags);
367}
368
369void KoColorSpaceRegistry::createColorConverters(const KoColorSpace *colorSpace, const QList<QPair<KoID, KoID> > &possibilities, KoColorConversionTransformation *&fromCS, KoColorConversionTransformation *&toCS) const
370{
371 QWriteLocker l(&d->registrylock);
372 d->colorConversionSystem->createColorConverters(colorSpace, possibilities, fromCS, toCS);
373}
374
375QString KoColorSpaceRegistry::Private::defaultProfileForCsIdImpl(const QString &csID)
376{
377 QString defaultProfileName;
378
379 KoColorSpaceFactory *csf = colorSpaceFactoryRegistry.value(csID);
380 if (csf) {
381 defaultProfileName = csf->defaultProfile();
382 } else {
383 dbgPigmentCSRegistry << "Unknown color space type : " << csID;
384 }
385
386 return defaultProfileName;
387}
388
389const KoColorProfile *KoColorSpaceRegistry::Private::profileForCsIdWithFallbackImpl(const QString &csID, const QString &profileName)
390{
391 const KoColorProfile *profile = 0;
392
393 // last attempt at getting a profile, sometimes the default profile, like adobe cmyk isn't available.
394 profile = profileStorage.profileByName(profileName);
395 if (!profile) {
396 dbgPigmentCSRegistry << "Profile not found :" << profileName;
397
398 // first try: default
399 profile = profileStorage.profileByName(defaultProfileForCsIdImpl(csID));
400
401 if (!profile) {
402 // second try: first profile in the list
403 QList<const KoColorProfile *> profiles = profileStorage.profilesFor(colorSpaceFactoryRegistry.value(csID));
404 if (profiles.isEmpty() || !profiles.first()) {
405 dbgPigmentCSRegistry << "Couldn't fetch a fallback profile:" << profileName;
406 qWarning() << "profileForCsIdWithFallbackImpl couldn't fetch a fallback profile for " << qUtf8Printable(profileName);
407 return 0;
408 }
409
410 profile = profiles.first();
411 }
412 }
413
414 return profile;
415}
416
417const KoColorSpace *KoColorSpaceRegistry::Private::lazyCreateColorSpaceImpl(const QString &csID, const KoColorProfile *profile)
418{
419 const KoColorSpace *cs = 0;
420
421 /*
422 * We need to check again here, a thread requesting the same colorspace could've added it
423 * already, in between the read unlock and write lock.
424 * TODO: We also potentially changed profileName content, which means we maybe are going to
425 * create a colorspace that's actually in the space registry cache, but currently this might
426 * not be an issue because the colorspace should be cached also by the factory, so it won't
427 * create a new instance. That being said, having two caches with the same stuff doesn't make
428 * much sense.
429 */
430 cs = getCachedColorSpaceImpl(csID, profile->name());
431 if (!cs) {
432 KoColorSpaceFactory *csf = colorSpaceFactoryRegistry.value(csID);
433 if (!csf) {
434 qWarning() << "Unable to create color space factory for" << csID;
435 return 0;
436 }
437 cs = csf->grabColorSpace(profile);
438 if (!cs) {
439 dbgPigmentCSRegistry << "Unable to create color space";
440 qWarning() << "lazyCreateColorSpaceImpl was unable to create a color space for " << csID;
441 return 0;
442 }
443
444 dbgPigmentCSRegistry << "colorspace count: " << csMap.count()
445 << ", adding name: " << idsToCacheName(cs->id(), cs->profile()->name())
446 << "\n\tcsID" << csID
447 << "\n\tcs->id()" << cs->id()
448 << "\n\tcs->profile()->name()" << cs->profile()->name()
449 << "\n\tprofile->name()" << profile->name();
450 Q_ASSERT(cs->id() == csID);
451 Q_ASSERT(cs->profile()->name() == profile->name());
452 csMap[idsToCacheName(cs->id(), cs->profile()->name())] = cs;
453 cs->d->deletability = OwnedByRegistryDoNotDelete;
454 }
455
456 return cs;
457}
458
459template<class LockPolicy>
460const KoColorSpace * KoColorSpaceRegistry::Private::colorSpace1(const QString &csID, const QString &pName)
461{
462 QString profileName = pName;
463
464 const KoColorSpace *cs = 0;
465
466 {
467 typename LockPolicy::ReadLocker l(&registrylock);
468
469 if (profileName.isEmpty()) {
470 profileName = defaultProfileForCsIdImpl(csID);
471 }
472
473 if (!profileName.isEmpty()) {
474 // quick attempt to fetch a cached color space
475 cs = getCachedColorSpaceImpl(csID, profileName);
476 }
477 }
478
479 if (!cs) {
480 // slow attempt to create a color space
481 typename LockPolicy::WriteLocker l(&registrylock);
482
483 const KoColorProfile *profile =
484 profileForCsIdWithFallbackImpl(csID, profileName);
485
486 if (!profile) return 0;
487
488 cs = lazyCreateColorSpaceImpl(csID, profile);
489 }
490 else {
491 KIS_SAFE_ASSERT_RECOVER_NOOP(cs->id() == csID);
492 KIS_SAFE_ASSERT_RECOVER_NOOP(cs->profile()->name() == profileName);
493 }
494
495 return cs;
496}
497
498
499const KoColorSpace * KoColorSpaceRegistry::Private::colorSpace1(const QString &csID, const KoColorProfile *profile)
500{
501 if (csID.isEmpty()) {
502 return 0;
503 } else if (!profile) {
504 return colorSpace1(csID);
505 }
506
507 const KoColorSpace *cs = 0;
508
509 {
510 QReadLocker l(&registrylock);
511 cs = getCachedColorSpaceImpl(csID, profile->name());
512 }
513
514 // the profile should have already been added to the registry by createColorProfile() method
515 KIS_SAFE_ASSERT_RECOVER(profileStorage.containsProfile(profile)) {
516 // warning! locking happens inside addProfile!
517 q->addProfile(profile);
518 }
519
520 if (!cs) {
521 // The profile was not stored and thus not the combination either
522 QWriteLocker l(&registrylock);
523 KoColorSpaceFactory *csf = colorSpaceFactoryRegistry.value(csID);
524
525 if (!csf) {
526 dbgPigmentCSRegistry << "Unknown color space type :" << csf;
527 return 0;
528 }
529
530 if (!csf->profileIsCompatible(profile)) {
531 dbgPigmentCSRegistry << "Profile is not compatible:" << csf << profile->name();
532 return 0;
533 }
534
535 cs = lazyCreateColorSpaceImpl(csID, profile);
536 }
537
538 return cs;
539}
540
542{
543 if (!d->alphaCs) {
544 d->alphaCs = d->colorSpace1(KoAlphaColorSpace::colorSpaceId());
545 }
546 Q_ASSERT(d->alphaCs);
547 return d->alphaCs;
548}
549
551{
552 if (!d->alphaU16Cs) {
553 d->alphaU16Cs = d->colorSpace1(KoAlphaU16ColorSpace::colorSpaceId());
554 }
555 Q_ASSERT(d->alphaU16Cs);
556 return d->alphaU16Cs;
557}
558
559#ifdef HAVE_OPENEXR
560const KoColorSpace * KoColorSpaceRegistry::alpha16f()
561{
562 if (!d->alphaF16Cs) {
563 d->alphaF16Cs = d->colorSpace1(KoAlphaF16ColorSpace::colorSpaceId());
564 }
565 Q_ASSERT(d->alphaF16Cs);
566 return d->alphaF16Cs;
567}
568#endif
569
571{
572 if (!d->alphaF32Cs) {
573 d->alphaF32Cs = d->colorSpace1(KoAlphaF32ColorSpace::colorSpaceId());
574 }
575 Q_ASSERT(d->alphaF32Cs);
576 return d->alphaF32Cs;
577}
578
579const KoColorSpace *KoColorSpaceRegistry::graya8(const QString &profile)
580{
581
582 if (profile.isEmpty()) {
583 KoColorSpaceFactory* factory = d->colorSpaceFactoryRegistry.get(GrayAColorModelID.id());
585 }
586 else {
588 }
589
590}
591
593{
594 if (!profile) {
595 return graya8();
596 }
597 else {
599 }
600
601}
602
603const KoColorSpace *KoColorSpaceRegistry::graya16(const QString &profile)
604{
605 if (profile.isEmpty()) {
606 KoColorSpaceFactory* factory = d->colorSpaceFactoryRegistry.get(GrayAColorModelID.id());
608 }
609 else {
611 }
612
613}
614
616{
617 if (!profile) {
618 return graya16();
619 }
620 else {
622 }
623}
624
625
626const KoColorSpace * KoColorSpaceRegistry::rgb8(const QString &profileName)
627{
628 if (profileName.isEmpty()) {
629 if (!d->rgbU8sRGB) {
630 d->rgbU8sRGB = d->colorSpace1(KoRgbU8ColorSpace::colorSpaceId());
631 }
632 Q_ASSERT(d->rgbU8sRGB);
633 return d->rgbU8sRGB;
634 }
635 return d->colorSpace1(KoRgbU8ColorSpace::colorSpaceId(), profileName);
636}
637
639{
640 if (profile == 0) {
641 if (!d->rgbU8sRGB) {
642 d->rgbU8sRGB = d->colorSpace1(KoRgbU8ColorSpace::colorSpaceId());
643 }
644 Q_ASSERT(d->rgbU8sRGB);
645 return d->rgbU8sRGB;
646 }
647 return d->colorSpace1(KoRgbU8ColorSpace::colorSpaceId(), profile);
648}
649
650const KoColorSpace * KoColorSpaceRegistry::rgb16(const QString &profileName)
651{
652 return d->colorSpace1(KoRgbU16ColorSpace::colorSpaceId(), profileName);
653}
654
656{
657 return d->colorSpace1(KoRgbU16ColorSpace::colorSpaceId(), profile);
658}
659
660const KoColorSpace * KoColorSpaceRegistry::lab16(const QString &profileName)
661{
662 if (profileName.isEmpty()) {
663 if (!d->lab16sLAB) {
664 d->lab16sLAB = d->colorSpace1(KoLabColorSpace::colorSpaceId());
665 }
666 return d->lab16sLAB;
667 }
668 return d->colorSpace1(KoLabColorSpace::colorSpaceId(), profileName);
669}
670
672{
673 if (profile == 0) {
674 if (!d->lab16sLAB) {
675 d->lab16sLAB = d->colorSpace1(KoLabColorSpace::colorSpaceId());
676 }
677 Q_ASSERT(d->lab16sLAB);
678 return d->lab16sLAB;
679 }
680 return d->colorSpace1(KoLabColorSpace::colorSpaceId(), profile);
681}
682
684{
685 return profileByName("Rec2020-elle-V4-g10.icc");
686}
687
689{
690 return profileByName("Krita Rec. 2100 Perceptual Quantizer (80cd/m²)");
691}
692
694{
695 return profileByName("sRGB-elle-V2-g10.icc");
696}
697
699{
700 return profileByName("sRGB-elle-V2-srgbtrc.icc");
701}
702
703const KoColorProfile *KoColorSpaceRegistry::profileFor(const KoColorProfileQuery &query, const bool generate) const
704{
705 if (query.primaries == PRIMARIES_ITU_R_BT_709_5) {
706 if (query.transfer == TRC_IEC_61966_2_1) {
707 return p709SRGBProfile();
708 } else if (query.transfer == TRC_LINEAR) {
709 return p709G10Profile();
710 }
711 }
712
714 if (query.transfer == TRC_ITU_R_BT_2100_0_PQ) {
715 return p2020PQProfile();
716 } else if (query.transfer == TRC_LINEAR) {
717 return p2020G10Profile();
718 }
719 }
720
721 QList<const KoColorProfile*> list = d->profileStorage.profilesFor(query.primaries, query.transfer);
722 if (!list.empty()) {
723 return list.first();
724 }
725
727 if (engine && generate) {
728 return engine->getProfile(query);
729 }
730
731 return nullptr;
732}
733
734static QMap<QColorSpace::Primaries, ColorPrimaries> mapQColorSpaceColorPrimaries {
735 {QColorSpace::Primaries::SRgb, PRIMARIES_ITU_R_BT_709_5},
736 {QColorSpace::Primaries::AdobeRgb, PRIMARIES_ADOBE_RGB_1998},
737 {QColorSpace::Primaries::DciP3D65, PRIMARIES_SMPTE_EG_432_1},
738 {QColorSpace::Primaries::ProPhotoRgb, PRIMARIES_PROPHOTO},
739#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
740 {QColorSpace::Primaries::Bt2020, PRIMARIES_ITU_R_BT_2020_2_AND_2100_0},
741#endif
742 {QColorSpace::Primaries::Custom, PRIMARIES_UNSPECIFIED}
743};
744
745static QMap<QColorSpace::TransferFunction, TransferCharacteristics> mapQColorSpaceColorTransfer {
746 {QColorSpace::TransferFunction::Linear, TRC_LINEAR},
747 {QColorSpace::TransferFunction::SRgb, TRC_IEC_61966_2_1},
748 {QColorSpace::TransferFunction::ProPhotoRgb, TRC_PROPHOTO},
749#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
750 {QColorSpace::TransferFunction::Bt2020, TRC_ITU_R_BT_2020_2_12bit},
751 {QColorSpace::TransferFunction::St2084, TRC_ITU_R_BT_2100_0_PQ},
752 {QColorSpace::TransferFunction::Hlg, TRC_ITU_R_BT_2100_0_HLG},
753#endif
754 {QColorSpace::TransferFunction::Custom, TRC_UNSPECIFIED}
755};
756
758{
759 if (!space.isValid()) return p709SRGBProfile();
760 const KoColorProfile *profile = nullptr;
761#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
762 ColorPrimaries primaries = mapQColorSpaceColorPrimaries.value(space.primaries(), PRIMARIES_UNSPECIFIED);
763 TransferCharacteristics transfer = mapQColorSpaceColorTransfer.value(space.transferFunction(), TRC_UNSPECIFIED);
764 if (space.transferFunction() == QColorSpace::TransferFunction::Gamma || (transfer == TRC_UNSPECIFIED && primaries == PRIMARIES_UNSPECIFIED)) {
765 // load icc profile.
766 QByteArray profileData = space.iccProfile();
768 } else {
769 QVector<double> colorants;
770 profile = profileFor(KoColorProfileQuery(primaries, transfer));
771 }
772#else
773 // 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.
774
775 if (space.colorModel() == QColorSpace::ColorModel::Rgb) {
776 if (space.transferFunction() == QColorSpace::TransferFunction::Gamma) {
777 // load icc profile.
778 QByteArray profileData = space.iccProfile();
780 }
781 ColorPrimaries primaries = mapQColorSpaceColorPrimaries.value(space.primaries(), PRIMARIES_UNSPECIFIED);
782 TransferCharacteristics transfer = mapQColorSpaceColorTransfer.value(space.transferFunction(), TRC_UNSPECIFIED);
783 // In qt6.9 we can get the 'primary points'.
784 if (!profile && (primaries != PRIMARIES_UNSPECIFIED) && transfer != TRC_UNSPECIFIED) {
785 profile = profileFor(KoColorProfileQuery(primaries, transfer));
786 }
787 } else if (space.colorModel() == QColorSpace::ColorModel::Gray) {
788 TransferCharacteristics transfer = mapQColorSpaceColorTransfer.value(space.transferFunction(), TRC_UNSPECIFIED);
789 if (space.transferFunction() == QColorSpace::TransferFunction::Gamma) {
790 QByteArray profileData = space.iccProfile();
792
793 }
794 QVector<double> colorants;
796 query.whitePoint = {space.whitePoint().x(), space.whitePoint().y()};
797 if (!profile && query.isValid()) {
798 profile = profileFor(query);
799 }
800 }
801#endif
802 if (!profile) {
803 // We might now have a non-qt supported colormodel. Let's load the raw data directly.
805 if (engine) {
806 QByteArray profileData = space.iccProfile();
807 profile = engine->addProfile(profileData);
808 }
809 }
810 if (profile) {
811 const KoColorProfile *preExisting = profileByUniqueId(profile->uniqueId());
812 if (!preExisting) {
813 preExisting = profileByName(profile->name());
814 }
815 if (preExisting) {
816 return preExisting;
817 } else {
818 addProfile(profile);
819 }
820 }
821
822 return profile;
823}
824
826{
827 if (!profile) return QColorSpace(QColorSpace::SRgb);
828 if (profile == p709SRGBProfile()) {
829 return QColorSpace(QColorSpace::SRgb);
830 }
831#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
832 if (profile == p2020PQProfile()) {
833 return QColorSpace(QColorSpace::Bt2100Pq);
834 }
835#endif
836 return QColorSpace::fromIccProfile(profile->rawData());
837}
838
840{
841 QReadLocker l(&d->registrylock);
842
843 QList<KoID> ids;
844 QList<KoColorSpaceFactory*> factories = d->colorSpaceFactoryRegistry.values();
845 Q_FOREACH (KoColorSpaceFactory* factory, factories) {
846 if (!ids.contains(factory->colorModelId())
847 && (option == AllColorSpaces || factory->userVisible())) {
848 ids << factory->colorModelId();
849 }
850 }
851 return ids;
852}
854{
855 return colorDepthList(colorModelId.id(), option);
856}
857
858
860{
861 QReadLocker l(&d->registrylock);
862
863 QList<KoID> ids;
864 QList<KoColorSpaceFactory*> factories = d->colorSpaceFactoryRegistry.values();
865 Q_FOREACH (KoColorSpaceFactory* factory, factories) {
866 if (!ids.contains(KoID(factory->colorDepthId()))
867 && factory->colorModelId().id() == colorModelId
868 && (option == AllColorSpaces || factory->userVisible())) {
869 ids << factory->colorDepthId();
870 }
871 }
872 QList<KoID> r;
873
876 if (ids.contains(Float16BitsColorDepthID)) r << Float16BitsColorDepthID;
877 if (ids.contains(Float32BitsColorDepthID)) r << Float32BitsColorDepthID;
878 if (ids.contains(Float64BitsColorDepthID)) r << Float64BitsColorDepthID;
879
880 return r;
881}
882
883QString KoColorSpaceRegistry::Private::colorSpaceIdImpl(const QString & colorModelId, const QString & colorDepthId) const
884{
885 for (auto it = colorSpaceFactoryRegistry.constBegin(); it != colorSpaceFactoryRegistry.constEnd(); ++it) {
886 if (it.value()->colorModelId().id() == colorModelId && it.value()->colorDepthId().id() == colorDepthId) {
887 return it.value()->id();
888 }
889 }
890 return "";
891}
892
893QString KoColorSpaceRegistry::colorSpaceId(const QString & colorModelId, const QString & colorDepthId) const
894{
895 QReadLocker l(&d->registrylock);
896 return d->colorSpaceIdImpl(colorModelId, colorDepthId);
897}
898
899QString KoColorSpaceRegistry::colorSpaceId(const KoID& colorModelId, const KoID& colorDepthId) const
900{
901 return colorSpaceId(colorModelId.id(), colorDepthId.id());
902}
903
904KoID KoColorSpaceRegistry::colorSpaceColorModelId(const QString & _colorSpaceId) const
905{
906 QReadLocker l(&d->registrylock);
907
908 KoColorSpaceFactory* factory = d->colorSpaceFactoryRegistry.get(_colorSpaceId);
909 if (factory) {
910 return factory->colorModelId();
911 } else {
912 return KoID();
913 }
914}
915
916KoID KoColorSpaceRegistry::colorSpaceColorDepthId(const QString & _colorSpaceId) const
917{
918 QReadLocker l(&d->registrylock);
919
920 KoColorSpaceFactory* factory = d->colorSpaceFactoryRegistry.get(_colorSpaceId);
921 if (factory) {
922 return factory->colorDepthId();
923 } else {
924 return KoID();
925 }
926}
927
929{
930 return d->colorConversionSystem;
931}
932
934{
935 return d->colorConversionCache;
936}
937
939{
940 if (_colorSpace->d->deletability != NotOwnedByRegistry) {
941 return _colorSpace;
942 } else if (*_colorSpace == *d->alphaCs) {
943 return d->alphaCs;
944 } else {
945 const KoColorSpace* cs = d->colorSpace1(_colorSpace->id(), _colorSpace->profile());
946 Q_ASSERT(cs);
947 Q_ASSERT(*cs == *_colorSpace);
948 return cs;
949 }
950}
951
953{
954 QReadLocker l(&d->registrylock);
955 QList<KoID> answer;
956 Q_FOREACH (const QString& key, d->colorSpaceFactoryRegistry.keys()) {
957 answer.append(KoID(key, d->colorSpaceFactoryRegistry.get(key)->name()));
958 }
959
960 return answer;
961}
962
964{
965 ProfileRegistrationInterface(KoColorSpaceRegistry::Private *_d) : d(_d) {}
966
967 const KoColorProfile* profileByName(const QString &profileName) const override {
968 return d->profileStorage.profileByName(profileName);
969 }
970
971 void registerNewProfile(KoColorProfile *profile) override {
972 d->profileStorage.addProfile(profile);
973 d->colorConversionSystem->insertColorProfile(profile);
974 }
975
977};
978
979const KoColorProfile* KoColorSpaceRegistry::createColorProfile(const QString& colorModelId, const QString& colorDepthId, const QByteArray& rawData)
980{
981 return createColorProfile(colorModelId, colorDepthId, rawData, {});
982}
983
984const KoColorProfile* KoColorSpaceRegistry::createColorProfile(const QString & colorModelId, const QString & colorDepthId, const QByteArray& rawData, CustomProfileNameAlias customProfileNameAlias)
985{
986 QWriteLocker l(&d->registrylock);
987 KoColorSpaceFactory* factory_ = d->colorSpaceFactoryRegistry.get(d->colorSpaceIdImpl(colorModelId, colorDepthId));
988
989 Private::ProfileRegistrationInterface interface(d);
990 return factory_->colorProfile(rawData, &interface, customProfileNameAlias);
991}
992
994{
995 QList<const KoColorSpace*> colorSpaces;
996
997 // TODO: thread-unsafe code: the factories might change right after the lock in released
998 // HINT: used in a unittest only!
999
1000 d->registrylock.lockForRead();
1001 QList<KoColorSpaceFactory*> factories = d->colorSpaceFactoryRegistry.values();
1002 d->registrylock.unlock();
1003
1004 Q_FOREACH (KoColorSpaceFactory* factory, factories) {
1005 // Don't test with ycbcr for now, since we don't have a default profile for it.
1006 if (factory->colorModelId().id().startsWith("Y")) continue;
1007 if (visibility == AllColorSpaces || factory->userVisible()) {
1008 if (pSelection == OnlyDefaultProfile) {
1009 const KoColorSpace *cs = d->colorSpace1(factory->id());
1010 if (cs) {
1011 colorSpaces.append(cs);
1012 }
1013 else {
1014 warnPigment << "Could not create colorspace for id" << factory->id() << "since there is no working default profile";
1015 }
1016 } else {
1018 Q_FOREACH (const KoColorProfile * profile, profiles) {
1019 const KoColorSpace *cs = d->colorSpace1(factory->id(), profile);
1020 if (cs) {
1021 colorSpaces.append(cs);
1022 }
1023 else {
1024 warnPigment << "Could not create colorspace for id" << factory->id() << "and profile" << profile->name();
1025 }
1026 }
1027 }
1028 }
1029 }
1030
1031 return colorSpaces;
1032}
#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
The KoColorProfileQuery struct.
TransferCharacteristics transfer
CICP-compatible enum value representing the white point and primaries.
ColorPrimaries primaries
Rgb Primaries of the profile. When empty, this is a query for a greyscale profile.
bool isValid() const
CICP-compatible enum value representing the transfer function.
KoColorimetryUtils::xy whitePoint
virtual QByteArray rawData() const
virtual QByteArray uniqueId() const =0
virtual KoColorProfile * clone() const =0
virtual const KoColorProfile * getProfile(const KoColorProfileQuery &query)=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
virtual bool profileIsCompatible(const KoColorProfile *profile) const =0
const KoColorSpace * grabColorSpace(const KoColorProfile *profile)
const KoColorProfile * colorProfile(const QByteArray &rawData, ProfileRegistrationInterface *registrationInterface, const CustomProfileNameAlias &customProfileNameAlias) const
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
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())
QHash< QString, QString > CustomProfileNameAlias
const KoColorProfile * profileFor(const KoColorProfileQuery &query, const bool generate=true) const
profileFor tries to find the profile that matches these characteristics, if no such profile is found,...
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
void removeProfileAlias(const QString &name)
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.