Skip to content

Commit

Permalink
Optimize usvg_tree::NodeExt::abs_transform.
Browse files Browse the repository at this point in the history
  • Loading branch information
RazrFalcon committed Dec 3, 2023
1 parent 9818899 commit 6eb5cf2
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 17 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ This changelog also contains important changes in dependencies.
### Added
- `usvg_tree::Text::flattened` that will contain an flattened/outlined text.
- `usvg_tree::Text::bounding_box`. Will be set only after text flattening.
- Optimize `usvg_tree::NodeExt::abs_transform` by storing absolute transforms in the tree
instead of calculating them each time.

### Changed
- `usvg_tree::Text::positions` was replaced with `usvg_tree::Text::dx` and `usvg_tree::Text::dy`.<br>
Expand Down
2 changes: 2 additions & 0 deletions crates/usvg-parser/src/converter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ pub(crate) fn convert_doc(svg_doc: &svgtree::Document, opt: &Options) -> Result<
convert_children(svg_doc.root(), &state, &mut cache, &mut tree.root);

remove_empty_groups(&mut tree);
tree.calculate_abs_transforms();

if restore_viewbox {
calculate_svg_bbox(&mut tree);
Expand Down Expand Up @@ -541,6 +542,7 @@ pub(crate) fn convert_group(
let g = parent.append_kind(NodeKind::Group(Group {
id,
transform,
abs_transform: Transform::identity(),
opacity,
blend_mode,
isolate,
Expand Down
1 change: 1 addition & 0 deletions crates/usvg-text-layout/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ pub trait TreeTextToPath {
impl TreeTextToPath for usvg_tree::Tree {
fn convert_text(&mut self, fontdb: &fontdb::Database) {
convert_text(self.root.clone(), fontdb);
self.calculate_abs_transforms();
}
}

Expand Down
67 changes: 50 additions & 17 deletions crates/usvg-tree/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -743,9 +743,19 @@ pub struct Group {
/// Can be empty.
pub id: String,

/// Element transform.
/// Element's transform.
pub transform: Transform,

/// Element's absolute transform.
///
/// Contains all ancestors transforms.
/// Will be set automatically by the parser or can be recalculated manually using
/// [`Tree::calculate_abs_transforms`].
///
/// Note that subroots, like clipPaths, masks and patterns, have their own root transform,
/// which isn't affected by the node that references this subroot.
pub abs_transform: Transform,

/// Group opacity.
///
/// After the group is rendered we should combine
Expand Down Expand Up @@ -777,6 +787,7 @@ impl Default for Group {
Group {
id: String::new(),
transform: Transform::default(),
abs_transform: Transform::default(),
opacity: Opacity::ONE,
blend_mode: BlendMode::Normal,
isolate: false,
Expand Down Expand Up @@ -999,6 +1010,16 @@ impl Tree {
pub fn filters<F: FnMut(Rc<filter::Filter>)>(&self, mut f: F) {
loop_over_filters(&self.root, &mut f)
}

/// Calculates absolute transforms for all nodes in the tree.
///
/// As of now, sets [`Group::abs_transform`].
///
/// Automatically called by the parser
/// and ideally should be called manually after each tree modification.
pub fn calculate_abs_transforms(&mut self) {
calculate_abs_transform(&self.root, Transform::identity());
}
}

fn has_text_nodes(root: &Node) -> bool {
Expand Down Expand Up @@ -1203,17 +1224,17 @@ pub trait NodeExt {
///
/// If a current node doesn't support transformation - a default
/// transform will be returned.
///
/// This method is cheap, since an absolute transform is already stored in
/// [`Group::abs_transform`].
fn abs_transform(&self) -> Transform;

/// Appends `kind` as a node child.
///
/// Shorthand for `Node::append(Node::new(Box::new(kind)))`.
fn append_kind(&self, kind: NodeKind) -> Node;

/// Calculates node's absolute bounding box.
///
/// Always returns `None` for `NodeKind::Text` since we cannot calculate its bbox
/// without converting it into paths first.
/// Returns `None` for `NodeKind::Text` unless it was flattened already.
fn calculate_bbox(&self) -> Option<Rect>;

/// Calls a closure for each subroot this `Node` has.
Expand Down Expand Up @@ -1248,19 +1269,13 @@ impl NodeExt for Node {
}

fn abs_transform(&self) -> Transform {
let mut ts_list = Vec::new();
for p in self.ancestors() {
if let NodeKind::Group(ref group) = *p.borrow() {
ts_list.push(group.transform);
}
}

let mut abs_ts = Transform::default();
for ts in ts_list.iter().rev() {
abs_ts = abs_ts.pre_concat(*ts);
if let NodeKind::Group(ref g) = *self.borrow() {
g.abs_transform
} else {
// Only groups can have a transform, therefore for paths, images and text
// we simply use the parent transform.
self.parent().map(|n| n.abs_transform()).unwrap_or_default()
}

abs_ts
}

#[inline]
Expand Down Expand Up @@ -1314,3 +1329,21 @@ fn calc_node_bbox(node: &Node, ts: Transform) -> Option<BBox> {
}
}
}

// TODO: test somehow
fn calculate_abs_transform(node: &Node, ts: Transform) {
if matches!(*node.borrow(), NodeKind::Group(_)) {
let mut abs_ts = ts;
if let NodeKind::Group(ref mut group) = *node.borrow_mut() {
group.abs_transform = ts.pre_concat(group.transform);
abs_ts = group.abs_transform;
}

for child in node.children() {
calculate_abs_transform(&child, abs_ts);
}
}

// Yes, subroots are not affected by the node's transform.
node.subroots(|root| calculate_abs_transform(&root, Transform::identity()));
}

0 comments on commit 6eb5cf2

Please sign in to comment.