13static QMatrix4x4 matrixFromColumns(
const QVector3D &first, 
const QVector3D &second, 
const QVector3D &third)
 
   16    ret(0, 0) = first.x();
 
   17    ret(1, 0) = first.y();
 
   18    ret(2, 0) = first.z();
 
   19    ret(0, 1) = second.x();
 
   20    ret(1, 1) = second.y();
 
   21    ret(2, 1) = second.z();
 
   22    ret(0, 2) = third.x();
 
   23    ret(1, 2) = third.y();
 
   24    ret(2, 2) = third.z();
 
   30    return QVector3D(xy.x() / xy.y(), 1, (1 - xy.x() - xy.y()) / xy.y());
 
 
   36    return QVector2D(xyz.x() / (xyz.x() + xyz.y() + xyz.z()), xyz.y() / (xyz.x() + xyz.y() + xyz.z()));
 
 
   41    static const QMatrix4x4 bradford = []() {
 
   54    static const QMatrix4x4 inverseBradford = []() {
 
   56        ret(0, 0) = 0.9869929;
 
   57        ret(0, 1) = -0.1470543;
 
   58        ret(0, 2) = 0.1599627;
 
   59        ret(1, 0) = 0.4323053;
 
   60        ret(1, 1) = 0.5183603;
 
   61        ret(1, 2) = 0.0492912;
 
   62        ret(2, 0) = -0.0085287;
 
   63        ret(2, 1) = 0.0400428;
 
   64        ret(2, 2) = 0.9684867;
 
   67    if (sourceWhitepoint == destinationWhitepoint) {
 
   70    const QVector3D factors = (bradford * 
xyToXYZ(destinationWhitepoint)) / (bradford * 
xyToXYZ(sourceWhitepoint));
 
   71    QMatrix4x4 adaptation{};
 
   72    adaptation(0, 0) = factors.x();
 
   73    adaptation(1, 1) = factors.y();
 
   74    adaptation(2, 2) = factors.z();
 
   75    return inverseBradford * adaptation * bradford;
 
 
   80    const auto component_scale = (matrixFromColumns(
red, 
green, 
blue)).inverted() * 
white;
 
   81    return matrixFromColumns(
red * component_scale.x(), 
green * component_scale.y(), 
blue * component_scale.z());
 
 
   87        m_red * (1 - factor) + one.
red() * factor,
 
   88        m_green * (1 - factor) + one.
green() * factor,
 
   89        m_blue * (1 - factor) + one.
blue() * factor,
 
 
   99    , m_toXYZ(calculateToXYZMatrix(xyToXYZ(red), xyToXYZ(green), xyToXYZ(blue), xyToXYZ(white)))
 
  100    , m_fromXYZ(m_toXYZ.inverted())
 
 
  105    : m_red(xyzToXY(red))
 
  106    , m_green(xyzToXY(green))
 
  107    , m_blue(xyzToXY(blue))
 
  108    , m_white(xyzToXY(white))
 
  109    , m_toXYZ(calculateToXYZMatrix(red, green, blue, white))
 
  110    , m_fromXYZ(m_toXYZ.inverted())
 
 
  172    QVector2D{0.64, 0.33},
 
  173    QVector2D{0.30, 0.60},
 
  174    QVector2D{0.15, 0.06},
 
  175    QVector2D{0.3127, 0.3290},
 
  178static const Colorimetry BT2020 = Colorimetry{
 
  179    QVector2D{0.708, 0.292},
 
  180    QVector2D{0.170, 0.797},
 
  181    QVector2D{0.131, 0.046},
 
  182    QVector2D{0.3127, 0.3290},
 
  199    : m_colorimetry(colorimety)
 
  200    , m_transferFunction(tf)
 
  201    , m_sdrColorimetry(sdrColorimetry)
 
  202    , m_sdrBrightness(sdrBrightness)
 
  203    , m_minHdrBrightness(minHdrBrightness)
 
  204    , m_maxFrameAverageBrightness(maxFrameAverageBrightness)
 
  205    , m_maxHdrHighlightBrightness(maxHdrHighlightBrightness)
 
 
  210    : m_colorimetry(
Colorimetry::fromName(colorimetry))
 
  211    , m_transferFunction(tf)
 
  212    , m_sdrColorimetry(sdrColorimetry)
 
  213    , m_sdrBrightness(sdrBrightness)
 
  214    , m_minHdrBrightness(minHdrBrightness)
 
  215    , m_maxFrameAverageBrightness(maxFrameAverageBrightness)
 
  216    , m_maxHdrHighlightBrightness(maxHdrHighlightBrightness)
 
 
  222    return m_colorimetry;
 
 
  227    return m_sdrColorimetry;
 
 
  232    return m_transferFunction;
 
 
  237    return m_sdrBrightness;
 
 
  242    return m_minHdrBrightness;
 
 
  247    return m_maxFrameAverageBrightness;
 
 
  252    return m_maxHdrHighlightBrightness;
 
 
  257    return m_colorimetry == other.m_colorimetry
 
  258        && m_transferFunction == other.m_transferFunction
 
  259        && m_sdrGamutWideness == other.m_sdrGamutWideness
 
  260        && m_sdrBrightness == other.m_sdrBrightness
 
  261        && m_minHdrBrightness == other.m_minHdrBrightness
 
  262        && m_maxFrameAverageBrightness == other.m_maxFrameAverageBrightness
 
  263        && m_maxHdrHighlightBrightness == other.m_maxHdrHighlightBrightness;
 
 
  266static float srgbToLinear(
float sRGB)
 
  268    if (
sRGB < 0.04045) {
 
  269        return std::max(
sRGB / 12.92, 0.0);
 
  271        return std::clamp(std::pow((
sRGB + 0.055) / 1.055, 12.0 / 5.0), 0.0, 1.0);
 
  275static float linearToSRGB(
float linear)
 
  278        return std::max(
linear / 12.92, 0.0);
 
  280        return std::clamp(std::pow(
linear, 5.0 / 12.0) * 1.055 - 0.055, 0.0, 1.0);
 
  284static float nitsToPQ(
float nits)
 
  286    const float normalized = std::clamp(nits / 10000.0f, 0.0f, 1.0f);
 
  287    const float c1 = 0.8359375;
 
  288    const float c2 = 18.8515625;
 
  289    const float c3 = 18.6875;
 
  290    const float m1 = 0.1593017578125;
 
  291    const float m2 = 78.84375;
 
  292    const float powed = std::pow(normalized, m1);
 
  293    const float num = c1 + c2 * powed;
 
  294    const float denum = 1 + c3 * powed;
 
  295    return std::pow(num / denum, m2);
 
  298static float pqToNits(
float pq)
 
  300    const float c1 = 0.8359375;
 
  301    const float c2 = 18.8515625;
 
  302    const float c3 = 18.6875;
 
  303    const float m1_inv = 1.0 / 0.1593017578125;
 
  304    const float m2_inv = 1.0 / 78.84375;
 
  305    const float powed = std::pow(pq, m2_inv);
 
  306    const float num = std::max(powed - c1, 0.0f);
 
  307    const float den = c2 - c3 * powed;
 
  308    return 10000.0f * std::pow(num / den, m1_inv);
 
  311static QVector3D clamp(
const QVector3D &vect, 
float min = 0, 
float max = 1)
 
  313    return QVector3D(std::clamp(vect.x(), min, max), std::clamp(vect.y(), min, max), std::clamp(vect.z(), min, max));
 
  320        return sdrBrightness * QVector3D(srgbToLinear(nits.x()), srgbToLinear(nits.y()), srgbToLinear(nits.z()));
 
  322        return sdrBrightness * QVector3D(std::pow(nits.x(), 2.2), std::pow(nits.y(), 2.2), std::pow(nits.z(), 2.2));
 
  328        return QVector3D(pqToNits(nits.x()), pqToNits(nits.y()), pqToNits(nits.z()));
 
 
  338        return QVector3D(linearToSRGB(clamped.x()), linearToSRGB(clamped.y()), linearToSRGB(clamped.z()));
 
  342        return QVector3D(std::pow(clamped.x(), 1 / 2.2), std::pow(clamped.y(), 1 / 2.2), std::pow(clamped.z(), 1 / 2.2));
 
  349        return QVector3D(nitsToPQ(rgb.x()), nitsToPQ(rgb.y()), nitsToPQ(rgb.z()));
 
 
  356    rgb = 
encodedToNits(rgb, m_transferFunction, m_sdrBrightness);
 
 
NamedTransferFunction transferFunction() const
double minHdrBrightness() const
static const ColorDescription sRGB
bool operator==(const ColorDescription &other) const
double maxHdrHighlightBrightness() const
const Colorimetry & colorimetry() const
static QVector3D nitsToEncoded(const QVector3D &rgb, NamedTransferFunction tf, double sdrBrightness)
double maxFrameAverageBrightness() const
const Colorimetry & sdrColorimetry() const
ColorDescription(const Colorimetry &colorimety, NamedTransferFunction tf, double sdrBrightness, double minHdrBrightness, double maxFrameAverageBrightness, double maxHdrHighlightBrightness, const Colorimetry &sdrColorimetry=Colorimetry::fromName(NamedColorimetry::BT709))
static QVector3D encodedToNits(const QVector3D &nits, NamedTransferFunction tf, double sdrBrightness)
double sdrBrightness() const
QVector3D mapTo(QVector3D rgb, const ColorDescription &other) const
static QMatrix4x4 calculateToXYZMatrix(QVector3D red, QVector3D green, QVector3D blue, QVector3D white)
Colorimetry interpolateGamutTo(const Colorimetry &one, double factor) const
static QMatrix4x4 chromaticAdaptationMatrix(QVector2D sourceWhitepoint, QVector2D destinationWhitepoint)
const QVector2D & blue() const
Colorimetry adaptedTo(QVector2D newWhitepoint) const
static QVector3D xyToXYZ(QVector2D xy)
QMatrix4x4 toOther(const Colorimetry &colorimetry) const
Colorimetry(QVector2D red, QVector2D green, QVector2D blue, QVector2D white)
const QVector2D & red() const
const QMatrix4x4 & toXYZ() const
static QVector2D xyzToXY(QVector3D xyz)
static const Colorimetry & fromName(NamedColorimetry name)
const QVector2D & white() const
const QVector2D & green() const
bool operator==(const Colorimetry &other) const
const QMatrix4x4 & fromXYZ() const