summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <[email protected]>2024-04-09 16:06:05 +0200
committerAllan Sandfeld Jensen <[email protected]>2024-05-31 16:24:52 +0200
commitce33e476670c82bdf50344f4ab9bdbb17e6feaea (patch)
tree796bcdf31cd286551f7e4dad8259d9bc028e7538
parent53ad7b2d9bf0a6b10656e8bed8eba518888e9495 (diff)
Add Bt2020 and Bt2100 color spaces
[ChangeLog][QtGui][QColorSpace] Bt.2020 and Bt.2100 (aka HDR10) formats have been added. Task-number: QTBUG-114722 Change-Id: I4ffce460202837e1077e34f48a9286981ee444bb Reviewed-by: Laszlo Agocs <[email protected]>
-rw-r--r--src/gui/CMakeLists.txt1
-rw-r--r--src/gui/kernel/qguiapplication.cpp4
-rw-r--r--src/gui/painting/qcolormatrix_p.h6
-rw-r--r--src/gui/painting/qcolorspace.cpp92
-rw-r--r--src/gui/painting/qcolorspace.h13
-rw-r--r--src/gui/painting/qcolortransferfunction_p.h5
-rw-r--r--src/gui/painting/qcolortransfergeneric_p.h109
-rw-r--r--src/gui/painting/qcolortransform.cpp18
-rw-r--r--src/gui/painting/qcolortrc_p.h101
-rw-r--r--src/gui/painting/qcolortrclut.cpp67
-rw-r--r--src/gui/painting/qcolortrclut_p.h11
-rw-r--r--src/gui/painting/qicc.cpp145
-rw-r--r--tests/auto/gui/painting/qcolorspace/resources/Rec. ITU-R BT.2100 PQ.iccbin0 -> 13300 bytes
-rw-r--r--tests/auto/gui/painting/qcolorspace/tst_qcolorspace.cpp14
14 files changed, 477 insertions, 109 deletions
diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt
index aed66563a79..ac1b7de9924 100644
--- a/src/gui/CMakeLists.txt
+++ b/src/gui/CMakeLists.txt
@@ -171,6 +171,7 @@ qt_internal_add_module(Gui
painting/qcolormatrix_p.h
painting/qcolorspace.cpp painting/qcolorspace.h painting/qcolorspace_p.h
painting/qcolortransferfunction_p.h
+ painting/qcolortransfergeneric_p.h
painting/qcolortransfertable_p.h
painting/qcolortransform.cpp painting/qcolortransform.h painting/qcolortransform_p.h
painting/qcolortrc_p.h
diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp
index 5228ac94779..2595c005e3d 100644
--- a/src/gui/kernel/qguiapplication.cpp
+++ b/src/gui/kernel/qguiapplication.cpp
@@ -4325,7 +4325,7 @@ const QColorTrcLut *QGuiApplicationPrivate::colorProfileForA8Text()
{
#ifdef Q_OS_WIN
if (!m_a8ColorProfile)
- m_a8ColorProfile = QColorTrcLut::fromGamma(2.31); // This is a hard-coded thing for Windows text rendering
+ m_a8ColorProfile = QColorTrcLut::fromGamma(2.31f); // This is a hard-coded thing for Windows text rendering
return m_a8ColorProfile.get();
#else
return colorProfileForA32Text();
@@ -4335,7 +4335,7 @@ const QColorTrcLut *QGuiApplicationPrivate::colorProfileForA8Text()
const QColorTrcLut *QGuiApplicationPrivate::colorProfileForA32Text()
{
if (!m_a32ColorProfile)
- m_a32ColorProfile = QColorTrcLut::fromGamma(fontSmoothingGamma);
+ m_a32ColorProfile = QColorTrcLut::fromGamma(float(fontSmoothingGamma));
return m_a32ColorProfile.get();
}
diff --git a/src/gui/painting/qcolormatrix_p.h b/src/gui/painting/qcolormatrix_p.h
index 6a9b9f4f9af..0c85222a9a5 100644
--- a/src/gui/painting/qcolormatrix_p.h
+++ b/src/gui/painting/qcolormatrix_p.h
@@ -336,6 +336,12 @@ public:
{ 0.1351922452f, 0.7118769884f, 0.0000000000f },
{ 0.0313525312f, 0.0000856627f, 0.8251883388f } };
}
+ static QColorMatrix toXyzFromBt2020()
+ {
+ return QColorMatrix { { 0.673447f, 0.279037f, -0.00192261f },
+ { 0.165665f, 0.675339f, 0.0299835f },
+ { 0.125092f, 0.0456238f, 0.797134f } };
+ }
friend inline bool comparesEqual(const QColorMatrix &lhs, const QColorMatrix &rhs);
Q_DECLARE_EQUALITY_COMPARABLE(QColorMatrix);
};
diff --git a/src/gui/painting/qcolorspace.cpp b/src/gui/painting/qcolorspace.cpp
index 99c88ee0bea..fce2ae4c85f 100644
--- a/src/gui/painting/qcolorspace.cpp
+++ b/src/gui/painting/qcolorspace.cpp
@@ -21,7 +21,7 @@ QT_BEGIN_NAMESPACE
Q_CONSTINIT QBasicMutex QColorSpacePrivate::s_lutWriteLock;
-Q_CONSTINIT static QAtomicPointer<QColorSpacePrivate> s_predefinedColorspacePrivates[QColorSpace::ProPhotoRgb] = {};
+Q_CONSTINIT static QAtomicPointer<QColorSpacePrivate> s_predefinedColorspacePrivates[QColorSpace::Bt2100Hlg] = {};
static void cleanupPredefinedColorspaces()
{
for (QAtomicPointer<QColorSpacePrivate> &ptr : s_predefinedColorspacePrivates) {
@@ -60,6 +60,12 @@ QColorSpacePrimaries::QColorSpacePrimaries(QColorSpace::Primaries primaries)
bluePoint = QPointF(0.0366, 0.0001);
whitePoint = QColorVector::D50Chromaticity();
break;
+ case QColorSpace::Primaries::Bt2020:
+ redPoint = QPointF(0.708, 0.292);
+ greenPoint = QPointF(0.170, 0.797);
+ bluePoint = QPointF(0.131, 0.046);
+ whitePoint = QColorVector::D65Chromaticity();
+ break;
default:
Q_UNREACHABLE();
}
@@ -132,6 +138,21 @@ QColorSpacePrivate::QColorSpacePrivate(QColorSpace::NamedColorSpace namedColorSp
transferFunction = QColorSpace::TransferFunction::ProPhotoRgb;
description = QStringLiteral("ProPhoto RGB");
break;
+ case QColorSpace::Bt2020:
+ primaries = QColorSpace::Primaries::Bt2020;
+ transferFunction = QColorSpace::TransferFunction::Bt2020;
+ description = QStringLiteral("BT.2020");
+ break;
+ case QColorSpace::Bt2100Pq:
+ primaries = QColorSpace::Primaries::Bt2020;
+ transferFunction = QColorSpace::TransferFunction::St2084;
+ description = QStringLiteral("BT.2100(PQ)");
+ break;
+ case QColorSpace::Bt2100Hlg:
+ primaries = QColorSpace::Primaries::Bt2020;
+ transferFunction = QColorSpace::TransferFunction::Hlg;
+ description = QStringLiteral("BT.2100(HLG)");
+ break;
default:
Q_UNREACHABLE();
}
@@ -292,6 +313,26 @@ void QColorSpacePrivate::identifyColorSpace()
}
}
break;
+ case QColorSpace::Primaries::Bt2020:
+ if (transferFunction == QColorSpace::TransferFunction::Bt2020) {
+ namedColorSpace = QColorSpace::Bt2020;
+ if (description.isEmpty())
+ description = QStringLiteral("BT.2020");
+ return;
+ }
+ if (transferFunction == QColorSpace::TransferFunction::St2084) {
+ namedColorSpace = QColorSpace::Bt2100Pq;
+ if (description.isEmpty())
+ description = QStringLiteral("BT.2100(PQ)");
+ return;
+ }
+ if (transferFunction == QColorSpace::TransferFunction::Hlg) {
+ namedColorSpace = QColorSpace::Bt2100Hlg;
+ if (description.isEmpty())
+ description = QStringLiteral("BT.2100(HLG)");
+ return;
+ }
+ break;
default:
break;
}
@@ -337,7 +378,7 @@ void QColorSpacePrivate::setTransferFunctionTable(const QList<uint16_t> &transfe
} else if (curve.isSRgb()) {
transferFunction = QColorSpace::TransferFunction::SRgb;
}
- trc[0].m_type = QColorTrc::Type::Function;
+ trc[0].m_type = QColorTrc::Type::ParameterizedFunction;
trc[0].m_fun = curve;
} else {
trc[0].m_type = QColorTrc::Type::Table;
@@ -363,21 +404,21 @@ void QColorSpacePrivate::setTransferFunctionTables(const QList<uint16_t> &redTra
transferFunction = QColorSpace::TransferFunction::Custom;
QColorTransferFunction curve;
if (redTable.asColorTransferFunction(&curve)) {
- trc[0].m_type = QColorTrc::Type::Function;
+ trc[0].m_type = QColorTrc::Type::ParameterizedFunction;
trc[0].m_fun = curve;
} else {
trc[0].m_type = QColorTrc::Type::Table;
trc[0].m_table = redTable;
}
if (greenTable.asColorTransferFunction(&curve)) {
- trc[1].m_type = QColorTrc::Type::Function;
+ trc[1].m_type = QColorTrc::Type::ParameterizedFunction;
trc[1].m_fun = curve;
} else {
trc[1].m_type = QColorTrc::Type::Table;
trc[1].m_table = greenTable;
}
if (blueTable.asColorTransferFunction(&curve)) {
- trc[2].m_type = QColorTrc::Type::Function;
+ trc[2].m_type = QColorTrc::Type::ParameterizedFunction;
trc[2].m_fun = curve;
} else {
trc[2].m_type = QColorTrc::Type::Table;
@@ -390,27 +431,34 @@ void QColorSpacePrivate::setTransferFunction()
{
switch (transferFunction) {
case QColorSpace::TransferFunction::Linear:
- trc[0].m_type = QColorTrc::Type::Function;
- trc[0].m_fun = QColorTransferFunction();
+ trc[0] = QColorTransferFunction();
if (qFuzzyIsNull(gamma))
gamma = 1.0f;
break;
case QColorSpace::TransferFunction::Gamma:
- trc[0].m_type = QColorTrc::Type::Function;
- trc[0].m_fun = QColorTransferFunction::fromGamma(gamma);
+ trc[0] = QColorTransferFunction::fromGamma(gamma);
break;
case QColorSpace::TransferFunction::SRgb:
- trc[0].m_type = QColorTrc::Type::Function;
- trc[0].m_fun = QColorTransferFunction::fromSRgb();
+ trc[0] = QColorTransferFunction::fromSRgb();
if (qFuzzyIsNull(gamma))
gamma = 2.31f;
break;
case QColorSpace::TransferFunction::ProPhotoRgb:
- trc[0].m_type = QColorTrc::Type::Function;
- trc[0].m_fun = QColorTransferFunction::fromProPhotoRgb();
+ trc[0] = QColorTransferFunction::fromProPhotoRgb();
if (qFuzzyIsNull(gamma))
gamma = 1.8f;
break;
+ case QColorSpace::TransferFunction::Bt2020:
+ trc[0] = QColorTransferFunction::fromBt2020();
+ if (qFuzzyIsNull(gamma))
+ gamma = 2.1f;
+ break;
+ case QColorSpace::TransferFunction::St2084:
+ trc[0] = QColorTransferGenericFunction::pq();
+ break;
+ case QColorSpace::TransferFunction::Hlg:
+ trc[0] = QColorTransferGenericFunction::hlg();
+ break;
case QColorSpace::TransferFunction::Custom:
break;
default:
@@ -530,6 +578,13 @@ void QColorSpacePrivate::clearElementListProcessingForEdit()
\l{http://www.color.org/chardata/rgb/DCIP3.xalter}{ICC registration of DCI-P3}
\value ProPhotoRgb The Pro Photo RGB color space, also known as ROMM RGB is a very wide gamut color space.
\l{http://www.color.org/chardata/rgb/rommrgb.xalter}{ICC registration of ROMM RGB}
+ \value [since 6.8] Bt2020 BT.2020, also known as Rec.2020 is a basic colorspace of HDR TVs.
+ \l{http://www.color.org/chardata/rgb/BT2020.xalter}{ICC registration of BT.2020}
+ \value [since 6.8] Bt2100Pq BT.2100(PQ), also known as Rec.2100 or HDR10 is an HDR encoding with the same
+ primaries as Bt2020 but using the Perceptual Quantizer transfer function.
+ \l{http://www.color.org/chardata/rgb/BT2100.xalter}{ICC registration of BT.2100}
+ \value [since 6.8] Bt2100Hlg BT.2100 (HLG) is an HDR encoding with the same
+ primaries as Bt2020 but using the Hybrid Log-Gamma transfer function.
*/
/*!
@@ -542,6 +597,7 @@ void QColorSpacePrivate::clearElementListProcessingForEdit()
\value AdobeRgb The Adobe RGB primaries
\value DciP3D65 The DCI-P3 primaries with the D65 whitepoint
\value ProPhotoRgb The ProPhoto RGB primaries with the D50 whitepoint
+ \value [since 6.8] Bt2020 The BT.2020 primaries with a D65 whitepoint
*/
/*!
@@ -554,6 +610,10 @@ void QColorSpacePrivate::clearElementListProcessingForEdit()
\value Gamma A transfer function that is a real gamma curve based on the value of gamma()
\value SRgb The sRGB transfer function, composed of linear and gamma parts
\value ProPhotoRgb The ProPhoto RGB transfer function, composed of linear and gamma parts
+ \value [since 6.8] Bt2020 The BT.2020 transfer function, composited of linear and gamma parts
+ \value [since 6.8] St2084 The SMPTE ST 2084 transfer function, also known Perceptual Quantizer(PQ).
+ \value [since 6.8] Hlg The Hybrid log-gamma transfer function.
+
*/
/*!
@@ -596,7 +656,7 @@ void QColorSpacePrivate::clearElementListProcessingForEdit()
*/
QColorSpace::QColorSpace(NamedColorSpace namedColorSpace)
{
- if (namedColorSpace < QColorSpace::SRgb || namedColorSpace > QColorSpace::ProPhotoRgb) {
+ if (namedColorSpace < QColorSpace::SRgb || namedColorSpace > QColorSpace::Bt2100Hlg) {
qWarning() << "QColorSpace attempted constructed from invalid QColorSpace::NamedColorSpace: " << int(namedColorSpace);
return;
}
@@ -1133,9 +1193,11 @@ bool QColorSpacePrivate::isValid() const noexcept
if (colorModel == QColorSpace::ColorModel::Gray) {
if (!trc[0].isValid())
return false;
- } else {
+ } else if (colorModel == QColorSpace::ColorModel::Rgb){
if (!trc[0].isValid() || !trc[1].isValid() || !trc[2].isValid())
return false;
+ } else {
+ return false;
}
return true;
}
diff --git a/src/gui/painting/qcolorspace.h b/src/gui/painting/qcolorspace.h
index 488cbd6a530..ecf66145616 100644
--- a/src/gui/painting/qcolorspace.h
+++ b/src/gui/painting/qcolorspace.h
@@ -26,7 +26,10 @@ public:
SRgbLinear,
AdobeRgb,
DisplayP3,
- ProPhotoRgb
+ ProPhotoRgb,
+ Bt2020,
+ Bt2100Pq,
+ Bt2100Hlg,
};
Q_ENUM(NamedColorSpace)
enum class Primaries {
@@ -34,7 +37,8 @@ public:
SRgb,
AdobeRgb,
DciP3D65,
- ProPhotoRgb
+ ProPhotoRgb,
+ Bt2020,
};
Q_ENUM(Primaries)
enum class TransferFunction {
@@ -42,7 +46,10 @@ public:
Linear,
Gamma,
SRgb,
- ProPhotoRgb
+ ProPhotoRgb,
+ Bt2020,
+ St2084,
+ Hlg,
};
Q_ENUM(TransferFunction)
enum class TransformModel : uint8_t {
diff --git a/src/gui/painting/qcolortransferfunction_p.h b/src/gui/painting/qcolortransferfunction_p.h
index 484cc691148..b9a09b4646a 100644
--- a/src/gui/painting/qcolortransferfunction_p.h
+++ b/src/gui/painting/qcolortransferfunction_p.h
@@ -114,6 +114,11 @@ public:
return QColorTransferFunction(1.0f, 0.0f, 1.0f / 16.0f, 16.0f / 512.0f, 0.0f, 0.0f, 1.8f,
Hints(Hint::Calculated));
}
+ static QColorTransferFunction fromBt2020()
+ {
+ return QColorTransferFunction(1.0f / 1.0993f, 0.0993f / 1.0993f, 1.0f / 4.5f, 0.08145f, 0.0f, 0.0f, 2.2f,
+ Hints(Hint::Calculated));
+ }
bool matches(const QColorTransferFunction &o) const
{
return paramCompare(m_a, o.m_a) && paramCompare(m_b, o.m_b)
diff --git a/src/gui/painting/qcolortransfergeneric_p.h b/src/gui/painting/qcolortransfergeneric_p.h
new file mode 100644
index 00000000000..b4e6e8dec70
--- /dev/null
+++ b/src/gui/painting/qcolortransfergeneric_p.h
@@ -0,0 +1,109 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QCOLORTRANSFERGENERIC_P_H
+#define QCOLORTRANSFERGENERIC_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/private/qtguiglobal_p.h>
+
+#include <cmath>
+
+QT_BEGIN_NAMESPACE
+
+// Defines the a generic transfer function for our HDR functions
+class QColorTransferGenericFunction
+{
+public:
+ using ConverterPtr = float (*)(float);
+ constexpr QColorTransferGenericFunction(ConverterPtr toLinear = nullptr, ConverterPtr fromLinear = nullptr) noexcept
+ : m_toLinear(toLinear), m_fromLinear(fromLinear)
+ {}
+
+ static QColorTransferGenericFunction hlg()
+ {
+ return QColorTransferGenericFunction(hlgToLinear, hlgFromLinear);
+ }
+ static QColorTransferGenericFunction pq()
+ {
+ return QColorTransferGenericFunction(pqToLinear, pqFromLinear);
+ }
+
+ float apply(float x) const
+ {
+ return m_toLinear(x);
+ }
+
+ float applyInverse(float x) const
+ {
+ return m_fromLinear(x);
+ }
+
+ bool operator==(const QColorTransferGenericFunction &o) const noexcept
+ {
+ return m_toLinear == o.m_toLinear && m_fromLinear == o.m_fromLinear;
+ }
+ bool operator!=(const QColorTransferGenericFunction &o) const noexcept
+ {
+ return m_toLinear != o.m_toLinear || m_fromLinear != o.m_fromLinear;
+ }
+
+private:
+ ConverterPtr m_toLinear = nullptr;
+ ConverterPtr m_fromLinear = nullptr;
+
+ // HLG from linear [0-12] -> [0-1]
+ static float hlgFromLinear(float x)
+ {
+ if (x > 1.f)
+ return m_hlg_a * std::log(x - m_hlg_b) + m_hlg_c;
+ return std::sqrt(x * 0.25f);
+ }
+
+ // HLG to linear [0-1] -> [0-12]
+ static float hlgToLinear(float x)
+ {
+ if (x < 0.5f)
+ return (x * x) * 4.f;
+ return std::exp((x - m_hlg_c) / m_hlg_a) + m_hlg_b;
+ }
+
+ constexpr static float m_hlg_a = 0.17883277f;
+ constexpr static float m_hlg_b = 1.f - (4.f * m_hlg_a);
+ constexpr static float m_hlg_c = 0.55991073f; // 0.5 - a * ln(4 * a)
+
+ // PQ from linear [0-64] -> [0-1]
+ static float pqFromLinear(float x)
+ {
+ x = std::pow(x * (1.f / m_pq_f), (1.f / m_pq_m1));
+ return std::pow((m_pq_c1 - x) / (m_pq_c3 * x - m_pq_c2), (1.f / m_pq_m2));
+ }
+
+ // PQ from linear [0-1] -> [0-64]
+ static float pqToLinear(float x)
+ {
+ x = std::pow(x, m_pq_m1);
+ return std::pow((m_pq_c1 + m_pq_c2 * x) / (1.f + m_pq_c3 * x), m_pq_m2) * m_pq_f;
+ }
+
+ constexpr static float m_pq_c1 = 107.f / 128.f; // c3 - c2 + 1
+ constexpr static float m_pq_c2 = 2413.f / 128.f;
+ constexpr static float m_pq_c3 = 2392.f / 128.f;
+ constexpr static float m_pq_m1 = 1305.f / 8192.f;
+ constexpr static float m_pq_m2 = 2523.f / 32.f;
+ constexpr static float m_pq_f = 64.f; // This might need to be set based on scene metadata
+};
+
+QT_END_NAMESPACE
+
+#endif // QCOLORTRANSFERGENERIC_P_H
diff --git a/src/gui/painting/qcolortransform.cpp b/src/gui/painting/qcolortransform.cpp
index 7692e853fee..cffcc4485de 100644
--- a/src/gui/painting/qcolortransform.cpp
+++ b/src/gui/painting/qcolortransform.cpp
@@ -22,16 +22,6 @@
QT_BEGIN_NAMESPACE
-std::shared_ptr<QColorTrcLut> lutFromTrc(const QColorTrc &trc)
-{
- if (trc.m_type == QColorTrc::Type::Table)
- return QColorTrcLut::fromTransferTable(trc.m_table);
- if (trc.m_type == QColorTrc::Type::Function)
- return QColorTrcLut::fromTransferFunction(trc.m_fun);
- qWarning() << "TRC uninitialized";
- return nullptr;
-}
-
void QColorTransformPrivate::updateLutsIn() const
{
if (colorSpaceIn->lut.generated.loadAcquire())
@@ -46,12 +36,12 @@ void QColorTransformPrivate::updateLutsIn() const
}
if (colorSpaceIn->trc[0] == colorSpaceIn->trc[1] && colorSpaceIn->trc[0] == colorSpaceIn->trc[2]) {
- colorSpaceIn->lut[0] = lutFromTrc(colorSpaceIn->trc[0]);
+ colorSpaceIn->lut[0] = QColorTrcLut::fromTrc(colorSpaceIn->trc[0]);
colorSpaceIn->lut[1] = colorSpaceIn->lut[0];
colorSpaceIn->lut[2] = colorSpaceIn->lut[0];
} else {
for (int i = 0; i < 3; ++i)
- colorSpaceIn->lut[i] = lutFromTrc(colorSpaceIn->trc[i]);
+ colorSpaceIn->lut[i] = QColorTrcLut::fromTrc(colorSpaceIn->trc[i]);
}
colorSpaceIn->lut.generated.storeRelease(1);
@@ -70,12 +60,12 @@ void QColorTransformPrivate::updateLutsOut() const
}
if (colorSpaceOut->trc[0] == colorSpaceOut->trc[1] && colorSpaceOut->trc[0] == colorSpaceOut->trc[2]) {
- colorSpaceOut->lut[0] = lutFromTrc(colorSpaceOut->trc[0]);
+ colorSpaceOut->lut[0] = QColorTrcLut::fromTrc(colorSpaceOut->trc[0]);
colorSpaceOut->lut[1] = colorSpaceOut->lut[0];
colorSpaceOut->lut[2] = colorSpaceOut->lut[0];
} else {
for (int i = 0; i < 3; ++i)
- colorSpaceOut->lut[i] = lutFromTrc(colorSpaceOut->trc[i]);
+ colorSpaceOut->lut[i] = QColorTrcLut::fromTrc(colorSpaceOut->trc[i]);
}
colorSpaceOut->lut.generated.storeRelease(1);
diff --git a/src/gui/painting/qcolortrc_p.h b/src/gui/painting/qcolortrc_p.h
index c7a1d1bc882..dbca91c7f64 100644
--- a/src/gui/painting/qcolortrc_p.h
+++ b/src/gui/painting/qcolortrc_p.h
@@ -17,6 +17,7 @@
#include <QtGui/private/qtguiglobal_p.h>
#include "qcolortransferfunction_p.h"
+#include "qcolortransfergeneric_p.h"
#include "qcolortransfertable_p.h"
QT_BEGIN_NAMESPACE
@@ -26,20 +27,23 @@ class Q_GUI_EXPORT QColorTrc
{
public:
QColorTrc() noexcept : m_type(Type::Uninitialized) { }
- QColorTrc(const QColorTransferFunction &fun) : m_type(Type::Function), m_fun(fun) { }
+ QColorTrc(const QColorTransferFunction &fun) : m_type(Type::ParameterizedFunction), m_fun(fun) { }
QColorTrc(const QColorTransferTable &table) : m_type(Type::Table), m_table(table) { }
- QColorTrc(QColorTransferFunction &&fun) noexcept : m_type(Type::Function), m_fun(std::move(fun)) { }
+ QColorTrc(const QColorTransferGenericFunction &hdr) : m_type(Type::GenericFunction), m_hdr(hdr) { }
+ QColorTrc(QColorTransferFunction &&fun) noexcept : m_type(Type::ParameterizedFunction), m_fun(std::move(fun)) { }
QColorTrc(QColorTransferTable &&table) noexcept : m_type(Type::Table), m_table(std::move(table)) { }
+ QColorTrc(QColorTransferGenericFunction &&hdr) noexcept : m_type(Type::GenericFunction), m_hdr(std::move(hdr)) { }
enum class Type {
Uninitialized,
- Function,
- Table
+ ParameterizedFunction,
+ GenericFunction,
+ Table,
};
bool isIdentity() const
{
- return (m_type == Type::Function && m_fun.isIdentity())
+ return (m_type == Type::ParameterizedFunction && m_fun.isIdentity())
|| (m_type == Type::Table && m_table.isIdentity());
}
bool isValid() const
@@ -48,64 +52,87 @@ public:
}
float apply(float x) const
{
- if (m_type == Type::Table)
- return m_table.apply(x);
- if (m_type == Type::Function)
- return m_fun.apply(x);
+ switch (m_type) {
+ case Type::ParameterizedFunction:
+ return fun().apply(x);
+ case Type::GenericFunction:
+ return hdr().apply(x);
+ case Type::Table:
+ return table().apply(x);
+ default:
+ break;
+ }
return x;
}
float applyExtended(float x) const
{
- if (x >= 0.0f && x <= 1.0f)
- return apply(x);
- if (m_type == Type::Function)
- return std::copysign(m_fun.apply(std::abs(x)), x);
- if (m_type == Type::Table)
- return x < 0.0f ? 0.0f : 1.0f;
+ switch (m_type) {
+ case Type::ParameterizedFunction:
+ return std::copysign(fun().apply(std::abs(x)), x);
+ case Type::GenericFunction:
+ return hdr().apply(x);
+ case Type::Table:
+ return table().apply(x);
+ default:
+ break;
+ }
return x;
}
float applyInverse(float x) const
{
- if (m_type == Type::Table)
- return m_table.applyInverse(x);
- if (m_type == Type::Function)
- return m_fun.inverted().apply(x);
+ switch (m_type) {
+ case Type::ParameterizedFunction:
+ return fun().inverted().apply(x);
+ case Type::GenericFunction:
+ return hdr().applyInverse(x);
+ case Type::Table:
+ return table().applyInverse(x);
+ default:
+ break;
+ }
return x;
}
float applyInverseExtended(float x) const
{
- if (x >= 0.0f && x <= 1.0f)
- return applyInverse(x);
- if (m_type == Type::Function)
+ switch (m_type) {
+ case Type::ParameterizedFunction:
return std::copysign(applyInverse(std::abs(x)), x);
- if (m_type == Type::Table)
- return x < 0.0f ? 0.0f : 1.0f;
+ case Type::GenericFunction:
+ return hdr().applyInverse(x);
+ case Type::Table:
+ return table().applyInverse(x);
+ default:
+ break;
+ }
return x;
}
- friend inline bool operator!=(const QColorTrc &o1, const QColorTrc &o2);
- friend inline bool operator==(const QColorTrc &o1, const QColorTrc &o2);
-
const QColorTransferTable &table() const { return m_table; }
+ const QColorTransferFunction &fun() const{ return m_fun; }
+ const QColorTransferGenericFunction &hdr() const { return m_hdr; }
+ Type type() const noexcept { return m_type; }
Type m_type;
+
+ friend inline bool comparesEqual(const QColorTrc &lhs, const QColorTrc &rhs);
+ Q_DECLARE_EQUALITY_COMPARABLE(QColorTrc);
+
QColorTransferFunction m_fun;
QColorTransferTable m_table;
+ QColorTransferGenericFunction m_hdr;
};
-inline bool operator!=(const QColorTrc &o1, const QColorTrc &o2)
+inline bool comparesEqual(const QColorTrc &o1, const QColorTrc &o2)
{
if (o1.m_type != o2.m_type)
- return true;
- if (o1.m_type == QColorTrc::Type::Function)
- return o1.m_fun != o2.m_fun;
+ return false;
+ if (o1.m_type == QColorTrc::Type::ParameterizedFunction)
+ return o1.m_fun == o2.m_fun;
if (o1.m_type == QColorTrc::Type::Table)
- return o1.m_table != o2.m_table;
- return false;
-}
-inline bool operator==(const QColorTrc &o1, const QColorTrc &o2)
-{
- return !(o1 != o2);
+ return o1.m_table == o2.m_table;
+ if (o1.m_type == QColorTrc::Type::GenericFunction)
+ return o1.m_hdr == o2.m_hdr;
+ return true;
}
QT_END_NAMESPACE
diff --git a/src/gui/painting/qcolortrclut.cpp b/src/gui/painting/qcolortrclut.cpp
index 8a7673bc009..1357aa41a69 100644
--- a/src/gui/painting/qcolortrclut.cpp
+++ b/src/gui/painting/qcolortrclut.cpp
@@ -3,7 +3,9 @@
#include "qcolortrclut_p.h"
#include "qcolortransferfunction_p.h"
+#include "qcolortransfergeneric_p.h"
#include "qcolortransfertable_p.h"
+#include "qcolortrc_p.h"
#include <qmath.h>
QT_BEGIN_NAMESPACE
@@ -13,69 +15,86 @@ std::shared_ptr<QColorTrcLut> QColorTrcLut::create()
return std::make_shared<Access>();
}
-std::shared_ptr<QColorTrcLut> QColorTrcLut::fromGamma(qreal gamma, Direction dir)
+std::shared_ptr<QColorTrcLut> QColorTrcLut::fromGamma(float gamma, Direction dir)
{
auto cp = create();
cp->setFromGamma(gamma, dir);
return cp;
}
-std::shared_ptr<QColorTrcLut> QColorTrcLut::fromTransferFunction(const QColorTransferFunction &fun, Direction dir)
+std::shared_ptr<QColorTrcLut> QColorTrcLut::fromTrc(const QColorTrc &trc, Direction dir)
{
+ if (!trc.isValid())
+ return nullptr;
auto cp = create();
- cp->setFromTransferFunction(fun, dir);
+ cp->setFromTrc(trc, dir);
return cp;
}
-std::shared_ptr<QColorTrcLut> QColorTrcLut::fromTransferTable(const QColorTransferTable &table, Direction dir)
+void QColorTrcLut::setFromGamma(float gamma, Direction dir)
{
- auto cp = create();
- cp->setFromTransferTable(table, dir);
- return cp;
+ constexpr float iRes = 1.f / float(Resolution);
+ if (dir & ToLinear) {
+ if (!m_toLinear)
+ m_toLinear.reset(new ushort[Resolution + 1]);
+ for (int i = 0; i <= Resolution; ++i)
+ m_toLinear[i] = ushort(qRound(qBound(0.f, qPow(i * iRes, gamma), 1.f) * (255 * 256)));
+ }
+
+ if (dir & FromLinear) {
+ const float iGamma = 1.f / gamma;
+ if (!m_fromLinear)
+ m_fromLinear.reset(new ushort[Resolution + 1]);
+ for (int i = 0; i <= Resolution; ++i)
+ m_fromLinear[i] = ushort(qRound(qBound(0.f, qPow(i * iRes, iGamma), 1.f) * (255 * 256)));
+ }
}
-void QColorTrcLut::setFromGamma(qreal gamma, Direction dir)
+void QColorTrcLut::setFromTransferFunction(const QColorTransferFunction &fun, Direction dir)
{
+ constexpr float iRes = 1.f / float(Resolution);
if (dir & ToLinear) {
if (!m_toLinear)
m_toLinear.reset(new ushort[Resolution + 1]);
for (int i = 0; i <= Resolution; ++i)
- m_toLinear[i] = ushort(qRound(qPow(i / qreal(Resolution), gamma) * (255 * 256)));
+ m_toLinear[i] = ushort(qRound(qBound(0.f, fun.apply(i * iRes), 1.f) * (255 * 256)));
}
if (dir & FromLinear) {
if (!m_fromLinear)
m_fromLinear.reset(new ushort[Resolution + 1]);
+ QColorTransferFunction inv = fun.inverted();
for (int i = 0; i <= Resolution; ++i)
- m_fromLinear[i] = ushort(qRound(qPow(i / qreal(Resolution), qreal(1) / gamma) * (255 * 256)));
+ m_fromLinear[i] = ushort(qRound(qBound(0.f, inv.apply(i * iRes), 1.f) * (255 * 256)));
}
}
-void QColorTrcLut::setFromTransferFunction(const QColorTransferFunction &fun, Direction dir)
+void QColorTrcLut::setFromTransferGenericFunction(const QColorTransferGenericFunction &fun, Direction dir)
{
+ constexpr float iRes = 1.f / float(Resolution);
if (dir & ToLinear) {
if (!m_toLinear)
m_toLinear.reset(new ushort[Resolution + 1]);
for (int i = 0; i <= Resolution; ++i)
- m_toLinear[i] = ushort(qRound(fun.apply(i / qreal(Resolution)) * (255 * 256)));
+ m_toLinear[i] = ushort(qRound(qBound(0.f, fun.apply(i * iRes), 1.f) * (255 * 256)));
}
if (dir & FromLinear) {
if (!m_fromLinear)
m_fromLinear.reset(new ushort[Resolution + 1]);
- QColorTransferFunction inv = fun.inverted();
for (int i = 0; i <= Resolution; ++i)
- m_fromLinear[i] = ushort(qRound(inv.apply(i / qreal(Resolution)) * (255 * 256)));
+ m_fromLinear[i] = ushort(qRound(qBound(0.f, fun.applyInverse(i * iRes), 1.f) * (255 * 256)));
}
}
void QColorTrcLut::setFromTransferTable(const QColorTransferTable &table, Direction dir)
{
+ constexpr float iRes = 1.f / float(Resolution);
if (dir & ToLinear) {
if (!m_toLinear)
m_toLinear.reset(new ushort[Resolution + 1]);
for (int i = 0; i <= Resolution; ++i)
- m_toLinear[i] = ushort(qBound(0, qRound(table.apply(i / qreal(Resolution)) * (255 * 256)), 65280));
+ m_toLinear[i] = ushort(qRound(table.apply(i * iRes) * (255 * 256)));
}
if (dir & FromLinear) {
@@ -83,10 +102,24 @@ void QColorTrcLut::setFromTransferTable(const QColorTransferTable &table, Direct
m_fromLinear.reset(new ushort[Resolution + 1]);
float minInverse = 0.0f;
for (int i = 0; i <= Resolution; ++i) {
- minInverse = table.applyInverse(i / qreal(Resolution), minInverse);
- m_fromLinear[i] = ushort(qBound(0, qRound(minInverse * (255 * 256)), 65280));
+ minInverse = table.applyInverse(i * iRes, minInverse);
+ m_fromLinear[i] = ushort(qRound(minInverse * (255 * 256)));
}
}
}
+void QColorTrcLut::setFromTrc(const QColorTrc &trc, Direction dir)
+{
+ switch (trc.m_type) {
+ case QColorTrc::Type::ParameterizedFunction:
+ return setFromTransferFunction(trc.fun(), dir);
+ case QColorTrc::Type::Table:
+ return setFromTransferTable(trc.table(), dir);
+ case QColorTrc::Type::GenericFunction:
+ return setFromTransferGenericFunction(trc.hdr(), dir);
+ case QColorTrc::Type::Uninitialized:
+ break;
+ }
+}
+
QT_END_NAMESPACE
diff --git a/src/gui/painting/qcolortrclut_p.h b/src/gui/painting/qcolortrclut_p.h
index 3ebab428090..15f7348836c 100644
--- a/src/gui/painting/qcolortrclut_p.h
+++ b/src/gui/painting/qcolortrclut_p.h
@@ -30,8 +30,10 @@
QT_BEGIN_NAMESPACE
+class QColorTransferGenericFunction;
class QColorTransferFunction;
class QColorTransferTable;
+class QColorTrc;
class Q_GUI_EXPORT QColorTrcLut
{
@@ -46,12 +48,13 @@ public:
BiLinear = ToLinear | FromLinear
};
- static std::shared_ptr<QColorTrcLut> fromGamma(qreal gamma, Direction dir = BiLinear);
- static std::shared_ptr<QColorTrcLut> fromTransferFunction(const QColorTransferFunction &transFn, Direction dir = BiLinear);
- static std::shared_ptr<QColorTrcLut> fromTransferTable(const QColorTransferTable &transTable, Direction dir = BiLinear);
- void setFromGamma(qreal gamma, Direction dir = BiLinear);
+ static std::shared_ptr<QColorTrcLut> fromGamma(float gamma, Direction dir = BiLinear);
+ static std::shared_ptr<QColorTrcLut> fromTrc(const QColorTrc &trc, Direction dir = BiLinear);
+ void setFromGamma(float gamma, Direction dir = BiLinear);
void setFromTransferFunction(const QColorTransferFunction &transFn, Direction dir = BiLinear);
void setFromTransferTable(const QColorTransferTable &transTable, Direction dir = BiLinear);
+ void setFromTransferGenericFunction(const QColorTransferGenericFunction &transfn, Direction dir);
+ void setFromTrc(const QColorTrc &trc, Direction dir);
// The following methods all convert opaque or unpremultiplied colors:
diff --git a/src/gui/painting/qicc.cpp b/src/gui/painting/qicc.cpp
index 957fd833f2c..574911788da 100644
--- a/src/gui/painting/qicc.cpp
+++ b/src/gui/painting/qicc.cpp
@@ -116,6 +116,7 @@ enum class Tag : quint32 {
mAB_ = IccTag('m', 'A', 'B', ' '),
mBA_ = IccTag('m', 'B', 'A', ' '),
chad = IccTag('c', 'h', 'a', 'd'),
+ cicp = IccTag('c', 'i', 'c', 'p'),
gamt = IccTag('g', 'a', 'm', 't'),
sf32 = IccTag('s', 'f', '3', '2'),
@@ -247,6 +248,13 @@ struct Sf32TagData : GenericTagData {
quint32_be value[9];
};
+struct CicpTagData : GenericTagData {
+ quint8 colorPrimaries;
+ quint8 transferCharacteristics;
+ quint8 matrixCoefficients;
+ quint8 videoFullRangeFlag;
+};
+
struct MatrixElement {
qint32_be e0;
qint32_be e1;
@@ -331,7 +339,7 @@ static int writeColorTrc(QDataStream &stream, const QColorTrc &trc)
return 12;
}
- if (trc.m_type == QColorTrc::Type::Function) {
+ if (trc.m_type == QColorTrc::Type::ParameterizedFunction) {
const QColorTransferFunction &fun = trc.m_fun;
stream << uint(Tag::para) << uint(0);
if (fun.isGamma()) {
@@ -352,6 +360,14 @@ static int writeColorTrc(QDataStream &stream, const QColorTrc &trc)
stream << toFixedS1516(fun.m_f);
return 12 + 7 * 4;
}
+ if (trc.m_type != QColorTrc::Type::Table) {
+ stream << uint(Tag::curv) << uint(0);
+ stream << uint(16);
+ for (uint i = 0; i < 16; ++i) {
+ stream << ushort(qBound(0, qRound(trc.apply(i / 15.f) * 65535.f), 65535));
+ }
+ return 12 + 16 * 2;
+ }
Q_ASSERT(trc.m_type == QColorTrc::Type::Table);
stream << uint(Tag::curv) << uint(0);
@@ -763,6 +779,7 @@ QByteArray toIccProfile(const QColorSpace &space)
fixedLengthTagCount = 2;
bool writeChad = false;
bool writeB2a = true;
+ bool writeCicp = false;
if (spaceDPtr->isThreeComponentMatrix() && !spaceDPtr->chad.isIdentity()) {
writeChad = true;
fixedLengthTagCount++;
@@ -778,10 +795,19 @@ QByteArray toIccProfile(const QColorSpace &space)
Q_ASSERT(!spaceDPtr->isThreeComponentMatrix());
varLengthTagCount--;
}
+ switch (spaceDPtr->transferFunction) {
+ case QColorSpace::TransferFunction::St2084:
+ case QColorSpace::TransferFunction::Hlg:
+ writeCicp = true;
+ fixedLengthTagCount++;
+ default:
+ break;
+ }
const int tagCount = fixedLengthTagCount + varLengthTagCount;
const uint profileDataOffset = 128 + 4 + 12 * tagCount;
uint variableTagTableOffsets = 128 + 4 + 12 * fixedLengthTagCount;
+
uint currentOffset = 0;
uint rTrcOffset = 0;
uint gTrcOffset = 0;
@@ -850,6 +876,10 @@ QByteArray toIccProfile(const QColorSpace &space)
stream << uint(Tag::chad) << uint(currentOffset) << uint(44);
currentOffset += 44;
}
+ if (writeCicp) {
+ stream << uint(Tag::cicp) << uint(currentOffset) << uint(12);
+ currentOffset += 12;
+ }
// From here the offset and size will be updated later:
if (spaceDPtr->colorModel == QColorSpace::ColorModel::Rgb) {
stream << uint(Tag::rTRC) << uint(0) << uint(0);
@@ -898,6 +928,18 @@ QByteArray toIccProfile(const QColorSpace &space)
stream << toFixedS1516(chad.g.z);
stream << toFixedS1516(chad.b.z);
}
+ if (writeCicp) {
+ stream << uint(Tag::cicp) << uint(0);
+ stream << uchar(2); // Unspecified primaries
+ if (spaceDPtr->transferFunction == QColorSpace::TransferFunction::St2084)
+ stream << uchar(16);
+ else if (spaceDPtr->transferFunction == QColorSpace::TransferFunction::Hlg)
+ stream << uchar(18);
+ else
+ Q_UNREACHABLE();
+ stream << uchar(0); // Only for YCbCr, otherwise 0
+ stream << uchar(1); // Video full range
+ }
// From now on the data is variable sized:
if (spaceDPtr->colorModel == QColorSpace::ColorModel::Rgb) {
@@ -1062,11 +1104,11 @@ static quint32 parseTRC(const QByteArrayView &tagData, QColorTrc &gamma, QColorT
}
const auto valueOffset = sizeof(CurvTagData);
if (curv.valueCount == 0) {
- gamma.m_type = QColorTrc::Type::Function;
+ gamma.m_type = QColorTrc::Type::ParameterizedFunction;
gamma.m_fun = QColorTransferFunction(); // Linear
} else if (curv.valueCount == 1) {
const quint16 v = qFromBigEndian<quint16>(tagData.constData() + valueOffset);
- gamma.m_type = QColorTrc::Type::Function;
+ gamma.m_type = QColorTrc::Type::ParameterizedFunction;
gamma.m_fun = QColorTransferFunction::fromGamma(v * (1.0f / 256.0f));
} else {
QList<quint16> tabl;
@@ -1084,7 +1126,7 @@ static quint32 parseTRC(const QByteArrayView &tagData, QColorTrc &gamma, QColorT
gamma.m_table = table;
} else {
qCDebug(lcIcc) << "Detected curv table as function";
- gamma.m_type = QColorTrc::Type::Function;
+ gamma.m_type = QColorTrc::Type::ParameterizedFunction;
gamma.m_fun = curve;
}
}
@@ -1101,7 +1143,7 @@ static quint32 parseTRC(const QByteArrayView &tagData, QColorTrc &gamma, QColorT
return 0;
qFromBigEndian<quint32>(tagData.constData() + parametersOffset, 1, parameters);
float g = fromFixedS1516(parameters[0]);
- gamma.m_type = QColorTrc::Type::Function;
+ gamma.m_type = QColorTrc::Type::ParameterizedFunction;
gamma.m_fun = QColorTransferFunction::fromGamma(g);
return 12 + 1 * 4;
}
@@ -1115,7 +1157,7 @@ static quint32 parseTRC(const QByteArrayView &tagData, QColorTrc &gamma, QColorT
float a = fromFixedS1516(parameters[1]);
float b = fromFixedS1516(parameters[2]);
float d = -b / a;
- gamma.m_type = QColorTrc::Type::Function;
+ gamma.m_type = QColorTrc::Type::ParameterizedFunction;
gamma.m_fun = QColorTransferFunction(a, b, 0.0f, d, 0.0f, 0.0f, g);
return 12 + 3 * 4;
}
@@ -1130,7 +1172,7 @@ static quint32 parseTRC(const QByteArrayView &tagData, QColorTrc &gamma, QColorT
float b = fromFixedS1516(parameters[2]);
float c = fromFixedS1516(parameters[3]);
float d = -b / a;
- gamma.m_type = QColorTrc::Type::Function;
+ gamma.m_type = QColorTrc::Type::ParameterizedFunction;
gamma.m_fun = QColorTransferFunction(a, b, 0.0f, d, c, c, g);
return 12 + 4 * 4;
}
@@ -1143,7 +1185,7 @@ static quint32 parseTRC(const QByteArrayView &tagData, QColorTrc &gamma, QColorT
float b = fromFixedS1516(parameters[2]);
float c = fromFixedS1516(parameters[3]);
float d = fromFixedS1516(parameters[4]);
- gamma.m_type = QColorTrc::Type::Function;
+ gamma.m_type = QColorTrc::Type::ParameterizedFunction;
gamma.m_fun = QColorTransferFunction(a, b, c, d, 0.0f, 0.0f, g);
return 12 + 5 * 4;
}
@@ -1158,7 +1200,7 @@ static quint32 parseTRC(const QByteArrayView &tagData, QColorTrc &gamma, QColorT
float d = fromFixedS1516(parameters[4]);
float e = fromFixedS1516(parameters[5]);
float f = fromFixedS1516(parameters[6]);
- gamma.m_type = QColorTrc::Type::Function;
+ gamma.m_type = QColorTrc::Type::ParameterizedFunction;
gamma.m_fun = QColorTransferFunction(a, b, c, d, e, f, g);
return 12 + 7 * 4;
}
@@ -1629,10 +1671,12 @@ static bool parseRgbMatrix(const QByteArray &data, const QHash<Tag, TagEntry> &t
} else if (colorspaceDPtr->toXyz == QColorMatrix::toXyzFromDciP3D65()) {
qCDebug(lcIcc) << "fromIccProfile: DCI-P3 D65 primaries detected";
colorspaceDPtr->primaries = QColorSpace::Primaries::DciP3D65;
- }
- if (colorspaceDPtr->toXyz == QColorMatrix::toXyzFromProPhotoRgb()) {
+ } else if (colorspaceDPtr->toXyz == QColorMatrix::toXyzFromProPhotoRgb()) {
qCDebug(lcIcc) << "fromIccProfile: ProPhoto RGB primaries detected";
colorspaceDPtr->primaries = QColorSpace::Primaries::ProPhotoRgb;
+ } else if (colorspaceDPtr->toXyz == QColorMatrix::toXyzFromBt2020()) {
+ qCDebug(lcIcc) << "fromIccProfile: BT.2020 primaries detected";
+ colorspaceDPtr->primaries = QColorSpace::Primaries::Bt2020;
}
return true;
}
@@ -1720,12 +1764,12 @@ static bool parseTRCs(const QByteArray &data, const QHash<Tag, TagEntry> &tagInd
colorspaceDPtr->trc[0] = QColorTransferFunction();
colorspaceDPtr->transferFunction = QColorSpace::TransferFunction::Linear;
colorspaceDPtr->gamma = 1.0f;
- } else if (rCurve.m_type == QColorTrc::Type::Function && rCurve.m_fun.isGamma()) {
+ } else if (rCurve.m_type == QColorTrc::Type::ParameterizedFunction && rCurve.m_fun.isGamma()) {
qCDebug(lcIcc) << "fromIccProfile: Simple gamma detected";
colorspaceDPtr->trc[0] = QColorTransferFunction::fromGamma(rCurve.m_fun.m_g);
colorspaceDPtr->transferFunction = QColorSpace::TransferFunction::Gamma;
colorspaceDPtr->gamma = rCurve.m_fun.m_g;
- } else if (rCurve.m_type == QColorTrc::Type::Function && rCurve.m_fun.isSRgb()) {
+ } else if (rCurve.m_type == QColorTrc::Type::ParameterizedFunction && rCurve.m_fun.isSRgb()) {
qCDebug(lcIcc) << "fromIccProfile: sRGB gamma detected";
colorspaceDPtr->trc[0] = QColorTransferFunction::fromSRgb();
colorspaceDPtr->transferFunction = QColorSpace::TransferFunction::SRgb;
@@ -1744,6 +1788,63 @@ static bool parseTRCs(const QByteArray &data, const QHash<Tag, TagEntry> &tagInd
return true;
}
+static bool parseCicp(const QByteArray &data, const TagEntry &tagEntry, QColorSpacePrivate *colorspaceDPtr)
+{
+ if (colorspaceDPtr->isPcsLab)
+ return false;
+ if (tagEntry.size < sizeof(CicpTagData) || qsizetype(tagEntry.size) > data.size())
+ return false;
+ const CicpTagData cicp = qFromUnaligned<CicpTagData>(data.constData() + tagEntry.offset);
+ if (cicp.type != uint32_t(Tag::cicp)) {
+ qCWarning(lcIcc, "fromIccProfile: bad cicp data type");
+ return false;
+ }
+ switch (cicp.transferCharacteristics) {
+ case 4:
+ colorspaceDPtr->transferFunction = QColorSpace::TransferFunction::Gamma;
+ colorspaceDPtr->gamma = 2.2f;
+ break;
+ case 5:
+ colorspaceDPtr->transferFunction = QColorSpace::TransferFunction::Gamma;
+ colorspaceDPtr->gamma = 2.8f;
+ break;
+ case 8:
+ colorspaceDPtr->transferFunction = QColorSpace::TransferFunction::Linear;
+ break;
+ case 13:
+ colorspaceDPtr->transferFunction = QColorSpace::TransferFunction::SRgb;
+ break;
+ case 1:
+ case 6:
+ case 14:
+ case 15:
+ colorspaceDPtr->transferFunction = QColorSpace::TransferFunction::Bt2020;
+ break;
+ case 16:
+ colorspaceDPtr->transferFunction = QColorSpace::TransferFunction::St2084;
+ break;
+ case 18:
+ colorspaceDPtr->transferFunction = QColorSpace::TransferFunction::Hlg;
+ break;
+ default:
+ return false;
+ }
+ switch (cicp.colorPrimaries) {
+ case 1:
+ colorspaceDPtr->primaries = QColorSpace::Primaries::SRgb;
+ break;
+ case 9:
+ colorspaceDPtr->primaries = QColorSpace::Primaries::Bt2020;
+ break;
+ case 12:
+ colorspaceDPtr->primaries = QColorSpace::Primaries::DciP3D65;
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
bool fromIccProfile(const QByteArray &data, QColorSpace *colorSpace)
{
if (data.size() < qsizetype(sizeof(ICCProfileHeader))) {
@@ -1832,12 +1933,22 @@ bool fromIccProfile(const QByteArray &data, QColorSpace *colorSpace)
colorSpace->detach();
QColorSpacePrivate *colorspaceDPtr = QColorSpacePrivate::get(*colorSpace);
+ colorspaceDPtr->isPcsLab = (header.pcs == uint(Tag::Lab_));
+ if (tagIndex.contains(Tag::cicp)) {
+ // Let cicp override nLut profiles if we fully recognize it.
+ if (parseCicp(data, tagIndex[Tag::cicp], colorspaceDPtr))
+ threeComponentMatrix = true;
+ if (colorspaceDPtr->primaries != QColorSpace::Primaries::Custom)
+ colorspaceDPtr->setToXyzMatrix();
+ if (colorspaceDPtr->transferFunction != QColorSpace::TransferFunction::Custom)
+ colorspaceDPtr->setTransferFunction();
+ }
+
if (threeComponentMatrix) {
- colorspaceDPtr->isPcsLab = false;
colorspaceDPtr->transformModel = QColorSpace::TransformModel::ThreeComponentMatrix;
if (header.inputColorSpace == uint(ColorSpaceType::Rgb)) {
- if (!parseRgbMatrix(data, tagIndex, colorspaceDPtr))
+ if (colorspaceDPtr->primaries == QColorSpace::Primaries::Custom && !parseRgbMatrix(data, tagIndex, colorspaceDPtr))
return false;
colorspaceDPtr->colorModel = QColorSpace::ColorModel::Rgb;
} else if (header.inputColorSpace == uint(ColorSpaceType::Gray)) {
@@ -1860,10 +1971,10 @@ bool fromIccProfile(const QByteArray &data, QColorSpace *colorSpace)
if (colorspaceDPtr->primaries != QColorSpace::Primaries::Custom)
colorspaceDPtr->setToXyzMatrix();
- if (!parseTRCs(data, tagIndex, colorspaceDPtr, header.inputColorSpace == uint(ColorSpaceType::Gray)))
+ if (colorspaceDPtr->transferFunction == QColorSpace::TransferFunction::Custom &&
+ !parseTRCs(data, tagIndex, colorspaceDPtr, header.inputColorSpace == uint(ColorSpaceType::Gray)))
return false;
} else {
- colorspaceDPtr->isPcsLab = (header.pcs == uint(Tag::Lab_));
colorspaceDPtr->transformModel = QColorSpace::TransformModel::ElementListProcessing;
if (header.inputColorSpace == uint(ColorSpaceType::Cmyk))
colorspaceDPtr->colorModel = QColorSpace::ColorModel::Cmyk;
diff --git a/tests/auto/gui/painting/qcolorspace/resources/Rec. ITU-R BT.2100 PQ.icc b/tests/auto/gui/painting/qcolorspace/resources/Rec. ITU-R BT.2100 PQ.icc
new file mode 100644
index 00000000000..e0ceaa12f80
--- /dev/null
+++ b/tests/auto/gui/painting/qcolorspace/resources/Rec. ITU-R BT.2100 PQ.icc
Binary files differ
diff --git a/tests/auto/gui/painting/qcolorspace/tst_qcolorspace.cpp b/tests/auto/gui/painting/qcolorspace/tst_qcolorspace.cpp
index 67480c721d7..6c103ce4f30 100644
--- a/tests/auto/gui/painting/qcolorspace/tst_qcolorspace.cpp
+++ b/tests/auto/gui/painting/qcolorspace/tst_qcolorspace.cpp
@@ -123,6 +123,15 @@ void tst_QColorSpace::namedColorSpaces_data()
QTest::newRow("ProPhoto RGB") << QColorSpace::ProPhotoRgb << true
<< QColorSpace::Primaries::ProPhotoRgb
<< QColorSpace::TransferFunction::ProPhotoRgb;
+ QTest::newRow("BT.2020") << QColorSpace::Bt2020 << true
+ << QColorSpace::Primaries::Bt2020
+ << QColorSpace::TransferFunction::Bt2020;
+ QTest::newRow("BT.2100 PQ") << QColorSpace::Bt2100Pq << true
+ << QColorSpace::Primaries::Bt2020
+ << QColorSpace::TransferFunction::St2084;
+ QTest::newRow("BT.2100 HLG") << QColorSpace::Bt2100Hlg << true
+ << QColorSpace::Primaries::Bt2020
+ << QColorSpace::TransferFunction::Hlg;
QTest::newRow("0") << QColorSpace::NamedColorSpace(0)
<< false
<< QColorSpace::Primaries::Custom
@@ -225,6 +234,11 @@ void tst_QColorSpace::fromIccProfile_data()
<< QColorSpace::TransferFunction::Custom
<< QColorSpace::TransformModel::ElementListProcessing
<< QColorSpace::ColorModel::Cmyk << QString("uCMY");
+ // BT.2100 PQ profile
+ QTest::newRow("BT.2100 PQ") << prefix + "Rec. ITU-R BT.2100 PQ.icc" << QColorSpace::Bt2100Pq
+ << QColorSpace::TransferFunction::St2084
+ << QColorSpace::TransformModel::ThreeComponentMatrix
+ << QColorSpace::ColorModel::Rgb << QString("Rec. ITU-R BT.2100 PQ");
}
void tst_QColorSpace::fromIccProfile()