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)