Add scale_content to shapes_pool

This commit is contained in:
alonso.torres
2025-10-30 17:47:20 +01:00
parent fcc9282304
commit f3b914534f
7 changed files with 75 additions and 94 deletions

View File

@@ -982,10 +982,6 @@
[] []
(h/call wasm/internal-module "_clean_modifiers")) (h/call wasm/internal-module "_clean_modifiers"))
(defn clean-geometry-modifiers
[]
(h/call wasm/internal-module "_clean_geometry_modifiers"))
(defn set-modifiers (defn set-modifiers
[modifiers] [modifiers]

View File

@@ -541,6 +541,7 @@ pub extern "C" fn set_structure_modifiers() {
with_state_mut!(state, { with_state_mut!(state, {
let mut structure = HashMap::new(); let mut structure = HashMap::new();
let mut scale_content = HashMap::new();
for entry in entries { for entry in entries {
match entry.entry_type { match entry.entry_type {
StructureEntryType::ScaleContent => { StructureEntryType::ScaleContent => {
@@ -548,7 +549,7 @@ pub extern "C" fn set_structure_modifiers() {
continue; continue;
}; };
for id in shape.all_children(&state.shapes, true, true) { for id in shape.all_children(&state.shapes, true, true) {
state.scale_content.insert(id, entry.value); scale_content.insert(id, entry.value);
} }
} }
_ => { _ => {
@@ -560,6 +561,9 @@ pub extern "C" fn set_structure_modifiers() {
} }
} }
} }
if !scale_content.is_empty() {
state.shapes.set_scale_content(scale_content);
}
if !structure.is_empty() { if !structure.is_empty() {
state.shapes.set_structure(structure); state.shapes.set_structure(structure);
} }
@@ -571,16 +575,7 @@ pub extern "C" fn set_structure_modifiers() {
#[no_mangle] #[no_mangle]
pub extern "C" fn clean_modifiers() { pub extern "C" fn clean_modifiers() {
with_state_mut!(state, { with_state_mut!(state, {
state.scale_content.clear(); state.shapes.clean_all();
state.shapes.clean_modifiers();
state.shapes.clean_structure();
});
}
#[no_mangle]
pub extern "C" fn clean_geometry_modifiers() {
with_state_mut!(state, {
state.shapes.clean_modifiers();
}); });
} }

View File

@@ -14,7 +14,7 @@ mod ui;
use skia_safe::{self as skia, Matrix, RRect, Rect}; use skia_safe::{self as skia, Matrix, RRect, Rect};
use std::borrow::Cow; use std::borrow::Cow;
use std::collections::{HashMap, HashSet}; use std::collections::HashSet;
use gpu_state::GpuState; use gpu_state::GpuState;
use options::RenderOptions; use options::RenderOptions;
@@ -439,7 +439,6 @@ impl RenderState {
pub fn render_shape( pub fn render_shape(
&mut self, &mut self,
shape: &Shape, shape: &Shape,
scale_content: Option<&f32>,
clip_bounds: Option<(Rect, Option<Corners>, Matrix)>, clip_bounds: Option<(Rect, Option<Corners>, Matrix)>,
fills_surface_id: SurfaceId, fills_surface_id: SurfaceId,
strokes_surface_id: SurfaceId, strokes_surface_id: SurfaceId,
@@ -449,12 +448,6 @@ impl RenderState {
offset: Option<(f32, f32)>, offset: Option<(f32, f32)>,
parent_shadows: Option<Vec<skia_safe::Paint>>, parent_shadows: Option<Vec<skia_safe::Paint>>,
) { ) {
let shape = if let Some(scale_content) = scale_content {
&shape.scale_content(*scale_content)
} else {
shape
};
let surface_ids = fills_surface_id as u32 let surface_ids = fills_surface_id as u32
| strokes_surface_id as u32 | strokes_surface_id as u32
| innershadows_surface_id as u32 | innershadows_surface_id as u32
@@ -819,12 +812,7 @@ impl RenderState {
} }
} }
pub fn start_render_loop( pub fn start_render_loop(&mut self, tree: ShapesPoolRef, timestamp: i32) -> Result<(), String> {
&mut self,
tree: ShapesPoolRef,
scale_content: &HashMap<Uuid, f32>,
timestamp: i32,
) -> Result<(), String> {
let scale = self.get_scale(); let scale = self.get_scale();
self.tile_viewbox.update(self.viewbox, scale); self.tile_viewbox.update(self.viewbox, scale);
@@ -872,7 +860,7 @@ impl RenderState {
self.current_tile = None; self.current_tile = None;
self.render_in_progress = true; self.render_in_progress = true;
self.apply_drawing_to_render_canvas(None); self.apply_drawing_to_render_canvas(None);
self.process_animation_frame(tree, scale_content, timestamp)?; self.process_animation_frame(tree, timestamp)?;
performance::end_measure!("start_render_loop"); performance::end_measure!("start_render_loop");
Ok(()) Ok(())
} }
@@ -880,13 +868,12 @@ impl RenderState {
pub fn process_animation_frame( pub fn process_animation_frame(
&mut self, &mut self,
tree: ShapesPoolRef, tree: ShapesPoolRef,
scale_content: &HashMap<Uuid, f32>,
timestamp: i32, timestamp: i32,
) -> Result<(), String> { ) -> Result<(), String> {
performance::begin_measure!("process_animation_frame"); performance::begin_measure!("process_animation_frame");
if self.render_in_progress { if self.render_in_progress {
if tree.len() != 0 { if tree.len() != 0 {
self.render_shape_tree_partial(tree, scale_content, timestamp)?; self.render_shape_tree_partial(tree, timestamp)?;
} else { } else {
println!("Empty tree"); println!("Empty tree");
} }
@@ -956,12 +943,7 @@ impl RenderState {
} }
#[inline] #[inline]
pub fn render_shape_exit( pub fn render_shape_exit(&mut self, element: &Shape, visited_mask: bool) {
&mut self,
element: &Shape,
visited_mask: bool,
scale_content: Option<&f32>,
) {
if visited_mask { if visited_mask {
// Because masked groups needs two rendering passes (first drawing // Because masked groups needs two rendering passes (first drawing
// the content and then drawing the mask), we need to do an // the content and then drawing the mask), we need to do an
@@ -1016,7 +998,6 @@ impl RenderState {
element_strokes.to_mut().clip_content = false; element_strokes.to_mut().clip_content = false;
self.render_shape( self.render_shape(
&element_strokes, &element_strokes,
scale_content,
None, None,
SurfaceId::Fills, SurfaceId::Fills,
SurfaceId::Strokes, SurfaceId::Strokes,
@@ -1102,7 +1083,6 @@ impl RenderState {
&mut self, &mut self,
shape: &Shape, shape: &Shape,
shadow: &Shadow, shadow: &Shadow,
scale_content: Option<&f32>,
clip_bounds: Option<(Rect, Option<Corners>, Matrix)>, clip_bounds: Option<(Rect, Option<Corners>, Matrix)>,
scale: f32, scale: f32,
translation: (f32, f32), translation: (f32, f32),
@@ -1152,7 +1132,6 @@ impl RenderState {
self.render_shape( self.render_shape(
&plain_shape, &plain_shape,
scale_content,
clip_bounds, clip_bounds,
SurfaceId::DropShadows, SurfaceId::DropShadows,
SurfaceId::DropShadows, SurfaceId::DropShadows,
@@ -1169,7 +1148,6 @@ impl RenderState {
pub fn render_shape_tree_partial_uncached( pub fn render_shape_tree_partial_uncached(
&mut self, &mut self,
tree: ShapesPoolRef, tree: ShapesPoolRef,
scale_content: &HashMap<Uuid, f32>,
timestamp: i32, timestamp: i32,
) -> Result<(bool, bool), String> { ) -> Result<(bool, bool), String> {
let mut iteration = 0; let mut iteration = 0;
@@ -1198,7 +1176,7 @@ impl RenderState {
} }
if visited_children { if visited_children {
self.render_shape_exit(element, visited_mask, scale_content.get(&element.id)); self.render_shape_exit(element, visited_mask);
continue; continue;
} }
@@ -1260,7 +1238,6 @@ impl RenderState {
self.render_drop_black_shadow( self.render_drop_black_shadow(
element, element,
shadow, shadow,
scale_content.get(&element.id),
clip_bounds, clip_bounds,
scale, scale,
translation, translation,
@@ -1280,7 +1257,6 @@ impl RenderState {
self.render_drop_black_shadow( self.render_drop_black_shadow(
shadow_shape, shadow_shape,
shadow, shadow,
scale_content.get(&element.id),
clip_bounds, clip_bounds,
scale, scale,
translation, translation,
@@ -1314,7 +1290,6 @@ impl RenderState {
self.render_shape( self.render_shape(
shadow_shape, shadow_shape,
scale_content.get(&element.id),
clip_bounds, clip_bounds,
SurfaceId::DropShadows, SurfaceId::DropShadows,
SurfaceId::DropShadows, SurfaceId::DropShadows,
@@ -1352,7 +1327,6 @@ impl RenderState {
self.render_shape( self.render_shape(
element, element,
scale_content.get(&element.id),
clip_bounds, clip_bounds,
SurfaceId::Fills, SurfaceId::Fills,
SurfaceId::Strokes, SurfaceId::Strokes,
@@ -1426,7 +1400,6 @@ impl RenderState {
pub fn render_shape_tree_partial( pub fn render_shape_tree_partial(
&mut self, &mut self,
tree: ShapesPoolRef, tree: ShapesPoolRef,
scale_content: &HashMap<Uuid, f32>,
timestamp: i32, timestamp: i32,
) -> Result<(), String> { ) -> Result<(), String> {
let mut should_stop = false; let mut should_stop = false;
@@ -1453,7 +1426,7 @@ impl RenderState {
} else { } else {
performance::begin_measure!("render_shape_tree::uncached"); performance::begin_measure!("render_shape_tree::uncached");
let (is_empty, early_return) = let (is_empty, early_return) =
self.render_shape_tree_partial_uncached(tree, scale_content, timestamp)?; self.render_shape_tree_partial_uncached(tree, timestamp)?;
if early_return { if early_return {
return Ok(()); return Ok(());
} }

View File

@@ -183,6 +183,7 @@ pub struct Shape {
pub extrect: OnceCell<math::Rect>, pub extrect: OnceCell<math::Rect>,
pub bounds: OnceCell<math::Bounds>, pub bounds: OnceCell<math::Bounds>,
pub svg_transform: Option<Matrix>, pub svg_transform: Option<Matrix>,
pub ignore_constraints: bool,
} }
// Returns all ancestor shapes of this shape, traversing up the parent hierarchy // Returns all ancestor shapes of this shape, traversing up the parent hierarchy
@@ -265,30 +266,24 @@ impl Shape {
extrect: OnceCell::new(), extrect: OnceCell::new(),
bounds: OnceCell::new(), bounds: OnceCell::new(),
svg_transform: None, svg_transform: None,
ignore_constraints: false,
} }
} }
pub fn scale_content(&self, value: f32) -> Self { pub fn scale_content(&mut self, value: f32) {
let mut result = self.clone(); self.ignore_constraints = true;
result.shape_type.scale_content(value); self.shape_type.scale_content(value);
result self.strokes.iter_mut().for_each(|s| s.scale_content(value));
.strokes
.iter_mut()
.for_each(|s| s.scale_content(value));
result
.shadows
.iter_mut()
.for_each(|s| s.scale_content(value));
if let Some(blur) = result.blur.as_mut() { self.shadows.iter_mut().for_each(|s| s.scale_content(value));
if let Some(blur) = self.blur.as_mut() {
blur.scale_content(value); blur.scale_content(value);
} }
result self.layout_item
.layout_item
.iter_mut() .iter_mut()
.for_each(|i| i.scale_content(value)); .for_each(|i| i.scale_content(value));
result
} }
pub fn invalidate_extrect(&mut self) { pub fn invalidate_extrect(&mut self) {

View File

@@ -23,7 +23,6 @@ fn propagate_children(
parent_bounds_after: &Bounds, parent_bounds_after: &Bounds,
transform: Matrix, transform: Matrix,
bounds: &HashMap<Uuid, Bounds>, bounds: &HashMap<Uuid, Bounds>,
scale_content: &HashMap<Uuid, f32>,
) -> VecDeque<Modifier> { ) -> VecDeque<Modifier> {
let children_ids = shape.children_ids(true); let children_ids = shape.children_ids(true);
@@ -38,8 +37,6 @@ fn propagate_children(
continue; continue;
}; };
let ignore_constraints = scale_content.contains_key(child_id);
let child_bounds = bounds.find(child); let child_bounds = bounds.find(child);
let constraint_h = match &shape.shape_type { let constraint_h = match &shape.shape_type {
@@ -77,7 +74,7 @@ fn propagate_children(
constraint_h, constraint_h,
constraint_v, constraint_v,
transform, transform,
ignore_constraints, child.ignore_constraints,
); );
result.push_back(Modifier::transform(*child_id, transform)); result.push_back(Modifier::transform(*child_id, transform));
@@ -229,7 +226,6 @@ fn propagate_transform(
&shape_bounds_after, &shape_bounds_after,
transform, transform,
bounds, bounds,
&state.scale_content,
); );
entries.append(&mut children); entries.append(&mut children);
} }
@@ -343,12 +339,6 @@ fn reflow_shape(
let shapes = &state.shapes; let shapes = &state.shapes;
let shape = if let Some(scale_content) = state.scale_content.get(id) {
&shape.scale_content(*scale_content)
} else {
shape
};
let Type::Frame(frame_data) = &shape.shape_type else { let Type::Frame(frame_data) = &shape.shape_type else {
return; return;
}; };
@@ -468,7 +458,6 @@ mod tests {
&bounds_after, &bounds_after,
transform, transform,
&HashMap::new(), &HashMap::new(),
&HashMap::new(),
); );
assert_eq!(result.len(), 1); assert_eq!(result.len(), 1);
@@ -499,8 +488,7 @@ mod tests {
let parent = shapes.get(&parent_id).unwrap(); let parent = shapes.get(&parent_id).unwrap();
let bounds = let bounds = calculate_group_bounds(parent, &shapes, &HashMap::new()).unwrap();
calculate_group_bounds(parent, &shapes, &HashMap::new()).unwrap();
assert_eq!(bounds.width(), 3.0); assert_eq!(bounds.width(), 3.0);
assert_eq!(bounds.height(), 3.0); assert_eq!(bounds.height(), 3.0);

View File

@@ -23,7 +23,6 @@ pub(crate) struct State<'a> {
pub text_editor_state: TextEditorState, pub text_editor_state: TextEditorState,
pub current_id: Option<Uuid>, pub current_id: Option<Uuid>,
pub shapes: ShapesPool<'a>, pub shapes: ShapesPool<'a>,
pub scale_content: HashMap<Uuid, f32>,
} }
impl<'a> State<'a> { impl<'a> State<'a> {
@@ -33,7 +32,6 @@ impl<'a> State<'a> {
text_editor_state: TextEditorState::new(), text_editor_state: TextEditorState::new(),
current_id: None, current_id: None,
shapes: ShapesPool::new(), shapes: ShapesPool::new(),
scale_content: HashMap::new(),
} }
} }
@@ -65,13 +63,13 @@ impl<'a> State<'a> {
pub fn start_render_loop(&mut self, timestamp: i32) -> Result<(), String> { pub fn start_render_loop(&mut self, timestamp: i32) -> Result<(), String> {
self.render_state self.render_state
.start_render_loop(&self.shapes, &self.scale_content, timestamp)?; .start_render_loop(&self.shapes, timestamp)?;
Ok(()) Ok(())
} }
pub fn process_animation_frame(&mut self, timestamp: i32) -> Result<(), String> { pub fn process_animation_frame(&mut self, timestamp: i32) -> Result<(), String> {
self.render_state self.render_state
.process_animation_frame(&self.shapes, &self.scale_content, timestamp)?; .process_animation_frame(&self.shapes, timestamp)?;
Ok(()) Ok(())
} }

View File

@@ -33,6 +33,7 @@ pub struct ShapesPoolImpl<'a> {
modified_shape_cache: HashMap<&'a Uuid, OnceCell<Shape>>, modified_shape_cache: HashMap<&'a Uuid, OnceCell<Shape>>,
modifiers: HashMap<&'a Uuid, skia::Matrix>, modifiers: HashMap<&'a Uuid, skia::Matrix>,
structure: HashMap<&'a Uuid, Vec<StructureEntry>>, structure: HashMap<&'a Uuid, Vec<StructureEntry>>,
scale_content: HashMap<&'a Uuid, f32>,
} }
// Type aliases to avoid writing lifetimes everywhere // Type aliases to avoid writing lifetimes everywhere
@@ -50,6 +51,7 @@ impl<'a> ShapesPoolImpl<'a> {
modified_shape_cache: HashMap::default(), modified_shape_cache: HashMap::default(),
modifiers: HashMap::default(), modifiers: HashMap::default(),
structure: HashMap::default(), structure: HashMap::default(),
scale_content: HashMap::default(),
} }
} }
@@ -162,6 +164,20 @@ impl<'a> ShapesPoolImpl<'a> {
} }
} }
// Rebuild scale_content with fresh references
if !self.scale_content.is_empty() {
let old_scale_content: Vec<(Uuid, f32)> = self
.scale_content
.drain()
.map(|(uuid_ref, scale)| (*uuid_ref, scale))
.collect();
for (uuid, scale) in old_scale_content {
if let Some(uuid_ref) = self.get_uuid_ref(&uuid) {
self.scale_content.insert(uuid_ref, scale);
}
}
}
// Rebuild modified_shape_cache with fresh references // Rebuild modified_shape_cache with fresh references
if !self.modified_shape_cache.is_empty() { if !self.modified_shape_cache.is_empty() {
let old_cache: Vec<(Uuid, OnceCell<Shape>)> = self let old_cache: Vec<(Uuid, OnceCell<Shape>)> = self
@@ -204,6 +220,7 @@ impl<'a> ShapesPoolImpl<'a> {
let shape_ptr = &self.shapes[idx] as *const Shape; let shape_ptr = &self.shapes[idx] as *const Shape;
let modifiers_ptr = &self.modifiers as *const HashMap<&'a Uuid, skia::Matrix>; let modifiers_ptr = &self.modifiers as *const HashMap<&'a Uuid, skia::Matrix>;
let structure_ptr = &self.structure as *const HashMap<&'a Uuid, Vec<StructureEntry>>; let structure_ptr = &self.structure as *const HashMap<&'a Uuid, Vec<StructureEntry>>;
let scale_content_ptr = &self.scale_content as *const HashMap<&'a Uuid, f32>;
let cache_ptr = &self.modified_shape_cache as *const HashMap<&'a Uuid, OnceCell<Shape>>; let cache_ptr = &self.modified_shape_cache as *const HashMap<&'a Uuid, OnceCell<Shape>>;
// Extend the lifetime of id to 'a - safe because it's the same Uuid stored in shapes[idx].id // Extend the lifetime of id to 'a - safe because it's the same Uuid stored in shapes[idx].id
@@ -212,15 +229,19 @@ impl<'a> ShapesPoolImpl<'a> {
if self.to_update_bool(&*shape_ptr) if self.to_update_bool(&*shape_ptr)
|| (*modifiers_ptr).contains_key(&id_ref) || (*modifiers_ptr).contains_key(&id_ref)
|| (*structure_ptr).contains_key(&id_ref) || (*structure_ptr).contains_key(&id_ref)
|| (*scale_content_ptr).contains_key(&id_ref)
{ {
if let Some(cell) = (*cache_ptr).get(&id_ref) { if let Some(cell) = (*cache_ptr).get(&id_ref) {
Some(cell.get_or_init(|| { Some(cell.get_or_init(|| {
let shape = &*shape_ptr; let mut shape = (*shape_ptr).transformed(
shape.transformed(
self, self,
(*modifiers_ptr).get(&id_ref), (*modifiers_ptr).get(&id_ref),
(*structure_ptr).get(&id_ref), (*structure_ptr).get(&id_ref),
) );
if let Some(scale) = (*scale_content_ptr).get(&id_ref) {
shape.scale_content(*scale);
}
shape
})) }))
} else { } else {
Some(&*shape_ptr) Some(&*shape_ptr)
@@ -240,12 +261,10 @@ impl<'a> ShapesPoolImpl<'a> {
self.shapes.iter_mut() self.shapes.iter_mut()
} }
#[allow(dead_code)]
fn clean_shape_cache(&mut self) { fn clean_shape_cache(&mut self) {
self.modified_shape_cache.clear() self.modified_shape_cache.clear()
} }
#[allow(dead_code)]
pub fn set_modifiers(&mut self, modifiers: HashMap<Uuid, skia::Matrix>) { pub fn set_modifiers(&mut self, modifiers: HashMap<Uuid, skia::Matrix>) {
// self.clean_shape_cache(); // self.clean_shape_cache();
@@ -272,7 +291,6 @@ impl<'a> ShapesPoolImpl<'a> {
} }
} }
#[allow(dead_code)]
pub fn set_structure(&mut self, structure: HashMap<Uuid, Vec<StructureEntry>>) { pub fn set_structure(&mut self, structure: HashMap<Uuid, Vec<StructureEntry>>) {
// Convert HashMap<Uuid, V> to HashMap<&'a Uuid, V> using references from shapes and // Convert HashMap<Uuid, V> to HashMap<&'a Uuid, V> using references from shapes and
// Initialize the cache cells because later we don't want to have the mutable pointer // Initialize the cache cells because later we don't want to have the mutable pointer
@@ -295,16 +313,33 @@ impl<'a> ShapesPoolImpl<'a> {
} }
} }
#[allow(dead_code)] pub fn set_scale_content(&mut self, scale_content: HashMap<Uuid, f32>) {
pub fn clean_modifiers(&mut self) { // Convert HashMap<Uuid, V> to HashMap<&'a Uuid, V> using references from shapes and
self.clean_shape_cache(); // Initialize the cache cells because later we don't want to have the mutable pointer
self.modifiers = HashMap::default(); let mut scale_content_with_refs = HashMap::with_capacity(scale_content.len());
let mut ids = Vec::<Uuid>::new();
for (uuid, value) in scale_content {
if let Some(uuid_ref) = self.get_uuid_ref(&uuid) {
scale_content_with_refs.insert(uuid_ref, value);
ids.push(*uuid_ref);
}
}
self.scale_content = scale_content_with_refs;
let all_ids = shapes::all_with_ancestors(&ids, self, true);
for uuid in all_ids {
if let Some(uuid_ref) = self.get_uuid_ref(&uuid) {
self.modified_shape_cache.insert(uuid_ref, OnceCell::new());
}
}
} }
#[allow(dead_code)] pub fn clean_all(&mut self) {
pub fn clean_structure(&mut self) {
self.clean_shape_cache(); self.clean_shape_cache();
self.modifiers = HashMap::default();
self.structure = HashMap::default(); self.structure = HashMap::default();
self.scale_content = HashMap::default();
} }
/// Get a reference to the Uuid stored in a shape, if it exists /// Get a reference to the Uuid stored in a shape, if it exists
@@ -346,6 +381,7 @@ impl<'a> ShapesPoolImpl<'a> {
modified_shape_cache: HashMap::default(), modified_shape_cache: HashMap::default(),
modifiers: HashMap::default(), modifiers: HashMap::default(),
structure: HashMap::default(), structure: HashMap::default(),
scale_content: HashMap::default(),
}; };
result.rebuild_references(); result.rebuild_references();