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 }