From d6083005e1b5a05e58dd6939ebbdca76e3812e81 Mon Sep 17 00:00:00 2001 From: "Spencer C. Imbleau" Date: Thu, 18 Jan 2024 23:01:56 -0500 Subject: [PATCH 1/7] fix: ignore DS_Store --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 273103933..61aac2c32 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ /target Cargo.lock /trace.json +.DS_Store examples/assets/downloads/* !examples/assets/downloads/.tracked From 34a6dce0d9c69eb86dd568912ca82a8a26a71896 Mon Sep 17 00:00:00 2001 From: "Spencer C. Imbleau" Date: Thu, 18 Jan 2024 23:13:08 -0500 Subject: [PATCH 2/7] chore: update to usvg 0.37 --- examples/scenes/src/svg.rs | 4 +- integrations/vello_svg/Cargo.toml | 2 +- integrations/vello_svg/src/lib.rs | 158 ++++++++++++++++++------------ 3 files changed, 99 insertions(+), 65 deletions(-) diff --git a/examples/scenes/src/svg.rs b/examples/scenes/src/svg.rs index dc0c13d8a..21709a33d 100644 --- a/examples/scenes/src/svg.rs +++ b/examples/scenes/src/svg.rs @@ -97,8 +97,8 @@ pub fn svg_function_of>( let start = Instant::now(); let mut new_scene = SceneFragment::new(); let mut builder = SceneBuilder::for_fragment(&mut new_scene); - vello_svg::render_tree(&mut builder, &svg); - let resolution = Vec2::new(svg.size.width(), svg.size.height()); + vello_svg::render_tree(&mut builder, &svg, None); + let resolution = Vec2::new(svg.size.width() as f64, svg.size.height() as f64); eprintln!("Encoded svg {name} in {:?}", start.elapsed()); (new_scene, resolution) } diff --git a/integrations/vello_svg/Cargo.toml b/integrations/vello_svg/Cargo.toml index 149e22135..e59a7b77e 100644 --- a/integrations/vello_svg/Cargo.toml +++ b/integrations/vello_svg/Cargo.toml @@ -13,4 +13,4 @@ repository.workspace = true [dependencies] vello = { path = "../../" } -usvg = "0.33" +usvg = "0.37" diff --git a/integrations/vello_svg/src/lib.rs b/integrations/vello_svg/src/lib.rs index 4b6fba451..563f275a6 100644 --- a/integrations/vello_svg/src/lib.rs +++ b/integrations/vello_svg/src/lib.rs @@ -33,22 +33,28 @@ //! - patterns use std::convert::Infallible; - use usvg::NodeExt; -use vello::kurbo::{Affine, BezPath, Rect, Stroke}; -use vello::peniko::{Brush, Color, Fill}; -use vello::SceneBuilder; +use vello::{ + kurbo::{Affine, BezPath, Point, Rect, Stroke}, + peniko::{Brush, Color, Fill}, + SceneBuilder, +}; + +/// Re-export vello. +pub use vello; +/// Re-export usvg. pub use usvg; -/// Append a [`usvg::Tree`] into a Vello [`SceneBuilder`], with default error handling -/// This will draw a red box over (some) unsupported elements +/// Append a [`usvg::Tree`] into a Vello [`SceneBuilder`], with default error +/// handling This will draw a red box over (some) unsupported elements /// /// Calls [`render_tree_with`] with an error handler implementing the above. /// -/// See the [module level documentation](crate#unsupported-features) for a list of some unsupported svg features -pub fn render_tree(sb: &mut SceneBuilder, svg: &usvg::Tree) { - render_tree_with(sb, svg, default_error_handler).unwrap_or_else(|e| match e {}); +/// See the [module level documentation](crate#unsupported-features) for a list +/// of some unsupported svg features +pub fn render_tree(sb: &mut SceneBuilder, svg: &usvg::Tree, transform: Option) { + render_tree_with(sb, svg, transform, default_error_handler).unwrap_or_else(|e| match e {}) } /// Append a [`usvg::Tree`] into a Vello [`SceneBuilder`]. @@ -56,55 +62,78 @@ pub fn render_tree(sb: &mut SceneBuilder, svg: &usvg::Tree) { /// Calls [`render_tree_with`] with [`default_error_handler`]. /// This will draw a red box over unsupported element types. /// -/// See the [module level documentation](crate#unsupported-features) for a list of some unsupported svg features +/// See the [module level documentation](crate#unsupported-features) for a list +/// of some unsupported svg features pub fn render_tree_with Result<(), E>, E>( sb: &mut SceneBuilder, svg: &usvg::Tree, + transform2: Option, mut on_err: F, ) -> Result<(), E> { for elt in svg.root.descendants() { let transform = { - let usvg::Transform { a, b, c, d, e, f } = elt.abs_transform(); - Affine::new([a, b, c, d, e, f]) + let usvg::Transform { + sx, + kx, + ky, + sy, + tx, + ty, + } = elt.abs_transform(); + let (a, b, c, d, e, f) = ( + sx as f64, ky as f64, kx as f64, sy as f64, tx as f64, ty as f64, + ); + if let Some(t2) = transform2 { + t2 * Affine::new([a, b, c, d, e, f]) + } else { + Affine::new([a, b, c, d, e, f]) + } }; match &*elt.borrow() { usvg::NodeKind::Group(_) => {} usvg::NodeKind::Path(path) => { let mut local_path = BezPath::new(); - // The semantics of SVG paths don't line up with `BezPath`; we must manually track initial points + // The semantics of SVG paths don't line up with `BezPath`; we + // must manually track initial points let mut just_closed = false; let mut most_recent_initial = (0., 0.); for elt in path.data.segments() { match elt { - usvg::PathSegment::MoveTo { x, y } => { + usvg::tiny_skia_path::PathSegment::MoveTo(p) => { + if std::mem::take(&mut just_closed) { + local_path.move_to(most_recent_initial); + } + most_recent_initial = (p.x.into(), p.y.into()); + local_path.move_to(most_recent_initial) + } + usvg::tiny_skia_path::PathSegment::LineTo(p) => { if std::mem::take(&mut just_closed) { local_path.move_to(most_recent_initial); } - most_recent_initial = (x, y); - local_path.move_to(most_recent_initial); + local_path.line_to::((p.x as f64, p.y as f64).into()) } - usvg::PathSegment::LineTo { x, y } => { + usvg::tiny_skia_path::PathSegment::QuadTo(p1, p2) => { if std::mem::take(&mut just_closed) { local_path.move_to(most_recent_initial); } - local_path.line_to((x, y)); + local_path.quad_to( + Point::new(p1.x as f64, p1.y as f64), + Point::new(p2.x as f64, p2.y as f64), + ) } - usvg::PathSegment::CurveTo { - x1, - y1, - x2, - y2, - x, - y, - } => { + usvg::tiny_skia_path::PathSegment::CubicTo(p1, p2, p3) => { if std::mem::take(&mut just_closed) { local_path.move_to(most_recent_initial); } - local_path.curve_to((x1, y1), (x2, y2), (x, y)); + local_path.curve_to( + Point::new(p1.x as f64, p1.y as f64), + Point::new(p2.x as f64, p2.y as f64), + Point::new(p3.x as f64, p3.y as f64), + ) } - usvg::PathSegment::ClosePath => { + usvg::tiny_skia_path::PathSegment::Close => { just_closed = true; - local_path.close_path(); + local_path.close_path() } } } @@ -115,9 +144,11 @@ pub fn render_tree_with Result<(), E if let Some((brush, brush_transform)) = paint_to_brush(&fill.paint, fill.opacity) { - // FIXME: Set the fill rule sb.fill( - Fill::NonZero, + match fill.rule { + usvg::FillRule::NonZero => Fill::NonZero, + usvg::FillRule::EvenOdd => Fill::EvenOdd, + }, transform, &brush, Some(brush_transform), @@ -131,9 +162,10 @@ pub fn render_tree_with Result<(), E if let Some((brush, brush_transform)) = paint_to_brush(&stroke.paint, stroke.opacity) { - // FIXME: handle stroke options such as linecap, linejoin, etc. + // FIXME: handle stroke options such as linecap, + // linejoin, etc. sb.stroke( - &Stroke::new(stroke.width.get()), + &Stroke::new(stroke.width.get() as f64), transform, &brush, Some(brush_transform), @@ -155,15 +187,15 @@ pub fn render_tree_with Result<(), E Ok(()) } -/// Error handler function for [`render_tree_with`] which draws a transparent red box -/// instead of unsupported SVG features +/// Error handler function for [`render_tree_with`] which draws a transparent +/// red box instead of unsupported SVG features pub fn default_error_handler(sb: &mut SceneBuilder, node: &usvg::Node) -> Result<(), Infallible> { if let Some(bb) = node.calculate_bbox() { let rect = Rect { - x0: bb.left(), - y0: bb.top(), - x1: bb.right(), - y1: bb.bottom(), + x0: bb.left() as f64, + y0: bb.top() as f64, + x1: bb.right() as f64, + y1: bb.bottom() as f64, }; sb.fill( Fill::NonZero, @@ -197,20 +229,21 @@ fn paint_to_brush(paint: &usvg::Paint, opacity: usvg::Opacity) -> Option<(Brush, cstop.color.g = stop.color.green; cstop.color.b = stop.color.blue; cstop.color.a = (stop.opacity * opacity).to_u8(); - cstop.offset = stop.offset.get() as f32; + cstop.offset = stop.offset.get(); cstop }) .collect(); - let start: vello::kurbo::Point = (gr.x1, gr.y1).into(); - let end: vello::kurbo::Point = (gr.x2, gr.y2).into(); - let transform = Affine::new([ - gr.transform.a, - gr.transform.b, - gr.transform.c, - gr.transform.d, - gr.transform.e, - gr.transform.f, - ]); + let start: vello::kurbo::Point = (gr.x1 as f64, gr.y1 as f64).into(); + let end: vello::kurbo::Point = (gr.x2 as f64, gr.y2 as f64).into(); + let (a, b, c, d, e, f) = ( + gr.transform.sx as f64, + gr.transform.ky as f64, + gr.transform.kx as f64, + gr.transform.sy as f64, + gr.transform.tx as f64, + gr.transform.ty as f64, + ); + let transform = Affine::new([a, b, c, d, e, f]); let gradient = vello::peniko::Gradient::new_linear(start, end).with_stops(stops.as_slice()); Some((Brush::Gradient(gradient), transform)) @@ -225,23 +258,24 @@ fn paint_to_brush(paint: &usvg::Paint, opacity: usvg::Opacity) -> Option<(Brush, cstop.color.g = stop.color.green; cstop.color.b = stop.color.blue; cstop.color.a = (stop.opacity * opacity).to_u8(); - cstop.offset = stop.offset.get() as f32; + cstop.offset = stop.offset.get(); cstop }) .collect(); - let start_center: vello::kurbo::Point = (gr.fx, gr.fy).into(); - let end_center: vello::kurbo::Point = (gr.cx, gr.cy).into(); + let start_center: vello::kurbo::Point = Point::new(gr.cx as f64, gr.cy as f64); + let end_center: vello::kurbo::Point = Point::new(gr.fx as f64, gr.fy as f64); let start_radius = 0_f32; - let end_radius = gr.r.get() as f32; - let transform = Affine::new([ - gr.transform.a, - gr.transform.b, - gr.transform.c, - gr.transform.d, - gr.transform.e, - gr.transform.f, - ]); + let end_radius = gr.r.get(); + let (a, b, c, d, e, f) = ( + gr.transform.sx as f64, + gr.transform.ky as f64, + gr.transform.kx as f64, + gr.transform.sy as f64, + gr.transform.tx as f64, + gr.transform.ty as f64, + ); + let transform = Affine::new([a, b, c, d, e, f]); let gradient = vello::peniko::Gradient::new_two_point_radial( start_center, start_radius, From c989acab674f02f46ce7ec873d8299c564874fd2 Mon Sep 17 00:00:00 2001 From: "Spencer C. Imbleau" Date: Thu, 18 Jan 2024 23:13:16 -0500 Subject: [PATCH 3/7] docs: update integrations --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1f0d91dfc..ff0fc620e 100644 --- a/README.md +++ b/README.md @@ -30,8 +30,7 @@ cargo run -p with_winit ### SVG -This repository also includes [`vello_svg`](./integrations/vello_svg/), which supports converting -a [`usvg`](https://crates.io/crates/usvg) `Tree` into a Vello scene. +This repository also includes [`vello_svg`](./integrations/vello_svg/), which supports converting a [`usvg`](https://crates.io/crates/usvg) `Tree` into a Vello scene. This is currently incomplete; see its crate level documentation for more information. @@ -41,6 +40,11 @@ This is used in the [winit](#winit) example for the SVG rendering. A separate integration for playing Lottie animations is available through the [`velato`](https://github.com/linebender/velato) crate. +### Community + +- [bevy-vello](https://github.com/vectorgameexperts/bevy-vello): A bevy integration to render lottie or svg files +- [vellottie](https://github.com/vectorgameexperts/vellottie): A non-serde lottie stepthrough parser and runtime for lottie files, with a mirror'ed runtime of velato + ## Examples Our examples are provided in separate packages in the [`examples`](examples) folder. From cd3cb6486c7e3f186f19d6bd3e9c396bccfa5731 Mon Sep 17 00:00:00 2001 From: "Spencer C. Imbleau" Date: Thu, 8 Feb 2024 12:24:02 -0500 Subject: [PATCH 4/7] docs: remove shoutouts --- README.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/README.md b/README.md index ff0fc620e..fe7fdae3e 100644 --- a/README.md +++ b/README.md @@ -40,11 +40,6 @@ This is used in the [winit](#winit) example for the SVG rendering. A separate integration for playing Lottie animations is available through the [`velato`](https://github.com/linebender/velato) crate. -### Community - -- [bevy-vello](https://github.com/vectorgameexperts/bevy-vello): A bevy integration to render lottie or svg files -- [vellottie](https://github.com/vectorgameexperts/vellottie): A non-serde lottie stepthrough parser and runtime for lottie files, with a mirror'ed runtime of velato - ## Examples Our examples are provided in separate packages in the [`examples`](examples) folder. From 36fd2f423de0e55edf28f4f610f8896f790c46dc Mon Sep 17 00:00:00 2001 From: "Spencer C. Imbleau" Date: Thu, 8 Feb 2024 12:26:10 -0500 Subject: [PATCH 5/7] docs: fullstop --- integrations/vello_svg/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integrations/vello_svg/src/lib.rs b/integrations/vello_svg/src/lib.rs index 563f275a6..0adf6e23d 100644 --- a/integrations/vello_svg/src/lib.rs +++ b/integrations/vello_svg/src/lib.rs @@ -47,7 +47,7 @@ pub use vello; pub use usvg; /// Append a [`usvg::Tree`] into a Vello [`SceneBuilder`], with default error -/// handling This will draw a red box over (some) unsupported elements +/// handling. This will draw a red box over (some) unsupported elements /// /// Calls [`render_tree_with`] with an error handler implementing the above. /// From 23aa7c7658ff56a3f4bcda70674cabf203174dcd Mon Sep 17 00:00:00 2001 From: "Spencer C. Imbleau" Date: Thu, 8 Feb 2024 12:38:15 -0500 Subject: [PATCH 6/7] fix: cleanup remaining comments --- integrations/vello_svg/src/lib.rs | 62 ++++++++++++++----------------- 1 file changed, 28 insertions(+), 34 deletions(-) diff --git a/integrations/vello_svg/src/lib.rs b/integrations/vello_svg/src/lib.rs index 0adf6e23d..ed05ebecf 100644 --- a/integrations/vello_svg/src/lib.rs +++ b/integrations/vello_svg/src/lib.rs @@ -53,8 +53,8 @@ pub use usvg; /// /// See the [module level documentation](crate#unsupported-features) for a list /// of some unsupported svg features -pub fn render_tree(sb: &mut SceneBuilder, svg: &usvg::Tree, transform: Option) { - render_tree_with(sb, svg, transform, default_error_handler).unwrap_or_else(|e| match e {}) +pub fn render_tree(sb: &mut SceneBuilder, svg: &usvg::Tree) { + render_tree_with(sb, svg, default_error_handler).unwrap_or_else(|e| match e {}) } /// Append a [`usvg::Tree`] into a Vello [`SceneBuilder`]. @@ -67,7 +67,6 @@ pub fn render_tree(sb: &mut SceneBuilder, svg: &usvg::Tree, transform: Option Result<(), E>, E>( sb: &mut SceneBuilder, svg: &usvg::Tree, - transform2: Option, mut on_err: F, ) -> Result<(), E> { for elt in svg.root.descendants() { @@ -80,14 +79,7 @@ pub fn render_tree_with Result<(), E tx, ty, } = elt.abs_transform(); - let (a, b, c, d, e, f) = ( - sx as f64, ky as f64, kx as f64, sy as f64, tx as f64, ty as f64, - ); - if let Some(t2) = transform2 { - t2 * Affine::new([a, b, c, d, e, f]) - } else { - Affine::new([a, b, c, d, e, f]) - } + Affine::new([sx, kx, ky, sy, tx, ty].map(f64::from)) }; match &*elt.borrow() { usvg::NodeKind::Group(_) => {} @@ -110,7 +102,7 @@ pub fn render_tree_with Result<(), E if std::mem::take(&mut just_closed) { local_path.move_to(most_recent_initial); } - local_path.line_to::((p.x as f64, p.y as f64).into()) + local_path.line_to(Point::new(p.x as f64, p.y as f64)) } usvg::tiny_skia_path::PathSegment::QuadTo(p1, p2) => { if std::mem::take(&mut just_closed) { @@ -233,17 +225,18 @@ fn paint_to_brush(paint: &usvg::Paint, opacity: usvg::Opacity) -> Option<(Brush, cstop }) .collect(); - let start: vello::kurbo::Point = (gr.x1 as f64, gr.y1 as f64).into(); - let end: vello::kurbo::Point = (gr.x2 as f64, gr.y2 as f64).into(); - let (a, b, c, d, e, f) = ( - gr.transform.sx as f64, - gr.transform.ky as f64, - gr.transform.kx as f64, - gr.transform.sy as f64, - gr.transform.tx as f64, - gr.transform.ty as f64, - ); - let transform = Affine::new([a, b, c, d, e, f]); + let start = Point::new(gr.x1 as f64, gr.y1 as f64); + let end = Point::new(gr.x2 as f64, gr.y2 as f64); + let arr = [ + gr.transform.sx, + gr.transform.ky, + gr.transform.kx, + gr.transform.sy, + gr.transform.tx, + gr.transform.ty, + ] + .map(f64::from); + let transform = Affine::new(arr); let gradient = vello::peniko::Gradient::new_linear(start, end).with_stops(stops.as_slice()); Some((Brush::Gradient(gradient), transform)) @@ -263,19 +256,20 @@ fn paint_to_brush(paint: &usvg::Paint, opacity: usvg::Opacity) -> Option<(Brush, }) .collect(); - let start_center: vello::kurbo::Point = Point::new(gr.cx as f64, gr.cy as f64); - let end_center: vello::kurbo::Point = Point::new(gr.fx as f64, gr.fy as f64); + let start_center = Point::new(gr.cx as f64, gr.cy as f64); + let end_center = Point::new(gr.fx as f64, gr.fy as f64); let start_radius = 0_f32; let end_radius = gr.r.get(); - let (a, b, c, d, e, f) = ( - gr.transform.sx as f64, - gr.transform.ky as f64, - gr.transform.kx as f64, - gr.transform.sy as f64, - gr.transform.tx as f64, - gr.transform.ty as f64, - ); - let transform = Affine::new([a, b, c, d, e, f]); + let arr = [ + gr.transform.sx, + gr.transform.ky, + gr.transform.kx, + gr.transform.sy, + gr.transform.tx, + gr.transform.ty, + ] + .map(f64::from); + let transform = Affine::new(arr); let gradient = vello::peniko::Gradient::new_two_point_radial( start_center, start_radius, From dc9e188556773c2db1cfae4739a925b5ae68db97 Mon Sep 17 00:00:00 2001 From: "Spencer C. Imbleau" Date: Thu, 8 Feb 2024 12:39:52 -0500 Subject: [PATCH 7/7] fix: example issue --- examples/scenes/src/svg.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/scenes/src/svg.rs b/examples/scenes/src/svg.rs index 21709a33d..02e363188 100644 --- a/examples/scenes/src/svg.rs +++ b/examples/scenes/src/svg.rs @@ -97,7 +97,7 @@ pub fn svg_function_of>( let start = Instant::now(); let mut new_scene = SceneFragment::new(); let mut builder = SceneBuilder::for_fragment(&mut new_scene); - vello_svg::render_tree(&mut builder, &svg, None); + vello_svg::render_tree(&mut builder, &svg); let resolution = Vec2::new(svg.size.width() as f64, svg.size.height() as f64); eprintln!("Encoded svg {name} in {:?}", start.elapsed()); (new_scene, resolution)