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::{PathEl, Size};
11use peniko::{
12    color::palette,
13    kurbo::{Affine, Point, Rect, Shape},
14    BrushRef, Color, GradientKind,
15};
16use peniko::{BlendMode, Compose, Mix};
17use resvg::tiny_skia::StrokeDash;
18use softbuffer::{Context, Surface};
19use std::cell::RefCell;
20use std::collections::HashMap;
21use std::num::NonZeroU32;
22use std::rc::Rc;
23use tiny_skia::{LineCap, LineJoin};
24
25thread_local! {
26    #[allow(clippy::type_complexity)]
27    static IMAGE_CACHE: RefCell<HashMap<Vec<u8>, (CacheColor, Rc<Pixmap>)>> = RefCell::new(HashMap::new());
28    #[allow(clippy::type_complexity)]
29    // The `u32` is a color encoded as a u32 so that it is hashable and eq.
30    static GLYPH_CACHE: RefCell<HashMap<(CacheKey, u32), (CacheColor, Option<Rc<Glyph>>)>> = RefCell::new(HashMap::new());
31    static SWASH_SCALER: RefCell<SwashScaler> = RefCell::new(SwashScaler::default());
32}
33
34fn cache_glyph(cache_color: CacheColor, cache_key: CacheKey, color: Color) -> Option<Rc<Glyph>> {
35    let c = color.to_rgba8();
36
37    if let Some(opt_glyph) = GLYPH_CACHE.with_borrow_mut(|gc| {
38        if let Some((color, glyph)) = gc.get_mut(&(cache_key, c.to_u32())) {
39            *color = cache_color;
40            Some(glyph.clone())
41        } else {
42            None
43        }
44    }) {
45        return opt_glyph;
46    };
47
48    let image = SWASH_SCALER.with_borrow_mut(|s| s.get_image(cache_key))?;
49
50    let result = if image.placement.width == 0 || image.placement.height == 0 {
51        // We can't create an empty `Pixmap`
52        None
53    } else {
54        let mut pixmap = Pixmap::new(image.placement.width, image.placement.height)?;
55
56        if image.content == SwashContent::Mask {
57            for (a, &alpha) in pixmap.pixels_mut().iter_mut().zip(image.data.iter()) {
58                *a = tiny_skia::Color::from_rgba8(c.r, c.g, c.b, alpha)
59                    .premultiply()
60                    .to_color_u8();
61            }
62        } else if image.content == SwashContent::Color {
63            for (a, b) in pixmap.pixels_mut().iter_mut().zip(image.data.chunks(4)) {
64                *a = tiny_skia::Color::from_rgba8(b[0], b[1], b[2], b[3])
65                    .premultiply()
66                    .to_color_u8();
67            }
68        } else {
69            return None;
70        }
71
72        Some(Rc::new(Glyph {
73            pixmap,
74            left: image.placement.left as f32,
75            top: image.placement.top as f32,
76        }))
77    };
78
79    GLYPH_CACHE
80        .with_borrow_mut(|gc| gc.insert((cache_key, c.to_u32()), (cache_color, result.clone())));
81
82    result
83}
84
85macro_rules! try_ret {
86    ($e:expr) => {
87        if let Some(e) = $e {
88            e
89        } else {
90            return;
91        }
92    };
93}
94
95struct Glyph {
96    pixmap: Pixmap,
97    left: f32,
98    top: f32,
99}
100
101#[derive(PartialEq, Clone, Copy)]
102struct CacheColor(bool);
103
104struct Layer {
105    pixmap: Pixmap,
106    /// clip is stored with the transform at the time clip is called
107    clip: Option<Rect>,
108    mask: Mask,
109    /// this transform should generally only be used when making a draw call to skia
110    transform: Affine,
111    // the transform that the layer was pushed with that will be used when applying the layer
112    combine_transform: Affine,
113    blend_mode: BlendMode,
114    alpha: f32,
115    window_scale: f64,
116    cache_color: CacheColor,
117}
118impl Layer {
119    /// the img_rect should already be in the correct transformed space along with the window_scale applied
120    fn clip_rect(&self, img_rect: Rect) -> Option<tiny_skia::Rect> {
121        if let Some(clip) = self.clip {
122            let clip = clip.intersect(img_rect);
123            to_skia_rect(clip)
124        } else {
125            to_skia_rect(img_rect)
126        }
127    }
128
129    /// Renders the pixmap at the position and transforms it with the given transform.
130    /// x and y should have already been scaled by the window scale
131    fn render_pixmap_direct(&mut self, img_pixmap: &Pixmap, x: f32, y: f32, transform: Affine) {
132        let img_rect = Rect::from_origin_size(
133            (x, y),
134            (img_pixmap.width() as f64, img_pixmap.height() as f64),
135        );
136        let paint = Paint {
137            shader: Pattern::new(
138                img_pixmap.as_ref(),
139                SpreadMode::Pad,
140                FilterQuality::Nearest,
141                1.0,
142                Transform::from_translate(x, y),
143            ),
144            ..Default::default()
145        };
146
147        let transform = transform.as_coeffs();
148        let transform = Transform::from_row(
149            transform[0] as f32,
150            transform[1] as f32,
151            transform[2] as f32,
152            transform[3] as f32,
153            transform[4] as f32,
154            transform[5] as f32,
155        );
156        if let Some(rect) = self.clip_rect(img_rect) {
157            self.pixmap.fill_rect(rect, &paint, transform, None);
158        }
159    }
160
161    fn render_pixmap_rect(&mut self, pixmap: &Pixmap, rect: tiny_skia::Rect) {
162        let paint = Paint {
163            shader: Pattern::new(
164                pixmap.as_ref(),
165                SpreadMode::Pad,
166                FilterQuality::Bilinear,
167                1.0,
168                Transform::from_scale(
169                    rect.width() / pixmap.width() as f32,
170                    rect.height() / pixmap.height() as f32,
171                ),
172            ),
173            ..Default::default()
174        };
175
176        self.pixmap.fill_rect(
177            rect,
178            &paint,
179            self.skia_transform(),
180            self.clip.is_some().then_some(&self.mask),
181        );
182    }
183
184    fn render_pixmap_with_paint(
185        &mut self,
186        pixmap: &Pixmap,
187        rect: tiny_skia::Rect,
188        paint: Option<Paint<'static>>,
189    ) {
190        let paint = if let Some(paint) = paint {
191            paint
192        } else {
193            return self.render_pixmap_rect(pixmap, rect);
194        };
195
196        let mut colored_bg = try_ret!(Pixmap::new(pixmap.width(), pixmap.height()));
197        colored_bg.fill_rect(
198            try_ret!(tiny_skia::Rect::from_xywh(
199                0.0,
200                0.0,
201                pixmap.width() as f32,
202                pixmap.height() as f32
203            )),
204            &paint,
205            Transform::identity(),
206            None,
207        );
208
209        let mask = Mask::from_pixmap(pixmap.as_ref(), MaskType::Alpha);
210        colored_bg.apply_mask(&mask);
211
212        self.render_pixmap_rect(&colored_bg, rect);
213    }
214
215    fn skia_transform(&self) -> Transform {
216        skia_transform(self.transform, self.window_scale as f32)
217    }
218}
219impl Layer {
220    /// The combine transform should be the transform that the layer is pushed with without combining with the previous transform. It will be used when combining layers to offset/transform this layer into the parent with the parent transform
221    fn new(
222        blend: impl Into<peniko::BlendMode>,
223        alpha: f32,
224        combine_transform: Affine,
225        clip: &impl Shape,
226        window_scale: f64,
227        cache_color: CacheColor,
228    ) -> Result<Self, anyhow::Error> {
229        let transform = Affine::IDENTITY;
230        let bbox = clip.bounding_box();
231        let scaled_box = Affine::scale(window_scale).transform_rect_bbox(bbox);
232        let width = scaled_box.width() as u32;
233        let height = scaled_box.height() as u32;
234        let mut mask = Mask::new(width, height).ok_or_else(|| anyhow!("unable to create mask"))?;
235        mask.fill_path(
236            &shape_to_path(clip).ok_or_else(|| anyhow!("unable to create clip shape"))?,
237            FillRule::Winding,
238            false,
239            Transform::from_scale(window_scale as f32, window_scale as f32),
240        );
241        Ok(Self {
242            pixmap: Pixmap::new(width, height).ok_or_else(|| anyhow!("unable to create pixmap"))?,
243            mask,
244            clip: Some(bbox),
245            transform,
246            combine_transform,
247            blend_mode: blend.into(),
248            alpha,
249            window_scale,
250            cache_color,
251        })
252    }
253
254    fn transform(&mut self, transform: Affine) {
255        self.transform *= transform;
256    }
257
258    fn clip(&mut self, shape: &impl Shape) {
259        self.clip = Some(
260            self.transform
261                .then_scale(self.window_scale)
262                .transform_rect_bbox(shape.bounding_box()),
263        );
264        let path = try_ret!(shape_to_path(shape));
265        self.mask.clear();
266        self.mask
267            .fill_path(&path, FillRule::Winding, false, self.skia_transform());
268    }
269
270    fn clear_clip(&mut self) {
271        self.clip = None;
272    }
273
274    fn stroke<'b, 's>(
275        &mut self,
276        shape: &impl Shape,
277        brush: impl Into<BrushRef<'b>>,
278        stroke: &'s peniko::kurbo::Stroke,
279    ) {
280        let paint = try_ret!(brush_to_paint(brush));
281        let path = try_ret!(shape_to_path(shape));
282        let line_cap = match stroke.end_cap {
283            peniko::kurbo::Cap::Butt => LineCap::Butt,
284            peniko::kurbo::Cap::Square => LineCap::Square,
285            peniko::kurbo::Cap::Round => LineCap::Round,
286        };
287        let line_join = match stroke.join {
288            peniko::kurbo::Join::Bevel => LineJoin::Bevel,
289            peniko::kurbo::Join::Miter => LineJoin::Miter,
290            peniko::kurbo::Join::Round => LineJoin::Round,
291        };
292        let stroke = Stroke {
293            width: stroke.width as f32,
294            miter_limit: stroke.miter_limit as f32,
295            line_cap,
296            line_join,
297            dash: (!stroke.dash_pattern.is_empty())
298                .then_some(StrokeDash::new(
299                    stroke.dash_pattern.iter().map(|v| *v as f32).collect(),
300                    stroke.dash_offset as f32,
301                ))
302                .flatten(),
303        };
304        self.pixmap.stroke_path(
305            &path,
306            &paint,
307            &stroke,
308            self.skia_transform(),
309            self.clip.is_some().then_some(&self.mask),
310        );
311    }
312
313    fn fill<'b>(&mut self, shape: &impl Shape, brush: impl Into<BrushRef<'b>>, _blur_radius: f64) {
314        // FIXME: Handle _blur_radius
315
316        let paint = try_ret!(brush_to_paint(brush));
317        if let Some(rect) = shape.as_rect() {
318            let rect = try_ret!(to_skia_rect(rect));
319            self.pixmap.fill_rect(
320                rect,
321                &paint,
322                self.skia_transform(),
323                self.clip.is_some().then_some(&self.mask),
324            );
325        } else {
326            let path = try_ret!(shape_to_path(shape));
327            self.pixmap.fill_path(
328                &path,
329                &paint,
330                FillRule::Winding,
331                self.skia_transform(),
332                self.clip.is_some().then_some(&self.mask),
333            );
334        }
335    }
336
337    fn draw_text_with_layout<'b>(
338        &mut self,
339        layout: impl Iterator<Item = LayoutRun<'b>>,
340        pos: impl Into<Point>,
341    ) {
342        // this pos is relative to the current transform, but not the current window scale.
343        // That is why we remove the window scale from the clip below
344        let pos: Point = pos.into();
345        let clip = self.clip;
346        let undo_transform = |r| {
347            Affine::scale(self.window_scale)
348                .inverse()
349                .transform_rect_bbox(r)
350        };
351        let scaled_clip = clip.map(undo_transform);
352
353        // we manually handle the offset so that the glyph_x and y can be scaled by the window_scale
354        let offset = self.transform.translation();
355        let transform = self.transform * Affine::translate((-offset.x, -offset.y));
356
357        for line in layout {
358            if let Some(rect) = scaled_clip {
359                let y = pos.y + offset.y + line.line_y as f64;
360                if y + (line.line_height as f64) < rect.y0 {
361                    continue;
362                }
363                if y - (line.line_height as f64) > rect.y1 {
364                    break;
365                }
366            }
367            'line_loop: for glyph_run in line.glyphs {
368                let x = glyph_run.x + pos.x as f32 + offset.x as f32;
369                let y = line.line_y + pos.y as f32 + offset.y as f32;
370                if let Some(rect) = scaled_clip {
371                    if ((x + glyph_run.w) as f64) < rect.x0 {
372                        continue;
373                    } else if x as f64 > rect.x1 {
374                        break 'line_loop;
375                    }
376                }
377
378                let glyph_x = x * self.window_scale as f32;
379                let glyph_y = y * self.window_scale as f32;
380                let font_size = glyph_run.font_size * self.window_scale as f32;
381
382                let (cache_key, new_x, new_y) = CacheKey::new(
383                    glyph_run.font_id,
384                    glyph_run.glyph_id,
385                    font_size,
386                    (glyph_x, glyph_y),
387                    glyph_run.cache_key_flags,
388                );
389
390                let color = glyph_run.color_opt.map_or(palette::css::BLACK, |c| {
391                    Color::from_rgba8(c.r(), c.g(), c.b(), c.a())
392                });
393
394                let glyph = cache_glyph(self.cache_color, cache_key, color);
395                if let Some(glyph) = glyph {
396                    self.render_pixmap_direct(
397                        &glyph.pixmap,
398                        new_x as f32 + glyph.left,
399                        new_y as f32 - glyph.top,
400                        transform,
401                    );
402                }
403            }
404        }
405    }
406
407    fn draw_svg<'b>(
408        &mut self,
409        svg: floem_renderer::Svg<'b>,
410        rect: Rect,
411        brush: Option<impl Into<BrushRef<'b>>>,
412    ) {
413        let width = (rect.width() * self.window_scale).round() as u32;
414        let height = (rect.height() * self.window_scale).round() as u32;
415
416        let rect = try_ret!(to_skia_rect(rect));
417
418        let paint = brush.and_then(|brush| brush_to_paint(brush));
419
420        if IMAGE_CACHE.with_borrow_mut(|ic| {
421            if let Some((color, non_colored_svg_pixmap)) = ic.get_mut(svg.hash) {
422                *color = self.cache_color;
423                let pixmap = non_colored_svg_pixmap.clone();
424                self.render_pixmap_with_paint(&pixmap, rect, paint.clone());
425                // return
426                true
427            } else {
428                // continue
429                false
430            }
431        }) {
432            return;
433        };
434
435        let mut non_colored_svg = try_ret!(tiny_skia::Pixmap::new(width, height));
436        let svg_transform = tiny_skia::Transform::from_scale(
437            width as f32 / svg.tree.size().width(),
438            height as f32 / svg.tree.size().height(),
439        );
440        resvg::render(svg.tree, svg_transform, &mut non_colored_svg.as_mut());
441
442        self.render_pixmap_with_paint(&non_colored_svg, rect, paint);
443
444        IMAGE_CACHE.with_borrow_mut(|ic| {
445            ic.insert(
446                svg.hash.to_owned(),
447                (self.cache_color, Rc::new(non_colored_svg)),
448            )
449        });
450    }
451
452    fn draw_img(&mut self, img: Img<'_>, rect: Rect) {
453        let rect = try_ret!(to_skia_rect(rect));
454        if IMAGE_CACHE.with_borrow_mut(|ic| {
455            if let Some((color, pixmap)) = ic.get_mut(img.hash) {
456                *color = self.cache_color;
457                let pixmap = pixmap.clone();
458                self.render_pixmap_rect(&pixmap, rect);
459                // return
460                true
461            } else {
462                // continue
463                false
464            }
465        }) {
466            return;
467        };
468
469        let image_data = img.img.data.data();
470        let mut pixmap = try_ret!(Pixmap::new(img.img.width, img.img.height));
471        for (a, b) in pixmap
472            .pixels_mut()
473            .iter_mut()
474            .zip(image_data.chunks_exact(4))
475        {
476            *a = tiny_skia::Color::from_rgba8(b[0], b[1], b[2], b[3])
477                .premultiply()
478                .to_color_u8();
479        }
480
481        self.render_pixmap_rect(&pixmap, rect);
482
483        IMAGE_CACHE.with_borrow_mut(|ic| {
484            ic.insert(img.hash.to_owned(), (self.cache_color, Rc::new(pixmap)))
485        });
486    }
487}
488
489pub struct TinySkiaRenderer<W> {
490    #[allow(unused)]
491    context: Context<W>,
492    surface: Surface<W, W>,
493    cache_color: CacheColor,
494    transform: Affine,
495    window_scale: f64,
496    layers: Vec<Layer>,
497}
498
499impl<W: raw_window_handle::HasWindowHandle + raw_window_handle::HasDisplayHandle>
500    TinySkiaRenderer<W>
501{
502    pub fn new(window: W, width: u32, height: u32, scale: f64, font_embolden: f32) -> Result<Self>
503    where
504        W: Clone,
505    {
506        let context = Context::new(window.clone())
507            .map_err(|err| anyhow!("unable to create context: {}", err))?;
508        let mut surface = Surface::new(&context, window)
509            .map_err(|err| anyhow!("unable to create surface: {}", err))?;
510        surface
511            .resize(
512                NonZeroU32::new(width).unwrap_or(NonZeroU32::new(1).unwrap()),
513                NonZeroU32::new(height).unwrap_or(NonZeroU32::new(1).unwrap()),
514            )
515            .map_err(|_| anyhow!("failed to resize surface"))?;
516
517        let pixmap =
518            Pixmap::new(width, height).ok_or_else(|| anyhow!("unable to create pixmap"))?;
519
520        let mask = Mask::new(width, height).ok_or_else(|| anyhow!("unable to create mask"))?;
521
522        // this is fine to modify the embolden here but it shouldn't be modified any other time
523        SWASH_SCALER.with_borrow_mut(|s| s.font_embolden = font_embolden);
524
525        let main_layer = Layer {
526            pixmap,
527            mask,
528            clip: None,
529            alpha: 1.,
530            transform: Affine::IDENTITY,
531            combine_transform: Affine::IDENTITY,
532            blend_mode: Mix::Normal.into(),
533            window_scale: scale,
534            cache_color: CacheColor(false),
535        };
536        Ok(Self {
537            context,
538            surface,
539            transform: Affine::IDENTITY,
540            window_scale: scale,
541            cache_color: CacheColor(false),
542            layers: vec![main_layer],
543        })
544    }
545
546    pub fn resize(&mut self, width: u32, height: u32, scale: f64) {
547        if width != self.layers[0].pixmap.width() || height != self.layers[0].pixmap.width() {
548            self.surface
549                .resize(
550                    NonZeroU32::new(width).unwrap_or(NonZeroU32::new(1).unwrap()),
551                    NonZeroU32::new(height).unwrap_or(NonZeroU32::new(1).unwrap()),
552                )
553                .expect("failed to resize surface");
554            self.layers[0].pixmap = Pixmap::new(width, height).expect("unable to create pixmap");
555            self.layers[0].mask = Mask::new(width, height).expect("unable to create mask");
556        }
557        self.layers[0].window_scale = scale;
558        self.window_scale = scale;
559    }
560
561    pub fn set_scale(&mut self, scale: f64) {
562        self.layers[0].window_scale = scale;
563        self.window_scale = scale;
564    }
565
566    pub fn scale(&self) -> f64 {
567        self.window_scale
568    }
569
570    pub fn size(&self) -> Size {
571        Size::new(
572            self.layers[0].pixmap.width() as f64,
573            self.layers[0].pixmap.height() as f64,
574        )
575    }
576}
577
578fn to_color(color: Color) -> tiny_skia::Color {
579    let c = color.to_rgba8();
580    tiny_skia::Color::from_rgba8(c.r, c.g, c.b, c.a)
581}
582
583fn to_point(point: Point) -> tiny_skia::Point {
584    tiny_skia::Point::from_xy(point.x as f32, point.y as f32)
585}
586
587impl<W: raw_window_handle::HasWindowHandle + raw_window_handle::HasDisplayHandle> Renderer
588    for TinySkiaRenderer<W>
589{
590    fn begin(&mut self, _capture: bool) {
591        assert!(self.layers.len() == 1);
592        let first_layer = self.layers.last_mut().unwrap();
593        first_layer.pixmap.fill(tiny_skia::Color::WHITE);
594        first_layer.clip = None;
595        first_layer.transform = Affine::IDENTITY;
596    }
597
598    fn stroke<'b, 's>(
599        &mut self,
600        shape: &impl Shape,
601        brush: impl Into<BrushRef<'b>>,
602        stroke: &'s peniko::kurbo::Stroke,
603    ) {
604        self.layers.last_mut().unwrap().stroke(shape, brush, stroke);
605    }
606
607    fn fill<'b>(&mut self, shape: &impl Shape, brush: impl Into<BrushRef<'b>>, blur_radius: f64) {
608        self.layers
609            .last_mut()
610            .unwrap()
611            .fill(shape, brush, blur_radius);
612    }
613
614    fn draw_text_with_layout<'b>(
615        &mut self,
616        layout: impl Iterator<Item = LayoutRun<'b>>,
617        pos: impl Into<Point>,
618    ) {
619        self.layers
620            .last_mut()
621            .unwrap()
622            .draw_text_with_layout(layout, pos);
623    }
624
625    fn draw_img(&mut self, img: Img<'_>, rect: Rect) {
626        self.layers.last_mut().unwrap().draw_img(img, rect);
627    }
628
629    fn draw_svg<'b>(
630        &mut self,
631        svg: floem_renderer::Svg<'b>,
632        rect: Rect,
633        brush: Option<impl Into<BrushRef<'b>>>,
634    ) {
635        self.layers.last_mut().unwrap().draw_svg(svg, rect, brush);
636    }
637
638    fn set_transform(&mut self, cumulative_transform: Affine) {
639        let uncombined = self.transform.inverse() * cumulative_transform;
640        self.transform = cumulative_transform;
641        self.layers.last_mut().unwrap().transform(uncombined);
642    }
643
644    fn set_z_index(&mut self, _z_index: i32) {
645        // FIXME: Remove this method?
646    }
647
648    fn clip(&mut self, shape: &impl Shape) {
649        self.layers.iter_mut().for_each(|l| l.clip(shape));
650    }
651
652    fn clear_clip(&mut self) {
653        self.layers.iter_mut().for_each(|l| l.clear_clip());
654    }
655
656    fn finish(&mut self) -> Option<peniko::Image> {
657        // Remove cache entries which were not accessed.
658        IMAGE_CACHE.with_borrow_mut(|ic| ic.retain(|_, (c, _)| *c == self.cache_color));
659        GLYPH_CACHE.with_borrow_mut(|gc| gc.retain(|_, (c, _)| *c == self.cache_color));
660
661        // Swap the cache color.
662        self.cache_color = CacheColor(!self.cache_color.0);
663
664        let mut buffer = self
665            .surface
666            .buffer_mut()
667            .expect("failed to get the surface buffer");
668
669        // Copy from `tiny_skia::Pixmap` to the format specified by `softbuffer::Buffer`.
670        for (out_pixel, pixel) in
671            (buffer.iter_mut()).zip(self.layers.last().unwrap().pixmap.pixels().iter())
672        {
673            *out_pixel = ((pixel.red() as u32) << 16)
674                | ((pixel.green() as u32) << 8)
675                | (pixel.blue() as u32);
676        }
677
678        buffer
679            .present()
680            .expect("failed to present the surface buffer");
681
682        None
683    }
684
685    fn push_layer(
686        &mut self,
687        blend: impl Into<peniko::BlendMode>,
688        alpha: f32,
689        transform: Affine,
690        clip: &impl Shape,
691    ) {
692        if let Ok(res) = Layer::new(
693            blend,
694            alpha,
695            transform,
696            clip,
697            self.window_scale,
698            self.cache_color,
699        ) {
700            self.layers.push(res);
701        }
702    }
703
704    fn pop_layer(&mut self) {
705        if self.layers.len() <= 1 {
706            // Don't pop the main layer
707            return;
708        }
709
710        let layer = self.layers.pop().unwrap();
711        let parent = self.layers.last_mut().unwrap();
712
713        apply_layer(&layer, parent);
714    }
715
716    fn debug_info(&self) -> String {
717        "name: tiny_skia".into()
718    }
719}
720
721fn shape_to_path(shape: &impl Shape) -> Option<Path> {
722    let mut builder = PathBuilder::new();
723    for element in shape.path_elements(0.1) {
724        match element {
725            PathEl::ClosePath => builder.close(),
726            PathEl::MoveTo(p) => builder.move_to(p.x as f32, p.y as f32),
727            PathEl::LineTo(p) => builder.line_to(p.x as f32, p.y as f32),
728            PathEl::QuadTo(p1, p2) => {
729                builder.quad_to(p1.x as f32, p1.y as f32, p2.x as f32, p2.y as f32)
730            }
731            PathEl::CurveTo(p1, p2, p3) => builder.cubic_to(
732                p1.x as f32,
733                p1.y as f32,
734                p2.x as f32,
735                p2.y as f32,
736                p3.x as f32,
737                p3.y as f32,
738            ),
739        }
740    }
741    builder.finish()
742}
743
744fn brush_to_paint<'b>(brush: impl Into<BrushRef<'b>>) -> Option<Paint<'static>> {
745    let shader = match brush.into() {
746        BrushRef::Solid(c) => Shader::SolidColor(to_color(c)),
747        BrushRef::Gradient(g) => {
748            let stops = g
749                .stops
750                .iter()
751                .map(|s| GradientStop::new(s.offset, to_color(s.color.to_alpha_color())))
752                .collect();
753            match g.kind {
754                GradientKind::Linear { start, end } => LinearGradient::new(
755                    to_point(start),
756                    to_point(end),
757                    stops,
758                    SpreadMode::Pad,
759                    Transform::identity(),
760                )?,
761                GradientKind::Radial {
762                    start_center,
763                    start_radius: _,
764                    end_center,
765                    end_radius,
766                } => {
767                    // FIXME: Doesn't use `start_radius`
768                    RadialGradient::new(
769                        to_point(start_center),
770                        to_point(end_center),
771                        end_radius,
772                        stops,
773                        SpreadMode::Pad,
774                        Transform::identity(),
775                    )?
776                }
777                GradientKind::Sweep { .. } => return None,
778            }
779        }
780        BrushRef::Image(_) => return None,
781    };
782    Some(Paint {
783        shader,
784        ..Default::default()
785    })
786}
787
788fn to_skia_rect(rect: Rect) -> Option<tiny_skia::Rect> {
789    tiny_skia::Rect::from_ltrb(
790        rect.x0 as f32,
791        rect.y0 as f32,
792        rect.x1 as f32,
793        rect.y1 as f32,
794    )
795}
796
797type TinyBlendMode = tiny_skia::BlendMode;
798
799enum BlendStrategy {
800    /// Can be directly mapped to a tiny-skia blend mode
801    SinglePass(TinyBlendMode),
802    /// Requires multiple operations
803    MultiPass {
804        first_pass: TinyBlendMode,
805        second_pass: TinyBlendMode,
806    },
807}
808
809fn determine_blend_strategy(peniko_mode: &BlendMode) -> BlendStrategy {
810    match (peniko_mode.mix, peniko_mode.compose) {
811        (Mix::Normal, compose) => BlendStrategy::SinglePass(compose_to_tiny_blend_mode(compose)),
812        (Mix::Clip, compose) => BlendStrategy::MultiPass {
813            first_pass: compose_to_tiny_blend_mode(compose),
814            second_pass: TinyBlendMode::Source,
815        },
816
817        (mix, Compose::SrcOver) => BlendStrategy::SinglePass(mix_to_tiny_blend_mode(mix)),
818
819        (mix, compose) => BlendStrategy::MultiPass {
820            first_pass: compose_to_tiny_blend_mode(compose),
821            second_pass: mix_to_tiny_blend_mode(mix),
822        },
823    }
824}
825
826fn compose_to_tiny_blend_mode(compose: Compose) -> TinyBlendMode {
827    match compose {
828        Compose::Clear => TinyBlendMode::Clear,
829        Compose::Copy => TinyBlendMode::Source,
830        Compose::Dest => TinyBlendMode::Destination,
831        Compose::SrcOver => TinyBlendMode::SourceOver,
832        Compose::DestOver => TinyBlendMode::DestinationOver,
833        Compose::SrcIn => TinyBlendMode::SourceIn,
834        Compose::DestIn => TinyBlendMode::DestinationIn,
835        Compose::SrcOut => TinyBlendMode::SourceOut,
836        Compose::DestOut => TinyBlendMode::DestinationOut,
837        Compose::SrcAtop => TinyBlendMode::SourceAtop,
838        Compose::DestAtop => TinyBlendMode::DestinationAtop,
839        Compose::Xor => TinyBlendMode::Xor,
840        Compose::Plus => TinyBlendMode::Plus,
841        Compose::PlusLighter => TinyBlendMode::Plus, // ??
842    }
843}
844
845fn mix_to_tiny_blend_mode(mix: Mix) -> TinyBlendMode {
846    match mix {
847        Mix::Normal => TinyBlendMode::SourceOver,
848        Mix::Multiply => TinyBlendMode::Multiply,
849        Mix::Screen => TinyBlendMode::Screen,
850        Mix::Overlay => TinyBlendMode::Overlay,
851        Mix::Darken => TinyBlendMode::Darken,
852        Mix::Lighten => TinyBlendMode::Lighten,
853        Mix::ColorDodge => TinyBlendMode::ColorDodge,
854        Mix::ColorBurn => TinyBlendMode::ColorBurn,
855        Mix::HardLight => TinyBlendMode::HardLight,
856        Mix::SoftLight => TinyBlendMode::SoftLight,
857        Mix::Difference => TinyBlendMode::Difference,
858        Mix::Exclusion => TinyBlendMode::Exclusion,
859        Mix::Hue => TinyBlendMode::Hue,
860        Mix::Saturation => TinyBlendMode::Saturation,
861        Mix::Color => TinyBlendMode::Color,
862        Mix::Luminosity => TinyBlendMode::Luminosity,
863        Mix::Clip => TinyBlendMode::SourceOver,
864    }
865}
866
867fn apply_layer(layer: &Layer, parent: &mut Layer) {
868    match determine_blend_strategy(&layer.blend_mode) {
869        BlendStrategy::SinglePass(blend_mode) => {
870            let mut paint = Paint {
871                blend_mode,
872                anti_alias: true,
873                ..Default::default()
874            };
875
876            let transform = skia_transform_with_scaled_translation(
877                parent.transform * layer.combine_transform,
878                layer.window_scale as f32,
879                1.,
880            );
881
882            let layer_pattern = Pattern::new(
883                layer.pixmap.as_ref(),
884                SpreadMode::Pad,
885                FilterQuality::Bilinear,
886                layer.alpha,
887                Transform::identity(),
888            );
889
890            paint.shader = layer_pattern;
891
892            let layer_rect = try_ret!(tiny_skia::Rect::from_xywh(
893                0.0,
894                0.0,
895                layer.pixmap.width() as f32,
896                layer.pixmap.height() as f32,
897            ));
898
899            parent.pixmap.fill_rect(
900                layer_rect,
901                &paint,
902                transform,
903                parent.clip.is_some().then_some(&parent.mask),
904            );
905        }
906        BlendStrategy::MultiPass {
907            first_pass,
908            second_pass,
909        } => {
910            let original_parent = parent.pixmap.clone();
911
912            let mut paint = Paint {
913                blend_mode: first_pass,
914                anti_alias: true,
915                ..Default::default()
916            };
917
918            let transform = skia_transform_with_scaled_translation(
919                parent.transform * layer.combine_transform,
920                layer.window_scale as f32,
921                1.,
922            );
923            let layer_pattern = Pattern::new(
924                layer.pixmap.as_ref(),
925                SpreadMode::Pad,
926                FilterQuality::Bilinear,
927                1.0,
928                Transform::identity(),
929            );
930
931            paint.shader = layer_pattern;
932
933            let layer_rect = try_ret!(tiny_skia::Rect::from_xywh(
934                0.0,
935                0.0,
936                layer.pixmap.width() as f32,
937                layer.pixmap.height() as f32,
938            ));
939
940            parent.pixmap.fill_rect(
941                layer_rect,
942                &paint,
943                transform,
944                parent.clip.is_some().then_some(&parent.mask),
945            );
946
947            let intermediate = parent.pixmap.clone();
948
949            parent.pixmap = original_parent;
950
951            let mut paint = Paint {
952                blend_mode: second_pass,
953                anti_alias: true,
954                ..Default::default()
955            };
956
957            let intermediate_pattern = Pattern::new(
958                intermediate.as_ref(),
959                SpreadMode::Pad,
960                FilterQuality::Bilinear,
961                1.0,
962                Transform::identity(),
963            );
964
965            paint.shader = intermediate_pattern;
966
967            parent.pixmap.fill_rect(
968                layer_rect,
969                &paint,
970                transform,
971                parent.clip.is_some().then_some(&parent.mask),
972            )
973        }
974    }
975    parent.transform *= layer.transform;
976}
977
978fn skia_transform(affine: Affine, window_scale: f32) -> Transform {
979    let transform = affine.as_coeffs();
980    Transform::from_row(
981        transform[0] as f32,
982        transform[1] as f32,
983        transform[2] as f32,
984        transform[3] as f32,
985        transform[4] as f32,
986        transform[5] as f32,
987    )
988    .post_scale(window_scale, window_scale)
989}
990
991fn skia_transform_with_scaled_translation(
992    affine: Affine,
993    translation_scale: f32,
994    render_scale: f32,
995) -> Transform {
996    let transform = affine.as_coeffs();
997    Transform::from_row(
998        transform[0] as f32,
999        transform[1] as f32,
1000        transform[2] as f32,
1001        transform[3] as f32,
1002        transform[4] as f32 * translation_scale,
1003        transform[5] as f32 * translation_scale,
1004    )
1005    .post_scale(render_scale, render_scale)
1006}