This repository has no description
1use super::renderable::SVGRenderable;
2use crate::{graphics::canvas::Canvas, rendering::svg};
3use measure_time::debug_time;
4use resvg::usvg;
5use std::sync::Arc;
6
7impl SVGRenderable for Canvas {
8 fn render_to_svg(
9 &self,
10 colormap: crate::ColorMapping,
11 cell_size: usize,
12 object_sizes: crate::graphics::objects::ObjectSizes,
13 _id: &str,
14 ) -> anyhow::Result<svg::Node> {
15 debug_time!("render_to_svg/canvas");
16 let background_color = self.background.unwrap_or_default();
17 let mut svg = svg::tag("svg").attr("xmlns", "http://www.w3.org/2000/svg");
18
19 svg.add(
20 svg::tag("rect")
21 .attr("x", -(self.canvas_outer_padding as i32))
22 .attr("y", -(self.canvas_outer_padding as i32))
23 .attr("width", self.width())
24 .attr("height", self.height())
25 .attr("fill", background_color.render(&self.colormap)),
26 );
27
28 for layer in self.layers.iter().filter(|layer| !layer.hidden).rev() {
29 svg.add(layer.render_to_svg(
30 colormap.clone(),
31 cell_size,
32 layer.object_sizes,
33 layer.name.as_str(),
34 )?);
35 }
36
37 let mut defs = svg::tag("defs");
38 for filter in self.unique_filters() {
39 defs.add(filter.render_to_svg(
40 colormap.clone(),
41 cell_size,
42 object_sizes,
43 "",
44 )?);
45 }
46
47 for pattern_fill in self.unique_pattern_fills() {
48 if let Some(patterndef) =
49 pattern_fill.pattern_definition(&self.colormap)
50 {
51 defs.add(patterndef);
52 }
53 }
54
55 svg.add(defs);
56
57 Ok(svg
58 .attr(
59 "viewBox",
60 format!(
61 "{0} {0} {1} {2}",
62 -(self.canvas_outer_padding as i32),
63 self.width(),
64 self.height()
65 ),
66 )
67 .attr("width", self.width())
68 .attr("height", self.height())
69 .into())
70 }
71}
72
73impl Canvas {
74 pub fn svg_to_pixmap(
75 &self,
76 width: u32,
77 height: u32,
78 contents: &str,
79 ) -> anyhow::Result<tiny_skia::Pixmap> {
80 let mut pixmap = self.create_pixmap(width, height);
81
82 let parsed_svg = &svg_to_usvg_tree(contents, &self.fontdb)?;
83
84 self.usvg_tree_to_pixmap(width, height, pixmap.as_mut(), parsed_svg);
85
86 Ok(pixmap)
87 }
88
89 pub fn render_to_pixmap(
90 &mut self,
91 width: u32,
92 height: u32,
93 ) -> anyhow::Result<tiny_skia::Pixmap> {
94 let svg_contents = self
95 .render_to_svg(
96 self.colormap.clone(),
97 self.cell_size,
98 self.object_sizes,
99 "",
100 )?
101 .to_string();
102 self.svg_to_pixmap(width, height, &svg_contents)
103 }
104
105 fn usvg_tree_to_pixmap(
106 &self,
107 width: u32,
108 height: u32,
109 mut pixmap_mut: tiny_skia::PixmapMut<'_>,
110 parsed_svg: &resvg::usvg::Tree,
111 ) {
112 debug_time!("usvg_tree_to_pixmap");
113 resvg::render(
114 parsed_svg,
115 tiny_skia::Transform::from_scale(
116 width as f32 / self.width() as f32,
117 height as f32 / self.height() as f32,
118 ),
119 &mut pixmap_mut,
120 );
121 }
122
123 fn create_pixmap(&self, width: u32, height: u32) -> tiny_skia::Pixmap {
124 debug_time!("create_pixmap");
125 tiny_skia::Pixmap::new(width, height).expect("Failed to create pixmap")
126 }
127
128 // previous_frame_at gives path to the previously rendered frame, which allows to copy on cache hits instead of having to re-write bytes again
129 pub fn render_to_png(
130 &mut self,
131 at: &str,
132 resolution: u32,
133 ) -> anyhow::Result<()> {
134 debug_time!("render_to_png");
135 let (width, height) = self.resolution_to_size(resolution);
136
137 self.render_to_pixmap(width, height).and_then(|pixmap| {
138 pixmap_to_png_data(pixmap).and_then(|data| write_png_data(data, at))
139 })
140 }
141
142 pub fn render_to_svg_string(&mut self) -> anyhow::Result<String> {
143 debug_time!("render_to_svg_string");
144
145 let rendered = self.render_to_svg(
146 self.colormap.clone(),
147 self.cell_size,
148 self.object_sizes,
149 "",
150 )?;
151
152 Ok(rendered.to_string())
153 }
154
155 pub fn render_to_svg_file(&mut self, at: &str) -> anyhow::Result<()> {
156 debug_time!("render_to_svg_file");
157
158 std::fs::write(at, self.render_to_svg_string()?)?;
159
160 Ok(())
161 }
162}
163
164fn svg_to_usvg_tree(
165 svg: &str,
166 fontdb: &Option<Arc<usvg::fontdb::Database>>,
167) -> anyhow::Result<resvg::usvg::Tree> {
168 debug_time!("svg_to_usvg_tree");
169 Ok(resvg::usvg::Tree::from_str(
170 svg,
171 &match fontdb {
172 Some(fontdb) => resvg::usvg::Options {
173 fontdb: fontdb.clone(),
174 ..Default::default()
175 },
176 None => resvg::usvg::Options::default(),
177 },
178 )?)
179}
180
181fn pixmap_to_png_data(pixmap: tiny_skia::Pixmap) -> anyhow::Result<Vec<u8>> {
182 debug_time!("pixmap_to_png_data");
183 Ok(pixmap.encode_png()?)
184}
185
186fn write_png_data(data: Vec<u8>, at: &str) -> anyhow::Result<()> {
187 debug_time!("write_png_data");
188 std::fs::write(at, data)?;
189 Ok(())
190}