From 9abf7a9d619041f1fe4f6e15507ef7a4c3b737b8 Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Sun, 11 Feb 2024 21:08:40 +0200 Subject: [PATCH] plugins/blur: Fix blurred region sticking outside panel popups The blur geometry is scaled in the global coordinate space, while it works fine with integer scale factors, it's not okay with fractional scale factors as it doesn't match how the ItemRenderer snaps quads to the pixel grid. Note that blur behind apps can be still off by one pixel, but it should be harder to notice it. In order to fix it, it would be great to apply effects behind Items, which is on my todo list. --- src/plugins/blur/blur.cpp | 42 +++++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/src/plugins/blur/blur.cpp b/src/plugins/blur/blur.cpp index 89e066b548..d16c4135ad 100644 --- a/src/plugins/blur/blur.cpp +++ b/src/plugins/blur/blur.cpp @@ -10,6 +10,7 @@ // KConfigSkeleton #include "blurconfig.h" +#include "core/pixelgrid.h" #include "core/rendertarget.h" #include "core/renderviewport.h" #include "effect/effecthandler.h" @@ -559,8 +560,29 @@ void BlurEffect::blur(const RenderTarget &renderTarget, const RenderViewport &vi blurShape = translated; } + const QRect backgroundRect = blurShape.boundingRect(); + const QRect deviceBackgroundRect = snapToPixelGrid(scaledRect(backgroundRect, viewport.scale())); + const auto opacity = w->opacity() * data.opacity(); + // Get the effective shape that will be actually blurred. It's possible that all of it will be clipped. - const QRegion effectiveShape = blurShape & region; + QList effectiveShape; + effectiveShape.reserve(blurShape.rectCount()); + if (region != infiniteRegion()) { + for (const QRect &clipRect : region) { + const QRectF deviceClipRect = snapToPixelGridF(scaledRect(clipRect, viewport.scale())) + .translated(-deviceBackgroundRect.topLeft()); + for (const QRect &shapeRect : blurShape) { + const QRectF deviceShapeRect = snapToPixelGridF(scaledRect(shapeRect.translated(-backgroundRect.topLeft()), viewport.scale())); + if (const QRectF intersected = deviceClipRect.intersected(deviceShapeRect); !intersected.isEmpty()) { + effectiveShape.append(intersected); + } + } + } + } else { + for (const QRect &rect : blurShape) { + effectiveShape.append(snapToPixelGridF(scaledRect(rect.translated(-backgroundRect.topLeft()), viewport.scale()))); + } + } if (effectiveShape.isEmpty()) { return; } @@ -572,10 +594,6 @@ void BlurEffect::blur(const RenderTarget &renderTarget, const RenderViewport &vi textureFormat = renderTarget.texture()->internalFormat(); } - const QRect backgroundRect = blurShape.boundingRect(); - const QRect deviceBackgroundRect = scaledRect(backgroundRect, viewport.scale()).toRect(); - const auto opacity = w->opacity() * data.opacity(); - if (renderInfo.framebuffers.size() != (m_iterationCount + 1) || renderInfo.textures[0]->size() != backgroundRect.size() || renderInfo.textures[0]->internalFormat() != textureFormat) { renderInfo.framebuffers.clear(); renderInfo.textures.clear(); @@ -611,7 +629,7 @@ void BlurEffect::blur(const RenderTarget &renderTarget, const RenderViewport &vi vbo->reset(); vbo->setAttribLayout(std::span(GLVertexBuffer::GLVertex2DLayout), sizeof(GLVertex2D)); - const int vertexCount = effectiveShape.rectCount() * 6; + const int vertexCount = effectiveShape.size() * 6; if (auto result = vbo->map(6 + vertexCount)) { auto map = *result; @@ -661,13 +679,11 @@ void BlurEffect::blur(const RenderTarget &renderTarget, const RenderViewport &vi } // The geometry that will be painted on screen, in device pixels. - for (const QRect &rect : effectiveShape) { - const QRectF localRect = scaledRect(rect, viewport.scale()).translated(-deviceBackgroundRect.topLeft()); - - const float x0 = std::round(localRect.left()); - const float y0 = std::round(localRect.top()); - const float x1 = std::round(localRect.right()); - const float y1 = std::round(localRect.bottom()); + for (const QRectF &rect : effectiveShape) { + const float x0 = rect.left(); + const float y0 = rect.top(); + const float x1 = rect.right(); + const float y1 = rect.bottom(); const float u0 = x0 / deviceBackgroundRect.width(); const float v0 = 1.0f - y0 / deviceBackgroundRect.height();