diff --git a/libs/pigment/KoColorSpaceRegistry.cpp b/libs/pigment/KoColorSpaceRegistry.cpp index 8ba041bd60..d6f3e8404b 100644 --- a/libs/pigment/KoColorSpaceRegistry.cpp +++ b/libs/pigment/KoColorSpaceRegistry.cpp @@ -1,847 +1,893 @@ /* * Copyright (c) 2003 Patrick Julien * Copyright (c) 2004,2010 Cyrille Berger * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KoColorSpaceRegistry.h" #include #include #include #include #include #include "KoPluginLoader.h" #include "KoGenericRegistry.h" #include "DebugPigment.h" #include "KoBasicHistogramProducers.h" #include "KoColorSpace.h" #include "KoColorProfile.h" #include "KoColorConversionCache.h" #include "KoColorConversionSystem.h" #include "colorspaces/KoAlphaColorSpace.h" #include "colorspaces/KoLabColorSpace.h" #include "colorspaces/KoRgbU16ColorSpace.h" #include "colorspaces/KoRgbU8ColorSpace.h" #include "colorspaces/KoSimpleColorSpaceEngine.h" #include "KoColorSpace_p.h" #include "kis_assert.h" #include "KoColorProfileStorage.h" #include #include Q_GLOBAL_STATIC(KoColorSpaceRegistry, s_instance) struct Q_DECL_HIDDEN KoColorSpaceRegistry::Private { // interface for KoColorSpaceFactory struct ProfileRegistrationInterface; // interface for KoColorConversionSystem struct ConversionSystemInterface; Private(KoColorSpaceRegistry *_q) : q(_q) {} KoColorSpaceRegistry *q; KoGenericRegistry colorSpaceFactoryRegistry; KoColorProfileStorage profileStorage; QHash csMap; QScopedPointer conversionSystemInterface; KoColorConversionSystem *colorConversionSystem; KoColorConversionCache* colorConversionCache; const KoColorSpace *rgbU8sRGB; const KoColorSpace *lab16sLAB; const KoColorSpace *alphaCs; const KoColorSpace *alphaU16Cs; #ifdef HAVE_OPENEXR const KoColorSpace *alphaF16Cs; #endif const KoColorSpace *alphaF32Cs; QReadWriteLock registrylock; /** * The function checks if a colorspace with a certain id and profile name can be found in the cache * NOTE: the function doesn't take any lock but it needs to be called inside a d->registryLock * locked either in read or write. * @param csId The colorspace id * @param profileName The colorspace profile name * @retval KoColorSpace The matching colorspace * @retval 0 Null pointer if not match */ const KoColorSpace* getCachedColorSpaceImpl(const QString & csId, const QString & profileName) const; QString idsToCacheName(const QString & csId, const QString & profileName) const; QString defaultProfileForCsIdImpl(const QString &csID); const KoColorProfile * profileForCsIdWithFallbackImpl(const QString &csID, const QString &profileName); QString colorSpaceIdImpl(const QString & colorModelId, const QString & colorDepthId) const; const KoColorSpace *lazyCreateColorSpaceImpl(const QString &csID, const KoColorProfile *profile); /** * Return a colorspace that works with the parameter profile. * @param profileName the name of the KoColorProfile to be combined with the colorspace * @return the wanted colorspace, or 0 when the cs and profile can not be combined. */ template const KoColorSpace * colorSpace1(const QString &colorSpaceId, const QString &pName = QString()); /** * Return a colorspace that works with the parameter profile. * @param colorSpaceId the ID string of the colorspace that you want to have returned * @param profile the profile be combined with the colorspace * @return the wanted colorspace, or 0 when the cs and profile can not be combined. */ const KoColorSpace * colorSpace1(const QString &colorSpaceId, const KoColorProfile *profile); }; struct KoColorSpaceRegistry::Private::ConversionSystemInterface : public KoColorConversionSystem::RegistryInterface { ConversionSystemInterface(KoColorSpaceRegistry *parentRegistry) : q(parentRegistry) { } const KoColorSpace * colorSpace(const QString & colorModelId, const QString & colorDepthId, const QString &profileName) { return q->d->colorSpace1(q->d->colorSpaceIdImpl(colorModelId, colorDepthId), profileName); } const KoColorSpaceFactory* colorSpaceFactory(const QString &colorModelId, const QString &colorDepthId) const { return q->d->colorSpaceFactoryRegistry.get(q->d->colorSpaceIdImpl(colorModelId, colorDepthId)); } QList profilesFor(const KoColorSpaceFactory * csf) const { return q->d->profileStorage.profilesFor(csf); } QList colorSpacesFor(const KoColorProfile* profile) const { QList csfs; Q_FOREACH (KoColorSpaceFactory* csf, q->d->colorSpaceFactoryRegistry.values()) { if (csf->profileIsCompatible(profile)) { csfs.push_back(csf); } } return csfs; } private: KoColorSpaceRegistry *q; }; KoColorSpaceRegistry* KoColorSpaceRegistry::instance() { if (!s_instance.exists()) { s_instance->init(); } return s_instance; } void KoColorSpaceRegistry::init() { d->rgbU8sRGB = 0; d->lab16sLAB = 0; d->alphaCs = 0; d->alphaU16Cs = 0; #ifdef HAVE_OPENEXR d->alphaF16Cs = 0; #endif d->alphaF32Cs = 0; d->conversionSystemInterface.reset(new Private::ConversionSystemInterface(this)); d->colorConversionSystem = new KoColorConversionSystem(d->conversionSystemInterface.data()); d->colorConversionCache = new KoColorConversionCache; KoColorSpaceEngineRegistry::instance()->add(new KoSimpleColorSpaceEngine()); addProfile(new KoDummyColorProfile); // Create the built-in colorspaces QList localFactories; localFactories << new KoAlphaColorSpaceFactory() << new KoAlphaU16ColorSpaceFactory() #ifdef HAVE_OPENEXR << new KoAlphaF16ColorSpaceFactory() #endif << new KoAlphaF32ColorSpaceFactory() << new KoLabColorSpaceFactory() << new KoRgbU8ColorSpaceFactory() << new KoRgbU16ColorSpaceFactory(); Q_FOREACH (KoColorSpaceFactory *factory, localFactories) { add(factory); } KoPluginLoader::PluginsConfig config; config.whiteList = "ColorSpacePlugins"; config.blacklist = "ColorSpacePluginsDisabled"; config.group = "calligra"; KoPluginLoader::instance()->load("Calligra/ColorSpace", "[X-Pigment-PluginVersion] == 28", config); KoPluginLoader::PluginsConfig configExtensions; configExtensions.whiteList = "ColorSpaceExtensionsPlugins"; configExtensions.blacklist = "ColorSpaceExtensionsPluginsDisabled"; configExtensions.group = "calligra"; KoPluginLoader::instance()->load("Calligra/ColorSpaceExtension", "[X-Pigment-PluginVersion] == 28", configExtensions); dbgPigment << "Loaded the following colorspaces:"; Q_FOREACH (const KoID& id, listKeys()) { dbgPigment << "\t" << id.id() << "," << id.name(); } } KoColorSpaceRegistry::KoColorSpaceRegistry() : d(new Private(this)) { d->colorConversionSystem = 0; d->colorConversionCache = 0; } KoColorSpaceRegistry::~KoColorSpaceRegistry() { // Just leak on exit... It's faster. // delete d->colorConversionSystem; // Q_FOREACH (KoColorProfile* profile, d->profileMap) { // delete profile; // } // d->profileMap.clear(); // Q_FOREACH (const KoColorSpace * cs, d->csMap) { // cs->d->deletability = OwnedByRegistryRegistryDeletes; // } // d->csMap.clear(); // // deleting colorspaces calls a function in the cache // delete d->colorConversionCache; // d->colorConversionCache = 0; // // Delete the colorspace factories // qDeleteAll(d->localFactories); delete d; } void KoColorSpaceRegistry::add(KoColorSpaceFactory* item) { QWriteLocker l(&d->registrylock); d->colorSpaceFactoryRegistry.add(item); d->colorConversionSystem->insertColorSpace(item); } void KoColorSpaceRegistry::remove(KoColorSpaceFactory* item) { QWriteLocker l(&d->registrylock); QList toremove; Q_FOREACH (const KoColorSpace * cs, d->csMap) { if (cs->id() == item->id()) { toremove.push_back(d->idsToCacheName(cs->id(), cs->profile()->name())); cs->d->deletability = OwnedByRegistryRegistryDeletes; } } Q_FOREACH (const QString& id, toremove) { d->csMap.remove(id); // TODO: should not it delete the color space when removing it from the map ? } d->colorSpaceFactoryRegistry.remove(item->id()); } void KoColorSpaceRegistry::addProfileAlias(const QString& name, const QString& to) { d->profileStorage.addProfileAlias(name, to); } QString KoColorSpaceRegistry::profileAlias(const QString& name) const { return d->profileStorage.profileAlias(name); } const KoColorProfile* KoColorSpaceRegistry::profileByName(const QString &name) const { return d->profileStorage.profileByName(name); } const KoColorProfile * KoColorSpaceRegistry::profileByUniqueId(const QByteArray &id) const { return d->profileStorage.profileByUniqueId(id); } QList KoColorSpaceRegistry::profilesFor(const QString &csID) const { QReadLocker l(&d->registrylock); return d->profileStorage.profilesFor(d->colorSpaceFactoryRegistry.value(csID)); } const KoColorSpace * KoColorSpaceRegistry::colorSpace(const QString & colorModelId, const QString & colorDepthId, const KoColorProfile *profile) { return d->colorSpace1(colorSpaceId(colorModelId, colorDepthId), profile); } const KoColorSpace * KoColorSpaceRegistry::colorSpace(const QString & colorModelId, const QString & colorDepthId, const QString &profileName) { return d->colorSpace1(colorSpaceId(colorModelId, colorDepthId), profileName); } const KoColorSpace * KoColorSpaceRegistry::colorSpace(const QString & colorModelId, const QString & colorDepthId) { return d->colorSpace1(colorSpaceId(colorModelId, colorDepthId)); } bool KoColorSpaceRegistry::profileIsCompatible(const KoColorProfile *profile, const QString &colorSpaceId) { QReadLocker l(&d->registrylock); KoColorSpaceFactory *csf = d->colorSpaceFactoryRegistry.value(colorSpaceId); return csf ? csf->profileIsCompatible(profile) : false; } void KoColorSpaceRegistry::addProfileToMap(KoColorProfile *p) { d->profileStorage.addProfile(p); } void KoColorSpaceRegistry::addProfile(KoColorProfile *p) { if (!p->valid()) return; QWriteLocker locker(&d->registrylock); if (p->valid()) { addProfileToMap(p); d->colorConversionSystem->insertColorProfile(p); } } void KoColorSpaceRegistry::addProfile(const KoColorProfile* profile) { addProfile(profile->clone()); } void KoColorSpaceRegistry::removeProfile(KoColorProfile* profile) { d->profileStorage.removeProfile(profile); // FIXME: how about removing it from conversion system? } const KoColorSpace* KoColorSpaceRegistry::Private::getCachedColorSpaceImpl(const QString & csID, const QString & profileName) const { auto it = csMap.find(idsToCacheName(csID, profileName)); if (it != csMap.end()) { return it.value(); } return 0; } QString KoColorSpaceRegistry::Private::idsToCacheName(const QString & csID, const QString & profileName) const { return csID + "" + profileName; } QString KoColorSpaceRegistry::defaultProfileForColorSpace(const QString &colorSpaceId) const { QReadLocker l(&d->registrylock); return d->defaultProfileForCsIdImpl(colorSpaceId); } KoColorConversionTransformation *KoColorSpaceRegistry::createColorConverter(const KoColorSpace *srcColorSpace, const KoColorSpace *dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const { QWriteLocker l(&d->registrylock); return d->colorConversionSystem->createColorConverter(srcColorSpace, dstColorSpace, renderingIntent, conversionFlags); } void KoColorSpaceRegistry::createColorConverters(const KoColorSpace *colorSpace, const QList > &possibilities, KoColorConversionTransformation *&fromCS, KoColorConversionTransformation *&toCS) const { QWriteLocker l(&d->registrylock); d->colorConversionSystem->createColorConverters(colorSpace, possibilities, fromCS, toCS); } QString KoColorSpaceRegistry::Private::defaultProfileForCsIdImpl(const QString &csID) { QString defaultProfileName; KoColorSpaceFactory *csf = colorSpaceFactoryRegistry.value(csID); if (csf) { defaultProfileName = csf->defaultProfile(); } else { dbgPigmentCSRegistry << "Unknown color space type : " << csID; } return defaultProfileName; } const KoColorProfile *KoColorSpaceRegistry::Private::profileForCsIdWithFallbackImpl(const QString &csID, const QString &profileName) { const KoColorProfile *profile = 0; // last attempt at getting a profile, sometimes the default profile, like adobe cmyk isn't available. profile = profileStorage.profileByName(profileName); if (!profile) { dbgPigmentCSRegistry << "Profile not found :" << profileName; // first try: default profile = profileStorage.profileByName(defaultProfileForCsIdImpl(csID)); if (!profile) { // second try: first profile in the list QList profiles = profileStorage.profilesFor(colorSpaceFactoryRegistry.value(csID)); if (profiles.isEmpty() || !profiles.first()) { dbgPigmentCSRegistry << "Couldn't fetch a fallback profile:" << profileName; qWarning() << "profileForCsIdWithFallbackImpl couldn't fetch a fallback profile for " << qUtf8Printable(profileName); return 0; } profile = profiles.first(); } } return profile; } const KoColorSpace *KoColorSpaceRegistry::Private::lazyCreateColorSpaceImpl(const QString &csID, const KoColorProfile *profile) { const KoColorSpace *cs = 0; /* * We need to check again here, a thread requesting the same colorspace could've added it * already, in between the read unlock and write lock. * TODO: We also potentially changed profileName content, which means we maybe are going to * create a colorspace that's actually in the space registry cache, but currently this might * not be an issue because the colorspace should be cached also by the factory, so it won't * create a new instance. That being said, having two caches with the same stuff doesn't make * much sense. */ cs = getCachedColorSpaceImpl(csID, profile->name()); if (!cs) { KoColorSpaceFactory *csf = colorSpaceFactoryRegistry.value(csID); cs = csf->grabColorSpace(profile); if (!cs) { dbgPigmentCSRegistry << "Unable to create color space"; qWarning() << "lazyCreateColorSpaceImpl was unable to create a color space for " << csID; return 0; } dbgPigmentCSRegistry << "colorspace count: " << csMap.count() << ", adding name: " << idsToCacheName(cs->id(), cs->profile()->name()) << "\n\tcsID" << csID << "\n\tcs->id()" << cs->id() << "\n\tcs->profile()->name()" << cs->profile()->name() << "\n\tprofile->name()" << profile->name(); Q_ASSERT(cs->id() == csID); Q_ASSERT(cs->profile()->name() == profile->name()); csMap[idsToCacheName(cs->id(), cs->profile()->name())] = cs; cs->d->deletability = OwnedByRegistryDoNotDelete; } return cs; } template const KoColorSpace * KoColorSpaceRegistry::Private::colorSpace1(const QString &csID, const QString &pName) { QString profileName = pName; const KoColorSpace *cs = 0; { typename LockPolicy::ReadLocker l(®istrylock); if (profileName.isEmpty()) { profileName = defaultProfileForCsIdImpl(csID); if (profileName.isEmpty()) return 0; } // quick attempt to fetch a cached color space cs = getCachedColorSpaceImpl(csID, profileName); } if (!cs) { // slow attempt to create a color space typename LockPolicy::WriteLocker l(®istrylock); const KoColorProfile *profile = profileForCsIdWithFallbackImpl(csID, profileName); if (!profile) return 0; cs = lazyCreateColorSpaceImpl(csID, profile); } else { KIS_SAFE_ASSERT_RECOVER_NOOP(cs->id() == csID); KIS_SAFE_ASSERT_RECOVER_NOOP(cs->profile()->name() == profileName); } return cs; } const KoColorSpace * KoColorSpaceRegistry::Private::colorSpace1(const QString &csID, const KoColorProfile *profile) { if (csID.isEmpty()) { return 0; } else if (!profile) { return colorSpace1(csID); } const KoColorSpace *cs = 0; { QReadLocker l(®istrylock); cs = getCachedColorSpaceImpl(csID, profile->name()); } // the profile should have already been added to the registry by createColorProfile() method KIS_SAFE_ASSERT_RECOVER(profileStorage.containsProfile(profile)) { // warning! locking happens inside addProfile! q->addProfile(profile); } if (!cs) { // The profile was not stored and thus not the combination either QWriteLocker l(®istrylock); KoColorSpaceFactory *csf = colorSpaceFactoryRegistry.value(csID); if (!csf) { dbgPigmentCSRegistry << "Unknown color space type :" << csf; return 0; } if (!csf->profileIsCompatible(profile)) { dbgPigmentCSRegistry << "Profile is not compatible:" << csf << profile->name(); return 0; } cs = lazyCreateColorSpaceImpl(csID, profile); } return cs; } const KoColorSpace * KoColorSpaceRegistry::alpha8() { if (!d->alphaCs) { d->alphaCs = d->colorSpace1(KoAlphaColorSpace::colorSpaceId()); } Q_ASSERT(d->alphaCs); return d->alphaCs; } const KoColorSpace * KoColorSpaceRegistry::alpha16() { if (!d->alphaU16Cs) { d->alphaU16Cs = d->colorSpace1(KoAlphaU16ColorSpace::colorSpaceId()); } Q_ASSERT(d->alphaU16Cs); return d->alphaU16Cs; } #ifdef HAVE_OPENEXR const KoColorSpace * KoColorSpaceRegistry::alpha16f() { if (!d->alphaF16Cs) { d->alphaF16Cs = d->colorSpace1(KoAlphaF16ColorSpace::colorSpaceId()); } Q_ASSERT(d->alphaF16Cs); return d->alphaF16Cs; } #endif const KoColorSpace * KoColorSpaceRegistry::alpha32f() { if (!d->alphaF32Cs) { d->alphaF32Cs = d->colorSpace1(KoAlphaF32ColorSpace::colorSpaceId()); } Q_ASSERT(d->alphaF32Cs); return d->alphaF32Cs; } +const KoColorSpace *KoColorSpaceRegistry::graya8(const QString &profile) +{ + + if (profile.isEmpty()) { + KoColorSpaceFactory* factory = d->colorSpaceFactoryRegistry.get(GrayAColorModelID.id()); + return KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Integer8BitsColorDepthID.id(), factory->defaultProfile()); + } + else { + return KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Integer8BitsColorDepthID.id(), profile); + } + +} + +const KoColorSpace *KoColorSpaceRegistry::graya8(const KoColorProfile *profile) +{ + if (!profile) { + return graya8(); + } + else { + return KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Integer8BitsColorDepthID.id(), profile); + } + +} + +const KoColorSpace *KoColorSpaceRegistry::graya16(const QString &profile) +{ + if (profile.isEmpty()) { + KoColorSpaceFactory* factory = d->colorSpaceFactoryRegistry.get(GrayAColorModelID.id()); + return KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Integer16BitsColorDepthID.id(), factory->defaultProfile()); + } + else { + return KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Integer16BitsColorDepthID.id(), profile); + } + +} + +const KoColorSpace *KoColorSpaceRegistry::graya16(const KoColorProfile *profile) +{ + if (!profile) { + return graya8(); + } + else { + return KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Integer8BitsColorDepthID.id(), profile); + } +} + const KoColorSpace * KoColorSpaceRegistry::rgb8(const QString &profileName) { if (profileName.isEmpty()) { if (!d->rgbU8sRGB) { d->rgbU8sRGB = d->colorSpace1(KoRgbU8ColorSpace::colorSpaceId()); } Q_ASSERT(d->rgbU8sRGB); return d->rgbU8sRGB; } return d->colorSpace1(KoRgbU8ColorSpace::colorSpaceId(), profileName); } const KoColorSpace * KoColorSpaceRegistry::rgb8(const KoColorProfile * profile) { if (profile == 0) { if (!d->rgbU8sRGB) { d->rgbU8sRGB = d->colorSpace1(KoRgbU8ColorSpace::colorSpaceId()); } Q_ASSERT(d->rgbU8sRGB); return d->rgbU8sRGB; } return d->colorSpace1(KoRgbU8ColorSpace::colorSpaceId(), profile); } const KoColorSpace * KoColorSpaceRegistry::rgb16(const QString &profileName) { return d->colorSpace1(KoRgbU16ColorSpace::colorSpaceId(), profileName); } const KoColorSpace * KoColorSpaceRegistry::rgb16(const KoColorProfile * profile) { return d->colorSpace1(KoRgbU16ColorSpace::colorSpaceId(), profile); } const KoColorSpace * KoColorSpaceRegistry::lab16(const QString &profileName) { if (profileName.isEmpty()) { if (!d->lab16sLAB) { d->lab16sLAB = d->colorSpace1(KoLabColorSpace::colorSpaceId()); } return d->lab16sLAB; } return d->colorSpace1(KoLabColorSpace::colorSpaceId(), profileName); } const KoColorSpace * KoColorSpaceRegistry::lab16(const KoColorProfile * profile) { if (profile == 0) { if (!d->lab16sLAB) { d->lab16sLAB = d->colorSpace1(KoLabColorSpace::colorSpaceId()); } Q_ASSERT(d->lab16sLAB); return d->lab16sLAB; } return d->colorSpace1(KoLabColorSpace::colorSpaceId(), profile); } const KoColorProfile *KoColorSpaceRegistry::p2020G10Profile() const { return profileByName("Rec2020-elle-V4-g10.icc"); } const KoColorProfile *KoColorSpaceRegistry::p2020PQProfile() const { return profileByName("High Dynamic Range UHDTV Wide Color Gamut Display (Rec. 2020) - SMPTE ST 2084 PQ EOTF"); } const KoColorProfile *KoColorSpaceRegistry::p709G10Profile() const { return profileByName("sRGB-elle-V2-g10.icc"); } const KoColorProfile *KoColorSpaceRegistry::p709SRGBProfile() const { return profileByName("sRGB-elle-V2-srgbtrc.icc"); } QList KoColorSpaceRegistry::colorModelsList(ColorSpaceListVisibility option) const { QReadLocker l(&d->registrylock); QList ids; QList factories = d->colorSpaceFactoryRegistry.values(); Q_FOREACH (KoColorSpaceFactory* factory, factories) { if (!ids.contains(factory->colorModelId()) && (option == AllColorSpaces || factory->userVisible())) { ids << factory->colorModelId(); } } return ids; } QList KoColorSpaceRegistry::colorDepthList(const KoID& colorModelId, ColorSpaceListVisibility option) const { return colorDepthList(colorModelId.id(), option); } QList KoColorSpaceRegistry::colorDepthList(const QString & colorModelId, ColorSpaceListVisibility option) const { QReadLocker l(&d->registrylock); QList ids; QList factories = d->colorSpaceFactoryRegistry.values(); Q_FOREACH (KoColorSpaceFactory* factory, factories) { if (!ids.contains(KoID(factory->colorDepthId())) && factory->colorModelId().id() == colorModelId && (option == AllColorSpaces || factory->userVisible())) { ids << factory->colorDepthId(); } } QList r; if (ids.contains(Integer8BitsColorDepthID)) r << Integer8BitsColorDepthID; if (ids.contains(Integer16BitsColorDepthID)) r << Integer16BitsColorDepthID; if (ids.contains(Float16BitsColorDepthID)) r << Float16BitsColorDepthID; if (ids.contains(Float32BitsColorDepthID)) r << Float32BitsColorDepthID; if (ids.contains(Float64BitsColorDepthID)) r << Float64BitsColorDepthID; return r; } QString KoColorSpaceRegistry::Private::colorSpaceIdImpl(const QString & colorModelId, const QString & colorDepthId) const { QList factories = colorSpaceFactoryRegistry.values(); Q_FOREACH (KoColorSpaceFactory* factory, factories) { if (factory->colorModelId().id() == colorModelId && factory->colorDepthId().id() == colorDepthId) { return factory->id(); } } return ""; } QString KoColorSpaceRegistry::colorSpaceId(const QString & colorModelId, const QString & colorDepthId) const { QReadLocker l(&d->registrylock); return d->colorSpaceIdImpl(colorModelId, colorDepthId); } QString KoColorSpaceRegistry::colorSpaceId(const KoID& colorModelId, const KoID& colorDepthId) const { return colorSpaceId(colorModelId.id(), colorDepthId.id()); } KoID KoColorSpaceRegistry::colorSpaceColorModelId(const QString & _colorSpaceId) const { QReadLocker l(&d->registrylock); KoColorSpaceFactory* factory = d->colorSpaceFactoryRegistry.get(_colorSpaceId); if (factory) { return factory->colorModelId(); } else { return KoID(); } } KoID KoColorSpaceRegistry::colorSpaceColorDepthId(const QString & _colorSpaceId) const { QReadLocker l(&d->registrylock); KoColorSpaceFactory* factory = d->colorSpaceFactoryRegistry.get(_colorSpaceId); if (factory) { return factory->colorDepthId(); } else { return KoID(); } } const KoColorConversionSystem* KoColorSpaceRegistry::colorConversionSystem() const { return d->colorConversionSystem; } KoColorConversionCache* KoColorSpaceRegistry::colorConversionCache() const { return d->colorConversionCache; } const KoColorSpace* KoColorSpaceRegistry::permanentColorspace(const KoColorSpace* _colorSpace) { if (_colorSpace->d->deletability != NotOwnedByRegistry) { return _colorSpace; } else if (*_colorSpace == *d->alphaCs) { return d->alphaCs; } else { const KoColorSpace* cs = d->colorSpace1(_colorSpace->id(), _colorSpace->profile()); Q_ASSERT(cs); Q_ASSERT(*cs == *_colorSpace); return cs; } } QList KoColorSpaceRegistry::listKeys() const { QReadLocker l(&d->registrylock); QList answer; Q_FOREACH (const QString& key, d->colorSpaceFactoryRegistry.keys()) { answer.append(KoID(key, d->colorSpaceFactoryRegistry.get(key)->name())); } return answer; } struct KoColorSpaceRegistry::Private::ProfileRegistrationInterface : public KoColorSpaceFactory::ProfileRegistrationInterface { ProfileRegistrationInterface(KoColorSpaceRegistry::Private *_d) : d(_d) {} const KoColorProfile* profileByName(const QString &profileName) const override { return d->profileStorage.profileByName(profileName); } void registerNewProfile(KoColorProfile *profile) override { d->profileStorage.addProfile(profile); d->colorConversionSystem->insertColorProfile(profile); } KoColorSpaceRegistry::Private *d; }; const KoColorProfile* KoColorSpaceRegistry::createColorProfile(const QString& colorModelId, const QString& colorDepthId, const QByteArray& rawData) { QWriteLocker l(&d->registrylock); KoColorSpaceFactory* factory_ = d->colorSpaceFactoryRegistry.get(d->colorSpaceIdImpl(colorModelId, colorDepthId)); Private::ProfileRegistrationInterface interface(d); return factory_->colorProfile(rawData, &interface); } QList KoColorSpaceRegistry::allColorSpaces(ColorSpaceListVisibility visibility, ColorSpaceListProfilesSelection pSelection) { QList colorSpaces; // TODO: thread-unsafe code: the factories might change right after the lock in released // HINT: used in a unittest only! d->registrylock.lockForRead(); QList factories = d->colorSpaceFactoryRegistry.values(); d->registrylock.unlock(); Q_FOREACH (KoColorSpaceFactory* factory, factories) { // Don't test with ycbcr for now, since we don't have a default profile for it. if (factory->colorModelId().id().startsWith("Y")) continue; if (visibility == AllColorSpaces || factory->userVisible()) { if (pSelection == OnlyDefaultProfile) { const KoColorSpace *cs = d->colorSpace1(factory->id()); if (cs) { colorSpaces.append(cs); } else { warnPigment << "Could not create colorspace for id" << factory->id() << "since there is no working default profile"; } } else { QList profiles = KoColorSpaceRegistry::instance()->profilesFor(factory->id()); Q_FOREACH (const KoColorProfile * profile, profiles) { const KoColorSpace *cs = d->colorSpace1(factory->id(), profile); if (cs) { colorSpaces.append(cs); } else { warnPigment << "Could not create colorspace for id" << factory->id() << "and profile" << profile->name(); } } } } } return colorSpaces; } diff --git a/libs/pigment/KoColorSpaceRegistry.h b/libs/pigment/KoColorSpaceRegistry.h index 0f4b71d0bb..8a16c5a091 100644 --- a/libs/pigment/KoColorSpaceRegistry.h +++ b/libs/pigment/KoColorSpaceRegistry.h @@ -1,384 +1,417 @@ /* * Copyright (c) 2003 Patrick Julien * Copyright (c) 2004,2010 Cyrille Berger * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KOCOLORSPACEREGISTRY_H #define KOCOLORSPACEREGISTRY_H #include #include #include #include "kritapigment_export.h" #include #include #include #include class KoColorProfile; class KoColorConversionSystem; class KoColorConversionCache; class KoColorConversionTransformation; /** * The registry for colorspaces and profiles. * This class contains: * - a registry of colorspace instantiated with specific profiles. * - a registry of singleton colorspace factories. * - a registry of icc profiles * * Locking policy details: * * Basically, we have two levels of locks in the registry: * 1) (outer level) is Private::registrylock, which controls the structures * of the color space registry itself * 2) (inner level) is KoColorProfileStorage::Private::lock controls * the structures related to profiles. * * The locks can be taken individually, but if you are going to take both * of them, you should always follow the order 1) registry; 2) profiles. * Otherwise you'll get a deadlock. * * To avoid recursive deadlocks, all the dependent classes * (KoColorConversionSystem and KoColorSpaceFactory) now do not use the direct * links to the registry. Instead, they use special private interfaces that * skip recursive locking and ensure we take a lock twice. */ class KRITAPIGMENT_EXPORT KoColorSpaceRegistry { public: KoColorSpaceRegistry(); enum ColorSpaceListVisibility { OnlyUserVisible = 1, ///< Only user visible color space AllColorSpaces = 4 ///< All color space even those not visible to the user }; enum ColorSpaceListProfilesSelection { OnlyDefaultProfile = 1, ///< Only add the default profile AllProfiles = 4 ///< Add all profiles }; /** * Return an instance of the KoColorSpaceRegistry * Creates an instance if that has never happened before and returns the singleton instance. */ static KoColorSpaceRegistry * instance(); virtual ~KoColorSpaceRegistry(); public: /** * add a color space to the registry * @param item the color space factory to add */ void add(KoColorSpaceFactory* item); /** * Remove a color space factory from the registry. Note that it is the * responsibility of the caller to ensure that the colorspaces are not * used anymore. */ void remove(KoColorSpaceFactory* item); /** * Add a profile to the profile map but do not add it to the * color conversion system yet. * @param profile the new profile to be registered. */ void addProfileToMap(KoColorProfile *p); /** * register the profile with the color space registry * @param profile the new profile to be registered so it can be combined with * colorspaces. */ void addProfile(KoColorProfile* profile); void addProfile(const KoColorProfile* profile); // TODO why ? void removeProfile(KoColorProfile* profile); /** * Create an alias to a profile with a different name. Then @ref profileByName * will return the profile @p to when passed @p name as a parameter. */ void addProfileAlias(const QString& name, const QString& to); /** * @return the profile alias, or name if not aliased */ QString profileAlias(const QString& name) const; /** * create a profile of the specified type. */ const KoColorProfile *createColorProfile(const QString & colorModelId, const QString & colorDepthId, const QByteArray& rawData); /** * Return a profile by its given name, or 0 if none registered. * @return a profile by its given name, or 0 if none registered. * @param name the product name as set on the profile. * @see addProfile() * @see KoColorProfile::productName() */ const KoColorProfile * profileByName(const QString & name) const ; /** * Returns a profile by its unique id stored/calculated in the header. * The first call to this function might take long, because the map is * created on the first use only (atm used by SVG only) * @param id unique ProfileID of the profile (MD5 sum of its header) * @return the profile or 0 if not found */ const KoColorProfile *profileByUniqueId(const QByteArray &id) const; bool profileIsCompatible(const KoColorProfile* profile, const QString &colorSpaceId); /** * Return the list of profiles for a colorspace with the argument id. * Profiles will not work with any color space, you can query which profiles * that are registered with this registry can be used in combination with the * argument factory. * @param colorSpaceId the colorspace-id with which all the returned profiles will work. * @return a list of profiles for the factory */ QList profilesFor(const QString& csID) const; QString defaultProfileForColorSpace(const QString &colorSpaceId) const; /** * This function is called by the color space to create a color conversion * between two color space. This function search in the graph of transformations * the best possible path between the two color space. */ KoColorConversionTransformation* createColorConverter(const KoColorSpace * srcColorSpace, const KoColorSpace * dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const; /** * This function creates two transformations, one from the color space and one to the * color space. The destination color space is picked from a list of color space, such * as the conversion between the two color space is of the best quality. * * The typical use case of this function is for KoColorTransformationFactory which * doesn't support all color spaces, so unsupported color space have to find an * acceptable conversion in order to use that KoColorTransformationFactory. * * @param colorSpace the source color space * @param possibilities a list of color space among which we need to find the best * conversion * @param fromCS the conversion from the source color space will be affected to this * variable * @param toCS the revert conversion to the source color space will be affected to this * variable */ void createColorConverters(const KoColorSpace* colorSpace, const QList< QPair >& possibilities, KoColorConversionTransformation*& fromCS, KoColorConversionTransformation*& toCS) const; /** * Return a colorspace that works with the parameter profile. * @param colorSpaceId the ID string of the colorspace that you want to have returned * @param profile the profile be combined with the colorspace * @return the wanted colorspace, or 0 when the cs and profile can not be combined. */ const KoColorSpace * colorSpace(const QString & colorModelId, const QString & colorDepthId, const KoColorProfile *profile); /** * Return a colorspace that works with the parameter profile. * @param profileName the name of the KoColorProfile to be combined with the colorspace * @return the wanted colorspace, or 0 when the cs and profile can not be combined. */ const KoColorSpace * colorSpace(const QString & colorModelId, const QString & colorDepthId, const QString &profileName); const KoColorSpace * colorSpace(const QString & colorModelId, const QString & colorDepthId); /** * Return the id of the colorspace that have the defined colorModelId with colorDepthId. * @param colorModelId id of the color model * @param colorDepthId id of the color depth * @return the id of the wanted colorspace, or "" if no colorspace correspond to those ids */ QString colorSpaceId(const QString & colorModelId, const QString & colorDepthId) const; /** * It's a convenient function that behave like the above. * Return the id of the colorspace that have the defined colorModelId with colorDepthId. * @param colorModelId id of the color model * @param colorDepthId id of the color depth * @return the id of the wanted colorspace, or "" if no colorspace correspond to those ids */ QString colorSpaceId(const KoID& colorModelId, const KoID& colorDepthId) const; /** * @return the identifier of the color model for the given color space id. * * This function is a compatibility function used to get the color space from * all kra files. */ KoID colorSpaceColorModelId(const QString & _colorSpaceId) const; /** * @return the identifier of the color depth for the given color space id. * * This function is a compatibility function used to get the color space from * all kra files. */ KoID colorSpaceColorDepthId(const QString & _colorSpaceId) const; /** * Convenience methods to get the often used alpha colorspaces */ const KoColorSpace *alpha8(); const KoColorSpace *alpha16(); #ifdef HAVE_OPENEXR const KoColorSpace *alpha16f(); #endif const KoColorSpace *alpha32f(); + /** + * Convenience method to get a GRAYA 8 bit colorspace. If a profile is not specified, + * the default profile defined in the GRAY colorspace will be used + * @param profile the profile name + * @return an 8 bit graya colorspace with the default profile or 0. + */ + const KoColorSpace *graya8(const QString &profile = QString()); + + /** + * Convenience method to get a GRAYA 8 bit colorspace. If a profile is not specified, + * the default profile defined in the GRAY colorspace will be used + * @param profile the profile + * @return an 8 bit graya colorspace with the default profile or 0. + */ + const KoColorSpace *graya8(const KoColorProfile *profile); + + /** + * Convenience method to get a GRAYA 16 bit colorspace. If a profile is not specified, + * the default profile defined in the GRAY colorspace will be used + * @param the profile + * @return an 8 bit graya colorspace with the default profile or 0. + */ + const KoColorSpace *graya16(const QString &profile = QString()); + + /** + * Convenience method to get a GRAYA 16 bit colorspace. If a profile is not specified, + * the default profile defined in the GRAY colorspace will be used + * @param the profile + * @return an 8 bit graya colorspace with the default profile or 0. + */ + const KoColorSpace *graya16(const KoColorProfile *profile); + + /** * Convenience method to get an RGBA 8bit colorspace. If a profile is not specified, * an sRGB profile will be used. * @param profileName the name of an RGB color profile * @return the wanted colorspace, or 0 if the color space and profile can not be combined. */ const KoColorSpace * rgb8(const QString &profileName = QString()); /** * Convenience method to get an RGBA 8bit colorspace with the given profile. * @param profile an RGB profile * @return the wanted colorspace, or 0 if the color space and profile can not be combined. */ const KoColorSpace * rgb8(const KoColorProfile * profile); /** * Convenience method to get an RGBA 16bit colorspace. If a profile is not specified, * an sRGB profile will be used. * @param profileName the name of an RGB color profile * @return the wanted colorspace, or 0 if the color space and profile can not be combined. */ const KoColorSpace * rgb16(const QString &profileName = QString()); /** * Convenience method to get an RGBA 16bit colorspace with the given profile. * @param profile an RGB profile * @return the wanted colorspace, or 0 if the color space and profile can not be combined. */ const KoColorSpace * rgb16(const KoColorProfile * profile); /** * Convenience method to get an Lab 16bit colorspace. If a profile is not specified, * an Lab profile with a D50 whitepoint will be used. * @param profileName the name of an Lab color profile * @return the wanted colorspace, or 0 if the color space and profile can not be combined. */ const KoColorSpace * lab16(const QString &profileName = QString()); /** * Convenience method to get an Lab 16bit colorspace with the given profile. * @param profile an Lab profile * @return the wanted colorspace, or 0 if the color space and profile can not be combined. */ const KoColorSpace * lab16(const KoColorProfile * profile); /** * Convenience method to get a standard profile for Rec.2020 linear light * color space */ const KoColorProfile *p2020G10Profile() const; /** * Convenience method to get a standard profile for Rec.2020 PQ color space */ const KoColorProfile *p2020PQProfile() const; /** * Convenience method to get a standard profile for Rec. 709 linear light * color space */ const KoColorProfile *p709G10Profile() const; /** * Convenience method to get a standard profile for Rec. 709 sRGB-tone- * response-curve profile */ const KoColorProfile *p709SRGBProfile() const; /** * @return the list of available color models */ QList colorModelsList(ColorSpaceListVisibility option) const; /** * @return the list of available color models for the given colorModelId */ QList colorDepthList(const KoID& colorModelId, ColorSpaceListVisibility option) const; /** * @return the list of available color models for the given colorModelId */ QList colorDepthList(const QString & colorModelId, ColorSpaceListVisibility option) const; /** * @return the cache of color conversion transformation to be use by KoColorSpace */ KoColorConversionCache* colorConversionCache() const; /** * @return a permanent colorspace owned by the registry, of the same type and profile * as the one given in argument */ const KoColorSpace* permanentColorspace(const KoColorSpace* _colorSpace); /** * This function return a list of all the keys in KoID format by using the name() method * on the objects stored in the registry. */ QList listKeys() const; private: friend class KisCsConversionTest; friend class KisIteratorTest; friend class KisIteratorNGTest; friend class KisPainterTest; friend class KisCrashFilterTest; friend class KoColorSpacesBenchmark; friend class TestKoColorSpaceSanity; friend class TestColorConversionSystem; friend struct FriendOfColorSpaceRegistry; /** * @return a list with an instance of all color space with their default profile. */ QList allColorSpaces(ColorSpaceListVisibility visibility, ColorSpaceListProfilesSelection pSelection); /** * @return the color conversion system use by the registry and the color * spaces to create color conversion transformation. * * WARNING: conversion system is guarded by the registry locks, don't * use it anywhere other than unittests! */ const KoColorConversionSystem* colorConversionSystem() const; private: KoColorSpaceRegistry(const KoColorSpaceRegistry&); KoColorSpaceRegistry operator=(const KoColorSpaceRegistry&); void init(); private: struct Private; Private * const d; }; #endif // KOCOLORSPACEREGISTRY_H