1
2use crate::tinyvg::*;
3use std::{error::Error, io};
4use usvg::tiny_skia_path as skia;
5
6pub trait Convert { fn from_usvg(svgd: &[u8]) ->
7 Result<Self, Box<dyn Error>> where Self: std::marker::Sized;
8}
9
10impl<R: io::Read, W: io::Write> Convert for TinyVG<R, W> {
11 fn from_usvg(svgd: &[u8]) -> Result<Self, Box<dyn Error>> {
12 let mut usvg_opts = usvg::Options::default();
13 usvg_opts.fontdb_mut().load_system_fonts();
14 let tree = usvg::Tree::from_data(svgd, &usvg_opts)?; let mut tvg = Self::new();
20 tvg.header.width = tree.size().width() .round() as _;
21 tvg.header.height = tree.size().height().round() as _;
22
23 let coordinate_limit = u32::max(tvg.header.width, tvg.header.height);
24 let (range_bits, mut scale_bits) = (16 - 1, 0);
28
29 while scale_bits < range_bits && (coordinate_limit << (scale_bits + 1)) <
30 (1 << range_bits) { scale_bits += 1; } tvg.header.scale = scale_bits;
31 convert_nodes(&mut tvg, tree.root(), &usvg::Transform::identity());
34 println!("{:?}, {} colors, {} cmds/paths", &tvg.header,
35 tvg.color_table.len(), tvg.commands.len()); Ok(tvg)
36 }
37}
38
39fn convert_nodes<R: io::Read, W: io::Write>(tvg: &mut TinyVG<R, W>,
40 parent: &usvg::Group, trfm: &usvg::Transform) {
41 for child in parent.children() { match child {
42 usvg::Node::Group(group) => convert_nodes(tvg, group, &trfm.pre_concat(group.transform())),
44
45 usvg::Node::Path(path) => if path.is_visible() {
46 let (coll, mut lwidth) = (convert_path(path.data(), trfm), 0.0);
47
48 let fill = path .fill().and_then(|fill|
49 convert_paint(tvg, fill.paint(), fill.opacity(), trfm));
50 let line = path.stroke().and_then(|line| {
51 lwidth = line.width().get(); convert_paint(tvg, line.paint(), line.opacity(), trfm) });
53
54 let cmd = match (fill, line) {
56 (Some(fill), None) => Command::FillPath(FillCMD { fill, coll }),
57 (None, Some(line)) => Command::DrawPath(DrawCMD { line, lwidth, coll }),
58 (Some(fill), Some(line)) =>
59 Command::OutlinePath(fill, DrawCMD { line, lwidth, coll }),
60
61 _ => { eprintln!("Neither fill nor line"); continue }
62 }; tvg.commands.push(cmd);
63 }
64
65 usvg::Node::Image(img) => if img.is_visible() {
66 match img.kind() { usvg::ImageKind::JPEG(_) |
67 usvg::ImageKind::PNG(_) | usvg::ImageKind::GIF(_) =>
68 eprintln!("TinyVG can't support raster images"),
69 usvg::ImageKind::SVG(svg) => convert_nodes(tvg, svg.root(), trfm),
70 }
71 }
72
73 usvg::Node::Text(text) => { let group = text.flattened();
74 convert_nodes(tvg, group, &trfm.pre_concat(group.transform()));
75 }
76 } }
77}
78
79fn convert_path(path: &skia::Path, trfm: &usvg::Transform) -> Vec<Segment> {
80 impl From<skia::Point> for Point { fn from(pt: skia::Point) -> Self { Self { x: pt.x, y: pt.y } }
82 }
83
84 let (mut coll, mut cmds) = (vec![], vec![]);
85 let mut start = Point { x: 0.0, y: 0.0 };
86 let tpath = if trfm.is_identity() { None
87 } else { path.clone().transform(*trfm) };
88
89 for seg in tpath.as_ref().unwrap_or(path).segments() {
90 let instr = match seg {
91 skia::PathSegment::MoveTo(pt) => { if !cmds.is_empty() { coll.push(Segment { start, cmds }); cmds = vec![]; }
93 start = pt.into(); continue
94 }
95 skia::PathSegment::LineTo(pt) => { SegInstr::Line { end: pt.into() }
97 }
98 skia::PathSegment::QuadTo(ctrl, end) => {
99 SegInstr::QuadBezier { ctrl: ctrl.into(), end: end.into() }
101 }
102 skia::PathSegment::CubicTo(ctrl0, ctrl1, end) => {
103 SegInstr::CubicBezier { ctrl: (ctrl0.into(), ctrl1.into()), end: end.into() }
106 }
107
108 skia::PathSegment::Close => SegInstr::ClosePath,
109 }; cmds.push(SegmentCommand { instr, lwidth: None });
110 } if !cmds.is_empty() { coll.push(Segment { start, cmds }); } coll
111}
112
113fn convert_paint<R: io::Read, W: io::Write>(tvg: &mut TinyVG<R, W>,
114 paint: &usvg::Paint, opacity: usvg::Opacity, _trfm: &usvg::Transform) -> Option<Style> {
115 let get_color = |stop: &usvg::Stop| {
116 let color = stop.color();
117 RGBA8888 { r: color.red, g: color.green, b: color.blue,
118 a: (stop.opacity() * opacity).to_u8() }
119 };
120
121 impl From<(f32, f32)> for Point { fn from(pt: (f32, f32)) -> Self { Self { x: pt.0, y: pt.1 } }
123 }
124
125 match paint { usvg::Paint::Pattern(_) => { eprintln!("Not support pattern painting"); None },
127 usvg::Paint::Color(color) => {
128 Some(Style::FlatColor(tvg.push_color(RGBA8888 { r: color.red,
129 g: color.green, b: color.blue, a: opacity.to_u8() })))
130 }
131 usvg::Paint::LinearGradient(grad) => {
132 let p0 = (grad.x1(), grad.y1()).into();
133 let p1 = (grad.x2(), grad.y2()).into();
134 let c0 = tvg.push_color(get_color(&grad.stops()[0]));
135 let c1 = tvg.push_color(get_color(&grad.stops()[1]));
136 Some(Style::LinearGradient { points: (p0, p1), cindex: (c0, c1) })
137 }
138 usvg::Paint::RadialGradient(grad) => {
139 let p0 = (grad.fx(), grad.fy()).into(); let p1 = (grad.cx(), grad.cy() + grad.r().get()).into();
141 let c0 = tvg.push_color(get_color(&grad.stops()[0]));
142 let c1 = tvg.push_color(get_color(&grad.stops()[1]));
143 Some(Style::RadialGradient { points: (p0, p1), cindex: (c0, c1) })
144 }
145 }
146}
147