File indexing completed on 2024-05-12 15:59:33
0001 /* 0002 * SPDX-FileCopyrightText: 2017 Dmitry Kazakov <dimula73@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "KoColorProfileStorage.h" 0008 0009 #include <cmath> 0010 0011 #include <QHash> 0012 #include <QReadWriteLock> 0013 #include <QString> 0014 0015 #include "KoColorSpaceFactory.h" 0016 #include "KoColorProfile.h" 0017 #include "kis_assert.h" 0018 0019 0020 struct KoColorProfileStorage::Private { 0021 QHash<QString, KoColorProfile * > profileMap; 0022 QHash<QByteArray, KoColorProfile * > profileUniqueIdMap; 0023 QHash<QString, QString> profileAlias; 0024 QReadWriteLock lock; 0025 0026 void populateUniqueIdMap(); 0027 0028 ~Private() 0029 { 0030 Q_FOREACH (KoColorProfile *p, profileMap) { 0031 delete p; 0032 } 0033 profileMap.clear(); 0034 profileUniqueIdMap.clear(); 0035 } 0036 }; 0037 0038 KoColorProfileStorage::KoColorProfileStorage() 0039 : d(new Private) 0040 { 0041 0042 } 0043 0044 KoColorProfileStorage::~KoColorProfileStorage() 0045 { 0046 } 0047 0048 void KoColorProfileStorage::addProfile(KoColorProfile *profile) 0049 { 0050 QWriteLocker locker(&d->lock); 0051 0052 if (profile->valid()) { 0053 d->profileMap[profile->name()] = profile; 0054 if (!d->profileUniqueIdMap.isEmpty()) { 0055 d->profileUniqueIdMap.insert(profile->uniqueId(), profile); 0056 } 0057 } 0058 } 0059 0060 void KoColorProfileStorage::removeProfile(KoColorProfile *profile) 0061 { 0062 QWriteLocker locker(&d->lock); 0063 0064 d->profileMap.remove(profile->name()); 0065 if (!d->profileUniqueIdMap.isEmpty()) { 0066 d->profileUniqueIdMap.remove(profile->uniqueId()); 0067 } 0068 } 0069 0070 bool KoColorProfileStorage::containsProfile(const KoColorProfile *profile) 0071 { 0072 QReadLocker l(&d->lock); 0073 return d->profileMap.contains(profile->name()); 0074 } 0075 0076 void KoColorProfileStorage::addProfileAlias(const QString &name, const QString &to) 0077 { 0078 QWriteLocker l(&d->lock); 0079 d->profileAlias[name] = to; 0080 } 0081 0082 QString KoColorProfileStorage::profileAlias(const QString &name) const 0083 { 0084 QReadLocker l(&d->lock); 0085 return d->profileAlias.value(name, name); 0086 } 0087 0088 const KoColorProfile *KoColorProfileStorage::profileByName(const QString &name) const 0089 { 0090 QReadLocker l(&d->lock); 0091 return d->profileMap.value(d->profileAlias.value(name, name), 0); 0092 } 0093 0094 void KoColorProfileStorage::Private::populateUniqueIdMap() 0095 { 0096 QWriteLocker l(&lock); 0097 profileUniqueIdMap.clear(); 0098 0099 for (auto it = profileMap.constBegin(); 0100 it != profileMap.constEnd(); 0101 ++it) { 0102 0103 KoColorProfile *profile = it.value(); 0104 QByteArray id = profile->uniqueId(); 0105 0106 if (!id.isEmpty()) { 0107 profileUniqueIdMap.insert(id, profile); 0108 } 0109 } 0110 } 0111 0112 0113 const KoColorProfile *KoColorProfileStorage::profileByUniqueId(const QByteArray &id) const 0114 { 0115 QReadLocker l(&d->lock); 0116 if (d->profileUniqueIdMap.isEmpty()) { 0117 l.unlock(); 0118 d->populateUniqueIdMap(); 0119 l.relock(); 0120 } 0121 return d->profileUniqueIdMap.value(id, 0); 0122 0123 } 0124 0125 QList<const KoColorProfile *> KoColorProfileStorage::profilesFor(const KoColorSpaceFactory *csf) const 0126 { 0127 QList<const KoColorProfile *> profiles; 0128 if (!csf) return profiles; 0129 0130 QReadLocker l(&d->lock); 0131 0132 QHash<QString, KoColorProfile * >::ConstIterator it; 0133 for (it = d->profileMap.constBegin(); it != d->profileMap.constEnd(); ++it) { 0134 KoColorProfile * profile = it.value(); 0135 if (csf->profileIsCompatible(profile)) { 0136 Q_ASSERT(profile); 0137 // if (profile->colorSpaceSignature() == csf->colorSpaceSignature()) { 0138 profiles.push_back(profile); 0139 } 0140 } 0141 return profiles; 0142 } 0143 0144 QList<const KoColorProfile *> KoColorProfileStorage::profilesFor(const QVector<double> &colorants, ColorPrimaries colorantType, TransferCharacteristics transferType, double error) 0145 { 0146 QList<const KoColorProfile *> profiles; 0147 0148 if (colorants.isEmpty() && colorantType == PRIMARIES_UNSPECIFIED && transferType == TRC_UNSPECIFIED) { 0149 return profiles; 0150 } 0151 0152 QReadLocker l(&d->lock); 0153 for (const KoColorProfile* profile : d->profileMap) { 0154 bool colorantMatch = (colorants.isEmpty() || colorantType != PRIMARIES_UNSPECIFIED); 0155 bool colorantTypeMatch = (colorantType == PRIMARIES_UNSPECIFIED); 0156 bool transferMatch = (transferType == 2); 0157 if (colorantType != PRIMARIES_UNSPECIFIED) { 0158 if (int(profile->getColorPrimaries()) == colorantType) { 0159 colorantTypeMatch = true; 0160 } 0161 } 0162 if (transferType != TRC_UNSPECIFIED) { 0163 if (int(profile->getTransferCharacteristics()) == transferType) { 0164 transferMatch = true; 0165 } 0166 } 0167 0168 if (!colorants.isEmpty() && colorantType == PRIMARIES_UNSPECIFIED) { 0169 QVector<qreal> wp = profile->getWhitePointxyY(); 0170 if (profile->hasColorants() && colorants.size() == 8) { 0171 QVector<qreal> col = profile->getColorantsxyY(); 0172 if (col.size() < 8 || wp.size() < 2) { 0173 // too few colorants, skip. 0174 continue; 0175 } 0176 QVector<double> compare = {wp[0], wp[1], col[0], col[1], col[3], col[4], col[6], col[7]}; 0177 0178 for (int i = 0; i < compare.size(); i++) { 0179 colorantMatch = std::fabs(compare[i] - colorants[i]) < error; 0180 if (!colorantMatch) { 0181 break; 0182 } 0183 } 0184 } else { 0185 if (wp.size() < 2 || colorants.size() < 2) { 0186 // too few colorants, skip. 0187 continue; 0188 } 0189 if (std::fabs(wp[0] - colorants[0]) < error && std::fabs(wp[1] - colorants[1]) < error) { 0190 colorantMatch = true; 0191 } 0192 } 0193 } 0194 0195 if (transferMatch && colorantMatch && colorantTypeMatch) { 0196 profiles.push_back(profile); 0197 } 0198 } 0199 0200 return profiles; 0201 }