🎉 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 {
flex-grow:1;
flex-grow: 1;
}
.label {

View File

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

View File

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

View File

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

View File

@ -24,7 +24,6 @@ use std::collections::{HashMap, HashSet};
use options::RenderOptions;
pub use surfaces::{SurfaceId, Surfaces};
// TODO_BLUR: should we add here BackgroundBlur
use crate::error::{Error, Result};
use crate::math;
use crate::shapes::{
@ -371,7 +370,7 @@ pub(crate) struct RenderState {
// 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.
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 show_grid: Option<Uuid>,
pub rulers: RulerState,
@ -588,7 +587,7 @@ impl RenderState {
backbuffer_crop_cache: HashMap::default(),
})
}
/// TODO_BLUR: ?
/// Combines every visible layer blur currently active (ancestors + shape)
/// into a single equivalent blur. Layer blur radii compound by adding their
/// variances (σ² = radius²), so we:
@ -650,10 +649,7 @@ impl RenderState {
{
return;
}
let blur = match shape
.blur
.filter(|b| !b.hidden && b.blur_type == BlurType::BackgroundBlur)
{
let blur = match shape.visible_background_blur() {
Some(blur) => blur,
None => return,
};
@ -1203,6 +1199,7 @@ impl RenderState {
&& parent_shadows.is_none()
&& !shape.needs_layer()
&& shape.blur.is_none()
&& shape.background_blur.is_none()
&& !has_inherited_blur
&& shape.shadows.is_empty()
&& shape.transform.is_identity()
@ -1312,20 +1309,9 @@ impl RenderState {
// We don't want to change the value in the global state
let mut shape: Cow<Shape> = Cow::Borrowed(shape);
// Remove background blur from the shape so it doesn't get processed
// as a layer blur. The actual rendering is done before the save_layer
// in render_background_blur() so it's independent of shape opacity.
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);
}
// Background blur is stored separately (shape.background_blur) and is
// rendered before the save_layer in render_background_blur(), so here
// shape.blur only ever holds a layer blur.
let frame_has_blur = Self::frame_clip_layer_blur(&shape).is_some();
let shape_has_blur = shape.blur.is_some();
@ -2842,6 +2828,7 @@ impl RenderState {
plain_shape_mut.clear_shadows();
plain_shape_mut.blur = None;
plain_shape_mut.background_blur = None;
// 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
@ -3645,10 +3632,8 @@ impl RenderState {
// assigned to this tile) because the blur snapshots Current
// which must contain the shapes behind it.
let tile_has_bg_blur = ids.iter().any(|id| {
tree.get(id).is_some_and(|s| {
s.blur
.is_some_and(|b| !b.hidden && b.blur_type == BlurType::BackgroundBlur)
})
tree.get(id)
.is_some_and(|s| s.visible_background_blur().is_some())
});
// 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 vertical_align: VerticalAlign,
pub blur: Option<Blur>,
pub background_blur: Option<Blur>,
pub opacity: f32,
pub hidden: bool,
pub svg: Option<skia::svg::Dom>,
@ -291,6 +292,7 @@ impl Shape {
opacity: 1.,
hidden: false,
blur: None,
background_blur: None,
svg: None,
svg_attrs: None,
shadows: Vec::with_capacity(1),
@ -314,6 +316,10 @@ impl Shape {
blur.scale_content(value);
}
if let Some(background_blur) = self.background_blur.as_mut() {
background_blur.scale_content(value);
}
self.layout_item
.iter_mut()
.for_each(|i| i.scale_content(value));
@ -631,6 +637,15 @@ impl Shape {
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) {
self.children.push(id);
}
@ -1421,6 +1436,7 @@ impl Shape {
}
self.blur.is_none()
&& self.background_blur.is_none()
&& self.shadows.is_empty()
&& (self.opacity - 1.0).abs() <= 1e-4
&& self.blend_mode().0 == skia::BlendMode::SrcOver
@ -1665,7 +1681,7 @@ impl Shape {
return false;
}
if self.blur.is_some() {
if self.blur.is_some() || self.background_blur.is_some() {
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]
fn test_set_corners() {
let mut shape = any_shape();

View File

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