1use skia_safe::canvas::SaveLayerRec;
2use skia_safe::path_builder::ArcSize;
3use skia_safe::rrect::Corner;
4use skia_safe::wrapper::PointerWrapper;
5use skia_safe::{
6 BlurStyle, ClipOp, MaskFilter, Matrix, Paint, PaintStyle, Path, PathBuilder, PathDirection,
7 PathEffect, Point, RRect, Rect, SamplingOptions, TileMode,
8};
9use std::any::{Any, TypeId};
10use std::f32::consts::SQRT_2;
11use vizia_style::LengthPercentageOrAuto;
12
13use hashbrown::HashMap;
14
15use crate::cache::CachedData;
16use crate::events::ViewHandler;
17use crate::prelude::*;
18use crate::resource::{ImageOrSvg, ResourceManager};
19use crate::text::{TextContext, resolved_text_direction};
20use vizia_input::MouseState;
21
22use super::ModelData;
23
24pub struct DrawContext<'a> {
57 pub(crate) current: Entity,
58 pub(crate) style: &'a Style,
59 pub(crate) cache: &'a mut CachedData,
60 pub(crate) tree: &'a Tree<Entity>,
61 pub(crate) models: &'a HashMap<Entity, HashMap<TypeId, Box<dyn ModelData>>>,
62 pub(crate) views: &'a mut HashMap<Entity, Box<dyn ViewHandler>>,
63 pub(crate) resource_manager: &'a ResourceManager,
64 pub(crate) text_context: &'a mut TextContext,
65 pub(crate) modifiers: &'a Modifiers,
66 pub(crate) mouse: &'a MouseState<Entity>,
67 pub(crate) windows: &'a mut HashMap<Entity, WindowState>,
68}
69
70macro_rules! get_units_property {
71 (
72 $(#[$meta:meta])*
73 $name:ident
74 ) => {
75 $(#[$meta])*
76 pub fn $name(&self) -> Units {
77 let result = self.style.$name.get(self.current);
78 if let Some(Units::Pixels(p)) = result {
79 Units::Pixels(self.logical_to_physical(*p))
80 } else {
81 result.copied().unwrap_or_default()
82 }
83 }
84 };
85}
86
87impl DrawContext<'_> {
88 pub fn with_current<T>(&mut self, entity: Entity, f: impl FnOnce(&mut DrawContext) -> T) -> T {
89 let current = self.current;
90 self.current = entity;
91 let t = f(self);
92 self.current = current;
93 t
94 }
95
96 pub fn bounds(&self) -> BoundingBox {
98 self.cache.get_bounds(self.current)
99 }
100
101 pub fn needs_redraw(&mut self) {
103 let parent_window = self.tree.get_parent_window(self.current).unwrap_or(Entity::root());
104 if let Some(window_state) = self.windows.get_mut(&parent_window) {
105 window_state.redraw_list.insert(self.current);
106 }
107 }
108
109 pub fn z_index(&self) -> i32 {
111 self.style.z_index.get(self.current).copied().unwrap_or_default()
112 }
113
114 pub fn scale_factor(&self) -> f32 {
116 self.style.dpi_factor as f32
117 }
118
119 pub fn modifiers(&self) -> &Modifiers {
121 self.modifiers
122 }
123
124 pub fn mouse(&self) -> &MouseState<Entity> {
126 self.mouse
127 }
128
129 pub fn clip_path(&self) -> Option<skia_safe::Path> {
131 self.cache.clip_path.get(self.current).cloned().flatten().or_else(|| {
133 let mut current = self.current;
134 while let Some(parent) = self.tree.get_parent(current) {
135 if let Some(clip_path) = self.cache.clip_path.get(parent).cloned().flatten() {
136 return Some(clip_path);
137 }
138 current = parent;
139 }
140 None
141 })
142 }
143
144 pub fn transform(&self) -> Matrix {
146 self.cache.transform.get(self.current).copied().unwrap_or_default()
147 }
148
149 pub fn visibility(&self) -> Option<Visibility> {
151 self.style.visibility.get(self.current).copied()
152 }
153
154 pub fn display(&self) -> Display {
156 self.style.display.get(self.current).copied().unwrap_or(Display::Flex)
157 }
158
159 pub fn opacity(&self) -> f32 {
161 self.style
162 .opacity
163 .get_resolved(self.current, &self.style.custom_opacity_props)
164 .unwrap_or(Opacity(1.0))
165 .0
166 }
167
168 pub fn default_font(&self) -> &[FamilyOwned] {
170 &self.style.default_font
171 }
172
173 pub fn font_size(&self) -> f32 {
175 let fs = self
176 .style
177 .font_size
178 .get_resolved(self.current, &self.style.custom_font_size_props)
179 .and_then(|f| f.0.to_px())
180 .unwrap_or(16.0);
181 self.logical_to_physical(fs)
182 }
183
184 pub fn font_weight(&self) -> FontWeight {
186 self.style.font_weight.get(self.current).copied().unwrap_or_default()
187 }
188
189 pub fn font_width(&self) -> FontWidth {
191 self.style.font_width.get(self.current).copied().unwrap_or_default()
192 }
193
194 pub fn font_slant(&self) -> FontSlant {
196 self.style.font_slant.get(self.current).copied().unwrap_or_default()
197 }
198
199 pub fn font_variation_settings(&self) -> &[FontVariation] {
201 self.style.font_variation_settings.get(self.current).map(Vec::as_slice).unwrap_or_default()
202 }
203
204 pub fn logical_to_physical(&self, logical: f32) -> f32 {
206 self.style.logical_to_physical(logical)
207 }
208
209 pub fn physical_to_logical(&self, physical: f32) -> f32 {
211 self.style.physical_to_logical(physical)
212 }
213
214 pub fn border_width(&self) -> f32 {
216 if let Some(length) =
217 self.style.border_width.get_resolved(self.current, &self.style.custom_length_props)
218 {
219 let bounds = self.bounds();
220 return length.to_pixels(bounds.w.min(bounds.h), self.scale_factor()).round();
221 }
222 0.0
223 }
224
225 pub fn outline_color(&self) -> Color {
227 if let Some(col) =
228 self.style.outline_color.get_resolved(self.current, &self.style.custom_color_props)
229 {
230 Color::rgba(col.r(), col.g(), col.b(), col.a())
231 } else {
232 Color::rgba(0, 0, 0, 0)
233 }
234 }
235
236 pub fn outline_width(&self) -> f32 {
238 if let Some(length) =
239 self.style.outline_width.get_resolved(self.current, &self.style.custom_length_props)
240 {
241 let bounds = self.bounds();
242 return length.to_pixels(bounds.w.min(bounds.h), self.scale_factor()).round();
243 }
244 0.0
245 }
246
247 pub fn outline_offset(&self) -> f32 {
249 if let Some(length) =
250 self.style.outline_offset.get_resolved(self.current, &self.style.custom_length_props)
251 {
252 let bounds = self.bounds();
253 return length.to_pixels(bounds.w.min(bounds.h), self.scale_factor()).round();
254 }
255 0.0
256 }
257
258 pub fn corner_top_left_radius(&self) -> f32 {
260 let bounds = self.bounds();
261 let scale = self.scale_factor();
262 self.style
263 .corner_top_left_radius
264 .get_resolved(self.current, &self.style.custom_length_props)
265 .map(|l| l.to_pixels(bounds.w.min(bounds.h), scale).round())
266 .unwrap_or(0.0)
267 }
268
269 pub fn corner_top_right_radius(&self) -> f32 {
271 let bounds = self.bounds();
272 let scale = self.scale_factor();
273 self.style
274 .corner_top_right_radius
275 .get_resolved(self.current, &self.style.custom_length_props)
276 .map(|l| l.to_pixels(bounds.w.min(bounds.h), scale).round())
277 .unwrap_or(0.0)
278 }
279
280 pub fn corner_bottom_left_radius(&self) -> f32 {
282 let bounds = self.bounds();
283 let scale = self.scale_factor();
284 self.style
285 .corner_bottom_left_radius
286 .get_resolved(self.current, &self.style.custom_length_props)
287 .map(|l| l.to_pixels(bounds.w.min(bounds.h), scale).round())
288 .unwrap_or(0.0)
289 }
290
291 pub fn corner_bottom_right_radius(&self) -> f32 {
293 let bounds = self.bounds();
294 let scale = self.scale_factor();
295 self.style
296 .corner_bottom_right_radius
297 .get_resolved(self.current, &self.style.custom_length_props)
298 .map(|l| l.to_pixels(bounds.w.min(bounds.h), scale).round())
299 .unwrap_or(0.0)
300 }
301
302 pub fn corner_top_left_shape(&self) -> CornerShape {
304 self.style.corner_top_left_shape.get(self.current).copied().unwrap_or_default()
305 }
306
307 pub fn corner_top_right_shape(&self) -> CornerShape {
309 self.style.corner_top_right_shape.get(self.current).copied().unwrap_or_default()
310 }
311
312 pub fn corner_bottom_left_shape(&self) -> CornerShape {
314 self.style.corner_bottom_left_shape.get(self.current).copied().unwrap_or_default()
315 }
316
317 pub fn corner_bottom_right_shape(&self) -> CornerShape {
319 self.style.corner_bottom_right_shape.get(self.current).copied().unwrap_or_default()
320 }
321
322 pub fn corner_top_left_smoothing(&self) -> f32 {
324 self.style.corner_top_left_smoothing.get(self.current).copied().unwrap_or_default()
325 }
326
327 pub fn corner_top_right_smoothing(&self) -> f32 {
329 self.style.corner_top_right_smoothing.get(self.current).copied().unwrap_or_default()
330 }
331
332 pub fn corner_bottom_left_smoothing(&self) -> f32 {
334 self.style.corner_bottom_left_smoothing.get(self.current).copied().unwrap_or_default()
335 }
336
337 pub fn corner_bottom_right_smoothing(&self) -> f32 {
339 self.style.corner_bottom_right_smoothing.get(self.current).copied().unwrap_or_default()
340 }
341
342 get_units_property!(
343 padding_left
345 );
346
347 get_units_property!(
348 padding_right
350 );
351
352 get_units_property!(
353 padding_top
355 );
356
357 get_units_property!(
358 padding_bottom
360 );
361
362 pub fn alignment(&self) -> Alignment {
364 self.style.alignment.get(self.current).copied().unwrap_or_default()
365 }
366
367 pub fn background_color(&self) -> Color {
369 if let Some(col) =
370 self.style.background_color.get_resolved(self.current, &self.style.custom_color_props)
371 {
372 Color::rgba(col.r(), col.g(), col.b(), col.a())
373 } else {
374 Color::rgba(0, 0, 0, 0)
375 }
376 }
377
378 pub fn border_color(&self) -> Color {
380 if let Some(col) =
381 self.style.border_color.get_resolved(self.current, &self.style.custom_color_props)
382 {
383 Color::rgba(col.r(), col.g(), col.b(), col.a())
384 } else {
385 Color::rgba(0, 0, 0, 0)
386 }
387 }
388
389 pub fn border_style(&self) -> BorderStyleKeyword {
391 self.style.border_style.get(self.current).copied().unwrap_or_default()
392 }
393
394 pub fn selection_color(&self) -> Color {
396 if let Some(col) =
397 self.style.selection_color.get_resolved(self.current, &self.style.custom_color_props)
398 {
399 Color::rgba(col.r(), col.g(), col.b(), col.a())
400 } else {
401 Color::rgba(0, 0, 0, 0)
402 }
403 }
404
405 pub fn caret_color(&self) -> Color {
407 if let Some(col) =
408 self.style.caret_color.get_resolved(self.current, &self.style.custom_color_props)
409 {
410 Color::rgba(col.r(), col.g(), col.b(), col.a())
411 } else {
412 Color::rgba(0, 0, 0, 0)
413 }
414 }
415
416 pub fn font_color(&self) -> Color {
418 if let Some(col) =
419 self.style.font_color.get_resolved(self.current, &self.style.custom_color_props)
420 {
421 Color::rgba(col.r(), col.g(), col.b(), col.a())
422 } else {
423 Color::rgba(0, 0, 0, 0)
424 }
425 }
426
427 pub fn text_wrap(&self) -> bool {
429 self.style.text_wrap.get(self.current).copied().unwrap_or(true)
430 }
431
432 pub fn text_align(&self) -> TextAlign {
434 self.style.text_align.get(self.current).copied().unwrap_or_default()
435 }
436
437 pub fn text_overflow(&self) -> TextOverflow {
439 self.style.text_overflow.get(self.current).copied().unwrap_or_default()
440 }
441
442 pub fn line_clamp(&self) -> Option<usize> {
444 self.style.line_clamp.get(self.current).copied().map(|lc| lc.0 as usize)
445 }
446
447 pub fn shadows(&self) -> Option<Vec<Shadow>> {
449 self.style.shadow.get_resolved(self.current, &self.style.custom_shadow_props)
450 }
451
452 pub fn filter(&self) -> Option<&Filter> {
454 self.style.filter.get(self.current)
455 }
456
457 pub fn backdrop_filter(&self) -> Option<&Filter> {
459 self.style.backdrop_filter.get(self.current)
460 }
461
462 pub fn background_images(&self) -> Option<&Vec<ImageOrGradient>> {
464 self.style.background_image.get(self.current)
465 }
466
467 pub fn background_size(&self) -> Vec<BackgroundSize> {
469 self.style.background_size.get(self.current).cloned().unwrap_or_default()
470 }
471
472 pub fn path(&mut self) -> Path {
473 if self.cache.path.get(self.current).is_none() {
474 self.cache.path.insert(self.current, self.build_path(self.bounds(), (0.0, 0.0)));
475 }
476 let bounds = self.bounds();
477 self.cache.path.get(self.current).unwrap().make_offset(bounds.top_left())
478 }
479
480 pub fn build_path(&self, bounds: BoundingBox, outset: (f32, f32)) -> Path {
482 let corner_top_left_radius = self.corner_top_left_radius();
483 let corner_top_right_radius = self.corner_top_right_radius();
484 let corner_bottom_right_radius = self.corner_bottom_right_radius();
485 let corner_bottom_left_radius = self.corner_bottom_left_radius();
486
487 let corner_top_left_shape = self.corner_top_left_shape();
488 let corner_top_right_shape = self.corner_top_right_shape();
489 let corner_bottom_right_shape = self.corner_bottom_right_shape();
490 let corner_bottom_left_shape = self.corner_bottom_left_shape();
491
492 let corner_top_left_smoothing = self.corner_top_left_smoothing();
493 let corner_top_right_smoothing = self.corner_top_right_smoothing();
494 let corner_bottom_right_smoothing = self.corner_bottom_right_smoothing();
495 let corner_bottom_left_smoothing = self.corner_bottom_left_smoothing();
496
497 let bounds = BoundingBox::from_min_max(0.0, 0.0, bounds.w, bounds.h);
498
499 let rect: Rect = bounds.into();
500
501 let mut rr = RRect::new_rect_radii(
502 rect,
503 &[
504 Point::new(corner_top_left_radius, corner_top_left_radius),
505 Point::new(corner_top_right_radius, corner_top_right_radius),
506 Point::new(corner_bottom_right_radius, corner_bottom_right_radius),
507 Point::new(corner_bottom_left_radius, corner_bottom_left_radius),
508 ],
509 );
510
511 rr = rr.with_outset(outset);
512
513 let x = rr.bounds().x();
514 let y = rr.bounds().y();
515 let width = rr.width();
516 let height = rr.height();
517 let mut should_offset = true;
518
519 let mut path = PathBuilder::new();
521
522 if width == height
523 && corner_bottom_left_radius == width / 2.0
524 && corner_bottom_right_radius == width / 2.0
525 && corner_top_left_radius == height / 2.0
526 && corner_top_right_radius == height / 2.0
527 {
528 path.add_circle((width / 2.0, height / 2.0), width / 2.0, Some(PathDirection::CW));
529 } else if corner_top_left_radius == corner_top_right_radius
530 && corner_top_right_radius == corner_bottom_right_radius
531 && corner_bottom_right_radius == corner_bottom_left_radius
532 && corner_top_left_smoothing == 0.0
533 && corner_top_left_smoothing == corner_top_right_smoothing
534 && corner_top_right_smoothing == corner_bottom_right_smoothing
535 && corner_bottom_right_smoothing == corner_bottom_left_smoothing
536 && corner_top_left_shape == CornerShape::Round
537 && corner_top_left_shape == corner_top_right_shape
538 && corner_top_right_shape == corner_bottom_right_shape
539 && corner_bottom_right_shape == corner_bottom_left_shape
540 {
541 path.add_rrect(rr, None, None);
542 should_offset = false;
543 } else {
544 let top_right = rr.radii(Corner::UpperRight).x;
545
546 if top_right > 0.0 {
547 let (a, b, c, d, l, p, radius) =
548 compute_smooth_corner(top_right, corner_top_right_smoothing, width, height);
549
550 path.move_to((f32::max(width / 2.0, width - p), 0.0));
551 if corner_top_right_shape == CornerShape::Round {
552 path.cubic_to(
553 (width - (p - a), 0.0),
554 (width - (p - a - b), 0.0),
555 (width - (p - a - b - c), d),
556 )
557 .r_arc_to((radius, radius), 0.0, ArcSize::Small, PathDirection::CW, (l, l))
558 .cubic_to(
559 (width, p - a - b),
560 (width, p - a),
561 (width, f32::min(height / 2.0, p)),
562 );
563 } else {
564 path.line_to((width, f32::min(height / 2.0, p)));
565 }
566 } else {
567 path.move_to((width / 2.0, 0.0))
568 .line_to((width, 0.0))
569 .line_to((width, height / 2.0));
570 }
571
572 let bottom_right = rr.radii(Corner::LowerRight).x;
573 if bottom_right > 0.0 {
574 let (a, b, c, d, l, p, radius) = compute_smooth_corner(
575 bottom_right,
576 corner_bottom_right_smoothing,
577 width,
578 height,
579 );
580
581 path.line_to((width, f32::max(height / 2.0, height - p)));
582 if corner_bottom_right_shape == CornerShape::Round {
583 path.cubic_to(
584 (width, height - (p - a)),
585 (width, height - (p - a - b)),
586 (width - d, height - (p - a - b - c)),
587 )
588 .r_arc_to((radius, radius), 0.0, ArcSize::Small, PathDirection::CW, (-l, l))
589 .cubic_to(
590 (width - (p - a - b), height),
591 (width - (p - a), height),
592 (f32::max(width / 2.0, width - p), height),
593 );
594 } else {
595 path.line_to((f32::max(width / 2.0, width - p), height));
596 }
597 } else {
598 path.line_to((width, height)).line_to((width / 2.0, height));
599 }
600
601 let bottom_left = rr.radii(Corner::LowerLeft).x;
602 if bottom_left > 0.0 {
603 let (a, b, c, d, l, p, radius) =
604 compute_smooth_corner(bottom_left, corner_bottom_left_smoothing, width, height);
605
606 path.line_to((f32::min(width / 2.0, p), height));
607 if corner_bottom_left_shape == CornerShape::Round {
608 path.cubic_to(
609 (p - a, height),
610 (p - a - b, height),
611 (p - a - b - c, height - d),
612 )
613 .r_arc_to((radius, radius), 0.0, ArcSize::Small, PathDirection::CW, (-l, -l))
614 .cubic_to(
615 (0.0, height - (p - a - b)),
616 (0.0, height - (p - a)),
617 (0.0, f32::max(height / 2.0, height - p)),
618 );
619 } else {
620 path.line_to((0.0, f32::max(height / 2.0, height - p)));
621 }
622 } else {
623 path.line_to((0.0, height)).line_to((0.0, height / 2.0));
624 }
625
626 let top_left = rr.radii(Corner::UpperLeft).x;
627 if top_left > 0.0 {
628 let (a, b, c, d, l, p, radius) =
629 compute_smooth_corner(top_left, corner_top_left_smoothing, width, height);
630
631 path.line_to((0.0, f32::min(height / 2.0, p)));
632 if corner_top_left_shape == CornerShape::Round {
633 path.cubic_to((0.0, p - a), (0.0, p - a - b), (d, p - a - b - c))
634 .r_arc_to((radius, radius), 0.0, ArcSize::Small, PathDirection::CW, (l, -l))
635 .cubic_to((p - a - b, 0.0), (p - a, 0.0), (f32::min(width / 2.0, p), 0.0));
636 } else {
637 path.line_to((f32::min(width / 2.0, p), 0.0));
638 }
639 } else {
640 path.line_to((0.0, 0.0));
641 }
642
643 path.close();
644 }
645
646 let path = path.detach();
647 if should_offset { path.make_offset((x, y)) } else { path }
648 }
649
650 pub fn draw_background(&mut self, canvas: &Canvas) {
652 let background_color = self.background_color();
653 if background_color.a() > 0 {
654 let path = self.path();
655
656 let mut paint = Paint::default();
657 paint.set_color(skia_safe::Color::from_argb(
658 background_color.a(),
659 background_color.r(),
660 background_color.g(),
661 background_color.b(),
662 ));
663 paint.set_anti_alias(true);
664 canvas.draw_path(&path, &paint);
665 }
666
667 self.draw_background_images(canvas);
668 }
669
670 pub fn draw_border(&mut self, canvas: &Canvas) {
672 let border_color = self.border_color();
673 let border_width = self.border_width();
674 let border_style = self.border_style();
675
676 if border_width > 0.0 && border_color.a() > 0 && border_style != BorderStyleKeyword::None {
677 let bounds = self.bounds();
678 let path = self
679 .build_path(bounds, (-border_width / 2.0, -border_width / 2.0))
680 .make_offset(bounds.top_left());
681 let mut paint = Paint::default();
682 paint.set_style(PaintStyle::Stroke);
683 paint.set_color(border_color);
684 paint.set_stroke_width(border_width);
685 match border_style {
686 BorderStyleKeyword::Dashed => {
687 paint.set_path_effect(PathEffect::dash(
688 &[border_width * 2.0, border_width],
689 0.0,
690 ));
691 }
692
693 BorderStyleKeyword::Dotted => {
694 paint.set_path_effect(PathEffect::dash(&[0.0, border_width * 2.0], 0.0));
695 paint.set_stroke_cap(skia_safe::PaintCap::Round);
696 }
697
698 _ => {}
699 }
700
701 paint.set_anti_alias(true);
702 canvas.draw_path(&path, &paint);
703 }
704 }
705
706 pub fn draw_outline(&mut self, canvas: &Canvas) {
708 let outline_width = self.outline_width();
709 let outline_color = self.outline_color();
710
711 if outline_width > 0.0 && outline_color.a() != 0 {
712 let outline_offset = self.outline_offset();
713
714 let bounds = self.bounds();
715
716 let half_outline_width = outline_width / 2.0;
717 let mut outline_path = self.build_path(
718 bounds,
719 (half_outline_width + outline_offset, half_outline_width + outline_offset),
720 );
721
722 outline_path = outline_path.make_offset(self.bounds().top_left());
723
724 let mut outline_paint = Paint::default();
725 outline_paint.set_color(outline_color);
726 outline_paint.set_stroke_width(outline_width);
727 outline_paint.set_style(PaintStyle::Stroke);
728 outline_paint.set_anti_alias(true);
729 canvas.draw_path(&outline_path, &outline_paint);
730 }
731 }
732
733 pub fn draw_shadows(&mut self, canvas: &Canvas) {
735 if let Some(shadows) = self.shadows() {
736 if shadows.is_empty() {
737 return;
738 }
739
740 let bounds = self.bounds();
741
742 let path = self.build_path(bounds, (0.0, 0.0)).make_offset(bounds.top_left());
743
744 for shadow in shadows.iter().rev() {
745 let shadow_color = shadow.color.unwrap_or_default();
746
747 let shadow_x_offset = shadow.x_offset.to_px().unwrap_or(0.0) * self.scale_factor();
748 let shadow_y_offset = shadow.y_offset.to_px().unwrap_or(0.0) * self.scale_factor();
749 let spread_radius =
750 shadow.spread_radius.as_ref().and_then(|l| l.to_px()).unwrap_or(0.0)
751 * self.scale_factor();
752
753 let blur_radius =
754 shadow.blur_radius.as_ref().and_then(|br| br.to_px()).unwrap_or(0.0);
755
756 if shadow_color.a() == 0
757 || (shadow_x_offset == 0.0
758 && shadow_y_offset == 0.0
759 && spread_radius == 0.0
760 && blur_radius == 0.0)
761 {
762 continue;
763 }
764
765 let mut shadow_paint = Paint::default();
766
767 let outset = if shadow.inset { -spread_radius } else { spread_radius };
768
769 shadow_paint.set_style(PaintStyle::Fill);
770
771 let mut shadow_path = self.build_path(bounds, (outset, outset));
772 shadow_path = shadow_path.make_offset(bounds.top_left());
773
774 shadow_paint.set_color(shadow_color);
775
776 if blur_radius > 0.0 {
777 shadow_paint.set_mask_filter(MaskFilter::blur(
778 BlurStyle::Normal,
779 blur_radius / 2.0,
780 false,
781 ));
782 }
783
784 shadow_path = shadow_path.make_offset((shadow_x_offset, shadow_y_offset));
785
786 if shadow.inset {
787 shadow_path = path.op(&shadow_path, skia_safe::PathOp::Difference).unwrap();
788 }
789
790 canvas.save();
791 canvas.clip_path(
792 &path,
793 if shadow.inset { ClipOp::Intersect } else { ClipOp::Difference },
794 true,
795 );
796 canvas.draw_path(&shadow_path, &shadow_paint);
797 canvas.restore();
798 }
799 }
800 }
801
802 fn draw_background_images(&mut self, canvas: &Canvas) {
804 let bounds = self.bounds();
805
806 if self.background_images().is_some() {
807 let path = self.path();
808 if let Some(images) = self.background_images() {
809 let image_sizes = self.background_size();
810
811 for (index, image) in images.iter().enumerate() {
812 match image {
813 ImageOrGradient::Gradient(gradient) => match gradient {
814 Gradient::Linear(linear_gradient) => {
815 let (start, end, parent_length) = match linear_gradient.direction {
816 LineDirection::Horizontal(horizontal_keyword) => {
817 match horizontal_keyword {
818 HorizontalPositionKeyword::Left => (
819 bounds.center_right(),
820 bounds.center_left(),
821 bounds.width(),
822 ),
823
824 HorizontalPositionKeyword::Right => (
825 bounds.center_left(),
826 bounds.center_right(),
827 bounds.width(),
828 ),
829 }
830 }
831
832 LineDirection::Vertical(vertical_keyword) => {
833 match vertical_keyword {
834 VerticalPositionKeyword::Top => (
835 bounds.center_bottom(),
836 bounds.center_top(),
837 bounds.height(),
838 ),
839
840 VerticalPositionKeyword::Bottom => (
841 bounds.center_top(),
842 bounds.center_bottom(),
843 bounds.height(),
844 ),
845 }
846 }
847
848 LineDirection::Corner { horizontal, vertical } => {
849 match (horizontal, vertical) {
850 (
851 HorizontalPositionKeyword::Right,
852 VerticalPositionKeyword::Bottom,
853 ) => (
854 bounds.top_left(),
855 bounds.bottom_right(),
856 bounds.diagonal(),
857 ),
858
859 (
860 HorizontalPositionKeyword::Right,
861 VerticalPositionKeyword::Top,
862 ) => (
863 bounds.bottom_left(),
864 bounds.top_right(),
865 bounds.diagonal(),
866 ),
867
868 _ => (bounds.top_left(), bounds.bottom_right(), 0.0),
869 }
870 }
871
872 LineDirection::Angle(angle) => {
873 let angle_rad = angle.to_radians();
874 let start_x = bounds.x
875 + ((angle_rad.sin() * bounds.w) - bounds.w) / -2.0;
876 let end_x = bounds.x
877 + ((angle_rad.sin() * bounds.w) + bounds.w) / 2.0;
878 let start_y = bounds.y
879 + ((angle_rad.cos() * bounds.h) + bounds.h) / 2.0;
880 let end_y = bounds.y
881 + ((angle_rad.cos() * bounds.h) - bounds.h) / -2.0;
882
883 let x = (end_x - start_x).abs();
884 let y = (end_y - start_y).abs();
885
886 let dist = (x * x + y * y).sqrt();
887
888 ((start_x, start_y), (end_x, end_y), dist)
889 }
890 };
891
892 let num_stops = linear_gradient.stops.len();
893
894 let mut stops = linear_gradient
895 .stops
896 .iter()
897 .enumerate()
898 .map(|(index, stop)| {
899 let pos = if let Some(pos) = &stop.position {
900 pos.to_pixels(parent_length, self.scale_factor())
901 / parent_length
902 } else {
903 index as f32 / (num_stops - 1) as f32
904 };
905 (pos, skia_safe::Color::from(stop.color))
906 })
907 .collect::<Vec<_>>();
908
909 if let Some(first) = stops.first() {
911 if first.0 != 0.0 {
912 stops.insert(0, (0.0, first.1));
913 }
914 }
915
916 if let Some(last) = stops.last() {
918 if last.0 != 1.0 {
919 stops.push((1.0, last.1));
920 }
921 }
922
923 let (offsets, colors): (Vec<f32>, Vec<skia_safe::Color>) =
924 stops.into_iter().unzip();
925 let colors4f: Vec<skia_safe::Color4f> =
926 colors.iter().copied().map(Into::into).collect();
927
928 let gradient_colors =
929 skia_safe::gradient_shader::GradientColors::new(
930 &colors4f,
931 Some(&offsets[..]),
932 TileMode::Clamp,
933 None,
934 );
935 let gradient = skia_safe::gradient_shader::Gradient::new(
936 gradient_colors,
937 skia_safe::gradient_shader::Interpolation::default(),
938 );
939 let shader = skia_safe::shaders::linear_gradient(
940 (Point::from(start), Point::from(end)),
941 &gradient,
942 None,
943 );
944
945 let mut paint = Paint::default();
946 paint.set_shader(shader);
947 paint.set_anti_alias(true);
948
949 canvas.draw_path(&path, &paint);
950 }
951
952 Gradient::Radial(radial_gradient) => {
953 let num_stops = radial_gradient.stops.len();
954
955 let mut stops = radial_gradient
956 .stops
957 .iter()
958 .enumerate()
959 .map(|(index, stop)| {
960 let pos = if let Some(pos) = &stop.position {
961 pos.to_pixels(bounds.width(), self.scale_factor())
962 / bounds.width()
963 } else {
964 index as f32 / (num_stops - 1) as f32
965 };
966
967 (pos, skia_safe::Color::from(stop.color))
968 })
969 .collect::<Vec<_>>();
970
971 if let Some(first) = stops.first() {
973 if first.0 != 0.0 {
974 stops.insert(0, (0.0, first.1));
975 }
976 }
977
978 if let Some(last) = stops.last() {
980 if last.0 != 1.0 {
981 stops.push((1.0, last.1));
982 }
983 }
984
985 let (offsets, colors): (Vec<f32>, Vec<skia_safe::Color>) =
986 stops.into_iter().unzip();
987 let colors4f: Vec<skia_safe::Color4f> =
988 colors.iter().copied().map(Into::into).collect();
989
990 let gradient_colors =
991 skia_safe::gradient_shader::GradientColors::new(
992 &colors4f,
993 Some(&offsets[..]),
994 TileMode::Clamp,
995 None,
996 );
997 let gradient = skia_safe::gradient_shader::Gradient::new(
998 gradient_colors,
999 skia_safe::gradient_shader::Interpolation::default(),
1000 );
1001 let shader = skia_safe::shaders::radial_gradient(
1002 (Point::from(bounds.center()), bounds.w.max(bounds.h)),
1003 &gradient,
1004 None,
1005 );
1006
1007 let mut paint = Paint::default();
1008 paint.set_shader(shader);
1009 paint.set_anti_alias(true);
1010
1011 canvas.draw_path(&path, &paint);
1012 }
1013
1014 _ => {}
1015 },
1016
1017 ImageOrGradient::Image(image_name) => {
1018 if let Some(image_id) = self.resource_manager.image_ids.get(image_name)
1019 {
1020 if let Some(image) = self.resource_manager.images.get(image_id) {
1021 match &image.image {
1022 ImageOrSvg::Image(image) => {
1023 let image_width = image.width();
1024 let image_height = image.height();
1025 let (width, height) = if let Some(background_size) =
1026 image_sizes.get(index)
1027 {
1028 match background_size {
1029 BackgroundSize::Explicit { width, height } => {
1030 let w = match width {
1031 LengthPercentageOrAuto::LengthPercentage(
1032 length,
1033 ) => {
1034 length.to_pixels(bounds.w, self.scale_factor())
1035 }
1036 LengthPercentageOrAuto::Auto => image_width as f32,
1037 };
1038
1039 let h = match height {
1040 LengthPercentageOrAuto::LengthPercentage(
1041 length,
1042 ) => {
1043 length.to_pixels(bounds.h, self.scale_factor())
1044 }
1045 LengthPercentageOrAuto::Auto => image_height as f32,
1046 };
1047
1048 (w, h)
1049 }
1050
1051 BackgroundSize::Contain => {
1052 let image_ratio = image_width as f32
1053 / image_height as f32;
1054 let container_ratio = bounds.w / bounds.h;
1055
1056 let (w, h) =
1057 if image_ratio > container_ratio {
1058 (bounds.w, bounds.w / image_ratio)
1059 } else {
1060 (bounds.h * image_ratio, bounds.h)
1061 };
1062
1063 (w, h)
1064 }
1065
1066 BackgroundSize::Cover => {
1067 let image_ratio = image_width as f32
1068 / image_height as f32;
1069 let container_ratio = bounds.w / bounds.h;
1070
1071 let (w, h) =
1072 if image_ratio < container_ratio {
1073 (bounds.w, bounds.w / image_ratio)
1074 } else {
1075 (bounds.h * image_ratio, bounds.h)
1076 };
1077
1078 (w, h)
1079 }
1080 }
1081 } else {
1082 (image_width as f32, image_height as f32)
1083 };
1084
1085 let posx = bounds.width() - width;
1086 let posy = bounds.height() - height;
1087
1088 let matrix = Matrix::rect_2_rect(
1089 Rect::new(
1090 0.0,
1091 0.0,
1092 image.width() as f32,
1093 image.height() as f32,
1094 ),
1095 Rect::new(
1096 bounds.left() + posx,
1097 bounds.top() + posy,
1098 bounds.left() + width,
1099 bounds.top() + height,
1100 ),
1101 None,
1102 )
1103 .unwrap_or_else(Matrix::new_identity);
1104
1105 let mut paint = Paint::default();
1106 paint.set_anti_alias(true);
1107 paint.set_shader(image.to_shader(
1108 (TileMode::Repeat, TileMode::Repeat),
1109 SamplingOptions::default(),
1110 &matrix,
1111 ));
1112
1113 canvas.draw_path(&path, &paint);
1114 }
1115
1116 ImageOrSvg::Svg(svg) => {
1117 canvas.save_layer(&SaveLayerRec::default());
1118 canvas.translate((bounds.x, bounds.y));
1119 let (scale_x, scale_y) = (
1120 bounds.width() / svg.inner().fContainerSize.fWidth,
1121 bounds.height()
1122 / svg.inner().fContainerSize.fHeight,
1123 );
1124
1125 if scale_x.is_finite() && scale_y.is_finite() {
1126 canvas.scale((scale_x, scale_y));
1127 } else {
1128 svg.clone().set_container_size((
1129 bounds.width(),
1130 bounds.height(),
1131 ));
1132 }
1133
1134 svg.render(canvas);
1135
1136 if let Some(color) = self.style.fill.get_resolved(
1137 self.current,
1138 &self.style.custom_color_props,
1139 ) {
1140 if color.a() != 0 {
1150 let mut paint = Paint::default();
1151 paint.set_anti_alias(true);
1152 paint.set_blend_mode(
1153 skia_safe::BlendMode::SrcIn,
1154 );
1155 paint.set_color(color);
1156 canvas.draw_paint(&paint);
1157 }
1158 }
1159 canvas.restore();
1160 }
1161 }
1162 }
1163 }
1164 }
1165 }
1166 }
1167 }
1168 }
1169 }
1170
1171 pub fn draw_text(&mut self, canvas: &Canvas) {
1173 if let Some(paragraph) = self.text_context.text_paragraphs.get(self.current) {
1174 let bounds = self.bounds();
1175
1176 let alignment = self.alignment();
1177
1178 let (mut top, _) = match alignment {
1179 Alignment::TopLeft => (0.0, 0.0),
1180 Alignment::TopCenter => (0.0, 0.5),
1181 Alignment::TopRight => (0.0, 1.0),
1182 Alignment::Left => (0.5, 0.0),
1183 Alignment::Center => (0.5, 0.5),
1184 Alignment::Right => (0.5, 1.0),
1185 Alignment::BottomLeft => (1.0, 0.0),
1186 Alignment::BottomCenter => (1.0, 0.5),
1187 Alignment::BottomRight => (1.0, 1.0),
1188 };
1189
1190 let padding_top = match self.padding_top() {
1191 Units::Pixels(val) => val,
1192 _ => 0.0,
1193 };
1194
1195 let padding_bottom = match self.padding_bottom() {
1196 Units::Pixels(val) => val,
1197 _ => 0.0,
1198 };
1199
1200 top *= bounds.height() - padding_top - padding_bottom - paragraph.height();
1201
1202 let mut padding_left = match self.padding_left() {
1203 Units::Pixels(val) => val,
1204 _ => 0.0,
1205 };
1206
1207 let mut padding_right = match self.padding_right() {
1208 Units::Pixels(val) => val,
1209 _ => 0.0,
1210 };
1211
1212 if resolved_text_direction(self.style, self.current) == Direction::RightToLeft {
1213 std::mem::swap(&mut padding_left, &mut padding_right);
1214 }
1215
1216 paragraph.paint(
1217 canvas,
1218 ((bounds.x + padding_left).round(), (bounds.y + padding_top + top).round()),
1219 );
1220 }
1221 }
1222}
1223
1224impl DataContext for DrawContext<'_> {
1225 fn try_data<T: 'static>(&self) -> Option<&T> {
1226 if let Some(t) = <dyn Any>::downcast_ref::<T>(&()) {
1228 return Some(t);
1229 }
1230
1231 for entity in self.current.parent_iter(self.tree) {
1232 if let Some(models) = self.models.get(&entity) {
1234 if let Some(model) = models.get(&TypeId::of::<T>()) {
1235 return model.downcast_ref::<T>();
1236 }
1237 }
1238
1239 if let Some(view_handler) = self.views.get(&entity) {
1241 if let Some(data) = view_handler.downcast_ref::<T>() {
1242 return Some(data);
1243 }
1244 }
1245 }
1246
1247 None
1248 }
1249}
1250
1251fn compute_smooth_corner(
1253 corner_radius: f32,
1254 smoothing: f32,
1255 width: f32,
1256 height: f32,
1257) -> (f32, f32, f32, f32, f32, f32, f32) {
1258 let max_p = f32::min(width, height) / 2.0;
1259 let corner_radius = f32::min(corner_radius, max_p);
1260
1261 let p = f32::min((1.0 + smoothing) * corner_radius, max_p);
1262
1263 let angle_alpha: f32;
1264 let angle_beta: f32;
1265
1266 if corner_radius <= max_p / 2.0 {
1267 angle_alpha = 45.0 * smoothing;
1268 angle_beta = 90.0 * (1.0 - smoothing);
1269 } else {
1270 let diff_ratio = (corner_radius - max_p / 2.0) / (max_p / 2.0);
1271
1272 angle_alpha = 45.0 * smoothing * (1.0 - diff_ratio);
1273 angle_beta = 90.0 * (1.0 - smoothing * (1.0 - diff_ratio));
1274 }
1275
1276 let angle_theta = (90.0 - angle_beta) / 2.0;
1277 let dist_p3_p4 = corner_radius * (angle_theta / 2.0).to_radians().tan();
1278
1279 let l = (angle_beta / 2.0).to_radians().sin() * corner_radius * SQRT_2;
1280 let c = dist_p3_p4 * angle_alpha.to_radians().cos();
1281 let d = c * angle_alpha.to_radians().tan();
1282 let b = (p - l - c - d) / 3.0;
1283 let a = 2.0 * b;
1284
1285 (a, b, c, d, l, p, corner_radius)
1286}