diff --git a/render-wasm/src/render.rs b/render-wasm/src/render.rs index 66d5dad19e..00c613a54f 100644 --- a/render-wasm/src/render.rs +++ b/render-wasm/src/render.rs @@ -22,7 +22,8 @@ pub use surfaces::{SurfaceId, Surfaces}; use crate::performance; use crate::shapes::{ - Blur, BlurType, Corners, Fill, Shadow, Shape, SolidColor, Stroke, StructureEntry, Type, + all_with_ancestors, Blur, BlurType, Corners, Fill, Shadow, Shape, SolidColor, Stroke, + StructureEntry, Type, }; use crate::state::ShapesPool; use crate::tiles::{self, PendingTiles, TileRect}; @@ -1819,20 +1820,8 @@ impl RenderState { tree: &mut ShapesPool, modifiers: &HashMap, ) { - let mut ancestors = IndexSet::new(); - for (uuid, matrix) in modifiers { - let mut shape = { - let Some(shape) = tree.get(uuid) else { - panic!("Invalid current shape") - }; - let shape: Cow = Cow::Borrowed(shape); - shape - }; - - shape.to_mut().apply_transform(matrix); - ancestors.insert(*uuid); - ancestors.extend(shape.all_ancestors(tree, false)); - } + let ids: Vec<_> = modifiers.keys().collect(); + let ancestors = all_with_ancestors(&ids, tree, false); self.invalidate_and_update_tiles(&ancestors, tree, modifiers); } diff --git a/render-wasm/src/shapes.rs b/render-wasm/src/shapes.rs index 2b22f658fd..33dfb266d4 100644 --- a/render-wasm/src/shapes.rs +++ b/render-wasm/src/shapes.rs @@ -182,6 +182,59 @@ pub struct Shape { pub extrect: OnceCell, } +// Returns all ancestor shapes of this shape, traversing up the parent hierarchy +// +// This function walks up the parent chain starting from this shape's parent, +// collecting all ancestor IDs. It stops when it reaches a nil UUID or when +// an ancestor is hidden (unless include_hidden is true). +// +// # Arguments +// * `shapes` - The shapes pool containing all shapes +// * `include_hidden` - Whether to include hidden ancestors in the result +// +// # Returns +// A set of ancestor UUIDs in traversal order (closest ancestor first) +pub fn all_with_ancestors( + shapes: &[&Uuid], + shapes_pool: &ShapesPool, + include_hidden: bool, +) -> IndexSet { + let mut pending = Vec::from(shapes); + let mut result = IndexSet::new(); + + while !pending.is_empty() { + let Some(current_id) = pending.pop() else { + break; + }; + + result.insert(*current_id); + + let Some(parent_id) = shapes_pool.get(current_id).and_then(|s| s.parent_id) else { + continue; + }; + + if parent_id == Uuid::nil() { + continue; + } + + if result.contains(&parent_id) { + continue; + } + + // Check if the ancestor is hidden + let Some(parent) = shapes_pool.get(&parent_id) else { + continue; + }; + + if !include_hidden && parent.hidden() { + continue; + } + + pending.push(&parent.id); + } + result +} + impl Shape { pub fn new(id: Uuid) -> Self { Self {