From 77d6994b8fef305a88fb7456b41d11cd7401ad41 Mon Sep 17 00:00:00 2001 From: nevaforget Date: Sat, 28 Mar 2026 23:28:40 +0100 Subject: [PATCH] fix: prevent edge darkening on GPU-blurred wallpaper (v0.6.3) GskBlurNode samples pixels outside texture bounds as transparent, causing visible darkening at wallpaper edges. Fix renders the texture with 3x-sigma padding before blur, then clips back to original size. Symmetric fix with moonset v0.7.1. --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/lockscreen.rs | 28 +++++++++++++++++++--------- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fd2c74a..bdc23a7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -575,7 +575,7 @@ dependencies = [ [[package]] name = "moonlock" -version = "0.6.2" +version = "0.6.3" dependencies = [ "gdk-pixbuf", "gdk4", diff --git a/Cargo.toml b/Cargo.toml index c057e46..7a6feb9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "moonlock" -version = "0.6.2" +version = "0.6.3" edition = "2024" description = "A secure Wayland lockscreen with GTK4, PAM and fingerprint support" license = "MIT" diff --git a/src/lockscreen.rs b/src/lockscreen.rs index 30a4ab3..a2ef083 100644 --- a/src/lockscreen.rs +++ b/src/lockscreen.rs @@ -483,6 +483,10 @@ fn create_background_picture( /// Render a blurred texture using the widget's GPU renderer. /// Returns None if the renderer is not available. +/// +/// To avoid edge darkening (blur samples transparent pixels outside bounds), +/// the texture is rendered with padding equal to 3x the blur sigma. The blur +/// is applied to the padded area, then cropped back to the original size. fn render_blurred_texture( widget: &impl IsA, texture: &gdk::Texture, @@ -490,18 +494,24 @@ fn render_blurred_texture( ) -> Option { let native = widget.native()?; let renderer = native.renderer()?; + + let w = texture.width() as f32; + let h = texture.height() as f32; + // Padding must cover the blur kernel radius (typically ~3x sigma) + let pad = (sigma * 3.0).ceil(); + let snapshot = gtk::Snapshot::new(); - let bounds = graphene::Rect::new( - 0.0, - 0.0, - texture.width() as f32, - texture.height() as f32, - ); + // Clip output to original texture size + snapshot.push_clip(&graphene::Rect::new(pad, pad, w, h)); snapshot.push_blur(sigma as f64); - snapshot.append_texture(texture, &bounds); - snapshot.pop(); + // Render texture with padding on all sides (edges repeat via oversized bounds) + snapshot.append_texture(texture, &graphene::Rect::new(0.0, 0.0, w + 2.0 * pad, h + 2.0 * pad)); + snapshot.pop(); // blur + snapshot.pop(); // clip + let node = snapshot.to_node()?; - Some(renderer.render_texture(&node, None)) + let viewport = graphene::Rect::new(pad, pad, w, h); + Some(renderer.render_texture(&node, Some(&viewport))) } /// Load an image file and set it as the avatar. Stores the texture in the cache.