1use std::{ops::AddAssign, rc::Rc};
4
5use crate::{
6 ArcRecordArgs, Brush, Color, ColorFilter, CommandRecorder, CommandRecording, DrawGroup,
7 DrawTransform, GroupMarker, ImageBitmap, ImageSampling, PathFillRule, Trapezoid,
8 normalized_band,
9 path::{DrawStyle, Path},
10 path_recording::{record_path, record_vector_path, vector_mask_rect},
11 stroke::Stroke,
12 text_primitive::{TextBlockAt, TextPrimitive, measured_text, text_primitive},
13 typography::{DrawTextMeasurer, DrawTextStyle, TextMeasurement},
14 vector_path_mask::PathSide,
15};
16
17#[derive(Clone, Copy, Debug, PartialEq, Default)]
19pub struct Point {
20 pub x: f32,
22 pub y: f32,
24}
25
26impl Point {
27 pub const fn new(x: f32, y: f32) -> Self {
29 Self { x, y }
30 }
31
32 pub const ZERO: Self = Self { x: 0.0, y: 0.0 };
34}
35
36#[derive(Clone, Copy, Debug, PartialEq, Default)]
38pub struct Size {
39 pub width: f32,
41 pub height: f32,
43}
44
45impl Size {
46 pub const fn new(width: f32, height: f32) -> Self {
48 Self { width, height }
49 }
50
51 pub const ZERO: Self = Self {
53 width: 0.0,
54 height: 0.0,
55 };
56}
57
58#[derive(Clone, Copy, Debug, Default, PartialEq)]
60pub struct Rect {
61 pub x: f32,
63 pub y: f32,
65 pub width: f32,
67 pub height: f32,
69}
70
71impl Rect {
72 pub const EMPTY: Self = Self {
76 x: 0.0,
77 y: 0.0,
78 width: 0.0,
79 height: 0.0,
80 };
81
82 pub fn from_origin_size(origin: Point, size: Size) -> Self {
84 Self {
85 x: origin.x,
86 y: origin.y,
87 width: size.width,
88 height: size.height,
89 }
90 }
91
92 pub fn from_size(size: Size) -> Self {
94 Self::from_origin_size(Point::ZERO, size)
95 }
96
97 pub fn size(&self) -> Size {
99 Size::new(self.width, self.height)
100 }
101
102 pub fn translate(&self, dx: f32, dy: f32) -> Self {
104 Self {
105 x: self.x + dx,
106 y: self.y + dy,
107 width: self.width,
108 height: self.height,
109 }
110 }
111
112 pub fn contains(&self, x: f32, y: f32) -> bool {
114 x >= self.x && y >= self.y && x <= self.x + self.width && y <= self.y + self.height
115 }
116
117 pub fn is_empty(&self) -> bool {
119 self.width <= 0.0 || self.height <= 0.0
120 }
121
122 pub fn intersect(&self, other: Self) -> Option<Self> {
124 let left = self.x.max(other.x);
125 let top = self.y.max(other.y);
126 let right = (self.x + self.width).min(other.x + other.width);
127 let bottom = (self.y + self.height).min(other.y + other.height);
128 let width = right - left;
129 let height = bottom - top;
130 if width <= 0.0 || height <= 0.0 {
131 None
132 } else {
133 Some(Self {
134 x: left,
135 y: top,
136 width,
137 height,
138 })
139 }
140 }
141
142 pub fn union(&self, other: Self) -> Self {
144 let left = self.x.min(other.x);
145 let top = self.y.min(other.y);
146 let right = (self.x + self.width).max(other.x + other.width);
147 let bottom = (self.y + self.height).max(other.y + other.height);
148 Self {
149 x: left,
150 y: top,
151 width: (right - left).max(0.0),
152 height: (bottom - top).max(0.0),
153 }
154 }
155}
156
157#[derive(Clone, Copy, Debug, Default, PartialEq)]
159pub struct EdgeInsets {
160 pub left: f32,
162 pub top: f32,
164 pub right: f32,
166 pub bottom: f32,
168}
169
170impl EdgeInsets {
171 pub fn uniform(all: f32) -> Self {
173 Self {
174 left: all,
175 top: all,
176 right: all,
177 bottom: all,
178 }
179 }
180
181 pub fn horizontal(horizontal: f32) -> Self {
183 Self {
184 left: horizontal,
185 right: horizontal,
186 ..Self::default()
187 }
188 }
189
190 pub fn vertical(vertical: f32) -> Self {
192 Self {
193 top: vertical,
194 bottom: vertical,
195 ..Self::default()
196 }
197 }
198
199 pub fn symmetric(horizontal: f32, vertical: f32) -> Self {
201 Self {
202 left: horizontal,
203 right: horizontal,
204 top: vertical,
205 bottom: vertical,
206 }
207 }
208
209 pub fn from_components(left: f32, top: f32, right: f32, bottom: f32) -> Self {
211 Self {
212 left,
213 top,
214 right,
215 bottom,
216 }
217 }
218
219 pub fn is_zero(&self) -> bool {
221 self.left == 0.0 && self.top == 0.0 && self.right == 0.0 && self.bottom == 0.0
222 }
223
224 pub fn horizontal_sum(&self) -> f32 {
226 self.left + self.right
227 }
228
229 pub fn vertical_sum(&self) -> f32 {
231 self.top + self.bottom
232 }
233
234 pub fn inset_rect(&self, size: Size) -> Rect {
237 Rect {
238 x: self.left,
239 y: self.top,
240 width: (size.width - self.horizontal_sum()).max(0.0),
241 height: (size.height - self.vertical_sum()).max(0.0),
242 }
243 }
244}
245
246impl AddAssign for EdgeInsets {
247 fn add_assign(&mut self, rhs: Self) {
248 self.left += rhs.left;
249 self.top += rhs.top;
250 self.right += rhs.right;
251 self.bottom += rhs.bottom;
252 }
253}
254
255#[derive(Clone, Copy, Debug, Default, PartialEq)]
257pub struct CornerRadii {
258 pub top_left: f32,
260 pub top_right: f32,
262 pub bottom_right: f32,
264 pub bottom_left: f32,
266}
267
268impl CornerRadii {
269 pub fn uniform(radius: f32) -> Self {
271 Self {
272 top_left: radius,
273 top_right: radius,
274 bottom_right: radius,
275 bottom_left: radius,
276 }
277 }
278}
279
280#[derive(Clone, Copy, Debug, PartialEq)]
282pub struct RoundedCornerShape {
283 radii: CornerRadii,
284}
285
286impl RoundedCornerShape {
287 pub fn new(top_left: f32, top_right: f32, bottom_right: f32, bottom_left: f32) -> Self {
289 Self {
290 radii: CornerRadii {
291 top_left,
292 top_right,
293 bottom_right,
294 bottom_left,
295 },
296 }
297 }
298
299 pub fn uniform(radius: f32) -> Self {
301 Self {
302 radii: CornerRadii::uniform(radius),
303 }
304 }
305
306 pub fn with_radii(radii: CornerRadii) -> Self {
308 Self { radii }
309 }
310
311 pub fn resolve(&self, width: f32, height: f32) -> CornerRadii {
314 let mut resolved = self.radii;
315 let max_width = (width / 2.0).max(0.0);
316 let max_height = (height / 2.0).max(0.0);
317 resolved.top_left = resolved.top_left.clamp(0.0, max_width).min(max_height);
318 resolved.top_right = resolved.top_right.clamp(0.0, max_width).min(max_height);
319 resolved.bottom_right = resolved.bottom_right.clamp(0.0, max_width).min(max_height);
320 resolved.bottom_left = resolved.bottom_left.clamp(0.0, max_width).min(max_height);
321 resolved
322 }
323
324 pub fn radii(&self) -> CornerRadii {
326 self.radii
327 }
328}
329
330#[derive(Clone, Copy, Debug, PartialEq)]
333pub struct TransformOrigin {
334 pub pivot_fraction_x: f32,
336 pub pivot_fraction_y: f32,
338}
339
340impl TransformOrigin {
341 pub const fn new(pivot_fraction_x: f32, pivot_fraction_y: f32) -> Self {
343 Self {
344 pivot_fraction_x,
345 pivot_fraction_y,
346 }
347 }
348
349 pub const CENTER: Self = Self::new(0.5, 0.5);
351}
352
353impl Default for TransformOrigin {
354 fn default() -> Self {
355 Self::CENTER
356 }
357}
358
359#[derive(Clone, Copy, Debug, Default, PartialEq)]
361pub enum LayerShape {
362 #[default]
364 Rectangle,
365 Rounded(RoundedCornerShape),
367}
368
369#[derive(Clone, Debug, PartialEq)]
372pub struct GraphicsLayer {
373 pub alpha: f32,
375 pub scale: f32,
377 pub scale_x: f32,
379 pub scale_y: f32,
381 pub rotation_x: f32,
383 pub rotation_y: f32,
385 pub rotation_z: f32,
387 pub camera_distance: f32,
390 pub transform_origin: TransformOrigin,
392 pub translation_x: f32,
394 pub translation_y: f32,
396 pub space: Option<std::sync::Arc<crate::space::LayerSpace>>,
401 pub shadow_elevation: f32,
403 pub ambient_shadow_color: Color,
405 pub spot_shadow_color: Color,
407 pub shape: LayerShape,
409 pub clip: bool,
411 pub compositing_strategy: CompositingStrategy,
413 pub blend_mode: BlendMode,
415 pub color_filter: Option<ColorFilter>,
417 pub render_effect: Option<crate::render_effect::RenderEffect>,
420 pub backdrop_effect: Option<crate::render_effect::RenderEffect>,
424}
425
426impl GraphicsLayer {
427 pub const DEFAULT: Self = Self {
429 alpha: 1.0,
430 scale: 1.0,
431 scale_x: 1.0,
432 scale_y: 1.0,
433 rotation_x: 0.0,
434 rotation_y: 0.0,
435 rotation_z: 0.0,
436 camera_distance: 8.0,
437 transform_origin: TransformOrigin::CENTER,
438 translation_x: 0.0,
439 translation_y: 0.0,
440 space: None,
441 shadow_elevation: 0.0,
442 ambient_shadow_color: Color::BLACK,
443 spot_shadow_color: Color::BLACK,
444 shape: LayerShape::Rectangle,
445 clip: false,
446 compositing_strategy: CompositingStrategy::Auto,
447 blend_mode: BlendMode::SrcOver,
448 color_filter: None,
449 render_effect: None,
450 backdrop_effect: None,
451 };
452
453 pub fn composite_alpha_8bit(alpha: f32) -> f32 {
477 (alpha.clamp(0.0, 1.0) * 255.0).floor() / 255.0
478 }
479}
480
481impl Default for GraphicsLayer {
482 fn default() -> Self {
483 Self::DEFAULT
484 }
485}
486
487#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
492pub enum BlendMode {
493 Clear,
495 Src,
497 Dst,
499 #[default]
501 SrcOver,
502 DstOver,
504 SrcIn,
506 DstIn,
508 SrcOut,
510 DstOut,
512 SrcAtop,
514 DstAtop,
516 Xor,
518 Plus,
520 Modulate,
522 Screen,
524 Overlay,
526 Darken,
528 Lighten,
530 ColorDodge,
532 ColorBurn,
534 HardLight,
536 SoftLight,
538 Difference,
540 Exclusion,
542 Multiply,
544 Hue,
546 Saturation,
548 Color,
550 Luminosity,
552}
553
554impl BlendMode {
555 pub const ALL: [Self; 29] = [
557 Self::Clear,
558 Self::Src,
559 Self::Dst,
560 Self::SrcOver,
561 Self::DstOver,
562 Self::SrcIn,
563 Self::DstIn,
564 Self::SrcOut,
565 Self::DstOut,
566 Self::SrcAtop,
567 Self::DstAtop,
568 Self::Xor,
569 Self::Plus,
570 Self::Modulate,
571 Self::Screen,
572 Self::Overlay,
573 Self::Darken,
574 Self::Lighten,
575 Self::ColorDodge,
576 Self::ColorBurn,
577 Self::HardLight,
578 Self::SoftLight,
579 Self::Difference,
580 Self::Exclusion,
581 Self::Multiply,
582 Self::Hue,
583 Self::Saturation,
584 Self::Color,
585 Self::Luminosity,
586 ];
587}
588
589#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
591pub enum CompositingStrategy {
592 #[default]
594 Auto,
595 Offscreen,
597 ModulateAlpha,
599}
600
601#[derive(Clone, Debug, PartialEq)]
603pub enum DrawPrimitive {
604 Content,
607 Blend {
609 primitive: Box<Self>,
611 blend_mode: BlendMode,
613 },
614 Rect {
616 rect: Rect,
618 brush: Brush,
620 stroke: Option<Stroke>,
623 },
624 RoundRect {
626 rect: Rect,
628 brush: Brush,
630 radii: CornerRadii,
632 stroke: Option<Stroke>,
635 },
636 Arc {
646 rect: Rect,
650 brush: Brush,
652 center: Point,
654 radius: f32,
657 start_angle: f32,
660 sweep_angle: f32,
662 stroke: Option<Stroke>,
664 inner_radius: f32,
666 },
667 Line {
670 rect: Rect,
673 brush: Brush,
675 start: Point,
677 end: Point,
679 stroke: Stroke,
681 },
682 Trapezoid {
685 rect: Rect,
688 brush: Brush,
690 trapezoid: Trapezoid,
692 },
693 Image {
695 rect: Rect,
697 image: ImageBitmap,
699 alpha: f32,
701 color_filter: Option<ColorFilter>,
703 sampling: ImageSampling,
705 src_rect: Option<Rect>,
709 },
710 Text(Box<TextPrimitive>),
712 Shadow(ShadowPrimitive),
715 Group(GroupMarker),
718}
719
720impl DrawPrimitive {
721 pub fn translate(self, dx: f32, dy: f32) -> Self {
724 match self {
725 Self::Content => Self::Content,
726 Self::Blend {
727 primitive,
728 blend_mode,
729 } => Self::Blend {
730 primitive: Box::new(primitive.translate(dx, dy)),
731 blend_mode,
732 },
733 Self::Rect {
734 rect,
735 brush,
736 stroke,
737 } => Self::Rect {
738 rect: rect.translate(dx, dy),
739 brush,
740 stroke,
741 },
742 Self::RoundRect {
743 rect,
744 brush,
745 radii,
746 stroke,
747 } => Self::RoundRect {
748 rect: rect.translate(dx, dy),
749 brush,
750 radii,
751 stroke,
752 },
753 Self::Arc {
754 rect,
755 brush,
756 center,
757 radius,
758 start_angle,
759 sweep_angle,
760 stroke,
761 inner_radius,
762 } => Self::Arc {
763 rect: rect.translate(dx, dy),
764 brush,
765 center: Point::new(center.x + dx, center.y + dy),
766 radius,
767 start_angle,
768 sweep_angle,
769 stroke,
770 inner_radius,
771 },
772 Self::Line {
773 rect,
774 brush,
775 start,
776 end,
777 stroke,
778 } => Self::Line {
779 rect: rect.translate(dx, dy),
780 brush,
781 start: Point::new(start.x + dx, start.y + dy),
782 end: Point::new(end.x + dx, end.y + dy),
783 stroke,
784 },
785 Self::Trapezoid {
786 rect,
787 brush,
788 trapezoid,
789 } => Self::Trapezoid {
790 rect: rect.translate(dx, dy),
791 brush,
792 trapezoid: trapezoid.translate(dx, dy),
793 },
794 Self::Image {
795 rect,
796 image,
797 alpha,
798 color_filter,
799 sampling,
800 src_rect,
801 } => Self::Image {
802 rect: rect.translate(dx, dy),
803 image,
804 alpha,
805 color_filter,
806 sampling,
807 src_rect,
808 },
809 Self::Text(mut text) => {
810 text.rect = text.rect.translate(dx, dy);
811 Self::Text(text)
812 }
813 Self::Shadow(ShadowPrimitive::Drop {
814 shape,
815 cutout,
816 blur_radius,
817 blend_mode,
818 }) => Self::Shadow(ShadowPrimitive::Drop {
819 shape: Box::new(shape.translate(dx, dy)),
820 cutout: cutout.map(|cutout| Box::new(cutout.translate(dx, dy))),
821 blur_radius,
822 blend_mode,
823 }),
824 Self::Shadow(ShadowPrimitive::Inner {
825 fill,
826 cutout,
827 blur_radius,
828 blend_mode,
829 clip_rect,
830 }) => Self::Shadow(ShadowPrimitive::Inner {
831 fill: Box::new(fill.translate(dx, dy)),
832 cutout: Box::new(cutout.translate(dx, dy)),
833 blur_radius,
834 blend_mode,
835 clip_rect: clip_rect.translate(dx, dy),
836 }),
837 Self::Group(GroupMarker::Start(group)) => {
838 Self::Group(GroupMarker::Start(group.translate(dx, dy)))
839 }
840 Self::Group(GroupMarker::End) => Self::Group(GroupMarker::End),
841 }
842 }
843}
844
845#[derive(Clone, Debug, PartialEq)]
848pub enum ShadowPrimitive {
849 Drop {
853 shape: Box<DrawPrimitive>,
855 cutout: Option<Box<DrawPrimitive>>,
857 blur_radius: f32,
859 blend_mode: BlendMode,
861 },
862 Inner {
864 fill: Box<DrawPrimitive>,
866 cutout: Box<DrawPrimitive>,
868 blur_radius: f32,
870 blend_mode: BlendMode,
872 clip_rect: Rect,
874 },
875}
876
877pub trait DrawScope {
881 fn size(&self) -> Size;
883 fn draw_content(&mut self);
887 fn draw_rect(&mut self, brush: Brush);
889 fn draw_rect_blend(&mut self, brush: Brush, blend_mode: BlendMode);
891 fn draw_rect_at(&mut self, rect: Rect, brush: Brush);
893 fn draw_rect_at_blend(&mut self, rect: Rect, brush: Brush, blend_mode: BlendMode);
895 fn draw_round_rect(&mut self, brush: Brush, radii: CornerRadii);
897 fn draw_round_rect_blend(&mut self, brush: Brush, radii: CornerRadii, blend_mode: BlendMode);
899 fn draw_round_rect_at(&mut self, rect: Rect, brush: Brush, radii: CornerRadii);
901 fn draw_circle(&mut self, brush: Brush, center: Point, radius: f32);
903 fn draw_circle_blend(
905 &mut self,
906 brush: Brush,
907 center: Point,
908 radius: f32,
909 blend_mode: BlendMode,
910 );
911
912 fn draw_rect_stroked(&mut self, brush: Brush, stroke: Stroke);
914 fn draw_rect_stroked_blend(&mut self, brush: Brush, stroke: Stroke, blend_mode: BlendMode);
916 fn draw_rect_at_stroked(&mut self, rect: Rect, brush: Brush, stroke: Stroke);
918 fn draw_rect_at_stroked_blend(
920 &mut self,
921 rect: Rect,
922 brush: Brush,
923 stroke: Stroke,
924 blend_mode: BlendMode,
925 );
926 fn draw_round_rect_stroked(&mut self, brush: Brush, radii: CornerRadii, stroke: Stroke);
928 fn draw_round_rect_stroked_blend(
930 &mut self,
931 brush: Brush,
932 radii: CornerRadii,
933 stroke: Stroke,
934 blend_mode: BlendMode,
935 );
936 fn draw_round_rect_at_stroked(
938 &mut self,
939 rect: Rect,
940 brush: Brush,
941 radii: CornerRadii,
942 stroke: Stroke,
943 );
944 fn draw_round_rect_at_stroked_blend(
946 &mut self,
947 rect: Rect,
948 brush: Brush,
949 radii: CornerRadii,
950 stroke: Stroke,
951 blend_mode: BlendMode,
952 );
953 fn draw_circle_stroked(&mut self, brush: Brush, center: Point, radius: f32, stroke: Stroke);
956 fn draw_circle_stroked_blend(
958 &mut self,
959 brush: Brush,
960 center: Point,
961 radius: f32,
962 stroke: Stroke,
963 blend_mode: BlendMode,
964 );
965
966 fn draw_arc(
979 &mut self,
980 brush: Brush,
981 center: Point,
982 radius: f32,
983 start_angle: f32,
984 sweep_angle: f32,
985 stroke: Stroke,
986 );
987 #[expect(clippy::too_many_arguments, reason = "Compose's parameters")]
989 fn draw_arc_blend(
990 &mut self,
991 brush: Brush,
992 center: Point,
993 radius: f32,
994 start_angle: f32,
995 sweep_angle: f32,
996 stroke: Stroke,
997 blend_mode: BlendMode,
998 );
999
1000 fn draw_line(&mut self, brush: Brush, start: Point, end: Point, stroke: Stroke);
1006 fn draw_line_blend(
1008 &mut self,
1009 brush: Brush,
1010 start: Point,
1011 end: Point,
1012 stroke: Stroke,
1013 blend_mode: BlendMode,
1014 );
1015
1016 fn draw_path(&mut self, path: &Path, brush: Brush, style: DrawStyle);
1025 fn draw_path_blend(
1027 &mut self,
1028 path: &Path,
1029 brush: Brush,
1030 style: DrawStyle,
1031 blend_mode: BlendMode,
1032 );
1033
1034 fn draw_annular_sector(
1043 &mut self,
1044 brush: Brush,
1045 center: Point,
1046 inner_radius: f32,
1047 outer_radius: f32,
1048 start_angle: f32,
1049 sweep_angle: f32,
1050 );
1051 #[expect(clippy::too_many_arguments, reason = "Compose's parameters")]
1053 fn draw_annular_sector_blend(
1054 &mut self,
1055 brush: Brush,
1056 center: Point,
1057 inner_radius: f32,
1058 outer_radius: f32,
1059 start_angle: f32,
1060 sweep_angle: f32,
1061 blend_mode: BlendMode,
1062 );
1063
1064 fn draw_image(&mut self, image: ImageBitmap);
1066 fn draw_image_blend(&mut self, image: ImageBitmap, blend_mode: BlendMode);
1068 fn draw_image_at(
1071 &mut self,
1072 rect: Rect,
1073 image: ImageBitmap,
1074 alpha: f32,
1075 color_filter: Option<ColorFilter>,
1076 );
1077 fn draw_image_at_sampled(
1079 &mut self,
1080 rect: Rect,
1081 image: ImageBitmap,
1082 alpha: f32,
1083 color_filter: Option<ColorFilter>,
1084 sampling: ImageSampling,
1085 );
1086 fn draw_image_at_blend(
1088 &mut self,
1089 rect: Rect,
1090 image: ImageBitmap,
1091 alpha: f32,
1092 color_filter: Option<ColorFilter>,
1093 blend_mode: BlendMode,
1094 );
1095 fn draw_image_src(
1098 &mut self,
1099 image: ImageBitmap,
1100 src_rect: Rect,
1101 dst_rect: Rect,
1102 alpha: f32,
1103 color_filter: Option<ColorFilter>,
1104 );
1105 fn draw_image_src_sampled(
1107 &mut self,
1108 image: ImageBitmap,
1109 src_rect: Rect,
1110 dst_rect: Rect,
1111 alpha: f32,
1112 color_filter: Option<ColorFilter>,
1113 sampling: ImageSampling,
1114 );
1115 fn draw_image_src_blend(
1117 &mut self,
1118 image: ImageBitmap,
1119 src_rect: Rect,
1120 dst_rect: Rect,
1121 alpha: f32,
1122 color_filter: Option<ColorFilter>,
1123 blend_mode: BlendMode,
1124 );
1125 fn draw_vector_path(&mut self, path: &crate::VectorPath, brush: Brush);
1133 fn draw_svg_path(&mut self, d: &str, brush: Brush) {
1139 if let Ok(path) = crate::VectorPath::parse(d) {
1140 self.draw_vector_path(&path, brush);
1141 }
1142 }
1143
1144 fn measure_text(&self, text: &str, style: &DrawTextStyle) -> TextMeasurement;
1151
1152 fn draw_text(&mut self, brush: Brush, text: &str, style: &DrawTextStyle) {
1155 self.draw_text_at(Rect::from_size(self.size()), brush, text, style);
1156 }
1157
1158 fn draw_text_at(&mut self, rect: Rect, brush: Brush, text: &str, style: &DrawTextStyle);
1165
1166 fn draw_text_from(&mut self, top_left: Point, brush: Brush, text: &str, style: &DrawTextStyle);
1172
1173 fn with_transform(
1179 &mut self,
1180 transform: &[DrawTransform<'_>],
1181 block: &mut dyn FnMut(&mut dyn DrawScope),
1182 );
1183
1184 fn translate(&mut self, dx: f32, dy: f32, block: &mut dyn FnMut(&mut dyn DrawScope)) {
1186 self.with_transform(&[DrawTransform::Translate { dx, dy }], block);
1187 }
1188
1189 fn scale(
1192 &mut self,
1193 scale_x: f32,
1194 scale_y: f32,
1195 pivot: Point,
1196 block: &mut dyn FnMut(&mut dyn DrawScope),
1197 ) {
1198 self.with_transform(
1199 &[DrawTransform::Scale {
1200 scale_x,
1201 scale_y,
1202 pivot,
1203 }],
1204 block,
1205 );
1206 }
1207
1208 fn rotate(&mut self, degrees: f32, pivot: Point, block: &mut dyn FnMut(&mut dyn DrawScope)) {
1211 self.with_transform(&[DrawTransform::Rotate { degrees, pivot }], block);
1212 }
1213
1214 fn inset(
1217 &mut self,
1218 left: f32,
1219 top: f32,
1220 right: f32,
1221 bottom: f32,
1222 block: &mut dyn FnMut(&mut dyn DrawScope),
1223 ) {
1224 self.with_transform(
1225 &[DrawTransform::Inset {
1226 left,
1227 top,
1228 right,
1229 bottom,
1230 }],
1231 block,
1232 );
1233 }
1234
1235 fn clip_rect(&mut self, rect: Rect, block: &mut dyn FnMut(&mut dyn DrawScope)) {
1237 self.with_transform(&[DrawTransform::ClipRect(rect)], block);
1238 }
1239
1240 fn clip_path(&mut self, path: &Path, block: &mut dyn FnMut(&mut dyn DrawScope)) {
1243 self.with_transform(&[DrawTransform::ClipPath(path)], block);
1244 }
1245
1246 fn into_primitives(self) -> Vec<DrawPrimitive>;
1249}
1250
1251pub struct DrawScopeDefault<'a> {
1255 size: Size,
1256 recording: CommandRecorder<'a>,
1257 text_measurer: Option<Rc<dyn DrawTextMeasurer>>,
1258}
1259
1260impl<'a> DrawScopeDefault<'a> {
1261 pub fn new(size: Size, recording: &'a mut CommandRecording) -> Self {
1264 Self::with_storage(size, None, recording)
1265 }
1266
1267 pub fn with_text_measurer(
1272 size: Size,
1273 text_measurer: Rc<dyn DrawTextMeasurer>,
1274 recording: &'a mut CommandRecording,
1275 ) -> Self {
1276 Self::with_storage(size, Some(text_measurer), recording)
1277 }
1278
1279 fn with_storage(
1280 size: Size,
1281 text_measurer: Option<Rc<dyn DrawTextMeasurer>>,
1282 recording: &'a mut CommandRecording,
1283 ) -> Self {
1284 Self {
1285 size,
1286 recording: recording.record(),
1287 text_measurer,
1288 }
1289 }
1290
1291 pub fn content_marker_count(&self) -> u32 {
1293 self.recording.content_markers()
1294 }
1295
1296 pub fn draw_inset(&mut self, insets: EdgeInsets, draw: impl FnOnce(&mut DrawScopeDefault<'_>)) {
1300 let inner = insets.inset_rect(self.size);
1301 let mut recording = CommandRecording::default();
1302 let mut scope = DrawScopeDefault::with_storage(
1303 inner.size(),
1304 self.text_measurer.clone(),
1305 &mut recording,
1306 );
1307 draw(&mut scope);
1308 scope.finish();
1309 self.push_recorded(crate::translate_recorded(
1310 recording.primitives_with_markers(),
1311 inner.x,
1312 inner.y,
1313 ));
1314 }
1315
1316 pub fn push_recorded(&mut self, primitives: impl IntoIterator<Item = DrawPrimitive>) {
1318 for primitive in primitives {
1319 self.recording.push_primitive(primitive);
1320 }
1321 }
1322
1323 pub fn finish(self) {
1325 self.recording.finish();
1326 }
1327
1328 fn push_blended_primitive(&mut self, primitive: DrawPrimitive, blend_mode: BlendMode) {
1329 if blend_mode != BlendMode::SrcOver {
1330 self.recording.push_other(DrawPrimitive::Blend {
1331 primitive: Box::new(primitive),
1332 blend_mode,
1333 });
1334 return;
1335 }
1336 self.recording.push_other(primitive);
1337 }
1338
1339 #[inline]
1340 fn push_arc(&mut self, args: &ArcRecordArgs<'_>) {
1341 let geometry = normalized_band(args);
1342 if geometry.is_degenerate() {
1343 return;
1344 }
1345 self.recording.push_scope_arc(args, &geometry);
1346 }
1347
1348 fn with_path_mask(
1353 &mut self,
1354 mask: &crate::VectorPath,
1355 rest: &[DrawTransform<'_>],
1356 block: &mut dyn FnMut(&mut dyn DrawScope),
1357 ) {
1358 let rect = vector_mask_rect(mask.bounds());
1359 self.recording.push_group_start(DrawGroup::masked(rect));
1360 self.with_transform(rest, block);
1361 record_vector_path(
1362 &mut self.recording,
1363 mask,
1364 &Brush::solid(Color::BLACK),
1365 rect,
1366 BlendMode::DstOut,
1367 PathSide::Outside,
1368 );
1369 self.recording.push_group_end();
1370 }
1371}
1372
1373impl DrawScope for DrawScopeDefault<'_> {
1374 fn size(&self) -> Size {
1375 self.size
1376 }
1377
1378 fn with_transform(
1379 &mut self,
1380 transform: &[DrawTransform<'_>],
1381 block: &mut dyn FnMut(&mut dyn DrawScope),
1382 ) {
1383 let Some((step, rest)) = transform.split_first() else {
1384 block(self);
1385 return;
1386 };
1387 let outer = self.size;
1388 let (group, size) = match *step {
1389 DrawTransform::Translate { dx, dy } => (DrawGroup::moved(dx, dy, outer), outer),
1390 DrawTransform::Scale {
1391 scale_x,
1392 scale_y,
1393 pivot,
1394 } => (DrawGroup::about(pivot, scale_x, scale_y, 0.0, outer), outer),
1395 DrawTransform::Rotate { degrees, pivot } => {
1396 (DrawGroup::about(pivot, 1.0, 1.0, degrees, outer), outer)
1397 }
1398 DrawTransform::Inset {
1399 left,
1400 top,
1401 right,
1402 bottom,
1403 } => {
1404 let inner = Size::new(
1405 (outer.width - left - right).max(0.0),
1406 (outer.height - top - bottom).max(0.0),
1407 );
1408 (DrawGroup::moved(left, top, inner), inner)
1409 }
1410 DrawTransform::ClipRect(rect) => (DrawGroup::clipped(rect), outer),
1411 DrawTransform::ClipPath(path) => {
1412 let mask = path.to_vector_path(PathFillRule::NonZero);
1413 if !mask.is_empty() {
1414 self.with_path_mask(&mask, rest, block);
1415 return;
1416 }
1417 (DrawGroup::clipped(Rect::EMPTY), outer)
1419 }
1420 };
1421 self.recording.push_group_start(group);
1422 self.size = size;
1423 self.with_transform(rest, block);
1424 self.size = outer;
1425 self.recording.push_group_end();
1426 }
1427
1428 fn draw_content(&mut self) {
1429 self.recording.push_content();
1430 }
1431
1432 fn draw_rect(&mut self, brush: Brush) {
1433 self.draw_rect_blend(brush, BlendMode::SrcOver);
1434 }
1435
1436 fn draw_rect_blend(&mut self, brush: Brush, blend_mode: BlendMode) {
1437 self.recording
1438 .push_rect(Rect::from_size(self.size), &brush, None, blend_mode);
1439 }
1440
1441 fn draw_rect_at(&mut self, rect: Rect, brush: Brush) {
1442 self.draw_rect_at_blend(rect, brush, BlendMode::SrcOver);
1443 }
1444
1445 fn draw_rect_at_blend(&mut self, rect: Rect, brush: Brush, blend_mode: BlendMode) {
1446 self.recording.push_rect(rect, &brush, None, blend_mode);
1447 }
1448
1449 fn draw_round_rect(&mut self, brush: Brush, radii: CornerRadii) {
1450 self.draw_round_rect_blend(brush, radii, BlendMode::SrcOver);
1451 }
1452
1453 fn draw_round_rect_blend(&mut self, brush: Brush, radii: CornerRadii, blend_mode: BlendMode) {
1454 self.recording
1455 .push_round_rect(Rect::from_size(self.size), &brush, radii, None, blend_mode);
1456 }
1457
1458 fn draw_round_rect_at(&mut self, rect: Rect, brush: Brush, radii: CornerRadii) {
1459 self.recording
1460 .push_round_rect(rect, &brush, radii, None, BlendMode::SrcOver);
1461 }
1462
1463 fn draw_rect_stroked(&mut self, brush: Brush, stroke: Stroke) {
1464 self.draw_rect_stroked_blend(brush, stroke, BlendMode::SrcOver);
1465 }
1466
1467 fn draw_rect_stroked_blend(&mut self, brush: Brush, stroke: Stroke, blend_mode: BlendMode) {
1468 self.draw_rect_at_stroked_blend(Rect::from_size(self.size), brush, stroke, blend_mode);
1469 }
1470
1471 fn draw_rect_at_stroked(&mut self, rect: Rect, brush: Brush, stroke: Stroke) {
1472 self.draw_rect_at_stroked_blend(rect, brush, stroke, BlendMode::SrcOver);
1473 }
1474
1475 fn draw_rect_at_stroked_blend(
1476 &mut self,
1477 rect: Rect,
1478 brush: Brush,
1479 stroke: Stroke,
1480 blend_mode: BlendMode,
1481 ) {
1482 if !stroke.is_visible() {
1483 return;
1484 }
1485 self.recording
1486 .push_rect(rect, &brush, Some(stroke), blend_mode);
1487 }
1488
1489 fn draw_round_rect_stroked(&mut self, brush: Brush, radii: CornerRadii, stroke: Stroke) {
1490 self.draw_round_rect_stroked_blend(brush, radii, stroke, BlendMode::SrcOver);
1491 }
1492
1493 fn draw_round_rect_stroked_blend(
1494 &mut self,
1495 brush: Brush,
1496 radii: CornerRadii,
1497 stroke: Stroke,
1498 blend_mode: BlendMode,
1499 ) {
1500 self.draw_round_rect_at_stroked_blend(
1501 Rect::from_size(self.size),
1502 brush,
1503 radii,
1504 stroke,
1505 blend_mode,
1506 );
1507 }
1508
1509 fn draw_round_rect_at_stroked(
1510 &mut self,
1511 rect: Rect,
1512 brush: Brush,
1513 radii: CornerRadii,
1514 stroke: Stroke,
1515 ) {
1516 self.draw_round_rect_at_stroked_blend(rect, brush, radii, stroke, BlendMode::SrcOver);
1517 }
1518
1519 fn draw_round_rect_at_stroked_blend(
1520 &mut self,
1521 rect: Rect,
1522 brush: Brush,
1523 radii: CornerRadii,
1524 stroke: Stroke,
1525 blend_mode: BlendMode,
1526 ) {
1527 if !stroke.is_visible() {
1528 return;
1529 }
1530 self.recording
1531 .push_round_rect(rect, &brush, radii, Some(stroke), blend_mode);
1532 }
1533
1534 fn draw_circle_stroked(&mut self, brush: Brush, center: Point, radius: f32, stroke: Stroke) {
1535 self.draw_circle_stroked_blend(brush, center, radius, stroke, BlendMode::SrcOver);
1536 }
1537
1538 fn draw_circle_stroked_blend(
1539 &mut self,
1540 brush: Brush,
1541 center: Point,
1542 radius: f32,
1543 stroke: Stroke,
1544 blend_mode: BlendMode,
1545 ) {
1546 if !stroke.is_visible() || !radius.is_finite() {
1547 return;
1548 }
1549 let radius = radius.max(0.0);
1550 let diameter = radius * 2.0;
1551 self.draw_round_rect_at_stroked_blend(
1552 Rect {
1553 x: center.x - radius,
1554 y: center.y - radius,
1555 width: diameter,
1556 height: diameter,
1557 },
1558 brush,
1559 CornerRadii::uniform(radius),
1560 stroke,
1561 blend_mode,
1562 );
1563 }
1564
1565 fn draw_arc(
1566 &mut self,
1567 brush: Brush,
1568 center: Point,
1569 radius: f32,
1570 start_angle: f32,
1571 sweep_angle: f32,
1572 stroke: Stroke,
1573 ) {
1574 self.draw_arc_blend(
1575 brush,
1576 center,
1577 radius,
1578 start_angle,
1579 sweep_angle,
1580 stroke,
1581 BlendMode::SrcOver,
1582 );
1583 }
1584
1585 fn draw_arc_blend(
1586 &mut self,
1587 brush: Brush,
1588 center: Point,
1589 radius: f32,
1590 start_angle: f32,
1591 sweep_angle: f32,
1592 stroke: Stroke,
1593 blend_mode: BlendMode,
1594 ) {
1595 if !stroke.is_visible() {
1596 return;
1597 }
1598 self.push_arc(&ArcRecordArgs {
1599 brush: &brush,
1600 center,
1601 radius,
1602 start_angle,
1603 sweep_angle,
1604 stroke: Some(stroke),
1605 inner_radius: 0.0,
1606 blend_mode,
1607 });
1608 }
1609
1610 fn draw_line(&mut self, brush: Brush, start: Point, end: Point, stroke: Stroke) {
1611 self.draw_line_blend(brush, start, end, stroke, BlendMode::SrcOver);
1612 }
1613
1614 fn draw_line_blend(
1615 &mut self,
1616 brush: Brush,
1617 start: Point,
1618 end: Point,
1619 stroke: Stroke,
1620 blend_mode: BlendMode,
1621 ) {
1622 self.recording
1623 .line_run(&brush, stroke, blend_mode)
1624 .push(start, end, stroke.cap);
1625 }
1626
1627 fn draw_path(&mut self, path: &Path, brush: Brush, style: DrawStyle) {
1628 self.draw_path_blend(path, brush, style, BlendMode::SrcOver);
1629 }
1630
1631 fn draw_path_blend(
1632 &mut self,
1633 path: &Path,
1634 brush: Brush,
1635 style: DrawStyle,
1636 blend_mode: BlendMode,
1637 ) {
1638 record_path(
1639 &mut self.recording,
1640 self.size,
1641 path,
1642 &brush,
1643 style,
1644 blend_mode,
1645 );
1646 }
1647
1648 fn draw_annular_sector(
1649 &mut self,
1650 brush: Brush,
1651 center: Point,
1652 inner_radius: f32,
1653 outer_radius: f32,
1654 start_angle: f32,
1655 sweep_angle: f32,
1656 ) {
1657 self.draw_annular_sector_blend(
1658 brush,
1659 center,
1660 inner_radius,
1661 outer_radius,
1662 start_angle,
1663 sweep_angle,
1664 BlendMode::SrcOver,
1665 );
1666 }
1667
1668 fn draw_annular_sector_blend(
1669 &mut self,
1670 brush: Brush,
1671 center: Point,
1672 inner_radius: f32,
1673 outer_radius: f32,
1674 start_angle: f32,
1675 sweep_angle: f32,
1676 blend_mode: BlendMode,
1677 ) {
1678 self.push_arc(&ArcRecordArgs {
1679 brush: &brush,
1680 center,
1681 radius: outer_radius,
1682 start_angle,
1683 sweep_angle,
1684 stroke: None,
1685 inner_radius,
1686 blend_mode,
1687 });
1688 }
1689
1690 fn draw_circle(&mut self, brush: Brush, center: Point, radius: f32) {
1691 self.draw_circle_blend(brush, center, radius, BlendMode::SrcOver);
1692 }
1693
1694 fn draw_circle_blend(
1695 &mut self,
1696 brush: Brush,
1697 center: Point,
1698 radius: f32,
1699 blend_mode: BlendMode,
1700 ) {
1701 let radius = radius.max(0.0);
1702 let diameter = radius * 2.0;
1703 self.recording.push_round_rect(
1704 Rect {
1705 x: center.x - radius,
1706 y: center.y - radius,
1707 width: diameter,
1708 height: diameter,
1709 },
1710 &brush,
1711 CornerRadii::uniform(radius),
1712 None,
1713 blend_mode,
1714 );
1715 }
1716
1717 fn draw_image(&mut self, image: ImageBitmap) {
1718 self.draw_image_blend(image, BlendMode::SrcOver);
1719 }
1720
1721 fn draw_image_blend(&mut self, image: ImageBitmap, blend_mode: BlendMode) {
1722 self.push_blended_primitive(
1723 DrawPrimitive::Image {
1724 rect: Rect::from_size(self.size),
1725 image,
1726 alpha: 1.0,
1727 color_filter: None,
1728 sampling: ImageSampling::Nearest,
1729 src_rect: None,
1730 },
1731 blend_mode,
1732 );
1733 }
1734
1735 fn draw_image_at(
1736 &mut self,
1737 rect: Rect,
1738 image: ImageBitmap,
1739 alpha: f32,
1740 color_filter: Option<ColorFilter>,
1741 ) {
1742 self.draw_image_at_sampled(rect, image, alpha, color_filter, ImageSampling::Nearest);
1743 }
1744
1745 fn draw_image_at_sampled(
1746 &mut self,
1747 rect: Rect,
1748 image: ImageBitmap,
1749 alpha: f32,
1750 color_filter: Option<ColorFilter>,
1751 sampling: ImageSampling,
1752 ) {
1753 self.push_blended_primitive(
1754 DrawPrimitive::Image {
1755 rect,
1756 image,
1757 alpha: alpha.clamp(0.0, 1.0),
1758 color_filter,
1759 sampling,
1760 src_rect: None,
1761 },
1762 BlendMode::SrcOver,
1763 );
1764 }
1765
1766 fn draw_image_at_blend(
1767 &mut self,
1768 rect: Rect,
1769 image: ImageBitmap,
1770 alpha: f32,
1771 color_filter: Option<ColorFilter>,
1772 blend_mode: BlendMode,
1773 ) {
1774 self.push_blended_primitive(
1775 DrawPrimitive::Image {
1776 rect,
1777 image,
1778 alpha: alpha.clamp(0.0, 1.0),
1779 color_filter,
1780 sampling: ImageSampling::Nearest,
1781 src_rect: None,
1782 },
1783 blend_mode,
1784 );
1785 }
1786
1787 fn draw_image_src(
1788 &mut self,
1789 image: ImageBitmap,
1790 src_rect: Rect,
1791 dst_rect: Rect,
1792 alpha: f32,
1793 color_filter: Option<ColorFilter>,
1794 ) {
1795 self.draw_image_src_blend(
1796 image,
1797 src_rect,
1798 dst_rect,
1799 alpha,
1800 color_filter,
1801 BlendMode::SrcOver,
1802 );
1803 }
1804
1805 fn draw_image_src_sampled(
1806 &mut self,
1807 image: ImageBitmap,
1808 src_rect: Rect,
1809 dst_rect: Rect,
1810 alpha: f32,
1811 color_filter: Option<ColorFilter>,
1812 sampling: ImageSampling,
1813 ) {
1814 self.push_blended_primitive(
1815 DrawPrimitive::Image {
1816 rect: dst_rect,
1817 image,
1818 alpha: alpha.clamp(0.0, 1.0),
1819 color_filter,
1820 sampling,
1821 src_rect: Some(src_rect),
1822 },
1823 BlendMode::SrcOver,
1824 );
1825 }
1826
1827 fn draw_image_src_blend(
1828 &mut self,
1829 image: ImageBitmap,
1830 src_rect: Rect,
1831 dst_rect: Rect,
1832 alpha: f32,
1833 color_filter: Option<ColorFilter>,
1834 blend_mode: BlendMode,
1835 ) {
1836 self.push_blended_primitive(
1837 DrawPrimitive::Image {
1838 rect: dst_rect,
1839 image,
1840 alpha: alpha.clamp(0.0, 1.0),
1841 color_filter,
1842 sampling: ImageSampling::Nearest,
1843 src_rect: Some(src_rect),
1844 },
1845 blend_mode,
1846 );
1847 }
1848
1849 fn draw_vector_path(&mut self, path: &crate::VectorPath, brush: Brush) {
1850 record_vector_path(
1851 &mut self.recording,
1852 path,
1853 &brush,
1854 Rect::from_size(self.size),
1855 BlendMode::SrcOver,
1856 PathSide::Inside,
1857 );
1858 }
1859
1860 fn measure_text(&self, text: &str, style: &DrawTextStyle) -> TextMeasurement {
1861 measured_text(self.text_measurer.as_deref(), text, style).measurement
1862 }
1863
1864 fn draw_text_at(&mut self, rect: Rect, brush: Brush, text: &str, style: &DrawTextStyle) {
1865 let at = TextBlockAt::Aligned(rect);
1866 let measurer = self.text_measurer.as_deref();
1867 if let Some(text) = text_primitive(measurer, at, brush, text, style) {
1868 self.recording.push_other(text);
1869 }
1870 }
1871
1872 fn draw_text_from(&mut self, top_left: Point, brush: Brush, text: &str, style: &DrawTextStyle) {
1873 let at = TextBlockAt::From(top_left);
1874 let measurer = self.text_measurer.as_deref();
1875 if let Some(text) = text_primitive(measurer, at, brush, text, style) {
1876 self.recording.push_other(text);
1877 }
1878 }
1879
1880 fn into_primitives(self) -> Vec<DrawPrimitive> {
1881 self.recording.into_primitives()
1882 }
1883}
1884
1885#[cfg(test)]
1886#[path = "tests/geometry_tests.rs"]
1887mod tests;