floem_tiny_skia_renderer/
lib.rs

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    /// The cache color value set for cache entries accessed this frame.
52    cache_color: CacheColor,
53
54    image_cache: HashMap<Vec<u8>, (CacheColor, Rc<Pixmap>)>,
55    // The `u32` is a color encoded as a u32 so that it is hashable and eq.
56    #[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                        // FIXME: Doesn't use `start_radius`
183                        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    /// Transform a `Rect`, applying `self.transform`, into a `tiny_skia::Rect` and
204    /// residual transform.
205    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    /// Renders the pixmap at the position and transforms it with the given transform.
225    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            // We can't create an empty `Pixmap`
338            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        // TODO: Finish dash
400        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        // FIXME: Handle _blur_radius
418
419        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 rtree = resvg::Tree::from_usvg(svg.tree);
548        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        // FIXME: Remove this method?
566    }
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        // Find the bounding box of transformed points
582        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        // Remove cache entries which were not accessed.
601        self.image_cache.retain(|_, (c, _)| *c == self.cache_color);
602        self.glyph_cache.retain(|_, (c, _)| *c == self.cache_color);
603
604        // Swap the cache color.
605        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        // Copy from `tiny_skia::Pixmap` to the format specified by `softbuffer::Buffer`.
613        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}