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 <laszlo.agocs@qt.io>
This commit is contained in:
parent
53ad7b2d9b
commit
ce33e47667
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
109
src/gui/painting/qcolortransfergeneric_p.h
Normal file
109
src/gui/painting/qcolortransfergeneric_p.h
Normal file
@ -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
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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,51 +15,49 @@ 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)
|
||||
{
|
||||
auto cp = create();
|
||||
cp->setFromTransferTable(table, dir);
|
||||
return cp;
|
||||
}
|
||||
|
||||
void QColorTrcLut::setFromGamma(qreal gamma, Direction dir)
|
||||
void QColorTrcLut::setFromGamma(float gamma, 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, 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(qPow(i / qreal(Resolution), qreal(1) / gamma) * (255 * 256)));
|
||||
m_fromLinear[i] = ushort(qRound(qBound(0.f, qPow(i * iRes, iGamma), 1.f) * (255 * 256)));
|
||||
}
|
||||
}
|
||||
|
||||
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(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) {
|
||||
@ -65,17 +65,36 @@ void QColorTrcLut::setFromTransferFunction(const QColorTransferFunction &fun, Di
|
||||
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, inv.apply(i * iRes), 1.f) * (255 * 256)));
|
||||
}
|
||||
}
|
||||
|
||||
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(qBound(0.f, fun.apply(i * iRes), 1.f) * (255 * 256)));
|
||||
}
|
||||
|
||||
if (dir & FromLinear) {
|
||||
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, 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
|
||||
|
@ -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:
|
||||
|
||||
|
@ -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;
|
||||
|
Binary file not shown.
@ -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()
|
||||
|
Loading…
x
Reference in New Issue
Block a user