colors/colordevice: make channel factors linear

The redshift table is in gamma 2.2 encoding and not linear, which means
that it only yields correct results with 1.0 pixel values. It also means
that when it's being applied in linear space in the color management shaders,
the result is quite wrong.

To fix that, this commit makes the channel factors linear and the backend
calculates the nonlinear factors where needed.
wilder/Plasma/6.2
Xaver Hugl 2 years ago
parent 788c186701
commit 36bec2d941
  1. 9
      src/backends/drm/drm_output.cpp
  2. 2
      src/backends/x11/standalone/x11_standalone_output.cpp
  3. 3
      src/colors/colordevice.cpp
  4. 51
      src/core/colorspace.cpp
  5. 2
      src/core/colorspace.h

@ -437,11 +437,12 @@ bool DrmOutput::doSetChannelFactors(const QVector3D &rgb)
if (!m_pipeline->activePending()) {
return false;
}
const auto inGamma22 = ColorDescription::nitsToEncoded(rgb, NamedTransferFunction::gamma22, 1);
if (m_pipeline->hasCTM()) {
QMatrix3x3 ctm;
ctm(0, 0) = rgb.x();
ctm(1, 1) = rgb.y();
ctm(2, 2) = rgb.z();
ctm(0, 0) = inGamma22.x();
ctm(1, 1) = inGamma22.y();
ctm(2, 2) = inGamma22.z();
m_pipeline->setCTM(ctm);
m_pipeline->setGammaRamp(nullptr);
if (DrmPipeline::commitPipelines({m_pipeline}, DrmPipeline::CommitMode::Test) == DrmPipeline::Error::None) {
@ -454,7 +455,7 @@ bool DrmOutput::doSetChannelFactors(const QVector3D &rgb)
}
}
if (m_pipeline->hasGammaRamp()) {
auto lut = ColorTransformation::createScalingTransform(rgb);
auto lut = ColorTransformation::createScalingTransform(inGamma22);
if (lut) {
m_pipeline->setGammaRamp(std::move(lut));
if (DrmPipeline::commitPipelines({m_pipeline}, DrmPipeline::CommitMode::Test) == DrmPipeline::Error::None) {

@ -46,7 +46,7 @@ bool X11Output::setChannelFactors(const QVector3D &rgb)
if (m_crtc == XCB_NONE) {
return true;
}
auto transformation = ColorTransformation::createScalingTransform(rgb);
auto transformation = ColorTransformation::createScalingTransform(ColorDescription::nitsToEncoded(rgb, NamedTransferFunction::gamma22, 1));
if (!transformation) {
return false;
}

@ -62,7 +62,8 @@ void ColorDevicePrivate::recalculateFactors()
const qreal zWhitePoint = interpolate(blackbodyColor[blackBodyColorIndex + 2],
blackbodyColor[blackBodyColorIndex + 5],
blendFactor);
temperatureFactors = QVector3D(xWhitePoint, yWhitePoint, zWhitePoint);
// the values in the blackbodyColor array are "gamma corrected", but we need a linear value
temperatureFactors = ColorDescription::encodedToNits(QVector3D(xWhitePoint, yWhitePoint, zWhitePoint), NamedTransferFunction::gamma22, 1);
}
simpleTransformation = brightnessFactors * temperatureFactors;
}

@ -344,34 +344,34 @@ static QVector3D clamp(const QVector3D &vect, float min = 0, float max = 1)
return QVector3D(std::clamp(vect.x(), min, max), std::clamp(vect.y(), min, max), std::clamp(vect.z(), min, max));
}
QVector3D ColorDescription::mapTo(QVector3D rgb, const ColorDescription &dst) const
QVector3D ColorDescription::encodedToNits(const QVector3D &nits, NamedTransferFunction tf, double sdrBrightness)
{
// transfer function -> nits
switch (m_transferFunction) {
switch (tf) {
case NamedTransferFunction::sRGB:
rgb = m_sdrBrightness * QVector3D(srgbToLinear(rgb.x()), srgbToLinear(rgb.y()), srgbToLinear(rgb.z()));
break;
return sdrBrightness * QVector3D(srgbToLinear(nits.x()), srgbToLinear(nits.y()), srgbToLinear(nits.z()));
case NamedTransferFunction::gamma22:
rgb = m_sdrBrightness * QVector3D(std::pow(rgb.x(), 2.2), std::pow(rgb.y(), 2.2), std::pow(rgb.z(), 2.2));
break;
return sdrBrightness * QVector3D(std::pow(nits.x(), 2.2), std::pow(nits.y(), 2.2), std::pow(nits.z(), 2.2));
case NamedTransferFunction::linear:
break;
return nits;
case NamedTransferFunction::scRGB:
rgb *= 80.0f;
break;
return nits * 80.0f;
case NamedTransferFunction::PerceptualQuantizer:
rgb = QVector3D(pqToNits(rgb.x()), pqToNits(rgb.y()), pqToNits(rgb.z()));
break;
return QVector3D(pqToNits(nits.x()), pqToNits(nits.y()), pqToNits(nits.z()));
}
Q_UNREACHABLE();
}
QVector3D ColorDescription::nitsToEncoded(const QVector3D &rgb, NamedTransferFunction tf, double sdrBrightness)
{
switch (tf) {
case NamedTransferFunction::sRGB: {
const auto clamped = clamp(rgb / sdrBrightness);
return QVector3D(linearToSRGB(clamped.x()), linearToSRGB(clamped.y()), linearToSRGB(clamped.z()));
}
case NamedTransferFunction::gamma22: {
const auto clamped = clamp(rgb / sdrBrightness);
return QVector3D(std::pow(clamped.x(), 1 / 2.2), std::pow(clamped.y(), 1 / 2.2), std::pow(clamped.z(), 1 / 2.2));
}
rgb = m_colorimetry.toOther(dst.colorimetry()) * rgb;
// nits -> transfer function
switch (dst.transferFunction()) {
case NamedTransferFunction::sRGB:
rgb = clamp(rgb / dst.sdrBrightness());
return QVector3D(linearToSRGB(rgb.x()), linearToSRGB(rgb.y()), linearToSRGB(rgb.z()));
case NamedTransferFunction::gamma22:
rgb = clamp(rgb / dst.sdrBrightness());
return QVector3D(std::pow(rgb.x(), 1 / 2.2), std::pow(rgb.y(), 1 / 2.2), std::pow(rgb.z(), 1 / 2.2));
case NamedTransferFunction::linear:
return rgb;
case NamedTransferFunction::scRGB:
@ -379,6 +379,13 @@ QVector3D ColorDescription::mapTo(QVector3D rgb, const ColorDescription &dst) co
case NamedTransferFunction::PerceptualQuantizer:
return QVector3D(nitsToPQ(rgb.x()), nitsToPQ(rgb.y()), nitsToPQ(rgb.z()));
}
return QVector3D();
Q_UNREACHABLE();
}
QVector3D ColorDescription::mapTo(QVector3D rgb, const ColorDescription &dst) const
{
rgb = encodedToNits(rgb, m_transferFunction, m_sdrBrightness);
rgb = m_colorimetry.toOther(dst.colorimetry()) * rgb;
return nitsToEncoded(rgb, dst.transferFunction(), dst.sdrBrightness());
}
}

@ -127,6 +127,8 @@ public:
* This color description describes display-referred sRGB, with a gamma22 transfer function
*/
static const ColorDescription sRGB;
static QVector3D encodedToNits(const QVector3D &nits, NamedTransferFunction tf, double sdrBrightness);
static QVector3D nitsToEncoded(const QVector3D &rgb, NamedTransferFunction tf, double sdrBrightness);
private:
Colorimetry m_colorimetry;

Loading…
Cancel
Save