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()) { if (!m_pipeline->activePending()) {
return false; return false;
} }
const auto inGamma22 = ColorDescription::nitsToEncoded(rgb, NamedTransferFunction::gamma22, 1);
if (m_pipeline->hasCTM()) { if (m_pipeline->hasCTM()) {
QMatrix3x3 ctm; QMatrix3x3 ctm;
ctm(0, 0) = rgb.x(); ctm(0, 0) = inGamma22.x();
ctm(1, 1) = rgb.y(); ctm(1, 1) = inGamma22.y();
ctm(2, 2) = rgb.z(); ctm(2, 2) = inGamma22.z();
m_pipeline->setCTM(ctm); m_pipeline->setCTM(ctm);
m_pipeline->setGammaRamp(nullptr); m_pipeline->setGammaRamp(nullptr);
if (DrmPipeline::commitPipelines({m_pipeline}, DrmPipeline::CommitMode::Test) == DrmPipeline::Error::None) { 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()) { if (m_pipeline->hasGammaRamp()) {
auto lut = ColorTransformation::createScalingTransform(rgb); auto lut = ColorTransformation::createScalingTransform(inGamma22);
if (lut) { if (lut) {
m_pipeline->setGammaRamp(std::move(lut)); m_pipeline->setGammaRamp(std::move(lut));
if (DrmPipeline::commitPipelines({m_pipeline}, DrmPipeline::CommitMode::Test) == DrmPipeline::Error::None) { 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) { if (m_crtc == XCB_NONE) {
return true; return true;
} }
auto transformation = ColorTransformation::createScalingTransform(rgb); auto transformation = ColorTransformation::createScalingTransform(ColorDescription::nitsToEncoded(rgb, NamedTransferFunction::gamma22, 1));
if (!transformation) { if (!transformation) {
return false; return false;
} }

@ -62,7 +62,8 @@ void ColorDevicePrivate::recalculateFactors()
const qreal zWhitePoint = interpolate(blackbodyColor[blackBodyColorIndex + 2], const qreal zWhitePoint = interpolate(blackbodyColor[blackBodyColorIndex + 2],
blackbodyColor[blackBodyColorIndex + 5], blackbodyColor[blackBodyColorIndex + 5],
blendFactor); 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; 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)); 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 (tf) {
switch (m_transferFunction) {
case NamedTransferFunction::sRGB: case NamedTransferFunction::sRGB:
rgb = m_sdrBrightness * QVector3D(srgbToLinear(rgb.x()), srgbToLinear(rgb.y()), srgbToLinear(rgb.z())); return sdrBrightness * QVector3D(srgbToLinear(nits.x()), srgbToLinear(nits.y()), srgbToLinear(nits.z()));
break;
case NamedTransferFunction::gamma22: 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)); return sdrBrightness * QVector3D(std::pow(nits.x(), 2.2), std::pow(nits.y(), 2.2), std::pow(nits.z(), 2.2));
break;
case NamedTransferFunction::linear: case NamedTransferFunction::linear:
break; return nits;
case NamedTransferFunction::scRGB: case NamedTransferFunction::scRGB:
rgb *= 80.0f; return nits * 80.0f;
break;
case NamedTransferFunction::PerceptualQuantizer: case NamedTransferFunction::PerceptualQuantizer:
rgb = QVector3D(pqToNits(rgb.x()), pqToNits(rgb.y()), pqToNits(rgb.z())); return QVector3D(pqToNits(nits.x()), pqToNits(nits.y()), pqToNits(nits.z()));
break; }
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: case NamedTransferFunction::linear:
return rgb; return rgb;
case NamedTransferFunction::scRGB: case NamedTransferFunction::scRGB:
@ -379,6 +379,13 @@ QVector3D ColorDescription::mapTo(QVector3D rgb, const ColorDescription &dst) co
case NamedTransferFunction::PerceptualQuantizer: case NamedTransferFunction::PerceptualQuantizer:
return QVector3D(nitsToPQ(rgb.x()), nitsToPQ(rgb.y()), nitsToPQ(rgb.z())); 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 * This color description describes display-referred sRGB, with a gamma22 transfer function
*/ */
static const ColorDescription sRGB; 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: private:
Colorimetry m_colorimetry; Colorimetry m_colorimetry;

Loading…
Cancel
Save