1use std::collections::HashMap;
2use std::sync::Arc;
3
4use anyhow::{Result, anyhow};
5use anyrender::{ImageRenderer as _, WindowRenderer as _};
6use anyrender_skia::{SkiaImageRenderer, SkiaWindowRenderer};
7use floem_renderer::text::{Glyph, GlyphRunProps};
8use floem_renderer::{Img, Renderer, Svg};
9use peniko::kurbo::{Affine, BezPath, Point, Rect, Shape, Size, Stroke};
10use peniko::{
11 Blob, Brush, BrushRef, Compose, Fill, ImageAlphaType, ImageData, ImageFormat, Mix, Style,
12};
13use raw_window_handle::{
14 DisplayHandle, HandleError, HasDisplayHandle, HasWindowHandle, WindowHandle,
15};
16use resvg::tiny_skia::{Pixmap, Transform};
17use winit::window::Window;
18
19const PATH_TOLERANCE: f64 = 0.1;
20
21enum Command {
22 Stroke {
23 shape: BezPath,
24 brush: Brush,
25 stroke: Stroke,
26 transform: Affine,
27 },
28 Fill {
29 shape: BezPath,
30 brush: Brush,
31 transform: Affine,
32 },
33 BoxShadow {
34 rect: Rect,
35 brush: peniko::Color,
36 radius: f64,
37 std_dev: f64,
38 transform: Affine,
39 },
40 PushLayer {
41 blend: peniko::BlendMode,
42 alpha: f32,
43 transform: Affine,
44 clip: BezPath,
45 },
46 PushClip {
47 transform: Affine,
48 clip: BezPath,
49 },
50 PopLayer,
51 Glyphs {
52 font: peniko::FontData,
53 font_size: f32,
54 hint: bool,
55 normalized_coords: Vec<i16>,
56 style: Style,
57 brush: Brush,
58 brush_alpha: f32,
59 transform: Affine,
60 glyph_transform: Option<Affine>,
61 glyphs: Vec<anyrender::Glyph>,
62 },
63 Image {
64 image: peniko::ImageBrush,
65 rect: Rect,
66 transform: Affine,
67 },
68 Svg {
69 image: peniko::ImageBrush,
70 rect: Rect,
71 transform: Affine,
72 brush: Option<Brush>,
73 },
74}
75
76pub struct SkiaRenderer {
77 window_renderer: SkiaWindowRenderer,
78 capture_renderer: SkiaImageRenderer,
79 commands: Vec<Command>,
80 svg_cache: HashMap<(Vec<u8>, u32, u32), peniko::ImageBrush>,
81 size: Size,
82 transform: Affine,
83 capture: bool,
84}
85
86#[derive(Clone)]
87struct AnyrenderWindow(Arc<dyn Window>);
88
89impl HasWindowHandle for AnyrenderWindow {
90 fn window_handle(&self) -> std::result::Result<WindowHandle<'_>, HandleError> {
91 self.0.window_handle()
92 }
93}
94
95impl HasDisplayHandle for AnyrenderWindow {
96 fn display_handle(&self) -> std::result::Result<DisplayHandle<'_>, HandleError> {
97 self.0.display_handle()
98 }
99}
100
101impl SkiaRenderer {
102 pub fn new(
103 window: Arc<dyn Window>,
104 width: u32,
105 height: u32,
106 scale: f64,
107 _font_embolden: f32,
108 ) -> Result<Self> {
109 let width = width.max(1);
110 let height = height.max(1);
111
112 let mut window_renderer = SkiaWindowRenderer::new();
113 let handle: Arc<dyn anyrender::WindowHandle> = Arc::new(AnyrenderWindow(window));
114 window_renderer.resume(handle, width, height);
115
116 let mut capture_renderer = SkiaImageRenderer::new(width, height);
117 capture_renderer.reset();
118
119 Ok(Self {
120 window_renderer,
121 capture_renderer,
122 commands: Vec::new(),
123 svg_cache: HashMap::new(),
124 size: Size::new(width as f64, height as f64),
125 transform: Affine::scale(scale),
126 capture: false,
127 })
128 }
129
130 pub fn resize(&mut self, width: u32, height: u32, _scale: f64) {
131 let width = width.max(1);
132 let height = height.max(1);
133 self.size = Size::new(width as f64, height as f64);
134 self.window_renderer.set_size(width, height);
135 self.capture_renderer.resize(width, height);
136 }
137
138 pub const fn set_scale(&mut self, _scale: f64) {}
139
140 pub const fn size(&self) -> Size {
141 self.size
142 }
143
144 fn current_scale(&self) -> (f64, f64) {
145 let coeffs = self.transform.as_coeffs();
146 let scale_x = coeffs[0].hypot(coeffs[1]);
147 let scale_y = coeffs[2].hypot(coeffs[3]);
148 (scale_x, scale_y)
149 }
150
151 fn replay<S: anyrender::PaintScene>(commands: &[Command], scene: &mut S) {
152 for command in commands {
153 match command {
154 Command::Stroke {
155 shape,
156 brush,
157 stroke,
158 transform,
159 } => {
160 scene.stroke(
161 stroke,
162 *transform,
163 anyrender::PaintRef::from(BrushRef::from(brush)),
164 None,
165 shape,
166 );
167 }
168 Command::Fill {
169 shape,
170 brush,
171 transform,
172 } => {
173 scene.fill(
174 Fill::NonZero,
175 *transform,
176 anyrender::PaintRef::from(BrushRef::from(brush)),
177 None,
178 shape,
179 );
180 }
181 Command::BoxShadow {
182 rect,
183 brush,
184 radius,
185 std_dev,
186 transform,
187 } => {
188 scene.draw_box_shadow(*transform, *rect, *brush, *radius, *std_dev);
189 }
190 Command::PushLayer {
191 blend,
192 alpha,
193 transform,
194 clip,
195 } => {
196 scene.push_layer(*blend, *alpha, *transform, clip);
197 }
198 Command::PushClip { transform, clip } => {
199 scene.push_clip_layer(*transform, clip);
200 }
201 Command::PopLayer => {
202 scene.pop_layer();
203 }
204 Command::Glyphs {
205 font,
206 font_size,
207 hint,
208 normalized_coords,
209 style,
210 brush,
211 brush_alpha,
212 transform,
213 glyph_transform,
214 glyphs,
215 } => {
216 scene.draw_glyphs(
217 font,
218 *font_size,
219 *hint,
220 normalized_coords,
221 style,
222 anyrender::PaintRef::from(BrushRef::from(brush)),
223 *brush_alpha,
224 *transform,
225 *glyph_transform,
226 glyphs.iter().copied(),
227 );
228 }
229 Command::Image {
230 image,
231 rect,
232 transform,
233 } => {
234 let image_transform = image_transform(*transform, *rect, image);
235 scene.fill(
236 Fill::NonZero,
237 image_transform,
238 image.as_ref(),
239 None,
240 &Rect::new(
241 0.0,
242 0.0,
243 image.image.width as f64,
244 image.image.height as f64,
245 ),
246 );
247 }
248 Command::Svg {
249 image,
250 rect,
251 transform,
252 brush,
253 } => {
254 let image_transform = image_transform(*transform, *rect, image);
255 let src_rect = Rect::new(
256 0.0,
257 0.0,
258 image.image.width as f64,
259 image.image.height as f64,
260 );
261
262 if let Some(brush) = brush {
263 scene.push_layer(
264 peniko::BlendMode::default(),
265 1.0,
266 image_transform,
267 &src_rect,
268 );
269 scene.fill(
270 Fill::NonZero,
271 image_transform,
272 image.as_ref(),
273 None,
274 &src_rect,
275 );
276 scene.push_layer(
277 peniko::BlendMode {
278 mix: Mix::Normal,
279 compose: Compose::SrcIn,
280 },
281 1.0,
282 image_transform,
283 &src_rect,
284 );
285 scene.fill(
286 Fill::NonZero,
287 image_transform,
288 anyrender::PaintRef::from(BrushRef::from(brush)),
289 None,
290 &src_rect,
291 );
292 scene.pop_layer();
293 scene.pop_layer();
294 } else {
295 scene.fill(
296 Fill::NonZero,
297 image_transform,
298 image.as_ref(),
299 None,
300 &src_rect,
301 );
302 }
303 }
304 }
305 }
306 }
307
308 fn cached_svg_image(
309 &mut self,
310 svg: Svg<'_>,
311 width: u32,
312 height: u32,
313 ) -> Result<peniko::ImageBrush> {
314 let key = (svg.hash.to_vec(), width, height);
315 if let Some(image) = self.svg_cache.get(&key) {
316 return Ok(image.clone());
317 }
318
319 let mut pixmap =
320 Pixmap::new(width, height).ok_or_else(|| anyhow!("failed to allocate svg pixmap"))?;
321 let transform = Transform::from_scale(
322 width as f32 / svg.tree.size().width(),
323 height as f32 / svg.tree.size().height(),
324 );
325 resvg::render(svg.tree, transform, &mut pixmap.as_mut());
326 let image = image_brush_from_rgba(width, height, pixmap.take());
327 self.svg_cache.insert(key, image.clone());
328 Ok(image)
329 }
330}
331
332impl Renderer for SkiaRenderer {
333 fn begin(&mut self, capture: bool) {
334 self.commands.clear();
335 self.transform = Affine::IDENTITY;
336 self.capture = capture;
337 }
338
339 fn set_transform(&mut self, transform: Affine) {
340 self.transform = transform;
341 }
342
343 fn set_z_index(&mut self, _z_index: i32) {}
344
345 fn clip(&mut self, shape: &impl Shape) {
346 self.commands.push(Command::PushClip {
347 transform: self.transform,
348 clip: shape.to_path(PATH_TOLERANCE),
349 });
350 }
351
352 fn clear_clip(&mut self) {
353 self.commands.push(Command::PopLayer);
354 }
355
356 fn stroke<'b, 's>(
357 &mut self,
358 shape: &impl Shape,
359 brush: impl Into<BrushRef<'b>>,
360 stroke: &'s Stroke,
361 ) {
362 self.commands.push(Command::Stroke {
363 shape: shape.to_path(PATH_TOLERANCE),
364 brush: brush.into().to_owned(),
365 stroke: stroke.clone(),
366 transform: self.transform,
367 });
368 }
369
370 fn fill<'b>(&mut self, path: &impl Shape, brush: impl Into<BrushRef<'b>>, blur_radius: f64) {
371 let brush = brush.into();
372
373 if blur_radius > 0.0
374 && let BrushRef::Solid(color) = brush
375 {
376 if let Some(rounded) = path.as_rounded_rect() {
377 let radii = rounded.radii();
378 if radii.top_left == radii.top_right
379 && radii.top_left == radii.bottom_left
380 && radii.top_left == radii.bottom_right
381 {
382 self.commands.push(Command::BoxShadow {
383 rect: rounded.rect(),
384 brush: color,
385 radius: radii.top_left,
386 std_dev: blur_radius,
387 transform: self.transform,
388 });
389 return;
390 }
391 } else if let Some(rect) = path.as_rect() {
392 self.commands.push(Command::BoxShadow {
393 rect,
394 brush: color,
395 radius: 0.0,
396 std_dev: blur_radius,
397 transform: self.transform,
398 });
399 return;
400 }
401 }
402
403 self.commands.push(Command::Fill {
404 shape: path.to_path(PATH_TOLERANCE),
405 brush: brush.to_owned(),
406 transform: self.transform,
407 });
408 }
409
410 fn push_layer(
411 &mut self,
412 blend: impl Into<peniko::BlendMode>,
413 alpha: f32,
414 transform: Affine,
415 clip: &impl Shape,
416 ) {
417 self.commands.push(Command::PushLayer {
418 blend: blend.into(),
419 alpha,
420 transform: self.transform * transform,
421 clip: clip.to_path(PATH_TOLERANCE),
422 });
423 }
424
425 fn pop_layer(&mut self) {
426 self.commands.push(Command::PopLayer);
427 }
428
429 fn draw_glyphs<'a>(
430 &mut self,
431 origin: Point,
432 props: &GlyphRunProps<'a>,
433 glyphs: impl Iterator<Item = Glyph> + 'a,
434 ) {
435 let transform = self.transform * Affine::translate((origin.x, origin.y)) * props.transform;
436 self.commands.push(Command::Glyphs {
437 font: props.font.clone(),
438 font_size: props.font_size,
439 hint: props.hint,
440 normalized_coords: props.normalized_coords.to_vec(),
441 style: props.style.to_owned(),
442 brush: props.brush.to_owned(),
443 brush_alpha: props.brush_alpha,
444 transform,
445 glyph_transform: props.glyph_transform,
446 glyphs: glyphs
447 .map(|glyph| anyrender::Glyph {
448 id: glyph.id,
449 x: glyph.x,
450 y: glyph.y,
451 })
452 .collect(),
453 });
454 }
455
456 fn draw_svg<'b>(&mut self, svg: Svg<'b>, rect: Rect, brush: Option<impl Into<BrushRef<'b>>>) {
457 let (scale_x, scale_y) = self.current_scale();
458 let width = (rect.width() * scale_x.abs()).round().max(1.0) as u32;
459 let height = (rect.height() * scale_y.abs()).round().max(1.0) as u32;
460 let image = match self.cached_svg_image(svg, width, height) {
461 Ok(image) => image,
462 Err(_) => return,
463 };
464
465 self.commands.push(Command::Svg {
466 image,
467 rect,
468 transform: self.transform,
469 brush: brush.map(|brush| brush.into().to_owned()),
470 });
471 }
472
473 fn draw_img(&mut self, img: Img<'_>, rect: Rect) {
474 self.commands.push(Command::Image {
475 image: img.img,
476 rect,
477 transform: self.transform,
478 });
479 }
480
481 fn finish(&mut self) -> Option<peniko::ImageBrush> {
482 if self.capture {
483 let commands = std::mem::take(&mut self.commands);
484 let mut buffer = vec![0; self.size.width as usize * self.size.height as usize * 4];
485 self.capture_renderer.reset();
486 self.capture_renderer
487 .render(|scene| Self::replay(&commands, scene), &mut buffer);
488 return Some(image_brush_from_rgba(
489 self.size.width as u32,
490 self.size.height as u32,
491 buffer,
492 ));
493 }
494
495 let commands = std::mem::take(&mut self.commands);
496 self.window_renderer
497 .render(|scene| Self::replay(&commands, scene));
498 None
499 }
500
501 fn debug_info(&self) -> String {
502 "name: Skia\ninfo: AnyRender Skia".to_string()
503 }
504}
505
506fn image_transform(transform: Affine, rect: Rect, image: &peniko::ImageBrush) -> Affine {
507 transform
508 .pre_scale_non_uniform(
509 rect.width().max(1.0) / image.image.width as f64,
510 rect.height().max(1.0) / image.image.height as f64,
511 )
512 .pre_translate((rect.min_x(), rect.min_y()).into())
513}
514
515fn image_brush_from_rgba(width: u32, height: u32, data: Vec<u8>) -> peniko::ImageBrush {
516 peniko::ImageBrush::new(ImageData {
517 data: Blob::new(Arc::new(data)),
518 format: ImageFormat::Rgba8,
519 alpha_type: ImageAlphaType::AlphaPremultiplied,
520 width,
521 height,
522 })
523}