core/colorspace: implement black point compensation

This commit is contained in:
Xaver Hugl 2024-08-12 02:25:29 +02:00
parent cd371d8618
commit 0d0135e237
6 changed files with 39 additions and 11 deletions

View file

@ -11,6 +11,14 @@
namespace KWin namespace KWin
{ {
ValueRange ValueRange::operator*(double mult) const
{
return ValueRange{
.min = min * mult,
.max = max * mult,
};
}
ColorPipeline ColorPipeline::create(const ColorDescription &from, const ColorDescription &to, RenderingIntent intent) ColorPipeline ColorPipeline::create(const ColorDescription &from, const ColorDescription &to, RenderingIntent intent)
{ {
const auto range1 = ValueRange(from.minLuminance(), from.maxHdrLuminance().value_or(from.referenceLuminance())); const auto range1 = ValueRange(from.minLuminance(), from.maxHdrLuminance().value_or(from.referenceLuminance()));
@ -19,11 +27,10 @@ ColorPipeline ColorPipeline::create(const ColorDescription &from, const ColorDes
.max = from.transferFunction().nitsToEncoded(range1.max), .max = from.transferFunction().nitsToEncoded(range1.max),
}); });
ret.addTransferFunction(from.transferFunction()); ret.addTransferFunction(from.transferFunction());
ret.addMultiplier(to.referenceLuminance() / from.referenceLuminance());
// FIXME this assumes that the range stays the same with matrix multiplication // FIXME this assumes that the range stays the same with matrix multiplication
// that's not necessarily true, and figuring out the actual range could be complicated.. // that's not necessarily true, and figuring out the actual range could be complicated..
ret.addMatrix(from.toOther(to, intent), ret.currentOutputRange()); ret.addMatrix(from.toOther(to, intent), ret.currentOutputRange() * (to.referenceLuminance() / from.referenceLuminance()));
ret.addInverseTransferFunction(to.transferFunction()); ret.addInverseTransferFunction(to.transferFunction());
return ret; return ret;

View file

@ -20,6 +20,7 @@ public:
double max = 1; double max = 1;
bool operator==(const ValueRange &) const = default; bool operator==(const ValueRange &) const = default;
ValueRange operator*(double mult) const;
}; };
class KWIN_EXPORT ColorTransferFunction class KWIN_EXPORT ColorTransferFunction

View file

