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

@@ -541,6 +541,7 @@ pub extern "C" fn set_structure_modifiers() {
with_state_mut!(state, {
let mut structure = HashMap::new();
let mut scale_content = HashMap::new();
for entry in entries {
match entry.entry_type {
StructureEntryType::ScaleContent => {
@@ -548,7 +549,7 @@ pub extern "C" fn set_structure_modifiers() {
continue;
};
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() {
state.shapes.set_structure(structure);
}
@@ -571,16 +575,7 @@ pub extern "C" fn set_structure_modifiers() {
#[no_mangle]
pub extern "C" fn clean_modifiers() {
with_state_mut!(state, {
state.scale_content.clear();
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();
state.shapes.clean_all();
});
}

View File

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

View File

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

View File

@@ -23,7 +23,6 @@ fn propagate_children(
parent_bounds_after: &Bounds,
transform: Matrix,
bounds: &HashMap<Uuid, Bounds>,
scale_content: &HashMap<Uuid, f32>,
) -> VecDeque<Modifier> {
let children_ids = shape.children_ids(true);
@@ -38,8 +37,6 @@ fn propagate_children(
continue;
};
let ignore_constraints = scale_content.contains_key(child_id);
let child_bounds = bounds.find(child);
let constraint_h = match &shape.shape_type {
@@ -77,7 +74,7 @@ fn propagate_children(
constraint_h,
constraint_v,
transform,
ignore_constraints,
child.ignore_constraints,
);
result.push_back(Modifier::transform(*child_id, transform));
@@ -229,7 +226,6 @@ fn propagate_transform(
&shape_bounds_after,
transform,
bounds,
&state.scale_content,
);
entries.append(&mut children);
}
@@ -343,12 +339,6 @@ fn reflow_shape(
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 {
return;
};
@@ -468,7 +458,6 @@ mod tests {
&bounds_after,
transform,
&HashMap::new(),
&HashMap::new(),
);
assert_eq!(result.len(), 1);
@@ -499,8 +488,7 @@ mod tests {
let parent = shapes.get(&parent_id).unwrap();
let bounds =
calculate_group_bounds(parent, &shapes, &HashMap::new()).unwrap();
let bounds = calculate_group_bounds(parent, &shapes, &HashMap::new()).unwrap();
assert_eq!(bounds.width(), 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 current_id: Option<Uuid>,
pub shapes: ShapesPool<'a>,
pub scale_content: HashMap<Uuid, f32>,
}
impl<'a> State<'a> {
@@ -33,7 +32,6 @@ impl<'a> State<'a> {
text_editor_state: TextEditorState::new(),
current_id: None,
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> {
self.render_state
.start_render_loop(&self.shapes, &self.scale_content, timestamp)?;
.start_render_loop(&self.shapes, timestamp)?;
Ok(())
}
pub fn process_animation_frame(&mut self, timestamp: i32) -> Result<(), String> {
self.render_state
.process_animation_frame(&self.shapes, &self.scale_content, timestamp)?;
.process_animation_frame(&self.shapes, timestamp)?;
Ok(())
}

View File

@@ -33,6 +33,7 @@ pub struct ShapesPoolImpl<'a> {
modified_shape_cache: HashMap<&'a Uuid, OnceCell<Shape>>,
modifiers: HashMap<&'a Uuid, skia::Matrix>,
structure: HashMap<&'a Uuid, Vec<StructureEntry>>,
scale_content: HashMap<&'a Uuid, f32>,
}
// Type aliases to avoid writing lifetimes everywhere
@@ -50,6 +51,7 @@ impl<'a> ShapesPoolImpl<'a> {
modified_shape_cache: HashMap::default(),
modifiers: 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
if !self.modified_shape_cache.is_empty() {
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 modifiers_ptr = &self.modifiers as *const HashMap<&'a Uuid, skia::Matrix>;
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>>;
// 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)
|| (*modifiers_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) {
Some(cell.get_or_init(|| {
let shape = &*shape_ptr;
shape.transformed(
let mut shape = (*shape_ptr).transformed(
self,
(*modifiers_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 {
Some(&*shape_ptr)
@@ -240,12 +261,10 @@ impl<'a> ShapesPoolImpl<'a> {
self.shapes.iter_mut()
}
#[allow(dead_code)]
fn clean_shape_cache(&mut self) {
self.modified_shape_cache.clear()
}
#[allow(dead_code)]
pub fn set_modifiers(&mut self, modifiers: HashMap<Uuid, skia::Matrix>) {
// 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>>) {
// 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
@@ -295,16 +313,33 @@ impl<'a> ShapesPoolImpl<'a> {
}
}
#[allow(dead_code)]
pub fn clean_modifiers(&mut self) {
self.clean_shape_cache();
self.modifiers = HashMap::default();
pub fn set_scale_content(&mut self, scale_content: HashMap<Uuid, f32>) {
// 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
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_structure(&mut self) {
pub fn clean_all(&mut self) {
self.clean_shape_cache();
self.modifiers = HashMap::default();
self.structure = HashMap::default();
self.scale_content = HashMap::default();
}
/// 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(),
modifiers: HashMap::default(),
structure: HashMap::default(),
scale_content: HashMap::default(),
};
result.rebuild_references();