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