Added a Rust runtime.

This patch adds a Rust runtime, rive-rs, that binds to rive-cpp and includes an optional Vello back-end. It also ports the Vello demo to this new runtime.

Stuff to do before merging:

- [ ] README.md & CONTRIBUTING.md
- [x] set up repo pushing
- [x] make more types public
- [x] events API

Diffs=
52a1a6f88 Added a Rust runtime. (#6027)

Co-authored-by: DragoČ™ Tiselice <dragos@rive.app>
diff --git a/.rive_head b/.rive_head
index 8da8b8d..413d351 100644
--- a/.rive_head
+++ b/.rive_head
@@ -1 +1 @@
-22077bedae4c277e5e8a81c732167ab5d559917c
+52a1a6f8888c9134de8dce0393dd3b0d6757f7de
diff --git a/vello/Cargo.toml b/vello/Cargo.toml
index 4a4d533..255a639 100644
--- a/vello/Cargo.toml
+++ b/vello/Cargo.toml
@@ -3,15 +3,9 @@
 version = "0.1.0"
 edition = "2021"
 
-[build-dependencies]
-cc = { version = "1.0", features = ["parallel"] }
-walkdir = "2.3.3"
-
 [dependencies]
-clap = { version = "4.3.19", features = ["derive"] }
-image = "0.24.6"
 pollster = "0.3.0"
-smallvec = "1.8.0"
-vello = { git = "https://github.com/linebender/vello", rev = "3cb5462" }
+rive-rs = { path = "../../rive_rs", features = ["vello"] }
+vello = { workspace = true }
 wgpu = "0.17.0"
 winit = "0.28.6"
diff --git a/vello/build.rs b/vello/build.rs
deleted file mode 100644
index 0f7163d..0000000
--- a/vello/build.rs
+++ /dev/null
@@ -1,116 +0,0 @@
-use std::{
-    env,
-    ffi::OsString,
-    path::{Path, PathBuf},
-    process::Command,
-};
-
-use walkdir::WalkDir;
-
-const CHECKOUT_DIRECTORY: &str = "target";
-
-struct Checkout {
-    path: PathBuf,
-}
-
-impl Checkout {
-    pub fn new(repo: &str, tag: &str) -> Self {
-        let name = repo.rsplit_once('/').expect("URL format invalid").1;
-        let mut path = PathBuf::from(CHECKOUT_DIRECTORY);
-
-        path.push(name);
-
-        if !path.is_dir() {
-            Command::new("git")
-                .args([
-                    "clone",
-                    "-b",
-                    tag,
-                    repo,
-                    &path.as_os_str().to_string_lossy(),
-                ])
-                .output()
-                .unwrap_or_else(|_| panic!("failed to clone {}; is git CLI available?", name));
-        }
-
-        Self { path }
-    }
-
-    pub fn join<P: AsRef<Path>>(&self, path: P) -> PathBuf {
-        self.path.join(path)
-    }
-}
-
-fn all_files_with_extension<P: AsRef<Path>>(
-    path: P,
-    extension: &str,
-) -> impl Iterator<Item = PathBuf> + '_ {
-    WalkDir::new(path).into_iter().filter_map(move |entry| {
-        entry
-            .ok()
-            .map(|entry| entry.into_path())
-            .filter(|path| path.extension() == Some(&OsString::from(extension)))
-    })
-}
-
-fn main() {
-    println!("cargo:rerun-if-changed=src/vello_renderer.hpp");
-    println!("cargo:rerun-if-changed=src/vello_renderer.cpp");
-    println!("cargo:rerun-if-changed=src/winit_viewer.cpp");
-
-    let harfbuzz = Checkout::new("https://github.com/harfbuzz/harfbuzz", "8.1.1");
-    let sheen_bidi = Checkout::new("https://github.com/Tehreer/SheenBidi", "v2.6");
-
-    let target = env::var("TARGET").unwrap();
-    let profile = env::var("PROFILE").unwrap();
-
-    let mut cfg = cc::Build::new();
-    cfg.cpp(true)
-        .flag_if_supported("-std=c++11") // for unix
-        .warnings(false)
-        .file(harfbuzz.join("src/harfbuzz.cc"));
-
-    if !target.contains("windows") {
-        cfg.define("HAVE_PTHREAD", "1");
-    }
-
-    if target.contains("apple") && profile.contains("release") {
-        cfg.define("HAVE_CORETEXT", "1");
-    }
-
-    if target.contains("windows") {
-        cfg.define("HAVE_DIRECTWRITE", "1");
-    }
-
-    if target.contains("windows-gnu") {
-        cfg.flag("-Wa,-mbig-obj");
-    }
-
-    cfg.compile("harfbuzz");
-
-    cc::Build::new()
-        .files(all_files_with_extension(sheen_bidi.join("Source"), "c"))
-        .include(sheen_bidi.join("Headers"))
-        .warnings(false)
-        .compile("sheenbidi");
-
-    cc::Build::new()
-        .cpp(true)
-        .files(all_files_with_extension("../src", "cpp"))
-        .files(all_files_with_extension(
-            "../viewer/src/viewer_content",
-            "cpp",
-        ))
-        .file("src/vello_renderer.cpp")
-        .file("src/winit_viewer.cpp")
-        .include("src")
-        .include("../include")
-        .include("../viewer/include")
-        .include(harfbuzz.join("src"))
-        .include(sheen_bidi.join("Headers"))
-        .flag("-std=c++14")
-        .warnings(false)
-        .define("RIVE_SKIP_IMGUI", None)
-        .define("WITH_RIVE_TEXT", None)
-        .compile("rive");
-}
diff --git a/vello/src/lib.rs b/vello/src/lib.rs
deleted file mode 100644
index f50f50a..0000000
--- a/vello/src/lib.rs
+++ /dev/null
@@ -1,564 +0,0 @@
-#![allow(clippy::missing_safety_doc)]
-
-use core::slice;
-use std::{io::Cursor, ptr::NonNull};
-
-use image::io::Reader;
-use smallvec::SmallVec;
-use util::{ScaleFromOrigin, UnwrapAndDeref};
-use vello::{
-    kurbo::{Affine, BezPath, Line, PathSeg, Point, Rect, Shape, Vec2},
-    peniko::{
-        BlendMode, Brush, BrushRef, Cap, Color, ColorStop, ColorStopsSource, Fill, Format,
-        Gradient, Image, Join, Mix, Stroke,
-    },
-    SceneBuilder, SceneFragment,
-};
-
-mod rive;
-mod util;
-mod viewer;
-
-pub use viewer::ViewerContent;
-
-fn from_bgra8(color: u32) -> Color {
-    Color::rgba8(
-        (color >> 16) as u8,
-        (color >> 8) as u8,
-        color as u8,
-        (color >> 24) as u8,
-    )
-}
-
-#[derive(Debug)]
-enum RenderStyle {
-    Fill,
-    Stroke(Stroke),
-}
-
-#[derive(Debug)]
-struct SliceStops<'s> {
-    colors: &'s [u32],
-    stops: &'s [f32],
-}
-
-impl ColorStopsSource for SliceStops<'_> {
-    fn collect_stops(&self, vec: &mut SmallVec<[ColorStop; 4]>) {
-        vec.extend(
-            self.colors
-                .iter()
-                .zip(self.stops.iter())
-                .map(|(&color, &offset)| ColorStop {
-                    offset,
-                    color: from_bgra8(color),
-                }),
-        );
-    }
-}
-
-#[no_mangle]
-pub unsafe extern "C" fn vello_gradient_new_linear(
-    sx: f32,
-    sy: f32,
-    ex: f32,
-    ey: f32,
-    colors_data: *const u32,
-    stops_data: *const f32,
-    len: usize,
-) -> Option<NonNull<Gradient>> {
-    let colors = slice::from_raw_parts(colors_data, len);
-    let stops = slice::from_raw_parts(stops_data, len);
-
-    let raw_stops = SliceStops { colors, stops };
-    let gradient =
-        Gradient::new_linear((sx as f64, sy as f64), (ex as f64, ey as f64)).with_stops(raw_stops);
-
-    NonNull::new(Box::into_raw(Box::new(gradient)))
-}
-
-#[no_mangle]
-pub unsafe extern "C" fn vello_gradient_new_radial(
-    cx: f32,
-    cy: f32,
-    radius: f32,
-    colors_data: *const u32,
-    stops_data: *const f32,
-    len: usize,
-) -> Option<NonNull<Gradient>> {
-    let colors = slice::from_raw_parts(colors_data, len);
-    let stops = slice::from_raw_parts(stops_data, len);
-
-    let raw_stops = SliceStops { colors, stops };
-    let gradient = Gradient::new_radial((cx as f64, cy as f64), radius).with_stops(raw_stops);
-
-    NonNull::new(Box::into_raw(Box::new(gradient)))
-}
-
-#[no_mangle]
-pub unsafe extern "C" fn vello_gradient_release(gradient: Option<NonNull<Gradient>>) {
-    gradient.map(|ptr| Box::from_raw(ptr.as_ptr())).unwrap();
-}
-
-#[no_mangle]
-pub unsafe extern "C" fn vello_image_new(data: *const u8, len: usize) -> Option<NonNull<Image>> {
-    let image = Reader::new(Cursor::new(slice::from_raw_parts(data, len)))
-        .with_guessed_format()
-        .ok()?
-        .decode()
-        .ok()?
-        .into_rgba8();
-    let width = image.width();
-    let height = image.height();
-
-    NonNull::new(Box::into_raw(Box::new(Image::new(
-        image.into_raw().into(),
-        Format::Rgba8,
-        width,
-        height,
-    ))))
-}
-
-#[no_mangle]
-pub unsafe extern "C" fn vello_image_release(image: Option<NonNull<Image>>) {
-    image.map(|ptr| Box::from_raw(ptr.as_ptr())).unwrap();
-}
-
-#[derive(Debug)]
-pub struct VelloPaint {
-    style: RenderStyle,
-    brush: Brush,
-    blend_mode: BlendMode,
-}
-
-impl Default for VelloPaint {
-    fn default() -> Self {
-        Self {
-            style: RenderStyle::Fill,
-            brush: Brush::Solid(Color::TRANSPARENT),
-            blend_mode: Mix::Normal.into(),
-        }
-    }
-}
-
-#[no_mangle]
-pub unsafe extern "C" fn vello_paint_new() -> Option<NonNull<VelloPaint>> {
-    NonNull::new(Box::into_raw(Box::default()))
-}
-
-#[no_mangle]
-pub unsafe extern "C" fn vello_paint_release(paint: Option<NonNull<VelloPaint>>) {
-    paint.map(|ptr| Box::from_raw(ptr.as_ptr())).unwrap();
-}
-
-#[no_mangle]
-pub unsafe extern "C" fn vello_paint_set_style(
-    mut paint: Option<NonNull<VelloPaint>>,
-    style: rive::RenderPaint,
-) {
-    paint.unwrap_and_deref().style = style.into();
-}
-
-#[no_mangle]
-pub unsafe extern "C" fn vello_paint_set_color(mut paint: Option<NonNull<VelloPaint>>, color: u32) {
-    paint.unwrap_and_deref().brush = Brush::Solid(from_bgra8(color));
-}
-
-#[no_mangle]
-pub unsafe extern "C" fn vello_paint_set_gradient(
-    mut paint: Option<NonNull<VelloPaint>>,
-    mut gradient: Option<NonNull<Gradient>>,
-) {
-    paint.unwrap_and_deref().brush = Brush::Gradient(gradient.unwrap_and_deref().clone());
-}
-
-#[no_mangle]
-pub unsafe extern "C" fn vello_paint_set_thickness(
-    mut paint: Option<NonNull<VelloPaint>>,
-    thickness: f32,
-) {
-    let style = &mut paint.unwrap_and_deref().style;
-    loop {
-        if let RenderStyle::Stroke(stroke) = style {
-            stroke.width = thickness;
-            break;
-        } else {
-            *style = RenderStyle::Stroke(Stroke::new(0.0));
-        }
-    }
-}
-
-#[no_mangle]
-pub unsafe extern "C" fn vello_paint_set_join(
-    mut paint: Option<NonNull<VelloPaint>>,
-    join: rive::StrokeJoin,
-) {
-    let style = &mut paint.unwrap_and_deref().style;
-    loop {
-        if let RenderStyle::Stroke(stroke) = style {
-            stroke.join = match join {
-                rive::StrokeJoin::Miter => Join::Miter,
-                rive::StrokeJoin::Round => Join::Round,
-                rive::StrokeJoin::Bevel => Join::Bevel,
-            };
-            break;
-        } else {
-            *style = RenderStyle::Stroke(Stroke::new(0.0));
-        }
-    }
-}
-
-#[no_mangle]
-pub unsafe extern "C" fn vello_paint_set_cap(
-    mut paint: Option<NonNull<VelloPaint>>,
-    cap: rive::StrokeCap,
-) {
-    let style = &mut paint.unwrap_and_deref().style;
-    loop {
-        if let RenderStyle::Stroke(stroke) = style {
-            stroke.start_cap = match cap {
-                rive::StrokeCap::Butt => Cap::Butt,
-                rive::StrokeCap::Round => Cap::Round,
-                rive::StrokeCap::Square => Cap::Square,
-            };
-            stroke.end_cap = stroke.start_cap;
-            break;
-        } else {
-            *style = RenderStyle::Stroke(Stroke::new(0.0));
-        }
-    }
-}
-
-#[no_mangle]
-pub unsafe extern "C" fn vello_paint_set_blend_mode(
-    mut paint: Option<NonNull<VelloPaint>>,
-    blend_mode: rive::BlendMode,
-) {
-    let mix: Mix = blend_mode.into();
-    paint.unwrap_and_deref().blend_mode = mix.into();
-}
-
-#[derive(Debug)]
-pub struct VelloPath {
-    path: BezPath,
-    fill: Fill,
-}
-
-impl Default for VelloPath {
-    fn default() -> Self {
-        Self {
-            path: Default::default(),
-            fill: Fill::NonZero,
-        }
-    }
-}
-
-#[no_mangle]
-pub unsafe extern "C" fn vello_path_new() -> Option<NonNull<VelloPath>> {
-    NonNull::new(Box::into_raw(Box::default()))
-}
-
-#[no_mangle]
-pub unsafe extern "C" fn vello_path_release(path: Option<NonNull<VelloPath>>) {
-    path.map(|ptr| Box::from_raw(ptr.as_ptr())).unwrap();
-}
-
-#[no_mangle]
-pub unsafe extern "C" fn vello_path_set_fill_rule(
-    mut path: Option<NonNull<VelloPath>>,
-    fill_rule: rive::FillRule,
-) {
-    path.unwrap_and_deref().fill = match fill_rule {
-        rive::FillRule::NonZero => Fill::NonZero,
-        rive::FillRule::EvenOdd => Fill::EvenOdd,
-    }
-}
-
-#[no_mangle]
-pub unsafe extern "C" fn vello_path_rewind(mut path: Option<NonNull<VelloPath>>) {
-    path.unwrap_and_deref().path.truncate(0);
-}
-
-#[no_mangle]
-pub unsafe extern "C" fn vello_path_extend(
-    mut path: Option<NonNull<VelloPath>>,
-    mut from: Option<NonNull<VelloPath>>,
-    mut transform: Option<NonNull<[f32; 6]>>,
-) {
-    let mut from = from.unwrap_and_deref().path.clone();
-    from.apply_affine(Affine::new(transform.unwrap_and_deref().map(Into::into)));
-
-    path.unwrap_and_deref()
-        .path
-        .extend(from.elements().iter().cloned());
-}
-
-#[no_mangle]
-pub unsafe extern "C" fn vello_path_move_to(mut path: Option<NonNull<VelloPath>>, x: f32, y: f32) {
-    path.unwrap_and_deref()
-        .path
-        .move_to(Point::new(x as f64, y as f64));
-}
-
-#[no_mangle]
-pub unsafe extern "C" fn vello_path_line_to(mut path: Option<NonNull<VelloPath>>, x: f32, y: f32) {
-    path.unwrap_and_deref()
-        .path
-        .line_to(Point::new(x as f64, y as f64));
-}
-
-#[no_mangle]
-pub unsafe extern "C" fn vello_path_cubic_to(
-    mut path: Option<NonNull<VelloPath>>,
-    ox: f32,
-    oy: f32,
-    ix: f32,
-    iy: f32,
-    x: f32,
-    y: f32,
-) {
-    path.unwrap_and_deref().path.curve_to(
-        Point::new(ox as f64, oy as f64),
-        Point::new(ix as f64, iy as f64),
-        Point::new(x as f64, y as f64),
-    );
-}
-
-#[no_mangle]
-pub unsafe extern "C" fn vello_path_close(mut path: Option<NonNull<VelloPath>>) {
-    path.unwrap_and_deref().path.close_path();
-}
-
-pub struct VelloRenderer {
-    pub scene: Box<SceneFragment>,
-    builder: SceneBuilder<'static>,
-    transforms: Vec<Affine>,
-    clips: Vec<bool>,
-}
-
-impl VelloRenderer {
-    fn last_transform(&mut self) -> &mut Affine {
-        self.transforms.last_mut().unwrap()
-    }
-
-    fn last_clip(&mut self) -> &mut bool {
-        self.clips.last_mut().unwrap()
-    }
-}
-
-impl Default for VelloRenderer {
-    fn default() -> Self {
-        let mut scene = Box::<SceneFragment>::default();
-        let builder = {
-            let scene_mut: &mut SceneFragment = &mut scene;
-            SceneBuilder::for_fragment(unsafe {
-                // Quite a hack until we have a better way to do this in Vello.
-                // Pretend that the scene fragment pointer lives for 'static.
-                std::mem::transmute(scene_mut)
-            })
-        };
-
-        Self {
-            scene,
-            builder,
-            transforms: vec![Affine::IDENTITY],
-            clips: vec![false],
-        }
-    }
-}
-
-#[no_mangle]
-pub unsafe extern "C" fn vello_renderer_save(mut renderer: Option<NonNull<VelloRenderer>>) {
-    let renderer = renderer.unwrap_and_deref();
-
-    let last_transform = *renderer.last_transform();
-
-    renderer.transforms.push(last_transform);
-    renderer.clips.push(false);
-}
-
-#[no_mangle]
-pub unsafe extern "C" fn vello_renderer_restore(mut renderer: Option<NonNull<VelloRenderer>>) {
-    let renderer = renderer.unwrap_and_deref();
-
-    renderer.transforms.pop();
-    if renderer.clips.pop().unwrap_or_default() {
-        renderer.builder.pop_layer();
-    }
-
-    if renderer.transforms.is_empty() {
-        renderer.transforms.push(Affine::IDENTITY);
-        renderer.clips.push(false);
-    }
-}
-
-#[no_mangle]
-pub unsafe extern "C" fn vello_renderer_transform(
-    mut renderer: Option<NonNull<VelloRenderer>>,
-    transform: *const [f32; 6],
-) {
-    let last_transform = renderer.unwrap_and_deref().last_transform();
-    *last_transform *= Affine::new((*transform).map(Into::into));
-}
-
-#[no_mangle]
-pub unsafe extern "C" fn vello_renderer_draw_path(
-    mut renderer: Option<NonNull<VelloRenderer>>,
-    mut path: Option<NonNull<VelloPath>>,
-    mut paint: Option<NonNull<VelloPaint>>,
-) {
-    let renderer = renderer.unwrap_and_deref();
-    let path = path.unwrap_and_deref();
-    let paint = paint.unwrap_and_deref();
-
-    let transform = *renderer.last_transform();
-
-    let builder = &mut renderer.builder;
-
-    let skip_blending = paint.blend_mode == Mix::Normal.into();
-
-    if !skip_blending {
-        builder.push_layer(paint.blend_mode, 1.0, transform, &path.path.bounding_box());
-    }
-
-    match &paint.style {
-        RenderStyle::Fill => builder.fill(path.fill, transform, &paint.brush, None, &path.path),
-        RenderStyle::Stroke(stroke) => {
-            builder.stroke(stroke, transform, &paint.brush, None, &path.path)
-        }
-    }
-
-    if !skip_blending {
-        builder.pop_layer();
-    }
-}
-
-#[no_mangle]
-pub unsafe extern "C" fn vello_renderer_clip_path(
-    mut renderer: Option<NonNull<VelloRenderer>>,
-    mut clip: Option<NonNull<VelloPath>>,
-) {
-    let renderer = renderer.unwrap_and_deref();
-
-    let transform = *renderer.last_transform();
-
-    if *renderer.last_clip() {
-        renderer.builder.pop_layer();
-    }
-
-    renderer
-        .builder
-        .push_layer(Mix::Clip, 1.0, transform, &clip.unwrap_and_deref().path);
-
-    *renderer.last_clip() = true;
-}
-
-#[no_mangle]
-pub unsafe extern "C" fn vello_renderer_draw_image(
-    mut renderer: Option<NonNull<VelloRenderer>>,
-    mut image: Option<NonNull<Image>>,
-    blend_mode: rive::BlendMode,
-    opacity: f32,
-) {
-    let renderer = renderer.unwrap_and_deref();
-    let image = image.unwrap_and_deref();
-    let mix: Mix = blend_mode.into();
-
-    let transform = renderer.last_transform().pre_translate(Vec2::new(
-        image.width as f64 * -0.5,
-        image.height as f64 * -0.5,
-    ));
-    let rect = Rect::new(0.0, 0.0, image.width as f64, image.height as f64);
-
-    let builder = &mut renderer.builder;
-
-    let skip_blending = mix == Mix::Normal && opacity == 1.0;
-
-    if skip_blending {
-        builder.push_layer(mix, opacity, transform, &rect);
-    }
-
-    builder.draw_image(image, transform);
-
-    if skip_blending {
-        builder.pop_layer();
-    }
-}
-
-fn triangle_path(points: [Point; 3]) -> BezPath {
-    BezPath::from_path_segments(
-        [
-            PathSeg::Line(Line::new(points[0], points[1])),
-            PathSeg::Line(Line::new(points[1], points[2])),
-            PathSeg::Line(Line::new(points[2], points[0])),
-        ]
-        .into_iter(),
-    )
-}
-
-#[no_mangle]
-pub unsafe extern "C" fn vello_renderer_draw_image_mesh(
-    mut renderer: Option<NonNull<VelloRenderer>>,
-    mut image: Option<NonNull<Image>>,
-    vertices_data: *const rive::Vec2D,
-    vertices_len: usize,
-    uvs_data: *const rive::Vec2D,
-    uvs_len: usize,
-    indices_data: *const u16,
-    indices_len: usize,
-    blend_mode: rive::BlendMode,
-    opacity: f32,
-) {
-    let renderer = renderer.unwrap_and_deref();
-    let image = image.unwrap_and_deref();
-
-    let vertices = slice::from_raw_parts(vertices_data, vertices_len);
-    let uvs = slice::from_raw_parts(uvs_data, uvs_len);
-    let indices = slice::from_raw_parts(indices_data, indices_len);
-
-    let mix: Mix = blend_mode.into();
-
-    for triangle_indices in indices.chunks_exact(3) {
-        let points = [
-            vertices[triangle_indices[0] as usize],
-            vertices[triangle_indices[1] as usize],
-            vertices[triangle_indices[2] as usize],
-        ];
-        let uvs = [
-            uvs[triangle_indices[0] as usize],
-            uvs[triangle_indices[1] as usize],
-            uvs[triangle_indices[2] as usize],
-        ];
-
-        let center = Point::new(
-            ((points[0].x + points[1].x + points[2].x) / 3.0) as f64,
-            ((points[0].y + points[1].y + points[2].y) / 3.0) as f64,
-        );
-
-        let path = triangle_path(points.map(|v| Point::new(v.x as f64, v.y as f64)));
-
-        let transform = renderer
-            .last_transform()
-            .pre_scale_from_origin(1.03, center);
-        let brush_transform = util::map_uvs_to_triangle(&points, &uvs, image.width, image.height);
-
-        let builder = &mut renderer.builder;
-
-        let skip_blending = mix == Mix::Normal;
-
-        if !skip_blending {
-            builder.push_layer(mix, opacity, transform, &path.bounding_box());
-        }
-
-        builder.fill(
-            Fill::NonZero,
-            transform,
-            BrushRef::Image(image),
-            Some(brush_transform),
-            &path,
-        );
-
-        if !skip_blending {
-            builder.pop_layer();
-        }
-    }
-}
diff --git a/vello/src/main.rs b/vello/src/main.rs
index 06a4dc0..9bb6c32 100644
--- a/vello/src/main.rs
+++ b/vello/src/main.rs
@@ -1,6 +1,6 @@
-use std::time::Instant;
+use std::{fs, time::Instant};
 
