summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2019-04-15 14:19:48 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2020-12-10 11:03:12 +0100
commit48346e8d2df287dd4b7e6d51de491c3bd3020255 (patch)
treeb2db9b6ebd8ded1e239124cd40e30961783193b2
parent667e5b1210cf8783c5b16445a09f30d14bc83c0f (diff)
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>
-rw-r--r--src/gui/painting/qcolorspace.cpp116
-rw-r--r--src/gui/painting/qcolorspace.h6
-rw-r--r--src/gui/painting/qcolorspace_p.h3
-rw-r--r--src/gui/painting/qcolortransfertable_p.h42
-rw-r--r--tests/auto/gui/painting/qcolorspace/tst_qcolorspace.cpp29
5 files changed, 173 insertions, 23 deletions
diff --git a/src/gui/painting/qcolorspace.cpp b/src/gui/painting/qcolorspace.cpp
index ed25ead262..0a87c35400 100644
--- a/src/gui/painting/qcolorspace.cpp
+++ b/src/gui/painting/qcolorspace.cpp
@@ -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) {
@@ -471,6 +520,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)
@@ -560,6 +637,27 @@ void QColorSpace::setTransferFunction(QColorSpace::TransferFunction transferFunc
}
/*!
+ 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.
@@ -577,6 +675,22 @@ QColorSpace QColorSpace::withTransferFunction(QColorSpace::TransferFunction tran
}
/*!
+ 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.
\sa primaries()
diff --git a/src/gui/painting/qcolorspace.h b/src/gui/painting/qcolorspace.h
index a53a786233..2d4de4c9a8 100644
--- a/src/gui/painting/qcolorspace.h
+++ b/src/gui/painting/qcolorspace.h
@@ -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,
diff --git a/src/gui/painting/qcolorspace_p.h b/src/gui/painting/qcolorspace_p.h
index da2d3a9c30..9dbedd9e05 100644
--- a/src/gui/painting/qcolorspace_p.h
+++ b/src/gui/painting/qcolorspace_p.h
@@ -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);
diff --git a/src/gui/painting/qcolortransfertable_p.h b/src/gui/painting/qcolortransfertable_p.h
index fdf68b78da..2812ed5685 100644
--- a/src/gui/painting/qcolortransfertable_p.h
+++ b/src/gui/painting/qcolortransfertable_p.h
@@ -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;
diff --git a/tests/auto/gui/painting/qcolorspace/tst_qcolorspace.cpp b/tests/auto/gui/painting/qcolorspace/tst_qcolorspace.cpp
index 3564700fe5..4acbfbba74 100644
--- a/tests/auto/gui/painting/qcolorspace/tst_qcolorspace.cpp
+++ b/tests/auto/gui/painting/qcolorspace/tst_qcolorspace.cpp
@@ -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"