intvg/
convert.rs

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)?;    //&std::fs::read(&path)?,
15
16        //Ok(Self::from_usvg(&tree)) }
17        //fn from_usvg(tree: &usvg::Tree) -> Self {
18
19        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 = match tvg.header.coordinate_range { CoordinateRange::Default => 16,
25        //    CoordinateRange::Reduced => 8, CoordinateRange::Enhanced => 32,
26        //} - 1; // coordinate_range/write_default isn't changed from default
27        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        // XXX: still need a traversely check CoordinateRange by a null writer?
32
33        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) =>     // XXX: trfm is needed on rendering only
43            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();    // XXX: need to apply transform?
52                convert_paint(tvg, line.paint(), line.opacity(), trfm) });
53
54            //match path.paint_order() {} // XXX:
55            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 {  //unsafe { std::mem::transmute(pt) }
81        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) => { //trfm.map_point(&mut pt);
92                if !cmds.is_empty() { coll.push(Segment { start, cmds }); cmds = vec![]; }
93                start = pt.into();  continue
94            }
95            skia::PathSegment::LineTo(pt) => { //trfm.map_point(&mut pt);
96                SegInstr::Line { end: pt.into() }
97            }
98            skia::PathSegment::QuadTo(ctrl, end) => {
99                //trfm.map_point(&mut ctrl);  trfm.map_point(&mut end);
100                SegInstr::QuadBezier { ctrl: ctrl.into(), end: end.into() }
101            }
102            skia::PathSegment::CubicTo(ctrl0, ctrl1, end) => {
103                //trfm.map_point(&mut ctrl0);
104                //trfm.map_point(&mut ctrl1);   trfm.map_point(&mut end);
105                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 {   // unsafe { std::mem::transmute(pt) }
122        fn from(pt: (f32, f32)) -> Self { Self { x: pt.0, y: pt.1 } }
123    }
124
125    match paint { usvg::Paint::Pattern(_) => {  // trfm should be applied here
126            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(); // focus/start, center/end
140            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