fix: prevent edge darkening on GPU-blurred wallpaper (v0.7.1)

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.
This commit is contained in:
nevaforget 2026-03-28 23:15:47 +01:00
parent 5a6900e85a
commit efc55aa372
4 changed files with 27 additions and 8 deletions

View File

@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
Format based on [Keep a Changelog](https://keepachangelog.com/).
## [0.7.1] - 2026-03-28
### Fixed
- Fix edge darkening on blurred wallpaper — GskBlurNode sampled transparent pixels outside texture bounds, now renders with 3x-sigma padding and crops back
## [0.7.0] - 2026-03-28
### Added

2
Cargo.lock generated
View File

@ -616,7 +616,7 @@ dependencies = [
[[package]]
name = "moonset"
version = "0.7.0"
version = "0.7.1"
dependencies = [
"dirs",
"gdk-pixbuf",

View File

@ -1,6 +1,6 @@
[package]
name = "moonset"
version = "0.7.0"
version = "0.7.1"
edition = "2024"
description = "Wayland session power menu with GTK4 and Layer Shell"
license = "MIT"

View File

@ -104,6 +104,10 @@ pub fn load_background_texture(bg_path: Option<&Path>) -> Option<gdk::Texture> {
// -- GPU blur via GskBlurNode -------------------------------------------------
/// Render a blurred texture using the GPU via GskBlurNode.
///
/// 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<gtk::Widget>,
texture: &gdk::Texture,
@ -111,15 +115,24 @@ fn render_blurred_texture(
) -> Option<gdk::Texture> {
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_rs::Rect::new(
0.0, 0.0, texture.width() as f32, texture.height() as f32,
);
// Clip output to original texture size
snapshot.push_clip(&graphene_rs::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_rs::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_rs::Rect::new(pad, pad, w, h);
Some(renderer.render_texture(&node, Some(&viewport)))
}
/// Fade out all windows and quit the app after the CSS transition completes.