File indexing completed on 2024-05-12 04:44:35
0001 // SPDX-FileCopyrightText: Lukas Sommer <sommerluk@gmail.com> 0002 // SPDX-License-Identifier: BSD-2-Clause OR MIT 0003 0004 #ifndef RGBCOLORSPACE_P_H 0005 #define RGBCOLORSPACE_P_H 0006 0007 // Include the header of the public class of this private implementation. 0008 // #include "rgbcolorspace.h" 0009 0010 #include "cielchd50values.h" 0011 #include "constpropagatingrawpointer.h" 0012 #include "helperconstants.h" 0013 #include "oklchvalues.h" 0014 #include <lcms2.h> 0015 #include <qdatetime.h> 0016 #include <qglobal.h> 0017 #include <qmap.h> 0018 #include <qstring.h> 0019 #include <qversionnumber.h> 0020 0021 namespace PerceptualColor 0022 { 0023 class RgbColorSpace; 0024 0025 /** @internal 0026 * 0027 * @brief Private implementation within the <em>Pointer to 0028 * implementation</em> idiom */ 0029 class RgbColorSpacePrivate final 0030 { 0031 private: 0032 [[nodiscard]] static QMap<cmsUInt32Number, QString> getIntentList(); 0033 0034 public: 0035 explicit RgbColorSpacePrivate(RgbColorSpace *backLink); 0036 /** @brief Default destructor 0037 * 0038 * The destructor is non-<tt>virtual</tt> because 0039 * the class as a whole is <tt>final</tt>. */ 0040 ~RgbColorSpacePrivate() noexcept = default; 0041 0042 // Data members: 0043 /** @brief The darkest in-gamut point on the L* axis. 0044 * @sa whitepointL 0045 * 0046 * @internal 0047 * 0048 * @todo Use cmsDetectBlackPoint? But “our” “blackpoint” is always on 0049 * the grey axis, but the real blackpoint not? Document this? */ 0050 qreal m_cielabD50BlackpointL = 0; 0051 /** @brief The lightest in-gamut point on the L* axis. 0052 * @sa blackpointL() */ 0053 qreal m_cielabD50WhitepointL = 100; 0054 /** @brief The darkest in-gamut point on the L* axis. 0055 * @sa whitepointL 0056 * 0057 * @internal 0058 * 0059 * @todo Use cmsDetectBlackPoint? But “our” “blackpoint” is always on 0060 * the grey axis, but the real blackpoint not? Document this? */ 0061 qreal m_oklabBlackpointL = 0; 0062 /** @brief The lightest in-gamut point on the L* axis. 0063 * @sa blackpointL() */ 0064 qreal m_oklabWhitepointL = 1; 0065 /** @brief Internal storage for property 0066 * @ref RgbColorSpace::profileAbsoluteFilePath */ 0067 QString m_profileAbsoluteFilePath; 0068 /** @brief Internal storage for property 0069 * @ref RgbColorSpace::profileClass */ 0070 cmsProfileClassSignature m_profileClass; 0071 /** @brief Internal storage for property 0072 * @ref RgbColorSpace::profileColorModel */ 0073 cmsColorSpaceSignature m_profileColorModel; 0074 /** @brief Internal storage for property 0075 * @ref RgbColorSpace::profileCopyright */ 0076 QString m_profileCopyright; 0077 /** @brief Internal storage for property 0078 * @ref RgbColorSpace::profileCreationDateTime */ 0079 QDateTime m_profileCreationDateTime; 0080 /** @brief Internal storage for property 0081 * @ref RgbColorSpace::profileFileSize */ 0082 qint64 m_profileFileSize = -1; 0083 /** @brief Internal storage for property 0084 * @ref RgbColorSpace::profileHasClut */ 0085 bool m_profileHasClut = false; 0086 /** @brief Internal storage for property 0087 * @ref RgbColorSpace::profileHasMatrixShaper */ 0088 bool m_profileHasMatrixShaper = false; 0089 /** @brief Internal storage for property 0090 * @ref RgbColorSpace::profileIccVersion */ 0091 QVersionNumber m_profileIccVersion; 0092 /** @brief Internal storage for property 0093 * @ref RgbColorSpace::profileManufacturer */ 0094 QString m_profileManufacturer; 0095 /** @brief Internal storage for property 0096 * @ref RgbColorSpace::profileMaximumCielchD50Chroma */ 0097 double m_profileMaximumCielchD50Chroma = CielchD50Values::maximumChroma; 0098 /** @brief Internal storage for property 0099 * @ref RgbColorSpace::profileMaximumOklchChroma */ 0100 double m_profileMaximumOklchChroma = OklchValues::maximumChroma; 0101 /** @brief Internal storage for property 0102 * @ref RgbColorSpace::profileModel */ 0103 QString m_profileModel; 0104 /** @brief Internal storage for property 0105 * @ref RgbColorSpace::profileName */ 0106 QString m_profileName; 0107 /** @brief Internal storage for property 0108 * @ref RgbColorSpace::profilePcsColorModel */ 0109 cmsColorSpaceSignature m_profilePcsColorModel; 0110 /** @brief A handle to a LittleCMS transform. */ 0111 cmsHTRANSFORM m_transformCielabD50ToRgb16Handle = nullptr; 0112 /** @brief A handle to a LittleCMS transform. */ 0113 cmsHTRANSFORM m_transformCielabD50ToRgbHandle = nullptr; 0114 /** @brief A handle to a LittleCMS transform. */ 0115 cmsHTRANSFORM m_transformRgbToCielabD50Handle = nullptr; 0116 0117 // Functions: 0118 static void deleteTransform(cmsHTRANSFORM *transformHandle); 0119 [[nodiscard]] double detectMaximumCielchD50Chroma() const; 0120 [[nodiscard]] double detectMaximumOklchChroma() const; 0121 [[nodiscard]] static QDateTime getCreationDateTimeFromProfile(cmsHPROFILE profileHandle); 0122 [[nodiscard]] static QVersionNumber getIccVersionFromProfile(cmsHPROFILE profileHandle); 0123 [[nodiscard]] static QString getInformationFromProfile(cmsHPROFILE profileHandle, cmsInfoType infoType); 0124 [[nodiscard]] bool initialize(cmsHPROFILE rgbProfileHandle); 0125 0126 /** @brief The rendering intents supported by the LittleCMS library. 0127 * 0128 * Contains all rendering intents supported by the LittleCMS library 0129 * against which this we are currently linking. Each entry contains 0130 * the code and the (english-language) description just as provided 0131 * by LittleCMS. 0132 * 0133 * Note that LittleCMS supports as built-in intents the four official 0134 * ICC intents and also some other, non-ICC intents. Furthermore, 0135 * LittleCMS plugins can provide even more intents. As of LittleCMS 2.13 0136 * the following built-in intents are available: 0137 * 0138 * | Type | Macro name | Code | 0139 * | :------ | :-------------------------------------------- | ---: | 0140 * | ICC | INTENT_PERCEPTUAL | 0 | 0141 * | ICC | INTENT_RELATIVE_COLORIMETRIC | 1 | 0142 * | ICC | INTENT_SATURATION | 2 | 0143 * | ICC | INTENT_ABSOLUTE_COLORIMETRIC | 3 | 0144 * | Non-ICC | INTENT_PRESERVE_K_ONLY_PERCEPTUAL | 10 | 0145 * | Non-ICC | INTENT_PRESERVE_K_ONLY_RELATIVE_COLORIMETRIC | 11 | 0146 * | Non-ICC | INTENT_PRESERVE_K_ONLY_SATURATION | 12 | 0147 * | Non-ICC | INTENT_PRESERVE_K_PLANE_PERCEPTUAL | 13 | 0148 * | Non-ICC | INTENT_PRESERVE_K_PLANE_RELATIVE_COLORIMETRIC | 14 | 0149 * | Non-ICC | INTENT_PRESERVE_K_PLANE_SATURATION | 15 | 0150 * 0151 * @todo Either actually <em>use</em> this code or <em>remove</em> this 0152 * code. */ 0153 static inline const QMap<cmsUInt32Number, QString> intentList = getIntentList(); 0154 0155 /** @brief Precision of HSV hue during maximum-chroma detection. 0156 * 0157 * @todo A value smaller than 0.001 does not make sense 0158 * currently, because QColor has only a limited precision for 0159 * HSV conversions. Furthermore, since Qt6 it’s floating point interface 0160 * has been defined with “float”. For a more exact solution, we would 0161 * have to implement our own HSV conversion first. */ 0162 static constexpr double chromaDetectionHuePrecision = gamutPrecisionCielab; 0163 /** @brief Increment factor for the maximum-chroma detection. 0164 * 0165 * The maximum-chroma detection, regardless of the precision, 0166 * might always return a value that is a bit too small. However, 0167 * we want to have @ref RgbColorSpace::profileMaximumCielchD50Chroma and 0168 * @ref RgbColorSpace::profileMaximumOklchChroma values that are equal 0169 * or slightly bigger than the actual maximum-chroma, to make sure to 0170 * not exclude valid values. Therefore, @ref detectMaximumCielchD50Chroma() 0171 * and @ref detectMaximumOklchChroma use this increment factor to 0172 * slightly increment the outcome of the chroma detection relative to 0173 * the original value, as a safety margin. Note that additionally, 0174 * an absolute increment should also be added, because of limited 0175 * precision in floating point operations. */ 0176 static constexpr double chromaDetectionIncrementFactor = 1.02; 0177 /** @brief For detecting CIELab in-gamut or out-of-gamut colors. 0178 * 0179 * For gamut detection, a roundtrip conversion is performed: Lab values 0180 * are converted to an RGB color space and backwards. If the distance 0181 * in euclidean space between the the original Lab value and the result 0182 * of the roundtrip is smaller than a certain value, it is considered 0183 * as an in-gamut value. 0184 * 0185 * This deviation limit should be as small as possible for a more correct 0186 * gamut boundary. But it must unfortunately also be big enough to ignore 0187 * rounding errors. The current value was chosen by trial-and-error. */ 0188 static constexpr qreal cielabDeviationLimit = 0.5; 0189 /** @brief For detecting Oklab in-gamut or out-of-gamut colors. 0190 * 0191 * For gamut detection, a roundtrip conversion is performed: Lab values 0192 * are converted to an RGB color space and backwards. If the distance 0193 * in euclidean space between the the original Lab value and the result 0194 * of the roundtrip is smaller than a certain value, it is considered 0195 * as an in-gamut value. 0196 * 0197 * This deviation limit should be as small as possible for a more correct 0198 * gamut boundary. But it must unfortunately also be big enough to ignore 0199 * rounding errors. The current value was chosen by trial-and-error. */ 0200 static constexpr qreal oklabDeviationLimit = 0.001; 0201 0202 private: 0203 Q_DISABLE_COPY(RgbColorSpacePrivate) 0204 0205 /** @brief Pointer to the object from which <em>this</em> object 0206 * is the private implementation. */ 0207 ConstPropagatingRawPointer<RgbColorSpace> q_pointer; 0208 }; 0209 0210 } // namespace PerceptualColor 0211 0212 #endif // RGBCOLORSPACE_P_H