Add a more ergonomic API for texture overrides

Signed-off-by: Nico Burns <nico@nicoburns.com>
diff --git a/vello/src/lib.rs b/vello/src/lib.rs
index f127ac1..db2037e 100644
--- a/vello/src/lib.rs
+++ b/vello/src/lib.rs
@@ -312,6 +312,15 @@
 )]
 pub(crate) type Result<T, E = Error> = std::result::Result<T, E>;
 
+/// An opaque handle to a texture registered with [`Renderer::register_texture`] that
+/// can be passed to [`Scene::draw_texture`] to draw the texture.
+#[derive(Copy, Clone, PartialEq, Hash)]
+pub struct TextureHandle {
+    pub(crate) id: u64,
+    pub(crate) width: u32,
+    pub(crate) height: u32,
+}
+
 /// Renders a scene into a texture or surface.
 ///
 /// Currently, each renderer only supports a single surface format, if it
@@ -520,6 +529,7 @@
     /// dimensions would be rendered.
     ///
     /// [data]: peniko::Image::data
+    #[deprecated = "Use register_texture and unregister_texture methods instead"]
     pub fn override_image(
         &mut self,
         image: &peniko::Image,
@@ -531,6 +541,40 @@
         }
     }
 
+    /// Register a [`wgpu::Texture`] with Vello. You will receive a [`TextureHandle`] which
+    /// can be used to draw the registered texture using [`Scene::draw_texture`]
+    ///
+    /// If the texture is no longer active then it should be unregistered using [`unregister_texture`](Self::unregister_texture)
+    pub fn register_texture(&mut self, texture: wgpu::Texture) -> TextureHandle {
+        // Generate a unique ID using peniko::Blob to guarantee it won't clash
+        // with a user-generated image Blob
+        let id = peniko::Blob::<()>::new(std::sync::Arc::new(&[])).id();
+
+        let handle = TextureHandle {
+            id,
+            width: texture.width(),
+            height: texture.height(),
+        };
+
+        // Create a texture base for the texture
+        let texture_base = wgpu::TexelCopyTextureInfoBase {
+            texture,
+            mip_level: 0,
+            origin: wgpu::Origin3d::ZERO,
+            aspect: wgpu::TextureAspect::All,
+        };
+
+        // Insert it into the overrides map
+        self.engine.image_overrides.insert(id, texture_base);
+
+        handle
+    }
+
+    /// Unregister a [`wgpu::Texture`] that was registered with [`register_texture`](Self::register_texture)
+    pub fn unregister_texture(&mut self, handle: TextureHandle) {
+        self.engine.image_overrides.remove(&handle.id);
+    }
+
     /// Reload the shaders. This should only be used during `vello` development
     #[cfg(feature = "hot_reload")]
     #[doc(hidden)] // End-users of Vello should not have `hot_reload` enabled.
diff --git a/vello/src/scene.rs b/vello/src/scene.rs
index 0e5f3ed..320c701 100644
--- a/vello/src/scene.rs
+++ b/vello/src/scene.rs
@@ -23,6 +23,8 @@
 use vello_encoding::BumpAllocatorMemory;
 use vello_encoding::{Encoding, Glyph, GlyphRun, NormalizedCoord, Patch, Transform};
 
+use crate::TextureHandle;
+
 // TODO - Document invariants and edge cases (#470)
 // - What happens when we pass a transform matrix with NaN values to the Scene?
 // - What happens if a push_layer isn't matched by a pop_layer?
@@ -306,6 +308,58 @@
         );
     }
 
+    /// Draws a texture at its natural size with the given transform
+    ///
+    /// To get a [`TextureHandle`] to use with this API, first register the texture with the renderer
+    /// using the [`Renderer::register_texture`](crate::Renderer::register_texture) method.
+    pub fn draw_texture(&mut self, handle: TextureHandle, transform: Affine) {
+        self.fill_texture(
+            Fill::NonZero,
+            transform,
+            1.0,
+            handle,
+            None,
+            &Rect::new(0.0, 0.0, handle.width as f64, handle.height as f64),
+            Extend::Pad,
+            Extend::Pad,
+        );
+    }
+
+    /// Fills a shape using the specified texture
+    ///
+    /// To get a [`TextureHandle`] to use with this API, first register the texture with the renderer
+    /// using the [`Renderer::register_texture`](crate::Renderer::register_texture) method.
+    pub fn fill_texture(
+        &mut self,
+        style: Fill,
+        transform: Affine,
+        alpha: f32,
+        handle: TextureHandle,
+        brush_transform: Option<Affine>,
+        shape: &impl Shape,
+        x_extend: Extend,
+        y_extend: Extend,
+    ) {
+        let dummy_image = Image {
+            data: Blob::from_raw_parts(Arc::new([]), handle.id),
+            width: handle.width,
+            height: handle.height,
+            format: peniko::ImageFormat::Rgba8,
+            quality: peniko::ImageQuality::High,
+            x_extend,
+            y_extend,
+            alpha,
+        };
+
+        self.fill(
+            style,
+            transform,
+            BrushRef::Image(&dummy_image),
+            brush_transform,
+            shape,
+        );
+    }
+
     /// Returns a builder for encoding a glyph run.
     pub fn draw_glyphs(&mut self, font: &Font) -> DrawGlyphs<'_> {
         // TODO: Integrate `BumpEstimator` with the glyph cache.