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.
This commit is contained in:
Vlad Zahorodnii 2024-02-11 21:08:40 +02:00
parent 2ca1d3fd4c
commit 9abf7a9d61

View file

@ -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<QRectF> 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<GLVertex2D>(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();