🎉 Suport in wasm for both layer and background blur

This commit is contained in:
alonso.torres 2026-06-15 10:58:03 +02:00 committed by Eva Marco
parent 100caa794d
commit 21cad83b2a
7 changed files with 83 additions and 45 deletions

View File

@ -76,7 +76,7 @@
} }
.disabled-label-tooltip { .disabled-label-tooltip {
flex-grow:1; flex-grow: 1;
} }
.label { .label {

View File

@ -944,21 +944,21 @@
(defn set-shape-blur (defn set-shape-blur
[blur] [blur]
(if (some? blur) (let [type (sr/translate-blur-type :layer-blur)]
(let [type (sr/translate-blur-type :layer-blur) (if (some? blur)
hidden (:hidden blur) (let [hidden (:hidden blur)
value (:value blur)] value (:value blur)]
(h/call wasm/internal-module "_set_shape_blur" type hidden value)) (h/call wasm/internal-module "_set_shape_blur" type hidden value))
(h/call wasm/internal-module "_clear_shape_blur"))) (h/call wasm/internal-module "_clear_shape_blur" type))))
(defn set-shape-background-blur (defn set-shape-background-blur
[background-blur] [background-blur]
(if (some? background-blur) (let [type (sr/translate-blur-type :background-blur)]
(let [type (sr/translate-blur-type :background-blur) (if (some? background-blur)
hidden (:hidden background-blur) (let [hidden (:hidden background-blur)
value (:value background-blur)] value (:value background-blur)]
(h/call wasm/internal-module "_set_shape_blur" type hidden value)) (h/call wasm/internal-module "_set_shape_blur" type hidden value))
(h/call wasm/internal-module "_clear_shape_blur"))) (h/call wasm/internal-module "_clear_shape_blur" type))))
(defn set-shape-corners (defn set-shape-corners
[corners] [corners]

View File

@ -130,7 +130,6 @@
(defn- set-wasm-attr! (defn- set-wasm-attr!
[shape k] [shape k]
(when wasm/context-initialized? (when wasm/context-initialized?
;;TODO_BLUR: ask about this,
(let [shape (case k (let [shape (case k
:svg-attrs (svg-filters/apply-svg-derived (assoc shape :svg-attrs (get shape :svg-attrs))) :svg-attrs (svg-filters/apply-svg-derived (assoc shape :svg-attrs (get shape :svg-attrs)))
(:fills :blur :shadow) (svg-filters/apply-svg-derived shape) (:fills :blur :shadow) (svg-filters/apply-svg-derived shape)

View File

@ -44,7 +44,6 @@
(set! context-initialized? false) (set! context-initialized? false)
(reset! context-lost? false)) (reset! context-lost? false))
;; TODO_BLUR: ask for blur-type??
(defonce serializers (defonce serializers
#js {:blur-type shared/RawBlurType #js {:blur-type shared/RawBlurType
:blend-mode shared/RawBlendMode :blend-mode shared/RawBlendMode

View File

@ -24,7 +24,6 @@ use std::collections::{HashMap, HashSet};
use options::RenderOptions; use options::RenderOptions;
pub use surfaces::{SurfaceId, Surfaces}; pub use surfaces::{SurfaceId, Surfaces};
// TODO_BLUR: should we add here BackgroundBlur
use crate::error::{Error, Result}; use crate::error::{Error, Result};
use crate::math; use crate::math;
use crate::shapes::{ use crate::shapes::{
@ -371,7 +370,7 @@ pub(crate) struct RenderState {
// migration to remove group-level fills is completed, this code should be removed. // migration to remove group-level fills is completed, this code should be removed.
// Frames contained in groups must reset this nested_fills stack pushing a new empty vector. // Frames contained in groups must reset this nested_fills stack pushing a new empty vector.
pub nested_fills: Vec<Vec<Fill>>, pub nested_fills: Vec<Vec<Fill>>,
pub nested_blurs: Vec<Option<Blur>>, // FIXME: why is this an option?, sholud be an option now? TODO_BLUR pub nested_blurs: Vec<Option<Blur>>, // FIXME: why is this an option?
pub nested_shadows: Vec<Vec<Shadow>>, pub nested_shadows: Vec<Vec<Shadow>>,
pub show_grid: Option<Uuid>, pub show_grid: Option<Uuid>,
pub rulers: RulerState, pub rulers: RulerState,
@ -588,7 +587,7 @@ impl RenderState {
backbuffer_crop_cache: HashMap::default(), backbuffer_crop_cache: HashMap::default(),
}) })
} }
/// TODO_BLUR: ?
/// Combines every visible layer blur currently active (ancestors + shape) /// Combines every visible layer blur currently active (ancestors + shape)
/// into a single equivalent blur. Layer blur radii compound by adding their /// into a single equivalent blur. Layer blur radii compound by adding their
/// variances (σ² = radius²), so we: /// variances (σ² = radius²), so we:
@ -650,10 +649,7 @@ impl RenderState {
{ {
return; return;
} }
let blur = match shape let blur = match shape.visible_background_blur() {
.blur
.filter(|b| !b.hidden && b.blur_type == BlurType::BackgroundBlur)
{
Some(blur) => blur, Some(blur) => blur,
None => return, None => return,
}; };
@ -1203,6 +1199,7 @@ impl RenderState {
&& parent_shadows.is_none() && parent_shadows.is_none()
&& !shape.needs_layer() && !shape.needs_layer()
&& shape.blur.is_none() && shape.blur.is_none()
&& shape.background_blur.is_none()
&& !has_inherited_blur && !has_inherited_blur
&& shape.shadows.is_empty() && shape.shadows.is_empty()
&& shape.transform.is_identity() && shape.transform.is_identity()
@ -1312,20 +1309,9 @@ impl RenderState {
// We don't want to change the value in the global state // We don't want to change the value in the global state
let mut shape: Cow<Shape> = Cow::Borrowed(shape); let mut shape: Cow<Shape> = Cow::Borrowed(shape);
// Remove background blur from the shape so it doesn't get processed // Background blur is stored separately (shape.background_blur) and is
// as a layer blur. The actual rendering is done before the save_layer // rendered before the save_layer in render_background_blur(), so here
// in render_background_blur() so it's independent of shape opacity. // shape.blur only ever holds a layer blur.
if !skip_effects
&& apply_to_current_surface
&& fills_surface_id == SurfaceId::Fills
&& !matches!(shape.shape_type, Type::Text(_))
&& !matches!(shape.shape_type, Type::SVGRaw(_))
&& shape
.blur
.is_some_and(|b| !b.hidden && b.blur_type == BlurType::BackgroundBlur)
{
shape.to_mut().set_blur(None);
}
let frame_has_blur = Self::frame_clip_layer_blur(&shape).is_some(); let frame_has_blur = Self::frame_clip_layer_blur(&shape).is_some();
let shape_has_blur = shape.blur.is_some(); let shape_has_blur = shape.blur.is_some();
@ -2842,6 +2828,7 @@ impl RenderState {
plain_shape_mut.clear_shadows(); plain_shape_mut.clear_shadows();
plain_shape_mut.blur = None; plain_shape_mut.blur = None;
plain_shape_mut.background_blur = None;
// Shadow rendering uses a single render_shape call with no render_shape_exit, // Shadow rendering uses a single render_shape call with no render_shape_exit,
// so strokes must be drawn here. Disable clip_content to avoid skip_strokes // so strokes must be drawn here. Disable clip_content to avoid skip_strokes
@ -3645,10 +3632,8 @@ impl RenderState {
// assigned to this tile) because the blur snapshots Current // assigned to this tile) because the blur snapshots Current
// which must contain the shapes behind it. // which must contain the shapes behind it.
let tile_has_bg_blur = ids.iter().any(|id| { let tile_has_bg_blur = ids.iter().any(|id| {
tree.get(id).is_some_and(|s| { tree.get(id)
s.blur .is_some_and(|s| s.visible_background_blur().is_some())
.is_some_and(|b| !b.hidden && b.blur_type == BlurType::BackgroundBlur)
})
}); });
// We only need first level shapes, in the same order as the parent node. // We only need first level shapes, in the same order as the parent node.

View File

@ -188,6 +188,7 @@ pub struct Shape {
pub blend_mode: BlendMode, pub blend_mode: BlendMode,
pub vertical_align: VerticalAlign, pub vertical_align: VerticalAlign,
pub blur: Option<Blur>, pub blur: Option<Blur>,
pub background_blur: Option<Blur>,
pub opacity: f32, pub opacity: f32,
pub hidden: bool, pub hidden: bool,
pub svg: Option<skia::svg::Dom>, pub svg: Option<skia::svg::Dom>,
@ -291,6 +292,7 @@ impl Shape {
opacity: 1., opacity: 1.,
hidden: false, hidden: false,
blur: None, blur: None,
background_blur: None,
svg: None, svg: None,
svg_attrs: None, svg_attrs: None,
shadows: Vec::with_capacity(1), shadows: Vec::with_capacity(1),
@ -314,6 +316,10 @@ impl Shape {
blur.scale_content(value); blur.scale_content(value);
} }
if let Some(background_blur) = self.background_blur.as_mut() {
background_blur.scale_content(value);
}
self.layout_item self.layout_item
.iter_mut() .iter_mut()
.for_each(|i| i.scale_content(value)); .for_each(|i| i.scale_content(value));
@ -631,6 +637,15 @@ impl Shape {
self.blur = blur; self.blur = blur;
} }
pub fn set_background_blur(&mut self, blur: Option<Blur>) {
self.invalidate_extrect();
self.background_blur = blur;
}
pub fn visible_background_blur(&self) -> Option<Blur> {
self.background_blur.filter(|blur| !blur.hidden)
}
pub fn add_child(&mut self, id: Uuid) { pub fn add_child(&mut self, id: Uuid) {
self.children.push(id); self.children.push(id);
} }
@ -1421,6 +1436,7 @@ impl Shape {
} }
self.blur.is_none() self.blur.is_none()
&& self.background_blur.is_none()
&& self.shadows.is_empty() && self.shadows.is_empty()
&& (self.opacity - 1.0).abs() <= 1e-4 && (self.opacity - 1.0).abs() <= 1e-4
&& self.blend_mode().0 == skia::BlendMode::SrcOver && self.blend_mode().0 == skia::BlendMode::SrcOver
@ -1665,7 +1681,7 @@ impl Shape {
return false; return false;
} }
if self.blur.is_some() { if self.blur.is_some() || self.background_blur.is_some() {
return false; return false;
} }
@ -1805,6 +1821,38 @@ mod tests {
) )
} }
#[test]
fn layer_blur_and_background_blur_can_coexist() {
let mut shape = any_shape();
let layer_blur = Blur::new(BlurType::LayerBlur, false, 4.0);
let background_blur = Blur::new(BlurType::BackgroundBlur, false, 8.0);
shape.set_blur(Some(layer_blur));
shape.set_background_blur(Some(background_blur));
assert_eq!(shape.blur, Some(layer_blur));
assert_eq!(shape.background_blur, Some(background_blur));
assert_eq!(shape.visible_background_blur(), Some(background_blur));
// Clearing one type must not affect the other.
shape.set_blur(None);
assert_eq!(shape.blur, None);
assert_eq!(shape.background_blur, Some(background_blur));
shape.set_blur(Some(layer_blur));
shape.set_background_blur(None);
assert_eq!(shape.blur, Some(layer_blur));
assert_eq!(shape.background_blur, None);
}
#[test]
fn hidden_background_blur_is_not_visible() {
let mut shape = any_shape();
shape.set_background_blur(Some(Blur::new(BlurType::BackgroundBlur, true, 8.0)));
assert_eq!(shape.visible_background_blur(), None);
}
#[test] #[test]
fn test_set_corners() { fn test_set_corners() {
let mut shape = any_shape(); let mut shape = any_shape();

View File

@ -29,14 +29,21 @@ impl From<RawBlurType> for BlurType {
#[no_mangle] #[no_mangle]
pub extern "C" fn set_shape_blur(blur_type: u8, hidden: bool, value: f32) { pub extern "C" fn set_shape_blur(blur_type: u8, hidden: bool, value: f32) {
with_current_shape_mut!(state, |shape: &mut Shape| { with_current_shape_mut!(state, |shape: &mut Shape| {
let blur_type = RawBlurType::from(blur_type); let blur_type: BlurType = RawBlurType::from(blur_type).into();
shape.set_blur(Some(Blur::new(blur_type.into(), hidden, value))); let blur = Some(Blur::new(blur_type, hidden, value));
match blur_type {
BlurType::LayerBlur => shape.set_blur(blur),
BlurType::BackgroundBlur => shape.set_background_blur(blur),
}
}); });
} }
#[no_mangle] #[no_mangle]
pub extern "C" fn clear_shape_blur() { pub extern "C" fn clear_shape_blur(blur_type: u8) {
with_current_shape_mut!(state, |shape: &mut Shape| { with_current_shape_mut!(state, |shape: &mut Shape| {
shape.set_blur(None); match RawBlurType::from(blur_type).into() {
BlurType::LayerBlur => shape.set_blur(None),
BlurType::BackgroundBlur => shape.set_background_blur(None),
}
}); });
} }