1#![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
49pub 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
61pub 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
77pub 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
90pub trait PaintScene {
92 fn reset(&mut self);
94
95 fn push_layer(
99 &mut self,
100 blend: impl Into<BlendMode>,
101 alpha: f32,
102 transform: Affine,
103 clip: &impl Shape,
104 );
105
106 fn push_clip_layer(&mut self, transform: Affine, clip: &impl Shape);
110
111 fn pop_layer(&mut self);
113
114 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 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 #[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 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 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 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}