-use rive_vello::{VelloRenderer, ViewerContent};
+use rive_rs::{Artboard, File, Instantiate, Viewport};
 use vello::{
     kurbo::{Affine, Rect, Vec2},
     peniko::{Color, Fill},
@@ -24,7 +24,8 @@
 const SCROLL_FACTOR_THRESHOLD: f64 = 100.0;
 
 fn main() {
-    let mut viewer_content: Option<ViewerContent> = None;
+    let mut viewport = Viewport::default();
+    let mut scene: Option<Box<dyn rive_rs::Scene>> = None;
 
     let event_loop = EventLoop::new();
     let mut cached_window: Option<Window> = None;
@@ -46,9 +47,7 @@
             match event {
                 WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
                 WindowEvent::Resized(size) => {
-                    if let Some(viewer_content) = &viewer_content {
-                        viewer_content.handle_resize(size.width, size.height);
-                    }
+                    viewport.resize(size.width, size.height);
 
                     render_cx.resize_surface(&mut render_state.surface, size.width, size.height);
                     render_state.window.request_redraw();
@@ -58,19 +57,23 @@
                     button: MouseButton::Left,
                     ..
                 } => {
-                    if let Some(viewer_content) = &viewer_content {
-                        let handler = match state {
-                            ElementState::Pressed => ViewerContent::handle_pointer_down,
-                            ElementState::Released => ViewerContent::handle_pointer_up,
-                        };
-
-                        handler(viewer_content, mouse_pos);
+                    if let Some(scene) = &mut scene {
+                        match state {
+                            ElementState::Pressed => scene.pointer_down(
+                                mouse_pos.x as f32,
+                                mouse_pos.y as f32,
+                                &viewport,
+                            ),
+                            ElementState::Released => {
+                                scene.pointer_up(mouse_pos.x as f32, mouse_pos.y as f32, &viewport)
+                            }
+                        }
                     }
                 }
                 WindowEvent::CursorMoved { position, .. } => {
                     mouse_pos = Vec2::new(position.x, position.y);
-                    if let Some(viewer_content) = &viewer_content {
-                        viewer_content.handle_pointer_move(mouse_pos);
+                    if let Some(scene) = &mut scene {
+                        scene.pointer_move(mouse_pos.x as f32, mouse_pos.y as f32, &viewport);
                     }
                 }
                 WindowEvent::MouseWheel { delta, .. } => match delta {
@@ -84,12 +87,12 @@
                     }
                 },
                 WindowEvent::DroppedFile(path) => {
-                    viewer_content = ViewerContent::new(path);
+                    scene = Some({
+                        let file = File::new(&fs::read(path).unwrap()).unwrap();
+                        let artboard = Artboard::instantiate(&file, None).unwrap();
 
-                    if let Some(viewer_content) = &viewer_content {
-                        let size = render_state.window.inner_size();
-                        viewer_content.handle_resize(size.width, size.height);
-                    }
+                        Box::<dyn rive_rs::Scene>::instantiate(&artboard, None).unwrap()
+                    });
                 }
                 _ => {}
             }
@@ -100,7 +103,7 @@
             }
         }
         Event::RedrawRequested(_) => {
-            let mut vello_renderer = VelloRenderer::default();
+            let mut rive_renderer = rive_rs::Renderer::default();
             let factor = (scroll_delta / SCROLL_FACTOR_THRESHOLD).max(1.0) as u32;
 
             let elapsed = &frame_start_time.elapsed();
@@ -142,15 +145,15 @@
                 .get_current_texture()
                 .expect("failed to get surface texture");
 
-            let mut scene = Scene::default();
-            let mut builder = SceneBuilder::for_scene(&mut scene);
+            let mut vello_scene = Scene::default();
+            let mut builder = SceneBuilder::for_scene(&mut vello_scene);
 
-            if let Some(viewer_content) = &mut viewer_content {
-                viewer_content.handle_draw(&mut vello_renderer, elapsed.as_secs_f64());
+            if let Some(scene) = &mut scene {
+                scene.advance_and_maybe_draw(&mut rive_renderer, *elapsed, &mut viewport);
 
                 for i in 0..factor.pow(2) {
                     builder.append(
-                        &vello_renderer.scene,
+                        rive_renderer.scene(),
                         Some(
                             Affine::default()
                                 .then_scale(1.0 / factor as f64)
@@ -162,7 +165,7 @@
                     );
                 }
             } else {
-                // Vello currently crashes when rendering an empty scene.
+                // Vello doesn't draw base color when there is no geometry.
                 builder.fill(
                     Fill::NonZero,
                     Affine::IDENTITY,
@@ -172,17 +175,19 @@
                 );
             }
 
-            vello::block_on_wgpu(
-                &device_handle.device,
-                renderer.as_mut().unwrap().render_to_surface_async(
+            if !vello_scene.data().is_empty() {
+                vello::block_on_wgpu(
                     &device_handle.device,
-                    &device_handle.queue,
-                    &scene,
-                    &surface_texture,
-                    &render_params,
-                ),
-            )
-            .expect("failed to render to surface");
+                    renderer.as_mut().unwrap().render_to_surface_async(
+                        &device_handle.device,
+                        &device_handle.queue,
+                        &vello_scene,
+                        &surface_texture,
+                        &render_params,
+                    ),
+                )
+                .expect("failed to render to surface");
+            }
 
             surface_texture.present();
             device_handle.device.poll(wgpu::Maintain::Poll);
@@ -220,6 +225,7 @@
                             timestamp_period: render_cx.devices[render_state.surface.dev_id]
                                 .queue
                                 .get_timestamp_period(),
+                            use_cpu: false,
                         },
                     )
                     .expect("Could create renderer"),
diff --git a/vello/src/rive.rs b/vello/src/rive.rs
deleted file mode 100644
index 7c6c222..0000000
--- a/vello/src/rive.rs
+++ /dev/null
@@ -1,98 +0,0 @@
-use vello::peniko::{Mix, Stroke};
-
-use crate::RenderStyle;
-
-#[repr(C)]
-#[derive(Debug, Default)]
-pub enum RenderPaint {
-    Stroke,
-    #[default]
-    Fill,
-}
-
-impl From<RenderPaint> for RenderStyle {
-    fn from(value: RenderPaint) -> Self {
-        match value {
-            RenderPaint::Stroke => Self::Stroke(Stroke::new(0.0)),
-            RenderPaint::Fill => Self::Fill,
-        }
-    }
-}
-
-#[repr(u32)]
-#[derive(Debug, Default)]
-pub enum StrokeJoin {
-    #[default]
-    Miter = 0,
-    Round = 1,
-    Bevel = 2,
-}
-
-#[repr(u32)]
-#[derive(Debug, Default)]
-pub enum StrokeCap {
-    #[default]
-    Butt = 0,
-    Round = 1,
-    Square = 2,
-}
-
-#[repr(u32)]
-#[derive(Debug, Default)]
-pub enum BlendMode {
-    #[default]
-    SrcOver = 3,
-    Screen = 14,
-    Overlay = 15,
-    Darken = 16,
-    Lighten = 17,
-    ColorDodge = 18,
-    ColorBurn = 19,
-    HardLight = 20,
-    SoftLight = 21,
-    Difference = 22,
-    Exclusion = 23,
-    Multiply = 24,
-    Hue = 25,
-    Saturation = 26,
-    Color = 27,
-    Luminosity = 28,
-}
-
-impl From<BlendMode> for Mix {
-    fn from(value: BlendMode) -> Self {
-        match value {
-            BlendMode::SrcOver => Self::Normal,
-            BlendMode::Screen => Self::Screen,
-            BlendMode::Overlay => Self::Overlay,
-            BlendMode::Darken => Self::Darken,
-            BlendMode::Lighten => Self::Lighten,
-            BlendMode::ColorDodge => Self::ColorDodge,
-            BlendMode::ColorBurn => Self::ColorBurn,
-            BlendMode::HardLight => Self::HardLight,
-            BlendMode::SoftLight => Self::SoftLight,
-            BlendMode::Difference => Self::Difference,
-            BlendMode::Exclusion => Self::Exclusion,
-            BlendMode::Multiply => Self::Multiply,
-            BlendMode::Hue => Self::Hue,
-            BlendMode::Saturation => Self::Saturation,
-            BlendMode::Color => Self::Color,
-            BlendMode::Luminosity => Self::Luminosity,
-        }
-    }
-}
-
-#[repr(C)]
-#[derive(Debug, Default)]
-pub enum FillRule {
-    #[default]
-    NonZero,
-    EvenOdd,
-}
-
-#[repr(C)]
-#[derive(Clone, Copy, Debug)]
-pub struct Vec2D {
-    pub x: f32,
-    pub y: f32,
-}
diff --git a/vello/src/util.rs b/vello/src/util.rs
deleted file mode 100644
index aba56d9..0000000
--- a/vello/src/util.rs
+++ /dev/null
@@ -1,157 +0,0 @@
-use std::{
-    ops::{Add, Mul, Neg, Sub},
-    ptr::NonNull,
-};
-
-use vello::kurbo::{Affine, Point};
-
-use crate::rive;
-
-pub trait UnwrapAndDeref {
-    type Deref;
-
-    unsafe fn unwrap_and_deref(&mut self) -> &mut Self::Deref;
-}
-
-impl<T> UnwrapAndDeref for Option<NonNull<T>> {
-    type Deref = T;
-
-    unsafe fn unwrap_and_deref(&mut self) -> &mut Self::Deref {
-        self.map(|mut ptr| ptr.as_mut()).unwrap()
-    }
-}
-
-#[derive(Clone, Copy, Debug)]
-struct Vec2 {
-    x: f32,
-    y: f32,
-}
-
-impl Vec2 {
-    pub fn new(x: f32, y: f32) -> Self {
-        Self { x, y }
-    }
-
-    pub fn splat(val: f32) -> Self {
-        Self::new(val, val)
-    }
-}
-
-impl Add for Vec2 {
-    type Output = Self;
-
-    fn add(self, rhs: Self) -> Self::Output {
-        Self::new(self.x + rhs.x, self.y + rhs.y)
-    }
-}
-
-impl Sub for Vec2 {
-    type Output = Self;
-
-    fn sub(self, rhs: Self) -> Self::Output {
-        Self::new(self.x - rhs.x, self.y - rhs.y)
-    }
-}
-
-impl Mul for Vec2 {
-    type Output = Self;
-
-    fn mul(self, rhs: Self) -> Self::Output {
-        Self::new(self.x * rhs.x, self.y * rhs.y)
-    }
-}
-
-impl Mul<f32> for Vec2 {
-    type Output = Self;
-
-    fn mul(self, rhs: f32) -> Self::Output {
-        self * Self::splat(rhs)
-    }
-}
-
-impl Neg for Vec2 {
-    type Output = Self;
-
-    fn neg(self) -> Self::Output {
-        Self::new(-self.x, -self.y)
-    }
-}
-
-/// Finds the affine transform that maps triangle `from` to triangle `to`. The algorithm is based
-/// on the [Simplex Affine Mapping] method which has a [Swift implementation].
-///
-/// [Simplex Affine Mapping]: https://www.researchgate.net/publication/332410209_Beginner%27s_guide_to_mapping_simplexes_affinely
-/// [Swift implementation]: https://rethunk.medium.com/finding-an-affine-transform-using-three-2d-point-correspondences-using-simplex-affine-mapping-255aeb4e8055
-fn simplex_affine_mapping(from: [Vec2; 3], to: [Vec2; 3]) -> Affine {
-    let [a, b, c] = from;
-    let [d, e, f] = to;
-
-    let det_recip = (a.x * b.y + b.x * c.y + c.x * a.y - a.x * c.y - b.x * a.y - c.x * b.y).recip();
-
-    let p = (d * (b.y - c.y) - e * (a.y - c.y) + f * (a.y - b.y)) * det_recip;
-
-    let q = (e * (a.x - c.x) - d * (b.x - c.x) - f * (a.x - b.x)) * det_recip;
-
-    let t = (d * (b.x * c.y - b.y * c.x) - e * (a.x * c.y - a.y * c.x)
-        + f * (a.x * b.y - a.y * b.x))
-        * det_recip;
-
-    Affine::new([
-        p.x as f64, p.y as f64, q.x as f64, q.y as f64, t.x as f64, t.y as f64,
-    ])
-}
-
-pub fn map_uvs_to_triangle(
-    points: &[rive::Vec2D; 3],
-    uvs: &[rive::Vec2D; 3],
-    width: u32,
-    height: u32,
-) -> Affine {
-    simplex_affine_mapping(
-        uvs.map(|v| Vec2::new(v.x * width as f32, v.y * height as f32)),
-        points.map(|v| Vec2::new(v.x, v.y)),
-    )
-}
-
-pub trait ScaleFromOrigin {
-    fn pre_scale_from_origin(self, scale: f64, origin: Point) -> Self;
-}
-
-impl ScaleFromOrigin for Affine {
-    fn pre_scale_from_origin(self, scale: f64, origin: Point) -> Self {
-        let origin = origin.to_vec2();
-        self.pre_translate(origin)
-            .pre_scale(scale)
-            .pre_translate(-origin)
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    use vello::kurbo;
-
-    #[test]
-    fn straightforward_sam() {
-        let from = [
-            Vec2::new(1.0, 1.0),
-            Vec2::new(3.0, 1.0),
-            Vec2::new(1.0, 3.0),
-        ];
-        let to = [
-            Vec2::new(4.0, 4.0),
-            Vec2::new(4.0, -1.0),
-            Vec2::new(-2.0, 4.0),
-        ];
-
-        let mapping = simplex_affine_mapping(from, to);
-
-        for (from, to) in from.into_iter().zip(to.into_iter()) {
-            let res = mapping * kurbo::Point::new(from.x as f64, from.y as f64);
-
-            assert_eq!(res.x as f32, to.x);
-            assert_eq!(res.y as f32, to.y);
-        }
-    }
-}
diff --git a/vello/src/vello_renderer.cpp b/vello/src/vello_renderer.cpp
deleted file mode 100644
index ffd59af..0000000
--- a/vello/src/vello_renderer.cpp
+++ /dev/null
@@ -1,175 +0,0 @@
-#include "vello_renderer.hpp"
-
-VelloPath::~VelloPath() { vello_path_release(m_path); }
-
-void VelloPath::rewind() { vello_path_rewind(m_path); }
-
-void VelloPath::addRenderPath(RenderPath* path, const Mat2D& transform)
-{
-    vello_path_extend(m_path, static_cast<VelloPath*>(path)->m_path, transform.values());
-}
-
-void VelloPath::fillRule(FillRule value) { vello_path_set_fill_rule(m_path, value); }
-
-void VelloPath::moveTo(float x, float y) { vello_path_move_to(m_path, x, y); }
-
-void VelloPath::lineTo(float x, float y) { vello_path_line_to(m_path, x, y); }
-
-void VelloPath::cubicTo(float ox, float oy, float ix, float iy, float x, float y)
-{
-    vello_path_cubic_to(m_path, ox, oy, ix, iy, x, y);
-}
-
-void VelloPath::close() { vello_path_close(m_path); }
-
-VelloGradient::~VelloGradient() { vello_gradient_release(m_gradient); }
-
-VelloImage::~VelloImage() { vello_image_release(m_image); }
-
-VelloPaint::~VelloPaint() { vello_paint_release(m_paint); }
-
-void VelloPaint::style(RenderPaintStyle style) { vello_paint_set_style(m_paint, style); }
-
-void VelloPaint::color(unsigned int value) { vello_paint_set_color(m_paint, value); }
-
-void VelloPaint::thickness(float value) { vello_paint_set_thickness(m_paint, value); }
-
-void VelloPaint::join(StrokeJoin value) { vello_paint_set_join(m_paint, value); }
-
-void VelloPaint::cap(StrokeCap value) { vello_paint_set_cap(m_paint, value); }
-
-void VelloPaint::blendMode(BlendMode value) { vello_paint_set_blend_mode(m_paint, value); }
-
-void VelloPaint::shader(rcp<RenderShader> shader)
-{
-    VelloGradient* gradient = (VelloGradient*)shader.get();
-
-    if (gradient != nullptr)
-    {
-        vello_paint_set_gradient(m_paint, gradient->gradient());
-    }
-}
-
-void VelloRenderer::save() { vello_renderer_save(m_renderer); }
-
-void VelloRenderer::restore() { vello_renderer_restore(m_renderer); }
-
-void VelloRenderer::transform(const Mat2D& transform)
-{
-    vello_renderer_transform(m_renderer, transform.values());
-}
-
-void VelloRenderer::clipPath(RenderPath* path)
-{
-    vello_renderer_clip_path(m_renderer, static_cast<VelloPath*>(path)->path());
-}
-
-void VelloRenderer::drawPath(RenderPath* path, RenderPaint* paint)
-{
-    vello_renderer_draw_path(m_renderer,
-                             static_cast<VelloPath*>(path)->path(),
-                             static_cast<VelloPaint*>(paint)->paint());
-}
-
-void VelloRenderer::drawImage(const RenderImage* image, BlendMode blend_mode, float opacity)
-{
-    vello_renderer_draw_image(m_renderer,
-                              static_cast<const VelloImage*>(image)->image(),
-                              blend_mode,
-                              opacity);
-}
-
-void VelloRenderer::drawImageMesh(const RenderImage* image,
-                                  rcp<RenderBuffer> vertices,
-                                  rcp<RenderBuffer> uvCoords,
-                                  rcp<RenderBuffer> indices,
-                                  uint32_t vertexCount,
-                                  uint32_t indexCount,
-                                  BlendMode blendMode,
-                                  float opacity)
-{
-    vello_renderer_draw_image_mesh(m_renderer,
-                                   static_cast<const VelloImage*>(image)->image(),
-                                   DataRenderBuffer::Cast(vertices.get())->vecs(),
-                                   vertexCount,
-                                   DataRenderBuffer::Cast(uvCoords.get())->vecs(),
-                                   vertexCount,
-                                   DataRenderBuffer::Cast(indices.get())->u16s(),
-                                   indexCount,
-                                   blendMode,
-                                   opacity);
-}
-
-rcp<RenderBuffer> VelloFactory::makeRenderBuffer(RenderBufferType type,
-                                                 RenderBufferFlags flags,
-                                                 size_t sizeInBytes)
-{
-    return make_rcp<DataRenderBuffer>(type, flags, sizeInBytes);
-}
-
-rcp<RenderShader> VelloFactory::makeLinearGradient(float sx,
-                                                   float sy,
-                                                   float ex,
-                                                   float ey,
-                                                   const ColorInt colors[], // [count]
-                                                   const float stops[],     // [count]
-                                                   size_t count)
-{
-    RawVelloGradient gradient = vello_gradient_new_linear(sx, sy, ex, ey, colors, stops, count);
-    return rcp<RenderShader>(new VelloGradient(std::move(gradient)));
-}
-
-rcp<RenderShader> VelloFactory::makeRadialGradient(float cx,
-                                                   float cy,
-                                                   float radius,
-                                                   const ColorInt colors[], // [count]
-                                                   const float stops[],     // [count]
-                                                   size_t count)
-{
-    RawVelloGradient gradient = vello_gradient_new_radial(cx, cy, radius, colors, stops, count);
-    return rcp<RenderShader>(new VelloGradient(std::move(gradient)));
-}
-
-std::unique_ptr<RenderPath> VelloFactory::makeRenderPath(RawPath& rawPath, FillRule fillRule)
-{
-    std::unique_ptr<VelloPath> path = std::make_unique<VelloPath>();
-
-    for (auto [verb, points] : rawPath)
-    {
-        switch (verb)
-        {
-            case PathVerb::move:
-                path->move(points[0]);
-                break;
-            case PathVerb::line:
-                path->line(points[1]);
-                break;
-            case PathVerb::cubic:
-                path->cubic(points[1], points[2], points[3]);
-                break;
-            case PathVerb::close:
-                path->close();
-                break;
-        }
-    }
-
-    return path;
-}
-
-std::unique_ptr<RenderPath> VelloFactory::makeEmptyRenderPath()
-{
-    return std::make_unique<VelloPath>();
-}
-
-std::unique_ptr<RenderPaint> VelloFactory::makeRenderPaint()
-{
-    return std::make_unique<VelloPaint>();
-}
-
-std::unique_ptr<RenderImage> VelloFactory::decodeImage(Span<const uint8_t> encoded)
-{
-    return std::make_unique<VelloImage>(vello_image_new(encoded.data(), encoded.size()));
-}
-
-static VelloFactory factory;
-rive::Factory* ViewerContent::RiveFactory() { return &factory; }
diff --git a/vello/src/vello_renderer.hpp b/vello/src/vello_renderer.hpp
deleted file mode 100644
index ab8a0c6..0000000
--- a/vello/src/vello_renderer.hpp
+++ /dev/null
@@ -1,222 +0,0 @@
-#ifndef _RIVE_VELLO_RENDERER_HPP_
-#define _RIVE_VELLO_RENDERER_HPP_
-
-#include "rive/factory.hpp"
-#include "rive/math/mat2d.hpp"
-#include "rive/math/path_types.hpp"
-#include "rive/math/vec2d.hpp"
-#include "rive/renderer.hpp"
-#include "utils/factory_utils.hpp"
-#include "viewer/viewer_content.hpp"
-
-#include "vello_renderer.hpp"
-
-using namespace rive;
-
-typedef void* RawVelloPath;
-typedef void* RawVelloGradient;
-typedef void* RawVelloImage;
-typedef void* RawVelloPaint;
-typedef void* RawVelloRenderer;
-
-extern "C"
-{
-    typedef void* RawVelloPath;
-    typedef void* RawVelloGradient;
-    typedef void* RawVelloImage;
-    typedef void* RawVelloPaint;
-    typedef void* RawVelloRenderer;
-
-    const RawVelloPath vello_path_new();
-    void vello_path_release(const RawVelloPath path);
-    void vello_path_rewind(const RawVelloPath path);
-    void vello_path_extend(const RawVelloPath path, void* other, const float* transform);
-    void vello_path_set_fill_rule(const RawVelloPath path, FillRule fill_rule);
-    void vello_path_move_to(const RawVelloPath path, float x, float y);
-    void vello_path_line_to(const RawVelloPath path, float x, float y);
-    void vello_path_cubic_to(const RawVelloPath path,
-                             float ox,
-                             float oy,
-                             float ix,
-                             float iy,
-                             float x,
-                             float y);
-    void vello_path_close(const RawVelloPath path);
-
-    const RawVelloGradient vello_gradient_new_linear(float sx,
-                                                     float sy,
-                                                     float ex,
-                                                     float ey,
-                                                     const uint32_t* colors_data,
-                                                     const float* stops_data,
-                                                     size_t len);
-    const RawVelloGradient vello_gradient_new_radial(float cx,
-                                                     float cy,
-                                                     float radius,
-                                                     const uint32_t* colors_data,
-                                                     const float* stops_data,
-                                                     size_t len);
-    void vello_gradient_release(const RawVelloGradient gradient);
-
-    const RawVelloImage vello_image_new(const uint8_t* data, size_t len);
-    void vello_image_release(const RawVelloImage image);
-
-    const RawVelloPaint vello_paint_new();
-    void vello_paint_release(const RawVelloPaint paint);
-    void vello_paint_set_style(const RawVelloPaint paint, RenderPaintStyle style);
-    void vello_paint_set_color(const RawVelloPaint paint, uint32_t color);
-    void vello_paint_set_thickness(const RawVelloPaint paint, float thickness);
-    void vello_paint_set_join(const RawVelloPaint paint, StrokeJoin join);
-    void vello_paint_set_cap(const RawVelloPaint paint, StrokeCap cap);
-    void vello_paint_set_blend_mode(const RawVelloPaint paint, BlendMode blend_mode);
-    void vello_paint_set_gradient(const RawVelloPaint paint, const RawVelloGradient gradient);
-
-    const RawVelloRenderer vello_renderer_new();
-    void vello_renderer_release(const RawVelloRenderer renderer);
-    void vello_renderer_save(const RawVelloRenderer renderer);
-    void vello_renderer_restore(const RawVelloRenderer renderer);
-    void vello_renderer_transform(const RawVelloRenderer renderer, const float* transform);
-    void vello_renderer_clip_path(const RawVelloRenderer renderer, const RawVelloPath path);
-    void vello_renderer_draw_path(const RawVelloRenderer renderer,
-                                  const RawVelloPath path,
-                                  const RawVelloPaint paint);
-    void vello_renderer_draw_image(const RawVelloRenderer renderer,
-                                   const RawVelloImage image,
-                                   BlendMode blend_mode,
-                                   float opacity);
-    void vello_renderer_draw_image_mesh(const RawVelloRenderer renderer,
-                                        const RawVelloImage image,
-                                        const Vec2D* vertices_data,
-                                        size_t vertices_len,
-                                        const Vec2D* uvs_data,
-                                        size_t uvs_len,
-                                        const uint16_t* indices_data,
-                                        size_t indices_len,
-                                        BlendMode blend_mode,
-                                        float opacity);
-}
-
-using namespace rive;
-
-class VelloPath : public RenderPath
-{
-private:
-    RawVelloPath m_path;
-
-public:
-    VelloPath() : m_path(vello_path_new()) {}
-    VelloPath(RawVelloPath& path) : m_path(std::move(path)) {}
-    ~VelloPath() override;
-
-    const RawVelloPath& path() const { return m_path; }
-
-    void rewind() override;
-    void addRenderPath(RenderPath* path, const Mat2D& transform) override;
-    void fillRule(FillRule value) override;
-    void moveTo(float x, float y) override;
-    void lineTo(float x, float y) override;
-    void cubicTo(float ox, float oy, float ix, float iy, float x, float y) override;
-    virtual void close() override;
-};
-
-class VelloGradient : public RenderShader
-{
-private:
-    RawVelloGradient m_gradient;
-
-public:
-    VelloGradient(RawVelloGradient gradient) : m_gradient(gradient) {}
-    ~VelloGradient() override;
-
-    const RawVelloGradient& gradient() const { return m_gradient; }
-};
-
-class VelloImage : public RenderImage
-{
-private:
-    RawVelloImage m_image;
-
-public:
-    VelloImage(RawVelloImage image) : m_image(image) {}
-    ~VelloImage() override;
-
-    const RawVelloImage& image() const { return m_image; }
-};
-
-class VelloPaint : public RenderPaint
-{
-private:
-    RawVelloPaint m_paint;
-
-public:
-    VelloPaint() : m_paint(vello_paint_new()) {}
-    ~VelloPaint() override;
-
-    const RawVelloPaint& paint() const { return m_paint; }
-
-    void style(RenderPaintStyle style) override;
-    void color(unsigned int value) override;
-    void thickness(float value) override;
-    void join(StrokeJoin value) override;
-    void cap(StrokeCap value) override;
-    void blendMode(BlendMode value) override;
-    void shader(rcp<RenderShader>) override;
-    void invalidateStroke() override {}
-};
-
-class VelloRenderer : public Renderer
-{
-protected:
-    RawVelloRenderer m_renderer;
-
-public:
-    VelloRenderer() : m_renderer(vello_renderer_new()) {}
-    VelloRenderer(RawVelloRenderer renderer) : m_renderer(std::move(renderer)) {}
-    ~VelloRenderer() override {}
-
-    void save() override;
-    void restore() override;
-    void transform(const Mat2D& transform) override;
-    void clipPath(RenderPath* path) override;
-    void drawPath(RenderPath* path, RenderPaint* paint) override;
-    void drawImage(const RenderImage*, BlendMode, float opacity) override;
-    void drawImageMesh(const RenderImage*,
-                       rcp<RenderBuffer> vertices_f32,
-                       rcp<RenderBuffer> uvCoords_f32,
-                       rcp<RenderBuffer> indices_u16,
-                       uint32_t vertexCount,
-                       uint32_t indexCount,
-                       BlendMode,
-                       float opacity) override;
-};
-
-class VelloFactory : public Factory
-{
-public:
-    rcp<RenderBuffer> makeRenderBuffer(RenderBufferType, RenderBufferFlags, size_t) override;
-
-    rcp<RenderShader> makeLinearGradient(float sx,
-                                         float sy,
-                                         float ex,
-                                         float ey,
-                                         const ColorInt colors[], // [count]
-                                         const float stops[],     // [count]
-                                         size_t count) override;
-
-    rcp<RenderShader> makeRadialGradient(float cx,
-                                         float cy,
-                                         float radius,
-                                         const ColorInt colors[], // [count]
-                                         const float stops[],     // [count]
-                                         size_t count) override;
-
-    std::unique_ptr<RenderPath> makeRenderPath(RawPath&, FillRule) override;
-
-    std::unique_ptr<RenderPath> makeEmptyRenderPath() override;
-
-    std::unique_ptr<RenderPaint> makeRenderPaint() override;
-
-    std::unique_ptr<RenderImage> decodeImage(Span<const uint8_t>) override;
-};
-
-#endif
diff --git a/vello/src/viewer.rs b/vello/src/viewer.rs
deleted file mode 100644
index 2b51a58..0000000
--- a/vello/src/viewer.rs
+++ /dev/null
@@ -1,113 +0,0 @@
-use std::{
-    ffi::CString, fs::File, io::Read, os::unix::prelude::OsStrExt, path::Path, ptr::NonNull,
-};
-
-use vello::kurbo::Vec2;
-
-use crate::VelloRenderer;
-
-#[derive(Debug)]
-enum RawViewerContent {}
-
-extern "C" {
-    fn viewer_content_new(raw_path: *const i8) -> Option<NonNull<RawViewerContent>>;
-    fn viewer_content_release(raw_viewer_content: Option<NonNull<RawViewerContent>>);
-    fn viewer_content_handle_resize(
-        raw_viewer_content: Option<NonNull<RawViewerContent>>,
-        width: i32,
-        height: i32,
-    );
-    // We're simply propagating the `VelloRender` pointer opaquely through the FFI.
-    #[allow(improper_ctypes)]
-    fn viewer_content_handle_draw(
-        raw_viewer_content: Option<NonNull<RawViewerContent>>,
-        raw_vello_renderer: Option<NonNull<VelloRenderer>>,
-        elapsed: f64,
-    );
-    fn viewer_content_handle_pointer_move(
-        raw_viewer_content: Option<NonNull<RawViewerContent>>,
-        x: f32,
-        y: f32,
-    );
-    fn viewer_content_handle_pointer_down(
-        raw_viewer_content: Option<NonNull<RawViewerContent>>,
-        x: f32,
-        y: f32,
-    );
-    fn viewer_content_handle_pointer_up(
-        raw_viewer_content: Option<NonNull<RawViewerContent>>,
-        x: f32,
-        y: f32,
-    );
-}
-
-#[derive(Debug)]
-pub struct ViewerContent {
-    raw_viewer_content: Option<NonNull<RawViewerContent>>,
-}
-
-impl ViewerContent {
-    pub fn new<P: AsRef<Path>>(path: P) -> Option<Self> {
-        let path = path.as_ref();
-
-        let header = {
-            let mut file = File::open(path).ok()?;
-            let mut buffer = [0; 4];
-
-            file.read_exact(&mut buffer).ok()?;
-
-            buffer
-        };
-
-        if &header != b"RIVE" {
-            return None;
-        }
-
-        let c_str = CString::new(path.as_os_str().as_bytes()).unwrap();
-        let raw_viewer_content = Some(unsafe { viewer_content_new(c_str.as_ptr())? });
-
-        Some(Self { raw_viewer_content })
-    }
-
-    pub fn handle_resize(&self, width: u32, height: u32) {
-        unsafe {
-            viewer_content_handle_resize(self.raw_viewer_content, width as i32, height as i32);
-        }
-    }
-
-    pub fn handle_draw(&mut self, renderer: &mut VelloRenderer, elapsed: f64) {
-        unsafe {
-            viewer_content_handle_draw(
-                self.raw_viewer_content,
-                NonNull::new(renderer as *mut VelloRenderer),
-                elapsed,
-            )
-        }
-    }
-
-    pub fn handle_pointer_move(&self, pos: Vec2) {
-        unsafe {
-            viewer_content_handle_pointer_move(self.raw_viewer_content, pos.x as f32, pos.y as f32);
-        }
-    }
-
-    pub fn handle_pointer_down(&self, pos: Vec2) {
-        unsafe {
-            viewer_content_handle_pointer_down(self.raw_viewer_content, pos.x as f32, pos.y as f32);
-        }
-    }
-
-    pub fn handle_pointer_up(&self, pos: Vec2) {
-        unsafe {
-            viewer_content_handle_pointer_up(self.raw_viewer_content, pos.x as f32, pos.y as f32);
-        }
-    }
-}
-
-impl Drop for ViewerContent {
-    fn drop(&mut self) {
-        unsafe {
-            viewer_content_release(self.raw_viewer_content);
-        }
-    }
-}
diff --git a/vello/src/winit_viewer.cpp b/vello/src/winit_viewer.cpp
deleted file mode 100644
index 9406af1..0000000
--- a/vello/src/winit_viewer.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-#include "viewer/viewer_content.hpp"
-
-#include "vello_renderer.hpp"
-
-extern "C"
-{
-    typedef void* RawViewerContent;
-
-    const RawViewerContent viewer_content_new(const char* filename)
-    {
-        return static_cast<void*>(ViewerContent::findHandler(filename).release());
-    }
-
-    void viewer_content_release(const RawViewerContent viewer_content)
-    {
-        std::unique_ptr<ViewerContent> val(std::move(static_cast<ViewerContent*>(viewer_content)));
-    }
-
-    void viewer_content_handle_resize(const RawViewerContent viewer_content,
-                                      int32_t width,
-                                      int32_t height)
-    {
-        static_cast<ViewerContent*>(viewer_content)->handleResize(width, height);
-    }
-
-    void viewer_content_handle_draw(const RawViewerContent viewer_content,
-                                    RawVelloRenderer raw_renderer,
-                                    double elapsed)
-    {
-        VelloRenderer renderer = VelloRenderer(raw_renderer);
-        static_cast<ViewerContent*>(viewer_content)->handleDraw(&renderer, elapsed);
-    }
-
-    void viewer_content_handle_pointer_move(const RawViewerContent viewer_content, float x, float y)
-    {
-        static_cast<ViewerContent*>(viewer_content)->handlePointerMove(x, y);
-    }
-
-    void viewer_content_handle_pointer_down(const RawViewerContent viewer_content, float x, float y)
-    {
-        static_cast<ViewerContent*>(viewer_content)->handlePointerDown(x, y);
-    }
-
-    void viewer_content_handle_pointer_up(const RawViewerContent viewer_content, float x, float y)
-    {
-        static_cast<ViewerContent*>(viewer_content)->handlePointerUp(x, y);
-    }
-}