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 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 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: Option<Rect>,
108 mask: Mask,
109 transform: Affine,
111 combine_transform: Affine,
113 blend_mode: BlendMode,
114 alpha: f32,
115 window_scale: f64,
116 cache_color: CacheColor,
117}
118impl Layer {
119 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 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 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 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 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 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 true
427 } else {
428 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 true
461 } else {
462 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 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 }
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 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 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 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 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 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 SinglePass(TinyBlendMode),
802 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, }
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}