This repository has no description
1use super::renderable::SVGRenderable;
2use crate::{
3 Object,
4 graphics::canvas::Canvas,
5 rendering::{
6 rasterization::{
7 create_pixmap, pixmap_to_png_data, svg_to_usvg_tree,
8 usvg_tree_to_pixmap, write_png_data,
9 },
10 svg,
11 },
12};
13use measure_time::debug_time;
14use std::path::PathBuf;
15
16impl SVGRenderable for Canvas {
17 fn render_to_svg(
18 &self,
19 colormap: crate::ColorMapping,
20 cell_size: usize,
21 object_sizes: crate::graphics::objects::ObjectSizes,
22 _id: &str,
23 ) -> anyhow::Result<svg::Node> {
24 debug_time!("render_to_svg/canvas");
25 let background_color = self.background.unwrap_or_default();
26 let mut svg = svg::tag("svg").attr("xmlns", "http://www.w3.org/2000/svg");
27
28 svg.add(
29 svg::tag("rect")
30 .attr("x", -(self.outer_padding as i32))
31 .attr("y", -(self.outer_padding as i32))
32 .attr("width", self.width())
33 .attr("height", self.height())
34 .attr("fill", background_color.render(&self.colormap)),
35 );
36
37 for layer in self.layers.iter().filter(|layer| !layer.hidden).rev() {
38 svg.add(layer.render_to_svg(
39 colormap.clone(),
40 cell_size,
41 layer.object_sizes,
42 layer.name.as_str(),
43 )?);
44 }
45
46 let mut defs = svg::tag("defs");
47 for filter in self.unique_filters() {
48 defs.add(filter.render_to_svg(
49 colormap.clone(),
50 cell_size,
51 object_sizes,
52 "",
53 )?);
54 }
55
56 for pattern_fill in self.unique_pattern_fills() {
57 if let Some(patterndef) =
58 pattern_fill.pattern_definition(&self.colormap)
59 {
60 defs.add(patterndef);
61 }
62 }
63
64 for layer in self.layers.iter() {
65 for Object { clip_to, .. } in layer.objects.values() {
66 if let Some(region) = clip_to {
67 defs.add(
68 svg::tag("clipPath")
69 .attr("id", region.clip_path_id())
70 .child(
71 svg::tag("rect")
72 .position(region.start, cell_size)
73 .size(*region, cell_size)
74 .node(),
75 ),
76 );
77 }
78 }
79 }
80
81 svg.add(defs);
82
83 Ok(svg
84 .attr(
85 "viewBox",
86 format!(
87 "{0} {0} {1} {2}",
88 -(self.outer_padding as i32),
89 self.width(),
90 self.height()
91 ),
92 )
93 .attr("width", self.width())
94 .attr("height", self.height())
95 .into())
96 }
97}
98
99impl Canvas {
100 pub fn svg_to_pixmap(
101 &self,
102 width: u32,
103 height: u32,
104 contents: &str,
105 ) -> anyhow::Result<tiny_skia::Pixmap> {
106 let mut pixmap = create_pixmap(width, height);
107
108 let parsed_svg = &svg_to_usvg_tree(contents, &self.fontdb)?;
109
110 usvg_tree_to_pixmap(self.dimensions(), pixmap.as_mut(), parsed_svg);
111
112 Ok(pixmap)
113 }
114
115 pub fn render_to_pixmap(
116 &mut self,
117 width: u32,
118 height: u32,
119 ) -> anyhow::Result<tiny_skia::Pixmap> {
120 let svg_contents = self
121 .render_to_svg(
122 self.colormap.clone(),
123 self.cell_size,
124 self.object_sizes,
125 "",
126 )?
127 .to_string();
128 self.svg_to_pixmap(width, height, &svg_contents)
129 }
130
131 // 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
132 pub fn render_to_png(
133 &mut self,
134 at: &str,
135 resolution: u32,
136 ) -> anyhow::Result<()> {
137 debug_time!("render_to_png");
138 let (width, height) = self.resolution_to_size(resolution);
139
140 self.render_to_pixmap(width, height).and_then(|pixmap| {
141 pixmap_to_png_data(pixmap).and_then(|data| write_png_data(data, at))
142 })
143 }
144
145 pub fn render_to_svg_string(&mut self) -> anyhow::Result<String> {
146 debug_time!("render_to_svg_string");
147
148 let rendered = self.render_to_svg(
149 self.colormap.clone(),
150 self.cell_size,
151 self.object_sizes,
152 "",
153 )?;
154
155 Ok(rendered.to_string())
156 }
157
158 pub fn render_to_svg_file(
159 &mut self,
160 at: impl Into<PathBuf>,
161 ) -> anyhow::Result<()> {
162 debug_time!("render_to_svg_file");
163
164 std::fs::write(at.into(), self.render_to_svg_string()?)?;
165
166 Ok(())
167 }
168}