diff --git a/examples/with_winit/src/pico_svg.rs b/examples/with_winit/src/pico_svg.rs index 2883ede..b3348ce 100644 --- a/examples/with_winit/src/pico_svg.rs +++ b/examples/with_winit/src/pico_svg.rs @@ -1,6 +1,6 @@ //! A loader for a tiny fragment of SVG -use std::str::FromStr; +use std::{num::ParseFloatError, str::FromStr}; use roxmltree::{Document, Node}; use vello::{ @@ -39,45 +39,71 @@ impl PicoSvg { let root = doc.root_element(); let mut items = Vec::new(); let mut parser = Parser::new(&mut items, scale); + let transform = if scale >= 0.0 { + Affine::scale(scale) + } else { + Affine::new([-scale, 0.0, 0.0, scale, 0.0, 0.0]) + }; + let props = RecursiveProperties { + transform, + fill: Some(Color::BLACK), + }; + // The root element is the svg document element, which we don't care about for node in root.children() { - parser.rec_parse(node)?; + parser.rec_parse(node, &props)?; } Ok(PicoSvg { items }) } } +#[derive(Clone)] +struct RecursiveProperties { + transform: Affine, + fill: Option, +} + impl<'a> Parser<'a> { fn new(items: &'a mut Vec, scale: f64) -> Parser<'a> { Parser { scale, items } } - fn rec_parse(&mut self, node: Node) -> Result<(), Box> { - let transform = if self.scale >= 0.0 { - Affine::scale(self.scale) - } else { - Affine::new([-self.scale, 0.0, 0.0, self.scale, 0.0, 1536.0]) - }; + fn rec_parse( + &mut self, + node: Node, + properties: &RecursiveProperties, + ) -> Result<(), Box> { if node.is_element() { + let mut properties = properties.clone(); + if let Some(fill_color) = node.attribute("fill") { + if fill_color == "none" { + properties.fill = None; + } else { + let color = parse_color(fill_color); + let color = modify_opacity(color, "fill-opacity", node); + // TODO: Handle recursive opacity properly + let color = modify_opacity(color, "opacity", node); + properties.fill = Some(color); + } + } + if let Some(transform) = node.attribute("transform") { + let new_transform = parse_transform(transform); + properties.transform = properties.transform * new_transform; + } match node.tag_name().name() { "g" => { for child in node.children() { - self.rec_parse(child)?; + self.rec_parse(child, &properties)?; } } "path" => { let d = node.attribute("d").ok_or("missing 'd' attribute")?; let bp = BezPath::from_svg(d)?; - let path = transform * bp; - // TODO: default fill color is black, but this is overridden in tiger to this logic. - if let Some(fill_color) = node.attribute("fill") { - if fill_color != "none" { - let color = parse_color(fill_color); - let color = modify_opacity(color, "fill-opacity", node); - self.items.push(Item::Fill(FillItem { - color, - path: path.clone(), - })); - } + let path = properties.transform * bp; + if let Some(color) = properties.fill { + self.items.push(Item::Fill(FillItem { + color, + path: path.clone(), + })); } if let Some(stroke_color) = node.attribute("stroke") { if stroke_color != "none" { @@ -87,19 +113,40 @@ impl<'a> Parser<'a> { )?; let color = parse_color(stroke_color); let color = modify_opacity(color, "stroke-opacity", node); + // TODO: Handle recursive opacity properly + let color = modify_opacity(color, "opacity", node); self.items .push(Item::Stroke(StrokeItem { width, color, path })); } } } - _ => (), + other => eprintln!("Unhandled node type {other}"), } } Ok(()) } } +fn parse_transform(transform: &str) -> Affine { + let transform = transform.trim(); + if transform.starts_with("matrix(") { + let vals = transform["matrix(".len()..transform.len() - 1] + .split(|c| matches!(c, ',' | ' ')) + .map(str::parse) + .collect::, ParseFloatError>>() + .expect("Could parse all values of 'matrix' as floats"); + Affine::new( + vals.try_into() + .expect("Should be six arguments to `matrix`"), + ) + } else { + eprintln!("Did not understand transform attribute {transform:?}"); + Affine::IDENTITY + } +} + fn parse_color(color: &str) -> Color { + let color = color.trim(); if color.as_bytes()[0] == b'#' { let mut hex = u32::from_str_radix(&color[1..], 16).unwrap(); if color.len() == 4 { diff --git a/src/render.rs b/src/render.rs index 5d4000c..352b6e7 100644 --- a/src/render.rs +++ b/src/render.rs @@ -362,7 +362,7 @@ pub fn render_encoding_full( ], ); - let segments_buf = ResourceProxy::new_buf(1 << 24, "segments_buf"); + let segments_buf = ResourceProxy::new_buf(1 << 26, "segments_buf"); recording.dispatch( shaders.path_coarse, (path_coarse_wgs, 1, 1), @@ -382,7 +382,7 @@ pub fn render_encoding_full( (path_wgs, 1, 1), [config_buf, path_buf, tile_buf], ); - let ptcl_buf = ResourceProxy::new_buf(1 << 24, "ptcl_buf"); + let ptcl_buf = ResourceProxy::new_buf(1 << 25, "ptcl_buf"); recording.dispatch( shaders.coarse, (width_in_bins, height_in_bins, 1),