From 653fba3dcf3488008959e360db4f960449756cb5 Mon Sep 17 00:00:00 2001 From: Nico Burns Date: Tue, 13 Jan 2026 20:42:31 +0000 Subject: [PATCH 1/2] Import kurbo/peniko directly rather than via vello Signed-off-by: Nico Burns --- Cargo.lock | 6 ++++++ Cargo.toml | 4 ++++ examples/scenes/Cargo.toml | 2 ++ examples/scenes/src/lib.rs | 4 ++-- examples/scenes/src/lottie.rs | 2 +- examples/scenes/src/simple_text.rs | 6 +++--- examples/scenes/src/test_scenes.rs | 2 +- examples/with_winit/Cargo.toml | 2 ++ examples/with_winit/src/lib.rs | 4 ++-- examples/with_winit/src/multi_touch.rs | 2 +- examples/with_winit/src/stats.rs | 4 ++-- src/import/builders.rs | 2 +- src/import/converters.rs | 4 ++-- src/runtime/model/fixed.rs | 3 +-- src/runtime/model/mod.rs | 4 ++-- src/runtime/model/spline.rs | 2 +- src/runtime/model/value.rs | 9 +++------ src/runtime/render.rs | 8 ++++---- 18 files changed, 40 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2b54aa5..031a42b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2285,6 +2285,8 @@ dependencies = [ "image", "inquire", "instant", + "kurbo", + "peniko", "rand", "skrifa 0.26.6", "ureq", @@ -2781,6 +2783,8 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" name = "velato" version = "0.8.1" dependencies = [ + "kurbo", + "peniko", "serde", "serde_json", "serde_repr", @@ -3777,8 +3781,10 @@ dependencies = [ "env_logger 0.11.8", "getrandom 0.2.16", "instant", + "kurbo", "log", "notify-debouncer-mini", + "peniko", "pollster", "scenes", "vello", diff --git a/Cargo.toml b/Cargo.toml index 1e8bdfa..b0dd89d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -87,12 +87,16 @@ clippy.wildcard_dependencies = "warn" [workspace.dependencies] # NOTE: Make sure to keep this in sync with the version badge in README.md vello = { version = "0.6.0", default-features = false } +kurbo = { version = "0.12.0" } +peniko = { version = "0.5.0" } [lints] workspace = true [dependencies] vello = { workspace = true } +kurbo = { workspace = true } +peniko = { workspace = true } # For the parser serde = { version = "1.0.228", features = ["derive"] } diff --git a/examples/scenes/Cargo.toml b/examples/scenes/Cargo.toml index 01e0e04..d27d08b 100644 --- a/examples/scenes/Cargo.toml +++ b/examples/scenes/Cargo.toml @@ -11,6 +11,8 @@ workspace = true [dependencies] vello = { workspace = true } +kurbo = { workspace = true } +peniko = { workspace = true } velato = { path = "../.." } anyhow = "1" clap = { version = "4.5.38", features = ["derive"] } diff --git a/examples/scenes/src/lib.rs b/examples/scenes/src/lib.rs index 7256d47..e7c25d2 100644 --- a/examples/scenes/src/lib.rs +++ b/examples/scenes/src/lib.rs @@ -37,9 +37,9 @@ pub use lottie::{default_scene, scene_from_files}; pub use simple_text::RobotoText; pub use test_scenes::test_scenes; +use kurbo::Vec2; +use peniko::{Color, color}; use vello::Scene; -use vello::kurbo::Vec2; -use vello::peniko::{Color, color}; pub struct SceneParams<'a> { pub time: f64, diff --git a/examples/scenes/src/lottie.rs b/examples/scenes/src/lottie.rs index 94a16f3..a14362d 100644 --- a/examples/scenes/src/lottie.rs +++ b/examples/scenes/src/lottie.rs @@ -7,6 +7,7 @@ use crate::{ExampleScene, SceneSet}; #[cfg(not(target_arch = "wasm32"))] use anyhow::{Ok, Result}; use instant::Instant; +use kurbo::{Affine, Vec2}; use std::sync::Arc; #[cfg(not(target_arch = "wasm32"))] use std::{ @@ -15,7 +16,6 @@ use std::{ }; use velato::Composition; use vello::Scene; -use vello::kurbo::{Affine, Vec2}; #[cfg(not(target_arch = "wasm32"))] pub fn scene_from_files(files: &[PathBuf]) -> Result { diff --git a/examples/scenes/src/simple_text.rs b/examples/scenes/src/simple_text.rs index e6c2b14..7622d07 100644 --- a/examples/scenes/src/simple_text.rs +++ b/examples/scenes/src/simple_text.rs @@ -1,11 +1,11 @@ // Copyright 2022 the Velato Authors // SPDX-License-Identifier: Apache-2.0 OR MIT +use kurbo::Affine; +use peniko::{Blob, Brush, BrushRef, FontData, StyleRef}; use skrifa::MetadataProvider; use skrifa::raw::FontRef; use std::sync::Arc; -use vello::kurbo::Affine; -use vello::peniko::{Blob, Brush, BrushRef, FontData, StyleRef}; use vello::{Glyph, Scene}; // This is very much a hack to get things working. @@ -113,7 +113,7 @@ impl RobotoText { transform: Affine, text: &str, ) { - use vello::peniko::{Color, Fill}; + use peniko::{Color, Fill}; let brush = brush.unwrap_or(&Brush::Solid(Color::WHITE)); self.add_run( scene, diff --git a/examples/scenes/src/test_scenes.rs b/examples/scenes/src/test_scenes.rs index 7f78b43..12aafd1 100644 --- a/examples/scenes/src/test_scenes.rs +++ b/examples/scenes/src/test_scenes.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT use crate::{ExampleScene, SceneConfig, SceneParams, SceneSet}; -use vello::kurbo::Affine; +use kurbo::Affine; use vello::*; macro_rules! scene { diff --git a/examples/with_winit/Cargo.toml b/examples/with_winit/Cargo.toml index 214baeb..7f186d6 100644 --- a/examples/with_winit/Cargo.toml +++ b/examples/with_winit/Cargo.toml @@ -21,6 +21,8 @@ workspace = true [dependencies] vello = { workspace = true, features = ["wgpu"] } +kurbo = { workspace = true } +peniko = { workspace = true } wgpu = { version = "26.0", features = ["vulkan", "metal", "dx12"] } scenes = { path = "../scenes" } anyhow = "1" diff --git a/examples/with_winit/src/lib.rs b/examples/with_winit/src/lib.rs index 23c5f3c..af0f346 100644 --- a/examples/with_winit/src/lib.rs +++ b/examples/with_winit/src/lib.rs @@ -24,10 +24,10 @@ use std::sync::Arc; use anyhow::Result; use clap::{CommandFactory, Parser}; +use kurbo::{Affine, Vec2}; +use peniko::Color; use scenes::{RobotoText, SceneParams, SceneSet}; -use vello::kurbo::{Affine, Vec2}; use vello::low_level::BumpAllocators; -use vello::peniko::Color; use vello::util::{RenderContext, RenderSurface}; use vello::{AaConfig, Renderer, RendererOptions, Scene, wgpu}; diff --git a/examples/with_winit/src/multi_touch.rs b/examples/with_winit/src/multi_touch.rs index db35439..1922640 100644 --- a/examples/with_winit/src/multi_touch.rs +++ b/examples/with_winit/src/multi_touch.rs @@ -4,7 +4,7 @@ /// Adapted from use std::{collections::BTreeMap, fmt::Debug}; -use vello::kurbo::{Point, Vec2}; +use kurbo::{Point, Vec2}; use winit::event::{Touch, TouchPhase}; /// All you probably need to know about a multi-touch gesture. diff --git a/examples/with_winit/src/stats.rs b/examples/with_winit/src/stats.rs index 8b2a600..3fe4cd1 100644 --- a/examples/with_winit/src/stats.rs +++ b/examples/with_winit/src/stats.rs @@ -1,11 +1,11 @@ // Copyright 2023 the Velato Authors // SPDX-License-Identifier: Apache-2.0 OR MIT +use kurbo::{Affine, PathEl, Rect, Stroke}; +use peniko::{Brush, Color, Fill}; use scenes::RobotoText; use std::collections::VecDeque; -use vello::kurbo::{Affine, PathEl, Rect, Stroke}; use vello::low_level::BumpAllocators; -use vello::peniko::{Brush, Color, Fill}; use vello::{AaConfig, Scene}; const SLIDING_WINDOW_SIZE: usize = 100; diff --git a/src/import/builders.rs b/src/import/builders.rs index e001f52..e2d7624 100644 --- a/src/import/builders.rs +++ b/src/import/builders.rs @@ -6,7 +6,7 @@ use super::defaults::FLOAT_VALUE_ONE_HUNDRED; use crate::runtime::model::Layer; use crate::schema::helpers::int_boolean::BoolInt; use crate::{runtime, schema}; -use vello::peniko::{self, BlendMode, Compose, Mix}; +use peniko::{self, BlendMode, Compose, Mix}; pub struct LayerSetupParams { pub layer_index: usize, diff --git a/src/import/converters.rs b/src/import/converters.rs index f3f1223..ed85cbc 100644 --- a/src/import/converters.rs +++ b/src/import/converters.rs @@ -18,9 +18,9 @@ use crate::schema::animated_properties::split_vector::SplitVector; use crate::schema::constants::gradient_type::GradientType; use crate::schema::helpers::int_boolean::BoolInt; use crate::{Composition, schema}; +use kurbo::{Cap, Join, Point, Size, Vec2}; +use peniko::{BlendMode, Color, Mix}; use std::collections::HashMap; -use vello::kurbo::{Cap, Join, Point, Size, Vec2}; -use vello::peniko::{BlendMode, Color, Mix}; fn process_layers( source_layers: &[schema::layers::AnyLayer], diff --git a/src/runtime/model/fixed.rs b/src/runtime/model/fixed.rs index c2546d1..75e1c0b 100644 --- a/src/runtime/model/fixed.rs +++ b/src/runtime/model/fixed.rs @@ -7,8 +7,7 @@ Representations of fixed (non-animated) values. use std::mem::swap; -use vello::kurbo::{self, Affine, Point, Vec2}; -use vello::peniko; +use kurbo::{Affine, Point, Vec2}; /// Fixed affine transformation. pub type Transform = kurbo::Affine; diff --git a/src/runtime/model/mod.rs b/src/runtime/model/mod.rs index e0522e5..c6fb4e4 100644 --- a/src/runtime/model/mod.rs +++ b/src/runtime/model/mod.rs @@ -1,9 +1,9 @@ // Copyright 2024 the Velato Authors // SPDX-License-Identifier: Apache-2.0 OR MIT +use kurbo::{Affine, PathEl, Point, Shape as _, Size, Vec2}; +use peniko::{BlendMode, Color}; use std::ops::Range; -use vello::kurbo::{self, Affine, PathEl, Point, Shape as _, Size, Vec2}; -use vello::peniko::{self, BlendMode, Color}; mod spline; mod value; diff --git a/src/runtime/model/spline.rs b/src/runtime/model/spline.rs index d4c5bfb..aa7d834 100644 --- a/src/runtime/model/spline.rs +++ b/src/runtime/model/spline.rs @@ -1,7 +1,7 @@ // Copyright 2024 the Velato Authors // SPDX-License-Identifier: Apache-2.0 OR MIT -use vello::kurbo::{PathEl, Point}; +use kurbo::{PathEl, Point}; /// Helper trait for converting cubic splines to paths. pub trait SplineToPath { diff --git a/src/runtime/model/value.rs b/src/runtime/model/value.rs index 97a2cb7..10ca3cb 100644 --- a/src/runtime/model/value.rs +++ b/src/runtime/model/value.rs @@ -1,12 +1,9 @@ // Copyright 2024 the Velato Authors // SPDX-License-Identifier: Apache-2.0 OR MIT -use vello::{ - kurbo::{ - self, ParamCurve, Point, - common::{solve_cubic, solve_itp}, - }, - peniko, +use kurbo::{ + ParamCurve, Point, + common::{solve_cubic, solve_itp}, }; /// Fixed or animated value. diff --git a/src/runtime/render.rs b/src/runtime/render.rs index 9a76d0d..b1588b1 100644 --- a/src/runtime/render.rs +++ b/src/runtime/render.rs @@ -3,13 +3,13 @@ use super::Composition; use super::model::{Content, Draw, Geometry, GroupTransform, Layer, Shape, fixed}; -use std::mem::swap; -use std::ops::Range; -use vello::kurbo::{ +use kurbo::{ Affine, BezPath, CubicBez, Line, ParamCurve, ParamCurveArclen, PathEl, PathSeg, Point, QuadBez, Rect, }; -use vello::peniko::{Fill, Mix}; +use peniko::{Fill, Mix}; +use std::mem::swap; +use std::ops::Range; /// Renders a composition into a scene. #[derive(Debug, Default)] From 2f9781e9a06c142390fb8778a657ee04c9c98348 Mon Sep 17 00:00:00 2001 From: Nico Burns Date: Wed, 14 Jan 2026 15:46:48 +0000 Subject: [PATCH 2/2] Reintroduce RenderSink trait and feature flag vello Signed-off-by: Nico Burns --- Cargo.toml | 10 +++++-- examples/scenes/src/lottie.rs | 2 +- src/lib.rs | 5 ++-- src/runtime/mod.rs | 5 +++- src/runtime/render.rs | 47 ++++++++++++++++------------- src/runtime/vello.rs | 56 +++++++++++++++++++++++++++++++++++ 6 files changed, 97 insertions(+), 28 deletions(-) create mode 100644 src/runtime/vello.rs diff --git a/Cargo.toml b/Cargo.toml index b0dd89d..e712a6b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -94,7 +94,7 @@ peniko = { version = "0.5.0" } workspace = true [dependencies] -vello = { workspace = true } +vello = { workspace = true, optional = true } kurbo = { workspace = true } peniko = { workspace = true } @@ -103,9 +103,13 @@ serde = { version = "1.0.228", features = ["derive"] } serde_json = "1.0.145" serde_repr = "0.1.20" +[dev-dependencies] +vello = { workspace = true } + [target.'cfg(target_arch = "wasm32")'.dev-dependencies] wasm-bindgen-test = "0.3.56" [features] -default = [] -wgpu = ["vello/wgpu"] +default = ["vello"] +vello = ["dep:vello"] +wgpu = ["vello", "vello/wgpu"] diff --git a/examples/scenes/src/lottie.rs b/examples/scenes/src/lottie.rs index a14362d..2155511 100644 --- a/examples/scenes/src/lottie.rs +++ b/examples/scenes/src/lottie.rs @@ -106,7 +106,7 @@ pub fn lottie_function_of>( let frame = ((start.elapsed().as_secs_f64() * lottie.frame_rate) % (lottie.frames.end - lottie.frames.start)) + lottie.frames.start; - renderer.render(lottie, frame, Affine::IDENTITY, 1.0) + renderer.render_to_vello_scene(lottie, frame, Affine::IDENTITY, 1.0) } let started = Instant::now(); let mut renderer = velato::Renderer::new(); diff --git a/src/lib.rs b/src/lib.rs index 9059807..e1328b9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,7 +28,7 @@ //! let frame = 0.0; // Arbitrary number chosen. Ensure it's a valid frame! //! let transform = vello::kurbo::Affine::IDENTITY; //! let alpha = 1.0; -//! let scene = renderer.render(&composition, frame, transform, alpha); +//! let scene = renderer.render_to_vello_scene(&composition, frame, transform, alpha); //! ``` //! //! # Unsupported features @@ -91,6 +91,7 @@ mod error; pub use error::Error; // Re-export vello +#[cfg(feature = "vello")] pub use vello; -pub use runtime::{Composition, Renderer, model}; +pub use runtime::{Composition, RenderSink, Renderer, model}; diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index e75fce4..196ea7a 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -9,9 +9,12 @@ use crate::schema::Animation; use std::collections::HashMap; use std::ops::Range; +#[cfg(feature = "vello")] +mod vello; + pub mod model; -pub use render::Renderer; +pub use render::{RenderSink, Renderer}; /// Model of a Lottie file. #[derive(Clone, Default, Debug)] diff --git a/src/runtime/render.rs b/src/runtime/render.rs index b1588b1..fed0e8e 100644 --- a/src/runtime/render.rs +++ b/src/runtime/render.rs @@ -11,6 +11,28 @@ use peniko::{Fill, Mix}; use std::mem::swap; use std::ops::Range; +pub trait RenderSink { + fn push_layer( + &mut self, + blend: impl Into, + alpha: f32, + transform: Affine, + shape: &impl kurbo::Shape, + ); + + fn push_clip_layer(&mut self, transform: Affine, shape: &impl kurbo::Shape); + + fn pop_layer(&mut self); + + fn draw( + &mut self, + stroke: Option<&fixed::Stroke>, + transform: Affine, + brush: &fixed::Brush, + shape: &impl kurbo::Shape, + ); +} + /// Renders a composition into a scene. #[derive(Debug, Default)] pub struct Renderer { @@ -24,19 +46,6 @@ impl Renderer { Self::default() } - /// Renders the animation at a given frame to a new scene. - pub fn render( - &mut self, - animation: &Composition, - frame: f64, - transform: Affine, - alpha: f64, - ) -> vello::Scene { - let mut scene = vello::Scene::new(); - self.append(animation, frame, transform, alpha, &mut scene); - scene - } - /// Renders and appends the animation at a given frame to the provided scene. pub fn append( &mut self, @@ -44,7 +53,7 @@ impl Renderer { frame: f64, transform: Affine, alpha: f64, - scene: &mut vello::Scene, + scene: &mut impl RenderSink, ) { self.batch.clear(); scene.push_clip_layer( @@ -77,7 +86,7 @@ impl Renderer { transform: Affine, alpha: f64, frame: f64, - scene: &mut vello::Scene, + scene: &mut impl RenderSink, ) { if !layer.frames.contains(&frame) { return; @@ -447,7 +456,7 @@ impl Batch { self.trim_elements.clear(); } - fn render(&self, scene: &mut vello::Scene) { + fn render(&self, scene: &mut impl RenderSink) { // Process all draws in reverse for draw in self.draws.iter().rev() { // Some nastiness to avoid cloning the brush if unnecessary @@ -460,11 +469,7 @@ impl Batch { for geometry in self.geometries[draw.geometry.clone()].iter() { let path = &self.elements[geometry.elements.clone()]; let transform = geometry.transform; - if let Some(stroke) = draw.stroke.as_ref() { - scene.stroke(stroke, transform, brush, None, &path); - } else { - scene.fill(Fill::NonZero, transform, brush, None, &path); - } + scene.draw(draw.stroke.as_ref(), transform, brush, &path); } } } diff --git a/src/runtime/vello.rs b/src/runtime/vello.rs new file mode 100644 index 0000000..addd4b6 --- /dev/null +++ b/src/runtime/vello.rs @@ -0,0 +1,56 @@ +// Copyright 2024 the Velato Authors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +use super::{Composition, RenderSink, Renderer, model::fixed}; + +use kurbo::{Affine, Shape}; +use peniko::{BlendMode, Fill}; + +impl RenderSink for vello::Scene { + fn push_layer( + &mut self, + blend: impl Into, + alpha: f32, + transform: Affine, + shape: &impl Shape, + ) { + self.push_layer(blend, alpha, transform, shape); + } + + fn push_clip_layer(&mut self, transform: Affine, shape: &impl Shape) { + self.push_clip_layer(transform, shape); + } + + fn pop_layer(&mut self) { + self.pop_layer(); + } + + fn draw( + &mut self, + stroke: Option<&fixed::Stroke>, + transform: Affine, + brush: &fixed::Brush, + shape: &impl Shape, + ) { + if let Some(stroke) = stroke { + self.stroke(stroke, transform, brush, None, shape); + } else { + self.fill(Fill::NonZero, transform, brush, None, shape); + } + } +} + +impl Renderer { + /// Renders the animation at a given frame to a new scene. + pub fn render_to_vello_scene( + &mut self, + animation: &Composition, + frame: f64, + transform: Affine, + alpha: f64, + ) -> vello::Scene { + let mut scene = vello::Scene::new(); + self.append(animation, frame, transform, alpha, &mut scene); + scene + } +}