@ -326,15 +326,36 @@ std::optional<double> ColorDescription::maxHdrLuminance() const
QMatrix4x4 ColorDescription::toOther(const ColorDescription &other, RenderingIntent intent) const QMatrix4x4 ColorDescription::toOther(const ColorDescription &other, RenderingIntent intent) const
{ {
QMatrix4x4 luminanceBefore;
QMatrix4x4 luminanceAfter;
if (intent == RenderingIntent::Perceptual || intent == RenderingIntent::RelativeColorimetricWithBPC) {
// add black point compensation: black and reference white from the source color space
// should both be mapped to black and reference white in the destination color space
// before color conversions, map [src min, src ref] to [0, 1]
luminanceBefore.scale(1.0 / (referenceLuminance() - minLuminance()));
luminanceBefore.translate(-minLuminance(), -minLuminance(), -minLuminance());
// afterwards, map [0, 1] again to [dst min, dst ref]
luminanceAfter.translate(other.minLuminance(), other.minLuminance(), other.minLuminance());
luminanceAfter.scale(other.referenceLuminance() - other.minLuminance());
} else {
// map only the reference luminance
luminanceBefore.scale(other.referenceLuminance() / referenceLuminance());
}
switch (intent) { switch (intent) {
case RenderingIntent::Perceptual: { case RenderingIntent::Perceptual: {
const Colorimetry &srcContainer = containerColorimetry() == NamedColorimetry::BT709 ? other.sdrColorimetry() : containerColorimetry(); const Colorimetry &srcContainer = containerColorimetry() == NamedColorimetry::BT709 ? other.sdrColorimetry() : containerColorimetry();
return other.containerColorimetry().fromXYZ() * Colorimetry::chromaticAdaptationMatrix(srcContainer.white(), other.containerColorimetry().white()) * srcContainer.toXYZ(); return luminanceAfter * other.containerColorimetry().fromXYZ() * Colorimetry::chromaticAdaptationMatrix(srcContainer.white(), other.containerColorimetry().white()) * srcContainer.toXYZ() * luminanceBefore;
}
case RenderingIntent::RelativeColorimetric: {
return luminanceAfter * other.containerColorimetry().fromXYZ() * Colorimetry::chromaticAdaptationMatrix(containerColorimetry().white(), other.containerColorimetry().white()) * containerColorimetry().toXYZ() * luminanceBefore;
}
case RenderingIntent::RelativeColorimetricWithBPC: {
return luminanceAfter * other.containerColorimetry().fromXYZ() * Colorimetry::chromaticAdaptationMatrix(containerColorimetry().white(), other.containerColorimetry().white()) * containerColorimetry().toXYZ() * luminanceBefore;
}
case RenderingIntent::AbsoluteColorimetric: {
return luminanceAfter * other.containerColorimetry().fromXYZ() * containerColorimetry().toXYZ() * luminanceBefore;
} }
case RenderingIntent::RelativeColorimetric:
return other.containerColorimetry().fromXYZ() * Colorimetry::chromaticAdaptationMatrix(containerColorimetry().white(), other.containerColorimetry().white()) * containerColorimetry().toXYZ();
case RenderingIntent::AbsoluteColorimetric:
return other.containerColorimetry().fromXYZ() * containerColorimetry().toXYZ();
} }
Q_UNREACHABLE(); Q_UNREACHABLE();
} }
@ -342,7 +363,6 @@ QMatrix4x4 ColorDescription::toOther(const ColorDescription &other, RenderingInt
QVector3D ColorDescription::mapTo(QVector3D rgb, const ColorDescription &dst, RenderingIntent intent) const QVector3D ColorDescription::mapTo(QVector3D rgb, const ColorDescription &dst, RenderingIntent intent) const
{ {
rgb = m_transferFunction.encodedToNits(rgb); rgb = m_transferFunction.encodedToNits(rgb);
rgb *= dst.referenceLuminance() / m_referenceLuminance;
rgb = toOther(dst, intent) * rgb; rgb = toOther(dst, intent) * rgb;
return dst.transferFunction().nitsToEncoded(rgb); return dst.transferFunction().nitsToEncoded(rgb);
} }

View file

@ -27,7 +27,7 @@ enum class RenderingIntent {
/* colorimetric mapping between color spaces, without whitepoint adaptation */ /* colorimetric mapping between color spaces, without whitepoint adaptation */
AbsoluteColorimetric, AbsoluteColorimetric,
/* colorimetric mapping between color spaces, with whitepoint adaptation and black point compensation */ /* colorimetric mapping between color spaces, with whitepoint adaptation and black point compensation */
// TODO RelativeColorimetricWithBPC, RelativeColorimetricWithBPC,
}; };
enum class NamedColorimetry { enum class NamedColorimetry {

View file

@ -94,7 +94,6 @@ vec4 encodingToNits(vec4 color, int sourceTransferFunction, float luminanceOffse
vec4 sourceEncodingToNitsInDestinationColorspace(vec4 color) { vec4 sourceEncodingToNitsInDestinationColorspace(vec4 color) {
color = encodingToNits(color, sourceNamedTransferFunction, sourceTransferFunctionParams.x, sourceTransferFunctionParams.y); color = encodingToNits(color, sourceNamedTransferFunction, sourceTransferFunctionParams.x, sourceTransferFunctionParams.y);
color.rgb = color.rgb * (destinationReferenceLuminance / sourceReferenceLuminance);
color.rgb = (colorimetryTransform * vec4(color.rgb, 1.0)).rgb; color.rgb = (colorimetryTransform * vec4(color.rgb, 1.0)).rgb;
return vec4(doTonemapping(color.rgb, maxDestinationLuminance), color.a); return vec4(doTonemapping(color.rgb, maxDestinationLuminance), color.a);
} }

View file

@ -46,7 +46,8 @@ void XXColorManagerV4::xx_color_manager_v4_bind_resource(Resource *resource)
send_supported_intent(resource->handle, render_intent::render_intent_perceptual); send_supported_intent(resource->handle, render_intent::render_intent_perceptual);
send_supported_intent(resource->handle, render_intent::render_intent_relative); send_supported_intent(resource->handle, render_intent::render_intent_relative);
send_supported_intent(resource->handle, render_intent::render_intent_absolute); send_supported_intent(resource->handle, render_intent::render_intent_absolute);
// TODO implement saturation and relative bpc intents send_supported_intent(resource->handle, render_intent::render_intent_relative_bpc);
// TODO implement saturation intent
} }
void XXColorManagerV4::xx_color_manager_v4_destroy(Resource *resource) void XXColorManagerV4::xx_color_manager_v4_destroy(Resource *resource)