blob: 43629b9817eea601d1c11ddff6e248f6e294637a [file]
// Copyright 2025 the Vello Authors
// SPDX-License-Identifier: Apache-2.0 OR MIT
//! SVG rendering example scene.
use std::fmt;
use vello_common::kurbo::{Affine, Stroke};
use vello_common::pico_svg::{Item, PicoSvg};
#[cfg(not(target_arch = "wasm32"))]
use std::path::{Path, PathBuf};
use crate::{ExampleScene, RenderingContext};
/// SVG scene that renders an SVG file
pub struct SvgScene {
transform: Affine,
svg: PicoSvg,
}
impl fmt::Debug for SvgScene {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "SvgScene")
}
}
impl ExampleScene for SvgScene {
fn render<T: RenderingContext>(
&mut self,
ctx: &mut T,
_resources: &mut T::Resources,
root_transform: Affine,
) {
render_svg(ctx, &self.svg.items, root_transform * self.transform);
}
}
impl SvgScene {
/// Create a new `SvgScene` with the Ghost Tiger SVG
pub fn tiger() -> Self {
// Load the ghost tiger SVG by default
#[cfg(target_arch = "wasm32")]
let svg_content = include_str!("../../../examples/assets/Ghostscript_Tiger.svg");
#[cfg(not(target_arch = "wasm32"))]
let svg_content = {
let cargo_dir = Path::new(env!("CARGO_MANIFEST_DIR"))
.canonicalize()
.unwrap();
&std::fs::read_to_string(cargo_dir.join("../../examples/assets/Ghostscript_Tiger.svg"))
.unwrap()
};
let svg = PicoSvg::load(svg_content, 1.0).expect("Failed to parse Ghost Tiger SVG");
Self {
transform: Affine::scale(3.0),
svg,
}
}
/// Create a new `SvgScene` with the content from a given file
#[cfg(not(target_arch = "wasm32"))]
pub fn with_svg_file(path: PathBuf) -> Result<Self, Box<dyn std::error::Error>> {
let svg_content = std::fs::read_to_string(path)?;
let svg = PicoSvg::load(&svg_content, 1.0)?;
Ok(Self {
transform: Affine::scale(3.0),
svg,
})
}
}
fn render_svg(ctx: &mut impl RenderingContext, items: &[Item], transform: Affine) {
ctx.set_transform(transform);
for item in items {
match item {
Item::Fill(fill_item) => {
ctx.set_paint(fill_item.color);
ctx.fill_path(&fill_item.path);
}
Item::Stroke(stroke_item) => {
let style = Stroke::new(stroke_item.width);
ctx.set_stroke(style);
ctx.set_paint(stroke_item.color);
ctx.stroke_path(&stroke_item.path);
}
Item::Group(group_item) => {
render_svg(ctx, &group_item.children, transform * group_item.affine);
ctx.set_transform(transform);
}
}
}
}