Instead of checking each element in the tree each time, which would be pretty slow, we have a ID<->Node HashMap to quickly retrieve a requested element. ## Links are groups The `` element in SVG is just a `

Since we really support only the static SVG subset, we can replace `

The main limitation of this approach, excluding the fact we're creating way more elements that we had initially, is that copied elements must not have an `id` attribute, otherwise we would end up with multiple duplicates. usvg-parser-0.36.0/src/clippath.rs000064400000000000000000000053021046102023000151310ustar 00000000000000// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. use std::rc::Rc; use std::str::FromStr; use usvg_tree::{ClipPath, Group, Node, NodeKind, Transform, Units}; use crate::converter; use crate::svgtree::{AId, EId, SvgNode}; pub(crate) fn convert( node: SvgNode, state: &converter::State, cache: &mut converter::Cache, ) -> Option

(path: &[Segment], kind: MarkerKind, mut draw_marker: P)
where
P: FnMut(tiny_skia_path::Point, usize),
{
match kind {
MarkerKind::Start => {
if let Some(Segment::MoveTo(p)) = path.first().cloned() {
draw_marker(p, 0);
}
}
MarkerKind::Middle => {
let total = path.len() - 1;
let mut i = 1;
while i < total {
let p = match path[i] {
Segment::MoveTo(p) => p,
Segment::LineTo(p) => p,
Segment::CubicTo(_, _, p) => p,
_ => {
i += 1;
continue;
}
};
draw_marker(p, i);
i += 1;
}
}
MarkerKind::End => {
let idx = path.len() - 1;
match path.last().cloned() {
Some(Segment::LineTo(p)) => {
draw_marker(p, idx);
}
Some(Segment::CubicTo(_, _, p)) => {
draw_marker(p, idx);
}
Some(Segment::Close) => {
let p = get_subpath_start(path, idx);
draw_marker(p, idx);
}
_ => {}
}
}
}
}
fn calc_vertex_angle(path: &[Segment], idx: usize) -> f32 {
if idx == 0 {
// First segment.
debug_assert!(path.len() > 1);
let seg1 = path[0];
let seg2 = path[1];
match (seg1, seg2) {
(Segment::MoveTo(pm), Segment::LineTo(p)) => calc_line_angle(pm.x, pm.y, p.x, p.y),
(Segment::MoveTo(pm), Segment::CubicTo(p1, _, p)) => {
if pm.x.approx_eq_ulps(&p1.x, 4) && pm.y.approx_eq_ulps(&p1.y, 4) {
calc_line_angle(pm.x, pm.y, p.x, p.y)
} else {
calc_line_angle(pm.x, pm.y, p1.x, p1.y)
}
}
_ => 0.0,
}
} else if idx == path.len() - 1 {
// Last segment.
let seg1 = path[idx - 1];
let seg2 = path[idx];
match (seg1, seg2) {
(_, Segment::MoveTo(_)) => 0.0, // unreachable
(_, Segment::LineTo(p)) => {
let prev = get_prev_vertex(path, idx);
calc_line_angle(prev.x, prev.y, p.x, p.y)
}
(_, Segment::CubicTo(p1, p2, p)) => {
if p2.x.approx_eq_ulps(&p.x, 4) && p2.y.approx_eq_ulps(&p.y, 4) {
calc_line_angle(p1.x, p1.y, p.x, p.y)
} else {
calc_line_angle(p2.x, p2.y, p.x, p.y)
}
}
(Segment::LineTo(p), Segment::Close) => {
let next = get_subpath_start(path, idx);
calc_line_angle(p.x, p.y, next.x, next.y)
}
(Segment::CubicTo(_, p2, p), Segment::Close) => {
let prev = get_prev_vertex(path, idx);
let next = get_subpath_start(path, idx);
calc_curves_angle(
prev.x, prev.y, p2.x, p2.y, p.x, p.y, next.x, next.y, next.x, next.y,
)
}
(_, Segment::Close) => 0.0,
}
} else {
// Middle segments.
let seg1 = path[idx];
let seg2 = path[idx + 1];
// TODO: Not sure if there is a better way.
match (seg1, seg2) {
(Segment::MoveTo(pm), Segment::LineTo(p)) => calc_line_angle(pm.x, pm.y, p.x, p.y),
(Segment::MoveTo(pm), Segment::CubicTo(p1, _, _)) => {
calc_line_angle(pm.x, pm.y, p1.x, p1.y)
}
(Segment::LineTo(p1), Segment::LineTo(p2)) => {
let prev = get_prev_vertex(path, idx);
calc_angle(prev.x, prev.y, p1.x, p1.y, p1.x, p1.y, p2.x, p2.y)
}
(Segment::CubicTo(_, c1_p2, c1_p), Segment::CubicTo(c2_p1, _, c2_p)) => {
let prev = get_prev_vertex(path, idx);
calc_curves_angle(
prev.x, prev.y, c1_p2.x, c1_p2.y, c1_p.x, c1_p.y, c2_p1.x, c2_p1.y, c2_p.x,
c2_p.y,
)
}
(Segment::LineTo(pl), Segment::CubicTo(p1, _, p)) => {
let prev = get_prev_vertex(path, idx);
calc_curves_angle(
prev.x, prev.y, prev.x, prev.y, pl.x, pl.y, p1.x, p1.y, p.x, p.y,
)
}
(Segment::CubicTo(_, p2, p), Segment::LineTo(pl)) => {
let prev = get_prev_vertex(path, idx);
calc_curves_angle(prev.x, prev.y, p2.x, p2.y, p.x, p.y, pl.x, pl.y, pl.x, pl.y)
}
(Segment::LineTo(p), Segment::MoveTo(_)) => {
let prev = get_prev_vertex(path, idx);
calc_line_angle(prev.x, prev.y, p.x, p.y)
}
(Segment::CubicTo(_, p2, p), Segment::MoveTo(_)) => {
if p.x.approx_eq_ulps(&p2.x, 4) && p.y.approx_eq_ulps(&p2.y, 4) {
let prev = get_prev_vertex(path, idx);
calc_line_angle(prev.x, prev.y, p.x, p.y)
} else {
calc_line_angle(p2.x, p2.y, p.x, p.y)
}
}
(Segment::LineTo(p), Segment::Close) => {
let prev = get_prev_vertex(path, idx);
let next = get_subpath_start(path, idx);
calc_angle(prev.x, prev.y, p.x, p.y, p.x, p.y, next.x, next.y)
}
(_, Segment::Close) => {
let prev = get_prev_vertex(path, idx);
let next = get_subpath_start(path, idx);
calc_line_angle(prev.x, prev.y, next.x, next.y)
}
(_, Segment::MoveTo(_)) | (Segment::Close, _) => 0.0,
}
}
}
fn calc_line_angle(x1: f32, y1: f32, x2: f32, y2: f32) -> f32 {
calc_angle(x1, y1, x2, y2, x1, y1, x2, y2)
}
fn calc_curves_angle(
px: f32,
py: f32, // previous vertex
cx1: f32,
cy1: f32, // previous control point
x: f32,
y: f32, // current vertex
cx2: f32,
cy2: f32, // next control point
nx: f32,
ny: f32, // next vertex
) -> f32 {
if cx1.approx_eq_ulps(&x, 4) && cy1.approx_eq_ulps(&y, 4) {
calc_angle(px, py, x, y, x, y, cx2, cy2)
} else if x.approx_eq_ulps(&cx2, 4) && y.approx_eq_ulps(&cy2, 4) {
calc_angle(cx1, cy1, x, y, x, y, nx, ny)
} else {
calc_angle(cx1, cy1, x, y, x, y, cx2, cy2)
}
}
fn calc_angle(x1: f32, y1: f32, x2: f32, y2: f32, x3: f32, y3: f32, x4: f32, y4: f32) -> f32 {
use std::f32::consts::*;
fn normalize(rad: f32) -> f32 {
let v = rad % (PI * 2.0);
if v < 0.0 {
v + PI * 2.0
} else {
v
}
}
fn vector_angle(vx: f32, vy: f32) -> f32 {
let rad = vy.atan2(vx);
if rad.is_nan() {
0.0
} else {
normalize(rad)
}
}
let in_a = vector_angle(x2 - x1, y2 - y1);
let out_a = vector_angle(x4 - x3, y4 - y3);
let d = (out_a - in_a) * 0.5;
let mut angle = in_a + d;
if FRAC_PI_2 < d.abs() {
angle -= PI;
}
normalize(angle).to_degrees()
}
fn get_subpath_start(segments: &[Segment], idx: usize) -> tiny_skia_path::Point {
let offset = segments.len() - idx;
for seg in segments.iter().rev().skip(offset) {
if let Segment::MoveTo(p) = *seg {
return p;
}
}
tiny_skia_path::Point::zero()
}
fn get_prev_vertex(segments: &[Segment], idx: usize) -> tiny_skia_path::Point {
match segments[idx - 1] {
Segment::MoveTo(p) => p,
Segment::LineTo(p) => p,
Segment::CubicTo(_, _, p) => p,
Segment::Close => get_subpath_start(segments, idx),
}
}
fn convert_rect(node: SvgNode, state: &converter::State) -> Option