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