core/colorspace: implement black point compensation
This commit is contained in:
parent
cd371d8618
commit
0d0135e237
6 changed files with 39 additions and 11 deletions
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in a new issue