floem_renderer/lib.rs
1//! Rendering abstraction layer for the Floem UI framework.
2//!
3//! This crate defines the [`Renderer`] trait that all Floem rendering backends must
4//! implement, along with supporting types for passing images and SVGs to the renderer.
5//! Floem ships with three backend implementations:
6//!
7//! - **Vello** (`floem_vello_renderer`) — GPU-accelerated renderer using the Vello scene graph (default).
8//! - **Skia** (`floem_skia_renderer`) — GPU-accelerated renderer using AnyRender's Skia backend.
9//! - **Vger** (`floem_vger_renderer`) — GPU-accelerated renderer using the Vger library.
10//! - **tiny-skia** (`floem_tiny_skia_renderer`) — CPU software renderer using tiny-skia.
11//!
12//! # Modules
13//!
14//! - [`text`] — Text layout, shaping, and font management built on [Parley](https://docs.rs/parley).
15//! - [`gpu_resources`] — Asynchronous wgpu adapter/device acquisition for GPU backends.
16//!
17//! # Re-exports
18//!
19//! [`tiny_skia`] and [`usvg`] are re-exported from [`resvg`](https://docs.rs/resvg) so that
20//! renderer backends and downstream crates can use consistent versions of these libraries
21//! without adding them as direct dependencies.
22
23pub mod text;
24use peniko::{
25 BlendMode, BrushRef,
26 kurbo::{Affine, Point, Rect, Shape, Stroke},
27};
28pub use resvg::tiny_skia;
29pub use resvg::usvg;
30
31pub mod gpu_resources;
32
33/// A reference to a parsed SVG tree paired with a cache key.
34///
35/// # Example
36///
37/// ```no_run
38/// use floem_renderer::{Svg, usvg};
39///
40/// let svg_text = r#"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
41/// <circle cx="12" cy="12" r="10" fill="currentColor"/>
42/// </svg>"#;
43///
44/// let tree = usvg::Tree::from_str(svg_text, &usvg::Options::default()).unwrap();
45/// let hash = b"my-icon-cache-key";
46///
47/// let svg = Svg {
48/// tree: &tree,
49/// hash: hash.as_slice(),
50/// };
51/// ```
52pub struct Svg<'a> {
53 /// The parsed SVG document.
54 pub tree: &'a usvg::Tree,
55 /// An opaque byte slice used as a cache key by the renderer.
56 pub hash: &'a [u8],
57}
58
59/// A raster image paired with a cache key.
60///
61/// # Example
62///
63/// ```no_run
64/// use floem_renderer::Img;
65///
66/// # fn make_image_brush() -> peniko::ImageBrush { todo!() }
67/// let brush = make_image_brush();
68/// let hash = b"photo-abc123";
69///
70/// let img = Img {
71/// img: brush,
72/// hash: hash.as_slice(),
73/// };
74/// ```
75pub struct Img<'a> {
76 /// The image data as a [`peniko::ImageBrush`].
77 pub img: peniko::ImageBrush,
78 /// An opaque byte slice used as a cache key by the renderer.
79 pub hash: &'a [u8],
80}
81
82/// The core rendering trait that every Floem backend must implement.
83///
84/// A frame is bracketed by [`begin`](Renderer::begin) and [`finish`](Renderer::finish).
85/// Between those calls the framework issues drawing commands — fills, strokes, text,
86/// images, SVGs — which the backend records or executes immediately depending on its
87/// architecture.
88///
89/// The typical call sequence within a single frame looks like:
90///
91/// ```text
92/// renderer.begin(capture);
93/// renderer.set_transform(..);
94/// renderer.fill(..); // background
95/// renderer.clip(..); // restrict drawing area
96/// renderer.draw_text_lines(..); // labels, editors
97/// renderer.draw_svg(..); // icons
98/// renderer.draw_img(..); // photos
99/// renderer.stroke(..); // borders
100/// renderer.clear_clip();
101/// renderer.finish();
102/// ```
103pub trait Renderer {
104 /// Begin a new frame.
105 ///
106 /// Must be called exactly once before any drawing commands.
107 /// When `capture` is `true` the renderer should record the frame into an
108 /// off-screen buffer so that [`finish`](Renderer::finish) can return it as
109 /// an [`ImageBrush`](peniko::ImageBrush). This is used by the Floem
110 /// inspector to take snapshots.
111 fn begin(&mut self, capture: bool);
112
113 /// Set the current affine transform in device/render-target coordinates.
114 ///
115 /// All subsequent drawing commands are transformed by `transform` until it
116 /// is changed again. The framework provides this as the final transform for
117 /// the current visual node, including window scaling, so backends should not
118 /// apply an additional global window-scale multiply to ordinary geometry.
119 ///
120 /// Raster-backed operations such as glyph and SVG caching may still derive
121 /// a rasterization scale from this transform to choose an appropriate pixel
122 /// resolution.
123 fn set_transform(&mut self, transform: Affine);
124
125 /// Set the z-index for subsequent draw commands.
126 ///
127 /// Not all backends honour this — Vello and tiny-skia rely on painter's
128 /// order instead.
129 fn set_z_index(&mut self, z_index: i32);
130
131 /// Clip all subsequent drawing to the interior of `shape`.
132 ///
133 /// The clip remains in effect until [`clear_clip`](Renderer::clear_clip) is
134 /// called. On the Vello backend clipping is implemented via
135 /// [`push_layer`](Renderer::push_layer) / [`pop_layer`](Renderer::pop_layer)
136 /// instead, so this method may be a no-op there.
137 fn clip(&mut self, shape: &impl Shape);
138
139 /// Remove the current clip region, allowing drawing to the full surface.
140 fn clear_clip(&mut self);
141
142 /// Stroke the outline of a [`Shape`].
143 ///
144 /// The `brush` defines the color or gradient and `stroke` controls the
145 /// line width, join style, dash pattern, etc.
146 fn stroke<'b, 's>(
147 &mut self,
148 shape: &impl Shape,
149 brush: impl Into<peniko::BrushRef<'b>>,
150 stroke: &'s Stroke,
151 );
152
153 /// Fill the interior of a [`Shape`] using the [non-zero fill rule].
154 ///
155 /// When `blur_radius` is greater than zero the fill is drawn with a
156 /// Gaussian blur, which is used for box shadows.
157 ///
158 /// [non-zero fill rule]: https://en.wikipedia.org/wiki/Nonzero-rule
159 fn fill<'b>(&mut self, path: &impl Shape, brush: impl Into<BrushRef<'b>>, blur_radius: f64);
160
161 /// Push a compositing layer onto the layer stack.
162 ///
163 /// Drawing commands issued after this call are composited into the layer.
164 /// Call [`pop_layer`](Renderer::pop_layer) to flatten the layer back into
165 /// the parent using the specified `blend` mode and `alpha`.
166 ///
167 /// The `clip` shape restricts drawing within the layer, and `transform` is
168 /// applied to the layer contents.
169 fn push_layer(
170 &mut self,
171 blend: impl Into<BlendMode>,
172 alpha: f32,
173 transform: Affine,
174 clip: &impl Shape,
175 );
176
177 /// Pop the topmost compositing layer pushed by [`push_layer`](Renderer::push_layer).
178 ///
179 /// The layer contents are composited into the parent surface using the
180 /// blend mode and alpha that were specified when the layer was pushed.
181 fn pop_layer(&mut self);
182
183 /// Draw a single glyph run.
184 fn draw_glyphs<'a>(
185 &mut self,
186 origin: Point,
187 props: &text::GlyphRunProps<'a>,
188 glyphs: impl Iterator<Item = text::Glyph> + 'a,
189 );
190
191 /// Draw an SVG image inside `rect`.
192 ///
193 /// When `brush` is `Some`, the SVG is rendered as a mask and filled with
194 /// the given brush — this is how Floem applies a color override to icons.
195 fn draw_svg<'b>(&mut self, svg: Svg<'b>, rect: Rect, brush: Option<impl Into<BrushRef<'b>>>);
196
197 /// Draw a raster image inside `rect`.
198 ///
199 /// The image is scaled to fit the destination rectangle.
200 fn draw_img(&mut self, img: Img<'_>, rect: Rect);
201
202 /// Finish the current frame and present it.
203 ///
204 /// If the frame was started with `capture = true`, the rendered content is
205 /// returned as an [`ImageBrush`](peniko::ImageBrush). Otherwise returns
206 /// `None` after presenting the frame to the screen.
207 fn finish(&mut self) -> Option<peniko::ImageBrush>;
208
209 /// Return a human-readable string identifying the renderer backend.
210 ///
211 /// Used by the Floem inspector. Implementations typically return a string
212 /// like `"name: Vello\ninfo: …"`.
213 fn debug_info(&self) -> String {
214 "Unknown".into()
215 }
216}