Skip to main content

anyrender/
lib.rs

1//! 2D drawing abstraction that allows applications/frameworks to support many rendering backends through
2//! a unified API.
3//!
4//! ### Painting a scene
5//!
6//! The core abstraction in AnyRender is the [`PaintScene`] trait.
7//!
8//! [`PaintScene`] is a "sink" which accepts drawing commands:
9//!
10//!   - Applications and libraries draw by pushing commands into a [`PaintScene`]
11//!   - Backends execute those commands to produce an output
12//!
13//! ### Rendering to surface or buffer
14//!
15//! In addition to PaintScene, there is:
16//!
17//!   - The [`ImageRenderer`] trait which provides an abstraction for rendering to a `Vec<u8>` RGBA8 buffer.
18//!   - The [`WindowRenderer`] trait which provides an abstraction for rendering to a surface/window
19//!
20//! ### SVG
21//!
22//! The [anyrender_svg](https://docs.rs/anyrender_svg) crate allows SVGs to be rendered using AnyRender
23//!
24//! ### Backends
25//!
26//! Currently existing backends are:
27//!  - [anyrender_vello](https://docs.rs/anyrender_vello)
28//!  - [anyrender_vello_cpu](https://docs.rs/anyrender_vello_cpu)
29
30#![allow(clippy::collapsible_if)]
31
32use kurbo::{Affine, Rect, Shape, Stroke};
33use peniko::{BlendMode, Brush, Color, Fill, FontData, ImageBrushRef, StyleRef};
34use recording::RenderCommand;
35use std::sync::Arc;
36
37pub mod wasm_send_sync;
38pub use wasm_send_sync::*;
39pub mod types;
40pub use types::*;
41mod null_backend;
42pub use null_backend::*;
43pub mod recording;
44pub use recording::Scene;
45
46#[cfg(feature = "serde")]
47mod svg_path_parser;
48
49/// Abstraction for rendering a scene to a window
50pub trait WindowRenderer {
51    type ScenePainter<'a>: PaintScene
52    where
53        Self: 'a;
54    fn resume(&mut self, window: Arc<dyn WindowHandle>, width: u32, height: u32);
55    fn suspend(&mut self);
56    fn is_active(&self) -> bool;
57    fn set_size(&mut self, width: u32, height: u32);
58    fn render<F: FnOnce(&mut Self::ScenePainter<'_>)>(&mut self, draw_fn: F);
59}
60
61/// Abstraction for rendering a scene to an image buffer
62pub trait ImageRenderer {
63    type ScenePainter<'a>: PaintScene
64    where
65        Self: 'a;
66    fn new(width: u32, height: u32) -> Self;
67    fn resize(&mut self, width: u32, height: u32);
68    fn reset(&mut self);
69    fn render_to_vec<F: FnOnce(&mut Self::ScenePainter<'_>)>(
70        &mut self,
71        draw_fn: F,
72        vec: &mut Vec<u8>,
73    );
74    fn render<F: FnOnce(&mut Self::ScenePainter<'_>)>(&mut self, draw_fn: F, buffer: &mut [u8]);
75}
76
77/// Draw a scene to a buffer using an `ImageRenderer`
78pub fn render_to_buffer<R: ImageRenderer, F: FnOnce(&mut R::ScenePainter<'_>)>(
79    draw_fn: F,
80    width: u32,
81    height: u32,
82) -> Vec<u8> {
83    let mut buf = Vec::with_capacity((width * height * 4) as usize);
84    let mut renderer = R::new(width, height);
85    renderer.render_to_vec(draw_fn, &mut buf);
86
87    buf
88}
89
90/// Abstraction for drawing a 2D scene
91pub trait PaintScene {
92    /// Removes all content from the scene
93    fn reset(&mut self);
94
95    /// Pushes a new layer clipped by the specified shape and composed with previous layers using the specified blend mode.
96    /// Every drawing command after this call will be clipped by the shape until the layer is popped.
97    /// However, the transforms are not saved or modified by the layer stack.
98    fn push_layer(
99        &mut self,
100        blend: impl Into<BlendMode>,
101        alpha: f32,
102        transform: Affine,
103        clip: &impl Shape,
104    );
105
106    /// Pushes a new clip layer clipped by the specified shape.
107    /// Every drawing command after this call will be clipped by the shape until the layer is popped.
108    /// However, the transforms are not saved or modified by the layer stack.
109    fn push_clip_layer(&mut self, transform: Affine, clip: &impl Shape);
110
111    /// Pops the current layer.
112    fn pop_layer(&mut self);
113
114    /// Strokes a shape using the specified style and brush.
115    fn stroke<'a>(
116        &mut self,
117        style: &Stroke,
118        transform: Affine,
119        brush: impl Into<PaintRef<'a>>,
120        brush_transform: Option<Affine>,
121        shape: &impl Shape,
122    );
123
124    /// Fills a shape using the specified style and brush.
125    fn fill<'a>(
126        &mut self,
127        style: Fill,
128        transform: Affine,
129        brush: impl Into<PaintRef<'a>>,
130        brush_transform: Option<Affine>,
131        shape: &impl Shape,
132    );
133
134    /// Draws a run of glyphs
135    #[allow(clippy::too_many_arguments)]
136    fn draw_glyphs<'a, 's: 'a>(
137        &'s mut self,
138        font: &'a FontData,
139        font_size: f32,
140        hint: bool,
141        normalized_coords: &'a [NormalizedCoord],
142        style: impl Into<StyleRef<'a>>,
143        brush: impl Into<PaintRef<'a>>,
144        brush_alpha: f32,
145        transform: Affine,
146        glyph_transform: Option<Affine>,
147        glyphs: impl Iterator<Item = Glyph>,
148    );
149
150    /// Draw a rounded rectangle blurred with a gaussian filter.
151    fn draw_box_shadow(
152        &mut self,
153        transform: Affine,
154        rect: Rect,
155        brush: Color,
156        radius: f64,
157        std_dev: f64,
158    );
159
160    // --- Provided methods
161
162    /// Append a recorded Scene Fragment to the current scene
163    fn append_scene(&mut self, scene: Scene, scene_transform: Affine) {
164        for cmd in scene.commands {
165            match cmd {
166                RenderCommand::PushLayer(cmd) => self.push_layer(
167                    cmd.blend,
168                    cmd.alpha,
169                    scene_transform * cmd.transform,
170                    &cmd.clip,
171                ),
172                RenderCommand::PushClipLayer(cmd) => {
173                    self.push_clip_layer(scene_transform * cmd.transform, &cmd.clip)
174                }
175                RenderCommand::PopLayer => self.pop_layer(),
176                RenderCommand::Stroke(cmd) => self.stroke(
177                    &cmd.style,
178                    scene_transform * cmd.transform,
179                    match cmd.brush {
180                        Brush::Solid(alpha_color) => Brush::Solid(alpha_color),
181                        Brush::Gradient(ref gradient) => Brush::Gradient(gradient),
182                        Brush::Image(ref image) => Brush::Image(image.as_ref()),
183                    },
184                    cmd.brush_transform,
185                    &cmd.shape,
186                ),
187                RenderCommand::Fill(cmd) => self.fill(
188                    cmd.fill,
189                    scene_transform * cmd.transform,
190                    match cmd.brush {
191                        Brush::Solid(alpha_color) => Brush::Solid(alpha_color),
192                        Brush::Gradient(ref gradient) => Brush::Gradient(gradient),
193                        Brush::Image(ref image) => Brush::Image(image.as_ref()),
194                    },
195                    cmd.brush_transform,
196                    &cmd.shape,
197                ),
198                RenderCommand::GlyphRun(cmd) => self.draw_glyphs(
199                    &cmd.font_data,
200                    cmd.font_size,
201                    cmd.hint,
202                    &cmd.normalized_coords,
203                    &cmd.style,
204                    match cmd.brush {
205                        Brush::Solid(alpha_color) => Brush::Solid(alpha_color),
206                        Brush::Gradient(ref gradient) => Brush::Gradient(gradient),
207                        Brush::Image(ref image) => Brush::Image(image.as_ref()),
208                    },
209                    cmd.brush_alpha,
210                    scene_transform * cmd.transform,
211                    cmd.glyph_transform,
212                    cmd.glyphs.into_iter(),
213                ),
214                RenderCommand::BoxShadow(cmd) => self.draw_box_shadow(
215                    scene_transform * cmd.transform,
216                    cmd.rect,
217                    cmd.brush,
218                    cmd.radius,
219                    cmd.std_dev,
220                ),
221            }
222        }
223    }
224
225    /// Utility method to draw an image at it's natural size. For more advanced image drawing use the `fill` method
226    fn draw_image(&mut self, image: ImageBrushRef, transform: Affine) {
227        self.fill(
228            Fill::NonZero,
229            transform,
230            image,
231            None,
232            &Rect::new(
233                0.0,
234                0.0,
235                image.image.width as f64,
236                image.image.height as f64,
237            ),
238        );
239    }
240}