File indexing completed on 2024-05-19 04:45:51

0001 // SPDX-FileCopyrightText: Lukas Sommer <sommerluk@gmail.com>
0002 // SPDX-License-Identifier: BSD-2-Clause OR MIT
0003 
0004 #ifndef HELPERCONVERSION_H
0005 #define HELPERCONVERSION_H
0006 
0007 #include "helperqttypes.h"
0008 #include "lchdouble.h"
0009 #include <lcms2.h>
0010 #include <qcolor.h>
0011 #include <qglobal.h>
0012 #include <type_traits>
0013 
0014 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0015 #include <qtmetamacros.h>
0016 #endif
0017 
0018 /** @internal
0019  *
0020  * @file
0021  *
0022  * Provides type conversions. */
0023 
0024 namespace PerceptualColor
0025 {
0026 
0027 Q_NAMESPACE
0028 
0029 /** @brief Identifiers for color models. */
0030 enum class ColorModel {
0031     CielabD50, /**< Cielab color space using a D50 illuminant.
0032         Lightness: [0, 100].<br/>
0033         a: unbound.<br/>
0034         b: unbound. */
0035     CielchD50, /**< Cielch color space using a D50 illuminant.
0036         Lightness: [0, 100].<br/>
0037         Chroma: unbound.<br/>
0038         Hue: [0, 360[. */
0039     Hsl360_1_1, /**< A HSL color space.
0040         Hue: [0, 360[.<br/>
0041         Saturation: [0, 1].<br/>
0042         Lightness: [0, 1]. */
0043     Hwb360_1_1, /**< A HWB color space.
0044         Hue: [0, 360[.<br/>
0045         Whiteness: [0, 1].<br/>
0046         Blackness: [0, 1]. */
0047     Invalid, /**< Represents invalid data. */
0048     OklabD65, /**< Oklab color space, which by definition always and
0049         exclusively uses a D65 illuminant.
0050 
0051         Lightness: [0, 1].<br/>
0052         a: unbound.<br/>
0053         b: unbound. */
0054     OklchD65, /**< Oklch color space, which by definition always and
0055         exclusively uses a D65 illuminant.
0056 
0057         Lightness: [0, 1].<br/>
0058         Chroma: unbound.<br/>
0059         Hue: [0, 360[. */
0060     Rgb1, /**< An Rgb color space.
0061         R: [0, 1].<br/>
0062         G: [0, 1].<br/>
0063         B: [0, 1]. */
0064     XyzD50, /**< Xyz color space using a D50 illuminant.
0065         X: unbound.<br/>
0066         Y: [0, 1]. Diffuse white has a luminance (Y) of 1.0<br/>
0067         Z: unbound. */
0068     XyzD65 /**< Xzy color space using a D65 illuminant.
0069         X: unbound.<br/>
0070         Y: [0, 1]. Diffuse white has a luminance (Y) of 1.0<br/>
0071         Z: unbound. */
0072 };
0073 Q_ENUM_NS(ColorModel)
0074 
0075 #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
0076 uint qHash(const ColorModel t, uint seed = 0); // clazy:exclude=qt6-qhash-signature
0077 #endif
0078 
0079 /** @internal
0080  *
0081  * @brief Converts from <tt>[0, 1]</tt> to <tt>[0, 255]</tt>.
0082  *
0083  * @param original A value on a scale <tt>[0, 1]</tt>.
0084  *
0085  * @returns Value converted to the scale <tt>[0, 255]</tt>, applying correct
0086  * rounding. Out-of-range values are bound to the valid range. */
0087 template<typename T>
0088 [[nodiscard]] constexpr quint8 fromFloatingToEightBit(const T &original)
0089 {
0090     static_assert( //
0091         std::is_floating_point<T>::value, //
0092         "Template fromFloatingToEightBit() only works with floating point types");
0093     const int rounded = qRound(original * 255);
0094     const auto bounded = qBound<int>(0, rounded, 255);
0095     return static_cast<quint8>(bounded);
0096 }
0097 
0098 /** @internal
0099  *
0100  * @brief Like <tt>QColor::fromRgbF</tt> but for all floating point types.
0101  *
0102  * @param red Red component. Range: <tt>[0, 1]</tt>
0103  * @param green See above.
0104  * @param blue See above.
0105  * @returns A corresponding <tt>QColor</tt> object. */
0106 template<typename T>
0107 [[nodiscard]] QColor qColorFromRgbDouble(T red, T green, T blue)
0108 {
0109     static_assert( //
0110         std::is_floating_point<T>::value, //
0111         "Template fromFloatingToEightBit() only works with floating point types");
0112     return QColor::fromRgbF(static_cast<QColorFloatType>(red), //
0113                             static_cast<QColorFloatType>(green), //
0114                             static_cast<QColorFloatType>(blue));
0115 }
0116 
0117 [[nodiscard]] cmsCIELab toCmsLab(const cmsCIELCh &value);
0118 
0119 [[nodiscard]] cmsCIELCh toCmsLch(const LchDouble &value);
0120 
0121 [[nodiscard]] LchDouble toLchDouble(const cmsCIELCh &value);
0122 
0123 [[nodiscard]] LchDouble toLchDouble(const cmsCIELab &value);
0124 
0125 /** @internal
0126  *
0127  * @brief White point D65 for 2°-observer.
0128  *
0129  * According to
0130  * <a href="https://en.wikipedia.org/w/index.php?title=Illuminant_D65&oldid=1100467073#Definition">
0131  * Wikipedia</a>:
0132  *
0133  * > “Using the standard 2° observer […] of D65 […] Normalizing for
0134  * > relative luminance (i.e. set Y=100), the XYZ tristimulus
0135  * > values are:<br/>
0136  * > X = 95.047<br/>
0137  * > Y = 100<br/>
0138  * > Z = 108.883”
0139  *
0140  * Normalizing this to Y = 1 as expected by LittleCMS, gives this value. */
0141 // TODO xxx This seems to be not longer used!?
0142 constexpr cmsCIEXYZ whitePointD65TwoDegree{0.95047, 1.00000, 1.08883};
0143 
0144 } // namespace PerceptualColor
0145 
0146 #endif // HELPERCONVERSION_H