Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/anyrender/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ serde = [
kurbo = { workspace = true }
peniko = { workspace = true }
raw-window-handle = { workspace = true }
rustc-hash = { workspace = true }

# Serde
serde = { workspace = true, features = ["derive"], optional = true }
51 changes: 26 additions & 25 deletions crates/anyrender/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
//! - [anyrender_vello_cpu](https://docs.rs/anyrender_vello_cpu)

use kurbo::{Affine, Rect, Shape, Stroke};
use peniko::{BlendMode, Brush, Color, Fill, FontData, ImageBrushRef, StyleRef};
use peniko::{BlendMode, Brush, Color, Fill, FontData, ImageBrush, StyleRef};
use recording::RenderCommand;
use std::sync::Arc;

Expand All @@ -39,10 +39,10 @@ pub use types::*;
mod null_backend;
pub use null_backend::*;
pub mod recording;
pub use recording::Scene;
pub use recording::{RecordingRenderContext, Scene};

/// Abstraction for rendering a scene to a window
pub trait WindowRenderer {
pub trait WindowRenderer: RenderContext {
type ScenePainter<'a>: PaintScene
where
Self: 'a;
Expand All @@ -54,7 +54,7 @@ pub trait WindowRenderer {
}

/// Abstraction for rendering a scene to an image buffer
pub trait ImageRenderer {
pub trait ImageRenderer: RenderContext {
type ScenePainter<'a>: PaintScene
where
Self: 'a;
Expand All @@ -66,7 +66,11 @@ pub trait ImageRenderer {
draw_fn: F,
vec: &mut Vec<u8>,
);
fn render<F: FnOnce(&mut Self::ScenePainter<'_>)>(&mut self, draw_fn: F, buffer: &mut [u8]);
fn render<F: FnOnce(&mut Self::ScenePainter<'_>)>(
&mut self,
draw_fn: F,
buffer: &mut [u8],
);
}

/// Draw a scene to a buffer using an `ImageRenderer`
Expand Down Expand Up @@ -171,21 +175,21 @@ pub trait PaintScene {
RenderCommand::Stroke(cmd) => self.stroke(
&cmd.style,
scene_transform * cmd.transform,
match cmd.brush {
Brush::Solid(alpha_color) => Brush::Solid(alpha_color),
Brush::Gradient(ref gradient) => Brush::Gradient(gradient),
Brush::Image(ref image) => Brush::Image(image.as_ref()),
match &cmd.brush {
Brush::Solid(alpha_color) => Paint::Solid(*alpha_color),
Brush::Gradient(gradient) => Paint::Gradient(gradient),
Brush::Image(image) => Paint::Image(*image),
},
cmd.brush_transform,
&cmd.shape,
),
RenderCommand::Fill(cmd) => self.fill(
cmd.fill,
scene_transform * cmd.transform,
match cmd.brush {
Brush::Solid(alpha_color) => Brush::Solid(alpha_color),
Brush::Gradient(ref gradient) => Brush::Gradient(gradient),
Brush::Image(ref image) => Brush::Image(image.as_ref()),
match &cmd.brush {
Brush::Solid(alpha_color) => Paint::Solid(*alpha_color),
Brush::Gradient(gradient) => Paint::Gradient(gradient),
Brush::Image(image) => Paint::Image(*image),
},
cmd.brush_transform,
&cmd.shape,
Expand All @@ -196,10 +200,10 @@ pub trait PaintScene {
cmd.hint,
&cmd.normalized_coords,
&cmd.style,
match cmd.brush {
Brush::Solid(alpha_color) => Brush::Solid(alpha_color),
Brush::Gradient(ref gradient) => Brush::Gradient(gradient),
Brush::Image(ref image) => Brush::Image(image.as_ref()),
match &cmd.brush {
Brush::Solid(alpha_color) => Paint::Solid(*alpha_color),
Brush::Gradient(gradient) => Paint::Gradient(gradient),
Brush::Image(image) => Paint::Image(*image),
},
cmd.brush_alpha,
scene_transform * cmd.transform,
Expand All @@ -217,19 +221,16 @@ pub trait PaintScene {
}
}

/// Utility method to draw an image at it's natural size. For more advanced image drawing use the `fill` method
fn draw_image(&mut self, image: ImageBrushRef, transform: Affine) {
/// Utility method to draw an image at its natural size. For more advanced image drawing use the `fill` method
fn draw_image(&mut self, image: ImageBrush<ImageResource>, transform: Affine) {
let width = image.image.width as f64;
let height = image.image.height as f64;
self.fill(
Fill::NonZero,
transform,
image,
None,
&Rect::new(
0.0,
0.0,
image.image.width as f64,
image.image.height as f64,
),
&Rect::new(0.0, 0.0, width, height),
);
}
}
40 changes: 36 additions & 4 deletions crates/anyrender/src/null_backend.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
//! A dummy implementation of the AnyRender traits while simply ignores all commands
//! A dummy implementation of the AnyRender traits that simply ignores all commands

use crate::{ImageRenderer, PaintScene, WindowHandle, WindowRenderer};
use crate::{
ImageRenderer, ImageResource, PaintScene, RenderContext, ResourceId, WindowHandle,
WindowRenderer,
};
use std::sync::Arc;

#[derive(Copy, Clone, Default)]
Expand All @@ -14,6 +17,18 @@ impl NullWindowRenderer {
}
}

impl RenderContext for NullWindowRenderer {
fn register_image(&mut self, image: peniko::ImageData) -> ImageResource {
ImageResource {
id: ResourceId(0),
width: image.width,
height: image.height,
}
}

fn unregister_resource(&mut self, _id: ResourceId) {}
}

impl WindowRenderer for NullWindowRenderer {
type ScenePainter<'a>
= NullScenePainter
Expand All @@ -37,7 +52,7 @@ impl WindowRenderer for NullWindowRenderer {
fn render<F: FnOnce(&mut Self::ScenePainter<'_>)>(&mut self, _draw_fn: F) {}
}

#[derive(Copy, Clone, Default)]
#[derive(Clone, Default)]
pub struct NullImageRenderer;

impl NullImageRenderer {
Expand All @@ -46,6 +61,18 @@ impl NullImageRenderer {
}
}

impl RenderContext for NullImageRenderer {
fn register_image(&mut self, image: peniko::ImageData) -> ImageResource {
ImageResource {
id: ResourceId(0),
width: image.width,
height: image.height,
}
}

fn unregister_resource(&mut self, _id: ResourceId) {}
}

impl ImageRenderer for NullImageRenderer {
type ScenePainter<'a>
= NullScenePainter
Expand All @@ -67,7 +94,12 @@ impl ImageRenderer for NullImageRenderer {
) {
}

fn render<F: FnOnce(&mut Self::ScenePainter<'_>)>(&mut self, _draw_fn: F, _buffer: &mut [u8]) {}
fn render<F: FnOnce(&mut Self::ScenePainter<'_>)>(
&mut self,
_draw_fn: F,
_buffer: &mut [u8],
) {
}
}

#[derive(Copy, Clone, Default)]
Expand Down
57 changes: 52 additions & 5 deletions crates/anyrender/src/recording.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use crate::{Glyph, NormalizedCoord, Paint, PaintRef, PaintScene};
use crate::{
Glyph, ImageResource, NormalizedCoord, Paint, PaintRef, PaintScene, RenderContext, ResourceId,
};
use kurbo::{Affine, BezPath, Rect, Shape, Stroke};
use peniko::{BlendMode, Brush, Color, Fill, FontData, ImageBrush, ImageData, Style, StyleRef};
use rustc_hash::FxHashMap;

#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
Expand All @@ -9,7 +12,7 @@ const DEFAULT_TOLERANCE: f64 = 0.1;

#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum RenderCommand<Font = FontData, Image = ImageData> {
pub enum RenderCommand<Font = FontData, Image = ImageResource> {
/// Pushes a new layer clipped by the specified shape and composed with previous layers using the specified blend mode.
/// Every drawing command after this call will be clipped by the shape until the layer is popped.
/// However, the transforms are not saved or modified by the layer stack.
Expand Down Expand Up @@ -98,7 +101,7 @@ pub struct FillCommand<Image> {
/// Draws a run of glyphs
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct GlyphRunCommand<Font = FontData, Image = ImageData> {
pub struct GlyphRunCommand<Font = FontData, Image = ImageResource> {
pub font_data: Font,
pub font_size: f32,
pub hint: bool,
Expand Down Expand Up @@ -152,11 +155,11 @@ impl Scene {
}
}

fn convert_paintref(&mut self, paint_ref: PaintRef<'_>) -> Brush {
fn convert_paintref(&mut self, paint_ref: PaintRef<'_>) -> Brush<ImageBrush<ImageResource>> {
match paint_ref {
Paint::Solid(color) => Brush::Solid(color),
Paint::Gradient(gradient) => Brush::Gradient(gradient.clone()),
Paint::Image(image) => Brush::Image(image.to_owned()),
Paint::Image(image) => Brush::Image(image),
// TODO: handle this somehow
Paint::Custom(_) => Brush::Solid(Color::TRANSPARENT),
}
Expand Down Expand Up @@ -293,6 +296,50 @@ impl PaintScene for Scene {
}
}

/// A [`RenderContext`] for use with recording scenes.
///
/// This context assigns [`ResourceId`]s and stores the associated [`ImageData`] in a
/// [`FxHashMap`] so that recorded scenes can later be serialized or replayed through a
/// backend-specific context.
pub struct RecordingRenderContext {
image_data: FxHashMap<ResourceId, ImageData>,
next_resource_id: u64,
}

impl RecordingRenderContext {
pub fn new() -> Self {
Self {
image_data: FxHashMap::default(),
next_resource_id: 0,
}
}

pub fn image_data(&self) -> &FxHashMap<ResourceId, ImageData> {
&self.image_data
}
}

impl Default for RecordingRenderContext {
fn default() -> Self {
Self::new()
}
}

impl RenderContext for RecordingRenderContext {
fn register_image(&mut self, image: ImageData) -> ImageResource {
let id = ResourceId(self.next_resource_id);
self.next_resource_id += 1;
let width = image.width;
let height = image.height;
self.image_data.insert(id, image);
ImageResource { id, width, height }
}

fn unregister_resource(&mut self, id: ResourceId) {
self.image_data.remove(&id);
}
}

/// Serde helper for serializing `BezPath` as an SVG path string.
#[cfg(feature = "serde")]
mod svg_path {
Expand Down
Loading