File indexing completed on 2024-05-12 15:59:33

0001 /*
0002  *  SPDX-FileCopyrightText: 2007 Cyrille Berger <cberger@cberger.net>
0003  *
0004  * SPDX-License-Identifier: LGPL-2.1-or-later
0005 */
0006 
0007 #include <algorithm>
0008 #include <array>
0009 #include <cmath>
0010 
0011 #include "KoColorProfile.h"
0012 #include "DebugPigment.h"
0013 #include "kis_assert.h"
0014 
0015 struct Q_DECL_HIDDEN KoColorProfile::Private {
0016     QString name;
0017     QString info;
0018     QString fileName;
0019     QString manufacturer;
0020     QString copyright;
0021     int primaries {-1};
0022     TransferCharacteristics characteristics {TRC_UNSPECIFIED};
0023 };
0024 
0025 KoColorProfile::KoColorProfile(const QString &fileName) : d(new Private)
0026 {
0027 //     dbgPigment <<" Profile filename =" << fileName;
0028     d->fileName = fileName;
0029 }
0030 
0031 KoColorProfile::KoColorProfile(const KoColorProfile& profile)
0032     : d(new Private(*profile.d))
0033 {
0034 }
0035 
0036 KoColorProfile::~KoColorProfile()
0037 {
0038     delete d;
0039 }
0040 
0041 bool KoColorProfile::load()
0042 {
0043     return false;
0044 }
0045 
0046 bool KoColorProfile::save(const QString & filename)
0047 {
0048     Q_UNUSED(filename);
0049     return false;
0050 }
0051 
0052 
0053 QString KoColorProfile::name() const
0054 {
0055     return d->name;
0056 }
0057 
0058 QString KoColorProfile::info() const
0059 {
0060     return d->info;
0061 }
0062 QString KoColorProfile::manufacturer() const
0063 {
0064     return d->manufacturer;
0065 }
0066 QString KoColorProfile::copyright() const
0067 {
0068     return d->copyright;
0069 }
0070 QString KoColorProfile::fileName() const
0071 {
0072     return d->fileName;
0073 }
0074 
0075 void KoColorProfile::setFileName(const QString &f)
0076 {
0077     d->fileName = f;
0078 }
0079 
0080 ColorPrimaries KoColorProfile::getColorPrimaries() const
0081 {
0082     if (d->primaries == -1) {
0083         ColorPrimaries primaries = PRIMARIES_UNSPECIFIED;
0084         QVector<qreal> wp = getWhitePointxyY();
0085 
0086         bool match = false;
0087         if (hasColorants()) {
0088             QVector<qreal> col = getColorantsxyY();
0089             if (col.size()<8) {
0090                 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(col.size() < 8, PRIMARIES_UNSPECIFIED);
0091                 //too few colorants.
0092                 d->primaries = int(primaries);
0093                 return (primaries);
0094             }
0095             QVector<double> colorants = {wp[0], wp[1], col[0], col[1], col[3], col[4], col[6], col[7]};
0096             QVector<double> compare;
0097 
0098             QVector<ColorPrimaries> primariesList = {PRIMARIES_ITU_R_BT_709_5, PRIMARIES_ITU_R_BT_601_6, PRIMARIES_ITU_R_BT_470_6_SYSTEM_M,
0099                                                      PRIMARIES_ITU_R_BT_2020_2_AND_2100_0, PRIMARIES_SMPTE_EG_432_1, PRIMARIES_SMPTE_RP_431_2,
0100                                                      PRIMARIES_SMPTE_ST_428_1, PRIMARIES_GENERIC_FILM, PRIMARIES_SMPTE_240M, PRIMARIES_EBU_Tech_3213_E,
0101                                                      PRIMARIES_ADOBE_RGB_1998, PRIMARIES_PROPHOTO, PRIMARIES_ITU_R_BT_470_6_SYSTEM_B_G};
0102 
0103             for (ColorPrimaries check: primariesList) {
0104                 colorantsForType(check, compare);
0105                 if (compare.size() <8) {
0106                     KIS_SAFE_ASSERT_RECOVER(compare.size() < 8) { continue; }
0107                     //too few colorants, skip.
0108                 }
0109                 match = true;
0110                 for (int i=0; i<colorants.size(); i++) {
0111                     match = std::fabs(colorants[i] - compare[i]) < 0.00001;
0112                     if (!match) {
0113                         break;
0114                     }
0115                 }
0116                 if (match) {
0117                     primaries = check;
0118                 }
0119             }
0120         }
0121 
0122         d->primaries = int(primaries);
0123     }
0124     return ColorPrimaries(d->primaries);
0125 }
0126 
0127 QString KoColorProfile::getColorPrimariesName(ColorPrimaries primaries)
0128 {
0129     switch (primaries) {
0130     case PRIMARIES_ITU_R_BT_709_5:
0131         return QStringLiteral("Rec. 709");
0132     case PRIMARIES_ITU_R_BT_470_6_SYSTEM_M:
0133         return QStringLiteral("BT. 470 System M");
0134     case PRIMARIES_ITU_R_BT_470_6_SYSTEM_B_G:
0135         return QStringLiteral("BT. 470 System B, G");
0136     case PRIMARIES_GENERIC_FILM:
0137         return QStringLiteral("Generic Film");
0138     case PRIMARIES_SMPTE_240M:
0139         return QStringLiteral("SMPTE 240 M");
0140     case PRIMARIES_ITU_R_BT_2020_2_AND_2100_0:
0141         return QStringLiteral("Rec. 2020");
0142     case PRIMARIES_ITU_R_BT_601_6:
0143         return QStringLiteral("Rec. 601");
0144     case PRIMARIES_SMPTE_EG_432_1:
0145         return QStringLiteral("Display P3");
0146     case PRIMARIES_SMPTE_RP_431_2:
0147         return QStringLiteral("DCI P3");
0148     case PRIMARIES_SMPTE_ST_428_1:
0149         return QStringLiteral("XYZ primaries");
0150     case PRIMARIES_EBU_Tech_3213_E:
0151         return QStringLiteral("EBU Tech 3213 E");
0152     case PRIMARIES_PROPHOTO:
0153         return QStringLiteral("ProPhoto");
0154     case PRIMARIES_ADOBE_RGB_1998:
0155         return QStringLiteral("A98");
0156     case PRIMARIES_UNSPECIFIED:
0157         break;
0158     }
0159     return QStringLiteral("Unspecified");
0160 }
0161 
0162 void KoColorProfile::colorantsForType(ColorPrimaries primaries, QVector<double> &colorants)
0163 {
0164     switch (ColorPrimaries(primaries)) {
0165     case PRIMARIES_UNSPECIFIED:
0166         break;
0167     case PRIMARIES_ITU_R_BT_470_6_SYSTEM_M:
0168         // Unquantisized.
0169         colorants = {0.310, 0.316};
0170         colorants.append({0.67, 0.33});
0171         colorants.append({0.21, 0.71});
0172         colorants.append({0.14, 0.08});
0173         //Illuminant C
0174         break;
0175     case PRIMARIES_ITU_R_BT_470_6_SYSTEM_B_G:
0176         // Unquantisized.
0177         colorants = {0.3127, 0.3290};
0178         colorants.append({0.64, 0.33});
0179         colorants.append({0.29, 0.60});
0180         colorants.append({0.1500, 0.06});
0181         break;
0182     case PRIMARIES_ITU_R_BT_601_6:
0183         colorants = {0.3127, 0.3290};
0184         colorants.append({0.630, 0.340});
0185         colorants.append({0.310, 0.595});
0186         colorants.append({0.155, 0.070});
0187         break;
0188     case PRIMARIES_SMPTE_240M:
0189         colorants = {0.3127, 0.3290};
0190         colorants.append({0.630, 0.340});
0191         colorants.append({0.310, 0.595});
0192         colorants.append({0.155, 0.070});
0193         break;
0194     case PRIMARIES_GENERIC_FILM:
0195         colorants = {0.310, 0.316};
0196         colorants.append({0.681, 0.319});
0197         colorants.append({0.243, 0.692});
0198         colorants.append({0.145, 0.049});
0199         //Illuminant C
0200         break;
0201     case PRIMARIES_ITU_R_BT_2020_2_AND_2100_0:
0202         //prequantization courtesy of Elle Stone.
0203         colorants = {0.3127, 0.3290};
0204         colorants.append({0.708012540607, 0.291993664388});
0205         colorants.append({0.169991652439, 0.797007778423});
0206         colorants.append({0.130997824007, 0.045996550894});
0207         break;
0208     case PRIMARIES_SMPTE_ST_428_1:
0209         colorants = {1.0/3, 1.0/3};
0210         colorants.append({1.0, 0});
0211         colorants.append({0, 1.0});
0212         colorants.append({0, 0});
0213         break;
0214     case PRIMARIES_SMPTE_RP_431_2:
0215         colorants = {0.314, 0.351};
0216         colorants.append({0.6800, 0.3200});
0217         colorants.append({0.2650, 0.6900});
0218         colorants.append({0.1500, 0.0600});
0219         break;
0220     case PRIMARIES_SMPTE_EG_432_1:
0221         colorants = {0.3127, 0.3290};
0222         colorants.append({0.6800, 0.3200});
0223         colorants.append({0.2650, 0.6900});
0224         colorants.append({0.1500, 0.0600});
0225         break;
0226     case PRIMARIES_EBU_Tech_3213_E:
0227         colorants = {0.3127, 0.3290};
0228         colorants.append({0.63, 0.34});
0229         colorants.append({0.295, 0.605});
0230         colorants.append({0.155, 0.077});
0231         break;
0232     case PRIMARIES_PROPHOTO:
0233         //prequantization courtesy of Elle Stone.
0234         colorants = {0.3457, 0.3585};
0235         colorants.append({0.7347, 0.2653});
0236         colorants.append({0.1596, 0.8404});
0237         colorants.append({0.0366, 0.0001});
0238         break;
0239     case PRIMARIES_ADOBE_RGB_1998:
0240         //prequantization courtesy of Elle Stone.
0241         colorants = {0.3127, 0.3290};
0242         colorants.append({0.639996511, 0.329996864});
0243         colorants.append({0.210005295, 0.710004866});
0244         colorants.append({0.149997606, 0.060003644});
0245         break;
0246     case PRIMARIES_ITU_R_BT_709_5:
0247     default:
0248         // Prequantisized colorants, courtesy of Elle Stone
0249         colorants = {0.3127, 0.3290};
0250         colorants.append({0.639998686, 0.330010138});
0251         colorants.append({0.300003784, 0.600003357});
0252         colorants.append({0.150002046, 0.059997204});
0253         break;
0254 
0255     }
0256 }
0257 
0258 TransferCharacteristics KoColorProfile::getTransferCharacteristics() const
0259 {
0260     // Parse from an estimated gamma
0261     const QVector<double> estimatedTRC = getEstimatedTRC();
0262     const double error = 0.0001;
0263     // Make sure the TRC is uniform across all channels
0264     const bool isUniformTRC = (estimatedTRC[0] == estimatedTRC[1] && estimatedTRC[0] == estimatedTRC[2]);
0265     if (d->characteristics == TRC_UNSPECIFIED && isUniformTRC && hasTRC()) {
0266         if (isLinear()) {
0267             d->characteristics = TRC_LINEAR;
0268         } else if (std::fabs(estimatedTRC[0] - (461.0 / 256.0)) < error) {
0269             // ICC v2 u8Fixed8Number calculation
0270             // Or can be prequantized as 1.80078125, courtesy of Elle Stone
0271             d->characteristics = TRC_GAMMA_1_8;
0272         } else if (std::fabs(estimatedTRC[0] - (563.0 / 256.0)) < error) {
0273             // Or can be prequantized as 2.19921875, courtesy of Elle Stone
0274             d->characteristics = TRC_A98;
0275         } else if (std::fabs(estimatedTRC[0] - 1.8) < error) {
0276             d->characteristics = TRC_GAMMA_1_8;
0277         } else if (std::fabs(estimatedTRC[0] - 2.2) < error) {
0278             d->characteristics = TRC_ITU_R_BT_470_6_SYSTEM_M;
0279         } else if (std::fabs(estimatedTRC[0] - 2.4) < error) {
0280             d->characteristics = TRC_GAMMA_2_4;
0281         } else if (std::fabs(estimatedTRC[0] - 2.8) < error) {
0282             d->characteristics = TRC_ITU_R_BT_470_6_SYSTEM_B_G;
0283         } else {
0284             // Escort to curve matching if no gamma is matched
0285             static constexpr std::array<TransferCharacteristics, 12> trcList = {{TRC_ITU_R_BT_709_5,
0286                                                                                  TRC_ITU_R_BT_470_6_SYSTEM_M,
0287                                                                                  TRC_ITU_R_BT_470_6_SYSTEM_B_G,
0288                                                                                  TRC_SMPTE_240M,
0289                                                                                  TRC_IEC_61966_2_1,
0290                                                                                  TRC_LOGARITHMIC_100,
0291                                                                                  TRC_LOGARITHMIC_100_sqrt10,
0292                                                                                  TRC_PROPHOTO,
0293                                                                                  TRC_GAMMA_1_8,
0294                                                                                  TRC_GAMMA_2_4,
0295                                                                                  TRC_A98,
0296                                                                                  TRC_LAB_L}};
0297             const auto characteristic =
0298                 std::find_if(trcList.begin(), trcList.end(), [&](const TransferCharacteristics &check) -> bool {
0299                     return compareTRC(check, static_cast<float>(error));
0300                 });
0301             if (characteristic != trcList.end()) {
0302                 d->characteristics = *characteristic;
0303             }
0304         }
0305     }
0306     return d->characteristics;
0307 }
0308 
0309 void KoColorProfile::setCharacteristics(ColorPrimaries primaries, TransferCharacteristics curve)
0310 {
0311     d->primaries = int(primaries);
0312     d->characteristics = curve;
0313 }
0314 
0315 QString KoColorProfile::getTransferCharacteristicName(TransferCharacteristics curve)
0316 {
0317     switch (curve) {
0318     case TRC_ITU_R_BT_709_5:
0319     case TRC_ITU_R_BT_601_6:
0320     case TRC_ITU_R_BT_2020_2_10bit:
0321         return QString("rec 709 trc");
0322     case TRC_ITU_R_BT_2020_2_12bit:
0323         return QString("rec 2020 12bit trc");
0324     case TRC_ITU_R_BT_470_6_SYSTEM_M:
0325         return QString("Gamma 2.2");
0326     case TRC_ITU_R_BT_470_6_SYSTEM_B_G:
0327         return QString("Gamma 2.8");
0328     case TRC_SMPTE_240M:
0329         return QString("SMPTE 240 trc");
0330     case TRC_LINEAR:
0331         return QString("Linear");
0332     case TRC_LOGARITHMIC_100:
0333         return QString("Logarithmic 100");
0334     case TRC_LOGARITHMIC_100_sqrt10:
0335         return QString("Logarithmic 100 sqrt10");
0336     case TRC_IEC_61966_2_4:
0337         return QString("IEC 61966 2.4");
0338     case TRC_ITU_R_BT_1361:
0339     case TRC_IEC_61966_2_1:
0340         return QString("sRGB trc");
0341     case TRC_SMPTE_ST_428_1:
0342         return QString("SMPTE ST 428");
0343     case TRC_ITU_R_BT_2100_0_PQ:
0344         return QString("Perceptual Quantizer");
0345     case TRC_ITU_R_BT_2100_0_HLG:
0346         return QString("Hybrid Log Gamma");
0347     case TRC_GAMMA_1_8:
0348         return QString("Gamma 1.8");
0349     case TRC_GAMMA_2_4:
0350         return QString("Gamma 2.4");
0351     case TRC_A98:
0352         return QString("Gamma A98");
0353     case TRC_PROPHOTO:
0354         return QString("ProPhoto trc");
0355     case TRC_LAB_L:
0356         return QString("Lab L* trc");
0357     case TRC_UNSPECIFIED:
0358         break;
0359     }
0360 
0361     return QString("Unspecified");
0362 }
0363 
0364 void KoColorProfile::setName(const QString &name)
0365 {
0366     d->name = name;
0367 }
0368 void KoColorProfile::setInfo(const QString &info)
0369 {
0370     d->info = info;
0371 }
0372 void KoColorProfile::setManufacturer(const QString &manufacturer)
0373 {
0374     d->manufacturer = manufacturer;
0375 }
0376 void KoColorProfile::setCopyright(const QString &copyright)
0377 {
0378     d->copyright = copyright;
0379 }