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 if let Some(clip_path) = self.cache.clip_path.get(self.current) {
133 return clip_path.clone();
134 }
135
136 if self.style.ignore_clipping.get(self.current).copied().unwrap_or(false) {
137 return None;
138 }
139
140 let mut current = self.current;
142 while let Some(parent) = self.tree.get_parent(current) {
143 if let Some(clip_path) = self.cache.clip_path.get(parent) {
145 return clip_path.clone();
146 }
147
148 if self.style.ignore_clipping.get(parent).copied().unwrap_or(false) {
149 return None;
150 }
151 current = parent;
152 }
153
154 None
155 }
156
157 pub fn transform(&self) -> Matrix {
159 self.cache.transform.get(self.current).copied().unwrap_or_default()
160 }
161
162 pub fn visibility(&self) -> Option<Visibility> {
164 self.style.visibility.get(self.current).copied()
165 }
166
167 pub fn display(&self) -> Display {
169 self.style.display.get(self.current).copied().unwrap_or(Display::Flex)
170 }
171
172 pub fn opacity(&self) -> f32 {
174 self.style
175 .opacity
176 .get_resolved(self.current, &self.style.custom_opacity_props)
177 .unwrap_or(Opacity(1.0))
178 .0
179 }
180
181 pub fn default_font(&self) -> &[FamilyOwned] {
183 &self.style.default_font
184 }
185
186 pub fn font_size(&self) -> f32 {
188 let fs = self
189 .style
190 .font_size
191 .get_resolved(self.current, &self.style.custom_font_size_props)
192 .and_then(|f| f.0.to_px())
193 .unwrap_or(16.0);
194 self.logical_to_physical(fs)
195 }
196
197 pub fn font_weight(&self) -> FontWeight {
199 self.style.font_weight.get(self.current).copied().unwrap_or_default()
200 }
201
202 pub fn font_width(&self) -> FontWidth {
204 self.style.font_width.get(self.current).copied().unwrap_or_default()
205 }
206
207 pub fn font_slant(&self) -> FontSlant {
209 self.style.font_slant.get(self.current).copied().unwrap_or_default()
210 }
211
212 pub fn font_variation_settings(&self) -> &[FontVariation] {
214 self.style.font_variation_settings.get(self.current).map(Vec::as_slice).unwrap_or_default()
215 }
216
217 pub fn logical_to_physical(&self, logical: f32) -> f32 {
219 self.style.logical_to_physical(logical)
220 }
221
222 pub fn physical_to_logical(&self, physical: f32) -> f32 {
224 self.style.physical_to_logical(physical)
225 }
226
227 pub fn border_top_width(&self) -> f32 {
229 let bounds = self.bounds();
230 self.style
231 .border_top_width
232 .get_resolved(self.current, &self.style.custom_length_props)
233 .map(|l| l.to_pixels(bounds.w.min(bounds.h), self.scale_factor()).round())
234 .unwrap_or(0.0)
235 }
236
237 pub fn border_right_width(&self) -> f32 {
239 let bounds = self.bounds();
240 self.style
241 .border_right_width
242 .get_resolved(self.current, &self.style.custom_length_props)
243 .map(|l| l.to_pixels(bounds.w.min(bounds.h), self.scale_factor()).round())
244 .unwrap_or(0.0)
245 }
246
247 pub fn border_bottom_width(&self) -> f32 {
249 let bounds = self.bounds();
250 self.style
251 .border_bottom_width
252 .get_resolved(self.current, &self.style.custom_length_props)
253 .map(|l| l.to_pixels(bounds.w.min(bounds.h), self.scale_factor()).round())
254 .unwrap_or(0.0)
255 }
256
257 pub fn border_left_width(&self) -> f32 {
259 let bounds = self.bounds();
260 self.style
261 .border_left_width
262 .get_resolved(self.current, &self.style.custom_length_props)
263 .map(|l| l.to_pixels(bounds.w.min(bounds.h), self.scale_factor()).round())
264 .unwrap_or(0.0)
265 }
266
267 pub fn border_top_color(&self) -> Color {
269 self.style
270 .border_top_color
271 .get_resolved(self.current, &self.style.custom_color_props)
272 .map(|c| Color::rgba(c.r(), c.g(), c.b(), c.a()))
273 .unwrap_or(Color::rgba(0, 0, 0, 0))
274 }
275
276 pub fn border_right_color(&self) -> Color {
278 self.style
279 .border_right_color
280 .get_resolved(self.current, &self.style.custom_color_props)
281 .map(|c| Color::rgba(c.r(), c.g(), c.b(), c.a()))
282 .unwrap_or(Color::rgba(0, 0, 0, 0))
283 }
284
285 pub fn border_bottom_color(&self) -> Color {
287 self.style
288 .border_bottom_color
289 .get_resolved(self.current, &self.style.custom_color_props)
290 .map(|c| Color::rgba(c.r(), c.g(), c.b(), c.a()))
291 .unwrap_or(Color::rgba(0, 0, 0, 0))
292 }
293
294 pub fn border_left_color(&self) -> Color {
296 self.style
297 .border_left_color
298 .get_resolved(self.current, &self.style.custom_color_props)
299 .map(|c| Color::rgba(c.r(), c.g(), c.b(), c.a()))
300 .unwrap_or(Color::rgba(0, 0, 0, 0))
301 }
302
303 pub fn border_top_style(&self) -> BorderStyleKeyword {
305 self.style.border_top_style.get(self.current).copied().unwrap_or_default()
306 }
307
308 pub fn border_right_style(&self) -> BorderStyleKeyword {
310 self.style.border_right_style.get(self.current).copied().unwrap_or_default()
311 }
312
313 pub fn border_bottom_style(&self) -> BorderStyleKeyword {
315 self.style.border_bottom_style.get(self.current).copied().unwrap_or_default()
316 }
317
318 pub fn border_left_style(&self) -> BorderStyleKeyword {
320 self.style.border_left_style.get(self.current).copied().unwrap_or_default()
321 }
322
323 pub fn outline_color(&self) -> Color {
325 if let Some(col) =
326 self.style.outline_color.get_resolved(self.current, &self.style.custom_color_props)
327 {
328 Color::rgba(col.r(), col.g(), col.b(), col.a())
329 } else {
330 Color::rgba(0, 0, 0, 0)
331 }
332 }
333
334 pub fn outline_width(&self) -> f32 {
336 if let Some(length) =
337 self.style.outline_width.get_resolved(self.current, &self.style.custom_length_props)
338 {
339 let bounds = self.bounds();
340 return length.to_pixels(bounds.w.min(bounds.h), self.scale_factor()).round();
341 }
342 0.0
343 }
344
345 pub fn outline_offset(&self) -> f32 {
347 if let Some(length) =
348 self.style.outline_offset.get_resolved(self.current, &self.style.custom_length_props)
349 {
350 let bounds = self.bounds();
351 return length.to_pixels(bounds.w.min(bounds.h), self.scale_factor()).round();
352 }
353 0.0
354 }
355
356 pub fn corner_top_left_radius(&self) -> f32 {
358 let bounds = self.bounds();
359 let scale = self.scale_factor();
360 self.style
361 .corner_top_left_radius
362 .get_resolved(self.current, &self.style.custom_length_props)
363 .map(|l| l.to_pixels(bounds.w.min(bounds.h), scale).round())
364 .unwrap_or(0.0)
365 }
366
367 pub fn corner_top_right_radius(&self) -> f32 {
369 let bounds = self.bounds();
370 let scale = self.scale_factor();
371 self.style
372 .corner_top_right_radius
373 .get_resolved(self.current, &self.style.custom_length_props)
374 .map(|l| l.to_pixels(bounds.w.min(bounds.h), scale).round())
375 .unwrap_or(0.0)
376 }
377
378 pub fn corner_bottom_left_radius(&self) -> f32 {
380 let bounds = self.bounds();
381 let scale = self.scale_factor();
382 self.style
383 .corner_bottom_left_radius
384 .get_resolved(self.current, &self.style.custom_length_props)
385 .map(|l| l.to_pixels(bounds.w.min(bounds.h), scale).round())
386 .unwrap_or(0.0)
387 }
388
389 pub fn corner_bottom_right_radius(&self) -> f32 {
391 let bounds = self.bounds();
392 let scale = self.scale_factor();
393 self.style
394 .corner_bottom_right_radius
395 .get_resolved(self.current, &self.style.custom_length_props)
396 .map(|l| l.to_pixels(bounds.w.min(bounds.h), scale).round())
397 .unwrap_or(0.0)
398 }
399
400 pub fn corner_top_left_shape(&self) -> CornerShape {
402 self.style.corner_top_left_shape.get(self.current).copied().unwrap_or_default()
403 }
404
405 pub fn corner_top_right_shape(&self) -> CornerShape {
407 self.style.corner_top_right_shape.get(self.current).copied().unwrap_or_default()
408 }
409
410 pub fn corner_bottom_left_shape(&self) -> CornerShape {
412 self.style.corner_bottom_left_shape.get(self.current).copied().unwrap_or_default()
413 }
414
415 pub fn corner_bottom_right_shape(&self) -> CornerShape {
417 self.style.corner_bottom_right_shape.get(self.current).copied().unwrap_or_default()
418 }
419
420 pub fn corner_top_left_smoothing(&self) -> f32 {
422 self.style.corner_top_left_smoothing.get(self.current).copied().unwrap_or_default()
423 }
424
425 pub fn corner_top_right_smoothing(&self) -> f32 {
427 self.style.corner_top_right_smoothing.get(self.current).copied().unwrap_or_default()
428 }
429
430 pub fn corner_bottom_left_smoothing(&self) -> f32 {
432 self.style.corner_bottom_left_smoothing.get(self.current).copied().unwrap_or_default()
433 }
434
435 pub fn corner_bottom_right_smoothing(&self) -> f32 {
437 self.style.corner_bottom_right_smoothing.get(self.current).copied().unwrap_or_default()
438 }
439
440 get_units_property!(
441 padding_left
443 );
444
445 get_units_property!(
446 padding_right
448 );
449
450 get_units_property!(
451 padding_top
453 );
454
455 get_units_property!(
456 padding_bottom
458 );
459
460 pub fn alignment(&self) -> Alignment {
462 self.style.alignment.get(self.current).copied().unwrap_or_default()
463 }
464
465 pub fn background_color(&self) -> Color {
467 if let Some(col) =
468 self.style.background_color.get_resolved(self.current, &self.style.custom_color_props)
469 {
470 Color::rgba(col.r(), col.g(), col.b(), col.a())
471 } else {
472 Color::rgba(0, 0, 0, 0)
473 }
474 }
475
476 pub fn border_color(&self) -> Color {
479 self.border_top_color()
480 }
481
482 pub fn border_style(&self) -> BorderStyleKeyword {
485 self.border_top_style()
486 }
487
488 pub fn border_width(&self) -> f32 {
491 self.border_top_width()
492 }
493
494 pub fn selection_color(&self) -> Color {
496 if let Some(col) =
497 self.style.selection_color.get_resolved(self.current, &self.style.custom_color_props)
498 {
499 Color::rgba(col.r(), col.g(), col.b(), col.a())
500 } else {
501 Color::rgba(0, 0, 0, 0)
502 }
503 }
504
505 pub fn caret_color(&self) -> Color {
507 if let Some(col) =
508 self.style.caret_color.get_resolved(self.current, &self.style.custom_color_props)
509 {
510 Color::rgba(col.r(), col.g(), col.b(), col.a())
511 } else {
512 Color::rgba(0, 0, 0, 0)
513 }
514 }
515
516 pub fn font_color(&self) -> Color {
518 if let Some(col) =
519 self.style.font_color.get_resolved(self.current, &self.style.custom_color_props)
520 {
521 Color::rgba(col.r(), col.g(), col.b(), col.a())
522 } else {
523 Color::rgba(0, 0, 0, 0)
524 }
525 }
526
527 pub fn text_wrap(&self) -> bool {
529 self.style.text_wrap.get(self.current).copied().unwrap_or(true)
530 }
531
532 pub fn text_align(&self) -> TextAlign {
534 self.style.text_align.get(self.current).copied().unwrap_or_default()
535 }
536
537 pub fn text_overflow(&self) -> TextOverflow {
539 self.style.text_overflow.get(self.current).copied().unwrap_or_default()
540 }
541
542 pub fn line_clamp(&self) -> Option<usize> {
544 self.style.line_clamp.get(self.current).copied().map(|lc| lc.0 as usize)
545 }
546
547 pub fn shadows(&self) -> Option<Vec<Shadow>> {
549 self.style.shadow.get_resolved(self.current, &self.style.custom_shadow_props)
550 }
551
552 pub fn filter(&self) -> Option<&Filter> {
554 self.style.filter.get(self.current)
555 }
556
557 pub fn backdrop_filter(&self) -> Option<&Filter> {
559 self.style.backdrop_filter.get(self.current)
560 }
561
562 pub fn background_images(&self) -> Option<&Vec<ImageOrGradient>> {
564 self.style.background_image.get(self.current)
565 }
566
567 pub fn background_size(&self) -> Vec<BackgroundSize> {
569 self.style.background_size.get(self.current).cloned().unwrap_or_default()
570 }
571
572 pub fn background_position(&self) -> Vec<Position> {
574 self.style.background_position.get(self.current).cloned().unwrap_or_default()
575 }
576
577 pub fn background_repeat(&self) -> Vec<BackgroundRepeat> {
579 self.style.background_repeat.get(self.current).cloned().unwrap_or_default()
580 }
581
582 pub fn path(&mut self) -> Path {
583 if self.cache.path.get(self.current).is_none() {
584 self.cache.path.insert(self.current, self.build_path(self.bounds(), (0.0, 0.0)));
585 }
586 let bounds = self.bounds();
587 self.cache.path.get(self.current).unwrap().make_offset(bounds.top_left())
588 }
589
590 pub fn build_path(&self, bounds: BoundingBox, outset: (f32, f32)) -> Path {
592 self.build_path_with_corners(
593 bounds,
594 outset,
595 (
596 self.corner_top_left_radius(),
597 self.corner_top_right_radius(),
598 self.corner_bottom_right_radius(),
599 self.corner_bottom_left_radius(),
600 ),
601 (
602 self.corner_top_left_shape(),
603 self.corner_top_right_shape(),
604 self.corner_bottom_right_shape(),
605 self.corner_bottom_left_shape(),
606 ),
607 (
608 self.corner_top_left_smoothing(),
609 self.corner_top_right_smoothing(),
610 self.corner_bottom_right_smoothing(),
611 self.corner_bottom_left_smoothing(),
612 ),
613 )
614 }
615
616 fn build_path_with_corners(
617 &self,
618 bounds: BoundingBox,
619 outset: (f32, f32),
620 corner_radii: (f32, f32, f32, f32),
621 corner_shapes: (CornerShape, CornerShape, CornerShape, CornerShape),
622 corner_smoothing: (f32, f32, f32, f32),
623 ) -> Path {
624 let (
625 corner_top_left_radius,
626 corner_top_right_radius,
627 corner_bottom_right_radius,
628 corner_bottom_left_radius,
629 ) = corner_radii;
630 let (
631 corner_top_left_shape,
632 corner_top_right_shape,
633 corner_bottom_right_shape,
634 corner_bottom_left_shape,
635 ) = corner_shapes;
636 let (
637 corner_top_left_smoothing,
638 corner_top_right_smoothing,
639 corner_bottom_right_smoothing,
640 corner_bottom_left_smoothing,
641 ) = corner_smoothing;
642
643 let bounds = BoundingBox::from_min_max(0.0, 0.0, bounds.w, bounds.h);
644
645 let rect: Rect = bounds.into();
646
647 let mut rr = RRect::new_rect_radii(
648 rect,
649 &[
650 Point::new(corner_top_left_radius, corner_top_left_radius),
651 Point::new(corner_top_right_radius, corner_top_right_radius),
652 Point::new(corner_bottom_right_radius, corner_bottom_right_radius),
653 Point::new(corner_bottom_left_radius, corner_bottom_left_radius),
654 ],
655 );
656
657 rr = rr.with_outset(outset);
658
659 let x = rr.bounds().x();
660 let y = rr.bounds().y();
661 let width = rr.width();
662 let height = rr.height();
663 let mut should_offset = true;
664
665 let mut path = PathBuilder::new();
666
667 if width == height
668 && corner_bottom_left_radius == width / 2.0
669 && corner_bottom_right_radius == width / 2.0
670 && corner_top_left_radius == height / 2.0
671 && corner_top_right_radius == height / 2.0
672 {
673 path.add_circle((width / 2.0, height / 2.0), width / 2.0, Some(PathDirection::CW));
674 } else if corner_top_left_radius == corner_top_right_radius
675 && corner_top_right_radius == corner_bottom_right_radius
676 && corner_bottom_right_radius == corner_bottom_left_radius
677 && corner_top_left_smoothing == 0.0
678 && corner_top_left_smoothing == corner_top_right_smoothing
679 && corner_top_right_smoothing == corner_bottom_right_smoothing
680 && corner_bottom_right_smoothing == corner_bottom_left_smoothing
681 && corner_top_left_shape == CornerShape::Round
682 && corner_top_left_shape == corner_top_right_shape
683 && corner_top_right_shape == corner_bottom_right_shape
684 && corner_bottom_right_shape == corner_bottom_left_shape
685 {
686 path.add_rrect(rr, None, None);
687 should_offset = false;
688 } else {
689 let top_right = rr.radii(Corner::UpperRight).x;
690
691 if top_right > 0.0 {
692 let (a, b, c, d, l, p, radius) =
693 compute_smooth_corner(top_right, corner_top_right_smoothing, width, height);
694
695 path.move_to((f32::max(width / 2.0, width - p), 0.0));
696 if corner_top_right_shape == CornerShape::Round {
697 path.cubic_to(
698 (width - (p - a), 0.0),
699 (width - (p - a - b), 0.0),
700 (width - (p - a - b - c), d),
701 )
702 .r_arc_to((radius, radius), 0.0, ArcSize::Small, PathDirection::CW, (l, l))
703 .cubic_to(
704 (width, p - a - b),
705 (width, p - a),
706 (width, f32::min(height / 2.0, p)),
707 );
708 } else {
709 path.line_to((width, f32::min(height / 2.0, p)));
710 }
711 } else {
712 path.move_to((width / 2.0, 0.0))
713 .line_to((width, 0.0))
714 .line_to((width, height / 2.0));
715 }
716
717 let bottom_right = rr.radii(Corner::LowerRight).x;
718 if bottom_right > 0.0 {
719 let (a, b, c, d, l, p, radius) = compute_smooth_corner(
720 bottom_right,
721 corner_bottom_right_smoothing,
722 width,
723 height,
724 );
725
726 path.line_to((width, f32::max(height / 2.0, height - p)));
727 if corner_bottom_right_shape == CornerShape::Round {
728 path.cubic_to(
729 (width, height - (p - a)),
730 (width, height - (p - a - b)),
731 (width - d, height - (p - a - b - c)),
732 )
733 .r_arc_to((radius, radius), 0.0, ArcSize::Small, PathDirection::CW, (-l, l))
734 .cubic_to(
735 (width - (p - a - b), height),
736 (width - (p - a), height),
737 (f32::max(width / 2.0, width - p), height),
738 );
739 } else {
740 path.line_to((f32::max(width / 2.0, width - p), height));
741 }
742 } else {
743 path.line_to((width, height)).line_to((width / 2.0, height));
744 }
745
746 let bottom_left = rr.radii(Corner::LowerLeft).x;
747 if bottom_left > 0.0 {
748 let (a, b, c, d, l, p, radius) =
749 compute_smooth_corner(bottom_left, corner_bottom_left_smoothing, width, height);
750
751 path.line_to((f32::min(width / 2.0, p), height));
752 if corner_bottom_left_shape == CornerShape::Round {
753 path.cubic_to(
754 (p - a, height),
755 (p - a - b, height),
756 (p - a - b - c, height - d),
757 )
758 .r_arc_to((radius, radius), 0.0, ArcSize::Small, PathDirection::CW, (-l, -l))
759 .cubic_to(
760 (0.0, height - (p - a - b)),
761 (0.0, height - (p - a)),
762 (0.0, f32::max(height / 2.0, height - p)),
763 );
764 } else {
765 path.line_to((0.0, f32::max(height / 2.0, height - p)));
766 }
767 } else {
768 path.line_to((0.0, height)).line_to((0.0, height / 2.0));
769 }
770
771 let top_left = rr.radii(Corner::UpperLeft).x;
772 if top_left > 0.0 {
773 let (a, b, c, d, l, p, radius) =
774 compute_smooth_corner(top_left, corner_top_left_smoothing, width, height);
775
776 path.line_to((0.0, f32::min(height / 2.0, p)));
777 if corner_top_left_shape == CornerShape::Round {
778 path.cubic_to((0.0, p - a), (0.0, p - a - b), (d, p - a - b - c))
779 .r_arc_to((radius, radius), 0.0, ArcSize::Small, PathDirection::CW, (l, -l))
780 .cubic_to((p - a - b, 0.0), (p - a, 0.0), (f32::min(width / 2.0, p), 0.0));
781 } else {
782 path.line_to((f32::min(width / 2.0, p), 0.0));
783 }
784 } else {
785 path.line_to((0.0, 0.0));
786 }
787
788 path.close();
789 }
790
791 let path = path.detach();
792 if should_offset { path.make_offset((x, y)) } else { path }
793 }
794
795 fn corner_oval(center_x: f32, center_y: f32, radius: f32) -> Rect {
796 Rect::from_xywh(center_x - radius, center_y - radius, radius * 2.0, radius * 2.0)
797 }
798
799 fn round_corner_side_path(
800 side_ix: usize,
801 bounds: BoundingBox,
802 outer_radii: (f32, f32, f32, f32),
803 inner_radii: (f32, f32, f32, f32),
804 widths: (f32, f32, f32, f32),
805 ) -> Option<Path> {
806 let bx = bounds.x;
807 let by = bounds.y;
808 let bw = bounds.w;
809 let bh = bounds.h;
810
811 let (r_tl, r_tr, r_br, r_bl) = outer_radii;
812 let (ir_tl, ir_tr, ir_br, ir_bl) = inner_radii;
813 let (top_width, right_width, bottom_width, left_width) = widths;
814
815 let mut path = PathBuilder::new();
816 let diag = SQRT_2.recip();
817
818 let outer_tl_center = Point::new(bx + r_tl, by + r_tl);
819 let outer_tr_center = Point::new(bx + bw - r_tr, by + r_tr);
820 let outer_br_center = Point::new(bx + bw - r_br, by + bh - r_br);
821 let outer_bl_center = Point::new(bx + r_bl, by + bh - r_bl);
822
823 let inner_tl_center = Point::new(bx + left_width + ir_tl, by + top_width + ir_tl);
824 let inner_tr_center = Point::new(bx + bw - right_width - ir_tr, by + top_width + ir_tr);
825 let inner_br_center =
826 Point::new(bx + bw - right_width - ir_br, by + bh - bottom_width - ir_br);
827 let inner_bl_center = Point::new(bx + left_width + ir_bl, by + bh - bottom_width - ir_bl);
828
829 match side_ix {
830 0 => {
831 let outer_start = if r_tl > 0.0 {
832 Point::new(outer_tl_center.x - r_tl * diag, outer_tl_center.y - r_tl * diag)
833 } else {
834 Point::new(bx, by)
835 };
836 path.move_to(outer_start);
837 if r_tl > 0.0 {
838 path.arc_to(
839 Self::corner_oval(outer_tl_center.x, outer_tl_center.y, r_tl),
840 225.0,
841 45.0,
842 false,
843 );
844 }
845 path.line_to((bx + bw - r_tr, by));
846 if r_tr > 0.0 {
847 path.arc_to(
848 Self::corner_oval(outer_tr_center.x, outer_tr_center.y, r_tr),
849 270.0,
850 45.0,
851 false,
852 );
853 }
854
855 let inner_split = if ir_tr > 0.0 {
856 Point::new(inner_tr_center.x + ir_tr * diag, inner_tr_center.y - ir_tr * diag)
857 } else {
858 Point::new(bx + bw - right_width, by + top_width)
859 };
860 path.line_to(inner_split);
861 if ir_tr > 0.0 {
862 path.arc_to(
863 Self::corner_oval(inner_tr_center.x, inner_tr_center.y, ir_tr),
864 315.0,
865 -45.0,
866 false,
867 );
868 }
869 path.line_to((bx + left_width + ir_tl, by + top_width));
870 if ir_tl > 0.0 {
871 path.arc_to(
872 Self::corner_oval(inner_tl_center.x, inner_tl_center.y, ir_tl),
873 270.0,
874 -45.0,
875 false,
876 );
877 }
878 }
879 1 => {
880 let outer_start = if r_tr > 0.0 {
881 Point::new(outer_tr_center.x + r_tr * diag, outer_tr_center.y - r_tr * diag)
882 } else {
883 Point::new(bx + bw, by)
884 };
885 path.move_to(outer_start);
886 if r_tr > 0.0 {
887 path.arc_to(
888 Self::corner_oval(outer_tr_center.x, outer_tr_center.y, r_tr),
889 315.0,
890 45.0,
891 false,
892 );
893 }
894 path.line_to((bx + bw, by + bh - r_br));
895 if r_br > 0.0 {
896 path.arc_to(
897 Self::corner_oval(outer_br_center.x, outer_br_center.y, r_br),
898 0.0,
899 45.0,
900 false,
901 );
902 }
903
904 let inner_split = if ir_br > 0.0 {
905 Point::new(inner_br_center.x + ir_br * diag, inner_br_center.y + ir_br * diag)
906 } else {
907 Point::new(bx + bw - right_width, by + bh - bottom_width)
908 };
909 path.line_to(inner_split);
910 if ir_br > 0.0 {
911 path.arc_to(
912 Self::corner_oval(inner_br_center.x, inner_br_center.y, ir_br),
913 45.0,
914 -45.0,
915 false,
916 );
917 }
918 path.line_to((bx + bw - right_width, by + top_width + ir_tr));
919 if ir_tr > 0.0 {
920 path.arc_to(
921 Self::corner_oval(inner_tr_center.x, inner_tr_center.y, ir_tr),
922 0.0,
923 -45.0,
924 false,
925 );
926 }
927 }
928 2 => {
929 let outer_start = if r_br > 0.0 {
930 Point::new(outer_br_center.x + r_br * diag, outer_br_center.y + r_br * diag)
931 } else {
932 Point::new(bx + bw, by + bh)
933 };
934 path.move_to(outer_start);
935 if r_br > 0.0 {
936 path.arc_to(
937 Self::corner_oval(outer_br_center.x, outer_br_center.y, r_br),
938 45.0,
939 45.0,
940 false,
941 );
942 }
943 path.line_to((bx + r_bl, by + bh));
944 if r_bl > 0.0 {
945 path.arc_to(
946 Self::corner_oval(outer_bl_center.x, outer_bl_center.y, r_bl),
947 90.0,
948 45.0,
949 false,
950 );
951 }
952
953 let inner_split = if ir_bl > 0.0 {
954 Point::new(inner_bl_center.x - ir_bl * diag, inner_bl_center.y + ir_bl * diag)
955 } else {
956 Point::new(bx + left_width, by + bh - bottom_width)
957 };
958 path.line_to(inner_split);
959 if ir_bl > 0.0 {
960 path.arc_to(
961 Self::corner_oval(inner_bl_center.x, inner_bl_center.y, ir_bl),
962 135.0,
963 -45.0,
964 false,
965 );
966 }
967 path.line_to((bx + bw - right_width - ir_br, by + bh - bottom_width));
968 if ir_br > 0.0 {
969 path.arc_to(
970 Self::corner_oval(inner_br_center.x, inner_br_center.y, ir_br),
971 90.0,
972 -45.0,
973 false,
974 );
975 }
976 }
977 3 => {
978 let outer_start = if r_bl > 0.0 {
979 Point::new(outer_bl_center.x - r_bl * diag, outer_bl_center.y + r_bl * diag)
980 } else {
981 Point::new(bx, by + bh)
982 };
983 path.move_to(outer_start);
984 if r_bl > 0.0 {
985 path.arc_to(
986 Self::corner_oval(outer_bl_center.x, outer_bl_center.y, r_bl),
987 135.0,
988 45.0,
989 false,
990 );
991 }
992 path.line_to((bx, by + r_tl));
993 if r_tl > 0.0 {
994 path.arc_to(
995 Self::corner_oval(outer_tl_center.x, outer_tl_center.y, r_tl),
996 180.0,
997 45.0,
998 false,
999 );
1000 }
1001
1002 let inner_split = if ir_tl > 0.0 {
1003 Point::new(inner_tl_center.x - ir_tl * diag, inner_tl_center.y - ir_tl * diag)
1004 } else {
1005 Point::new(bx + left_width, by + top_width)
1006 };
1007 path.line_to(inner_split);
1008 if ir_tl > 0.0 {
1009 path.arc_to(
1010 Self::corner_oval(inner_tl_center.x, inner_tl_center.y, ir_tl),
1011 225.0,
1012 -45.0,
1013 false,
1014 );
1015 }
1016 path.line_to((bx + left_width, by + bh - bottom_width - ir_bl));
1017 if ir_bl > 0.0 {
1018 path.arc_to(
1019 Self::corner_oval(inner_bl_center.x, inner_bl_center.y, ir_bl),
1020 180.0,
1021 -45.0,
1022 false,
1023 );
1024 }
1025 }
1026 _ => return None,
1027 }
1028
1029 path.close();
1030 Some(path.detach())
1031 }
1032
1033 fn bevel_corner_side_path(
1034 side_ix: usize,
1035 bounds: BoundingBox,
1036 outer_radii: (f32, f32, f32, f32),
1037 inner_radii: (f32, f32, f32, f32),
1038 widths: (f32, f32, f32, f32),
1039 ) -> Option<Path> {
1040 let bx = bounds.x;
1041 let by = bounds.y;
1042 let bw = bounds.w;
1043 let bh = bounds.h;
1044
1045 let (r_tl, r_tr, r_br, r_bl) = outer_radii;
1046 let (ir_tl, ir_tr, ir_br, ir_bl) = inner_radii;
1047 let (top_width, right_width, bottom_width, left_width) = widths;
1048
1049 let otl = r_tl.min(bw * 0.5).min(bh * 0.5);
1050 let otr = r_tr.min(bw * 0.5).min(bh * 0.5);
1051 let obr = r_br.min(bw * 0.5).min(bh * 0.5);
1052 let obl = r_bl.min(bw * 0.5).min(bh * 0.5);
1053
1054 let itl = ir_tl
1055 .max(0.0)
1056 .min((bw - left_width - right_width).max(0.0) * 0.5)
1057 .min((bh - top_width - bottom_width).max(0.0) * 0.5);
1058 let itr = ir_tr
1059 .max(0.0)
1060 .min((bw - left_width - right_width).max(0.0) * 0.5)
1061 .min((bh - top_width - bottom_width).max(0.0) * 0.5);
1062 let ibr = ir_br
1063 .max(0.0)
1064 .min((bw - left_width - right_width).max(0.0) * 0.5)
1065 .min((bh - top_width - bottom_width).max(0.0) * 0.5);
1066 let ibl = ir_bl
1067 .max(0.0)
1068 .min((bw - left_width - right_width).max(0.0) * 0.5)
1069 .min((bh - top_width - bottom_width).max(0.0) * 0.5);
1070
1071 let outer_tl_top = Point::new(bx + otl, by);
1072 let outer_tl_left = Point::new(bx, by + otl);
1073 let outer_tr_top = Point::new(bx + bw - otr, by);
1074 let outer_tr_right = Point::new(bx + bw, by + otr);
1075 let outer_br_right = Point::new(bx + bw, by + bh - obr);
1076 let outer_br_bottom = Point::new(bx + bw - obr, by + bh);
1077 let outer_bl_bottom = Point::new(bx + obl, by + bh);
1078 let outer_bl_left = Point::new(bx, by + bh - obl);
1079
1080 let inner_tl_top = Point::new(bx + left_width + itl, by + top_width);
1081 let inner_tl_left = Point::new(bx + left_width, by + top_width + itl);
1082 let inner_tr_top = Point::new(bx + bw - right_width - itr, by + top_width);
1083 let inner_tr_right = Point::new(bx + bw - right_width, by + top_width + itr);
1084 let inner_br_right = Point::new(bx + bw - right_width, by + bh - bottom_width - ibr);
1085 let inner_br_bottom = Point::new(bx + bw - right_width - ibr, by + bh - bottom_width);
1086 let inner_bl_bottom = Point::new(bx + left_width + ibl, by + bh - bottom_width);
1087 let inner_bl_left = Point::new(bx + left_width, by + bh - bottom_width - ibl);
1088
1089 let mut path = PathBuilder::new();
1090 match side_ix {
1091 0 => {
1092 path.move_to(outer_tl_left);
1093 path.line_to(outer_tl_top);
1094 path.line_to(outer_tr_top);
1095 path.line_to(outer_tr_right);
1096 path.line_to(inner_tr_right);
1097 path.line_to(inner_tr_top);
1098 path.line_to(inner_tl_top);
1099 path.line_to(inner_tl_left);
1100 }
1101 1 => {
1102 path.move_to(outer_tr_top);
1103 path.line_to(outer_tr_right);
1104 path.line_to(outer_br_right);
1105 path.line_to(outer_br_bottom);
1106 path.line_to(inner_br_bottom);
1107 path.line_to(inner_br_right);
1108 path.line_to(inner_tr_right);
1109 path.line_to(inner_tr_top);
1110 }
1111 2 => {
1112 path.move_to(outer_br_right);
1113 path.line_to(outer_br_bottom);
1114 path.line_to(outer_bl_bottom);
1115 path.line_to(outer_bl_left);
1116 path.line_to(inner_bl_left);
1117 path.line_to(inner_bl_bottom);
1118 path.line_to(inner_br_bottom);
1119 path.line_to(inner_br_right);
1120 }
1121 3 => {
1122 path.move_to(outer_bl_bottom);
1123 path.line_to(outer_bl_left);
1124 path.line_to(outer_tl_left);
1125 path.line_to(outer_tl_top);
1126 path.line_to(inner_tl_top);
1127 path.line_to(inner_tl_left);
1128 path.line_to(inner_bl_left);
1129 path.line_to(inner_bl_bottom);
1130 }
1131 _ => return None,
1132 }
1133
1134 path.close();
1135 Some(path.detach())
1136 }
1137
1138 fn round_corner_side_stroke_path(
1139 side_ix: usize,
1140 bounds: BoundingBox,
1141 outer_radii: (f32, f32, f32, f32),
1142 ) -> Option<Path> {
1143 let bx = bounds.x;
1144 let by = bounds.y;
1145 let bw = bounds.w;
1146 let bh = bounds.h;
1147
1148 let (r_tl, r_tr, r_br, r_bl) = outer_radii;
1149
1150 let mut path = PathBuilder::new();
1151 let diag = SQRT_2.recip();
1152
1153 let outer_tl_center = Point::new(bx + r_tl, by + r_tl);
1154 let outer_tr_center = Point::new(bx + bw - r_tr, by + r_tr);
1155 let outer_br_center = Point::new(bx + bw - r_br, by + bh - r_br);
1156 let outer_bl_center = Point::new(bx + r_bl, by + bh - r_bl);
1157
1158 match side_ix {
1159 0 => {
1160 let outer_start = if r_tl > 0.0 {
1161 Point::new(outer_tl_center.x - r_tl * diag, outer_tl_center.y - r_tl * diag)
1162 } else {
1163 Point::new(bx, by)
1164 };
1165 path.move_to(outer_start);
1166 if r_tl > 0.0 {
1167 path.arc_to(
1168 Self::corner_oval(outer_tl_center.x, outer_tl_center.y, r_tl),
1169 225.0,
1170 45.0,
1171 false,
1172 );
1173 }
1174 path.line_to((bx + bw - r_tr, by));
1175 if r_tr > 0.0 {
1176 path.arc_to(
1177 Self::corner_oval(outer_tr_center.x, outer_tr_center.y, r_tr),
1178 270.0,
1179 45.0,
1180 false,
1181 );
1182 }
1183 }
1184 1 => {
1185 let outer_start = if r_tr > 0.0 {
1186 Point::new(outer_tr_center.x + r_tr * diag, outer_tr_center.y - r_tr * diag)
1187 } else {
1188 Point::new(bx + bw, by)
1189 };
1190 path.move_to(outer_start);
1191 if r_tr > 0.0 {
1192 path.arc_to(
1193 Self::corner_oval(outer_tr_center.x, outer_tr_center.y, r_tr),
1194 315.0,
1195 45.0,
1196 false,
1197 );
1198 }
1199 path.line_to((bx + bw, by + bh - r_br));
1200 if r_br > 0.0 {
1201 path.arc_to(
1202 Self::corner_oval(outer_br_center.x, outer_br_center.y, r_br),
1203 0.0,
1204 45.0,
1205 false,
1206 );
1207 }
1208 }
1209 2 => {
1210 let outer_start = if r_br > 0.0 {
1211 Point::new(outer_br_center.x + r_br * diag, outer_br_center.y + r_br * diag)
1212 } else {
1213 Point::new(bx + bw, by + bh)
1214 };
1215 path.move_to(outer_start);
1216 if r_br > 0.0 {
1217 path.arc_to(
1218 Self::corner_oval(outer_br_center.x, outer_br_center.y, r_br),
1219 45.0,
1220 45.0,
1221 false,
1222 );
1223 }
1224 path.line_to((bx + r_bl, by + bh));
1225 if r_bl > 0.0 {
1226 path.arc_to(
1227 Self::corner_oval(outer_bl_center.x, outer_bl_center.y, r_bl),
1228 90.0,
1229 45.0,
1230 false,
1231 );
1232 }
1233 }
1234 3 => {
1235 let outer_start = if r_bl > 0.0 {
1236 Point::new(outer_bl_center.x - r_bl * diag, outer_bl_center.y + r_bl * diag)
1237 } else {
1238 Point::new(bx, by + bh)
1239 };
1240 path.move_to(outer_start);
1241 if r_bl > 0.0 {
1242 path.arc_to(
1243 Self::corner_oval(outer_bl_center.x, outer_bl_center.y, r_bl),
1244 135.0,
1245 45.0,
1246 false,
1247 );
1248 }
1249 path.line_to((bx, by + r_tl));
1250 if r_tl > 0.0 {
1251 path.arc_to(
1252 Self::corner_oval(outer_tl_center.x, outer_tl_center.y, r_tl),
1253 180.0,
1254 45.0,
1255 false,
1256 );
1257 }
1258 }
1259 _ => return None,
1260 }
1261
1262 Some(path.detach())
1263 }
1264
1265 fn bevel_corner_side_stroke_path(
1266 side_ix: usize,
1267 bounds: BoundingBox,
1268 outer_radii: (f32, f32, f32, f32),
1269 ) -> Option<Path> {
1270 let bx = bounds.x;
1271 let by = bounds.y;
1272 let bw = bounds.w;
1273 let bh = bounds.h;
1274
1275 let (r_tl, r_tr, r_br, r_bl) = outer_radii;
1276
1277 let otl = r_tl.min(bw * 0.5).min(bh * 0.5);
1278 let otr = r_tr.min(bw * 0.5).min(bh * 0.5);
1279 let obr = r_br.min(bw * 0.5).min(bh * 0.5);
1280 let obl = r_bl.min(bw * 0.5).min(bh * 0.5);
1281
1282 let outer_tl_top = Point::new(bx + otl, by);
1283 let outer_tl_left = Point::new(bx, by + otl);
1284 let outer_tr_top = Point::new(bx + bw - otr, by);
1285 let outer_tr_right = Point::new(bx + bw, by + otr);
1286 let outer_br_right = Point::new(bx + bw, by + bh - obr);
1287 let outer_br_bottom = Point::new(bx + bw - obr, by + bh);
1288 let outer_bl_bottom = Point::new(bx + obl, by + bh);
1289 let outer_bl_left = Point::new(bx, by + bh - obl);
1290
1291 let mut path = PathBuilder::new();
1292 match side_ix {
1293 0 => {
1294 path.move_to(outer_tl_left);
1295 path.line_to(outer_tl_top);
1296 path.line_to(outer_tr_top);
1297 path.line_to(outer_tr_right);
1298 }
1299 1 => {
1300 path.move_to(outer_tr_top);
1301 path.line_to(outer_tr_right);
1302 path.line_to(outer_br_right);
1303 path.line_to(outer_br_bottom);
1304 }
1305 2 => {
1306 path.move_to(outer_br_right);
1307 path.line_to(outer_br_bottom);
1308 path.line_to(outer_bl_bottom);
1309 path.line_to(outer_bl_left);
1310 }
1311 3 => {
1312 path.move_to(outer_bl_bottom);
1313 path.line_to(outer_bl_left);
1314 path.line_to(outer_tl_left);
1315 path.line_to(outer_tl_top);
1316 }
1317 _ => return None,
1318 }
1319
1320 Some(path.detach())
1321 }
1322
1323 fn smooth_corner_side_path(
1324 side_ix: usize,
1325 bounds: BoundingBox,
1326 outer_radii: (f32, f32, f32, f32),
1327 inner_radii: (f32, f32, f32, f32),
1328 corner_shapes: (CornerShape, CornerShape, CornerShape, CornerShape),
1329 corner_smoothing: (f32, f32, f32, f32),
1330 widths: (f32, f32, f32, f32),
1331 ) -> Option<Path> {
1332 let bx = bounds.x;
1333 let by = bounds.y;
1334 let bw = bounds.w;
1335 let bh = bounds.h;
1336
1337 let (r_tl, r_tr, r_br, r_bl) = outer_radii;
1338 let (ir_tl, ir_tr, ir_br, ir_bl) = inner_radii;
1339 let (corner_tl_shape, corner_tr_shape, corner_br_shape, corner_bl_shape) = corner_shapes;
1340 let (corner_tl_smoothing, corner_tr_smoothing, corner_br_smoothing, corner_bl_smoothing) =
1341 corner_smoothing;
1342 let (top_width, right_width, bottom_width, left_width) = widths;
1343
1344 let mut path = PathBuilder::new();
1345
1346 match side_ix {
1347 0 => {
1348 let otl = r_tl.min(bw * 0.5).min(bh * 0.5);
1350 let otr = r_tr.min(bw * 0.5).min(bh * 0.5);
1351 let itl = ir_tl
1352 .max(0.0)
1353 .min((bw - left_width - right_width).max(0.0) * 0.5)
1354 .min((bh - top_width - bottom_width).max(0.0) * 0.5);
1355 let itr = ir_tr
1356 .max(0.0)
1357 .min((bw - left_width - right_width).max(0.0) * 0.5)
1358 .min((bh - top_width - bottom_width).max(0.0) * 0.5);
1359
1360 path.move_to((bx + left_width, by + top_width + itl));
1361
1362 if otl > 0.0 && corner_tl_shape == CornerShape::Round {
1364 let (a, b, c, d, l, p, radius) =
1365 compute_smooth_corner(otl, corner_tl_smoothing, bw, bh);
1366 let start_x = bx + f32::max(left_width * 0.5, left_width + p - left_width);
1367 let start_y = by + top_width;
1368 path.line_to((start_x, start_y));
1369 path.cubic_to(
1370 (bx + left_width + (p - a), by),
1371 (bx + left_width + (p - a - b), by),
1372 (bx + left_width + (p - a - b - c), d + by),
1373 )
1374 .r_arc_to((radius, radius), 0.0, ArcSize::Small, PathDirection::CW, (l, l))
1375 .cubic_to(
1376 (bx + left_width + (p - a - b), by + top_width),
1377 (bx + left_width + (p - a), by + top_width),
1378 (bx + left_width + p.min(bw * 0.5), by + top_width),
1379 );
1380 } else if otl > 0.0 {
1381 let p = otl.min((bw * 0.5).min(bh * 0.5));
1382 path.line_to((bx + left_width + p, by + top_width));
1383 } else {
1384 path.line_to((bx + left_width, by + top_width));
1385 }
1386
1387 if otr > 0.0 {
1389 let p = otr.min((bw * 0.5).min(bh * 0.5));
1390 path.line_to((bx + bw - right_width - p, by + top_width));
1391 if corner_tr_shape == CornerShape::Round {
1392 let (a, b, c, d, l, radius_val, _) =
1393 compute_smooth_corner(otr, corner_tr_smoothing, bw, bh);
1394 path.cubic_to(
1395 (bx + bw - right_width - (p - a), by),
1396 (bx + bw - right_width - (p - a - b), by),
1397 (bx + bw - right_width - (p - a - b - c) + d, by),
1398 )
1399 .r_arc_to(
1400 (radius_val, radius_val),
1401 0.0,
1402 ArcSize::Small,
1403 PathDirection::CW,
1404 (l, l),
1405 )
1406 .cubic_to(
1407 (bx + bw - right_width - (p - a - b), by + top_width),
1408 (bx + bw - right_width - (p - a), by + top_width),
1409 (bx + bw - right_width - p.min(bw * 0.5), by + top_width),
1410 );
1411 }
1412 } else {
1413 path.line_to((bx + bw - right_width, by + top_width));
1414 }
1415
1416 path.line_to((bx + bw - right_width, by + top_width + itr));
1417
1418 if itr > 0.0 && corner_tr_shape == CornerShape::Round {
1420 let (a, b, c, d, l, p, radius) = compute_smooth_corner(
1421 itr,
1422 corner_tr_smoothing,
1423 bw - left_width - right_width,
1424 bh - top_width - bottom_width,
1425 );
1426 path.cubic_to(
1427 (bx + bw - right_width - (p - a), by + top_width),
1428 (bx + bw - right_width - (p - a - b), by + top_width),
1429 (bx + bw - right_width - (p - a - b - c), by + top_width + d),
1430 )
1431 .r_arc_to((radius, radius), 0.0, ArcSize::Small, PathDirection::CW, (-l, l))
1432 .cubic_to(
1433 (bx + bw - right_width, by + top_width + (p - a - b)),
1434 (bx + bw - right_width, by + top_width + (p - a)),
1435 (
1436 bx + bw - right_width,
1437 by + top_width + p.min((bh - top_width - bottom_width) * 0.5),
1438 ),
1439 );
1440 } else if itr > 0.0 {
1441 let p = itr.min(
1442 ((bh - top_width - bottom_width) * 0.5)
1443 .min((bw - left_width - right_width) * 0.5),
1444 );
1445 path.line_to((bx + bw - right_width, by + top_width + p));
1446 }
1447
1448 if itl > 0.0 && corner_tl_shape == CornerShape::Round {
1450 let (a, b, c, d, l, p, radius) = compute_smooth_corner(
1451 itl,
1452 corner_tl_smoothing,
1453 bw - left_width - right_width,
1454 bh - top_width - bottom_width,
1455 );
1456 path.line_to((bx + left_width + p, by + top_width));
1457 path.cubic_to(
1458 (bx + left_width + (p - a), by + top_width),
1459 (bx + left_width + (p - a - b), by + top_width),
1460 (bx + left_width + (p - a - b - c), by + top_width + d),
1461 )
1462 .r_arc_to((radius, radius), 0.0, ArcSize::Small, PathDirection::CW, (-l, -l))
1463 .cubic_to(
1464 (bx + left_width, by + top_width + (p - a - b)),
1465 (bx + left_width, by + top_width + (p - a)),
1466 (
1467 bx + left_width,
1468 by + top_width + p.min((bh - top_width - bottom_width) * 0.5),
1469 ),
1470 );
1471 } else if itl > 0.0 {
1472 let p = itl.min(
1473 ((bh - top_width - bottom_width) * 0.5)
1474 .min((bw - left_width - right_width) * 0.5),
1475 );
1476 path.line_to((bx + left_width, by + top_width + p));
1477 }
1478 }
1479 1 => {
1480 let otr = r_tr.min(bw * 0.5).min(bh * 0.5);
1482 let obr = r_br.min(bw * 0.5).min(bh * 0.5);
1483 let itr = ir_tr
1484 .max(0.0)
1485 .min((bw - left_width - right_width).max(0.0) * 0.5)
1486 .min((bh - top_width - bottom_width).max(0.0) * 0.5);
1487 let ibr = ir_br
1488 .max(0.0)
1489 .min((bw - left_width - right_width).max(0.0) * 0.5)
1490 .min((bh - top_width - bottom_width).max(0.0) * 0.5);
1491
1492 path.move_to((bx + bw - right_width - itr, by + top_width));
1493
1494 if otr > 0.0 && corner_tr_shape == CornerShape::Round {
1496 let (a, b, c, d, l, p, radius) =
1497 compute_smooth_corner(otr, corner_tr_smoothing, bw, bh);
1498 let start_x = bx + bw - right_width;
1499 path.line_to((start_x, by + top_width + p));
1500 path.cubic_to(
1501 (start_x, by + (p - a)),
1502 (start_x, by + (p - a - b)),
1503 (start_x - d, by + (p - a - b - c)),
1504 )
1505 .r_arc_to((radius, radius), 0.0, ArcSize::Small, PathDirection::CW, (-l, l))
1506 .cubic_to(
1507 (start_x - (p - a - b), by),
1508 (start_x - (p - a), by),
1509 (start_x - p.min(bw * 0.5), by),
1510 );
1511 } else if otr > 0.0 {
1512 let p = otr.min((bw * 0.5).min(bh * 0.5));
1513 path.line_to((bx + bw - right_width, by + top_width + p));
1514 } else {
1515 path.line_to((bx + bw - right_width, by + top_width));
1516 }
1517
1518 if obr > 0.0 {
1520 let p = obr.min((bw * 0.5).min(bh * 0.5));
1521 path.line_to((bx + bw - right_width, by + bh - bottom_width - p));
1522 if corner_br_shape == CornerShape::Round {
1523 let (a, b, c, d, l, radius_val, _) =
1524 compute_smooth_corner(obr, corner_br_smoothing, bw, bh);
1525 path.cubic_to(
1526 (bx + bw, by + bh - bottom_width - (p - a)),
1527 (bx + bw, by + bh - bottom_width - (p - a - b)),
1528 (bx + bw, by + bh - bottom_width - (p - a - b - c) + d),
1529 )
1530 .r_arc_to(
1531 (radius_val, radius_val),
1532 0.0,
1533 ArcSize::Small,
1534 PathDirection::CW,
1535 (l, l),
1536 )
1537 .cubic_to(
1538 (bx + bw - (p - a - b), by + bh),
1539 (bx + bw - (p - a), by + bh),
1540 (bx + bw - p.min(bw * 0.5), by + bh),
1541 );
1542 }
1543 } else {
1544 path.line_to((bx + bw - right_width, by + bh - bottom_width));
1545 }
1546
1547 path.line_to((bx + bw - right_width - ibr, by + bh - bottom_width));
1548
1549 if ibr > 0.0 && corner_br_shape == CornerShape::Round {
1551 let (a, b, c, d, l, p, radius) = compute_smooth_corner(
1552 ibr,
1553 corner_br_smoothing,
1554 bw - left_width - right_width,
1555 bh - top_width - bottom_width,
1556 );
1557 path.cubic_to(
1558 (bx + bw - right_width - (p - a), by + bh - bottom_width),
1559 (bx + bw - right_width - (p - a - b), by + bh - bottom_width),
1560 (bx + bw - right_width - (p - a - b - c), by + bh - bottom_width + d),
1561 )
1562 .r_arc_to((radius, radius), 0.0, ArcSize::Small, PathDirection::CW, (-l, -l))
1563 .cubic_to(
1564 (bx + bw - right_width, by + bh - bottom_width - (p - a - b)),
1565 (bx + bw - right_width, by + bh - bottom_width - (p - a)),
1566 (
1567 bx + bw - right_width,
1568 by + bh - bottom_width - p.min((bh - top_width - bottom_width) * 0.5),
1569 ),
1570 );
1571 } else if ibr > 0.0 {
1572 let p = ibr.min(
1573 ((bh - top_width - bottom_width) * 0.5)
1574 .min((bw - left_width - right_width) * 0.5),
1575 );
1576 path.line_to((bx + bw - right_width, by + bh - bottom_width - p));
1577 }
1578
1579 if itr > 0.0 && corner_tr_shape == CornerShape::Round {
1581 let (a, b, c, d, l, p, radius) = compute_smooth_corner(
1582 itr,
1583 corner_tr_smoothing,
1584 bw - left_width - right_width,
1585 bh - top_width - bottom_width,
1586 );
1587 path.line_to((bx + bw - right_width, by + top_width + p));
1588 path.cubic_to(
1589 (bx + bw - right_width, by + top_width + (p - a)),
1590 (bx + bw - right_width, by + top_width + (p - a - b)),
1591 (bx + bw - right_width + d, by + top_width + (p - a - b - c)),
1592 )
1593 .r_arc_to((radius, radius), 0.0, ArcSize::Small, PathDirection::CW, (l, -l))
1594 .cubic_to(
1595 (bx + bw - right_width - (p - a - b), by + top_width),
1596 (bx + bw - right_width - (p - a), by + top_width),
1597 (
1598 bx + bw - right_width - p.min((bw - left_width - right_width) * 0.5),
1599 by + top_width,
1600 ),
1601 );
1602 } else if itr > 0.0 {
1603 let p = itr.min(
1604 ((bh - top_width - bottom_width) * 0.5)
1605 .min((bw - left_width - right_width) * 0.5),
1606 );
1607 path.line_to((bx + bw - right_width, by + top_width + p));
1608 }
1609 }
1610 2 => {
1611 let obr = r_br.min(bw * 0.5).min(bh * 0.5);
1613 let obl = r_bl.min(bw * 0.5).min(bh * 0.5);
1614 let ibr = ir_br
1615 .max(0.0)
1616 .min((bw - left_width - right_width).max(0.0) * 0.5)
1617 .min((bh - top_width - bottom_width).max(0.0) * 0.5);
1618 let ibl = ir_bl
1619 .max(0.0)
1620 .min((bw - left_width - right_width).max(0.0) * 0.5)
1621 .min((bh - top_width - bottom_width).max(0.0) * 0.5);
1622
1623 path.move_to((bx + bw - right_width - ibr, by + bh - bottom_width));
1624
1625 if obr > 0.0 && corner_br_shape == CornerShape::Round {
1627 let (a, b, c, d, l, p, radius) =
1628 compute_smooth_corner(obr, corner_br_smoothing, bw, bh);
1629 let end_x = bx + bw - right_width;
1630 path.line_to((end_x - p, by + bh - bottom_width));
1631 path.cubic_to(
1632 (end_x - (p - a), by + bh),
1633 (end_x - (p - a - b), by + bh),
1634 (end_x - (p - a - b - c) + d, by + bh),
1635 )
1636 .r_arc_to((radius, radius), 0.0, ArcSize::Small, PathDirection::CW, (-l, l))
1637 .cubic_to(
1638 (end_x - (p - a - b), by + bh - bottom_width),
1639 (end_x - (p - a), by + bh - bottom_width),
1640 (end_x - p.min(bw * 0.5), by + bh - bottom_width),
1641 );
1642 } else if obr > 0.0 {
1643 let p = obr.min((bw * 0.5).min(bh * 0.5));
1644 path.line_to((bx + bw - right_width - p, by + bh - bottom_width));
1645 } else {
1646 path.line_to((bx + bw - right_width, by + bh - bottom_width));
1647 }
1648
1649 if obl > 0.0 {
1651 let p = obl.min((bw * 0.5).min(bh * 0.5));
1652 path.line_to((bx + left_width + p, by + bh - bottom_width));
1653 if corner_bl_shape == CornerShape::Round {
1654 let (a, b, c, d, l, radius_val, _) =
1655 compute_smooth_corner(obl, corner_bl_smoothing, bw, bh);
1656 path.cubic_to(
1657 (bx + left_width + (p - a), by + bh),
1658 (bx + left_width + (p - a - b), by + bh),
1659 (bx + left_width + (p - a - b - c) - d, by + bh),
1660 )
1661 .r_arc_to(
1662 (radius_val, radius_val),
1663 0.0,
1664 ArcSize::Small,
1665 PathDirection::CW,
1666 (-l, -l),
1667 )
1668 .cubic_to(
1669 (bx + left_width + (p - a - b), by + bh - bottom_width),
1670 (bx + left_width + (p - a), by + bh - bottom_width),
1671 (bx + left_width + p.min(bw * 0.5), by + bh - bottom_width),
1672 );
1673 }
1674 } else {
1675 path.line_to((bx + left_width, by + bh - bottom_width));
1676 }
1677
1678 path.line_to((bx + left_width + ibl, by + bh - bottom_width));
1679
1680 if ibl > 0.0 && corner_bl_shape == CornerShape::Round {
1682 let (a, b, c, d, l, p, radius) = compute_smooth_corner(
1683 ibl,
1684 corner_bl_smoothing,
1685 bw - left_width - right_width,
1686 bh - top_width - bottom_width,
1687 );
1688 path.cubic_to(
1689 (bx + left_width + (p - a), by + bh - bottom_width),
1690 (bx + left_width + (p - a - b), by + bh - bottom_width),
1691 (bx + left_width + (p - a - b - c), by + bh - bottom_width + d),
1692 )
1693 .r_arc_to((radius, radius), 0.0, ArcSize::Small, PathDirection::CW, (l, -l))
1694 .cubic_to(
1695 (bx + left_width, by + bh - bottom_width - (p - a - b)),
1696 (bx + left_width, by + bh - bottom_width - (p - a)),
1697 (
1698 bx + left_width,
1699 by + bh - bottom_width - p.min((bh - top_width - bottom_width) * 0.5),
1700 ),
1701 );
1702 } else if ibl > 0.0 {
1703 let p = ibl.min(
1704 ((bh - top_width - bottom_width) * 0.5)
1705 .min((bw - left_width - right_width) * 0.5),
1706 );
1707 path.line_to((bx + left_width, by + bh - bottom_width - p));
1708 }
1709
1710 if ibr > 0.0 && corner_br_shape == CornerShape::Round {
1712 let (a, b, c, d, l, p, radius) = compute_smooth_corner(
1713 ibr,
1714 corner_br_smoothing,
1715 bw - left_width - right_width,
1716 bh - top_width - bottom_width,
1717 );
1718 path.line_to((bx + bw - right_width - p, by + bh - bottom_width));
1719 path.cubic_to(
1720 (bx + bw - right_width - (p - a), by + bh - bottom_width),
1721 (bx + bw - right_width - (p - a - b), by + bh - bottom_width),
1722 (bx + bw - right_width - (p - a - b - c), by + bh - bottom_width + d),
1723 )
1724 .r_arc_to((radius, radius), 0.0, ArcSize::Small, PathDirection::CW, (l, l))
1725 .cubic_to(
1726 (bx + bw - right_width, by + bh - bottom_width - (p - a - b)),
1727 (bx + bw - right_width, by + bh - bottom_width - (p - a)),
1728 (
1729 bx + bw - right_width,
1730 by + bh - bottom_width - p.min((bh - top_width - bottom_width) * 0.5),
1731 ),
1732 );
1733 } else if ibr > 0.0 {
1734 let p = ibr.min(
1735 ((bh - top_width - bottom_width) * 0.5)
1736 .min((bw - left_width - right_width) * 0.5),
1737 );
1738 path.line_to((bx + bw - right_width, by + bh - bottom_width - p));
1739 }
1740 }
1741 3 => {
1742 let obl = r_bl.min(bw * 0.5).min(bh * 0.5);
1744 let otl = r_tl.min(bw * 0.5).min(bh * 0.5);
1745 let ibl = ir_bl
1746 .max(0.0)
1747 .min((bw - left_width - right_width).max(0.0) * 0.5)
1748 .min((bh - top_width - bottom_width).max(0.0) * 0.5);
1749 let itl = ir_tl
1750 .max(0.0)
1751 .min((bw - left_width - right_width).max(0.0) * 0.5)
1752 .min((bh - top_width - bottom_width).max(0.0) * 0.5);
1753
1754 path.move_to((bx + left_width + ibl, by + bh - bottom_width));
1755
1756 if obl > 0.0 && corner_bl_shape == CornerShape::Round {
1758 let (a, b, c, d, l, p, radius) =
1759 compute_smooth_corner(obl, corner_bl_smoothing, bw, bh);
1760 path.line_to((bx + left_width, by + bh - bottom_width - p));
1761 path.cubic_to(
1762 (bx, by + bh - bottom_width - (p - a)),
1763 (bx, by + bh - bottom_width - (p - a - b)),
1764 (bx, by + bh - bottom_width - (p - a - b - c) - d),
1765 )
1766 .r_arc_to((radius, radius), 0.0, ArcSize::Small, PathDirection::CW, (l, -l))
1767 .cubic_to(
1768 (bx + (p - a - b), by),
1769 (bx + (p - a), by),
1770 (bx + p.min(bw * 0.5), by),
1771 );
1772 } else if obl > 0.0 {
1773 let p = obl.min((bw * 0.5).min(bh * 0.5));
1774 path.line_to((bx + left_width, by + bh - bottom_width - p));
1775 } else {
1776 path.line_to((bx + left_width, by + bh - bottom_width));
1777 }
1778
1779 if otl > 0.0 {
1781 let p = otl.min((bw * 0.5).min(bh * 0.5));
1782 path.line_to((bx + left_width, by + top_width + p));
1783 if corner_tl_shape == CornerShape::Round {
1784 let (a, b, c, d, l, radius_val, _) =
1785 compute_smooth_corner(otl, corner_tl_smoothing, bw, bh);
1786 path.cubic_to(
1787 (bx, by + top_width + (p - a)),
1788 (bx, by + top_width + (p - a - b)),
1789 (bx, by + top_width + (p - a - b - c) - d),
1790 )
1791 .r_arc_to(
1792 (radius_val, radius_val),
1793 0.0,
1794 ArcSize::Small,
1795 PathDirection::CW,
1796 (l, l),
1797 )
1798 .cubic_to(
1799 (bx + (p - a - b), by),
1800 (bx + (p - a), by),
1801 (bx + p.min(bw * 0.5), by),
1802 );
1803 }
1804 } else {
1805 path.line_to((bx + left_width, by + top_width));
1806 }
1807
1808 path.line_to((bx + left_width + itl, by + top_width));
1809
1810 if itl > 0.0 && corner_tl_shape == CornerShape::Round {
1812 let (a, b, c, d, l, p, radius) = compute_smooth_corner(
1813 itl,
1814 corner_tl_smoothing,
1815 bw - left_width - right_width,
1816 bh - top_width - bottom_width,
1817 );
1818 path.cubic_to(
1819 (bx + left_width + (p - a), by + top_width),
1820 (bx + left_width + (p - a - b), by + top_width),
1821 (bx + left_width + (p - a - b - c), by + top_width + d),
1822 )
1823 .r_arc_to((radius, radius), 0.0, ArcSize::Small, PathDirection::CW, (l, l))
1824 .cubic_to(
1825 (bx + left_width, by + top_width + (p - a - b)),
1826 (bx + left_width, by + top_width + (p - a)),
1827 (
1828 bx + left_width,
1829 by + top_width + p.min((bh - top_width - bottom_width) * 0.5),
1830 ),
1831 );
1832 } else if itl > 0.0 {
1833 let p = itl.min(
1834 ((bh - top_width - bottom_width) * 0.5)
1835 .min((bw - left_width - right_width) * 0.5),
1836 );
1837 path.line_to((bx + left_width, by + top_width + p));
1838 }
1839
1840 if ibl > 0.0 && corner_bl_shape == CornerShape::Round {
1842 let (a, b, c, d, l, p, radius) = compute_smooth_corner(
1843 ibl,
1844 corner_bl_smoothing,
1845 bw - left_width - right_width,
1846 bh - top_width - bottom_width,
1847 );
1848 path.line_to((bx + left_width, by + bh - bottom_width - p));
1849 path.cubic_to(
1850 (bx + left_width, by + bh - bottom_width - (p - a)),
1851 (bx + left_width, by + bh - bottom_width - (p - a - b)),
1852 (bx + left_width - d, by + bh - bottom_width - (p - a - b - c)),
1853 )
1854 .r_arc_to((radius, radius), 0.0, ArcSize::Small, PathDirection::CW, (-l, -l))
1855 .cubic_to(
1856 (bx + left_width + (p - a - b), by + bh),
1857 (bx + left_width + (p - a), by + bh),
1858 (bx + left_width + p.min((bw - left_width - right_width) * 0.5), by + bh),
1859 );
1860 } else if ibl > 0.0 {
1861 let p = ibl.min(
1862 ((bh - top_width - bottom_width) * 0.5)
1863 .min((bw - left_width - right_width) * 0.5),
1864 );
1865 path.line_to((bx + left_width, by + bh - bottom_width - p));
1866 }
1867 }
1868 _ => return None,
1869 }
1870
1871 path.close();
1872 Some(path.detach())
1873 }
1874
1875 pub fn draw_background(&mut self, canvas: &Canvas) {
1877 let background_color = self.background_color();
1878 if background_color.a() > 0 {
1879 let path = self.path();
1880
1881 let mut paint = Paint::default();
1882 paint.set_color(skia_safe::Color::from_argb(
1883 background_color.a(),
1884 background_color.r(),
1885 background_color.g(),
1886 background_color.b(),
1887 ));
1888 paint.set_anti_alias(true);
1889 canvas.draw_path(&path, &paint);
1890 }
1891
1892 self.draw_background_images(canvas);
1893 }
1894
1895 pub fn draw_border(&mut self, canvas: &Canvas) {
1902 let top_width = self.border_top_width();
1903 let right_width = self.border_right_width();
1904 let bottom_width = self.border_bottom_width();
1905 let left_width = self.border_left_width();
1906
1907 let top_color = self.border_top_color();
1908 let right_color = self.border_right_color();
1909 let bottom_color = self.border_bottom_color();
1910 let left_color = self.border_left_color();
1911
1912 let top_style = self.border_top_style();
1913 let right_style = self.border_right_style();
1914 let bottom_style = self.border_bottom_style();
1915 let left_style = self.border_left_style();
1916
1917 let top_vis = top_width > 0.0 && top_color.a() > 0 && top_style != BorderStyleKeyword::None;
1918 let right_vis =
1919 right_width > 0.0 && right_color.a() > 0 && right_style != BorderStyleKeyword::None;
1920 let bottom_vis =
1921 bottom_width > 0.0 && bottom_color.a() > 0 && bottom_style != BorderStyleKeyword::None;
1922 let left_vis =
1923 left_width > 0.0 && left_color.a() > 0 && left_style != BorderStyleKeyword::None;
1924
1925 if !top_vis && !right_vis && !bottom_vis && !left_vis {
1926 return;
1927 }
1928
1929 let bounds = self.bounds();
1930 let bx = bounds.x;
1931 let by = bounds.y;
1932 let bw = bounds.w;
1933 let bh = bounds.h;
1934
1935 let r_tl = self.corner_top_left_radius();
1937 let r_tr = self.corner_top_right_radius();
1938 let r_br = self.corner_bottom_right_radius();
1939 let r_bl = self.corner_bottom_left_radius();
1940
1941 let ir_tl = (r_tl - top_width.max(left_width)).max(0.0);
1943 let ir_tr = (r_tr - top_width.max(right_width)).max(0.0);
1944 let ir_br = (r_br - bottom_width.max(right_width)).max(0.0);
1945 let ir_bl = (r_bl - bottom_width.max(left_width)).max(0.0);
1946
1947 let inner_w = (bw - left_width - right_width).max(0.0);
1949 let inner_h = (bh - top_width - bottom_width).max(0.0);
1950 let inner_is_empty = inner_w <= 0.0 || inner_h <= 0.0;
1951
1952 let corner_shapes = (
1953 self.corner_top_left_shape(),
1954 self.corner_top_right_shape(),
1955 self.corner_bottom_right_shape(),
1956 self.corner_bottom_left_shape(),
1957 );
1958 let corner_smoothing = (
1959 self.corner_top_left_smoothing(),
1960 self.corner_top_right_smoothing(),
1961 self.corner_bottom_right_smoothing(),
1962 self.corner_bottom_left_smoothing(),
1963 );
1964 let uniform_borders = top_width == right_width
1965 && right_width == bottom_width
1966 && bottom_width == left_width
1967 && top_color == right_color
1968 && right_color == bottom_color
1969 && bottom_color == left_color
1970 && top_style == right_style
1971 && right_style == bottom_style
1972 && bottom_style == left_style;
1973 let uniform_corners = r_tl == r_tr
1974 && r_tr == r_br
1975 && r_br == r_bl
1976 && corner_shapes.0 == corner_shapes.1
1977 && corner_shapes.1 == corner_shapes.2
1978 && corner_shapes.2 == corner_shapes.3
1979 && corner_smoothing.0 == corner_smoothing.1
1980 && corner_smoothing.1 == corner_smoothing.2
1981 && corner_smoothing.2 == corner_smoothing.3;
1982 let exact_round_solid = !inner_is_empty
1983 && corner_shapes.0 == CornerShape::Round
1984 && corner_shapes.1 == CornerShape::Round
1985 && corner_shapes.2 == CornerShape::Round
1986 && corner_shapes.3 == CornerShape::Round
1987 && corner_smoothing.0 == 0.0
1988 && corner_smoothing.1 == 0.0
1989 && corner_smoothing.2 == 0.0
1990 && corner_smoothing.3 == 0.0;
1991 let exact_bevel_solid = !inner_is_empty
1992 && corner_shapes.0 == CornerShape::Bevel
1993 && corner_shapes.1 == CornerShape::Bevel
1994 && corner_shapes.2 == CornerShape::Bevel
1995 && corner_shapes.3 == CornerShape::Bevel
1996 && corner_smoothing.0 == 0.0
1997 && corner_smoothing.1 == 0.0
1998 && corner_smoothing.2 == 0.0
1999 && corner_smoothing.3 == 0.0;
2000 let exact_smooth_solid = !inner_is_empty
2001 && corner_shapes.0 == CornerShape::Round
2002 && corner_shapes.1 == CornerShape::Round
2003 && corner_shapes.2 == CornerShape::Round
2004 && corner_shapes.3 == CornerShape::Round
2005 && (corner_smoothing.0 > 0.0
2006 || corner_smoothing.1 > 0.0
2007 || corner_smoothing.2 > 0.0
2008 || corner_smoothing.3 > 0.0);
2009
2010 let outer_path = self
2012 .build_path_with_corners(
2013 bounds,
2014 (0.0, 0.0),
2015 (r_tl, r_tr, r_br, r_bl),
2016 corner_shapes,
2017 corner_smoothing,
2018 )
2019 .make_offset(bounds.top_left());
2020
2021 let inner_path = if inner_is_empty {
2023 None
2024 } else {
2025 let inner_bounds = BoundingBox::from_min_max(0.0, 0.0, inner_w, inner_h);
2026 Some(
2027 self.build_path_with_corners(
2028 inner_bounds,
2029 (0.0, 0.0),
2030 (ir_tl, ir_tr, ir_br, ir_bl),
2031 corner_shapes,
2032 corner_smoothing,
2033 )
2034 .make_offset((bx + left_width, by + top_width)),
2035 )
2036 };
2037
2038 let ring_path = if let Some(inner) = inner_path.as_ref() {
2039 outer_path
2040 .op(inner, skia_safe::PathOp::Difference)
2041 .unwrap_or_else(|| outer_path.clone())
2042 } else {
2043 outer_path.clone()
2044 };
2045
2046 if uniform_borders && uniform_corners {
2047 match top_style {
2048 BorderStyleKeyword::Dashed | BorderStyleKeyword::Dotted => {
2049 canvas.save();
2050 canvas.clip_path(&ring_path, ClipOp::Intersect, true);
2051
2052 let half = top_width * 0.5;
2053 let stroke_path = self
2054 .build_path_with_corners(
2055 bounds,
2056 (-half, -half),
2057 (r_tl, r_tr, r_br, r_bl),
2058 corner_shapes,
2059 corner_smoothing,
2060 )
2061 .make_offset(bounds.top_left());
2062
2063 let mut paint = Paint::default();
2064 paint.set_style(PaintStyle::Stroke);
2065 paint.set_color(top_color);
2066 paint.set_stroke_width(top_width);
2067 if top_style == BorderStyleKeyword::Dashed {
2068 paint.set_path_effect(PathEffect::dash(&[top_width * 2.0, top_width], 0.0));
2069 } else {
2070 paint.set_path_effect(PathEffect::dash(&[0.0, top_width * 2.0], 0.0));
2071 paint.set_stroke_cap(skia_safe::PaintCap::Round);
2072 }
2073 paint.set_anti_alias(true);
2074 canvas.draw_path(&stroke_path, &paint);
2075 canvas.restore();
2076 return;
2077 }
2078
2079 _ => {
2080 let mut paint = Paint::default();
2081 paint.set_color(top_color);
2082 paint.set_anti_alias(true);
2083 canvas.draw_path(&ring_path, &paint);
2084 return;
2085 }
2086 }
2087 }
2088
2089 let mask_top: [Point; 4] = [
2091 Point::new(bx, by),
2092 Point::new(bx + bw, by),
2093 Point::new(bx + bw - right_width, by + top_width),
2094 Point::new(bx + left_width, by + top_width),
2095 ];
2096 let mask_right: [Point; 4] = [
2097 Point::new(bx + bw - right_width, by + top_width),
2098 Point::new(bx + bw, by),
2099 Point::new(bx + bw, by + bh),
2100 Point::new(bx + bw - right_width, by + bh - bottom_width),
2101 ];
2102 let mask_bottom: [Point; 4] = [
2103 Point::new(bx + left_width, by + bh - bottom_width),
2104 Point::new(bx + bw - right_width, by + bh - bottom_width),
2105 Point::new(bx + bw, by + bh),
2106 Point::new(bx, by + bh),
2107 ];
2108 let mask_left: [Point; 4] = [
2109 Point::new(bx, by),
2110 Point::new(bx + left_width, by + top_width),
2111 Point::new(bx + left_width, by + bh - bottom_width),
2112 Point::new(bx, by + bh),
2113 ];
2114
2115 let sides: [(usize, bool, Color, BorderStyleKeyword, f32, [Point; 4]); 4] = [
2116 (0, top_vis, top_color, top_style, top_width, mask_top),
2117 (1, right_vis, right_color, right_style, right_width, mask_right),
2118 (2, bottom_vis, bottom_color, bottom_style, bottom_width, mask_bottom),
2119 (3, left_vis, left_color, left_style, left_width, mask_left),
2120 ];
2121
2122 for (side_ix, vis, color, style, side_width, mask_pts) in sides {
2123 if !vis {
2124 continue;
2125 }
2126
2127 let mut side_mask = PathBuilder::new();
2128 side_mask.move_to(mask_pts[0]);
2129 side_mask.line_to(mask_pts[1]);
2130 side_mask.line_to(mask_pts[2]);
2131 side_mask.line_to(mask_pts[3]);
2132 side_mask.close();
2133 let side_mask = side_mask.detach();
2134 let Some(side_region) = ring_path.op(&side_mask, skia_safe::PathOp::Intersect) else {
2135 continue;
2136 };
2137
2138 match style {
2139 BorderStyleKeyword::Dashed | BorderStyleKeyword::Dotted => {
2140 canvas.save();
2141 canvas.clip_path(&side_region, ClipOp::Intersect, true);
2142
2143 let stroke_path = if exact_round_solid {
2144 Self::round_corner_side_stroke_path(
2145 side_ix,
2146 bounds,
2147 (r_tl, r_tr, r_br, r_bl),
2148 )
2149 .unwrap_or_else(|| {
2150 let mut side_path = PathBuilder::new();
2151 let (sx, sy, ex, ey) = match side_ix {
2152 0 => (
2153 bx + left_width * 0.5,
2154 by + top_width * 0.5,
2155 bx + bw - right_width * 0.5,
2156 by + top_width * 0.5,
2157 ),
2158 1 => (
2159 bx + bw - right_width * 0.5,
2160 by + top_width * 0.5,
2161 bx + bw - right_width * 0.5,
2162 by + bh - bottom_width * 0.5,
2163 ),
2164 2 => (
2165 bx + left_width * 0.5,
2166 by + bh - bottom_width * 0.5,
2167 bx + bw - right_width * 0.5,
2168 by + bh - bottom_width * 0.5,
2169 ),
2170 _ => (
2171 bx + left_width * 0.5,
2172 by + top_width * 0.5,
2173 bx + left_width * 0.5,
2174 by + bh - bottom_width * 0.5,
2175 ),
2176 };
2177 side_path.move_to((sx, sy));
2178 side_path.line_to((ex, ey));
2179 side_path.detach()
2180 })
2181 } else if exact_bevel_solid {
2182 Self::bevel_corner_side_stroke_path(
2183 side_ix,
2184 bounds,
2185 (r_tl, r_tr, r_br, r_bl),
2186 )
2187 .unwrap_or_else(|| {
2188 let mut side_path = PathBuilder::new();
2189 let (sx, sy, ex, ey) = match side_ix {
2190 0 => (
2191 bx + left_width * 0.5,
2192 by + top_width * 0.5,
2193 bx + bw - right_width * 0.5,
2194 by + top_width * 0.5,
2195 ),
2196 1 => (
2197 bx + bw - right_width * 0.5,
2198 by + top_width * 0.5,
2199 bx + bw - right_width * 0.5,
2200 by + bh - bottom_width * 0.5,
2201 ),
2202 2 => (
2203 bx + left_width * 0.5,
2204 by + bh - bottom_width * 0.5,
2205 bx + bw - right_width * 0.5,
2206 by + bh - bottom_width * 0.5,
2207 ),
2208 _ => (
2209 bx + left_width * 0.5,
2210 by + top_width * 0.5,
2211 bx + left_width * 0.5,
2212 by + bh - bottom_width * 0.5,
2213 ),
2214 };
2215 side_path.move_to((sx, sy));
2216 side_path.line_to((ex, ey));
2217 side_path.detach()
2218 })
2219 } else if exact_smooth_solid {
2220 self.build_path_with_corners(
2221 bounds,
2222 (-side_width * 0.5, -side_width * 0.5),
2223 (r_tl, r_tr, r_br, r_bl),
2224 corner_shapes,
2225 corner_smoothing,
2226 )
2227 .make_offset(bounds.top_left())
2228 } else {
2229 let mut side_path = PathBuilder::new();
2230 let (sx, sy, ex, ey) = match side_ix {
2231 0 => (
2232 bx + left_width * 0.5,
2233 by + top_width * 0.5,
2234 bx + bw - right_width * 0.5,
2235 by + top_width * 0.5,
2236 ),
2237 1 => (
2238 bx + bw - right_width * 0.5,
2239 by + top_width * 0.5,
2240 bx + bw - right_width * 0.5,
2241 by + bh - bottom_width * 0.5,
2242 ),
2243 2 => (
2244 bx + left_width * 0.5,
2245 by + bh - bottom_width * 0.5,
2246 bx + bw - right_width * 0.5,
2247 by + bh - bottom_width * 0.5,
2248 ),
2249 _ => (
2250 bx + left_width * 0.5,
2251 by + top_width * 0.5,
2252 bx + left_width * 0.5,
2253 by + bh - bottom_width * 0.5,
2254 ),
2255 };
2256 side_path.move_to((sx, sy));
2257 side_path.line_to((ex, ey));
2258 side_path.detach()
2259 };
2260 let mut paint = Paint::default();
2261 paint.set_style(PaintStyle::Stroke);
2262 paint.set_color(color);
2263 paint.set_stroke_width(side_width);
2264 if style == BorderStyleKeyword::Dashed {
2265 paint.set_path_effect(PathEffect::dash(
2266 &[side_width * 2.0, side_width],
2267 0.0,
2268 ));
2269 } else {
2270 paint.set_path_effect(PathEffect::dash(&[0.0, side_width * 2.0], 0.0));
2271 paint.set_stroke_cap(skia_safe::PaintCap::Round);
2272 }
2273 paint.set_anti_alias(true);
2274 canvas.draw_path(&stroke_path, &paint);
2275 canvas.restore();
2276 }
2277 _ => {
2278 let exact_side_path = if exact_round_solid {
2279 Self::round_corner_side_path(
2280 side_ix,
2281 bounds,
2282 (r_tl, r_tr, r_br, r_bl),
2283 (ir_tl, ir_tr, ir_br, ir_bl),
2284 (top_width, right_width, bottom_width, left_width),
2285 )
2286 } else if exact_bevel_solid {
2287 Self::bevel_corner_side_path(
2288 side_ix,
2289 bounds,
2290 (r_tl, r_tr, r_br, r_bl),
2291 (ir_tl, ir_tr, ir_br, ir_bl),
2292 (top_width, right_width, bottom_width, left_width),
2293 )
2294 } else if exact_smooth_solid {
2295 Self::smooth_corner_side_path(
2296 side_ix,
2297 bounds,
2298 (r_tl, r_tr, r_br, r_bl),
2299 (ir_tl, ir_tr, ir_br, ir_bl),
2300 corner_shapes,
2301 corner_smoothing,
2302 (top_width, right_width, bottom_width, left_width),
2303 )
2304 } else {
2305 None
2306 };
2307 let mut paint = Paint::default();
2309 paint.set_color(color);
2310 paint.set_anti_alias(true);
2311 if let Some(path) = exact_side_path {
2312 let Some(constrained_path) =
2313 path.op(&ring_path, skia_safe::PathOp::Intersect)
2314 else {
2315 continue;
2316 };
2317 canvas.draw_path(&constrained_path, &paint);
2318 } else {
2319 canvas.draw_path(&side_region, &paint);
2320 }
2321 }
2322 }
2323 }
2324 }
2325
2326 pub fn draw_outline(&mut self, canvas: &Canvas) {
2328 let outline_width = self.outline_width();
2329 let outline_color = self.outline_color();
2330
2331 if outline_width > 0.0 && outline_color.a() != 0 {
2332 let outline_offset = self.outline_offset();
2333
2334 let bounds = self.bounds();
2335
2336 let half_outline_width = outline_width / 2.0;
2337 let mut outline_path = self.build_path(
2338 bounds,
2339 (half_outline_width + outline_offset, half_outline_width + outline_offset),
2340 );
2341
2342 outline_path = outline_path.make_offset(self.bounds().top_left());
2343
2344 let mut outline_paint = Paint::default();
2345 outline_paint.set_color(outline_color);
2346 outline_paint.set_stroke_width(outline_width);
2347 outline_paint.set_style(PaintStyle::Stroke);
2348 outline_paint.set_anti_alias(true);
2349 canvas.draw_path(&outline_path, &outline_paint);
2350 }
2351 }
2352
2353 pub fn draw_shadows(&mut self, canvas: &Canvas) {
2355 if let Some(shadows) = self.shadows() {
2356 if shadows.is_empty() {
2357 return;
2358 }
2359
2360 let bounds = self.bounds();
2361
2362 let path = self.build_path(bounds, (0.0, 0.0)).make_offset(bounds.top_left());
2363
2364 for shadow in shadows.iter().rev() {
2365 let shadow_color = shadow.color.unwrap_or_default();
2366
2367 let shadow_x_offset = shadow.x_offset.to_px().unwrap_or(0.0) * self.scale_factor();
2368 let shadow_y_offset = shadow.y_offset.to_px().unwrap_or(0.0) * self.scale_factor();
2369 let spread_radius =
2370 shadow.spread_radius.as_ref().and_then(|l| l.to_px()).unwrap_or(0.0)
2371 * self.scale_factor();
2372
2373 let blur_radius =
2374 shadow.blur_radius.as_ref().and_then(|br| br.to_px()).unwrap_or(0.0);
2375
2376 if shadow_color.a() == 0
2377 || (shadow_x_offset == 0.0
2378 && shadow_y_offset == 0.0
2379 && spread_radius == 0.0
2380 && blur_radius == 0.0)
2381 {
2382 continue;
2383 }
2384
2385 let mut shadow_paint = Paint::default();
2386
2387 let outset = if shadow.inset { -spread_radius } else { spread_radius };
2388
2389 shadow_paint.set_style(PaintStyle::Fill);
2390
2391 let mut shadow_path = self.build_path(bounds, (outset, outset));
2392 shadow_path = shadow_path.make_offset(bounds.top_left());
2393
2394 shadow_paint.set_color(shadow_color);
2395
2396 if blur_radius > 0.0 {
2397 shadow_paint.set_mask_filter(MaskFilter::blur(
2398 BlurStyle::Normal,
2399 blur_radius / 2.0,
2400 false,
2401 ));
2402 }
2403
2404 shadow_path = shadow_path.make_offset((shadow_x_offset, shadow_y_offset));
2405
2406 if shadow.inset {
2407 shadow_path = path.op(&shadow_path, skia_safe::PathOp::Difference).unwrap();
2408 }
2409
2410 canvas.save();
2411 canvas.clip_path(
2412 &path,
2413 if shadow.inset { ClipOp::Intersect } else { ClipOp::Difference },
2414 true,
2415 );
2416 canvas.draw_path(&shadow_path, &shadow_paint);
2417 canvas.restore();
2418 }
2419 }
2420 }
2421
2422 fn draw_background_images(&mut self, canvas: &Canvas) {
2424 let bounds = self.bounds();
2425
2426 if self.background_images().is_some() {
2427 let path = self.path();
2428 if let Some(images) = self.background_images() {
2429 let image_sizes = self.background_size();
2430 let image_positions = self.background_position();
2431 let image_repeats = self.background_repeat();
2432
2433 for (index, image) in images.iter().enumerate() {
2434 match image {
2435 ImageOrGradient::Gradient(gradient) => match gradient {
2436 Gradient::Linear(linear_gradient) => {
2437 let (start, end, parent_length) = match linear_gradient.direction {
2438 LineDirection::Horizontal(horizontal_keyword) => {
2439 match horizontal_keyword {
2440 HorizontalPositionKeyword::Left => (
2441 bounds.center_right(),
2442 bounds.center_left(),
2443 bounds.width(),
2444 ),
2445
2446 HorizontalPositionKeyword::Right => (
2447 bounds.center_left(),
2448 bounds.center_right(),
2449 bounds.width(),
2450 ),
2451 }
2452 }
2453
2454 LineDirection::Vertical(vertical_keyword) => {
2455 match vertical_keyword {
2456 VerticalPositionKeyword::Top => (
2457 bounds.center_bottom(),
2458 bounds.center_top(),
2459 bounds.height(),
2460 ),
2461
2462 VerticalPositionKeyword::Bottom => (
2463 bounds.center_top(),
2464 bounds.center_bottom(),
2465 bounds.height(),
2466 ),
2467 }
2468 }
2469
2470 LineDirection::Corner { horizontal, vertical } => {
2471 match (horizontal, vertical) {
2472 (
2473 HorizontalPositionKeyword::Right,
2474 VerticalPositionKeyword::Bottom,
2475 ) => (
2476 bounds.top_left(),
2477 bounds.bottom_right(),
2478 bounds.diagonal(),
2479 ),
2480
2481 (
2482 HorizontalPositionKeyword::Right,
2483 VerticalPositionKeyword::Top,
2484 ) => (
2485 bounds.bottom_left(),
2486 bounds.top_right(),
2487 bounds.diagonal(),
2488 ),
2489
2490 _ => (bounds.top_left(), bounds.bottom_right(), 0.0),
2491 }
2492 }
2493
2494 LineDirection::Angle(angle) => {
2495 let angle_rad = angle.to_radians();
2496 let start_x = bounds.x
2497 + ((angle_rad.sin() * bounds.w) - bounds.w) / -2.0;
2498 let end_x = bounds.x
2499 + ((angle_rad.sin() * bounds.w) + bounds.w) / 2.0;
2500 let start_y = bounds.y
2501 + ((angle_rad.cos() * bounds.h) + bounds.h) / 2.0;
2502 let end_y = bounds.y
2503 + ((angle_rad.cos() * bounds.h) - bounds.h) / -2.0;
2504
2505 let x = (end_x - start_x).abs();
2506 let y = (end_y - start_y).abs();
2507
2508 let dist = (x * x + y * y).sqrt();
2509
2510 ((start_x, start_y), (end_x, end_y), dist)
2511 }
2512 };
2513
2514 let num_stops = linear_gradient.stops.len();
2515
2516 let mut stops = linear_gradient
2517 .stops
2518 .iter()
2519 .enumerate()
2520 .map(|(index, stop)| {
2521 let pos = if let Some(pos) = &stop.position {
2522 pos.to_pixels(parent_length, self.scale_factor())
2523 / parent_length
2524 } else {
2525 index as f32 / (num_stops - 1) as f32
2526 };
2527 (pos, skia_safe::Color::from(stop.color))
2528 })
2529 .collect::<Vec<_>>();
2530
2531 if let Some(first) = stops.first() {
2533 if first.0 != 0.0 {
2534 stops.insert(0, (0.0, first.1));
2535 }
2536 }
2537
2538 if let Some(last) = stops.last() {
2540 if last.0 != 1.0 {
2541 stops.push((1.0, last.1));
2542 }
2543 }
2544
2545 let (offsets, colors): (Vec<f32>, Vec<skia_safe::Color>) =
2546 stops.into_iter().unzip();
2547 let colors4f: Vec<skia_safe::Color4f> =
2548 colors.iter().copied().map(Into::into).collect();
2549
2550 let gradient_colors =
2551 skia_safe::gradient_shader::GradientColors::new(
2552 &colors4f,
2553 Some(&offsets[..]),
2554 TileMode::Clamp,
2555 None,
2556 );
2557 let gradient = skia_safe::gradient_shader::Gradient::new(
2558 gradient_colors,
2559 skia_safe::gradient_shader::Interpolation::default(),
2560 );
2561 let shader = skia_safe::shaders::linear_gradient(
2562 (Point::from(start), Point::from(end)),
2563 &gradient,
2564 None,
2565 );
2566
2567 let mut paint = Paint::default();
2568 paint.set_shader(shader);
2569 paint.set_anti_alias(true);
2570
2571 canvas.draw_path(&path, &paint);
2572 }
2573
2574 Gradient::Radial(radial_gradient) => {
2575 let num_stops = radial_gradient.stops.len();
2576
2577 let mut stops = radial_gradient
2578 .stops
2579 .iter()
2580 .enumerate()
2581 .map(|(index, stop)| {
2582 let pos = if let Some(pos) = &stop.position {
2583 pos.to_pixels(bounds.width(), self.scale_factor())
2584 / bounds.width()
2585 } else {
2586 index as f32 / (num_stops - 1) as f32
2587 };
2588
2589 (pos, skia_safe::Color::from(stop.color))
2590 })
2591 .collect::<Vec<_>>();
2592
2593 if let Some(first) = stops.first() {
2595 if first.0 != 0.0 {
2596 stops.insert(0, (0.0, first.1));
2597 }
2598 }
2599
2600 if let Some(last) = stops.last() {
2602 if last.0 != 1.0 {
2603 stops.push((1.0, last.1));
2604 }
2605 }
2606
2607 let (offsets, colors): (Vec<f32>, Vec<skia_safe::Color>) =
2608 stops.into_iter().unzip();
2609 let colors4f: Vec<skia_safe::Color4f> =
2610 colors.iter().copied().map(Into::into).collect();
2611
2612 let gradient_colors =
2613 skia_safe::gradient_shader::GradientColors::new(
2614 &colors4f,
2615 Some(&offsets[..]),
2616 TileMode::Clamp,
2617 None,
2618 );
2619 let gradient = skia_safe::gradient_shader::Gradient::new(
2620 gradient_colors,
2621 skia_safe::gradient_shader::Interpolation::default(),
2622 );
2623 let shader = skia_safe::shaders::radial_gradient(
2624 (Point::from(bounds.center()), bounds.w.max(bounds.h)),
2625 &gradient,
2626 None,
2627 );
2628
2629 let mut paint = Paint::default();
2630 paint.set_shader(shader);
2631 paint.set_anti_alias(true);
2632
2633 canvas.draw_path(&path, &paint);
2634 }
2635
2636 _ => {}
2637 },
2638
2639 ImageOrGradient::Image(image_name) => {
2640 if let Some(image_id) = self.resource_manager.image_ids.get(image_name)
2641 {
2642 if let Some(image) = self.resource_manager.images.get(image_id) {
2643 match &image.image {
2644 ImageOrSvg::Image(image) => {
2645 let image_width = image.width();
2646 let image_height = image.height();
2647 let (width, height) = if let Some(background_size) =
2648 image_sizes.get(index)
2649 {
2650 match background_size {
2651 BackgroundSize::Explicit { width, height } => {
2652 let w = match width {
2653 LengthPercentageOrAuto::LengthPercentage(
2654 length,
2655 ) => {
2656 length.to_pixels(bounds.w, self.scale_factor())
2657 }
2658 LengthPercentageOrAuto::Auto => image_width as f32,
2659 };
2660
2661 let h = match height {
2662 LengthPercentageOrAuto::LengthPercentage(
2663 length,
2664 ) => {
2665 length.to_pixels(bounds.h, self.scale_factor())
2666 }
2667 LengthPercentageOrAuto::Auto => image_height as f32,
2668 };
2669
2670 (w, h)
2671 }
2672
2673 BackgroundSize::Contain => {
2674 let image_ratio = image_width as f32
2675 / image_height as f32;
2676 let container_ratio = bounds.w / bounds.h;
2677
2678 let (w, h) =
2679 if image_ratio > container_ratio {
2680 (bounds.w, bounds.w / image_ratio)
2681 } else {
2682 (bounds.h * image_ratio, bounds.h)
2683 };
2684
2685 (w, h)
2686 }
2687
2688 BackgroundSize::Cover => {
2689 let image_ratio = image_width as f32
2690 / image_height as f32;
2691 let container_ratio = bounds.w / bounds.h;
2692
2693 let (w, h) =
2694 if image_ratio < container_ratio {
2695 (bounds.w, bounds.w / image_ratio)
2696 } else {
2697 (bounds.h * image_ratio, bounds.h)
2698 };
2699
2700 (w, h)
2701 }
2702 }
2703 } else {
2704 (image_width as f32, image_height as f32)
2705 };
2706
2707 let position = image_positions
2708 .get(index)
2709 .cloned()
2710 .or_else(|| image_positions.last().cloned())
2711 .unwrap_or_default();
2712
2713 let posx =
2714 position.x.to_length_or_percentage().to_pixels(
2715 bounds.width() - width,
2716 self.scale_factor(),
2717 );
2718 let posy =
2719 position.y.to_length_or_percentage().to_pixels(
2720 bounds.height() - height,
2721 self.scale_factor(),
2722 );
2723 let repeat = image_repeats
2724 .get(index)
2725 .copied()
2726 .or_else(|| image_repeats.last().copied())
2727 .unwrap_or(BackgroundRepeat::Repeat);
2728
2729 if width <= 0.0 || height <= 0.0 {
2730 continue;
2731 }
2732
2733 let mut paint = Paint::default();
2734 paint.set_anti_alias(true);
2735
2736 let origin_x = bounds.left() + posx;
2737 let origin_y = bounds.top() + posy;
2738
2739 let mut start_x = origin_x;
2740 let mut start_y = origin_y;
2741
2742 if matches!(
2743 repeat,
2744 BackgroundRepeat::Repeat
2745 | BackgroundRepeat::RepeatX
2746 ) {
2747 let tiles_to_left =
2748 ((bounds.left() - origin_x) / width).floor();
2749 start_x = origin_x + tiles_to_left * width;
2750 if start_x > bounds.left() {
2751 start_x -= width;
2752 }
2753 }
2754
2755 if matches!(
2756 repeat,
2757 BackgroundRepeat::Repeat
2758 | BackgroundRepeat::RepeatY
2759 ) {
2760 let tiles_to_top =
2761 ((bounds.top() - origin_y) / height).floor();
2762 start_y = origin_y + tiles_to_top * height;
2763 if start_y > bounds.top() {
2764 start_y -= height;
2765 }
2766 }
2767
2768 canvas.save();
2769 canvas.clip_path(&path, ClipOp::Intersect, true);
2770
2771 match repeat {
2772 BackgroundRepeat::NoRepeat => {
2773 let dst = Rect::new(
2774 origin_x,
2775 origin_y,
2776 origin_x + width,
2777 origin_y + height,
2778 );
2779 canvas.draw_image_rect_with_sampling_options(
2780 image,
2781 None,
2782 dst,
2783 SamplingOptions::default(),
2784 &paint,
2785 );
2786 }
2787
2788 BackgroundRepeat::RepeatX => {
2789 let mut x = start_x;
2790 while x < bounds.right() {
2791 let dst = Rect::new(
2792 x,
2793 origin_y,
2794 x + width,
2795 origin_y + height,
2796 );
2797 canvas
2798 .draw_image_rect_with_sampling_options(
2799 image,
2800 None,
2801 dst,
2802 SamplingOptions::default(),
2803 &paint,
2804 );
2805 x += width;
2806 }
2807 }
2808
2809 BackgroundRepeat::RepeatY => {
2810 let mut y = start_y;
2811 while y < bounds.bottom() {
2812 let dst = Rect::new(
2813 origin_x,
2814 y,
2815 origin_x + width,
2816 y + height,
2817 );
2818 canvas
2819 .draw_image_rect_with_sampling_options(
2820 image,
2821 None,
2822 dst,
2823 SamplingOptions::default(),
2824 &paint,
2825 );
2826 y += height;
2827 }
2828 }
2829
2830 BackgroundRepeat::Repeat => {
2831 let mut y = start_y;
2832 while y < bounds.bottom() {
2833 let mut x = start_x;
2834 while x < bounds.right() {
2835 let dst = Rect::new(
2836 x,
2837 y,
2838 x + width,
2839 y + height,
2840 );
2841 canvas.draw_image_rect_with_sampling_options(
2842 image,
2843 None,
2844 dst,
2845 SamplingOptions::default(),
2846 &paint,
2847 );
2848 x += width;
2849 }
2850 y += height;
2851 }
2852 }
2853 }
2854
2855 canvas.restore();
2856 }
2857
2858 ImageOrSvg::Svg(svg) => {
2859 canvas.save_layer(&SaveLayerRec::default());
2860 canvas.translate((bounds.x, bounds.y));
2861 let (scale_x, scale_y) = (
2862 bounds.width() / svg.inner().fContainerSize.fWidth,
2863 bounds.height()
2864 / svg.inner().fContainerSize.fHeight,
2865 );
2866
2867 if scale_x.is_finite() && scale_y.is_finite() {
2868 canvas.scale((scale_x, scale_y));
2869 } else {
2870 svg.clone().set_container_size((
2871 bounds.width(),
2872 bounds.height(),
2873 ));
2874 }
2875
2876 svg.render(canvas);
2877
2878 if let Some(color) = self.style.fill.get_resolved(
2879 self.current,
2880 &self.style.custom_color_props,
2881 ) {
2882 if color.a() != 0 {
2892 let mut paint = Paint::default();
2893 paint.set_anti_alias(true);
2894 paint.set_blend_mode(
2895 skia_safe::BlendMode::SrcIn,
2896 );
2897 paint.set_color(color);
2898 canvas.draw_paint(&paint);
2899 }
2900 }
2901 canvas.restore();
2902 }
2903 }
2904 }
2905 }
2906 }
2907 }
2908 }
2909 }
2910 }
2911 }
2912
2913 pub fn draw_text(&mut self, canvas: &Canvas) {
2915 if let Some(paragraph) = self.text_context.text_paragraphs.get(self.current) {
2916 let bounds = self.bounds();
2917
2918 let alignment = self.alignment();
2919
2920 let (mut top, _) = match alignment {
2921 Alignment::TopLeft => (0.0, 0.0),
2922 Alignment::TopCenter => (0.0, 0.5),
2923 Alignment::TopRight => (0.0, 1.0),
2924 Alignment::Left => (0.5, 0.0),
2925 Alignment::Center => (0.5, 0.5),
2926 Alignment::Right => (0.5, 1.0),
2927 Alignment::BottomLeft => (1.0, 0.0),
2928 Alignment::BottomCenter => (1.0, 0.5),
2929 Alignment::BottomRight => (1.0, 1.0),
2930 };
2931
2932 let padding_top = match self.padding_top() {
2933 Units::Pixels(val) => val,
2934 _ => 0.0,
2935 };
2936
2937 let padding_bottom = match self.padding_bottom() {
2938 Units::Pixels(val) => val,
2939 _ => 0.0,
2940 };
2941
2942 top *= bounds.height() - padding_top - padding_bottom - paragraph.height();
2943
2944 let mut padding_left = match self.padding_left() {
2945 Units::Pixels(val) => val,
2946 _ => 0.0,
2947 };
2948
2949 let mut padding_right = match self.padding_right() {
2950 Units::Pixels(val) => val,
2951 _ => 0.0,
2952 };
2953
2954 if resolved_text_direction(self.style, self.current) == Direction::RightToLeft {
2955 std::mem::swap(&mut padding_left, &mut padding_right);
2956 }
2957
2958 paragraph.paint(
2959 canvas,
2960 ((bounds.x + padding_left).round(), (bounds.y + padding_top + top).round()),
2961 );
2962 }
2963 }
2964}
2965
2966impl DataContext for DrawContext<'_> {
2967 fn try_data<T: 'static>(&self) -> Option<&T> {
2968 if let Some(t) = <dyn Any>::downcast_ref::<T>(&()) {
2970 return Some(t);
2971 }
2972
2973 for entity in self.current.parent_iter(self.tree) {
2974 if let Some(models) = self.models.get(&entity) {
2976 if let Some(model) = models.get(&TypeId::of::<T>()) {
2977 return model.downcast_ref::<T>();
2978 }
2979 }
2980
2981 if let Some(view_handler) = self.views.get(&entity) {
2983 if let Some(data) = view_handler.downcast_ref::<T>() {
2984 return Some(data);
2985 }
2986 }
2987 }
2988
2989 None
2990 }
2991}
2992
2993fn compute_smooth_corner(
2995 corner_radius: f32,
2996 smoothing: f32,
2997 width: f32,
2998 height: f32,
2999) -> (f32, f32, f32, f32, f32, f32, f32) {
3000 let max_p = f32::min(width, height) / 2.0;
3001 let corner_radius = f32::min(corner_radius, max_p);
3002
3003 let p = f32::min((1.0 + smoothing) * corner_radius, max_p);
3004
3005 let angle_alpha: f32;
3006 let angle_beta: f32;
3007
3008 if corner_radius <= max_p / 2.0 {
3009 angle_alpha = 45.0 * smoothing;
3010 angle_beta = 90.0 * (1.0 - smoothing);
3011 } else {
3012 let diff_ratio = (corner_radius - max_p / 2.0) / (max_p / 2.0);
3013
3014 angle_alpha = 45.0 * smoothing * (1.0 - diff_ratio);
3015 angle_beta = 90.0 * (1.0 - smoothing * (1.0 - diff_ratio));
3016 }
3017
3018 let angle_theta = (90.0 - angle_beta) / 2.0;
3019 let dist_p3_p4 = corner_radius * (angle_theta / 2.0).to_radians().tan();
3020
3021 let l = (angle_beta / 2.0).to_radians().sin() * corner_radius * SQRT_2;
3022 let c = dist_p3_p4 * angle_alpha.to_radians().cos();
3023 let d = c * angle_alpha.to_radians().tan();
3024 let b = (p - l - c - d) / 3.0;
3025 let a = 2.0 * b;
3026
3027 (a, b, c, d, l, p, corner_radius)
3028}