diff --git a/README.md b/README.md
index d162c5005..485ca67a0 100644
--- a/README.md
+++ b/README.md
@@ -1,16 +1,22 @@
# kwin-effects-forceblur [![AUR Version](https://img.shields.io/aur/version/kwin-effects-forceblur)](https://aur.archlinux.org/packages/kwin-effects-forceblur)
-A fork of the KWin Blur effect for KDE Plasma 6 with the ability to blur any window on Wayland and X11.
+Kwin-effects-forceblur (name subject to change) is a fork of the KWin Blur effect for KDE Plasma 6 with several improvements and bug fixes.
Latest features are available on the ``develop`` branch.
-![image](https://github.com/taj-ny/kwin-effects-forceblur/assets/79316397/9d2f337e-badd-4d95-ba55-96c80202e196)
-Window opacity has been set to 85% for System Settings and Dolphin, Firefox uses a transparent theme | [NixOS configuration](https://github.com/taj-ny/nix-config)
+![image](https://github.com/taj-ny/kwin-effects-forceblur/assets/79316397/1078cf12-e6da-43c7-80b4-d90a8b0f3404)
+Window opacity has been set to 85% for System Settings, Dolphin and VSCodium, Firefox uses a transparent theme | [NixOS configuration](https://github.com/taj-ny/nix-config)
# Features
- Wayland support
+- Force blur
+- Rounded corners with optional anti-aliasing
- Draw image behind windows instead of blurring (can be used with a blurred image of the wallpaper in order to achieve a very similar effect to blur but with **much** lower GPU usage)
-- Rounded corners
-- Fix for [artifacts](https://github.com/taj-ny/kwin-effects-forceblur/pull/38) when using a transparent color scheme
+
+### Bug fixes
+Fixes for blur-related Plasma bugs that haven't been patched yet.
+
+- Blur may sometimes disappear during animations
+- [Transparent color schemes don't work properly with the Breeze application style](https://github.com/taj-ny/kwin-effects-forceblur/pull/38)
# Installation
diff --git a/flake.lock b/flake.lock
index 88663c665..409ad87c0 100644
--- a/flake.lock
+++ b/flake.lock
@@ -2,11 +2,11 @@
"nodes": {
"nixpkgs": {
"locked": {
- "lastModified": 1716509168,
- "narHash": "sha256-4zSIhSRRIoEBwjbPm3YiGtbd8HDWzFxJjw5DYSDy1n8=",
+ "lastModified": 1718160348,
+ "narHash": "sha256-9YrUjdztqi4Gz8n3mBuqvCkMo4ojrA6nASwyIKWMpus=",
"owner": "NixOS",
"repo": "nixpkgs",
- "rev": "bfb7a882678e518398ce9a31a881538679f6f092",
+ "rev": "57d6973abba7ea108bac64ae7629e7431e0199b6",
"type": "github"
},
"original": {
diff --git a/src/blur.cpp b/src/blur.cpp
index 9ee260ddb..645c86506 100644
--- a/src/blur.cpp
+++ b/src/blur.cpp
@@ -14,7 +14,9 @@
#include "core/rendertarget.h"
#include "core/renderviewport.h"
#include "effect/effecthandler.h"
+#include "opengl/glutils.h"
#include "opengl/glplatform.h"
+#include "utils.h"
#include "utils/xcbutils.h"
#include "wayland/blur.h"
#include "wayland/display.h"
@@ -93,15 +95,41 @@ BlurEffect::BlurEffect()
}
m_texturePass.shader = ShaderManager::instance()->generateShaderFromFile(ShaderTrait::MapTexture,
- QStringLiteral(":/effects/blur/shaders/vertex.vert"),
- QStringLiteral(":/effects/blur/shaders/texture.frag"));
+ QStringLiteral(":/effects/blur/shaders/vertex.vert"),
+ QStringLiteral(":/effects/blur/shaders/texture.frag"));
if (!m_texturePass.shader) {
- qCWarning(KWIN_BLUR) << "Failed to load noise pass shader";
+ qCWarning(KWIN_BLUR) << "Failed to load texture pass shader";
return;
} else {
m_texturePass.mvpMatrixLocation = m_texturePass.shader->uniformLocation("modelViewProjectionMatrix");
m_texturePass.textureSizeLocation = m_texturePass.shader->uniformLocation("textureSize");
m_texturePass.texStartPosLocation = m_texturePass.shader->uniformLocation("texStartPos");
+ m_texturePass.regionSizeLocation = m_texturePass.shader->uniformLocation("regionSize");
+ }
+
+ m_roundedCorners.shader = ShaderManager::instance()->generateShaderFromFile(ShaderTrait::MapTexture,
+ QStringLiteral(":/effects/blur/shaders/vertex.vert"),
+ QStringLiteral(":/effects/blur/shaders/roundedcorners.frag"));
+ if (!m_roundedCorners.shader) {
+ qCWarning(KWIN_BLUR) << "Failed to load rounded corners shader";
+ return;
+ } else {
+ m_roundedCorners.roundTopLeftCornerLocation = m_roundedCorners.shader->uniformLocation("roundTopLeftCorner");
+ m_roundedCorners.roundTopRightCornerLocation = m_roundedCorners.shader->uniformLocation("roundTopRightCorner");
+ m_roundedCorners.roundBottomLeftCornerLocation = m_roundedCorners.shader->uniformLocation("roundBottomLeftCorner");
+ m_roundedCorners.roundBottomRightCornerLocation = m_roundedCorners.shader->uniformLocation("roundBottomRightCorner");
+
+ m_roundedCorners.topCornerRadiusLocation = m_roundedCorners.shader->uniformLocation("topCornerRadius");
+ m_roundedCorners.bottomCornerRadiusLocation = m_roundedCorners.shader->uniformLocation("bottomCornerRadius");
+
+ m_roundedCorners.antialiasingLocation = m_roundedCorners.shader->uniformLocation("antialiasing");
+
+ m_roundedCorners.regionSizeLocation = m_roundedCorners.shader->uniformLocation("regionSize");
+
+ m_roundedCorners.beforeBlurTextureLocation = m_roundedCorners.shader->uniformLocation("beforeBlurTexture");
+ m_roundedCorners.afterBlurTextureLocation = m_roundedCorners.shader->uniformLocation("afterBlurTexture");
+
+ m_roundedCorners.mvpMatrixLocation = m_roundedCorners.shader->uniformLocation("modelViewProjectionMatrix");
}
initBlurStrengthValues();
@@ -223,8 +251,11 @@ void BlurEffect::reconfigure(ReconfigureFlags flags)
m_blurDecorations = BlurConfig::blurDecorations();
m_windowClasses = BlurConfig::windowClasses().split("\n");
m_transparentBlur = BlurConfig::transparentBlur();
- m_topCornerRadius = BlurConfig::topCornerRadius();
- m_bottomCornerRadius = BlurConfig::bottomCornerRadius();
+ m_windowTopCornerRadius = BlurConfig::topCornerRadius();
+ m_windowBottomCornerRadius = BlurConfig::bottomCornerRadius();
+ m_menuCornerRadius = BlurConfig::menuCornerRadius();
+ m_dockCornerRadius = BlurConfig::dockCornerRadius();
+ m_roundedCornersAntialiasing = BlurConfig::roundedCornersAntialiasing();
m_roundCornersOfMaximizedWindows = BlurConfig::roundCornersOfMaximizedWindows();
m_blurMenus = BlurConfig::blurMenus();
m_blurDocks = BlurConfig::blurDocks();
@@ -232,13 +263,16 @@ void BlurEffect::reconfigure(ReconfigureFlags flags)
m_fakeBlur = BlurConfig::fakeBlur();
m_fakeBlurImage = BlurConfig::fakeBlurImage();
+ // Antialiasing does take up a bit of space, so the corner radius will be reduced by the offset in order to leave some space.
+ m_cornerRadiusOffset = m_roundedCornersAntialiasing == 0 ? 0 : std::round(m_roundedCornersAntialiasing) + 2;
+
QImage fakeBlurImage(m_fakeBlurImage);
m_hasValidFakeBlurTexture = !fakeBlurImage.isNull();
if (m_hasValidFakeBlurTexture) {
m_texturePass.texture = GLTexture::upload(fakeBlurImage);
}
- updateCornerRegions();
+ m_corners.clear();
for (EffectWindow *w : effects->stackingOrder()) {
updateBlurRegion(w);
@@ -291,7 +325,6 @@ void BlurEffect::updateBlurRegion(EffectWindow *w)
if (shouldForceBlur(w)) {
content = w->expandedGeometry().toRect().translated(-w->x(), -w->y());
if (m_blurDecorations && w->decoration()) {
- const QMargins borders = w->decoration()->borders();
frame = w->frameGeometry().toRect().translated(-w->x(), -w->y());
}
}
@@ -308,34 +341,115 @@ void BlurEffect::updateBlurRegion(EffectWindow *w)
}
}
-void BlurEffect::updateCornerRegions()
+void BlurEffect::generateRoundedCornerMasks(int radius, QRegion &left, QRegion &right, bool top) const
+{
+ // This method uses OpenGL to draw circles, since the ones drawn by Qt are terrible.
+
+ left = QRegion();
+ right = QRegion();
+
+ if (radius == 0) {
+ return;
+ }
+
+ float size = radius * 2;
+ auto cornersTexture = GLTexture::allocate(GL_RGB8, QSize(size, size));
+ auto cornersFramebuffer = std::make_unique(cornersTexture.get());
+
+ GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer();
+ vbo->reset();
+ vbo->setAttribLayout(std::span(GLVertexBuffer::GLVertex2DLayout), sizeof(GLVertex2D));
+ if (auto result = vbo->map(6)) {
+ auto map = *result;
+
+ size_t vboIndex = 0;
+
+ const float x0 = 0;
+ const float y0 = 0;
+ const float x1 = size;
+ const float y1 = size;
+
+ const float u0 = x0 / size;
+ const float v0 = 1.0f - y0 / size;
+ const float u1 = x1 / size;
+ const float v1 = 1.0f - y1 / size;
+
+ // first triangle
+ map[vboIndex++] = GLVertex2D{
+ .position = QVector2D(x0, y0),
+ .texcoord = QVector2D(u0, v0),
+ };
+ map[vboIndex++] = GLVertex2D{
+ .position = QVector2D(x1, y1),
+ .texcoord = QVector2D(u1, v1),
+ };
+ map[vboIndex++] = GLVertex2D{
+ .position = QVector2D(x0, y1),
+ .texcoord = QVector2D(u0, v1),
+ };
+
+ // second triangle
+ map[vboIndex++] = GLVertex2D{
+ .position = QVector2D(x0, y0),
+ .texcoord = QVector2D(u0, v0),
+ };
+ map[vboIndex++] = GLVertex2D{
+ .position = QVector2D(x1, y0),
+ .texcoord = QVector2D(u1, v0),
+ };
+ map[vboIndex++] = GLVertex2D{
+ .position = QVector2D(x1, y1),
+ .texcoord = QVector2D(u1, v1),
+ };
+
+ vbo->unmap();
+ } else {
+ qCWarning(KWIN_BLUR) << "Failed to map vertex buffer";
+ return;
+ }
+
+ vbo->bindArrays();
+
+ QMatrix4x4 projectionMatrix;
+ projectionMatrix.ortho(QRect(0, 0, size, size));
+
+ ShaderManager::instance()->pushShader(m_roundedCorners.shader.get());
+ m_roundedCorners.shader->setUniform(m_roundedCorners.roundTopLeftCornerLocation, false);
+ m_roundedCorners.shader->setUniform(m_roundedCorners.roundTopRightCornerLocation, false);
+ m_roundedCorners.shader->setUniform(m_roundedCorners.roundBottomLeftCornerLocation, true);
+ m_roundedCorners.shader->setUniform(m_roundedCorners.roundBottomRightCornerLocation, true);
+ m_roundedCorners.shader->setUniform(m_roundedCorners.topCornerRadiusLocation, static_cast(0));
+ m_roundedCorners.shader->setUniform(m_roundedCorners.bottomCornerRadiusLocation, radius);
+ m_roundedCorners.shader->setUniform(m_roundedCorners.antialiasingLocation, static_cast(0));
+ m_roundedCorners.shader->setUniform(m_roundedCorners.regionSizeLocation, QVector2D(size, size));
+ m_roundedCorners.shader->setUniform(m_roundedCorners.mvpMatrixLocation, projectionMatrix);
+
+ GLFramebuffer::pushFramebuffer(cornersFramebuffer.get());
+ vbo->draw(GL_TRIANGLES, 0, 6);
+ GLFramebuffer::popFramebuffer();
+ ShaderManager::instance()->popShader();
+ vbo->unbindArrays();
+
+ QImage img = cornersTexture->toImage().mirrored().copy(0, 0, radius, radius);
+ if (!top) {
+ img.mirror();
+ }
+
+ left = QRegion(QBitmap::fromImage(img.createMaskFromColor(QColor(Qt::black).rgb(), Qt::MaskOutColor), Qt::DiffuseAlphaDither));
+ right = QRegion(QBitmap::fromImage(img.mirrored(true, false).createMaskFromColor(QColor(Qt::black).rgb(), Qt::MaskOutColor), Qt::DiffuseAlphaDither));;
+}
+
+std::array BlurEffect::roundedCorners(int topCornerRadius, int bottomCornerRadius, qreal scale)
{
- QRegion square = QRegion(0, 0, m_topCornerRadius, m_topCornerRadius);
- QRegion circle = QRegion(0, 0, 2 * m_topCornerRadius, 2 * m_topCornerRadius, QRegion::RegionType::Ellipse);
- m_topLeftCorner = QRegion(0, 0, m_topCornerRadius, m_topCornerRadius);
- m_topRightCorner = QRegion(0, 0, m_topCornerRadius, m_topCornerRadius);
-
- m_topLeftCorner &= circle;
- m_topLeftCorner ^= square;
- circle.translate(-m_topCornerRadius, 0);
- m_topRightCorner &= circle;
- m_topRightCorner ^= square;
-
- square = QRegion(0, 0, m_bottomCornerRadius, m_bottomCornerRadius);
- circle = QRegion(0, 0, 2 * m_bottomCornerRadius, 2 * m_bottomCornerRadius, QRegion::RegionType::Ellipse);
-
- m_bottomLeftCorner = QRegion(0, 0, m_bottomCornerRadius, m_bottomCornerRadius);
- m_bottomRightCorner = QRegion(0, 0, m_bottomCornerRadius, m_bottomCornerRadius);
- circle.translate(0, -m_bottomCornerRadius);
- m_bottomLeftCorner &= circle;
- m_bottomLeftCorner ^= square;
-
- circle.translate(0, m_bottomCornerRadius);
- circle.translate(-m_bottomCornerRadius, 0);
- circle.translate(0, -m_bottomCornerRadius);
-
- m_bottomRightCorner &= circle;
- m_bottomRightCorner ^= square;
+ const auto key = std::make_tuple(topCornerRadius, bottomCornerRadius, scale);
+ if (m_corners.contains(key)) {
+ return m_corners[key];
+ }
+
+ std::array corners;
+ generateRoundedCornerMasks(topCornerRadius, corners[0], corners[1], true);
+ generateRoundedCornerMasks(bottomCornerRadius, corners[2], corners[3], false);
+ return m_corners[key] = corners;
}
void BlurEffect::slotWindowAdded(EffectWindow *w)
@@ -458,7 +572,7 @@ QRegion BlurEffect::decorationBlurRegion(const EffectWindow *w) const
return decorationRegion.intersected(w->decoration()->blurRegion());
}
-QRegion BlurEffect::blurRegion(EffectWindow *w, bool noRoundedCorners) const
+QRegion BlurEffect::blurRegion(EffectWindow *w) const
{
QRegion region;
@@ -481,23 +595,27 @@ QRegion BlurEffect::blurRegion(EffectWindow *w, bool noRoundedCorners) const
}
}
- bool isMaximized = effects->clientArea(MaximizeArea, effects->activeScreen(), effects->currentDesktop()) == w->frameGeometry();
- if (!w->isDock() && !noRoundedCorners && (!isMaximized || m_roundCornersOfMaximizedWindows)) {
- if (m_topCornerRadius && (!w->decoration() || (w->decoration() && m_blurDecorations))) {
- QPoint topRightPosition = QPoint(w->rect().width() - m_topCornerRadius, 0);
- region -= m_topLeftCorner;
- region -= m_topRightCorner.translated(topRightPosition);
- }
+ return region;
+}
- if (m_bottomCornerRadius) {
- QPoint bottomLeftPosition = QPoint(0, w->rect().height() - m_bottomCornerRadius);
- QPoint bottomRightPosition = QPoint(w->rect().width() - m_bottomCornerRadius, w->rect().height() - m_bottomCornerRadius);
- region -= m_bottomLeftCorner.translated(bottomLeftPosition);
- region -= m_bottomRightCorner.translated(bottomRightPosition);
+QRegion BlurEffect::transformedBlurRegion(QRegion blurRegion, const WindowPaintData &data) const
+{
+ if (data.xScale() != 1 || data.yScale() != 1) {
+ QPoint pt = blurRegion.boundingRect().topLeft();
+ QRegion scaledShape;
+ for (const QRect &r : blurRegion) {
+ const QPointF topLeft(pt.x() + (r.x() - pt.x()) * data.xScale() + data.xTranslation(),
+ pt.y() + (r.y() - pt.y()) * data.yScale() + data.yTranslation());
+ const QPoint bottomRight(std::floor(topLeft.x() + r.width() * data.xScale()) - 1,
+ std::floor(topLeft.y() + r.height() * data.yScale()) - 1);
+ scaledShape += QRect(QPoint(std::floor(topLeft.x()), std::floor(topLeft.y())), bottomRight);
}
+ blurRegion = scaledShape;
+ } else if (data.xTranslation() || data.yTranslation()) {
+ blurRegion.translate(std::round(data.xTranslation()), std::round(data.yTranslation()));
}
- return region;
+ return blurRegion;
}
void BlurEffect::prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime)
@@ -519,17 +637,31 @@ void BlurEffect::prePaintWindow(EffectWindow *w, WindowPrePaintData &data, std::
bool hasFakeBlur = m_fakeBlur && m_hasValidFakeBlurTexture && !blurArea.isEmpty();
if (hasFakeBlur) {
data.opaque += blurArea;
- }
- if (shouldForceBlur(w) && m_paintAsTranslucent) {
- if (hasFakeBlur) {
- // Remove rounded corners region
- data.opaque -= blurRegion(w, true).translated(w->pos().toPoint()) - blurArea;
+ int topCornerRadius;
+ int bottomCornerRadius;
+ if (isMenu(w)) {
+ topCornerRadius = bottomCornerRadius = m_menuCornerRadius;
+ } else if (w->isDock()) {
+ topCornerRadius = bottomCornerRadius = m_dockCornerRadius;
} else {
- data.setTranslucent();
+ topCornerRadius = m_windowTopCornerRadius;
+ bottomCornerRadius = m_windowBottomCornerRadius;
+ }
+
+ if (!w->isDock() || (w->isDock() && isDockFloating(w, blurArea))) {
+ const QRect blurRect = blurArea.boundingRect();
+ data.opaque -= QRect(blurRect.x(), blurRect.y(), topCornerRadius, topCornerRadius);
+ data.opaque -= QRect(blurRect.x() + blurRect.width() - topCornerRadius, blurRect.y(), topCornerRadius, topCornerRadius);
+ data.opaque -= QRect(blurRect.x(), blurRect.y() + blurRect.height() - bottomCornerRadius, bottomCornerRadius, bottomCornerRadius);
+ data.opaque -= QRect(blurRect.x() + blurRect.width() - bottomCornerRadius, blurRect.y() + blurRect.height() - bottomCornerRadius, bottomCornerRadius, bottomCornerRadius);
}
}
+ if (shouldForceBlur(w) && m_paintAsTranslucent) {
+ data.setTranslucent();
+ }
+
effects->prePaintWindow(w, data, presentTime);
if (!hasFakeBlur) {
@@ -605,7 +737,7 @@ bool BlurEffect::shouldBlur(const EffectWindow *w, int mask, const WindowPaintDa
bool BlurEffect::shouldForceBlur(const EffectWindow *w) const
{
- if ((!m_blurDocks && w->isDock()) || (!m_blurMenus && (w->isMenu() || w->isDropdownMenu() || w->isPopupMenu() || w->isPopupWindow()))) {
+ if ((!m_blurDocks && w->isDock()) || (!m_blurMenus && isMenu(w))) {
return false;
}
@@ -671,52 +803,99 @@ void BlurEffect::blur(const RenderTarget &renderTarget, const RenderViewport &vi
return;
}
- // Compute the effective blur shape. Note that if the window is transformed, so will be the blur shape.
- QRegion blurShape = blurRegion(w).translated(w->pos().toPoint());
- if (data.xScale() != 1 || data.yScale() != 1) {
- QPoint pt = blurShape.boundingRect().topLeft();
- QRegion scaledShape;
- for (const QRect &r : blurShape) {
- const QPointF topLeft(pt.x() + (r.x() - pt.x()) * data.xScale() + data.xTranslation(),
- pt.y() + (r.y() - pt.y()) * data.yScale() + data.yTranslation());
- const QPoint bottomRight(std::floor(topLeft.x() + r.width() * data.xScale()) - 1,
- std::floor(topLeft.y() + r.height() * data.yScale()) - 1);
- scaledShape += QRect(QPoint(std::floor(topLeft.x()), std::floor(topLeft.y())), bottomRight);
- }
- blurShape = scaledShape;
- } else if (data.xTranslation() || data.yTranslation()) {
- blurShape.translate(std::round(data.xTranslation()), std::round(data.yTranslation()));
+ int topCornerRadius;
+ int bottomCornerRadius;
+ if (isMenu(w)) {
+ topCornerRadius = bottomCornerRadius = m_menuCornerRadius;
+ } else if (w->isDock()) {
+ topCornerRadius = bottomCornerRadius = m_dockCornerRadius;
+ } else {
+ topCornerRadius = m_windowTopCornerRadius;
+ bottomCornerRadius = m_windowBottomCornerRadius;
}
+ topCornerRadius = std::max(0, (int)std::round(topCornerRadius * viewport.scale()) - m_cornerRadiusOffset);
+ bottomCornerRadius = std::max(0, (int)std::round(bottomCornerRadius * viewport.scale()) - m_cornerRadiusOffset);
+ bool hasRoundedCorners = topCornerRadius || bottomCornerRadius;
+
+ const QRegion rawBlurRegion = blurRegion(w);
+ const QRegion blurShape = transformedBlurRegion(rawBlurRegion.translated(w->pos().toPoint()), data);
const QRect backgroundRect = blurShape.boundingRect();
+
+ // The blur shape has to be moved to 0,0 before being scaled, otherwise the size may end up being off by 1 pixel.
+ QRegion scaledBlurShape = scaledRegion(blurShape.translated(-backgroundRect.topLeft()), viewport.scale());
const QRect deviceBackgroundRect = snapToPixelGrid(scaledRect(backgroundRect, viewport.scale()));
+
+ bool roundTopLeftCorner = false;
+ bool roundTopRightCorner = false;
+ bool roundBottomLeftCorner = false;
+ bool roundBottomRightCorner = false;
+ const bool isMaximized = effects->clientArea(MaximizeArea, effects->activeScreen(), effects->currentDesktop()) == w->frameGeometry();
+ if (hasRoundedCorners && ((!w->isFullScreen() && !isMaximized) || m_roundCornersOfMaximizedWindows)) {
+ if (w->isDock()) {
+ if (isDockFloating(w, rawBlurRegion)) {
+ roundTopLeftCorner = roundTopRightCorner = topCornerRadius;
+ roundBottomLeftCorner = roundBottomRightCorner = bottomCornerRadius;
+ }
+ } else {
+ // Ensure the blur region corners touch the window corners before rounding them.
+ if (topCornerRadius && (!w->decoration() || (w->decoration() && m_blurDecorations))) {
+ roundTopLeftCorner = rawBlurRegion.intersects(QRect(0, 0, topCornerRadius, topCornerRadius));
+ roundTopRightCorner = rawBlurRegion.intersects(QRect(w->width() - topCornerRadius, 0, topCornerRadius, topCornerRadius));
+ }
+ if (bottomCornerRadius) {
+ roundBottomLeftCorner = rawBlurRegion.intersects(QRect(0, w->height() - bottomCornerRadius, bottomCornerRadius, bottomCornerRadius));
+ roundBottomRightCorner = rawBlurRegion.intersects(QRect(w->width() - bottomCornerRadius, w->height() - bottomCornerRadius, bottomCornerRadius, bottomCornerRadius));
+ }
+ hasRoundedCorners = roundTopLeftCorner || roundTopRightCorner || roundBottomLeftCorner || roundBottomRightCorner;
+ }
+
+ const auto corners = roundedCorners(topCornerRadius, bottomCornerRadius, viewport.scale());
+ const QRect blurRect = scaledBlurShape.boundingRect();
+
+ if (roundTopLeftCorner) {
+ scaledBlurShape -= corners[0];
+ }
+ if (roundTopRightCorner) {
+ scaledBlurShape -= corners[1].translated(blurRect.width() - topCornerRadius, 0);
+ }
+
+ if (roundBottomLeftCorner) {
+ scaledBlurShape -= corners[2].translated(0, blurRect.height() - bottomCornerRadius);
+ }
+ if (roundBottomRightCorner) {
+ scaledBlurShape -= corners[3].translated(blurRect.width() - bottomCornerRadius, blurRect.height() - bottomCornerRadius);
+ }
+ }
+
const auto opacity = m_transparentBlur
? w->opacity() * data.opacity()
: data.opacity();
// Get the effective shape that will be actually blurred. It's possible that all of it will be clipped.
- QList effectiveShape;
- effectiveShape.reserve(blurShape.rectCount());
+ QRegion effectiveShape;
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);
+ const QRect deviceClipRect = snapToPixelGrid(scaledRect(clipRect, viewport.scale()))
+ .translated(-deviceBackgroundRect.topLeft());
+ for (const QRect &shapeRect : scaledBlurShape) {
+ const QRect deviceShapeRect = shapeRect;
+ if (const QRect intersected = deviceClipRect.intersected(deviceShapeRect); !intersected.isEmpty()) {
+ effectiveShape += intersected;
}
}
}
} else {
- for (const QRect &rect : blurShape) {
- effectiveShape.append(snapToPixelGridF(scaledRect(rect.translated(-backgroundRect.topLeft()), viewport.scale())));
+ for (const QRect &rect : scaledBlurShape) {
+ effectiveShape += rect;
}
}
- if (effectiveShape.isEmpty()) {
+ if (!effectiveShape.rectCount()) {
return;
}
+ const bool hasAntialiasedRoundedCorners = hasRoundedCorners && m_roundedCornersAntialiasing > 0;
+
// Maybe reallocate offscreen render targets. Keep in mind that the first one contains
// original background behind the window, it's not blurred.
GLenum textureFormat = GL_RGBA8;
@@ -748,6 +927,7 @@ void BlurEffect::blur(const RenderTarget &renderTarget, const RenderViewport &vi
}
// Fetch the pixels behind the shape that is going to be blurred.
+ // This framebuffer is left unchanged, so we can use that for rounding corners.
const QRegion dirtyRegion = region & backgroundRect;
for (const QRect &dirtyRect : dirtyRegion) {
renderInfo.framebuffers[0]->blitFromRenderTarget(renderTarget, viewport, dirtyRect, dirtyRect.translated(-backgroundRect.topLeft()));
@@ -759,7 +939,7 @@ void BlurEffect::blur(const RenderTarget &renderTarget, const RenderViewport &vi
vbo->reset();
vbo->setAttribLayout(std::span(GLVertexBuffer::GLVertex2DLayout), sizeof(GLVertex2D));
- const int vertexCount = effectiveShape.size() * 6;
+ const int vertexCount = effectiveShape.rectCount() * 6;
if (auto result = vbo->map(6 + vertexCount)) {
auto map = *result;
@@ -857,29 +1037,62 @@ void BlurEffect::blur(const RenderTarget &renderTarget, const RenderViewport &vi
vbo->bindArrays();
+ /*
+ * The anti-aliasing implementation is actually really bad, but that's the best I can do for now. Suprisingly,
+ * there are no performance issues.
+ *
+ * Anti-aliasing is done by a shader that paints rounded rectangles. It's a modified version of
+ * https://www.shadertoy.com/view/ldfSDj.
+ * The shader requires two textures: the blur region before being blurred and after being blurred.
+ * The first texture can simply be taken from renderInfo.textures[0], as it's left unchanged.
+ * The second texture is more tricky. We could just blit, but that's slow. A faster solution is to create a virtual
+ * framebuffer with a texture attached to it and paint the blur in that framebuffer instead of the screen.
+ *
+ * Since only a fragment of the window may be painted, the shader allows to toggle rounding for each corner.
+ */
+
+ const auto finalBlurTexture = GLTexture::allocate(textureFormat, backgroundRect.size());
+ finalBlurTexture->setFilter(GL_LINEAR);
+ finalBlurTexture->setWrapMode(GL_CLAMP_TO_EDGE);
+ const auto finalBlurFramebuffer = std::make_unique(finalBlurTexture.get());
+
if (m_fakeBlur && m_hasValidFakeBlurTexture) {
ShaderManager::instance()->pushShader(m_texturePass.shader.get());
- QMatrix4x4 projectionMatrix = viewport.projectionMatrix();
- projectionMatrix.translate(deviceBackgroundRect.x(), deviceBackgroundRect.y());
+ QMatrix4x4 projectionMatrix;
+ if (hasAntialiasedRoundedCorners) {
+ projectionMatrix.ortho(QRectF(0.0, 0.0, backgroundRect.width(), backgroundRect.height()));
+ GLFramebuffer::pushFramebuffer(finalBlurFramebuffer.get());
+ } else {
+ projectionMatrix = viewport.projectionMatrix();
+ projectionMatrix.translate(deviceBackgroundRect.x(), deviceBackgroundRect.y());
+ }
m_texturePass.shader->setUniform(m_texturePass.mvpMatrixLocation, projectionMatrix);
m_texturePass.shader->setUniform(m_texturePass.textureSizeLocation, QVector2D(m_texturePass.texture.get()->width(), m_texturePass.texture.get()->height()));
- m_texturePass.shader->setUniform(m_texturePass.texStartPosLocation, QVector2D(0, 0));
+ m_texturePass.shader->setUniform(m_texturePass.texStartPosLocation, QVector2D(backgroundRect.x(), backgroundRect.y()));
+ m_texturePass.shader->setUniform(m_texturePass.regionSizeLocation, QVector2D(backgroundRect.width(), backgroundRect.height()));
m_texturePass.texture.get()->bind();
- glEnable(GL_BLEND);
- float o = 1.0f - (opacity);
- o = 1.0f - o * o;
- glBlendColor(0, 0, 0, o);
- glBlendFunc(GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA);
+ if (!hasAntialiasedRoundedCorners) {
+ glEnable(GL_BLEND);
+ float o = 1.0f - (opacity);
+ o = 1.0f - o * o;
+ glBlendColor(0, 0, 0, o);
+ glBlendFunc(GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA);
+ }
- vbo->draw(GL_TRIANGLES, 6, vertexCount);
+ vbo->draw(GL_TRIANGLES, hasAntialiasedRoundedCorners ? 0 : 6, hasAntialiasedRoundedCorners ? 6 : vertexCount);
- glDisable(GL_BLEND);
+ if (!hasAntialiasedRoundedCorners) {
+ glDisable(GL_BLEND);
+ }
ShaderManager::instance()->popShader();
+ if (hasAntialiasedRoundedCorners) {
+ GLFramebuffer::popFramebuffer();
+ }
} else {
// The downsample pass of the dual Kawase algorithm: the background will be scaled down 50% every iteration.
{
@@ -896,7 +1109,7 @@ void BlurEffect::blur(const RenderTarget &renderTarget, const RenderViewport &vi
const auto &draw = renderInfo.framebuffers[i];
const QVector2D halfpixel(0.5 / read->colorAttachment()->width(),
- 0.5 / read->colorAttachment()->height());
+ 0.5 / read->colorAttachment()->height());
m_downsamplePass.shader->setUniform(m_downsamplePass.halfpixelLocation, halfpixel);
read->colorAttachment()->bind();
@@ -934,8 +1147,14 @@ void BlurEffect::blur(const RenderTarget &renderTarget, const RenderViewport &vi
GLFramebuffer::popFramebuffer();
const auto &read = renderInfo.framebuffers[1];
- projectionMatrix = viewport.projectionMatrix();
- projectionMatrix.translate(deviceBackgroundRect.x(), deviceBackgroundRect.y());
+ if (hasAntialiasedRoundedCorners) {
+ GLFramebuffer::pushFramebuffer(finalBlurFramebuffer.get());
+ projectionMatrix = QMatrix4x4();
+ projectionMatrix.ortho(QRectF(0.0, 0.0, backgroundRect.width(), backgroundRect.height()));
+ } else {
+ projectionMatrix = viewport.projectionMatrix();
+ projectionMatrix.translate(deviceBackgroundRect.x(), deviceBackgroundRect.y());
+ }
m_upsamplePass.shader->setUniform(m_upsamplePass.mvpMatrixLocation, projectionMatrix);
const QVector2D halfpixel(0.5 / read->colorAttachment()->width(),
@@ -945,7 +1164,7 @@ void BlurEffect::blur(const RenderTarget &renderTarget, const RenderViewport &vi
read->colorAttachment()->bind();
// Modulate the blurred texture with the window opacity if the window isn't opaque
- if (opacity < 1.0) {
+ if (!hasAntialiasedRoundedCorners && opacity < 1.0) {
glEnable(GL_BLEND);
float o = 1.0f - (opacity);
o = 1.0f - o * o;
@@ -953,9 +1172,9 @@ void BlurEffect::blur(const RenderTarget &renderTarget, const RenderViewport &vi
glBlendFunc(GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA);
}
- vbo->draw(GL_TRIANGLES, 6, vertexCount);
+ vbo->draw(GL_TRIANGLES, hasAntialiasedRoundedCorners ? 0 : 6, hasAntialiasedRoundedCorners ? 6 : vertexCount);
- if (opacity < 1.0) {
+ if (!hasAntialiasedRoundedCorners && opacity < 1.0) {
glDisable(GL_BLEND);
}
@@ -975,8 +1194,13 @@ void BlurEffect::blur(const RenderTarget &renderTarget, const RenderViewport &vi
if (GLTexture *noiseTexture = ensureNoiseTexture()) {
ShaderManager::instance()->pushShader(m_noisePass.shader.get());
- QMatrix4x4 projectionMatrix = viewport.projectionMatrix();
- projectionMatrix.translate(deviceBackgroundRect.x(), deviceBackgroundRect.y());
+ QMatrix4x4 projectionMatrix;
+ if (hasAntialiasedRoundedCorners) {
+ projectionMatrix.ortho(QRectF(0.0, 0.0, backgroundRect.width(), backgroundRect.height()));
+ } else {
+ projectionMatrix = viewport.projectionMatrix();
+ projectionMatrix.translate(deviceBackgroundRect.x(), deviceBackgroundRect.y());
+ }
m_noisePass.shader->setUniform(m_noisePass.mvpMatrixLocation, projectionMatrix);
m_noisePass.shader->setUniform(m_noisePass.noiseTextureSizeLocation, QVector2D(noiseTexture->width(), noiseTexture->height()));
@@ -984,13 +1208,60 @@ void BlurEffect::blur(const RenderTarget &renderTarget, const RenderViewport &vi
noiseTexture->bind();
- vbo->draw(GL_TRIANGLES, 6, vertexCount);
+ vbo->draw(GL_TRIANGLES, hasAntialiasedRoundedCorners ? 0 : 6, hasAntialiasedRoundedCorners ? 6 : vertexCount);
ShaderManager::instance()->popShader();
}
glDisable(GL_BLEND);
}
+
+ if (hasAntialiasedRoundedCorners) {
+ GLFramebuffer::popFramebuffer();
+ }
+ }
+
+ if (hasAntialiasedRoundedCorners) {
+ QMatrix4x4 projectionMatrix = viewport.projectionMatrix();
+ projectionMatrix.translate(deviceBackgroundRect.x(), deviceBackgroundRect.y());
+
+ // The Y axis is flipped in OpenGL.
+ // TODO Rename the uniforms
+ ShaderManager::instance()->pushShader(m_roundedCorners.shader.get());
+ m_roundedCorners.shader->setUniform(m_roundedCorners.roundTopLeftCornerLocation, roundBottomLeftCorner);
+ m_roundedCorners.shader->setUniform(m_roundedCorners.roundTopRightCornerLocation, roundBottomRightCorner);
+ m_roundedCorners.shader->setUniform(m_roundedCorners.roundBottomLeftCornerLocation, roundTopLeftCorner);
+ m_roundedCorners.shader->setUniform(m_roundedCorners.roundBottomRightCornerLocation, roundTopRightCorner);
+ m_roundedCorners.shader->setUniform(m_roundedCorners.topCornerRadiusLocation, bottomCornerRadius + m_cornerRadiusOffset);
+ m_roundedCorners.shader->setUniform(m_roundedCorners.bottomCornerRadiusLocation, topCornerRadius + m_cornerRadiusOffset);
+ m_roundedCorners.shader->setUniform(m_roundedCorners.antialiasingLocation, m_roundedCornersAntialiasing);
+ m_roundedCorners.shader->setUniform(m_roundedCorners.regionSizeLocation, QVector2D(deviceBackgroundRect.width(), deviceBackgroundRect.height()));
+ m_roundedCorners.shader->setUniform(m_roundedCorners.mvpMatrixLocation, projectionMatrix);
+
+ glUniform1i(m_roundedCorners.beforeBlurTextureLocation, 0);
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, renderInfo.textures[0]->texture());
+
+ glUniform1i(m_roundedCorners.afterBlurTextureLocation, 1);
+ glActiveTexture(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D, finalBlurTexture->texture());
+
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ if (opacity < 1.0f) {
+ float o = 1.0f - (opacity);
+ o = 1.0f - o * o;
+ glBlendColor(0, 0, 0, o);
+ glBlendFunc(GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA);
+ }
+
+ vbo->draw(GL_TRIANGLES, 6, vertexCount);
+
+ glDisable(GL_BLEND);
+ glActiveTexture(GL_TEXTURE0);
+ renderInfo.textures[0]->unbind();
+ finalBlurTexture->unbind();
+ ShaderManager::instance()->popShader();
}
vbo->unbindArrays();
@@ -1006,11 +1277,6 @@ bool BlurEffect::blocksDirectScanout() const
return false;
}
-bool BlurEffect::hasFakeBlur(EffectWindow *w) const
-{
- return m_fakeBlur && m_hasValidFakeBlurTexture && !blurRegion(w).isEmpty();
-}
-
} // namespace KWin
#include "moc_blur.cpp"
diff --git a/src/blur.h b/src/blur.h
index fa135646c..fde304191 100644
--- a/src/blur.h
+++ b/src/blur.h
@@ -77,16 +77,27 @@ public Q_SLOTS:
private:
void initBlurStrengthValues();
- QRegion blurRegion(EffectWindow *w, bool noRoundedCorners = false) const;
+ QRegion blurRegion(EffectWindow *w) const;
QRegion decorationBlurRegion(const EffectWindow *w) const;
+ QRegion transformedBlurRegion(QRegion blurRegion, const WindowPaintData &data) const;
bool decorationSupportsBlurBehind(const EffectWindow *w) const;
bool shouldBlur(const EffectWindow *w, int mask, const WindowPaintData &data);
bool shouldForceBlur(const EffectWindow *w) const;
void updateBlurRegion(EffectWindow *w);
- void updateCornerRegions();
void blur(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const QRegion ®ion, WindowPaintData &data);
GLTexture *ensureNoiseTexture();
- bool hasFakeBlur(EffectWindow *w) const;
+
+ /*
+ * @returns An array containing rounded corner masks for the given screen scale and radii. If no masks exist, they
+ * will be generated.
+ */
+ std::array roundedCorners(int topCornerRadius, int bottomCornerRadius, qreal scale);
+
+ /*
+ * Generates rounded corner masks for the left and right corner of the given radius.
+ * @param top Whether the corners belong to the top part of the window.
+ */
+ void generateRoundedCornerMasks(int radius, QRegion &left, QRegion &right, bool top) const;
private:
struct
@@ -123,10 +134,33 @@ public Q_SLOTS:
int mvpMatrixLocation;
int textureSizeLocation;
int texStartPosLocation;
+ int regionSizeLocation;
std::unique_ptr texture;
} m_texturePass;
+ struct
+ {
+ std::unique_ptr shader;
+
+ int roundTopLeftCornerLocation;
+ int roundTopRightCornerLocation;
+ int roundBottomLeftCornerLocation;
+ int roundBottomRightCornerLocation;
+
+ int topCornerRadiusLocation;
+ int bottomCornerRadiusLocation;
+
+ int antialiasingLocation;
+
+ int regionSizeLocation;
+
+ int beforeBlurTextureLocation;
+ int afterBlurTextureLocation;
+
+ int mvpMatrixLocation;
+ } m_roundedCorners;
+
bool m_valid = false;
long net_wm_blur_region = 0;
QRegion m_paintedArea; // keeps track of all painted areas (from bottom to top)
@@ -142,9 +176,6 @@ public Q_SLOTS:
bool m_blurNonMatching;
bool m_blurDecorations;
bool m_transparentBlur;
- int m_topCornerRadius;
- int m_bottomCornerRadius;
- bool m_roundCornersOfMaximizedWindows;
bool m_blurMenus;
bool m_blurDocks;
bool m_paintAsTranslucent;
@@ -153,11 +184,17 @@ public Q_SLOTS:
bool m_hasValidFakeBlurTexture;
- // Regions to subtract from the blurred region
- QRegion m_topLeftCorner;
- QRegion m_topRightCorner;
- QRegion m_bottomLeftCorner;
- QRegion m_bottomRightCorner;
+ int m_windowTopCornerRadius;
+ int m_windowBottomCornerRadius;
+ int m_menuCornerRadius;
+ int m_dockCornerRadius;
+ float m_roundedCornersAntialiasing;
+ bool m_roundCornersOfMaximizedWindows;
+ int m_cornerRadiusOffset;
+
+ // Corner masks where the key is the screen scale and the value is an array of the masks
+ // (top left, top right, bottom left, bottom right). Used for rounding the blur region.
+ std::map, std::array> m_corners;
struct OffsetStruct
{
diff --git a/src/blur.kcfg b/src/blur.kcfg
index 04544a0af..734cf1af6 100644
--- a/src/blur.kcfg
+++ b/src/blur.kcfg
@@ -34,6 +34,15 @@ class3
0
+
+ 0
+
+
+ 0
+
+
+ 1.0
+
false
diff --git a/src/blur.qrc b/src/blur.qrc
index 8039e2b48..d7c8b16b0 100644
--- a/src/blur.qrc
+++ b/src/blur.qrc
@@ -4,6 +4,8 @@
shaders/downsample_core.frag
shaders/noise.frag
shaders/noise_core.frag
+ shaders/roundedcorners.frag
+ shaders/roundedcorners_core.frag
shaders/texture.frag
shaders/texture_core.frag
shaders/upsample.frag
diff --git a/src/kcm/blur_config.ui b/src/kcm/blur_config.ui
index b89605845..4d542f015 100644
--- a/src/kcm/blur_config.ui
+++ b/src/kcm/blur_config.ui
@@ -321,7 +321,7 @@
-
- Top corner radius
+ Window top corner radius
@@ -339,7 +339,7 @@
-
- Bottom corner radius
+ Window bottom corner radius
@@ -352,6 +352,60 @@
+ -
+
+
-
+
+
+ Menu corner radius
+
+
+
+ -
+
+
+ 0
+
+
+
+
+
+ -
+
+
-
+
+
+ Dock corner radius
+
+
+
+ -
+
+
+ 0
+
+
+
+
+
+ -
+
+
-
+
+
+ Anti-aliasing
+
+
+
+ -
+
+
+ 0
+
+
+
+
+
-
diff --git a/src/shaders/roundedcorners.frag b/src/shaders/roundedcorners.frag
new file mode 100644
index 000000000..58cfd2653
--- /dev/null
+++ b/src/shaders/roundedcorners.frag
@@ -0,0 +1,53 @@
+// Modified version of https://www.shadertoy.com/view/ldfSDj
+
+uniform bool roundTopLeftCorner;
+uniform bool roundTopRightCorner;
+uniform bool roundBottomLeftCorner;
+uniform bool roundBottomRightCorner;
+
+uniform int topCornerRadius;
+uniform int bottomCornerRadius;
+
+uniform float antialiasing;
+
+uniform vec2 regionSize;
+
+uniform sampler2D beforeBlurTexture;
+uniform sampler2D afterBlurTexture;
+
+varying vec2 uv;
+
+float udRoundBox(vec2 p, vec2 b, vec2 fragCoord)
+{
+ float radius = 0.0;
+ if ((fragCoord.y <= topCornerRadius)
+ && ((roundTopLeftCorner && fragCoord.x <= topCornerRadius)
+ || (roundTopRightCorner && fragCoord.x >= regionSize.x - topCornerRadius))) {
+ radius = topCornerRadius;
+ p.y -= radius;
+ } else if ((fragCoord.y >= regionSize.y - bottomCornerRadius)
+ && ((roundBottomLeftCorner && fragCoord.x <= bottomCornerRadius)
+ || (roundBottomRightCorner && fragCoord.x >= regionSize.x - bottomCornerRadius))) {
+ radius = bottomCornerRadius;
+ p.y += radius;
+ }
+
+ return length(max(abs(p) - (b + vec2(0.0, radius)) + radius, 0.0)) - radius;
+}
+
+void main(void)
+{
+ vec2 halfRegionSize = regionSize * 0.5;
+ vec2 fragCoord = uv * regionSize;
+ float box = udRoundBox(fragCoord - halfRegionSize, halfRegionSize, fragCoord);
+
+ // If antialiasing is 0, the shader will be used to generate corner masks.
+ vec3 foreground = vec3(1.0, 1.0, 1.0);
+ vec3 background = vec3(0.0, 0.0, 0.0);
+ if (antialiasing > 0.0) {
+ foreground = texture2D(afterBlurTexture, uv).rgb;
+ background = texture2D(beforeBlurTexture, uv).rgb;
+ }
+
+ gl_FragColor = vec4(mix(foreground, background, smoothstep(0.0, antialiasing, box)), 1.0);
+}
\ No newline at end of file
diff --git a/src/shaders/roundedcorners_core.frag b/src/shaders/roundedcorners_core.frag
new file mode 100644
index 000000000..e0233d703
--- /dev/null
+++ b/src/shaders/roundedcorners_core.frag
@@ -0,0 +1,57 @@
+#version 140
+
+// Modified version of https://www.shadertoy.com/view/ldfSDj
+
+uniform bool roundTopLeftCorner;
+uniform bool roundTopRightCorner;
+uniform bool roundBottomLeftCorner;
+uniform bool roundBottomRightCorner;
+
+uniform int topCornerRadius;
+uniform int bottomCornerRadius;
+
+uniform float antialiasing;
+
+uniform vec2 regionSize;
+
+uniform sampler2D beforeBlurTexture;
+uniform sampler2D afterBlurTexture;
+
+in vec2 uv;
+
+out vec4 fragColor;
+
+float udRoundBox(vec2 p, vec2 b, vec2 fragCoord)
+{
+ float radius = 0.0;
+ if ((fragCoord.y <= topCornerRadius)
+ && ((roundTopLeftCorner && fragCoord.x <= topCornerRadius)
+ || (roundTopRightCorner && fragCoord.x >= regionSize.x - topCornerRadius))) {
+ radius = topCornerRadius;
+ p.y -= radius;
+ } else if ((fragCoord.y >= regionSize.y - bottomCornerRadius)
+ && ((roundBottomLeftCorner && fragCoord.x <= bottomCornerRadius)
+ || (roundBottomRightCorner && fragCoord.x >= regionSize.x - bottomCornerRadius))) {
+ radius = bottomCornerRadius;
+ p.y += radius;
+ }
+
+ return length(max(abs(p) - (b + vec2(0.0, radius)) + radius, 0.0)) - radius;
+}
+
+void main(void)
+{
+ vec2 halfRegionSize = regionSize * 0.5;
+ vec2 fragCoord = uv * regionSize;
+ float box = udRoundBox(fragCoord - halfRegionSize, halfRegionSize, fragCoord);
+
+ // If antialiasing is 0, the shader will be used to generate corner masks.
+ vec3 foreground = vec3(1.0, 1.0, 1.0);
+ vec3 background = vec3(0.0, 0.0, 0.0);
+ if (antialiasing > 0.0) {
+ foreground = texture(afterBlurTexture, uv).rgb;
+ background = texture(beforeBlurTexture, uv).rgb;
+ }
+
+ fragColor = vec4(mix(foreground, background, smoothstep(0.0, antialiasing, box)), 1.0);
+}
diff --git a/src/shaders/texture.frag b/src/shaders/texture.frag
index 61b0dd133..2d0348e78 100644
--- a/src/shaders/texture.frag
+++ b/src/shaders/texture.frag
@@ -1,12 +1,12 @@
uniform sampler2D texUnit;
uniform vec2 textureSize;
uniform vec2 texStartPos;
+uniform vec2 regionSize;
varying vec2 uv;
void main(void)
{
- vec2 tex = vec2((texStartPos.xy + gl_FragCoord.xy) / textureSize);
-
+ vec2 tex = (texStartPos.xy + vec2(uv.x, 1.0 - uv.y) * regionSize) / textureSize;
gl_FragColor = vec4(texture2D(texUnit, tex).rgb, 0);
}
diff --git a/src/shaders/texture_core.frag b/src/shaders/texture_core.frag
index 3e3583361..afc99a28d 100644
--- a/src/shaders/texture_core.frag
+++ b/src/shaders/texture_core.frag
@@ -3,6 +3,7 @@
uniform sampler2D texUnit;
uniform vec2 textureSize;
uniform vec2 texStartPos;
+uniform vec2 regionSize;
in vec2 uv;
@@ -10,7 +11,6 @@ out vec4 fragColor;
void main(void)
{
- vec2 tex = vec2((texStartPos.xy + gl_FragCoord.xy) / textureSize);
-
+ vec2 tex = (texStartPos.xy + vec2(uv.x, 1.0 - uv.y) * regionSize) / textureSize;
fragColor = vec4(texture(texUnit, tex).rgb, 0);
}
diff --git a/src/utils.h b/src/utils.h
new file mode 100644
index 000000000..218793153
--- /dev/null
+++ b/src/utils.h
@@ -0,0 +1,32 @@
+#pragma once
+
+#include
+#include "core/pixelgrid.h"
+
+namespace KWin
+{
+
+inline QRegion scaledRegion(const QRegion ®ion, qreal scale)
+{
+ QRegion scaledRegion;
+ for (const QRect &rect : region) {
+ scaledRegion += snapToPixelGridF(scaledRect(QRectF(rect), scale)).toRect();
+ }
+
+ return scaledRegion;
+}
+
+inline bool isMenu(const EffectWindow *w)
+{
+ return w->isMenu() || w->isDropdownMenu() || w->isPopupMenu() || w->isPopupWindow();
+}
+
+inline bool isDockFloating(const EffectWindow *dock, const QRegion blurRegion)
+{
+ // If the pixel at (0, height / 2) for horizontal panels and (width / 2, 0) for vertical panels doesn't belong to
+ // the blur region, the dock is most likely floating. The (0,0) pixel may be outside the blur region if the dock
+ // can float but isn't at the moment.
+ return !blurRegion.intersects(QRect(0, dock->height() / 2, 1, 1)) && !blurRegion.intersects(QRect(dock->width() / 2, 0, 1, 1));
+}
+
+}
\ No newline at end of file