1use std::fs::File;
6use std::io::prelude::*;
7use std::path;
8
9use anyhow::{bail, ensure};
10use euclid::default::Transform2D;
11use euclid::{point2, size2};
12
13use crate::color::Color;
14use crate::geometry::{Point, Size};
15use crate::render::{BlendMode, Context, Fill, FillRule, Layer, Path, PathBuilder, Raster, Style};
16
17#[derive(Clone, Debug, PartialEq)]
18enum PathCommand {
19 MoveTo(Point),
20 LineTo(Point),
21 QuadTo(Point, Point),
22 CubicTo(Point, Point, Point),
23 RatQuadTo(Point, Point, f32),
24 Close,
25}
26
27#[derive(Clone, Debug, PartialEq)]
28struct SvgPath {
29 pub commands: Vec<PathCommand>,
30 pub fill_rule: FillRule,
31 pub color: Color,
32}
33
34impl SvgPath {
35 pub fn apply_commands(&self, path_builder: &mut PathBuilder) {
36 let mut initial_point = None;
37
38 for command in &self.commands {
39 match *command {
40 PathCommand::MoveTo(p) => {
41 initial_point = Some(p);
42 path_builder.move_to(p);
43 }
44 PathCommand::LineTo(p) => {
45 path_builder.line_to(p);
46 }
47 PathCommand::QuadTo(p1, p2) => {
48 path_builder.quad_to(p1, p2);
49 }
50 PathCommand::RatQuadTo(p1, p2, w) => {
51 path_builder.rat_quad_to(p1, p2, w);
52 }
53 PathCommand::CubicTo(p1, p2, p3) => {
54 path_builder.cubic_to(p1, p2, p3);
55 }
56 PathCommand::Close => {
57 if let Some(initial_point) = initial_point {
58 path_builder.line_to(initial_point);
59 }
60 }
61 }
62 }
63 }
64}
65
66#[derive(Debug)]
67struct Parser {
68 bytes: Vec<u8>,
69 index: usize,
70}
71
72impl Parser {
73 pub fn new(bytes: Vec<u8>) -> Self {
74 Self { bytes, index: 4 }
75 }
76
77 fn parse_u8(&mut self) -> u8 {
78 let value = self.bytes[self.index];
79 self.index += 1;
80 value
81 }
82
83 fn parse_i16(&mut self) -> i16 {
84 let mut bytes = [0; 2];
85 bytes.copy_from_slice(&self.bytes[self.index..self.index + 2]);
86 self.index += 2;
87 i16::from_le_bytes(bytes)
88 }
89
90 fn parse_f32(&mut self) -> f32 {
91 let mut bytes = [0; 4];
92 bytes.copy_from_slice(&self.bytes[self.index..self.index + 4]);
93 self.index += 4;
94 f32::from_le_bytes(bytes)
95 }
96
97 fn len(&self) -> usize {
98 self.bytes.len() - self.index
99 }
100
101 fn parse_fill_rule(&mut self) -> anyhow::Result<FillRule> {
102 ensure!(self.len() >= 1, "missing fill rule");
103
104 match self.parse_u8() {
105 0 => Ok(FillRule::NonZero),
106 1 => Ok(FillRule::EvenOdd),
107 _ => bail!("incorrect fill rule"),
108 }
109 }
110
111 fn parse_color(&mut self) -> anyhow::Result<Color> {
112 ensure!(self.len() >= 4, "missing color");
113
114 Ok(Color { r: self.parse_u8(), g: self.parse_u8(), b: self.parse_u8(), a: self.parse_u8() })
115 }
116
117 fn parse_point(&mut self) -> Point {
118 let x = self.parse_i16() as f32 / 16.0;
119 let y = self.parse_i16() as f32 / 16.0;
120
121 point2(x, y)
122 }
123
124 fn parse_commands(&mut self) -> anyhow::Result<Vec<PathCommand>> {
125 let mut commands = Vec::new();
126
127 loop {
128 ensure!(self.len() >= 1, "missing commands");
129
130 match self.parse_u8() {
131 0 => {
132 ensure!(self.len() >= 4 * 1, "move to missing points");
133
134 commands.push(PathCommand::MoveTo(self.parse_point()));
135 }
136 1 => {
137 ensure!(self.len() >= 4 * 1, "line to missing points");
138
139 commands.push(PathCommand::LineTo(self.parse_point()));
140 }
141 2 => {
142 ensure!(self.len() >= 4 * 2, "quad to missing points");
143
144 commands.push(PathCommand::QuadTo(self.parse_point(), self.parse_point()));
145 }
146 3 => {
147 ensure!(self.len() >= 4 * 3, "cubic to missing points");
148
149 commands.push(PathCommand::CubicTo(
150 self.parse_point(),
151 self.parse_point(),
152 self.parse_point(),
153 ));
154 }
155 4 => {
156 ensure!(self.len() >= 4 * 3, "rational quad to missing points");
157
158 commands.push(PathCommand::RatQuadTo(
159 self.parse_point(),
160 self.parse_point(),
161 self.parse_f32(),
162 ));
163 }
164 5 => commands.push(PathCommand::Close),
165 6 => break,
166 _ => bail!("unrecognized command"),
167 }
168 }
169
170 Ok(commands)
171 }
172
173 pub fn parse_shed(&mut self) -> anyhow::Result<Shed> {
174 ensure!(self.len() >= 8, "missing size");
175
176 let width = self.parse_f32();
177 let height = self.parse_f32();
178
179 let mut paths = Vec::new();
180
181 while self.len() > 0 {
182 paths.push(SvgPath {
183 fill_rule: self.parse_fill_rule()?,
184 color: self.parse_color()?,
185 commands: self.parse_commands()?,
186 });
187 }
188
189 Ok(Shed { paths, size: size2(width, height) })
190 }
191}
192
193#[derive(Debug)]
194pub struct Shed {
195 paths: Vec<SvgPath>,
196 size: Size,
197}
198
199impl Shed {
200 pub fn open<P: AsRef<path::Path>>(path: P) -> anyhow::Result<Self> {
201 let mut file = File::open(path)?;
202 let mut bytes = Vec::new();
203
204 file.read_to_end(&mut bytes)?;
205
206 ensure!(&bytes[..4] == "shed".as_bytes(), ".shed file missing header");
207
208 let mut parser = Parser::new(bytes);
209
210 parser.parse_shed()
211 }
212
213 pub fn paths(&self, context: &mut Context) -> Vec<(Path, Style)> {
214 self.paths
215 .iter()
216 .map(|path| {
217 let mut path_builder = context.path_builder().expect("failed to get PathBuilder");
218
219 path.apply_commands(&mut path_builder);
220
221 let style = Style {
222 fill_rule: path.fill_rule,
223 fill: Fill::Solid(path.color),
224 blend_mode: BlendMode::Over,
225 };
226
227 (path_builder.build(), style)
228 })
229 .collect()
230 }
231
232 pub fn size(&self) -> Size {
233 self.size
234 }
235
236 pub fn rasters(
237 &self,
238 context: &mut Context,
239 transform: Option<&Transform2D<f32>>,
240 ) -> Vec<(Raster, Style)> {
241 self.paths
242 .iter()
243 .map(|path| {
244 let mut path_builder = context.path_builder().expect("failed to get PathBuilder");
245
246 path.apply_commands(&mut path_builder);
247
248 let style = Style {
249 fill_rule: path.fill_rule,
250 fill: Fill::Solid(path.color),
251 blend_mode: BlendMode::Over,
252 };
253
254 let mut raster_builder =
255 context.raster_builder().expect("failed to get RasterBuilder");
256 raster_builder.add(&path_builder.build(), transform);
257
258 (raster_builder.build(), style)
259 })
260 .collect()
261 }
262
263 pub fn layers(
264 &self,
265 context: &mut Context,
266 transform: Option<&Transform2D<f32>>,
267 ) -> Vec<Layer> {
268 self.paths
269 .iter()
270 .map(|path| {
271 let mut path_builder = context.path_builder().expect("failed to get PathBuilder");
272
273 path.apply_commands(&mut path_builder);
274
275 let style = Style {
276 fill_rule: path.fill_rule,
277 fill: Fill::Solid(path.color),
278 blend_mode: BlendMode::Over,
279 };
280
281 let mut raster_builder =
282 context.raster_builder().expect("failed to get RasterBuilder");
283 raster_builder.add(&path_builder.build(), transform);
284
285 Layer { raster: raster_builder.build(), clip: None, style }
286 })
287 .collect()
288 }
289}
290
291#[cfg(test)]
292mod tests {
293 use super::*;
294
295 #[test]
296 fn paths() {
297 let bytes = vec![
298 b's', b'h', b'e', b'd', 0, 0, 32, 65, 0, 0, 160, 65, 0, 255, 0, 0, 255, 0, 16, 0, 32, 0, 5, 6, 1, 0, 255, 0, 255, 0, 48, 0, 64, 0, 1, 80, 0, 96, 0, 5, 6, ];
313
314 let mut parser = Parser::new(bytes);
315
316 let shed = parser.parse_shed().unwrap();
317
318 assert_eq!(shed.size.width, 10.0);
319 assert_eq!(shed.size.height, 20.0);
320
321 assert_eq!(
322 shed.paths,
323 vec![
324 SvgPath {
325 commands: vec![PathCommand::MoveTo(point2(1.0, 2.0)), PathCommand::Close],
326 fill_rule: FillRule::NonZero,
327 color: Color { r: 255, g: 0, b: 0, a: 255 },
328 },
329 SvgPath {
330 commands: vec![
331 PathCommand::MoveTo(point2(3.0, 4.0)),
332 PathCommand::LineTo(point2(5.0, 6.0)),
333 PathCommand::Close,
334 ],
335 fill_rule: FillRule::EvenOdd,
336 color: Color { r: 0, g: 255, b: 0, a: 255 },
337 },
338 ]
339 );
340 }
341}