Skip to content

Commit

Permalink
Merge pull request #422 from simbleau/update-integrations
Browse files Browse the repository at this point in the history
  • Loading branch information
simbleau committed Feb 13, 2024
2 parents 6f0621e + dc9e188 commit b8f09b6
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 65 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/target
Cargo.lock
/trace.json
.DS_Store

examples/assets/downloads/*
!examples/assets/downloads/.tracked
Expand Down
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
2 changes: 1 addition & 1 deletion examples/scenes/src/svg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ pub fn svg_function_of<R: AsRef<str>>(
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());
let resolution = Vec2::new(svg.size.width() as f64, svg.size.height() as f64);
eprintln!("Encoded svg {name} in {:?}", start.elapsed());
(new_scene, resolution)
}
Expand Down
2 changes: 1 addition & 1 deletion integrations/vello_svg/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ repository.workspace = true

[dependencies]
vello = { path = "../../" }
usvg = "0.33"
usvg = "0.37"
150 changes: 89 additions & 61 deletions integrations/vello_svg/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,78 +33,99 @@
//! - 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
/// 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 {});
render_tree_with(sb, svg, default_error_handler).unwrap_or_else(|e| match e {})
}

/// Append a [`usvg::Tree`] into a Vello [`SceneBuilder`].
///
/// 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<F: FnMut(&mut SceneBuilder, &usvg::Node) -> Result<(), E>, E>(
sb: &mut SceneBuilder,
svg: &usvg::Tree,
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();
Affine::new([sx, kx, ky, sy, tx, ty].map(f64::from))
};
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(Point::new(p.x as f64, p.y as f64))
}
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()
}
}
}
Expand All @@ -115,9 +136,11 @@ pub fn render_tree_with<F: FnMut(&mut SceneBuilder, &usvg::Node) -> 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),
Expand All @@ -131,9 +154,10 @@ pub fn render_tree_with<F: FnMut(&mut SceneBuilder, &usvg::Node) -> 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),
Expand All @@ -155,15 +179,15 @@ pub fn render_tree_with<F: FnMut(&mut SceneBuilder, &usvg::Node) -> 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,
Expand Down Expand Up @@ -197,20 +221,22 @@ 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 = 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))
Expand All @@ -225,23 +251,25 @@ 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 = 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() 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 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,
Expand Down

0 comments on commit b8f09b6

Please sign in to comment.