1use skia_safe::canvas::SaveLayerRec;
2use skia_safe::wrapper::PointerWrapper;
3use skia_safe::{
4 BlurStyle, ClipOp, MaskFilter, Matrix, Paint, PaintStyle, Path, PathBuilder, PathEffect, Point,
5 RRect, Rect, SamplingOptions, TileMode,
6};
7use std::any::{Any, TypeId};
8use std::f32::consts::SQRT_2;
9use vizia_style::LengthPercentageOrAuto;
10
11use hashbrown::HashMap;
12
13use crate::cache::CachedData;
14use crate::events::ViewHandler;
15use crate::prelude::*;
16use crate::resource::{ImageOrSvg, ResourceManager};
17use crate::text::{TextContext, resolved_text_direction};
18use vizia_input::MouseState;
19
20use super::ModelData;
21
22pub struct DrawContext<'a> {
55 pub(crate) current: Entity,
56 pub(crate) style: &'a Style,
57 pub(crate) cache: &'a mut CachedData,
58 pub(crate) tree: &'a Tree<Entity>,
59 pub(crate) models: &'a HashMap<Entity, HashMap<TypeId, Box<dyn ModelData>>>,
60 pub(crate) views: &'a mut HashMap<Entity, Box<dyn ViewHandler>>,
61 pub(crate) resource_manager: &'a ResourceManager,
62 pub(crate) text_context: &'a mut TextContext,
63 pub(crate) modifiers: &'a Modifiers,
64 pub(crate) mouse: &'a MouseState<Entity>,
65 pub(crate) windows: &'a mut HashMap<Entity, WindowState>,
66}
67
68macro_rules! get_units_property {
69 (
70 $(#[$meta:meta])*
71 $name:ident
72 ) => {
73 $(#[$meta])*
74 pub fn $name(&self) -> Units {
75 let result = self.style.$name.get(self.current);
76 if let Some(Units::Pixels(p)) = result {
77 Units::Pixels(self.logical_to_physical(*p))
78 } else {
79 result.copied().unwrap_or_default()
80 }
81 }
82 };
83}
84
85impl DrawContext<'_> {
86 pub fn with_current<T>(&mut self, entity: Entity, f: impl FnOnce(&mut DrawContext) -> T) -> T {
87 let current = self.current;
88 self.current = entity;
89 let t = f(self);
90 self.current = current;
91 t
92 }
93
94 pub fn bounds(&self) -> BoundingBox {
96 self.cache.get_bounds(self.current)
97 }
98
99 pub fn needs_redraw(&mut self) {
101 let parent_window = self.tree.get_parent_window(self.current).unwrap_or(Entity::root());
102 if let Some(window_state) = self.windows.get_mut(&parent_window) {
103 window_state.redraw_list.insert(self.current);
104 }
105 }
106
107 pub fn z_index(&self) -> i32 {
109 self.style.z_index.get(self.current).copied().unwrap_or_default()
110 }
111
112 pub fn scale_factor(&self) -> f32 {
114 self.style.dpi_factor as f32
115 }
116
117 pub fn modifiers(&self) -> &Modifiers {
119 self.modifiers
120 }
121
122 pub fn mouse(&self) -> &MouseState<Entity> {
124 self.mouse
125 }
126
127 pub fn clip_path(&self) -> Option<skia_safe::Path> {
129 if let Some(clip_path) = self.cache.clip_path.get(self.current) {
131 return clip_path.clone();
132 }
133
134 if self.style.ignore_clipping.get(self.current).copied().unwrap_or(false) {
135 return None;
136 }
137
138 let mut current = self.current;
140 while let Some(parent) = self.tree.get_parent(current) {
141 if let Some(clip_path) = self.cache.clip_path.get(parent) {
143 return clip_path.clone();
144 }
145
146 if self.style.ignore_clipping.get(parent).copied().unwrap_or(false) {
147 return None;
148 }
149 current = parent;
150 }
151
152 None
153 }
154
155 pub fn transform(&self) -> Matrix {
157 self.cache.transform.get(self.current).copied().unwrap_or_default()
158 }
159
160 pub fn visibility(&self) -> Option<Visibility> {
162 self.style.visibility.get(self.current).copied()
163 }
164
165 pub fn display(&self) -> Display {
167 self.style.display.get(self.current).copied().unwrap_or(Display::Flex)
168 }
169
170 pub fn opacity(&self) -> f32 {
172 self.style
173 .opacity
174 .get_resolved(self.current, &self.style.custom_opacity_props)
175 .unwrap_or(Opacity(1.0))
176 .0
177 }
178
179 pub fn default_font(&self) -> &[FamilyOwned] {
181 &self.style.default_font
182 }
183
184 pub fn font_size(&self) -> f32 {
186 let fs = self
187 .style
188 .font_size
189 .get_resolved(self.current, &self.style.custom_font_size_props)
190 .and_then(|f| f.0.to_px())
191 .unwrap_or(16.0);
192 self.logical_to_physical(fs)
193 }
194
195 pub fn font_weight(&self) -> FontWeight {
197 self.style.font_weight.get(self.current).copied().unwrap_or_default()
198 }
199
200 pub fn font_width(&self) -> FontWidth {
202 self.style.font_width.get(self.current).copied().unwrap_or_default()
203 }
204
205 pub fn font_slant(&self) -> FontSlant {
207 self.style.font_slant.get(self.current).copied().unwrap_or_default()
208 }
209
210 pub fn font_variation_settings(&self) -> &[FontVariation] {
212 self.style.font_variation_settings.get(self.current).map(Vec::as_slice).unwrap_or_default()
213 }
214
215 pub fn logical_to_physical(&self, logical: f32) -> f32 {
217 self.style.logical_to_physical(logical)
218 }
219
220 pub fn physical_to_logical(&self, physical: f32) -> f32 {
222 self.style.physical_to_logical(physical)
223 }
224
225 pub fn border_top_width(&self) -> f32 {
227 let bounds = self.bounds();
228 self.style
229 .border_top_width
230 .get_resolved(self.current, &self.style.custom_length_props)
231 .map(|l| l.to_pixels(bounds.w.min(bounds.h), self.scale_factor()).round())
232 .unwrap_or(0.0)
233 }
234
235 pub fn border_right_width(&self) -> f32 {
237 let bounds = self.bounds();
238 self.style
239 .border_right_width
240 .get_resolved(self.current, &self.style.custom_length_props)
241 .map(|l| l.to_pixels(bounds.w.min(bounds.h), self.scale_factor()).round())
242 .unwrap_or(0.0)
243 }
244
245 pub fn border_bottom_width(&self) -> f32 {
247 let bounds = self.bounds();
248 self.style
249 .border_bottom_width
250 .get_resolved(self.current, &self.style.custom_length_props)
251 .map(|l| l.to_pixels(bounds.w.min(bounds.h), self.scale_factor()).round())
252 .unwrap_or(0.0)
253 }
254
255 pub fn border_left_width(&self) -> f32 {
257 let bounds = self.bounds();
258 self.style
259 .border_left_width
260 .get_resolved(self.current, &self.style.custom_length_props)
261 .map(|l| l.to_pixels(bounds.w.min(bounds.h), self.scale_factor()).round())
262 .unwrap_or(0.0)
263 }
264
265 pub fn border_top_color(&self) -> Color {
267 self.style
268 .border_top_color
269 .get_resolved(self.current, &self.style.custom_color_props)
270 .map(|c| Color::rgba(c.r(), c.g(), c.b(), c.a()))
271 .unwrap_or(Color::rgba(0, 0, 0, 0))
272 }
273
274 pub fn border_right_color(&self) -> Color {
276 self.style
277 .border_right_color
278 .get_resolved(self.current, &self.style.custom_color_props)
279 .map(|c| Color::rgba(c.r(), c.g(), c.b(), c.a()))
280 .unwrap_or(Color::rgba(0, 0, 0, 0))
281 }
282
283 pub fn border_bottom_color(&self) -> Color {
285 self.style
286 .border_bottom_color
287 .get_resolved(self.current, &self.style.custom_color_props)
288 .map(|c| Color::rgba(c.r(), c.g(), c.b(), c.a()))
289 .unwrap_or(Color::rgba(0, 0, 0, 0))
290 }
291
292 pub fn border_left_color(&self) -> Color {
294 self.style
295 .border_left_color
296 .get_resolved(self.current, &self.style.custom_color_props)
297 .map(|c| Color::rgba(c.r(), c.g(), c.b(), c.a()))
298 .unwrap_or(Color::rgba(0, 0, 0, 0))
299 }
300
301 pub fn border_top_style(&self) -> BorderStyleKeyword {
303 self.style.border_top_style.get(self.current).copied().unwrap_or_default()
304 }
305
306 pub fn border_right_style(&self) -> BorderStyleKeyword {
308 self.style.border_right_style.get(self.current).copied().unwrap_or_default()
309 }
310
311 pub fn border_bottom_style(&self) -> BorderStyleKeyword {
313 self.style.border_bottom_style.get(self.current).copied().unwrap_or_default()
314 }
315
316 pub fn border_left_style(&self) -> BorderStyleKeyword {
318 self.style.border_left_style.get(self.current).copied().unwrap_or_default()
319 }
320
321 pub fn outline_color(&self) -> Color {
323 if let Some(col) =
324 self.style.outline_color.get_resolved(self.current, &self.style.custom_color_props)
325 {
326 Color::rgba(col.r(), col.g(), col.b(), col.a())
327 } else {
328 Color::rgba(0, 0, 0, 0)
329 }
330 }
331
332 pub fn outline_width(&self) -> f32 {
334 if let Some(length) =
335 self.style.outline_width.get_resolved(self.current, &self.style.custom_length_props)
336 {
337 let bounds = self.bounds();
338 return length.to_pixels(bounds.w.min(bounds.h), self.scale_factor()).round();
339 }
340 0.0
341 }
342
343 pub fn outline_offset(&self) -> f32 {
345 if let Some(length) =
346 self.style.outline_offset.get_resolved(self.current, &self.style.custom_length_props)
347 {
348 let bounds = self.bounds();
349 return length.to_pixels(bounds.w.min(bounds.h), self.scale_factor()).round();
350 }
351 0.0
352 }
353
354 pub fn corner_top_left_radius(&self) -> f32 {
356 let bounds = self.bounds();
357 let scale = self.scale_factor();
358 self.style
359 .corner_top_left_radius
360 .get_resolved(self.current, &self.style.custom_length_props)
361 .map(|l| l.to_pixels(bounds.w.min(bounds.h), scale).round())
362 .unwrap_or(0.0)
363 }
364
365 pub fn corner_top_right_radius(&self) -> f32 {
367 let bounds = self.bounds();
368 let scale = self.scale_factor();
369 self.style
370 .corner_top_right_radius
371 .get_resolved(self.current, &self.style.custom_length_props)
372 .map(|l| l.to_pixels(bounds.w.min(bounds.h), scale).round())
373 .unwrap_or(0.0)
374 }
375
376 pub fn corner_bottom_left_radius(&self) -> f32 {
378 let bounds = self.bounds();
379 let scale = self.scale_factor();
380 self.style
381 .corner_bottom_left_radius
382 .get_resolved(self.current, &self.style.custom_length_props)
383 .map(|l| l.to_pixels(bounds.w.min(bounds.h), scale).round())
384 .unwrap_or(0.0)
385 }
386
387 pub fn corner_bottom_right_radius(&self) -> f32 {
389 let bounds = self.bounds();
390 let scale = self.scale_factor();
391 self.style
392 .corner_bottom_right_radius
393 .get_resolved(self.current, &self.style.custom_length_props)
394 .map(|l| l.to_pixels(bounds.w.min(bounds.h), scale).round())
395 .unwrap_or(0.0)
396 }
397
398 get_units_property!(
399 padding_left
401 );
402
403 get_units_property!(
404 padding_right
406 );
407
408 get_units_property!(
409 padding_top
411 );
412
413 get_units_property!(
414 padding_bottom
416 );
417
418 pub fn alignment(&self) -> Alignment {
420 self.style.alignment.get(self.current).copied().unwrap_or_default()
421 }
422
423 pub fn background_color(&self) -> Color {
425 if let Some(col) =
426 self.style.background_color.get_resolved(self.current, &self.style.custom_color_props)
427 {
428 Color::rgba(col.r(), col.g(), col.b(), col.a())
429 } else {
430 Color::rgba(0, 0, 0, 0)
431 }
432 }
433
434 pub fn border_color(&self) -> Color {
437 self.border_top_color()
438 }
439
440 pub fn border_style(&self) -> BorderStyleKeyword {
443 self.border_top_style()
444 }
445
446 pub fn border_width(&self) -> f32 {
449 self.border_top_width()
450 }
451
452 pub fn selection_color(&self) -> Color {
454 if let Some(col) =
455 self.style.selection_color.get_resolved(self.current, &self.style.custom_color_props)
456 {
457 Color::rgba(col.r(), col.g(), col.b(), col.a())
458 } else {
459 Color::rgba(0, 0, 0, 0)
460 }
461 }
462
463 pub fn caret_color(&self) -> Color {
465 if let Some(col) =
466 self.style.caret_color.get_resolved(self.current, &self.style.custom_color_props)
467 {
468 Color::rgba(col.r(), col.g(), col.b(), col.a())
469 } else {
470 Color::rgba(0, 0, 0, 0)
471 }
472 }
473
474 pub fn font_color(&self) -> Color {
476 if let Some(col) =
477 self.style.font_color.get_resolved(self.current, &self.style.custom_color_props)
478 {
479 Color::rgba(col.r(), col.g(), col.b(), col.a())
480 } else {
481 Color::rgba(0, 0, 0, 0)
482 }
483 }
484
485 pub fn text_wrap(&self) -> bool {
487 self.style.text_wrap.get(self.current).copied().unwrap_or(true)
488 }
489
490 pub fn text_align(&self) -> TextAlign {
492 self.style.text_align.get(self.current).copied().unwrap_or_default()
493 }
494
495 pub fn text_overflow(&self) -> TextOverflow {
497 self.style.text_overflow.get(self.current).copied().unwrap_or_default()
498 }
499
500 pub fn line_clamp(&self) -> Option<usize> {
502 self.style.line_clamp.get(self.current).copied().map(|lc| lc.0 as usize)
503 }
504
505 pub fn shadows(&self) -> Option<Vec<Shadow>> {
507 self.style.shadow.get_resolved(self.current, &self.style.custom_shadow_props)
508 }
509
510 pub fn filter(&self) -> Option<&Filter> {
512 self.style.filter.get(self.current)
513 }
514
515 pub fn backdrop_filter(&self) -> Option<&Filter> {
517 self.style.backdrop_filter.get(self.current)
518 }
519
520 pub fn background_images(&self) -> Option<&Vec<ImageOrGradient>> {
522 self.style.background_image.get(self.current)
523 }
524
525 pub fn background_size(&self) -> Vec<BackgroundSize> {
527 self.style.background_size.get(self.current).cloned().unwrap_or_default()
528 }
529
530 pub fn background_position(&self) -> Vec<Position> {
532 self.style.background_position.get(self.current).cloned().unwrap_or_default()
533 }
534
535 pub fn background_repeat(&self) -> Vec<BackgroundRepeat> {
537 self.style.background_repeat.get(self.current).cloned().unwrap_or_default()
538 }
539
540 pub fn path(&mut self) -> Path {
541 if self.cache.path.get(self.current).is_none() {
542 self.cache.path.insert(self.current, self.build_path(self.bounds(), (0.0, 0.0)));
543 }
544 let bounds = self.bounds();
545 self.cache.path.get(self.current).unwrap().make_offset(bounds.top_left())
546 }
547
548 pub fn build_path(&self, bounds: BoundingBox, outset: (f32, f32)) -> Path {
550 self.build_path_with_radii(
551 bounds,
552 outset,
553 (
554 self.corner_top_left_radius(),
555 self.corner_top_right_radius(),
556 self.corner_bottom_right_radius(),
557 self.corner_bottom_left_radius(),
558 ),
559 )
560 }
561
562 fn build_rrect_with_radii(
563 &self,
564 bounds: BoundingBox,
565 outset: (f32, f32),
566 corner_radii: (f32, f32, f32, f32),
567 ) -> RRect {
568 let (top_left, top_right, bottom_right, bottom_left) = corner_radii;
569 RRect::new_rect_radii(
570 Rect::from_xywh(0.0, 0.0, bounds.w, bounds.h),
571 &[
572 Point::new(top_left, top_left),
573 Point::new(top_right, top_right),
574 Point::new(bottom_right, bottom_right),
575 Point::new(bottom_left, bottom_left),
576 ],
577 )
578 .with_outset(outset)
579 }
580
581 fn build_path_with_radii(
582 &self,
583 bounds: BoundingBox,
584 outset: (f32, f32),
585 corner_radii: (f32, f32, f32, f32),
586 ) -> Path {
587 let mut path = PathBuilder::new();
588 path.add_rrect(self.build_rrect_with_radii(bounds, outset, corner_radii), None, None);
589 path.detach()
590 }
591
592 fn rrect(&self, bounds: BoundingBox, outset: (f32, f32)) -> RRect {
593 self.build_rrect_with_radii(
594 bounds,
595 outset,
596 (
597 self.corner_top_left_radius(),
598 self.corner_top_right_radius(),
599 self.corner_bottom_right_radius(),
600 self.corner_bottom_left_radius(),
601 ),
602 )
603 .with_offset(bounds.top_left())
604 }
605
606 fn corner_oval(center_x: f32, center_y: f32, radius: f32) -> Rect {
607 Rect::from_xywh(center_x - radius, center_y - radius, radius * 2.0, radius * 2.0)
608 }
609
610 fn round_corner_side_path(
611 side_ix: usize,
612 bounds: BoundingBox,
613 outer_radii: (f32, f32, f32, f32),
614 inner_radii: (f32, f32, f32, f32),
615 widths: (f32, f32, f32, f32),
616 ) -> Option<Path> {
617 let bx = bounds.x;
618 let by = bounds.y;
619 let bw = bounds.w;
620 let bh = bounds.h;
621
622 let (r_tl, r_tr, r_br, r_bl) = outer_radii;
623 let (ir_tl, ir_tr, ir_br, ir_bl) = inner_radii;
624 let (top_width, right_width, bottom_width, left_width) = widths;
625
626 let mut path = PathBuilder::new();
627 let diag = SQRT_2.recip();
628
629 let outer_tl_center = Point::new(bx + r_tl, by + r_tl);
630 let outer_tr_center = Point::new(bx + bw - r_tr, by + r_tr);
631 let outer_br_center = Point::new(bx + bw - r_br, by + bh - r_br);
632 let outer_bl_center = Point::new(bx + r_bl, by + bh - r_bl);
633
634 let inner_tl_center = Point::new(bx + left_width + ir_tl, by + top_width + ir_tl);
635 let inner_tr_center = Point::new(bx + bw - right_width - ir_tr, by + top_width + ir_tr);
636 let inner_br_center =
637 Point::new(bx + bw - right_width - ir_br, by + bh - bottom_width - ir_br);
638 let inner_bl_center = Point::new(bx + left_width + ir_bl, by + bh - bottom_width - ir_bl);
639
640 match side_ix {
641 0 => {
642 let outer_start = if r_tl > 0.0 {
643 Point::new(outer_tl_center.x - r_tl * diag, outer_tl_center.y - r_tl * diag)
644 } else {
645 Point::new(bx, by)
646 };
647 path.move_to(outer_start);
648 if r_tl > 0.0 {
649 path.arc_to(
650 Self::corner_oval(outer_tl_center.x, outer_tl_center.y, r_tl),
651 225.0,
652 45.0,
653 false,
654 );
655 }
656 path.line_to((bx + bw - r_tr, by));
657 if r_tr > 0.0 {
658 path.arc_to(
659 Self::corner_oval(outer_tr_center.x, outer_tr_center.y, r_tr),
660 270.0,
661 45.0,
662 false,
663 );
664 }
665
666 let inner_split = if ir_tr > 0.0 {
667 Point::new(inner_tr_center.x + ir_tr * diag, inner_tr_center.y - ir_tr * diag)
668 } else {
669 Point::new(bx + bw - right_width, by + top_width)
670 };
671 path.line_to(inner_split);
672 if ir_tr > 0.0 {
673 path.arc_to(
674 Self::corner_oval(inner_tr_center.x, inner_tr_center.y, ir_tr),
675 315.0,
676 -45.0,
677 false,
678 );
679 }
680 path.line_to((bx + left_width + ir_tl, by + top_width));
681 if ir_tl > 0.0 {
682 path.arc_to(
683 Self::corner_oval(inner_tl_center.x, inner_tl_center.y, ir_tl),
684 270.0,
685 -45.0,
686 false,
687 );
688 }
689 }
690 1 => {
691 let outer_start = if r_tr > 0.0 {
692 Point::new(outer_tr_center.x + r_tr * diag, outer_tr_center.y - r_tr * diag)
693 } else {
694 Point::new(bx + bw, by)
695 };
696 path.move_to(outer_start);
697 if r_tr > 0.0 {
698 path.arc_to(
699 Self::corner_oval(outer_tr_center.x, outer_tr_center.y, r_tr),
700 315.0,
701 45.0,
702 false,
703 );
704 }
705 path.line_to((bx + bw, by + bh - r_br));
706 if r_br > 0.0 {
707 path.arc_to(
708 Self::corner_oval(outer_br_center.x, outer_br_center.y, r_br),
709 0.0,
710 45.0,
711 false,
712 );
713 }
714
715 let inner_split = if ir_br > 0.0 {
716 Point::new(inner_br_center.x + ir_br * diag, inner_br_center.y + ir_br * diag)
717 } else {
718 Point::new(bx + bw - right_width, by + bh - bottom_width)
719 };
720 path.line_to(inner_split);
721 if ir_br > 0.0 {
722 path.arc_to(
723 Self::corner_oval(inner_br_center.x, inner_br_center.y, ir_br),
724 45.0,
725 -45.0,
726 false,
727 );
728 }
729 path.line_to((bx + bw - right_width, by + top_width + ir_tr));
730 if ir_tr > 0.0 {
731 path.arc_to(
732 Self::corner_oval(inner_tr_center.x, inner_tr_center.y, ir_tr),
733 0.0,
734 -45.0,
735 false,
736 );
737 }
738 }
739 2 => {
740 let outer_start = if r_br > 0.0 {
741 Point::new(outer_br_center.x + r_br * diag, outer_br_center.y + r_br * diag)
742 } else {
743 Point::new(bx + bw, by + bh)
744 };
745 path.move_to(outer_start);
746 if r_br > 0.0 {
747 path.arc_to(
748 Self::corner_oval(outer_br_center.x, outer_br_center.y, r_br),
749 45.0,
750 45.0,
751 false,
752 );
753 }
754 path.line_to((bx + r_bl, by + bh));
755 if r_bl > 0.0 {
756 path.arc_to(
757 Self::corner_oval(outer_bl_center.x, outer_bl_center.y, r_bl),
758 90.0,
759 45.0,
760 false,
761 );
762 }
763
764 let inner_split = if ir_bl > 0.0 {
765 Point::new(inner_bl_center.x - ir_bl * diag, inner_bl_center.y + ir_bl * diag)
766 } else {
767 Point::new(bx + left_width, by + bh - bottom_width)
768 };
769 path.line_to(inner_split);
770 if ir_bl > 0.0 {
771 path.arc_to(
772 Self::corner_oval(inner_bl_center.x, inner_bl_center.y, ir_bl),
773 135.0,
774 -45.0,
775 false,
776 );
777 }
778 path.line_to((bx + bw - right_width - ir_br, by + bh - bottom_width));
779 if ir_br > 0.0 {
780 path.arc_to(
781 Self::corner_oval(inner_br_center.x, inner_br_center.y, ir_br),
782 90.0,
783 -45.0,
784 false,
785 );
786 }
787 }
788 3 => {
789 let outer_start = if r_bl > 0.0 {
790 Point::new(outer_bl_center.x - r_bl * diag, outer_bl_center.y + r_bl * diag)
791 } else {
792 Point::new(bx, by + bh)
793 };
794 path.move_to(outer_start);
795 if r_bl > 0.0 {
796 path.arc_to(
797 Self::corner_oval(outer_bl_center.x, outer_bl_center.y, r_bl),
798 135.0,
799 45.0,
800 false,
801 );
802 }
803 path.line_to((bx, by + r_tl));
804 if r_tl > 0.0 {
805 path.arc_to(
806 Self::corner_oval(outer_tl_center.x, outer_tl_center.y, r_tl),
807 180.0,
808 45.0,
809 false,
810 );
811 }
812
813 let inner_split = if ir_tl > 0.0 {
814 Point::new(inner_tl_center.x - ir_tl * diag, inner_tl_center.y - ir_tl * diag)
815 } else {
816 Point::new(bx + left_width, by + top_width)
817 };
818 path.line_to(inner_split);
819 if ir_tl > 0.0 {
820 path.arc_to(
821 Self::corner_oval(inner_tl_center.x, inner_tl_center.y, ir_tl),
822 225.0,
823 -45.0,
824 false,
825 );
826 }
827 path.line_to((bx + left_width, by + bh - bottom_width - ir_bl));
828 if ir_bl > 0.0 {
829 path.arc_to(
830 Self::corner_oval(inner_bl_center.x, inner_bl_center.y, ir_bl),
831 180.0,
832 -45.0,
833 false,
834 );
835 }
836 }
837 _ => return None,
838 }
839
840 path.close();
841 Some(path.detach())
842 }
843
844 fn round_corner_side_stroke_path(
845 side_ix: usize,
846 bounds: BoundingBox,
847 outer_radii: (f32, f32, f32, f32),
848 ) -> Option<Path> {
849 let bx = bounds.x;
850 let by = bounds.y;
851 let bw = bounds.w;
852 let bh = bounds.h;
853
854 let (r_tl, r_tr, r_br, r_bl) = outer_radii;
855
856 let mut path = PathBuilder::new();
857 let diag = SQRT_2.recip();
858
859 let outer_tl_center = Point::new(bx + r_tl, by + r_tl);
860 let outer_tr_center = Point::new(bx + bw - r_tr, by + r_tr);
861 let outer_br_center = Point::new(bx + bw - r_br, by + bh - r_br);
862 let outer_bl_center = Point::new(bx + r_bl, by + bh - r_bl);
863
864 match side_ix {
865 0 => {
866 let outer_start = if r_tl > 0.0 {
867 Point::new(outer_tl_center.x - r_tl * diag, outer_tl_center.y - r_tl * diag)
868 } else {
869 Point::new(bx, by)
870 };
871 path.move_to(outer_start);
872 if r_tl > 0.0 {
873 path.arc_to(
874 Self::corner_oval(outer_tl_center.x, outer_tl_center.y, r_tl),
875 225.0,
876 45.0,
877 false,
878 );
879 }
880 path.line_to((bx + bw - r_tr, by));
881 if r_tr > 0.0 {
882 path.arc_to(
883 Self::corner_oval(outer_tr_center.x, outer_tr_center.y, r_tr),
884 270.0,
885 45.0,
886 false,
887 );
888 }
889 }
890 1 => {
891 let outer_start = if r_tr > 0.0 {
892 Point::new(outer_tr_center.x + r_tr * diag, outer_tr_center.y - r_tr * diag)
893 } else {
894 Point::new(bx + bw, by)
895 };
896 path.move_to(outer_start);
897 if r_tr > 0.0 {
898 path.arc_to(
899 Self::corner_oval(outer_tr_center.x, outer_tr_center.y, r_tr),
900 315.0,
901 45.0,
902 false,
903 );
904 }
905 path.line_to((bx + bw, by + bh - r_br));
906 if r_br > 0.0 {
907 path.arc_to(
908 Self::corner_oval(outer_br_center.x, outer_br_center.y, r_br),
909 0.0,
910 45.0,
911 false,
912 );
913 }
914 }
915 2 => {
916 let outer_start = if r_br > 0.0 {
917 Point::new(outer_br_center.x + r_br * diag, outer_br_center.y + r_br * diag)
918 } else {
919 Point::new(bx + bw, by + bh)
920 };
921 path.move_to(outer_start);
922 if r_br > 0.0 {
923 path.arc_to(
924 Self::corner_oval(outer_br_center.x, outer_br_center.y, r_br),
925 45.0,
926 45.0,
927 false,
928 );
929 }
930 path.line_to((bx + r_bl, by + bh));
931 if r_bl > 0.0 {
932 path.arc_to(
933 Self::corner_oval(outer_bl_center.x, outer_bl_center.y, r_bl),
934 90.0,
935 45.0,
936 false,
937 );
938 }
939 }
940 3 => {
941 let outer_start = if r_bl > 0.0 {
942 Point::new(outer_bl_center.x - r_bl * diag, outer_bl_center.y + r_bl * diag)
943 } else {
944 Point::new(bx, by + bh)
945 };
946 path.move_to(outer_start);
947 if r_bl > 0.0 {
948 path.arc_to(
949 Self::corner_oval(outer_bl_center.x, outer_bl_center.y, r_bl),
950 135.0,
951 45.0,
952 false,
953 );
954 }
955 path.line_to((bx, by + r_tl));
956 if r_tl > 0.0 {
957 path.arc_to(
958 Self::corner_oval(outer_tl_center.x, outer_tl_center.y, r_tl),
959 180.0,
960 45.0,
961 false,
962 );
963 }
964 }
965 _ => return None,
966 }
967
968 Some(path.detach())
969 }
970
971 pub fn draw_background(&mut self, canvas: &Canvas) {
973 let background_color = self.background_color();
974 if background_color.a() > 0 {
975 let mut paint = Paint::default();
976 paint.set_color(skia_safe::Color::from_argb(
977 background_color.a(),
978 background_color.r(),
979 background_color.g(),
980 background_color.b(),
981 ));
982 paint.set_anti_alias(true);
983 canvas.draw_rrect(self.rrect(self.bounds(), (0.0, 0.0)), &paint);
984 }
985
986 self.draw_background_images(canvas);
987 }
988
989 pub fn draw_border(&mut self, canvas: &Canvas) {
996 let top_width = self.border_top_width();
997 let right_width = self.border_right_width();
998 let bottom_width = self.border_bottom_width();
999 let left_width = self.border_left_width();
1000
1001 let top_color = self.border_top_color();
1002 let right_color = self.border_right_color();
1003 let bottom_color = self.border_bottom_color();
1004 let left_color = self.border_left_color();
1005
1006 let top_style = self.border_top_style();
1007 let right_style = self.border_right_style();
1008 let bottom_style = self.border_bottom_style();
1009 let left_style = self.border_left_style();
1010
1011 let top_vis = top_width > 0.0 && top_color.a() > 0 && top_style != BorderStyleKeyword::None;
1012 let right_vis =
1013 right_width > 0.0 && right_color.a() > 0 && right_style != BorderStyleKeyword::None;
1014 let bottom_vis =
1015 bottom_width > 0.0 && bottom_color.a() > 0 && bottom_style != BorderStyleKeyword::None;
1016 let left_vis =
1017 left_width > 0.0 && left_color.a() > 0 && left_style != BorderStyleKeyword::None;
1018
1019 if !top_vis && !right_vis && !bottom_vis && !left_vis {
1020 return;
1021 }
1022
1023 let bounds = self.bounds();
1024 let bx = bounds.x;
1025 let by = bounds.y;
1026 let bw = bounds.w;
1027 let bh = bounds.h;
1028
1029 let r_tl = self.corner_top_left_radius();
1031 let r_tr = self.corner_top_right_radius();
1032 let r_br = self.corner_bottom_right_radius();
1033 let r_bl = self.corner_bottom_left_radius();
1034
1035 let ir_tl = (r_tl - top_width.max(left_width)).max(0.0);
1037 let ir_tr = (r_tr - top_width.max(right_width)).max(0.0);
1038 let ir_br = (r_br - bottom_width.max(right_width)).max(0.0);
1039 let ir_bl = (r_bl - bottom_width.max(left_width)).max(0.0);
1040
1041 let inner_w = (bw - left_width - right_width).max(0.0);
1043 let inner_h = (bh - top_width - bottom_width).max(0.0);
1044 let inner_is_empty = inner_w <= 0.0 || inner_h <= 0.0;
1045
1046 let uniform_borders = top_width == right_width
1047 && right_width == bottom_width
1048 && bottom_width == left_width
1049 && top_color == right_color
1050 && right_color == bottom_color
1051 && bottom_color == left_color
1052 && top_style == right_style
1053 && right_style == bottom_style
1054 && bottom_style == left_style;
1055 let corner_radii = (r_tl, r_tr, r_br, r_bl);
1056 let outer_rrect = self
1057 .build_rrect_with_radii(bounds, (0.0, 0.0), corner_radii)
1058 .with_offset(bounds.top_left());
1059 let inner_rrect = (!inner_is_empty).then(|| {
1060 self.build_rrect_with_radii(
1061 BoundingBox::from_min_max(0.0, 0.0, inner_w, inner_h),
1062 (0.0, 0.0),
1063 (ir_tl, ir_tr, ir_br, ir_bl),
1064 )
1065 .with_offset((bx + left_width, by + top_width))
1066 });
1067
1068 if uniform_borders {
1069 let mut paint = Paint::default();
1070 paint.set_color(top_color);
1071 paint.set_anti_alias(true);
1072
1073 match top_style {
1074 BorderStyleKeyword::Dashed | BorderStyleKeyword::Dotted => {
1075 paint.set_style(PaintStyle::Stroke);
1076 paint.set_stroke_width(top_width);
1077 if top_style == BorderStyleKeyword::Dashed {
1078 paint.set_path_effect(PathEffect::dash(&[top_width * 2.0, top_width], 0.0));
1079 } else {
1080 paint.set_path_effect(PathEffect::dash(&[0.0, top_width * 2.0], 0.0));
1081 paint.set_stroke_cap(skia_safe::PaintCap::Round);
1082 }
1083 canvas.draw_rrect(
1084 outer_rrect.with_inset((top_width * 0.5, top_width * 0.5)),
1085 &paint,
1086 );
1087 }
1088 _ => {
1089 if let Some(inner_rrect) = inner_rrect {
1090 canvas.draw_drrect(outer_rrect, inner_rrect, &paint);
1091 } else {
1092 canvas.draw_rrect(outer_rrect, &paint);
1093 }
1094 }
1095 }
1096 return;
1097 }
1098
1099 let outer_path = self
1101 .build_path_with_radii(bounds, (0.0, 0.0), corner_radii)
1102 .make_offset(bounds.top_left());
1103
1104 let inner_path = if inner_is_empty {
1106 None
1107 } else {
1108 let inner_bounds = BoundingBox::from_min_max(0.0, 0.0, inner_w, inner_h);
1109 Some(
1110 self.build_path_with_radii(inner_bounds, (0.0, 0.0), (ir_tl, ir_tr, ir_br, ir_bl))
1111 .make_offset((bx + left_width, by + top_width)),
1112 )
1113 };
1114
1115 let ring_path = (if let Some(inner) = inner_path.as_ref() {
1116 outer_path
1117 .op(inner, skia_safe::PathOp::Difference)
1118 .unwrap_or_else(|| outer_path.clone())
1119 } else {
1120 outer_path.clone()
1121 })
1122 .with_is_volatile(true);
1123
1124 let mask_top: [Point; 4] = [
1126 Point::new(bx, by),
1127 Point::new(bx + bw, by),
1128 Point::new(bx + bw - right_width, by + top_width),
1129 Point::new(bx + left_width, by + top_width),
1130 ];
1131 let mask_right: [Point; 4] = [
1132 Point::new(bx + bw - right_width, by + top_width),
1133 Point::new(bx + bw, by),
1134 Point::new(bx + bw, by + bh),
1135 Point::new(bx + bw - right_width, by + bh - bottom_width),
1136 ];
1137 let mask_bottom: [Point; 4] = [
1138 Point::new(bx + left_width, by + bh - bottom_width),
1139 Point::new(bx + bw - right_width, by + bh - bottom_width),
1140 Point::new(bx + bw, by + bh),
1141 Point::new(bx, by + bh),
1142 ];
1143 let mask_left: [Point; 4] = [
1144 Point::new(bx, by),
1145 Point::new(bx + left_width, by + top_width),
1146 Point::new(bx + left_width, by + bh - bottom_width),
1147 Point::new(bx, by + bh),
1148 ];
1149
1150 let sides: [(usize, bool, Color, BorderStyleKeyword, f32, [Point; 4]); 4] = [
1151 (0, top_vis, top_color, top_style, top_width, mask_top),
1152 (1, right_vis, right_color, right_style, right_width, mask_right),
1153 (2, bottom_vis, bottom_color, bottom_style, bottom_width, mask_bottom),
1154 (3, left_vis, left_color, left_style, left_width, mask_left),
1155 ];
1156
1157 for (side_ix, vis, color, style, side_width, mask_pts) in sides {
1158 if !vis {
1159 continue;
1160 }
1161
1162 let mut side_mask = PathBuilder::new();
1163 side_mask.move_to(mask_pts[0]);
1164 side_mask.line_to(mask_pts[1]);
1165 side_mask.line_to(mask_pts[2]);
1166 side_mask.line_to(mask_pts[3]);
1167 side_mask.close();
1168 let side_mask = side_mask.detach();
1169 let Some(side_region) = ring_path.op(&side_mask, skia_safe::PathOp::Intersect) else {
1170 continue;
1171 };
1172 let side_region = side_region.with_is_volatile(true);
1173
1174 match style {
1175 BorderStyleKeyword::Dashed | BorderStyleKeyword::Dotted => {
1176 canvas.save();
1177 canvas.clip_path(&side_region, ClipOp::Intersect, true);
1178
1179 let stroke_path = (if !inner_is_empty {
1180 Self::round_corner_side_stroke_path(
1181 side_ix,
1182 bounds,
1183 (r_tl, r_tr, r_br, r_bl),
1184 )
1185 .unwrap_or_else(|| {
1186 let mut side_path = PathBuilder::new();
1187 let (sx, sy, ex, ey) = match side_ix {
1188 0 => (
1189 bx + left_width * 0.5,
1190 by + top_width * 0.5,
1191 bx + bw - right_width * 0.5,
1192 by + top_width * 0.5,
1193 ),
1194 1 => (
1195 bx + bw - right_width * 0.5,
1196 by + top_width * 0.5,
1197 bx + bw - right_width * 0.5,
1198 by + bh - bottom_width * 0.5,
1199 ),
1200 2 => (
1201 bx + left_width * 0.5,
1202 by + bh - bottom_width * 0.5,
1203 bx + bw - right_width * 0.5,
1204 by + bh - bottom_width * 0.5,
1205 ),
1206 _ => (
1207 bx + left_width * 0.5,
1208 by + top_width * 0.5,
1209 bx + left_width * 0.5,
1210 by + bh - bottom_width * 0.5,
1211 ),
1212 };
1213 side_path.move_to((sx, sy));
1214 side_path.line_to((ex, ey));
1215 side_path.detach()
1216 })
1217 } else {
1218 let mut side_path = PathBuilder::new();
1219 let (sx, sy, ex, ey) = match side_ix {
1220 0 => (
1221 bx + left_width * 0.5,
1222 by + top_width * 0.5,
1223 bx + bw - right_width * 0.5,
1224 by + top_width * 0.5,
1225 ),
1226 1 => (
1227 bx + bw - right_width * 0.5,
1228 by + top_width * 0.5,
1229 bx + bw - right_width * 0.5,
1230 by + bh - bottom_width * 0.5,
1231 ),
1232 2 => (
1233 bx + left_width * 0.5,
1234 by + bh - bottom_width * 0.5,
1235 bx + bw - right_width * 0.5,
1236 by + bh - bottom_width * 0.5,
1237 ),
1238 _ => (
1239 bx + left_width * 0.5,
1240 by + top_width * 0.5,
1241 bx + left_width * 0.5,
1242 by + bh - bottom_width * 0.5,
1243 ),
1244 };
1245 side_path.move_to((sx, sy));
1246 side_path.line_to((ex, ey));
1247 side_path.detach()
1248 })
1249 .with_is_volatile(true);
1250 let mut paint = Paint::default();
1251 paint.set_style(PaintStyle::Stroke);
1252 paint.set_color(color);
1253 paint.set_stroke_width(side_width);
1254 if style == BorderStyleKeyword::Dashed {
1255 paint.set_path_effect(PathEffect::dash(
1256 &[side_width * 2.0, side_width],
1257 0.0,
1258 ));
1259 } else {
1260 paint.set_path_effect(PathEffect::dash(&[0.0, side_width * 2.0], 0.0));
1261 paint.set_stroke_cap(skia_safe::PaintCap::Round);
1262 }
1263 paint.set_anti_alias(true);
1264 canvas.draw_path(&stroke_path, &paint);
1265 canvas.restore();
1266 }
1267 _ => {
1268 let exact_side_path = if !inner_is_empty {
1269 Self::round_corner_side_path(
1270 side_ix,
1271 bounds,
1272 (r_tl, r_tr, r_br, r_bl),
1273 (ir_tl, ir_tr, ir_br, ir_bl),
1274 (top_width, right_width, bottom_width, left_width),
1275 )
1276 } else {
1277 None
1278 };
1279 let mut paint = Paint::default();
1281 paint.set_color(color);
1282 paint.set_anti_alias(true);
1283 if let Some(path) = exact_side_path {
1284 let Some(constrained_path) =
1285 path.op(&ring_path, skia_safe::PathOp::Intersect)
1286 else {
1287 continue;
1288 };
1289 let constrained_path = constrained_path.with_is_volatile(true);
1290 canvas.draw_path(&constrained_path, &paint);
1291 } else {
1292 canvas.draw_path(&side_region, &paint);
1293 }
1294 }
1295 }
1296 }
1297 }
1298
1299 pub fn draw_outline(&mut self, canvas: &Canvas) {
1301 let outline_width = self.outline_width();
1302 let outline_color = self.outline_color();
1303
1304 if outline_width > 0.0 && outline_color.a() != 0 {
1305 let outline_offset = self.outline_offset();
1306
1307 let bounds = self.bounds();
1308
1309 let half_outline_width = outline_width / 2.0;
1310 let outline_rrect = self.rrect(
1311 bounds,
1312 (half_outline_width + outline_offset, half_outline_width + outline_offset),
1313 );
1314
1315 let mut outline_paint = Paint::default();
1316 outline_paint.set_color(outline_color);
1317 outline_paint.set_stroke_width(outline_width);
1318 outline_paint.set_style(PaintStyle::Stroke);
1319 outline_paint.set_anti_alias(true);
1320 canvas.draw_rrect(outline_rrect, &outline_paint);
1321 }
1322 }
1323
1324 pub fn draw_shadows(&mut self, canvas: &Canvas) {
1326 if let Some(shadows) = self.shadows() {
1327 if shadows.is_empty() {
1328 return;
1329 }
1330
1331 let bounds = self.bounds();
1332
1333 let rrect = self.rrect(bounds, (0.0, 0.0));
1334
1335 for shadow in shadows.iter().rev() {
1336 let shadow_color = shadow.color.unwrap_or_default();
1337
1338 let shadow_x_offset = shadow.x_offset.to_px().unwrap_or(0.0) * self.scale_factor();
1339 let shadow_y_offset = shadow.y_offset.to_px().unwrap_or(0.0) * self.scale_factor();
1340 let spread_radius =
1341 shadow.spread_radius.as_ref().and_then(|l| l.to_px()).unwrap_or(0.0)
1342 * self.scale_factor();
1343
1344 let blur_radius =
1345 shadow.blur_radius.as_ref().and_then(|br| br.to_px()).unwrap_or(0.0);
1346
1347 if shadow_color.a() == 0
1348 || (shadow_x_offset == 0.0
1349 && shadow_y_offset == 0.0
1350 && spread_radius == 0.0
1351 && blur_radius == 0.0)
1352 {
1353 continue;
1354 }
1355
1356 let mut shadow_paint = Paint::default();
1357
1358 let outset = if shadow.inset { -spread_radius } else { spread_radius };
1359
1360 shadow_paint.set_style(PaintStyle::Fill);
1361
1362 shadow_paint.set_color(shadow_color);
1363
1364 if blur_radius > 0.0 {
1365 shadow_paint.set_mask_filter(MaskFilter::blur(
1366 BlurStyle::Normal,
1367 blur_radius / 2.0,
1368 false,
1369 ));
1370 }
1371
1372 canvas.save();
1373 if shadow.inset {
1374 let path = self.build_path(bounds, (0.0, 0.0)).make_offset(bounds.top_left());
1375 let shadow_path = self
1376 .build_path(bounds, (outset, outset))
1377 .make_offset(bounds.top_left())
1378 .make_offset((shadow_x_offset, shadow_y_offset));
1379 let shadow_path = path
1380 .op(&shadow_path, skia_safe::PathOp::Difference)
1381 .unwrap()
1382 .with_is_volatile(true);
1383 canvas.clip_rrect(rrect, ClipOp::Intersect, true);
1384 canvas.draw_path(&shadow_path, &shadow_paint);
1385 } else {
1386 let shadow_rrect = self
1387 .rrect(bounds, (outset, outset))
1388 .with_offset((shadow_x_offset, shadow_y_offset));
1389 canvas.clip_rrect(rrect, ClipOp::Difference, true);
1390 canvas.draw_rrect(shadow_rrect, &shadow_paint);
1391 }
1392 canvas.restore();
1393 }
1394 }
1395 }
1396
1397 fn draw_background_images(&mut self, canvas: &Canvas) {
1399 let bounds = self.bounds();
1400
1401 if self.background_images().is_some() {
1402 let rrect = self.rrect(bounds, (0.0, 0.0));
1403 if let Some(images) = self.background_images() {
1404 let image_sizes = self.background_size();
1405 let image_positions = self.background_position();
1406 let image_repeats = self.background_repeat();
1407
1408 for (index, image) in images.iter().enumerate() {
1409 match image {
1410 ImageOrGradient::Gradient(gradient) => match gradient {
1411 Gradient::Linear(linear_gradient) => {
1412 let (start, end, parent_length) = match linear_gradient.direction {
1413 LineDirection::Horizontal(horizontal_keyword) => {
1414 match horizontal_keyword {
1415 HorizontalPositionKeyword::Left => (
1416 bounds.center_right(),
1417 bounds.center_left(),
1418 bounds.width(),
1419 ),
1420
1421 HorizontalPositionKeyword::Right => (
1422 bounds.center_left(),
1423 bounds.center_right(),
1424 bounds.width(),
1425 ),
1426 }
1427 }
1428
1429 LineDirection::Vertical(vertical_keyword) => {
1430 match vertical_keyword {
1431 VerticalPositionKeyword::Top => (
1432 bounds.center_bottom(),
1433 bounds.center_top(),
1434 bounds.height(),
1435 ),
1436
1437 VerticalPositionKeyword::Bottom => (
1438 bounds.center_top(),
1439 bounds.center_bottom(),
1440 bounds.height(),
1441 ),
1442 }
1443 }
1444
1445 LineDirection::Corner { horizontal, vertical } => {
1446 match (horizontal, vertical) {
1447 (
1448 HorizontalPositionKeyword::Right,
1449 VerticalPositionKeyword::Bottom,
1450 ) => (
1451 bounds.top_left(),
1452 bounds.bottom_right(),
1453 bounds.diagonal(),
1454 ),
1455
1456 (
1457 HorizontalPositionKeyword::Right,
1458 VerticalPositionKeyword::Top,
1459 ) => (
1460 bounds.bottom_left(),
1461 bounds.top_right(),
1462 bounds.diagonal(),
1463 ),
1464
1465 _ => (bounds.top_left(), bounds.bottom_right(), 0.0),
1466 }
1467 }
1468
1469 LineDirection::Angle(angle) => {
1470 let angle_rad = angle.to_radians();
1471 let start_x = bounds.x
1472 + ((angle_rad.sin() * bounds.w) - bounds.w) / -2.0;
1473 let end_x = bounds.x
1474 + ((angle_rad.sin() * bounds.w) + bounds.w) / 2.0;
1475 let start_y = bounds.y
1476 + ((angle_rad.cos() * bounds.h) + bounds.h) / 2.0;
1477 let end_y = bounds.y
1478 + ((angle_rad.cos() * bounds.h) - bounds.h) / -2.0;
1479
1480 let x = (end_x - start_x).abs();
1481 let y = (end_y - start_y).abs();
1482
1483 let dist = (x * x + y * y).sqrt();
1484
1485 ((start_x, start_y), (end_x, end_y), dist)
1486 }
1487 };
1488
1489 let num_stops = linear_gradient.stops.len();
1490
1491 let mut stops = linear_gradient
1492 .stops
1493 .iter()
1494 .enumerate()
1495 .map(|(index, stop)| {
1496 let pos = if let Some(pos) = &stop.position {
1497 pos.to_pixels(parent_length, self.scale_factor())
1498 / parent_length
1499 } else {
1500 index as f32 / (num_stops - 1) as f32
1501 };
1502 (pos, skia_safe::Color::from(stop.color))
1503 })
1504 .collect::<Vec<_>>();
1505
1506 if let Some(first) = stops.first() {
1508 if first.0 != 0.0 {
1509 stops.insert(0, (0.0, first.1));
1510 }
1511 }
1512
1513 if let Some(last) = stops.last() {
1515 if last.0 != 1.0 {
1516 stops.push((1.0, last.1));
1517 }
1518 }
1519
1520 let (offsets, colors): (Vec<f32>, Vec<skia_safe::Color>) =
1521 stops.into_iter().unzip();
1522 let colors4f: Vec<skia_safe::Color4f> =
1523 colors.iter().copied().map(Into::into).collect();
1524
1525 let gradient_colors =
1526 skia_safe::gradient_shader::GradientColors::new(
1527 &colors4f,
1528 Some(&offsets[..]),
1529 TileMode::Clamp,
1530 None,
1531 );
1532 let gradient = skia_safe::gradient_shader::Gradient::new(
1533 gradient_colors,
1534 skia_safe::gradient_shader::Interpolation::default(),
1535 );
1536 let shader = skia_safe::shaders::linear_gradient(
1537 (Point::from(start), Point::from(end)),
1538 &gradient,
1539 None,
1540 );
1541
1542 let mut paint = Paint::default();
1543 paint.set_shader(shader);
1544 paint.set_anti_alias(true);
1545
1546 canvas.draw_rrect(rrect, &paint);
1547 }
1548
1549 Gradient::Radial(radial_gradient) => {
1550 let num_stops = radial_gradient.stops.len();
1551
1552 let mut stops = radial_gradient
1553 .stops
1554 .iter()
1555 .enumerate()
1556 .map(|(index, stop)| {
1557 let pos = if let Some(pos) = &stop.position {
1558 pos.to_pixels(bounds.width(), self.scale_factor())
1559 / bounds.width()
1560 } else {
1561 index as f32 / (num_stops - 1) as f32
1562 };
1563
1564 (pos, skia_safe::Color::from(stop.color))
1565 })
1566 .collect::<Vec<_>>();
1567
1568 if let Some(first) = stops.first() {
1570 if first.0 != 0.0 {
1571 stops.insert(0, (0.0, first.1));
1572 }
1573 }
1574
1575 if let Some(last) = stops.last() {
1577 if last.0 != 1.0 {
1578 stops.push((1.0, last.1));
1579 }
1580 }
1581
1582 let (offsets, colors): (Vec<f32>, Vec<skia_safe::Color>) =
1583 stops.into_iter().unzip();
1584 let colors4f: Vec<skia_safe::Color4f> =
1585 colors.iter().copied().map(Into::into).collect();
1586
1587 let gradient_colors =
1588 skia_safe::gradient_shader::GradientColors::new(
1589 &colors4f,
1590 Some(&offsets[..]),
1591 TileMode::Clamp,
1592 None,
1593 );
1594 let gradient = skia_safe::gradient_shader::Gradient::new(
1595 gradient_colors,
1596 skia_safe::gradient_shader::Interpolation::default(),
1597 );
1598 let shader = skia_safe::shaders::radial_gradient(
1599 (Point::from(bounds.center()), bounds.w.max(bounds.h)),
1600 &gradient,
1601 None,
1602 );
1603
1604 let mut paint = Paint::default();
1605 paint.set_shader(shader);
1606 paint.set_anti_alias(true);
1607
1608 canvas.draw_rrect(rrect, &paint);
1609 }
1610
1611 _ => {}
1612 },
1613
1614 ImageOrGradient::Image(image_name) => {
1615 if let Some(image_id) = self.resource_manager.image_ids.get(image_name)
1616 {
1617 if let Some(image) = self.resource_manager.images.get(image_id) {
1618 match &image.image {
1619 ImageOrSvg::Image(image) => {
1620 let image_width = image.width();
1621 let image_height = image.height();
1622 let (width, height) = if let Some(background_size) =
1623 image_sizes.get(index)
1624 {
1625 match background_size {
1626 BackgroundSize::Explicit { width, height } => {
1627 let w = match width {
1628 LengthPercentageOrAuto::LengthPercentage(
1629 length,
1630 ) => {
1631 length.to_pixels(bounds.w, self.scale_factor())
1632 }
1633 LengthPercentageOrAuto::Auto => image_width as f32,
1634 };
1635
1636 let h = match height {
1637 LengthPercentageOrAuto::LengthPercentage(
1638 length,
1639 ) => {
1640 length.to_pixels(bounds.h, self.scale_factor())
1641 }
1642 LengthPercentageOrAuto::Auto => image_height as f32,
1643 };
1644
1645 (w, h)
1646 }
1647
1648 BackgroundSize::Contain => {
1649 let image_ratio = image_width as f32
1650 / image_height as f32;
1651 let container_ratio = bounds.w / bounds.h;
1652
1653 let (w, h) =
1654 if image_ratio > container_ratio {
1655 (bounds.w, bounds.w / image_ratio)
1656 } else {
1657 (bounds.h * image_ratio, bounds.h)
1658 };
1659
1660 (w, h)
1661 }
1662
1663 BackgroundSize::Cover => {
1664 let image_ratio = image_width as f32
1665 / image_height as f32;
1666 let container_ratio = bounds.w / bounds.h;
1667
1668 let (w, h) =
1669 if image_ratio < container_ratio {
1670 (bounds.w, bounds.w / image_ratio)
1671 } else {
1672 (bounds.h * image_ratio, bounds.h)
1673 };
1674
1675 (w, h)
1676 }
1677 }
1678 } else {
1679 (image_width as f32, image_height as f32)
1680 };
1681
1682 let position = image_positions
1683 .get(index)
1684 .cloned()
1685 .or_else(|| image_positions.last().cloned())
1686 .unwrap_or_default();
1687
1688 let posx =
1689 position.x.to_length_or_percentage().to_pixels(
1690 bounds.width() - width,
1691 self.scale_factor(),
1692 );
1693 let posy =
1694 position.y.to_length_or_percentage().to_pixels(
1695 bounds.height() - height,
1696 self.scale_factor(),
1697 );
1698 let repeat = image_repeats
1699 .get(index)
1700 .copied()
1701 .or_else(|| image_repeats.last().copied())
1702 .unwrap_or(BackgroundRepeat::Repeat);
1703
1704 if width <= 0.0 || height <= 0.0 {
1705 continue;
1706 }
1707
1708 let mut paint = Paint::default();
1709 paint.set_anti_alias(true);
1710
1711 let origin_x = bounds.left() + posx;
1712 let origin_y = bounds.top() + posy;
1713
1714 let mut start_x = origin_x;
1715 let mut start_y = origin_y;
1716
1717 if matches!(
1718 repeat,
1719 BackgroundRepeat::Repeat
1720 | BackgroundRepeat::RepeatX
1721 ) {
1722 let tiles_to_left =
1723 ((bounds.left() - origin_x) / width).floor();
1724 start_x = origin_x + tiles_to_left * width;
1725 if start_x > bounds.left() {
1726 start_x -= width;
1727 }
1728 }
1729
1730 if matches!(
1731 repeat,
1732 BackgroundRepeat::Repeat
1733 | BackgroundRepeat::RepeatY
1734 ) {
1735 let tiles_to_top =
1736 ((bounds.top() - origin_y) / height).floor();
1737 start_y = origin_y + tiles_to_top * height;
1738 if start_y > bounds.top() {
1739 start_y -= height;
1740 }
1741 }
1742
1743 canvas.save();
1744 canvas.clip_rrect(rrect, ClipOp::Intersect, true);
1745
1746 match repeat {
1747 BackgroundRepeat::NoRepeat => {
1748 let dst = Rect::new(
1749 origin_x,
1750 origin_y,
1751 origin_x + width,
1752 origin_y + height,
1753 );
1754 canvas.draw_image_rect_with_sampling_options(
1755 image,
1756 None,
1757 dst,
1758 SamplingOptions::default(),
1759 &paint,
1760 );
1761 }
1762
1763 BackgroundRepeat::RepeatX => {
1764 let mut x = start_x;
1765 while x < bounds.right() {
1766 let dst = Rect::new(
1767 x,
1768 origin_y,
1769 x + width,
1770 origin_y + height,
1771 );
1772 canvas
1773 .draw_image_rect_with_sampling_options(
1774 image,
1775 None,
1776 dst,
1777 SamplingOptions::default(),
1778 &paint,
1779 );
1780 x += width;
1781 }
1782 }
1783
1784 BackgroundRepeat::RepeatY => {
1785 let mut y = start_y;
1786 while y < bounds.bottom() {
1787 let dst = Rect::new(
1788 origin_x,
1789 y,
1790 origin_x + width,
1791 y + height,
1792 );
1793 canvas
1794 .draw_image_rect_with_sampling_options(
1795 image,
1796 None,
1797 dst,
1798 SamplingOptions::default(),
1799 &paint,
1800 );
1801 y += height;
1802 }
1803 }
1804
1805 BackgroundRepeat::Repeat => {
1806 let mut y = start_y;
1807 while y < bounds.bottom() {
1808 let mut x = start_x;
1809 while x < bounds.right() {
1810 let dst = Rect::new(
1811 x,
1812 y,
1813 x + width,
1814 y + height,
1815 );
1816 canvas.draw_image_rect_with_sampling_options(
1817 image,
1818 None,
1819 dst,
1820 SamplingOptions::default(),
1821 &paint,
1822 );
1823 x += width;
1824 }
1825 y += height;
1826 }
1827 }
1828 }
1829
1830 canvas.restore();
1831 }
1832
1833 ImageOrSvg::Svg(svg) => {
1834 canvas.save_layer(&SaveLayerRec::default());
1835 canvas.translate((bounds.x, bounds.y));
1836 let (scale_x, scale_y) = (
1837 bounds.width() / svg.inner().fContainerSize.fWidth,
1838 bounds.height()
1839 / svg.inner().fContainerSize.fHeight,
1840 );
1841
1842 if scale_x.is_finite() && scale_y.is_finite() {
1843 canvas.scale((scale_x, scale_y));
1844 } else {
1845 svg.clone().set_container_size((
1846 bounds.width(),
1847 bounds.height(),
1848 ));
1849 }
1850
1851 svg.render(canvas);
1852
1853 if let Some(color) = self.style.fill.get_resolved(
1854 self.current,
1855 &self.style.custom_color_props,
1856 ) {
1857 if color.a() != 0 {
1867 let mut paint = Paint::default();
1868 paint.set_anti_alias(true);
1869 paint.set_blend_mode(
1870 skia_safe::BlendMode::SrcIn,
1871 );
1872 paint.set_color(color);
1873 canvas.draw_paint(&paint);
1874 }
1875 }
1876 canvas.restore();
1877 }
1878 }
1879 }
1880 }
1881 }
1882 }
1883 }
1884 }
1885 }
1886 }
1887
1888 pub fn draw_text(&mut self, canvas: &Canvas) {
1890 if let Some(paragraph) = self.text_context.text_paragraphs.get(self.current) {
1891 let bounds = self.bounds();
1892
1893 let alignment = self.alignment();
1894
1895 let (mut top, _) = match alignment {
1896 Alignment::TopLeft => (0.0, 0.0),
1897 Alignment::TopCenter => (0.0, 0.5),
1898 Alignment::TopRight => (0.0, 1.0),
1899 Alignment::Left => (0.5, 0.0),
1900 Alignment::Center => (0.5, 0.5),
1901 Alignment::Right => (0.5, 1.0),
1902 Alignment::BottomLeft => (1.0, 0.0),
1903 Alignment::BottomCenter => (1.0, 0.5),
1904 Alignment::BottomRight => (1.0, 1.0),
1905 };
1906
1907 let padding_top = match self.padding_top() {
1908 Units::Pixels(val) => val,
1909 _ => 0.0,
1910 };
1911
1912 let padding_bottom = match self.padding_bottom() {
1913 Units::Pixels(val) => val,
1914 _ => 0.0,
1915 };
1916
1917 top *= bounds.height() - padding_top - padding_bottom - paragraph.height();
1918
1919 let mut padding_left = match self.padding_left() {
1920 Units::Pixels(val) => val,
1921 _ => 0.0,
1922 };
1923
1924 let mut padding_right = match self.padding_right() {
1925 Units::Pixels(val) => val,
1926 _ => 0.0,
1927 };
1928
1929 if resolved_text_direction(self.style, self.current) == Direction::RightToLeft {
1930 std::mem::swap(&mut padding_left, &mut padding_right);
1931 }
1932
1933 paragraph.paint(
1934 canvas,
1935 ((bounds.x + padding_left).round(), (bounds.y + padding_top + top).round()),
1936 );
1937 }
1938 }
1939}
1940
1941impl DataContext for DrawContext<'_> {
1942 fn try_data<T: 'static>(&self) -> Option<&T> {
1943 if let Some(t) = <dyn Any>::downcast_ref::<T>(&()) {
1945 return Some(t);
1946 }
1947
1948 for entity in self.current.parent_iter(self.tree) {
1949 if let Some(models) = self.models.get(&entity) {
1951 if let Some(model) = models.get(&TypeId::of::<T>()) {
1952 return model.downcast_ref::<T>();
1953 }
1954 }
1955
1956 if let Some(view_handler) = self.views.get(&entity) {
1958 if let Some(data) = view_handler.downcast_ref::<T>() {
1959 return Some(data);
1960 }
1961 }
1962 }
1963
1964 None
1965 }
1966}