1use anyhow::{anyhow, Result};
2use floem_renderer::swash::SwashScaler;
3use floem_renderer::text::{CacheKey, LayoutRun, SwashContent};
4use floem_renderer::tiny_skia::{
5 self, FillRule, FilterQuality, GradientStop, LinearGradient, Mask, MaskType, Paint, Path,
6 PathBuilder, Pattern, Pixmap, RadialGradient, Shader, SpreadMode, Stroke, Transform,
7};
8use floem_renderer::Img;
9use floem_renderer::Renderer;
10use peniko::kurbo::{self, PathEl, Size, Vec2};
11use peniko::{
12 color::palette,
13 kurbo::{Affine, Point, Rect, Shape},
14 BrushRef, Color, GradientKind,
15};
16use softbuffer::{Context, Surface};
17use std::collections::HashMap;
18use std::num::NonZeroU32;
19use std::rc::Rc;
20use tiny_skia::{LineCap, LineJoin};
21
22macro_rules! try_ret {
23 ($e:expr) => {
24 if let Some(e) = $e {
25 e
26 } else {
27 return;
28 }
29 };
30}
31
32struct Glyph {
33 pixmap: Pixmap,
34 left: f32,
35 top: f32,
36}
37
38#[derive(PartialEq, Clone, Copy)]
39struct CacheColor(bool);
40
41pub struct TinySkiaRenderer<W> {
42 #[allow(unused)]
43 context: Context<W>,
44 surface: Surface<W, W>,
45 pixmap: Pixmap,
46 mask: Mask,
47 scale: f64,
48 transform: Affine,
49 clip: Option<Rect>,
50
51 cache_color: CacheColor,
53
54 image_cache: HashMap<Vec<u8>, (CacheColor, Rc<Pixmap>)>,
55 #[allow(clippy::type_complexity)]
57 glyph_cache: HashMap<(CacheKey, u32), (CacheColor, Option<Rc<Glyph>>)>,
58 swash_scaler: SwashScaler,
59}
60
61impl<W: raw_window_handle::HasWindowHandle + raw_window_handle::HasDisplayHandle>
62 TinySkiaRenderer<W>
63{
64 pub fn new(window: W, width: u32, height: u32, scale: f64, font_embolden: f32) -> Result<Self>
65 where
66 W: Clone,
67 {
68 let context = Context::new(window.clone())
69 .map_err(|err| anyhow!("unable to create context: {}", err))?;
70 let mut surface = Surface::new(&context, window)
71 .map_err(|err| anyhow!("unable to create surface: {}", err))?;
72 surface
73 .resize(
74 NonZeroU32::new(width).unwrap_or(NonZeroU32::new(1).unwrap()),
75 NonZeroU32::new(height).unwrap_or(NonZeroU32::new(1).unwrap()),
76 )
77 .map_err(|_| anyhow!("failed to resize surface"))?;
78
79 let pixmap =
80 Pixmap::new(width, height).ok_or_else(|| anyhow!("unable to create pixmap"))?;
81
82 let mask = Mask::new(width, height).ok_or_else(|| anyhow!("unable to create mask"))?;
83
84 Ok(Self {
85 context,
86 surface,
87 pixmap,
88 mask,
89 scale,
90 transform: Affine::IDENTITY,
91 clip: None,
92 cache_color: CacheColor(false),
93 image_cache: Default::default(),
94 glyph_cache: Default::default(),
95 swash_scaler: SwashScaler::new(font_embolden),
96 })
97 }
98
99 pub fn resize(&mut self, width: u32, height: u32, scale: f64) {
100 if width != self.pixmap.width() || height != self.pixmap.width() {
101 self.surface
102 .resize(
103 NonZeroU32::new(width).unwrap_or(NonZeroU32::new(1).unwrap()),
104 NonZeroU32::new(height).unwrap_or(NonZeroU32::new(1).unwrap()),
105 )
106 .expect("failed to resize surface");
107 self.pixmap = Pixmap::new(width, height).expect("unable to create pixmap");
108 self.mask = Mask::new(width, height).expect("unable to create mask");
109 }
110 self.scale = scale;
111 }
112
113 pub fn set_scale(&mut self, scale: f64) {
114 self.scale = scale;
115 }
116
117 pub const fn scale(&self) -> f64 {
118 self.scale
119 }
120
121 pub fn size(&self) -> Size {
122 Size::new(self.pixmap.width() as f64, self.pixmap.height() as f64)
123 }
124}
125
126fn to_color(color: Color) -> tiny_skia::Color {
127 let c = color.to_rgba8();
128 tiny_skia::Color::from_rgba8(c.r, c.g, c.b, c.a)
129}
130
131fn to_point(point: Point) -> tiny_skia::Point {
132 tiny_skia::Point::from_xy(point.x as f32, point.y as f32)
133}
134
135impl<W> TinySkiaRenderer<W> {
136 fn shape_to_path(&self, shape: &impl Shape) -> Option<Path> {
137 let mut builder = PathBuilder::new();
138 for element in shape.path_elements(0.1) {
139 match element {
140 PathEl::ClosePath => builder.close(),
141 PathEl::MoveTo(p) => builder.move_to(p.x as f32, p.y as f32),
142 PathEl::LineTo(p) => builder.line_to(p.x as f32, p.y as f32),
143 PathEl::QuadTo(p1, p2) => {
144 builder.quad_to(p1.x as f32, p1.y as f32, p2.x as f32, p2.y as f32)
145 }
146 PathEl::CurveTo(p1, p2, p3) => builder.cubic_to(
147 p1.x as f32,
148 p1.y as f32,
149 p2.x as f32,
150 p2.y as f32,
151 p3.x as f32,
152 p3.y as f32,
153 ),
154 }
155 }
156 builder.finish()
157 }
158
159 fn brush_to_paint<'b>(&self, brush: impl Into<BrushRef<'b>>) -> Option<Paint<'static>> {
160 let shader = match brush.into() {
161 BrushRef::Solid(c) => Shader::SolidColor(to_color(c)),
162 BrushRef::Gradient(g) => {
163 let stops = g
164 .stops
165 .iter()
166 .map(|s| GradientStop::new(s.offset, to_color(s.color.to_alpha_color())))
167 .collect();
168 match g.kind {
169 GradientKind::Linear { start, end } => LinearGradient::new(
170 to_point(start),
171 to_point(end),
172 stops,
173 SpreadMode::Pad,
174 Transform::identity(),
175 )?,
176 GradientKind::Radial {
177 start_center,
178 start_radius: _,
179 end_center,
180 end_radius,
181 } => {
182 RadialGradient::new(
184 to_point(start_center),
185 to_point(end_center),
186 end_radius,
187 stops,
188 SpreadMode::Pad,
189 Transform::identity(),
190 )?
191 }
192 GradientKind::Sweep { .. } => return None,
193 }
194 }
195 BrushRef::Image(_) => return None,
196 };
197 Some(Paint {
198 shader,
199 ..Default::default()
200 })
201 }
202
203 fn rect(&self, rect: Rect) -> Option<tiny_skia::Rect> {
206 tiny_skia::Rect::from_ltrb(
207 rect.x0 as f32,
208 rect.y0 as f32,
209 rect.x1 as f32,
210 rect.y1 as f32,
211 )
212 }
213
214 fn clip_rect(&self, rect: tiny_skia::Rect) -> Option<tiny_skia::Rect> {
215 let clip = if let Some(clip) = self.clip {
216 clip
217 } else {
218 return Some(rect);
219 };
220 let clip = self.rect(clip.scale_from_origin(self.scale))?;
221 clip.intersect(&rect)
222 }
223
224 fn render_pixmap_direct(&mut self, pixmap: &Pixmap, x: f32, y: f32, transform: kurbo::Affine) {
226 let rect = try_ret!(tiny_skia::Rect::from_xywh(
227 x,
228 y,
229 pixmap.width() as f32,
230 pixmap.height() as f32,
231 ));
232 let paint = Paint {
233 shader: Pattern::new(
234 pixmap.as_ref(),
235 SpreadMode::Pad,
236 FilterQuality::Nearest,
237 1.0,
238 Transform::from_translate(x, y),
239 ),
240 ..Default::default()
241 };
242
243 let transform = transform.as_coeffs();
244 let scale = self.scale as f32;
245 let transform = Transform::from_row(
246 transform[0] as f32,
247 transform[1] as f32,
248 transform[2] as f32,
249 transform[3] as f32,
250 transform[4] as f32,
251 transform[5] as f32,
252 )
253 .post_scale(scale, scale);
254 if let Some(rect) = self.clip_rect(rect) {
255 self.pixmap.fill_rect(rect, &paint, transform, None);
256 }
257 }
258
259 fn render_pixmap_rect(&mut self, pixmap: &Pixmap, rect: tiny_skia::Rect) {
260 let paint = Paint {
261 shader: Pattern::new(
262 pixmap.as_ref(),
263 SpreadMode::Pad,
264 FilterQuality::Bilinear,
265 1.0,
266 Transform::from_scale(
267 rect.width() / pixmap.width() as f32,
268 rect.height() / pixmap.height() as f32,
269 ),
270 ),
271 ..Default::default()
272 };
273
274 self.pixmap.fill_rect(
275 rect,
276 &paint,
277 self.current_transform(),
278 self.clip.is_some().then_some(&self.mask),
279 );
280 }
281
282 fn render_pixmap_paint(
283 &mut self,
284 pixmap: &Pixmap,
285 rect: tiny_skia::Rect,
286 paint: Option<Paint<'static>>,
287 ) {
288 let paint = if let Some(paint) = paint {
289 paint
290 } else {
291 return self.render_pixmap_rect(pixmap, rect);
292 };
293
294 let mut fill = try_ret!(Pixmap::new(pixmap.width(), pixmap.height()));
295 fill.fill_rect(
296 try_ret!(tiny_skia::Rect::from_xywh(
297 0.0,
298 0.0,
299 pixmap.width() as f32,
300 pixmap.height() as f32
301 )),
302 &paint,
303 Transform::identity(),
304 None,
305 );
306
307 let mask = Mask::from_pixmap(pixmap.as_ref(), MaskType::Alpha);
308 fill.apply_mask(&mask);
309
310 self.render_pixmap_rect(&fill, rect);
311 }
312
313 fn current_transform(&self) -> Transform {
314 let transform = self.transform.as_coeffs();
315 let scale = self.scale as f32;
316 Transform::from_row(
317 transform[0] as f32,
318 transform[1] as f32,
319 transform[2] as f32,
320 transform[3] as f32,
321 transform[4] as f32,
322 transform[5] as f32,
323 )
324 .post_scale(scale, scale)
325 }
326
327 fn cache_glyph(&mut self, cache_key: CacheKey, color: Color) -> Option<Rc<Glyph>> {
328 let c = color.to_rgba8();
329 if let Some((color, glyph)) = self.glyph_cache.get_mut(&(cache_key, c.to_u32())) {
330 *color = self.cache_color;
331 return glyph.clone();
332 }
333
334 let image = self.swash_scaler.get_image(cache_key)?;
335
336 let result = if image.placement.width == 0 || image.placement.height == 0 {
337 None
339 } else {
340 let mut pixmap = Pixmap::new(image.placement.width, image.placement.height)?;
341
342 if image.content == SwashContent::Mask {
343 for (a, &alpha) in pixmap.pixels_mut().iter_mut().zip(image.data.iter()) {
344 *a = tiny_skia::Color::from_rgba8(c.r, c.g, c.b, alpha)
345 .premultiply()
346 .to_color_u8();
347 }
348 } else if image.content == SwashContent::Color {
349 for (a, b) in pixmap.pixels_mut().iter_mut().zip(image.data.chunks(4)) {
350 *a = tiny_skia::Color::from_rgba8(b[0], b[1], b[2], b[3])
351 .premultiply()
352 .to_color_u8();
353 }
354 } else {
355 return None;
356 }
357
358 Some(Rc::new(Glyph {
359 pixmap,
360 left: image.placement.left as f32,
361 top: image.placement.top as f32,
362 }))
363 };
364
365 self.glyph_cache
366 .insert((cache_key, c.to_u32()), (self.cache_color, result.clone()));
367
368 result
369 }
370}
371
372impl<W: raw_window_handle::HasWindowHandle + raw_window_handle::HasDisplayHandle> Renderer
373 for TinySkiaRenderer<W>
374{
375 fn begin(&mut self, _capture: bool) {
376 self.transform = Affine::IDENTITY;
377 self.pixmap.fill(tiny_skia::Color::WHITE);
378 self.clip = None;
379 }
380
381 fn stroke<'b, 's>(
382 &mut self,
383 shape: &impl Shape,
384 brush: impl Into<BrushRef<'b>>,
385 stroke: &'s peniko::kurbo::Stroke,
386 ) {
387 let paint = try_ret!(self.brush_to_paint(brush));
388 let path = try_ret!(self.shape_to_path(shape));
389 let line_cap = match stroke.end_cap {
390 peniko::kurbo::Cap::Butt => LineCap::Butt,
391 peniko::kurbo::Cap::Square => LineCap::Square,
392 peniko::kurbo::Cap::Round => LineCap::Round,
393 };
394 let line_join = match stroke.join {
395 peniko::kurbo::Join::Bevel => LineJoin::Bevel,
396 peniko::kurbo::Join::Miter => LineJoin::Miter,
397 peniko::kurbo::Join::Round => LineJoin::Round,
398 };
399 let stroke = Stroke {
401 width: stroke.width as f32,
402 miter_limit: stroke.miter_limit as f32,
403 line_cap,
404 line_join,
405 dash: None,
406 };
407 self.pixmap.stroke_path(
408 &path,
409 &paint,
410 &stroke,
411 self.current_transform(),
412 self.clip.is_some().then_some(&self.mask),
413 );
414 }
415
416 fn fill<'b>(&mut self, shape: &impl Shape, brush: impl Into<BrushRef<'b>>, _blur_radius: f64) {
417 let paint = try_ret!(self.brush_to_paint(brush));
420 if let Some(rect) = shape.as_rect() {
421 let rect = try_ret!(self.rect(rect));
422 self.pixmap
423 .fill_rect(rect, &paint, self.current_transform(), None);
424 } else {
425 let path = try_ret!(self.shape_to_path(shape));
426 self.pixmap.fill_path(
427 &path,
428 &paint,
429 FillRule::Winding,
430 self.current_transform(),
431 self.clip.is_some().then_some(&self.mask),
432 );
433 }
434 }
435
436 fn draw_text_with_layout<'b>(
437 &mut self,
438 layout: impl Iterator<Item = LayoutRun<'b>>,
439 pos: impl Into<Point>,
440 ) {
441 let offset = self.transform.translation();
442 let pos: Point = pos.into();
443 let clip = self.clip;
444
445 let transform = self.transform
446 * Affine::translate(Vec2::new(-offset.x, -offset.y))
447 * Affine::scale(1.0 / self.scale);
448
449 for line in layout {
450 if let Some(rect) = clip {
451 let y = pos.y + offset.y + line.line_y as f64;
452 if y + (line.line_height as f64) < rect.y0 {
453 continue;
454 }
455 if y - (line.line_height as f64) > rect.y1 {
456 break;
457 }
458 }
459 'line_loop: for glyph_run in line.glyphs {
460 let x = glyph_run.x + pos.x as f32 + offset.x as f32;
461 let y = line.line_y + pos.y as f32 + offset.y as f32;
462 if let Some(rect) = clip {
463 if ((x + glyph_run.w) as f64) < rect.x0 {
464 continue;
465 } else if x as f64 > rect.x1 {
466 break 'line_loop;
467 }
468 }
469
470 let glyph_x = x * self.scale as f32;
471 let glyph_y = (y * self.scale as f32).round();
472 let font_size = (glyph_run.font_size * self.scale as f32).round() as u32;
473
474 let (cache_key, new_x, new_y) = CacheKey::new(
475 glyph_run.font_id,
476 glyph_run.glyph_id,
477 font_size as f32,
478 (glyph_x, glyph_y),
479 glyph_run.cache_key_flags,
480 );
481
482 let color = glyph_run.color_opt.map_or(palette::css::BLACK, |c| {
483 Color::from_rgba8(c.r(), c.g(), c.b(), c.a())
484 });
485
486 let pixmap = self.cache_glyph(cache_key, color);
487 if let Some(glyph) = pixmap {
488 self.render_pixmap_direct(
489 &glyph.pixmap,
490 new_x as f32 + glyph.left,
491 new_y as f32 - glyph.top,
492 transform,
493 );
494 }
495 }
496 }
497 }
498
499 fn draw_img(&mut self, img: Img<'_>, rect: Rect) {
500 let rect = try_ret!(self.rect(rect));
501 if let Some((color, pixmap)) = self.image_cache.get_mut(img.hash) {
502 *color = self.cache_color;
503 let pixmap = pixmap.clone();
504 self.render_pixmap_rect(&pixmap, rect);
505 return;
506 }
507
508 let image_data = img.img.data.data();
509 let mut pixmap = try_ret!(Pixmap::new(img.img.width, img.img.height));
510 for (a, b) in pixmap
511 .pixels_mut()
512 .iter_mut()
513 .zip(image_data.chunks_exact(4))
514 {
515 *a = tiny_skia::Color::from_rgba8(b[0], b[1], b[2], b[3])
516 .premultiply()
517 .to_color_u8();
518 }
519
520 self.render_pixmap_rect(&pixmap, rect);
521
522 self.image_cache
523 .insert(img.hash.to_owned(), (self.cache_color, Rc::new(pixmap)));
524 }
525
526 fn draw_svg<'b>(
527 &mut self,
528 svg: floem_renderer::Svg<'b>,
529 rect: Rect,
530 brush: Option<impl Into<BrushRef<'b>>>,
531 ) {
532 let width = (rect.width() * self.scale).round() as u32;
533 let height = (rect.height() * self.scale).round() as u32;
534
535 let rect = try_ret!(self.rect(rect));
536
537 let paint = brush.and_then(|brush| self.brush_to_paint(brush));
538
539 if let Some((color, pixmap)) = self.image_cache.get_mut(svg.hash) {
540 *color = self.cache_color;
541 let pixmap = pixmap.clone();
542 self.render_pixmap_paint(&pixmap, rect, paint);
543 return;
544 }
545
546 let mut pixmap = try_ret!(tiny_skia::Pixmap::new(width, height));
547 let svg_transform = tiny_skia::Transform::from_scale(
549 width as f32 / svg.tree.size().width(),
550 height as f32 / svg.tree.size().height(),
551 );
552 resvg::render(svg.tree, svg_transform, &mut pixmap.as_mut());
553
554 self.render_pixmap_paint(&pixmap, rect, paint);
555
556 self.image_cache
557 .insert(svg.hash.to_owned(), (self.cache_color, Rc::new(pixmap)));
558 }
559
560 fn transform(&mut self, transform: Affine) {
561 self.transform = transform;
562 }
563
564 fn set_z_index(&mut self, _z_index: i32) {
565 }
567
568 fn clip(&mut self, shape: &impl Shape) {
569 let rect = if let Some(rect) = shape.as_rect() {
570 rect
571 } else if let Some(rect) = shape.as_rounded_rect() {
572 rect.rect()
573 } else {
574 shape.bounding_box()
575 };
576
577 let p0 = self.transform * Point::new(rect.x0, rect.y0);
578 let p1 = self.transform * Point::new(rect.x1, rect.y0);
579 let p2 = self.transform * Point::new(rect.x0, rect.y1);
580 let p3 = self.transform * Point::new(rect.x1, rect.y1);
581 let x0 = p0.x.min(p1.x).min(p2.x).min(p3.x);
583 let y0 = p0.y.min(p1.y).min(p2.y).min(p3.y);
584 let x1 = p0.x.max(p1.x).max(p2.x).max(p3.x);
585 let y1 = p0.y.max(p1.y).max(p2.y).max(p3.y);
586
587 self.clip = Some(Rect::new(x0, y0, x1, y1));
588
589 self.mask.clear();
590 let path = try_ret!(self.shape_to_path(shape));
591 self.mask
592 .fill_path(&path, FillRule::Winding, false, self.current_transform());
593 }
594
595 fn clear_clip(&mut self) {
596 self.clip = None;
597 }
598
599 fn finish(&mut self) -> Option<peniko::Image> {
600 self.image_cache.retain(|_, (c, _)| *c == self.cache_color);
602 self.glyph_cache.retain(|_, (c, _)| *c == self.cache_color);
603
604 self.cache_color = CacheColor(!self.cache_color.0);
606
607 let mut buffer = self
608 .surface
609 .buffer_mut()
610 .expect("failed to get the surface buffer");
611
612 for (out_pixel, pixel) in (buffer.iter_mut()).zip(self.pixmap.pixels().iter()) {
614 *out_pixel =
615 (pixel.red() as u32) << 16 | (pixel.green() as u32) << 8 | (pixel.blue() as u32);
616 }
617
618 buffer
619 .present()
620 .expect("failed to present the surface buffer");
621
622 None
623 }
624}