Add colorspace transfer functions based on tables of inputs
This is the most basic way to represent custom transfer functions. Change-Id: I529fb647ece82c03e85ef77b056d9daf13fe5a61 Reviewed-by: Morten Johan Sørvig <morten.sorvig@qt.io> Reviewed-by: Eirik Aavitsland <eirik.aavitsland@qt.io>bb10
parent
667e5b1210
commit
48346e8d2d
|
|
@ -1,6 +1,6 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2018 The Qt Company Ltd.
|
||||
** Copyright (C) 2020 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtGui module of the Qt Toolkit.
|
||||
|
|
@ -221,6 +221,29 @@ QColorSpacePrivate::QColorSpacePrivate(const QColorSpacePrimaries &primaries,
|
|||
setTransferFunction();
|
||||
}
|
||||
|
||||
QColorSpacePrivate::QColorSpacePrivate(QColorSpace::Primaries primaries, const QVector<uint16_t> &transferFunctionTable)
|
||||
: primaries(primaries)
|
||||
, transferFunction(QColorSpace::TransferFunction::Custom)
|
||||
, gamma(0)
|
||||
{
|
||||
setTransferFunctionTable(transferFunctionTable);
|
||||
identifyColorSpace();
|
||||
initialize();
|
||||
}
|
||||
|
||||
QColorSpacePrivate::QColorSpacePrivate(const QColorSpacePrimaries &primaries, const QVector<uint16_t> &transferFunctionTable)
|
||||
: primaries(QColorSpace::Primaries::Custom)
|
||||
, transferFunction(QColorSpace::TransferFunction::Custom)
|
||||
, gamma(0)
|
||||
{
|
||||
Q_ASSERT(primaries.areValid());
|
||||
toXyz = primaries.toXyzMatrix();
|
||||
whitePoint = QColorVector(primaries.whitePoint);
|
||||
setTransferFunctionTable(transferFunctionTable);
|
||||
identifyColorSpace();
|
||||
initialize();
|
||||
}
|
||||
|
||||
void QColorSpacePrivate::identifyColorSpace()
|
||||
{
|
||||
switch (primaries) {
|
||||
|
|
@ -298,6 +321,32 @@ void QColorSpacePrivate::setToXyzMatrix()
|
|||
whitePoint = QColorVector(colorSpacePrimaries.whitePoint);
|
||||
}
|
||||
|
||||
void QColorSpacePrivate::setTransferFunctionTable(const QVector<uint16_t> &transferFunctionTable)
|
||||
{
|
||||
QColorTransferTable table(transferFunctionTable.size(), transferFunctionTable);
|
||||
if (!table.isEmpty() && !table.checkValidity()) {
|
||||
qWarning() << "Invalid transfer function table given to QColorSpace";
|
||||
trc[0].m_type = QColorTrc::Type::Uninitialized;
|
||||
return;
|
||||
}
|
||||
transferFunction = QColorSpace::TransferFunction::Custom;
|
||||
QColorTransferFunction curve;
|
||||
if (table.asColorTransferFunction(&curve)) {
|
||||
// Table recognized as a specific curve
|
||||
if (curve.isLinear()) {
|
||||
transferFunction = QColorSpace::TransferFunction::Linear;
|
||||
gamma = 1.0f;
|
||||
} else if (curve.isSRgb()) {
|
||||
transferFunction = QColorSpace::TransferFunction::SRgb;
|
||||
}
|
||||
trc[0].m_type = QColorTrc::Type::Function;
|
||||
trc[0].m_fun = curve;
|
||||
} else {
|
||||
trc[0].m_type = QColorTrc::Type::Table;
|
||||
trc[0].m_table = table;
|
||||
}
|
||||
}
|
||||
|
||||
void QColorSpacePrivate::setTransferFunction()
|
||||
{
|
||||
switch (transferFunction) {
|
||||
|
|
@ -470,6 +519,20 @@ QColorSpace::QColorSpace(QColorSpace::Primaries primaries, float gamma)
|
|||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
Creates a custom color space with the primaries \a primaries, using a custom transfer function
|
||||
described by \a transferFunctionTable.
|
||||
|
||||
The table should contain at least 2 values, and contain an monotonically increasing list
|
||||
of values from 0 to 65535.
|
||||
|
||||
\since 6.1
|
||||
*/
|
||||
QColorSpace::QColorSpace(QColorSpace::Primaries gamut, const QVector<uint16_t> &transferFunctionTable)
|
||||
: d_ptr(new QColorSpacePrivate(gamut, transferFunctionTable))
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
Creates a custom colorspace with a primaries based on the chromaticities of the primary colors \a whitePoint,
|
||||
\a redPoint, \a greenPoint and \a bluePoint, and using the transfer function \a transferFunction and optionally \a gamma.
|
||||
|
|
@ -486,6 +549,20 @@ QColorSpace::QColorSpace(const QPointF &whitePoint, const QPointF &redPoint,
|
|||
d_ptr = new QColorSpacePrivate(primaries, transferFunction, gamma);
|
||||
}
|
||||
|
||||
/*!
|
||||
Creates a custom color space with primaries based on the chromaticities of the primary colors \a whitePoint,
|
||||
\a redPoint, \a greenPoint and \a bluePoint, and using the custom transfer function described by
|
||||
\a transferFunctionTable.
|
||||
|
||||
\since 6.1
|
||||
*/
|
||||
QColorSpace::QColorSpace(const QPointF &whitePoint, const QPointF &redPoint,
|
||||
const QPointF &greenPoint, const QPointF &bluePoint,
|
||||
const QVector<uint16_t> &transferFunctionTable)
|
||||
: d_ptr(new QColorSpacePrivate({whitePoint, redPoint, greenPoint, bluePoint}, transferFunctionTable))
|
||||
{
|
||||
}
|
||||
|
||||
QColorSpace::~QColorSpace() = default;
|
||||
|
||||
QT_DEFINE_QESDP_SPECIALIZATION_DTOR(QColorSpacePrivate)
|
||||
|
|
@ -559,6 +636,27 @@ void QColorSpace::setTransferFunction(QColorSpace::TransferFunction transferFunc
|
|||
d_ptr->setTransferFunction();
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the transfer function to \a transferFunctionTable.
|
||||
|
||||
\since 6.1
|
||||
\sa transferFunction()
|
||||
*/
|
||||
void QColorSpace::setTransferFunction(const QVector<uint16_t> &transferFunctionTable)
|
||||
{
|
||||
if (!d_ptr) {
|
||||
d_ptr = new QColorSpacePrivate(Primaries::Custom, transferFunctionTable);
|
||||
d_ptr->ref.ref();
|
||||
return;
|
||||
}
|
||||
detach();
|
||||
d_ptr->description.clear();
|
||||
d_ptr->setTransferFunctionTable(transferFunctionTable);
|
||||
d_ptr->gamma = 0;
|
||||
d_ptr->identifyColorSpace();
|
||||
d_ptr->setTransferFunction();
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a copy of this color space, except using the transfer function
|
||||
\a transferFunction and \a gamma.
|
||||
|
|
@ -576,6 +674,22 @@ QColorSpace QColorSpace::withTransferFunction(QColorSpace::TransferFunction tran
|
|||
return out;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a copy of this color space, except using the transfer function
|
||||
described by \a transferFunctionTable.
|
||||
|
||||
\since 6.1
|
||||
\sa transferFunction(), setTransferFunction()
|
||||
*/
|
||||
QColorSpace QColorSpace::withTransferFunction(const QVector<uint16_t> &transferFunctionTable) const
|
||||
{
|
||||
if (!isValid())
|
||||
return *this;
|
||||
QColorSpace out(*this);
|
||||
out.setTransferFunction(transferFunctionTable);
|
||||
return out;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the primaries to those of the \a primariesId set.
|
||||
|
||||
|
|
|
|||
|
|
@ -86,9 +86,13 @@ public:
|
|||
QColorSpace(NamedColorSpace namedColorSpace);
|
||||
QColorSpace(Primaries primaries, TransferFunction transferFunction, float gamma = 0.0f);
|
||||
QColorSpace(Primaries primaries, float gamma);
|
||||
QColorSpace(Primaries primaries, const QVector<uint16_t> &transferFunctionTable);
|
||||
QColorSpace(const QPointF &whitePoint, const QPointF &redPoint,
|
||||
const QPointF &greenPoint, const QPointF &bluePoint,
|
||||
TransferFunction transferFunction, float gamma = 0.0f);
|
||||
QColorSpace(const QPointF &whitePoint, const QPointF &redPoint,
|
||||
const QPointF &greenPoint, const QPointF &bluePoint,
|
||||
const QVector<uint16_t> &transferFunctionTable);
|
||||
~QColorSpace();
|
||||
|
||||
QColorSpace(const QColorSpace &colorSpace) noexcept;
|
||||
|
|
@ -110,7 +114,9 @@ public:
|
|||
float gamma() const noexcept;
|
||||
|
||||
void setTransferFunction(TransferFunction transferFunction, float gamma = 0.0f);
|
||||
void setTransferFunction(const QVector<uint16_t> &transferFunctionTable);
|
||||
QColorSpace withTransferFunction(TransferFunction transferFunction, float gamma = 0.0f) const;
|
||||
QColorSpace withTransferFunction(const QVector<uint16_t> &transferFunctionTable) const;
|
||||
|
||||
void setPrimaries(Primaries primariesId);
|
||||
void setPrimaries(const QPointF &whitePoint, const QPointF &redPoint,
|
||||
|
|
|
|||
|
|
@ -92,7 +92,9 @@ public:
|
|||
QColorSpacePrivate();
|
||||
QColorSpacePrivate(QColorSpace::NamedColorSpace namedColorSpace);
|
||||
QColorSpacePrivate(QColorSpace::Primaries primaries, QColorSpace::TransferFunction transferFunction, float gamma);
|
||||
QColorSpacePrivate(QColorSpace::Primaries primaries, const QVector<uint16_t> &transferFunctionTable);
|
||||
QColorSpacePrivate(const QColorSpacePrimaries &primaries, QColorSpace::TransferFunction transferFunction, float gamma);
|
||||
QColorSpacePrivate(const QColorSpacePrimaries &primaries, const QVector<uint16_t> &transferFunctionTable);
|
||||
QColorSpacePrivate(const QColorSpacePrivate &other) = default;
|
||||
|
||||
static const QColorSpacePrivate *get(const QColorSpace &colorSpace)
|
||||
|
|
@ -109,6 +111,7 @@ public:
|
|||
void setToXyzMatrix();
|
||||
void setTransferFunction();
|
||||
void identifyColorSpace();
|
||||
void setTransferFunctionTable(const QVector<uint16_t> &transferFunctionTable);
|
||||
QColorTransform transformationToColorSpace(const QColorSpacePrivate *out) const;
|
||||
|
||||
static constexpr QColorSpace::NamedColorSpace Unknown = QColorSpace::NamedColorSpace(0);
|
||||
|
|
|
|||
|
|
@ -55,6 +55,8 @@
|
|||
#include "qcolortransferfunction_p.h"
|
||||
|
||||
#include <QList>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
|
@ -114,11 +116,11 @@ public:
|
|||
|
||||
float apply(float x) const
|
||||
{
|
||||
x = std::min(std::max(x, 0.0f), 1.0f);
|
||||
x = std::clamp(x, 0.0f, 1.0f);
|
||||
x *= m_tableSize - 1;
|
||||
uint32_t lo = static_cast<uint32_t>(std::floor(x));
|
||||
uint32_t hi = std::min(lo + 1, m_tableSize - 1);
|
||||
float frac = x - lo;
|
||||
const uint32_t lo = static_cast<uint32_t>(std::floor(x));
|
||||
const uint32_t hi = std::min(lo + 1, m_tableSize);
|
||||
const float frac = x - lo;
|
||||
if (!m_table16.isEmpty())
|
||||
return (m_table16[lo] * (1.0f - frac) + m_table16[hi] * frac) * (1.0f/65535.0f);
|
||||
if (!m_table8.isEmpty())
|
||||
|
|
@ -135,34 +137,30 @@ public:
|
|||
if (x >= 1.0f)
|
||||
return 1.0f;
|
||||
if (!m_table16.isEmpty()) {
|
||||
float v = x * 65535.0f;
|
||||
uint32_t i = std::floor(resultLargerThan * (m_tableSize - 1)) + 1;
|
||||
for ( ; i < m_tableSize; ++i) {
|
||||
if (m_table16[i] > v)
|
||||
break;
|
||||
}
|
||||
const float v = x * 65535.0f;
|
||||
uint32_t i = static_cast<uint32_t>(std::floor(resultLargerThan * (m_tableSize - 1))) + 1;
|
||||
auto it = std::lower_bound(m_table16.cbegin() + i, m_table16.cend(), v);
|
||||
i = it - m_table16.cbegin();
|
||||
if (i >= m_tableSize - 1)
|
||||
return 1.0f;
|
||||
float y1 = m_table16[i - 1];
|
||||
float y2 = m_table16[i];
|
||||
const float y1 = m_table16[i - 1];
|
||||
const float y2 = m_table16[i];
|
||||
Q_ASSERT(x >= y1 && x < y2);
|
||||
float fr = (v - y1) / (y2 - y1);
|
||||
const float fr = (v - y1) / (y2 - y1);
|
||||
return (i + fr) * (1.0f / (m_tableSize - 1));
|
||||
|
||||
}
|
||||
if (!m_table8.isEmpty()) {
|
||||
float v = x * 255.0f;
|
||||
uint32_t i = std::floor(resultLargerThan * (m_tableSize - 1)) + 1;
|
||||
for ( ; i < m_tableSize; ++i) {
|
||||
if (m_table8[i] > v)
|
||||
break;
|
||||
}
|
||||
const float v = x * 255.0f;
|
||||
uint32_t i = static_cast<uint32_t>(std::floor(resultLargerThan * (m_tableSize - 1))) + 1;
|
||||
auto it = std::lower_bound(m_table8.cbegin() + i, m_table8.cend(), v);
|
||||
i = it - m_table8.cbegin();
|
||||
if (i >= m_tableSize - 1)
|
||||
return 1.0f;
|
||||
float y1 = m_table8[i - 1];
|
||||
float y2 = m_table8[i];
|
||||
const float y1 = m_table8[i - 1];
|
||||
const float y2 = m_table8[i];
|
||||
Q_ASSERT(x >= y1 && x < y2);
|
||||
float fr = (v - y1) / (y2 - y1);
|
||||
const float fr = (v - y1) / (y2 - y1);
|
||||
return (i + fr) * (1.0f / (m_tableSize - 1));
|
||||
}
|
||||
return x;
|
||||
|
|
|
|||
|
|
@ -74,6 +74,8 @@ private slots:
|
|||
|
||||
void changeTransferFunction();
|
||||
void changePrimaries();
|
||||
|
||||
void transferFunctionTable();
|
||||
};
|
||||
|
||||
tst_QColorSpace::tst_QColorSpace()
|
||||
|
|
@ -468,5 +470,32 @@ void tst_QColorSpace::changePrimaries()
|
|||
QCOMPARE(cs, QColorSpace(QColorSpace::SRgbLinear));
|
||||
}
|
||||
|
||||
void tst_QColorSpace::transferFunctionTable()
|
||||
{
|
||||
QVector<quint16> linearTable = { 0, 65535 };
|
||||
|
||||
// Check linearSRgb is recognized
|
||||
QColorSpace linearSRgb(QColorSpace::Primaries::SRgb, linearTable);
|
||||
QCOMPARE(linearSRgb.primaries(), QColorSpace::Primaries::SRgb);
|
||||
QCOMPARE(linearSRgb.transferFunction(), QColorSpace::TransferFunction::Linear);
|
||||
QCOMPARE(linearSRgb.gamma(), 1.0);
|
||||
QCOMPARE(linearSRgb, QColorSpace::SRgbLinear);
|
||||
|
||||
// Check other linear is recognized
|
||||
QColorSpace linearARgb(QColorSpace::Primaries::AdobeRgb, linearTable);
|
||||
QCOMPARE(linearARgb.primaries(), QColorSpace::Primaries::AdobeRgb);
|
||||
QCOMPARE(linearARgb.transferFunction(), QColorSpace::TransferFunction::Linear);
|
||||
QCOMPARE(linearARgb.gamma(), 1.0);
|
||||
|
||||
// Check custom transfer function.
|
||||
QVector<quint16> customTable = { 0, 10, 100, 10000, 65535 };
|
||||
QColorSpace customSRgb(QColorSpace::Primaries::SRgb, customTable);
|
||||
QCOMPARE(customSRgb.primaries(), QColorSpace::Primaries::SRgb);
|
||||
QCOMPARE(customSRgb.transferFunction(), QColorSpace::TransferFunction::Custom);
|
||||
|
||||
customSRgb.setTransferFunction(linearTable);
|
||||
QCOMPARE(customSRgb, QColorSpace::SRgbLinear);
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QColorSpace)
|
||||
#include "tst_qcolorspace.moc"
|
||||
|
|
|
|||
Loading…
Reference in New Issue