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 }