From 833476a11131599f6dd1cc518273e474e5d3a9ea Mon Sep 17 00:00:00 2001 From: Xaver Hugl Date: Mon, 12 Aug 2024 01:33:06 +0200 Subject: [PATCH] core/colorspace: add rendering intents Rendering intents describe how to handle mapping between different colorspaces, what to do with out of gamut values and what to do if the whitepoint doesn't match. This way, clients can choose which behavior their content should get. --- autotests/test_colorspaces.cpp | 62 ++++++++++++------- src/backends/drm/drm_egl_layer.cpp | 4 +- src/backends/drm/drm_egl_layer.h | 2 +- src/backends/drm/drm_egl_layer_surface.cpp | 2 +- src/backends/drm/drm_output.cpp | 4 +- src/backends/drm/drm_virtual_egl_layer.cpp | 2 +- src/backends/drm/drm_virtual_egl_layer.h | 2 +- src/backends/wayland/wayland_egl_backend.cpp | 2 +- src/backends/wayland/wayland_egl_backend.h | 2 +- src/compositor_wayland.cpp | 2 +- src/core/colorpipeline.cpp | 4 +- src/core/colorpipeline.h | 2 +- src/core/colorspace.cpp | 16 +++-- src/core/colorspace.h | 21 ++++++- src/core/outputlayer.cpp | 4 +- src/core/outputlayer.h | 2 +- src/effect/effecthandler.cpp | 2 +- src/effect/offscreeneffect.cpp | 2 +- src/opengl/glshader.cpp | 6 +- src/opengl/glshader.h | 2 +- src/plugins/colorpicker/colorpicker.cpp | 2 +- src/plugins/mouseclick/mouseclick.cpp | 2 +- src/plugins/mousemark/mousemark.cpp | 2 +- .../screencast/outputscreencastsource.cpp | 2 +- .../screencast/regionscreencastsource.cpp | 2 +- src/plugins/screenshot/screenshot.cpp | 2 +- src/plugins/showpaint/showpaint.cpp | 2 +- src/plugins/snaphelper/snaphelper.cpp | 2 +- .../startupfeedback/startupfeedback.cpp | 2 +- src/plugins/touchpoints/touchpoints.cpp | 2 +- src/plugins/zoom/zoom.cpp | 4 +- src/scene/item.cpp | 10 +++ src/scene/item.h | 3 + src/scene/itemrenderer_opengl.cpp | 6 +- src/scene/itemrenderer_opengl.h | 1 + src/scene/surfaceitem_wayland.cpp | 1 + src/wayland/surface.cpp | 5 ++ src/wayland/surface.h | 1 + src/wayland/surface_p.h | 1 + src/wayland/xx_colormanagement_v4.cpp | 26 +++++++- 40 files changed, 155 insertions(+), 68 deletions(-) diff --git a/autotests/test_colorspaces.cpp b/autotests/test_colorspaces.cpp index f96e8017d6..899ffb9958 100644 --- a/autotests/test_colorspaces.cpp +++ b/autotests/test_colorspaces.cpp @@ -70,11 +70,16 @@ void TestColorspaces::roundtripConversion() const QVector3D green(0, 1, 0); const QVector3D blue(0, 0, 1); const QVector3D white(1, 1, 1); - - QVERIFY(compareVectors(dst.mapTo(src.mapTo(red, dst), src), red, requiredAccuracy)); - QVERIFY(compareVectors(dst.mapTo(src.mapTo(green, dst), src), green, requiredAccuracy)); - QVERIFY(compareVectors(dst.mapTo(src.mapTo(blue, dst), src), blue, requiredAccuracy)); - QVERIFY(compareVectors(dst.mapTo(src.mapTo(white, dst), src), white, requiredAccuracy)); + constexpr std::array renderingIntents = { + RenderingIntent::RelativeColorimetric, + RenderingIntent::AbsoluteColorimetric, + }; + for (const RenderingIntent intent : renderingIntents) { + QVERIFY(compareVectors(dst.mapTo(src.mapTo(red, dst, intent), src, intent), red, requiredAccuracy)); + QVERIFY(compareVectors(dst.mapTo(src.mapTo(green, dst, intent), src, intent), green, requiredAccuracy)); + QVERIFY(compareVectors(dst.mapTo(src.mapTo(blue, dst, intent), src, intent), blue, requiredAccuracy)); + QVERIFY(compareVectors(dst.mapTo(src.mapTo(white, dst, intent), src, intent), white, requiredAccuracy)); + } } void TestColorspaces::nonNormalizedPrimaries() @@ -83,7 +88,7 @@ void TestColorspaces::nonNormalizedPrimaries() const auto from = Colorimetry::fromName(NamedColorimetry::BT709); const auto to = Colorimetry(Colorimetry::xyToXYZ(from.red()) * 2, Colorimetry::xyToXYZ(from.green()) * 2, Colorimetry::xyToXYZ(from.blue()) * 2, Colorimetry::xyToXYZ(from.white()) * 2); - const auto convertedWhite = from.toOther(to) * QVector3D(1, 1, 1); + const auto convertedWhite = from.toOther(to, RenderingIntent::RelativeColorimetric) * QVector3D(1, 1, 1); QCOMPARE_LE(std::abs(1 - convertedWhite.x()), s_resolution10bit); QCOMPARE_LE(std::abs(1 - convertedWhite.y()), s_resolution10bit); QCOMPARE_LE(std::abs(1 - convertedWhite.z()), s_resolution10bit); @@ -110,11 +115,18 @@ void TestColorspaces::testIdentityTransformation() QFETCH(TransferFunction::Type, transferFunction); const ColorDescription color(colorimetry, TransferFunction(transferFunction), 100, 0, 100, 100); - const auto pipeline = ColorPipeline::create(color, color); - if (!pipeline.isIdentity()) { - qWarning() << pipeline; + constexpr std::array renderingIntents = { + RenderingIntent::Perceptual, + RenderingIntent::RelativeColorimetric, + RenderingIntent::AbsoluteColorimetric, + }; + for (const RenderingIntent intent : renderingIntents) { + const auto pipeline = ColorPipeline::create(color, color, intent); + if (!pipeline.isIdentity()) { + qWarning() << pipeline; + } + QVERIFY(pipeline.isIdentity()); } - QVERIFY(pipeline.isIdentity()); } void TestColorspaces::testColorPipeline_data() @@ -124,17 +136,22 @@ void TestColorspaces::testColorPipeline_data() QTest::addColumn("dstBlack"); QTest::addColumn("dstGray"); QTest::addColumn("dstWhite"); + QTest::addColumn("intent"); - QTest::addRow("sRGB -> rec.2020") << ColorDescription(NamedColorimetry::BT709, TransferFunction(TransferFunction::gamma22), TransferFunction::defaultReferenceLuminanceFor(TransferFunction::gamma22), 0, std::nullopt, std::nullopt) - << ColorDescription(NamedColorimetry::BT2020, TransferFunction(TransferFunction::PerceptualQuantizer), 500, 0, std::nullopt, std::nullopt) - << QVector3D(0.044, 0.044, 0.044) - << QVector3D(0.517, 0.517, 0.517) - << QVector3D(0.677, 0.677, 0.677); - QTest::addRow("sRGB -> scRGB") << ColorDescription(NamedColorimetry::BT709, TransferFunction(TransferFunction::gamma22), TransferFunction::defaultReferenceLuminanceFor(TransferFunction::gamma22), 0, std::nullopt, std::nullopt) - << ColorDescription(NamedColorimetry::BT709, TransferFunction(TransferFunction::linear, 0, 80), 80, 0, std::nullopt, std::nullopt) - << QVector3D(0.0001, 0.0001, 0.0001) - << QVector3D(0.2177376408240310, 0.2177376408240310, 0.2177376408240310) - << QVector3D(1, 1, 1); + QTest::addRow("sRGB -> rec.2020 relative colorimetric") + << ColorDescription(NamedColorimetry::BT709, TransferFunction(TransferFunction::gamma22), TransferFunction::defaultReferenceLuminanceFor(TransferFunction::gamma22), 0, std::nullopt, std::nullopt) + << ColorDescription(NamedColorimetry::BT2020, TransferFunction(TransferFunction::PerceptualQuantizer), 500, 0, std::nullopt, std::nullopt) + << QVector3D(0.044, 0.044, 0.044) + << QVector3D(0.517, 0.517, 0.517) + << QVector3D(0.677, 0.677, 0.677) + << RenderingIntent::RelativeColorimetric; + QTest::addRow("sRGB -> scRGB relative colorimetric") + << ColorDescription(NamedColorimetry::BT709, TransferFunction(TransferFunction::gamma22), TransferFunction::defaultReferenceLuminanceFor(TransferFunction::gamma22), 0, std::nullopt, std::nullopt) + << ColorDescription(NamedColorimetry::BT709, TransferFunction(TransferFunction::linear, 0, 80), 80, 0, std::nullopt, std::nullopt) + << QVector3D(0.0001, 0.0001, 0.0001) + << QVector3D(0.2177376408240310, 0.2177376408240310, 0.2177376408240310) + << QVector3D(1, 1, 1) + << RenderingIntent::RelativeColorimetric; } void TestColorspaces::testColorPipeline() @@ -144,13 +161,14 @@ void TestColorspaces::testColorPipeline() QFETCH(QVector3D, dstBlack); QFETCH(QVector3D, dstGray); QFETCH(QVector3D, dstWhite); + QFETCH(RenderingIntent, intent); - const auto pipeline = ColorPipeline::create(srcColor, dstColor); + const auto pipeline = ColorPipeline::create(srcColor, dstColor, intent); QVERIFY(compareVectors(pipeline.evaluate(QVector3D(0, 0, 0)), dstBlack, s_resolution10bit)); QVERIFY(compareVectors(pipeline.evaluate(QVector3D(0.5, 0.5, 0.5)), dstGray, s_resolution10bit)); QVERIFY(compareVectors(pipeline.evaluate(QVector3D(1, 1, 1)), dstWhite, s_resolution10bit)); - const auto inversePipeline = ColorPipeline::create(dstColor, srcColor); + const auto inversePipeline = ColorPipeline::create(dstColor, srcColor, intent); QVERIFY(compareVectors(inversePipeline.evaluate(dstBlack), QVector3D(0, 0, 0), s_resolution10bit)); QVERIFY(compareVectors(inversePipeline.evaluate(dstGray), QVector3D(0.5, 0.5, 0.5), s_resolution10bit)); QVERIFY(compareVectors(inversePipeline.evaluate(dstWhite), QVector3D(1, 1, 1), s_resolution10bit)); diff --git a/src/backends/drm/drm_egl_layer.cpp b/src/backends/drm/drm_egl_layer.cpp index 7dd65ff891..ff4abe4afc 100644 --- a/src/backends/drm/drm_egl_layer.cpp +++ b/src/backends/drm/drm_egl_layer.cpp @@ -86,7 +86,7 @@ ColorDescription EglGbmLayer::colorDescription() const return m_surface.colorDescription(); } -bool EglGbmLayer::doAttemptScanout(GraphicsBuffer *buffer, const ColorDescription &color, const std::shared_ptr &frame) +bool EglGbmLayer::doAttemptScanout(GraphicsBuffer *buffer, const ColorDescription &color, RenderingIntent intent, const std::shared_ptr &frame) { static bool valid; static const bool directScanoutDisabled = qEnvironmentVariableIntValue("KWIN_DRM_NO_DIRECT_SCANOUT", &valid) == 1 && valid; @@ -97,7 +97,7 @@ bool EglGbmLayer::doAttemptScanout(GraphicsBuffer *buffer, const ColorDescriptio // TODO make the icc profile output a color pipeline too? return false; } - ColorPipeline pipeline = ColorPipeline::create(color, m_pipeline->output()->scanoutColorDescription()); + ColorPipeline pipeline = ColorPipeline::create(color, m_pipeline->output()->scanoutColorDescription(), intent); if (m_pipeline->output()->needsChannelFactorFallback()) { pipeline.addTransferFunction(m_pipeline->output()->scanoutColorDescription().transferFunction()); pipeline.addMultiplier(m_pipeline->output()->effectiveChannelFactors()); diff --git a/src/backends/drm/drm_egl_layer.h b/src/backends/drm/drm_egl_layer.h index 93483a2e88..ddb4c0c8d3 100644 --- a/src/backends/drm/drm_egl_layer.h +++ b/src/backends/drm/drm_egl_layer.h @@ -40,7 +40,7 @@ public: const ColorPipeline &colorPipeline() const override; private: - bool doAttemptScanout(GraphicsBuffer *buffer, const ColorDescription &color, const std::shared_ptr &frame) override; + bool doAttemptScanout(GraphicsBuffer *buffer, const ColorDescription &color, RenderingIntent intent, const std::shared_ptr &frame) override; std::shared_ptr m_scanoutBuffer; ColorPipeline m_colorPipeline; diff --git a/src/backends/drm/drm_egl_layer_surface.cpp b/src/backends/drm/drm_egl_layer_surface.cpp index 96ec1cf9a6..a89d497e24 100644 --- a/src/backends/drm/drm_egl_layer_surface.cpp +++ b/src/backends/drm/drm_egl_layer_surface.cpp @@ -193,7 +193,7 @@ bool EglGbmLayerSurface::endRendering(const QRegion &damagedRegion, OutputFrame if (m_surface->iccShader) { m_surface->iccShader->setUniforms(m_surface->iccProfile, m_surface->intermediaryColorDescription, m_surface->channelFactors); } else { - binder.shader()->setColorspaceUniforms(m_surface->intermediaryColorDescription, m_surface->targetColorDescription); + binder.shader()->setColorspaceUniforms(m_surface->intermediaryColorDescription, m_surface->targetColorDescription, RenderingIntent::RelativeColorimetric); QMatrix4x4 ctm; ctm(0, 0) = m_surface->channelFactors.x(); ctm(1, 1) = m_surface->channelFactors.y(); diff --git a/src/backends/drm/drm_output.cpp b/src/backends/drm/drm_output.cpp index ea35c40d7d..d3ec260e5f 100644 --- a/src/backends/drm/drm_output.cpp +++ b/src/backends/drm/drm_output.cpp @@ -491,7 +491,7 @@ void DrmOutput::tryKmsColorOffloading() const QVector3D channelFactors = effectiveChannelFactors(); const double maxLuminance = colorDescription().maxHdrLuminance().value_or(colorDescription().referenceLuminance()); const ColorDescription optimal = colorDescription().transferFunction().type == TransferFunction::gamma22 ? colorDescription() : colorDescription().withTransferFunction(TransferFunction(TransferFunction::gamma22, 0, maxLuminance)); - ColorPipeline colorPipeline = ColorPipeline::create(optimal, colorDescription()); + ColorPipeline colorPipeline = ColorPipeline::create(optimal, colorDescription(), RenderingIntent::RelativeColorimetric); colorPipeline.addTransferFunction(colorDescription().transferFunction()); colorPipeline.addMultiplier(channelFactors); colorPipeline.addInverseTransferFunction(colorDescription().transferFunction()); @@ -517,7 +517,7 @@ bool DrmOutput::needsChannelFactorFallback() const QVector3D DrmOutput::effectiveChannelFactors() const { - QVector3D adaptedChannelFactors = Colorimetry::fromName(NamedColorimetry::BT709).toOther(m_state.colorDescription.containerColorimetry()) * m_channelFactors; + QVector3D adaptedChannelFactors = Colorimetry::fromName(NamedColorimetry::BT709).toOther(m_state.colorDescription.containerColorimetry(), RenderingIntent::RelativeColorimetric) * m_channelFactors; // normalize red to be the original brightness value again adaptedChannelFactors *= m_channelFactors.x() / adaptedChannelFactors.x(); if (m_state.highDynamicRange || !m_brightnessDevice) { diff --git a/src/backends/drm/drm_virtual_egl_layer.cpp b/src/backends/drm/drm_virtual_egl_layer.cpp index 4d2aa046a5..b0ef995f9c 100644 --- a/src/backends/drm/drm_virtual_egl_layer.cpp +++ b/src/backends/drm/drm_virtual_egl_layer.cpp @@ -133,7 +133,7 @@ std::shared_ptr VirtualEglGbmLayer::texture() const return nullptr; } -bool VirtualEglGbmLayer::doAttemptScanout(GraphicsBuffer *buffer, const ColorDescription &color, const std::shared_ptr &frame) +bool VirtualEglGbmLayer::doAttemptScanout(GraphicsBuffer *buffer, const ColorDescription &color, RenderingIntent intent, const std::shared_ptr &frame) { static bool valid; static const bool directScanoutDisabled = qEnvironmentVariableIntValue("KWIN_DRM_NO_DIRECT_SCANOUT", &valid) == 1 && valid; diff --git a/src/backends/drm/drm_virtual_egl_layer.h b/src/backends/drm/drm_virtual_egl_layer.h index b8cb2a40f2..2f7a5fde6f 100644 --- a/src/backends/drm/drm_virtual_egl_layer.h +++ b/src/backends/drm/drm_virtual_egl_layer.h @@ -44,7 +44,7 @@ public: const ColorDescription &colorDescription() const; private: - bool doAttemptScanout(GraphicsBuffer *buffer, const ColorDescription &color, const std::shared_ptr &frame) override; + bool doAttemptScanout(GraphicsBuffer *buffer, const ColorDescription &color, RenderingIntent intent, const std::shared_ptr &frame) override; std::shared_ptr createGbmSwapchain() const; bool doesGbmSwapchainFit(EglSwapchain *swapchain) const; diff --git a/src/backends/wayland/wayland_egl_backend.cpp b/src/backends/wayland/wayland_egl_backend.cpp index ce6b96a128..54742a3c84 100644 --- a/src/backends/wayland/wayland_egl_backend.cpp +++ b/src/backends/wayland/wayland_egl_backend.cpp @@ -111,7 +111,7 @@ bool WaylandEglPrimaryLayer::doEndFrame(const QRegion &renderedRegion, const QRe return true; } -bool WaylandEglPrimaryLayer::doAttemptScanout(GraphicsBuffer *buffer, const ColorDescription &color, const std::shared_ptr &frame) +bool WaylandEglPrimaryLayer::doAttemptScanout(GraphicsBuffer *buffer, const ColorDescription &color, RenderingIntent intent, const std::shared_ptr &frame) { Q_ASSERT(!m_presentationBuffer); // TODO use viewporter to relax this check diff --git a/src/backends/wayland/wayland_egl_backend.h b/src/backends/wayland/wayland_egl_backend.h index be255c0b1c..f28c036189 100644 --- a/src/backends/wayland/wayland_egl_backend.h +++ b/src/backends/wayland/wayland_egl_backend.h @@ -44,7 +44,7 @@ public: std::optional doBeginFrame() override; bool doEndFrame(const QRegion &renderedRegion, const QRegion &damagedRegion, OutputFrame *frame) override; - bool doAttemptScanout(GraphicsBuffer *buffer, const ColorDescription &color, const std::shared_ptr &frame) override; + bool doAttemptScanout(GraphicsBuffer *buffer, const ColorDescription &color, RenderingIntent intent, const std::shared_ptr &frame) override; DrmDevice *scanoutDevice() const override; QHash> supportedDrmFormats() const override; diff --git a/src/compositor_wayland.cpp b/src/compositor_wayland.cpp index 4c08a21ccb..526a53e674 100644 --- a/src/compositor_wayland.cpp +++ b/src/compositor_wayland.cpp @@ -287,7 +287,7 @@ static bool checkForBlackBackground(SurfaceItem *background) } const QRgb rgb = view.image()->pixel(0, 0); const QVector3D encoded(qRed(rgb) / 255.0, qGreen(rgb) / 255.0, qBlue(rgb) / 255.0); - const QVector3D nits = background->colorDescription().mapTo(encoded, ColorDescription(NamedColorimetry::BT709, TransferFunction(TransferFunction::linear), 100, 0, std::nullopt, std::nullopt)); + const QVector3D nits = background->colorDescription().mapTo(encoded, ColorDescription(NamedColorimetry::BT709, TransferFunction(TransferFunction::linear), 100, 0, std::nullopt, std::nullopt), background->renderingIntent()); // below 0.1 nits, it shouldn't be noticeable that we replace it with black return nits.lengthSquared() <= (0.1 * 0.1); } diff --git a/src/core/colorpipeline.cpp b/src/core/colorpipeline.cpp index 65f58e4320..337d7aba09 100644 --- a/src/core/colorpipeline.cpp +++ b/src/core/colorpipeline.cpp @@ -11,7 +11,7 @@ namespace KWin { -ColorPipeline ColorPipeline::create(const ColorDescription &from, const ColorDescription &to) +ColorPipeline ColorPipeline::create(const ColorDescription &from, const ColorDescription &to, RenderingIntent intent) { const auto range1 = ValueRange(from.minLuminance(), from.maxHdrLuminance().value_or(from.referenceLuminance())); ColorPipeline ret(ValueRange{ @@ -23,7 +23,7 @@ ColorPipeline ColorPipeline::create(const ColorDescription &from, const ColorDes // 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.. - ret.addMatrix(from.containerColorimetry().toOther(to.containerColorimetry()), ret.currentOutputRange()); + ret.addMatrix(from.containerColorimetry().toOther(to.containerColorimetry(), intent), ret.currentOutputRange()); ret.addInverseTransferFunction(to.transferFunction()); return ret; diff --git a/src/core/colorpipeline.h b/src/core/colorpipeline.h index 568cf3268d..651bad8a3f 100644 --- a/src/core/colorpipeline.h +++ b/src/core/colorpipeline.h @@ -79,7 +79,7 @@ public: explicit ColorPipeline(); explicit ColorPipeline(const ValueRange &inputRange); - static ColorPipeline create(const ColorDescription &from, const ColorDescription &to); + static ColorPipeline create(const ColorDescription &from, const ColorDescription &to, RenderingIntent intent); ColorPipeline merged(const ColorPipeline &onTop) const; diff --git a/src/core/colorspace.cpp b/src/core/colorspace.cpp index 0c8a027aa9..56ab7c8b9c 100644 --- a/src/core/colorspace.cpp +++ b/src/core/colorspace.cpp @@ -126,10 +126,16 @@ const QMatrix4x4 &Colorimetry::fromXYZ() const return m_fromXYZ; } -QMatrix4x4 Colorimetry::toOther(const Colorimetry &other) const +QMatrix4x4 Colorimetry::toOther(const Colorimetry &other, RenderingIntent intent) const { - // rendering intent is relative colorimetric, so adapt to the different whitepoint - return other.fromXYZ() * chromaticAdaptationMatrix(this->white(), other.white()) * toXYZ(); + switch (intent) { + case RenderingIntent::Perceptual: + case RenderingIntent::RelativeColorimetric: + return other.fromXYZ() * chromaticAdaptationMatrix(this->white(), other.white()) * toXYZ(); + case RenderingIntent::AbsoluteColorimetric: + return other.fromXYZ() * toXYZ(); + } + Q_UNREACHABLE(); } Colorimetry Colorimetry::adaptedTo(QVector2D newWhitepoint) const @@ -267,11 +273,11 @@ std::optional ColorDescription::maxHdrLuminance() const return m_maxHdrLuminance; } -QVector3D ColorDescription::mapTo(QVector3D rgb, const ColorDescription &dst) const +QVector3D ColorDescription::mapTo(QVector3D rgb, const ColorDescription &dst, RenderingIntent intent) const { rgb = m_transferFunction.encodedToNits(rgb); rgb *= dst.referenceLuminance() / m_referenceLuminance; - rgb = m_containerColorimetry.toOther(dst.containerColorimetry()) * rgb; + rgb = m_containerColorimetry.toOther(dst.containerColorimetry(), intent) * rgb; return dst.transferFunction().nitsToEncoded(rgb); } diff --git a/src/core/colorspace.h b/src/core/colorspace.h index df216d400f..8639f48da8 100644 --- a/src/core/colorspace.h +++ b/src/core/colorspace.h @@ -14,6 +14,22 @@ namespace KWin { +/** + * rendering intents describe how colors should be mapped between different color spaces + */ +enum class RenderingIntent { + /* "vendor specific", preserves the overall color appearance */ + Perceptual, + /* "vendor specific", maps saturated colors to be saturated in the target color space too */ + // TODO Saturation, + /* colorimetric mapping between color spaces, with whitepoint adaptation */ + RelativeColorimetric, + /* colorimetric mapping between color spaces, without whitepoint adaptation */ + AbsoluteColorimetric, + /* colorimetric mapping between color spaces, with whitepoint adaptation and black point compensation */ + // TODO RelativeColorimetricWithBPC, +}; + enum class NamedColorimetry { BT709, BT2020, @@ -56,9 +72,8 @@ public: const QMatrix4x4 &fromXYZ() const; /** * @returns a matrix that transforms from linear RGB in this colorimetry to linear RGB in the other colorimetry - * the rendering intent is relative colorimetric */ - QMatrix4x4 toOther(const Colorimetry &colorimetry) const; + QMatrix4x4 toOther(const Colorimetry &colorimetry, RenderingIntent intent) const; bool operator==(const Colorimetry &other) const; bool operator==(NamedColorimetry name) const; /** @@ -164,7 +179,7 @@ public: bool operator==(const ColorDescription &other) const = default; - QVector3D mapTo(QVector3D rgb, const ColorDescription &other) const; + QVector3D mapTo(QVector3D rgb, const ColorDescription &other, RenderingIntent intent) const; ColorDescription withTransferFunction(const TransferFunction &func) const; /** diff --git a/src/core/outputlayer.cpp b/src/core/outputlayer.cpp index 7b5ecf7b16..0e986aa374 100644 --- a/src/core/outputlayer.cpp +++ b/src/core/outputlayer.cpp @@ -62,7 +62,7 @@ bool OutputLayer::needsRepaint() const return !m_repaints.isEmpty(); } -bool OutputLayer::doAttemptScanout(GraphicsBuffer *buffer, const ColorDescription &color, const std::shared_ptr &frame) +bool OutputLayer::doAttemptScanout(GraphicsBuffer *buffer, const ColorDescription &color, RenderingIntent intent, const std::shared_ptr &frame) { return false; } @@ -91,7 +91,7 @@ bool OutputLayer::attemptScanout(SurfaceItem *surfaceItem, const std::shared_ptr m_bufferTransform = surfaceItem->bufferTransform(); const auto desiredTransform = m_output ? m_output->transform() : OutputTransform::Kind::Normal; m_offloadTransform = m_bufferTransform.combine(desiredTransform.inverted()); - const bool ret = doAttemptScanout(buffer, surfaceItem->colorDescription(), frame); + const bool ret = doAttemptScanout(buffer, surfaceItem->colorDescription(), surfaceItem->renderingIntent(), frame); if (ret) { surfaceItem->resetDamage(); // ensure the pixmap is updated when direct scanout ends diff --git a/src/core/outputlayer.h b/src/core/outputlayer.h index 4eb65915bf..dac157f50e 100644 --- a/src/core/outputlayer.h +++ b/src/core/outputlayer.h @@ -95,7 +95,7 @@ public: OutputTransform bufferTransform() const; protected: - virtual bool doAttemptScanout(GraphicsBuffer *buffer, const ColorDescription &color, const std::shared_ptr &frame); + virtual bool doAttemptScanout(GraphicsBuffer *buffer, const ColorDescription &color, RenderingIntent intent, const std::shared_ptr &frame); virtual std::optional doBeginFrame() = 0; virtual bool doEndFrame(const QRegion &renderedRegion, const QRegion &damagedRegion, OutputFrame *frame) = 0; diff --git a/src/effect/effecthandler.cpp b/src/effect/effecthandler.cpp index 6b5efccd9e..4e7498c9f5 100644 --- a/src/effect/effecthandler.cpp +++ b/src/effect/effecthandler.cpp @@ -1501,7 +1501,7 @@ void EffectsHandler::renderOffscreenQuickView(const RenderTarget &renderTarget, if (a != 1.0) { shader->setUniform(GLShader::Vec4Uniform::ModulationConstant, QVector4D(a, a, a, a)); } - shader->setColorspaceUniforms(ColorDescription::sRGB, renderTarget.colorDescription()); + shader->setColorspaceUniforms(ColorDescription::sRGB, renderTarget.colorDescription(), RenderingIntent::Perceptual); const bool alphaBlending = w->hasAlphaChannel() || (a != 1.0); if (alphaBlending) { diff --git a/src/effect/offscreeneffect.cpp b/src/effect/offscreeneffect.cpp index d59e8901f4..b2ac70ac38 100644 --- a/src/effect/offscreeneffect.cpp +++ b/src/effect/offscreeneffect.cpp @@ -189,7 +189,7 @@ void OffscreenData::paint(const RenderTarget &renderTarget, const RenderViewport shader->setUniform(GLShader::Vec3Uniform::PrimaryBrightness, QVector3D(toXYZ(1, 0), toXYZ(1, 1), toXYZ(1, 2))); shader->setUniform(GLShader::IntUniform::TextureWidth, m_texture->width()); shader->setUniform(GLShader::IntUniform::TextureHeight, m_texture->height()); - shader->setColorspaceUniforms(ColorDescription::sRGB, renderTarget.colorDescription()); + shader->setColorspaceUniforms(ColorDescription::sRGB, renderTarget.colorDescription(), RenderingIntent::Perceptual); const bool clipping = region != infiniteRegion(); const QRegion clipRegion = clipping ? viewport.mapToRenderTarget(region) : infiniteRegion(); diff --git a/src/opengl/glshader.cpp b/src/opengl/glshader.cpp index d6978da92c..a63f2ec1c9 100644 --- a/src/opengl/glshader.cpp +++ b/src/opengl/glshader.cpp @@ -469,10 +469,10 @@ QMatrix4x4 GLShader::getUniformMatrix4x4(const char *name) } } -bool GLShader::setColorspaceUniforms(const ColorDescription &src, const ColorDescription &dst) +bool GLShader::setColorspaceUniforms(const ColorDescription &src, const ColorDescription &dst, RenderingIntent intent) { - const auto &srcColorimetry = src.containerColorimetry() == NamedColorimetry::BT709 ? dst.sdrColorimetry() : src.containerColorimetry(); - return setUniform(Mat4Uniform::ColorimetryTransformation, srcColorimetry.toOther(dst.containerColorimetry())) + const auto &srcColorimetry = intent == RenderingIntent::Perceptual && src.containerColorimetry() == NamedColorimetry::BT709 ? dst.sdrColorimetry() : src.containerColorimetry(); + return setUniform(Mat4Uniform::ColorimetryTransformation, srcColorimetry.toOther(dst.containerColorimetry(), intent)) && setUniform(IntUniform::SourceNamedTransferFunction, src.transferFunction().type) && setUniform(Vec2Uniform::SourceTransferFunctionParams, QVector2D(src.transferFunction().minLuminance, src.transferFunction().maxLuminance - src.transferFunction().minLuminance)) && setUniform(FloatUniform::SourceReferenceLuminance, src.referenceLuminance()) diff --git a/src/opengl/glshader.h b/src/opengl/glshader.h index a0d2a47ff2..2bd6599775 100644 --- a/src/opengl/glshader.h +++ b/src/opengl/glshader.h @@ -138,7 +138,7 @@ public: bool setUniform(ColorUniform uniform, const QVector4D &value); bool setUniform(ColorUniform uniform, const QColor &value); - bool setColorspaceUniforms(const ColorDescription &src, const ColorDescription &dst); + bool setColorspaceUniforms(const ColorDescription &src, const ColorDescription &dst, RenderingIntent intent); protected: GLShader(unsigned int flags = NoFlags); diff --git a/src/plugins/colorpicker/colorpicker.cpp b/src/plugins/colorpicker/colorpicker.cpp index f48c7e2f33..74cebf49a2 100644 --- a/src/plugins/colorpicker/colorpicker.cpp +++ b/src/plugins/colorpicker/colorpicker.cpp @@ -67,7 +67,7 @@ void ColorPickerEffect::paintScreen(const RenderTarget &renderTarget, const Rend const QPoint texturePosition = viewport.mapToRenderTarget(m_scheduledPosition).toPoint(); glReadPixels(texturePosition.x(), renderTarget.size().height() - texturePosition.y() - PIXEL_SIZE, PIXEL_SIZE, PIXEL_SIZE, GL_RGBA, GL_FLOAT, data.data()); - QVector3D sRGB = 255 * renderTarget.colorDescription().mapTo(QVector3D(data[0], data[1], data[2]), ColorDescription::sRGB); + QVector3D sRGB = 255 * renderTarget.colorDescription().mapTo(QVector3D(data[0], data[1], data[2]), ColorDescription::sRGB, RenderingIntent::RelativeColorimetric); QDBusConnection::sessionBus().send(m_replyMessage.createReply(QColor(sRGB.x(), sRGB.y(), sRGB.z()))); setPicking(false); m_scheduledPosition = QPoint(-1, -1); diff --git a/src/plugins/mouseclick/mouseclick.cpp b/src/plugins/mouseclick/mouseclick.cpp index 6948861e40..ec030277b0 100644 --- a/src/plugins/mouseclick/mouseclick.cpp +++ b/src/plugins/mouseclick/mouseclick.cpp @@ -300,7 +300,7 @@ void MouseClickEffect::paintScreenSetupGl(const RenderTarget &renderTarget, cons { GLShader *shader = ShaderManager::instance()->pushShader(ShaderTrait::UniformColor | ShaderTrait::TransformColorspace); shader->setUniform(GLShader::Mat4Uniform::ModelViewProjectionMatrix, projectionMatrix); - shader->setColorspaceUniforms(ColorDescription::sRGB, renderTarget.colorDescription()); + shader->setColorspaceUniforms(ColorDescription::sRGB, renderTarget.colorDescription(), RenderingIntent::Perceptual); glLineWidth(m_lineWidth); glEnable(GL_BLEND); diff --git a/src/plugins/mousemark/mousemark.cpp b/src/plugins/mousemark/mousemark.cpp index 162e099ce0..410a20ca91 100644 --- a/src/plugins/mousemark/mousemark.cpp +++ b/src/plugins/mousemark/mousemark.cpp @@ -118,7 +118,7 @@ void MouseMarkEffect::paintScreen(const RenderTarget &renderTarget, const Render const auto scale = viewport.scale(); ShaderBinder binder(ShaderTrait::UniformColor | ShaderTrait::TransformColorspace); binder.shader()->setUniform(GLShader::Mat4Uniform::ModelViewProjectionMatrix, viewport.projectionMatrix()); - binder.shader()->setColorspaceUniforms(ColorDescription::sRGB, renderTarget.colorDescription()); + binder.shader()->setColorspaceUniforms(ColorDescription::sRGB, renderTarget.colorDescription(), RenderingIntent::Perceptual); binder.shader()->setUniform(GLShader::ColorUniform::Color, color); QList verts; for (const Mark &mark : std::as_const(marks)) { diff --git a/src/plugins/screencast/outputscreencastsource.cpp b/src/plugins/screencast/outputscreencastsource.cpp index 92a4cf28cf..4d1518b64d 100644 --- a/src/plugins/screencast/outputscreencastsource.cpp +++ b/src/plugins/screencast/outputscreencastsource.cpp @@ -72,7 +72,7 @@ void OutputScreenCastSource::render(GLFramebuffer *target) projectionMatrix.scale(1, -1); projectionMatrix.ortho(QRect(QPoint(), textureSize())); shaderBinder.shader()->setUniform(GLShader::Mat4Uniform::ModelViewProjectionMatrix, projectionMatrix); - shaderBinder.shader()->setColorspaceUniforms(colorDescription, ColorDescription::sRGB); + shaderBinder.shader()->setColorspaceUniforms(colorDescription, ColorDescription::sRGB, RenderingIntent::Perceptual); GLFramebuffer::pushFramebuffer(target); outputTexture->render(textureSize()); diff --git a/src/plugins/screencast/regionscreencastsource.cpp b/src/plugins/screencast/regionscreencastsource.cpp index ab50a287cb..32863a407d 100644 --- a/src/plugins/screencast/regionscreencastsource.cpp +++ b/src/plugins/screencast/regionscreencastsource.cpp @@ -107,7 +107,7 @@ void RegionScreenCastSource::blit(Output *output) projectionMatrix.translate(outputGeometry.left(), outputGeometry.top()); shaderBinder.shader()->setUniform(GLShader::Mat4Uniform::ModelViewProjectionMatrix, projectionMatrix); - shaderBinder.shader()->setColorspaceUniforms(colorDescription, ColorDescription::sRGB); + shaderBinder.shader()->setColorspaceUniforms(colorDescription, ColorDescription::sRGB, RenderingIntent::Perceptual); outputTexture->render(outputGeometry.size()); GLFramebuffer::popFramebuffer(); diff --git a/src/plugins/screenshot/screenshot.cpp b/src/plugins/screenshot/screenshot.cpp index 150d329275..9551f2ab21 100644 --- a/src/plugins/screenshot/screenshot.cpp +++ b/src/plugins/screenshot/screenshot.cpp @@ -385,7 +385,7 @@ QImage ScreenShotEffect::blitScreenshot(const RenderTarget &renderTarget, const if (renderTarget.texture()) { GLFramebuffer::pushFramebuffer(&target); ShaderBinder binder(ShaderTrait::MapTexture | ShaderTrait::TransformColorspace); - binder.shader()->setColorspaceUniforms(renderTarget.colorDescription(), ColorDescription::sRGB); + binder.shader()->setColorspaceUniforms(renderTarget.colorDescription(), ColorDescription::sRGB, RenderingIntent::Perceptual); QMatrix4x4 projectionMatrix; projectionMatrix.scale(1, -1); projectionMatrix *= renderTarget.transform().toMatrix(); diff --git a/src/plugins/showpaint/showpaint.cpp b/src/plugins/showpaint/showpaint.cpp index 5b1c01b0c3..5164e26b69 100644 --- a/src/plugins/showpaint/showpaint.cpp +++ b/src/plugins/showpaint/showpaint.cpp @@ -72,7 +72,7 @@ void ShowPaintEffect::paintGL(const RenderTarget &renderTarget, const QMatrix4x4 vbo->reset(); ShaderBinder binder(ShaderTrait::UniformColor | ShaderTrait::TransformColorspace); binder.shader()->setUniform(GLShader::Mat4Uniform::ModelViewProjectionMatrix, projection); - binder.shader()->setColorspaceUniforms(ColorDescription::sRGB, renderTarget.colorDescription()); + binder.shader()->setColorspaceUniforms(ColorDescription::sRGB, renderTarget.colorDescription(), RenderingIntent::Perceptual); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); QColor color = s_colors[m_colorIndex]; diff --git a/src/plugins/snaphelper/snaphelper.cpp b/src/plugins/snaphelper/snaphelper.cpp index 9a17ba0ddd..2d96e33a78 100644 --- a/src/plugins/snaphelper/snaphelper.cpp +++ b/src/plugins/snaphelper/snaphelper.cpp @@ -113,7 +113,7 @@ void SnapHelperEffect::paintScreen(const RenderTarget &renderTarget, const Rende vbo->reset(); ShaderBinder binder(ShaderTrait::UniformColor | ShaderTrait::TransformColorspace); binder.shader()->setUniform(GLShader::Mat4Uniform::ModelViewProjectionMatrix, viewport.projectionMatrix()); - binder.shader()->setColorspaceUniforms(ColorDescription::sRGB, renderTarget.colorDescription()); + binder.shader()->setColorspaceUniforms(ColorDescription::sRGB, renderTarget.colorDescription(), RenderingIntent::Perceptual); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); diff --git a/src/plugins/startupfeedback/startupfeedback.cpp b/src/plugins/startupfeedback/startupfeedback.cpp index 2feeb34738..9e8e34a284 100644 --- a/src/plugins/startupfeedback/startupfeedback.cpp +++ b/src/plugins/startupfeedback/startupfeedback.cpp @@ -239,7 +239,7 @@ void StartupFeedbackEffect::paintScreen(const RenderTarget &renderTarget, const QMatrix4x4 mvp = viewport.projectionMatrix(); mvp.translate(pixelGeometry.x(), pixelGeometry.y()); shader->setUniform(GLShader::Mat4Uniform::ModelViewProjectionMatrix, mvp); - shader->setColorspaceUniforms(ColorDescription::sRGB, renderTarget.colorDescription()); + shader->setColorspaceUniforms(ColorDescription::sRGB, renderTarget.colorDescription(), RenderingIntent::Perceptual); texture->render(pixelGeometry.size()); ShaderManager::instance()->popShader(); glDisable(GL_BLEND); diff --git a/src/plugins/touchpoints/touchpoints.cpp b/src/plugins/touchpoints/touchpoints.cpp index a1addbe961..32e9840dd0 100644 --- a/src/plugins/touchpoints/touchpoints.cpp +++ b/src/plugins/touchpoints/touchpoints.cpp @@ -232,7 +232,7 @@ void TouchPointsEffect::paintScreenSetupGl(const RenderTarget &renderTarget, con { GLShader *shader = ShaderManager::instance()->pushShader(ShaderTrait::UniformColor); shader->setUniform(GLShader::Mat4Uniform::ModelViewProjectionMatrix, projectionMatrix); - shader->setColorspaceUniforms(ColorDescription::sRGB, renderTarget.colorDescription()); + shader->setColorspaceUniforms(ColorDescription::sRGB, renderTarget.colorDescription(), RenderingIntent::Perceptual); glLineWidth(m_lineWidth); glEnable(GL_BLEND); diff --git a/src/plugins/zoom/zoom.cpp b/src/plugins/zoom/zoom.cpp index 445dd1a5b4..cdf891dcd5 100644 --- a/src/plugins/zoom/zoom.cpp +++ b/src/plugins/zoom/zoom.cpp @@ -399,7 +399,7 @@ void ZoomEffect::paintScreen(const RenderTarget &renderTarget, const RenderViewp matrix.translate(offscreen.viewport.x() * scale, offscreen.viewport.y() * scale); shader->setUniform(GLShader::Mat4Uniform::ModelViewProjectionMatrix, viewport.projectionMatrix() * matrix); - shader->setColorspaceUniforms(offscreen.color, renderTarget.colorDescription()); + shader->setColorspaceUniforms(offscreen.color, renderTarget.colorDescription(), RenderingIntent::Perceptual); offscreen.texture->render(offscreen.viewport.size() * scale); } @@ -423,7 +423,7 @@ void ZoomEffect::paintScreen(const RenderTarget &renderTarget, const RenderViewp glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); auto s = ShaderManager::instance()->pushShader(ShaderTrait::MapTexture | ShaderTrait::TransformColorspace); - s->setColorspaceUniforms(ColorDescription::sRGB, renderTarget.colorDescription()); + s->setColorspaceUniforms(ColorDescription::sRGB, renderTarget.colorDescription(), RenderingIntent::Perceptual); QMatrix4x4 mvp = viewport.projectionMatrix(); mvp.translate(p.x() * scale, p.y() * scale); s->setUniform(GLShader::Mat4Uniform::ModelViewProjectionMatrix, mvp); diff --git a/src/scene/item.cpp b/src/scene/item.cpp index cf27fc0a63..dddce5776a 100644 --- a/src/scene/item.cpp +++ b/src/scene/item.cpp @@ -491,11 +491,21 @@ const ColorDescription &Item::colorDescription() const return m_colorDescription; } +RenderingIntent Item::renderingIntent() const +{ + return m_renderingIntent; +} + void Item::setColorDescription(const ColorDescription &description) { m_colorDescription = description; } +void Item::setRenderingIntent(RenderingIntent intent) +{ + m_renderingIntent = intent; +} + PresentationModeHint Item::presentationHint() const { return m_presentationHint; diff --git a/src/scene/item.h b/src/scene/item.h index 7e5bff1ac0..6441fd6f17 100644 --- a/src/scene/item.h +++ b/src/scene/item.h @@ -113,6 +113,7 @@ public: WindowQuadList quads() const; virtual void preprocess(); const ColorDescription &colorDescription() const; + RenderingIntent renderingIntent() const; PresentationModeHint presentationHint() const; Q_SIGNALS: @@ -136,6 +137,7 @@ protected: virtual WindowQuadList buildQuads() const; void discardQuads(); void setColorDescription(const ColorDescription &description); + void setRenderingIntent(RenderingIntent intent); void setPresentationHint(PresentationModeHint hint); void setScene(Scene *scene); @@ -169,6 +171,7 @@ private: mutable std::optional m_quads; mutable std::optional> m_sortedChildItems; ColorDescription m_colorDescription = ColorDescription::sRGB; + RenderingIntent m_renderingIntent = RenderingIntent::Perceptual; PresentationModeHint m_presentationHint = PresentationModeHint::VSync; }; diff --git a/src/scene/itemrenderer_opengl.cpp b/src/scene/itemrenderer_opengl.cpp index 9725d56593..a4b605ae98 100644 --- a/src/scene/itemrenderer_opengl.cpp +++ b/src/scene/itemrenderer_opengl.cpp @@ -165,6 +165,7 @@ void ItemRendererOpenGL::createRenderNode(Item *item, RenderContext *context) .hasAlpha = true, .coordinateType = UnnormalizedCoordinates, .colorDescription = item->colorDescription(), + .renderingIntent = item->renderingIntent(), .bufferReleasePoint = nullptr, }); } @@ -179,6 +180,7 @@ void ItemRendererOpenGL::createRenderNode(Item *item, RenderContext *context) .hasAlpha = true, .coordinateType = UnnormalizedCoordinates, .colorDescription = item->colorDescription(), + .renderingIntent = item->renderingIntent(), .bufferReleasePoint = nullptr, }); } @@ -195,6 +197,7 @@ void ItemRendererOpenGL::createRenderNode(Item *item, RenderContext *context) .hasAlpha = pixmap->hasAlphaChannel(), .coordinateType = NormalizedCoordinates, .colorDescription = item->colorDescription(), + .renderingIntent = item->renderingIntent(), .bufferReleasePoint = surfaceItem->bufferReleasePoint(), }); } @@ -209,6 +212,7 @@ void ItemRendererOpenGL::createRenderNode(Item *item, RenderContext *context) .hasAlpha = imageItem->image().hasAlphaChannel(), .coordinateType = NormalizedCoordinates, .colorDescription = item->colorDescription(), + .renderingIntent = item->renderingIntent(), .bufferReleasePoint = nullptr, }); } @@ -369,7 +373,7 @@ void ItemRendererOpenGL::renderItem(const RenderTarget &renderTarget, const Rend shader->setUniform(GLShader::Vec4Uniform::ModulationConstant, modulate(renderNode.opacity, data.brightness())); } if (traits & ShaderTrait::TransformColorspace) { - shader->setColorspaceUniforms(renderNode.colorDescription, renderTarget.colorDescription()); + shader->setColorspaceUniforms(renderNode.colorDescription, renderTarget.colorDescription(), renderNode.renderingIntent); } if (std::holds_alternative(renderNode.texture)) { diff --git a/src/scene/itemrenderer_opengl.h b/src/scene/itemrenderer_opengl.h index fe2e22569d..af87221810 100644 --- a/src/scene/itemrenderer_opengl.h +++ b/src/scene/itemrenderer_opengl.h @@ -31,6 +31,7 @@ public: bool hasAlpha = false; TextureCoordinateType coordinateType = UnnormalizedCoordinates; ColorDescription colorDescription; + RenderingIntent renderingIntent; std::shared_ptr bufferReleasePoint; }; diff --git a/src/scene/surfaceitem_wayland.cpp b/src/scene/surfaceitem_wayland.cpp index 51510ad08b..0b439c8d84 100644 --- a/src/scene/surfaceitem_wayland.cpp +++ b/src/scene/surfaceitem_wayland.cpp @@ -201,6 +201,7 @@ void SurfaceItemWayland::freeze() void SurfaceItemWayland::handleColorDescriptionChanged() { setColorDescription(m_surface->colorDescription()); + setRenderingIntent(m_surface->renderingIntent()); } void SurfaceItemWayland::handlePresentationModeHintChanged() diff --git a/src/wayland/surface.cpp b/src/wayland/surface.cpp index 6f2a601d81..d4779b3a5b 100644 --- a/src/wayland/surface.cpp +++ b/src/wayland/surface.cpp @@ -1107,6 +1107,11 @@ const ColorDescription &SurfaceInterface::colorDescription() const return d->current->colorDescription; } +RenderingIntent SurfaceInterface::renderingIntent() const +{ + return d->current->renderingIntent; +} + void SurfaceInterface::setPreferredColorDescription(const ColorDescription &descr) { if (d->preferredColorDescription == descr) { diff --git a/src/wayland/surface.h b/src/wayland/surface.h index 152f1accc1..0b713d5bae 100644 --- a/src/wayland/surface.h +++ b/src/wayland/surface.h @@ -340,6 +340,7 @@ public: void setLastTransaction(Transaction *transaction); const ColorDescription &colorDescription() const; + RenderingIntent renderingIntent() const; void setPreferredColorDescription(const ColorDescription &descr); diff --git a/src/wayland/surface_p.h b/src/wayland/surface_p.h index 66e217c143..2c11f515e9 100644 --- a/src/wayland/surface_p.h +++ b/src/wayland/surface_p.h @@ -72,6 +72,7 @@ struct SurfaceState ContentType contentType = ContentType::None; PresentationModeHint presentationHint = PresentationModeHint::VSync; ColorDescription colorDescription = ColorDescription::sRGB; + RenderingIntent renderingIntent = RenderingIntent::Perceptual; std::unique_ptr presentationFeedback; struct { diff --git a/src/wayland/xx_colormanagement_v4.cpp b/src/wayland/xx_colormanagement_v4.cpp index 5f23430a2a..1e55d9a6cd 100644 --- a/src/wayland/xx_colormanagement_v4.cpp +++ b/src/wayland/xx_colormanagement_v4.cpp @@ -39,7 +39,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_relative); - // TODO implement the other rendering intents + send_supported_intent(resource->handle, render_intent::render_intent_absolute); + // TODO implement saturation and relative bpc intents } void XXColorManagerV4::xx_color_manager_v4_destroy(Resource *resource) @@ -143,15 +144,36 @@ void XXColorSurfaceV4::xx_color_management_surface_v4_destroy(Resource *resource wl_resource_destroy(resource->handle); } +static std::optional waylandToKwinIntent(uint32_t intent) +{ + switch (intent) { + case QtWaylandServer::xx_color_manager_v4::render_intent::render_intent_perceptual: + return RenderingIntent::Perceptual; + case QtWaylandServer::xx_color_manager_v4::render_intent::render_intent_relative: + return RenderingIntent::RelativeColorimetric; + case QtWaylandServer::xx_color_manager_v4::render_intent::render_intent_absolute: + return RenderingIntent::AbsoluteColorimetric; + case QtWaylandServer::xx_color_manager_v4::render_intent::render_intent_saturation: + case QtWaylandServer::xx_color_manager_v4::render_intent::render_intent_relative_bpc: + return std::nullopt; + } + return std::nullopt; +} + void XXColorSurfaceV4::xx_color_management_surface_v4_set_image_description(Resource *resource, struct ::wl_resource *image_description, uint32_t render_intent) { if (!m_surface) { return; } + const std::optional intent = waylandToKwinIntent(render_intent); + if (!intent) { + wl_resource_post_error(resource->handle, XX_COLOR_MANAGER_V4_ERROR_UNSUPPORTED_FEATURE, "rendering intent is not supported"); + return; + } const auto priv = SurfaceInterfacePrivate::get(m_surface); priv->pending->colorDescription = XXImageDescriptionV4::get(image_description)->description(); + priv->pending->renderingIntent = *intent; priv->pending->colorDescriptionIsSet = true; - // TODO render_intent } void XXColorSurfaceV4::xx_color_management_surface_v4_unset_image_description(Resource *resource)