1use std::{ops::AddAssign, rc::Rc};
4
5use crate::{
6 ArcRecordArgs, Brush, Color, ColorFilter, CommandRecorder, CommandRecording, ImageBitmap,
7 ImageSampling, PathFillRule, Trapezoid, normalized_band,
8 path::{DrawStyle, Path},
9 stroke::{LineGeometry, Stroke},
10 typography::{
11 DrawTextMeasurer, DrawTextStyle, TextAlign, TextMeasurement, TextVerticalAlign,
12 estimate_text_measurement,
13 },
14};
15
16const VECTOR_PATH_MASK_CACHE_ENTRIES: usize = 96;
17const VECTOR_PATH_MASK_CACHE_BYTES: usize = 8 * 1024 * 1024;
18
19pub(crate) struct VectorPathMaskCache {
20 entries: Vec<(u64, ImageBitmap)>,
21 bytes: usize,
22}
23
24impl VectorPathMaskCache {
25 pub(crate) const fn new() -> Self {
26 Self {
27 entries: Vec::new(),
28 bytes: 0,
29 }
30 }
31
32 fn get(&mut self, key: u64) -> Option<ImageBitmap> {
33 let index = self.entries.iter().position(|(seen, _)| *seen == key)?;
34 let entry = self.entries.remove(index);
35 let image = entry.1.clone();
36 self.entries.push(entry);
37 Some(image)
38 }
39
40 fn put(&mut self, key: u64, image: ImageBitmap) {
41 let bytes = image.pixels().len();
42 if bytes > VECTOR_PATH_MASK_CACHE_BYTES {
43 return;
44 }
45 self.bytes += bytes;
46 self.entries.push((key, image));
47 while self.entries.len() > VECTOR_PATH_MASK_CACHE_ENTRIES
48 || self.bytes > VECTOR_PATH_MASK_CACHE_BYTES
49 {
50 let (_, dropped) = self.entries.remove(0);
51 self.bytes = self.bytes.saturating_sub(dropped.pixels().len());
52 }
53 }
54}
55
56fn vector_path_mask_key(
57 path: &crate::VectorPath,
58 origin: Point,
59 mask_size: (usize, usize),
60 rgb: [u8; 3],
61 alpha: f32,
62) -> u64 {
63 use std::hash::Hasher;
64 let mut hasher = crate::fx_hash::FxHasher::default();
65 hasher.write_u8(path.fill_rule() as u8);
66 hasher.write_u32(origin.x.to_bits());
67 hasher.write_u32(origin.y.to_bits());
68 hasher.write_usize(mask_size.0);
69 hasher.write_usize(mask_size.1);
70 hasher.write(&rgb);
71 hasher.write_u32(alpha.to_bits());
72 for subpath in path.subpaths() {
73 hasher.write_usize(subpath.len());
74 for point in subpath {
75 hasher.write_u32(point.x.to_bits());
76 hasher.write_u32(point.y.to_bits());
77 }
78 }
79 hasher.finish()
80}
81
82fn vector_path_mask_cache_get(key: u64) -> Option<ImageBitmap> {
83 crate::thread_state::with(|state| state.vector_path_masks.borrow_mut().get(key))
84}
85
86fn vector_path_mask_cache_put(key: u64, image: ImageBitmap) {
87 crate::thread_state::with(|state| state.vector_path_masks.borrow_mut().put(key, image));
88}
89
90#[derive(Clone, Copy, Debug, PartialEq, Default)]
92pub struct Point {
93 pub x: f32,
95 pub y: f32,
97}
98
99impl Point {
100 pub const fn new(x: f32, y: f32) -> Self {
102 Self { x, y }
103 }
104
105 pub const ZERO: Point = Point { x: 0.0, y: 0.0 };
107}
108
109#[derive(Clone, Copy, Debug, PartialEq, Default)]
111pub struct Size {
112 pub width: f32,
114 pub height: f32,
116}
117
118impl Size {
119 pub const fn new(width: f32, height: f32) -> Self {
121 Self { width, height }
122 }
123
124 pub const ZERO: Size = Size {
126 width: 0.0,
127 height: 0.0,
128 };
129}
130
131#[derive(Clone, Copy, Debug, PartialEq)]
133pub struct Rect {
134 pub x: f32,
136 pub y: f32,
138 pub width: f32,
140 pub height: f32,
142}
143
144impl Rect {
145 pub const EMPTY: Rect = Rect {
149 x: 0.0,
150 y: 0.0,
151 width: 0.0,
152 height: 0.0,
153 };
154
155 pub fn from_origin_size(origin: Point, size: Size) -> Self {
157 Self {
158 x: origin.x,
159 y: origin.y,
160 width: size.width,
161 height: size.height,
162 }
163 }
164
165 pub fn from_size(size: Size) -> Self {
167 Self {
168 x: 0.0,
169 y: 0.0,
170 width: size.width,
171 height: size.height,
172 }
173 }
174
175 pub fn translate(&self, dx: f32, dy: f32) -> Self {
177 Self {
178 x: self.x + dx,
179 y: self.y + dy,
180 width: self.width,
181 height: self.height,
182 }
183 }
184
185 pub fn contains(&self, x: f32, y: f32) -> bool {
187 x >= self.x && y >= self.y && x <= self.x + self.width && y <= self.y + self.height
188 }
189
190 pub fn is_empty(&self) -> bool {
192 self.width <= 0.0 || self.height <= 0.0
193 }
194
195 pub fn intersect(&self, other: Rect) -> Option<Rect> {
197 let left = self.x.max(other.x);
198 let top = self.y.max(other.y);
199 let right = (self.x + self.width).min(other.x + other.width);
200 let bottom = (self.y + self.height).min(other.y + other.height);
201 let width = right - left;
202 let height = bottom - top;
203 if width <= 0.0 || height <= 0.0 {
204 None
205 } else {
206 Some(Rect {
207 x: left,
208 y: top,
209 width,
210 height,
211 })
212 }
213 }
214
215 pub fn union(&self, other: Rect) -> Rect {
217 let left = self.x.min(other.x);
218 let top = self.y.min(other.y);
219 let right = (self.x + self.width).max(other.x + other.width);
220 let bottom = (self.y + self.height).max(other.y + other.height);
221 Rect {
222 x: left,
223 y: top,
224 width: (right - left).max(0.0),
225 height: (bottom - top).max(0.0),
226 }
227 }
228}
229
230#[derive(Clone, Copy, Debug, Default, PartialEq)]
232pub struct EdgeInsets {
233 pub left: f32,
235 pub top: f32,
237 pub right: f32,
239 pub bottom: f32,
241}
242
243impl EdgeInsets {
244 pub fn uniform(all: f32) -> Self {
246 Self {
247 left: all,
248 top: all,
249 right: all,
250 bottom: all,
251 }
252 }
253
254 pub fn horizontal(horizontal: f32) -> Self {
256 Self {
257 left: horizontal,
258 right: horizontal,
259 ..Self::default()
260 }
261 }
262
263 pub fn vertical(vertical: f32) -> Self {
265 Self {
266 top: vertical,
267 bottom: vertical,
268 ..Self::default()
269 }
270 }
271
272 pub fn symmetric(horizontal: f32, vertical: f32) -> Self {
274 Self {
275 left: horizontal,
276 right: horizontal,
277 top: vertical,
278 bottom: vertical,
279 }
280 }
281
282 pub fn from_components(left: f32, top: f32, right: f32, bottom: f32) -> Self {
284 Self {
285 left,
286 top,
287 right,
288 bottom,
289 }
290 }
291
292 pub fn is_zero(&self) -> bool {
294 self.left == 0.0 && self.top == 0.0 && self.right == 0.0 && self.bottom == 0.0
295 }
296
297 pub fn horizontal_sum(&self) -> f32 {
299 self.left + self.right
300 }
301
302 pub fn vertical_sum(&self) -> f32 {
304 self.top + self.bottom
305 }
306
307 pub fn inset_rect(&self, size: Size) -> Rect {
310 Rect {
311 x: self.left,
312 y: self.top,
313 width: (size.width - self.horizontal_sum()).max(0.0),
314 height: (size.height - self.vertical_sum()).max(0.0),
315 }
316 }
317}
318
319impl AddAssign for EdgeInsets {
320 fn add_assign(&mut self, rhs: Self) {
321 self.left += rhs.left;
322 self.top += rhs.top;
323 self.right += rhs.right;
324 self.bottom += rhs.bottom;
325 }
326}
327
328#[derive(Clone, Copy, Debug, Default, PartialEq)]
330pub struct CornerRadii {
331 pub top_left: f32,
333 pub top_right: f32,
335 pub bottom_right: f32,
337 pub bottom_left: f32,
339}
340
341impl CornerRadii {
342 pub fn uniform(radius: f32) -> Self {
344 Self {
345 top_left: radius,
346 top_right: radius,
347 bottom_right: radius,
348 bottom_left: radius,
349 }
350 }
351}
352
353#[derive(Clone, Copy, Debug, PartialEq)]
355pub struct RoundedCornerShape {
356 radii: CornerRadii,
357}
358
359impl RoundedCornerShape {
360 pub fn new(top_left: f32, top_right: f32, bottom_right: f32, bottom_left: f32) -> Self {
362 Self {
363 radii: CornerRadii {
364 top_left,
365 top_right,
366 bottom_right,
367 bottom_left,
368 },
369 }
370 }
371
372 pub fn uniform(radius: f32) -> Self {
374 Self {
375 radii: CornerRadii::uniform(radius),
376 }
377 }
378
379 pub fn with_radii(radii: CornerRadii) -> Self {
381 Self { radii }
382 }
383
384 pub fn resolve(&self, width: f32, height: f32) -> CornerRadii {
387 let mut resolved = self.radii;
388 let max_width = (width / 2.0).max(0.0);
389 let max_height = (height / 2.0).max(0.0);
390 resolved.top_left = resolved.top_left.clamp(0.0, max_width).min(max_height);
391 resolved.top_right = resolved.top_right.clamp(0.0, max_width).min(max_height);
392 resolved.bottom_right = resolved.bottom_right.clamp(0.0, max_width).min(max_height);
393 resolved.bottom_left = resolved.bottom_left.clamp(0.0, max_width).min(max_height);
394 resolved
395 }
396
397 pub fn radii(&self) -> CornerRadii {
399 self.radii
400 }
401}
402
403#[derive(Clone, Copy, Debug, PartialEq)]
406pub struct TransformOrigin {
407 pub pivot_fraction_x: f32,
409 pub pivot_fraction_y: f32,
411}
412
413impl TransformOrigin {
414 pub const fn new(pivot_fraction_x: f32, pivot_fraction_y: f32) -> Self {
416 Self {
417 pivot_fraction_x,
418 pivot_fraction_y,
419 }
420 }
421
422 pub const CENTER: TransformOrigin = TransformOrigin::new(0.5, 0.5);
424}
425
426impl Default for TransformOrigin {
427 fn default() -> Self {
428 Self::CENTER
429 }
430}
431
432#[derive(Clone, Copy, Debug, Default, PartialEq)]
434pub enum LayerShape {
435 #[default]
437 Rectangle,
438 Rounded(RoundedCornerShape),
440}
441
442#[derive(Clone, Debug, PartialEq)]
445pub struct GraphicsLayer {
446 pub alpha: f32,
448 pub scale: f32,
450 pub scale_x: f32,
452 pub scale_y: f32,
454 pub rotation_x: f32,
456 pub rotation_y: f32,
458 pub rotation_z: f32,
460 pub camera_distance: f32,
463 pub transform_origin: TransformOrigin,
465 pub translation_x: f32,
467 pub translation_y: f32,
469 pub shadow_elevation: f32,
471 pub ambient_shadow_color: Color,
473 pub spot_shadow_color: Color,
475 pub shape: LayerShape,
477 pub clip: bool,
479 pub compositing_strategy: CompositingStrategy,
481 pub blend_mode: BlendMode,
483 pub color_filter: Option<ColorFilter>,
485 pub render_effect: Option<crate::render_effect::RenderEffect>,
488 pub backdrop_effect: Option<crate::render_effect::RenderEffect>,
492}
493
494impl GraphicsLayer {
495 pub const DEFAULT: Self = Self {
497 alpha: 1.0,
498 scale: 1.0,
499 scale_x: 1.0,
500 scale_y: 1.0,
501 rotation_x: 0.0,
502 rotation_y: 0.0,
503 rotation_z: 0.0,
504 camera_distance: 8.0,
505 transform_origin: TransformOrigin::CENTER,
506 translation_x: 0.0,
507 translation_y: 0.0,
508 shadow_elevation: 0.0,
509 ambient_shadow_color: Color::BLACK,
510 spot_shadow_color: Color::BLACK,
511 shape: LayerShape::Rectangle,
512 clip: false,
513 compositing_strategy: CompositingStrategy::Auto,
514 blend_mode: BlendMode::SrcOver,
515 color_filter: None,
516 render_effect: None,
517 backdrop_effect: None,
518 };
519
520 pub fn composite_alpha_8bit(alpha: f32) -> f32 {
544 (alpha.clamp(0.0, 1.0) * 255.0).floor() / 255.0
545 }
546}
547
548impl Default for GraphicsLayer {
549 fn default() -> Self {
550 Self::DEFAULT
551 }
552}
553
554#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
559pub enum BlendMode {
560 Clear,
562 Src,
564 Dst,
566 #[default]
568 SrcOver,
569 DstOver,
571 SrcIn,
573 DstIn,
575 SrcOut,
577 DstOut,
579 SrcAtop,
581 DstAtop,
583 Xor,
585 Plus,
587 Modulate,
589 Screen,
591 Overlay,
593 Darken,
595 Lighten,
597 ColorDodge,
599 ColorBurn,
601 HardLight,
603 SoftLight,
605 Difference,
607 Exclusion,
609 Multiply,
611 Hue,
613 Saturation,
615 Color,
617 Luminosity,
619}
620
621impl BlendMode {
622 pub const ALL: [BlendMode; 29] = [
624 BlendMode::Clear,
625 BlendMode::Src,
626 BlendMode::Dst,
627 BlendMode::SrcOver,
628 BlendMode::DstOver,
629 BlendMode::SrcIn,
630 BlendMode::DstIn,
631 BlendMode::SrcOut,
632 BlendMode::DstOut,
633 BlendMode::SrcAtop,
634 BlendMode::DstAtop,
635 BlendMode::Xor,
636 BlendMode::Plus,
637 BlendMode::Modulate,
638 BlendMode::Screen,
639 BlendMode::Overlay,
640 BlendMode::Darken,
641 BlendMode::Lighten,
642 BlendMode::ColorDodge,
643 BlendMode::ColorBurn,
644 BlendMode::HardLight,
645 BlendMode::SoftLight,
646 BlendMode::Difference,
647 BlendMode::Exclusion,
648 BlendMode::Multiply,
649 BlendMode::Hue,
650 BlendMode::Saturation,
651 BlendMode::Color,
652 BlendMode::Luminosity,
653 ];
654}
655
656#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
658pub enum CompositingStrategy {
659 #[default]
661 Auto,
662 Offscreen,
664 ModulateAlpha,
666}
667
668#[derive(Clone, Debug, PartialEq)]
670pub enum DrawPrimitive {
671 Content,
674 Blend {
676 primitive: Box<DrawPrimitive>,
678 blend_mode: BlendMode,
680 },
681 Rect {
683 rect: Rect,
685 brush: Brush,
687 stroke: Option<Stroke>,
690 },
691 RoundRect {
693 rect: Rect,
695 brush: Brush,
697 radii: CornerRadii,
699 stroke: Option<Stroke>,
702 },
703 Arc {
713 rect: Rect,
717 brush: Brush,
719 center: Point,
721 radius: f32,
724 start_angle: f32,
727 sweep_angle: f32,
729 stroke: Option<Stroke>,
731 inner_radius: f32,
733 },
734 Line {
737 rect: Rect,
740 brush: Brush,
742 start: Point,
744 end: Point,
746 stroke: Stroke,
748 },
749 Trapezoid {
752 rect: Rect,
755 brush: Brush,
757 trapezoid: Trapezoid,
759 },
760 Image {
762 rect: Rect,
764 image: ImageBitmap,
766 alpha: f32,
768 color_filter: Option<ColorFilter>,
770 sampling: ImageSampling,
772 src_rect: Option<Rect>,
776 },
777 Text(Box<TextPrimitive>),
779 Shadow(ShadowPrimitive),
782}
783
784#[derive(Clone, Debug, PartialEq)]
793pub struct TextPrimitive {
794 pub rect: Rect,
797 pub text: std::rc::Rc<str>,
800 pub style: DrawTextStyle,
802 pub color: Color,
806}
807
808fn shared_text_str(text: &str) -> Rc<str> {
818 use std::hash::{Hash, Hasher};
819
820 const POOL_CAPACITY: usize = 256;
821
822 let mut hasher = crate::FxHasher::default();
823 text.hash(&mut hasher);
824 let key = hasher.finish();
825
826 crate::thread_state::with(|state| {
827 let mut pool = state.shared_texts.borrow_mut();
828 if let Some(shared) = pool.get(&key)
829 && &**shared == text
830 {
831 return Rc::clone(shared);
832 }
833 let shared: Rc<str> = Rc::from(text);
834 if pool.len() >= POOL_CAPACITY {
835 pool.clear();
836 }
837 pool.insert(key, Rc::clone(&shared));
838 shared
839 })
840}
841
842impl DrawPrimitive {
843 pub fn translate(self, dx: f32, dy: f32) -> Self {
846 match self {
847 DrawPrimitive::Content => DrawPrimitive::Content,
848 DrawPrimitive::Blend {
849 primitive,
850 blend_mode,
851 } => DrawPrimitive::Blend {
852 primitive: Box::new(primitive.translate(dx, dy)),
853 blend_mode,
854 },
855 DrawPrimitive::Rect {
856 rect,
857 brush,
858 stroke,
859 } => DrawPrimitive::Rect {
860 rect: rect.translate(dx, dy),
861 brush,
862 stroke,
863 },
864 DrawPrimitive::RoundRect {
865 rect,
866 brush,
867 radii,
868 stroke,
869 } => DrawPrimitive::RoundRect {
870 rect: rect.translate(dx, dy),
871 brush,
872 radii,
873 stroke,
874 },
875 DrawPrimitive::Arc {
876 rect,
877 brush,
878 center,
879 radius,
880 start_angle,
881 sweep_angle,
882 stroke,
883 inner_radius,
884 } => DrawPrimitive::Arc {
885 rect: rect.translate(dx, dy),
886 brush,
887 center: Point::new(center.x + dx, center.y + dy),
888 radius,
889 start_angle,
890 sweep_angle,
891 stroke,
892 inner_radius,
893 },
894 DrawPrimitive::Line {
895 rect,
896 brush,
897 start,
898 end,
899 stroke,
900 } => DrawPrimitive::Line {
901 rect: rect.translate(dx, dy),
902 brush,
903 start: Point::new(start.x + dx, start.y + dy),
904 end: Point::new(end.x + dx, end.y + dy),
905 stroke,
906 },
907 DrawPrimitive::Trapezoid {
908 rect,
909 brush,
910 trapezoid,
911 } => DrawPrimitive::Trapezoid {
912 rect: rect.translate(dx, dy),
913 brush,
914 trapezoid: trapezoid.translate(dx, dy),
915 },
916 DrawPrimitive::Image {
917 rect,
918 image,
919 alpha,
920 color_filter,
921 sampling,
922 src_rect,
923 } => DrawPrimitive::Image {
924 rect: rect.translate(dx, dy),
925 image,
926 alpha,
927 color_filter,
928 sampling,
929 src_rect,
930 },
931 DrawPrimitive::Text(mut text) => {
932 text.rect = text.rect.translate(dx, dy);
933 DrawPrimitive::Text(text)
934 }
935 DrawPrimitive::Shadow(ShadowPrimitive::Drop {
936 shape,
937 cutout,
938 blur_radius,
939 blend_mode,
940 }) => DrawPrimitive::Shadow(ShadowPrimitive::Drop {
941 shape: Box::new(shape.translate(dx, dy)),
942 cutout: cutout.map(|cutout| Box::new(cutout.translate(dx, dy))),
943 blur_radius,
944 blend_mode,
945 }),
946 DrawPrimitive::Shadow(ShadowPrimitive::Inner {
947 fill,
948 cutout,
949 blur_radius,
950 blend_mode,
951 clip_rect,
952 }) => DrawPrimitive::Shadow(ShadowPrimitive::Inner {
953 fill: Box::new(fill.translate(dx, dy)),
954 cutout: Box::new(cutout.translate(dx, dy)),
955 blur_radius,
956 blend_mode,
957 clip_rect: clip_rect.translate(dx, dy),
958 }),
959 }
960 }
961}
962
963#[derive(Clone, Debug, PartialEq)]
966pub enum ShadowPrimitive {
967 Drop {
971 shape: Box<DrawPrimitive>,
973 cutout: Option<Box<DrawPrimitive>>,
975 blur_radius: f32,
977 blend_mode: BlendMode,
979 },
980 Inner {
982 fill: Box<DrawPrimitive>,
984 cutout: Box<DrawPrimitive>,
986 blur_radius: f32,
988 blend_mode: BlendMode,
990 clip_rect: Rect,
992 },
993}
994
995pub trait DrawScope {
999 fn size(&self) -> Size;
1001 fn draw_content(&mut self);
1005 fn draw_rect(&mut self, brush: Brush);
1007 fn draw_rect_blend(&mut self, brush: Brush, blend_mode: BlendMode);
1009 fn draw_rect_at(&mut self, rect: Rect, brush: Brush);
1011 fn draw_rect_at_blend(&mut self, rect: Rect, brush: Brush, blend_mode: BlendMode);
1013 fn draw_round_rect(&mut self, brush: Brush, radii: CornerRadii);
1015 fn draw_round_rect_blend(&mut self, brush: Brush, radii: CornerRadii, blend_mode: BlendMode);
1017 fn draw_round_rect_at(&mut self, rect: Rect, brush: Brush, radii: CornerRadii);
1019 fn draw_circle(&mut self, brush: Brush, center: Point, radius: f32);
1021 fn draw_circle_blend(
1023 &mut self,
1024 brush: Brush,
1025 center: Point,
1026 radius: f32,
1027 blend_mode: BlendMode,
1028 );
1029
1030 fn draw_rect_stroked(&mut self, brush: Brush, stroke: Stroke);
1032 fn draw_rect_stroked_blend(&mut self, brush: Brush, stroke: Stroke, blend_mode: BlendMode);
1034 fn draw_rect_at_stroked(&mut self, rect: Rect, brush: Brush, stroke: Stroke);
1036 fn draw_rect_at_stroked_blend(
1038 &mut self,
1039 rect: Rect,
1040 brush: Brush,
1041 stroke: Stroke,
1042 blend_mode: BlendMode,
1043 );
1044 fn draw_round_rect_stroked(&mut self, brush: Brush, radii: CornerRadii, stroke: Stroke);
1046 fn draw_round_rect_stroked_blend(
1048 &mut self,
1049 brush: Brush,
1050 radii: CornerRadii,
1051 stroke: Stroke,
1052 blend_mode: BlendMode,
1053 );
1054 fn draw_round_rect_at_stroked(
1056 &mut self,
1057 rect: Rect,
1058 brush: Brush,
1059 radii: CornerRadii,
1060 stroke: Stroke,
1061 );
1062 fn draw_round_rect_at_stroked_blend(
1064 &mut self,
1065 rect: Rect,
1066 brush: Brush,
1067 radii: CornerRadii,
1068 stroke: Stroke,
1069 blend_mode: BlendMode,
1070 );
1071 fn draw_circle_stroked(&mut self, brush: Brush, center: Point, radius: f32, stroke: Stroke);
1074 fn draw_circle_stroked_blend(
1076 &mut self,
1077 brush: Brush,
1078 center: Point,
1079 radius: f32,
1080 stroke: Stroke,
1081 blend_mode: BlendMode,
1082 );
1083
1084 fn draw_arc(
1097 &mut self,
1098 brush: Brush,
1099 center: Point,
1100 radius: f32,
1101 start_angle: f32,
1102 sweep_angle: f32,
1103 stroke: Stroke,
1104 );
1105 #[expect(clippy::too_many_arguments)]
1107 fn draw_arc_blend(
1108 &mut self,
1109 brush: Brush,
1110 center: Point,
1111 radius: f32,
1112 start_angle: f32,
1113 sweep_angle: f32,
1114 stroke: Stroke,
1115 blend_mode: BlendMode,
1116 );
1117
1118 fn draw_line(&mut self, brush: Brush, start: Point, end: Point, stroke: Stroke);
1124 fn draw_line_blend(
1126 &mut self,
1127 brush: Brush,
1128 start: Point,
1129 end: Point,
1130 stroke: Stroke,
1131 blend_mode: BlendMode,
1132 );
1133
1134 fn draw_path(&mut self, path: &Path, brush: Brush, style: DrawStyle);
1143 fn draw_path_blend(
1145 &mut self,
1146 path: &Path,
1147 brush: Brush,
1148 style: DrawStyle,
1149 blend_mode: BlendMode,
1150 );
1151
1152 fn draw_annular_sector(
1161 &mut self,
1162 brush: Brush,
1163 center: Point,
1164 inner_radius: f32,
1165 outer_radius: f32,
1166 start_angle: f32,
1167 sweep_angle: f32,
1168 );
1169 #[expect(clippy::too_many_arguments)]
1171 fn draw_annular_sector_blend(
1172 &mut self,
1173 brush: Brush,
1174 center: Point,
1175 inner_radius: f32,
1176 outer_radius: f32,
1177 start_angle: f32,
1178 sweep_angle: f32,
1179 blend_mode: BlendMode,
1180 );
1181
1182 fn draw_image(&mut self, image: ImageBitmap);
1184 fn draw_image_blend(&mut self, image: ImageBitmap, blend_mode: BlendMode);
1186 fn draw_image_at(
1189 &mut self,
1190 rect: Rect,
1191 image: ImageBitmap,
1192 alpha: f32,
1193 color_filter: Option<ColorFilter>,
1194 );
1195 fn draw_image_at_sampled(
1197 &mut self,
1198 rect: Rect,
1199 image: ImageBitmap,
1200 alpha: f32,
1201 color_filter: Option<ColorFilter>,
1202 sampling: ImageSampling,
1203 );
1204 fn draw_image_at_blend(
1206 &mut self,
1207 rect: Rect,
1208 image: ImageBitmap,
1209 alpha: f32,
1210 color_filter: Option<ColorFilter>,
1211 blend_mode: BlendMode,
1212 );
1213 fn draw_image_src(
1216 &mut self,
1217 image: ImageBitmap,
1218 src_rect: Rect,
1219 dst_rect: Rect,
1220 alpha: f32,
1221 color_filter: Option<ColorFilter>,
1222 );
1223 fn draw_image_src_sampled(
1225 &mut self,
1226 image: ImageBitmap,
1227 src_rect: Rect,
1228 dst_rect: Rect,
1229 alpha: f32,
1230 color_filter: Option<ColorFilter>,
1231 sampling: ImageSampling,
1232 );
1233 fn draw_image_src_blend(
1235 &mut self,
1236 image: ImageBitmap,
1237 src_rect: Rect,
1238 dst_rect: Rect,
1239 alpha: f32,
1240 color_filter: Option<ColorFilter>,
1241 blend_mode: BlendMode,
1242 );
1243 fn draw_vector_path(&mut self, path: &crate::VectorPath, brush: Brush);
1252 fn draw_svg_path(&mut self, d: &str, brush: Brush) {
1258 if let Ok(path) = crate::VectorPath::parse(d) {
1259 self.draw_vector_path(&path, brush);
1260 }
1261 }
1262
1263 fn measure_text(&self, text: &str, style: &DrawTextStyle) -> TextMeasurement;
1270
1271 fn draw_text(&mut self, brush: Brush, text: &str, style: &DrawTextStyle) {
1274 self.draw_text_at(Rect::from_size(self.size()), brush, text, style);
1275 }
1276
1277 fn draw_text_at(&mut self, rect: Rect, brush: Brush, text: &str, style: &DrawTextStyle);
1284
1285 fn draw_text_from(&mut self, top_left: Point, brush: Brush, text: &str, style: &DrawTextStyle) {
1291 if text.is_empty() {
1292 return;
1293 }
1294 let measurement = self.measure_text(text, style);
1295 self.draw_text_at(
1296 Rect::from_origin_size(top_left, measurement.size),
1297 brush,
1298 text,
1299 &DrawTextStyle {
1300 align: TextAlign::Left,
1301 vertical_align: TextVerticalAlign::Top,
1302 ..style.clone()
1303 },
1304 );
1305 }
1306
1307 fn into_primitives(self) -> Vec<DrawPrimitive>;
1310}
1311
1312pub fn align_text_block(rect: Rect, measurement: TextMeasurement, style: &DrawTextStyle) -> Point {
1318 let x = match style.align {
1319 TextAlign::Left => rect.x,
1320 TextAlign::Center => rect.x + (rect.width - measurement.size.width) * 0.5,
1321 TextAlign::Right => rect.x + rect.width - measurement.size.width,
1322 };
1323 let y = match style.vertical_align {
1324 TextVerticalAlign::Top => rect.y,
1325 TextVerticalAlign::Center => rect.y + (rect.height - measurement.size.height) * 0.5,
1326 TextVerticalAlign::Bottom => rect.y + rect.height - measurement.size.height,
1327 TextVerticalAlign::Baseline => rect.y - measurement.first_baseline,
1328 };
1329 Point::new(x, y)
1330}
1331
1332#[derive(Default)]
1335pub struct DrawScopeDefault {
1336 size: Size,
1337 recording: CommandRecorder,
1338 text_measurer: Option<Rc<dyn DrawTextMeasurer>>,
1339}
1340
1341impl DrawScopeDefault {
1342 pub fn new(size: Size) -> Self {
1344 Self::with_storage(size, None, CommandRecording::default())
1345 }
1346
1347 pub fn with_text_measurer(size: Size, text_measurer: Rc<dyn DrawTextMeasurer>) -> Self {
1352 Self::with_storage(size, Some(text_measurer), CommandRecording::default())
1353 }
1354
1355 pub fn with_text_measurer_reusing(
1358 size: Size,
1359 text_measurer: Rc<dyn DrawTextMeasurer>,
1360 storage: CommandRecording,
1361 ) -> Self {
1362 Self::with_storage(size, Some(text_measurer), storage)
1363 }
1364
1365 fn with_storage(
1366 size: Size,
1367 text_measurer: Option<Rc<dyn DrawTextMeasurer>>,
1368 recording: CommandRecording,
1369 ) -> Self {
1370 let recording = CommandRecorder::reusing(recording);
1371 Self {
1372 size,
1373 recording,
1374 text_measurer,
1375 }
1376 }
1377
1378 pub fn content_marker_count(&self) -> u32 {
1380 self.recording.content_markers()
1381 }
1382
1383 pub fn inset(&mut self, insets: EdgeInsets, draw: impl FnOnce(&mut DrawScopeDefault)) {
1387 let inner = insets.inset_rect(self.size);
1388 let mut scope = Self::with_storage(
1389 Size {
1390 width: inner.width,
1391 height: inner.height,
1392 },
1393 self.text_measurer.clone(),
1394 CommandRecording::default(),
1395 );
1396 draw(&mut scope);
1397 let recording = scope.finish();
1398 self.push_recorded(
1399 recording
1400 .primitives_with_markers()
1401 .map(|primitive| primitive.translate(inner.x, inner.y)),
1402 );
1403 }
1404
1405 pub fn push_recorded(&mut self, primitives: impl IntoIterator<Item = DrawPrimitive>) {
1407 for primitive in primitives {
1408 self.recording.push_primitive(primitive);
1409 }
1410 }
1411
1412 pub fn finish(self) -> CommandRecording {
1415 self.recording.finish()
1416 }
1417
1418 fn push_blended_primitive(&mut self, primitive: DrawPrimitive, blend_mode: BlendMode) {
1419 if blend_mode != BlendMode::SrcOver {
1420 self.recording.push_other(DrawPrimitive::Blend {
1421 primitive: Box::new(primitive),
1422 blend_mode,
1423 });
1424 return;
1425 }
1426 self.recording.push_other(primitive);
1427 }
1428
1429 #[expect(clippy::too_many_arguments)]
1430 #[inline]
1431 fn push_arc(
1432 &mut self,
1433 brush: Brush,
1434 center: Point,
1435 radius: f32,
1436 start_angle: f32,
1437 sweep_angle: f32,
1438 stroke: Option<Stroke>,
1439 inner_radius: f32,
1440 blend_mode: BlendMode,
1441 ) {
1442 let args = ArcRecordArgs {
1443 brush: &brush,
1444 center,
1445 radius,
1446 start_angle,
1447 sweep_angle,
1448 stroke,
1449 inner_radius,
1450 blend_mode,
1451 };
1452 let geometry = normalized_band(&args);
1453 if geometry.is_degenerate() {
1454 return;
1455 }
1456 self.recording.push_scope_arc(&args, &geometry);
1457 }
1458
1459 fn push_path_fill(&mut self, path: &Path, brush: &Brush, blend_mode: BlendMode) {
1463 let brush_rect = Rect::from_size(self.size);
1464 let recording = &mut self.recording;
1465 let sliced = crate::thread_state::with(|state| {
1466 let mut slicer = state.path_slicer.borrow_mut();
1467 let contours = path
1468 .contours()
1469 .iter()
1470 .map(|contour| contour.points.as_slice());
1471 slicer
1472 .slice(contours, PathFillRule::NonZero)
1473 .map(|slices| recording.push_trapezoids(brush_rect, slices, brush, blend_mode))
1474 .is_some()
1475 });
1476 if !sliced {
1477 self.push_vector_path(
1478 &path.to_vector_path(PathFillRule::NonZero),
1479 brush,
1480 blend_mode,
1481 );
1482 }
1483 }
1484
1485 fn push_vector_path(&mut self, path: &crate::VectorPath, brush: &Brush, blend_mode: BlendMode) {
1488 const SUPERSAMPLE: f32 = 2.0;
1489 const MAX_MASK_PIXELS: f32 = 4096.0;
1490
1491 if path.is_empty() {
1492 return;
1493 }
1494 let bounds = path.bounds();
1495 if bounds.width <= 0.0 || bounds.height <= 0.0 {
1496 return;
1497 }
1498
1499 let color = match brush {
1500 Brush::Solid(color) => *color,
1501 Brush::LinearGradient { colors, .. }
1502 | Brush::RadialGradient { colors, .. }
1503 | Brush::SweepGradient { colors, .. } => match colors.first() {
1504 Some(color) => *color,
1505 None => return,
1506 },
1507 };
1508 if color.3 <= 0.0 {
1509 return;
1510 }
1511
1512 let origin = Point::new(bounds.x.floor() - 1.0, bounds.y.floor() - 1.0);
1513 let rect_width = (bounds.x + bounds.width).ceil() - origin.x + 1.0;
1514 let rect_height = (bounds.y + bounds.height).ceil() - origin.y + 1.0;
1515 let mask_width = (rect_width * SUPERSAMPLE)
1516 .ceil()
1517 .clamp(1.0, MAX_MASK_PIXELS) as usize;
1518 let mask_height = (rect_height * SUPERSAMPLE)
1519 .ceil()
1520 .clamp(1.0, MAX_MASK_PIXELS) as usize;
1521
1522 let red = (color.0.clamp(0.0, 1.0) * 255.0 + 0.5) as u8;
1523 let green = (color.1.clamp(0.0, 1.0) * 255.0 + 0.5) as u8;
1524 let blue = (color.2.clamp(0.0, 1.0) * 255.0 + 0.5) as u8;
1525 let alpha = color.3.clamp(0.0, 1.0);
1526 let key = vector_path_mask_key(
1527 path,
1528 origin,
1529 (mask_width, mask_height),
1530 [red, green, blue],
1531 alpha,
1532 );
1533 let cached = vector_path_mask_cache_get(key);
1534 let image = match cached {
1535 Some(image) => image,
1536 None => {
1537 let mask = path.coverage_mask(mask_width, mask_height, origin, SUPERSAMPLE, alpha);
1538 let Ok(image) = ImageBitmap::from_alpha8(
1539 mask_width as u32,
1540 mask_height as u32,
1541 [red, green, blue],
1542 mask,
1543 ) else {
1544 return;
1545 };
1546 vector_path_mask_cache_put(key, image.clone());
1547 image
1548 }
1549 };
1550
1551 let image = DrawPrimitive::Image {
1552 rect: Rect {
1553 x: origin.x,
1554 y: origin.y,
1555 width: rect_width,
1556 height: rect_height,
1557 },
1558 image,
1559 alpha: 1.0,
1560 color_filter: None,
1561 sampling: ImageSampling::Linear,
1562 src_rect: None,
1563 };
1564 self.recording
1565 .push_other(if blend_mode == BlendMode::SrcOver {
1566 image
1567 } else {
1568 DrawPrimitive::Blend {
1569 primitive: Box::new(image),
1570 blend_mode,
1571 }
1572 });
1573 }
1574}
1575
1576impl DrawScope for DrawScopeDefault {
1577 fn size(&self) -> Size {
1578 self.size
1579 }
1580
1581 fn draw_content(&mut self) {
1582 self.recording.push_content();
1583 }
1584
1585 fn draw_rect(&mut self, brush: Brush) {
1586 self.draw_rect_blend(brush, BlendMode::SrcOver);
1587 }
1588
1589 fn draw_rect_blend(&mut self, brush: Brush, blend_mode: BlendMode) {
1590 self.recording
1591 .push_rect(Rect::from_size(self.size), &brush, None, blend_mode);
1592 }
1593
1594 fn draw_rect_at(&mut self, rect: Rect, brush: Brush) {
1595 self.draw_rect_at_blend(rect, brush, BlendMode::SrcOver);
1596 }
1597
1598 fn draw_rect_at_blend(&mut self, rect: Rect, brush: Brush, blend_mode: BlendMode) {
1599 self.recording.push_rect(rect, &brush, None, blend_mode);
1600 }
1601
1602 fn draw_round_rect(&mut self, brush: Brush, radii: CornerRadii) {
1603 self.draw_round_rect_blend(brush, radii, BlendMode::SrcOver);
1604 }
1605
1606 fn draw_round_rect_blend(&mut self, brush: Brush, radii: CornerRadii, blend_mode: BlendMode) {
1607 self.recording
1608 .push_round_rect(Rect::from_size(self.size), &brush, radii, None, blend_mode);
1609 }
1610
1611 fn draw_round_rect_at(&mut self, rect: Rect, brush: Brush, radii: CornerRadii) {
1612 self.recording
1613 .push_round_rect(rect, &brush, radii, None, BlendMode::SrcOver);
1614 }
1615
1616 fn draw_rect_stroked(&mut self, brush: Brush, stroke: Stroke) {
1617 self.draw_rect_stroked_blend(brush, stroke, BlendMode::SrcOver);
1618 }
1619
1620 fn draw_rect_stroked_blend(&mut self, brush: Brush, stroke: Stroke, blend_mode: BlendMode) {
1621 self.draw_rect_at_stroked_blend(Rect::from_size(self.size), brush, stroke, blend_mode);
1622 }
1623
1624 fn draw_rect_at_stroked(&mut self, rect: Rect, brush: Brush, stroke: Stroke) {
1625 self.draw_rect_at_stroked_blend(rect, brush, stroke, BlendMode::SrcOver);
1626 }
1627
1628 fn draw_rect_at_stroked_blend(
1629 &mut self,
1630 rect: Rect,
1631 brush: Brush,
1632 stroke: Stroke,
1633 blend_mode: BlendMode,
1634 ) {
1635 if !stroke.is_visible() {
1636 return;
1637 }
1638 self.recording
1639 .push_rect(rect, &brush, Some(stroke), blend_mode);
1640 }
1641
1642 fn draw_round_rect_stroked(&mut self, brush: Brush, radii: CornerRadii, stroke: Stroke) {
1643 self.draw_round_rect_stroked_blend(brush, radii, stroke, BlendMode::SrcOver);
1644 }
1645
1646 fn draw_round_rect_stroked_blend(
1647 &mut self,
1648 brush: Brush,
1649 radii: CornerRadii,
1650 stroke: Stroke,
1651 blend_mode: BlendMode,
1652 ) {
1653 self.draw_round_rect_at_stroked_blend(
1654 Rect::from_size(self.size),
1655 brush,
1656 radii,
1657 stroke,
1658 blend_mode,
1659 );
1660 }
1661
1662 fn draw_round_rect_at_stroked(
1663 &mut self,
1664 rect: Rect,
1665 brush: Brush,
1666 radii: CornerRadii,
1667 stroke: Stroke,
1668 ) {
1669 self.draw_round_rect_at_stroked_blend(rect, brush, radii, stroke, BlendMode::SrcOver);
1670 }
1671
1672 fn draw_round_rect_at_stroked_blend(
1673 &mut self,
1674 rect: Rect,
1675 brush: Brush,
1676 radii: CornerRadii,
1677 stroke: Stroke,
1678 blend_mode: BlendMode,
1679 ) {
1680 if !stroke.is_visible() {
1681 return;
1682 }
1683 self.recording
1684 .push_round_rect(rect, &brush, radii, Some(stroke), blend_mode);
1685 }
1686
1687 fn draw_circle_stroked(&mut self, brush: Brush, center: Point, radius: f32, stroke: Stroke) {
1688 self.draw_circle_stroked_blend(brush, center, radius, stroke, BlendMode::SrcOver);
1689 }
1690
1691 fn draw_circle_stroked_blend(
1692 &mut self,
1693 brush: Brush,
1694 center: Point,
1695 radius: f32,
1696 stroke: Stroke,
1697 blend_mode: BlendMode,
1698 ) {
1699 if !stroke.is_visible() || !radius.is_finite() {
1700 return;
1701 }
1702 let radius = radius.max(0.0);
1703 let diameter = radius * 2.0;
1704 self.draw_round_rect_at_stroked_blend(
1705 Rect {
1706 x: center.x - radius,
1707 y: center.y - radius,
1708 width: diameter,
1709 height: diameter,
1710 },
1711 brush,
1712 CornerRadii::uniform(radius),
1713 stroke,
1714 blend_mode,
1715 );
1716 }
1717
1718 fn draw_arc(
1719 &mut self,
1720 brush: Brush,
1721 center: Point,
1722 radius: f32,
1723 start_angle: f32,
1724 sweep_angle: f32,
1725 stroke: Stroke,
1726 ) {
1727 self.draw_arc_blend(
1728 brush,
1729 center,
1730 radius,
1731 start_angle,
1732 sweep_angle,
1733 stroke,
1734 BlendMode::SrcOver,
1735 );
1736 }
1737
1738 fn draw_arc_blend(
1739 &mut self,
1740 brush: Brush,
1741 center: Point,
1742 radius: f32,
1743 start_angle: f32,
1744 sweep_angle: f32,
1745 stroke: Stroke,
1746 blend_mode: BlendMode,
1747 ) {
1748 if !stroke.is_visible() {
1749 return;
1750 }
1751 self.push_arc(
1752 brush,
1753 center,
1754 radius,
1755 start_angle,
1756 sweep_angle,
1757 Some(stroke),
1758 0.0,
1759 blend_mode,
1760 );
1761 }
1762
1763 fn draw_line(&mut self, brush: Brush, start: Point, end: Point, stroke: Stroke) {
1764 self.draw_line_blend(brush, start, end, stroke, BlendMode::SrcOver);
1765 }
1766
1767 fn draw_line_blend(
1768 &mut self,
1769 brush: Brush,
1770 start: Point,
1771 end: Point,
1772 stroke: Stroke,
1773 blend_mode: BlendMode,
1774 ) {
1775 let line = LineGeometry::new(start, end, stroke);
1776 if line.is_degenerate() {
1777 return;
1778 }
1779 self.recording.push_line(&line, &brush, stroke, blend_mode);
1780 }
1781
1782 fn draw_path(&mut self, path: &Path, brush: Brush, style: DrawStyle) {
1783 self.draw_path_blend(path, brush, style, BlendMode::SrcOver);
1784 }
1785
1786 fn draw_path_blend(
1787 &mut self,
1788 path: &Path,
1789 brush: Brush,
1790 style: DrawStyle,
1791 blend_mode: BlendMode,
1792 ) {
1793 let (stroke, dash) = match style {
1794 DrawStyle::Fill => {
1795 self.push_path_fill(path, &brush, blend_mode);
1796 return;
1797 }
1798 DrawStyle::Stroke(stroke) => (stroke, None),
1799 DrawStyle::DashedStroke(stroke, dash) => (stroke, Some(dash)),
1800 };
1801 if !stroke.is_visible() {
1802 return;
1803 }
1804 let recording = &mut self.recording;
1805 let mut push = |line: LineGeometry| {
1806 if !line.is_degenerate() {
1807 let stroke = Stroke {
1808 cap: line.cap,
1809 ..stroke
1810 };
1811 recording.push_line(&line, &brush, stroke, blend_mode);
1812 }
1813 };
1814 for contour in path.contours() {
1815 let Some(dash) = dash else {
1816 crate::for_each_stroke_line(&contour.points, contour.closed, stroke, &mut push);
1817 continue;
1818 };
1819 let closing = contour
1820 .closed
1821 .then(|| contour.points.first().copied())
1822 .flatten();
1823 let points: std::borrow::Cow<'_, [Point]> = match closing {
1824 Some(first) => {
1825 std::borrow::Cow::Owned(contour.points.iter().copied().chain([first]).collect())
1826 }
1827 None => std::borrow::Cow::Borrowed(&contour.points),
1828 };
1829 dash.for_each_dash(&points, |piece| {
1830 crate::for_each_stroke_line(piece, false, stroke, &mut push);
1831 });
1832 }
1833 }
1834
1835 fn draw_annular_sector(
1836 &mut self,
1837 brush: Brush,
1838 center: Point,
1839 inner_radius: f32,
1840 outer_radius: f32,
1841 start_angle: f32,
1842 sweep_angle: f32,
1843 ) {
1844 self.draw_annular_sector_blend(
1845 brush,
1846 center,
1847 inner_radius,
1848 outer_radius,
1849 start_angle,
1850 sweep_angle,
1851 BlendMode::SrcOver,
1852 );
1853 }
1854
1855 fn draw_annular_sector_blend(
1856 &mut self,
1857 brush: Brush,
1858 center: Point,
1859 inner_radius: f32,
1860 outer_radius: f32,
1861 start_angle: f32,
1862 sweep_angle: f32,
1863 blend_mode: BlendMode,
1864 ) {
1865 self.push_arc(
1866 brush,
1867 center,
1868 outer_radius,
1869 start_angle,
1870 sweep_angle,
1871 None,
1872 inner_radius,
1873 blend_mode,
1874 );
1875 }
1876
1877 fn draw_circle(&mut self, brush: Brush, center: Point, radius: f32) {
1878 self.draw_circle_blend(brush, center, radius, BlendMode::SrcOver);
1879 }
1880
1881 fn draw_circle_blend(
1882 &mut self,
1883 brush: Brush,
1884 center: Point,
1885 radius: f32,
1886 blend_mode: BlendMode,
1887 ) {
1888 let radius = radius.max(0.0);
1889 let diameter = radius * 2.0;
1890 self.recording.push_round_rect(
1891 Rect {
1892 x: center.x - radius,
1893 y: center.y - radius,
1894 width: diameter,
1895 height: diameter,
1896 },
1897 &brush,
1898 CornerRadii::uniform(radius),
1899 None,
1900 blend_mode,
1901 );
1902 }
1903
1904 fn draw_image(&mut self, image: ImageBitmap) {
1905 self.draw_image_blend(image, BlendMode::SrcOver);
1906 }
1907
1908 fn draw_image_blend(&mut self, image: ImageBitmap, blend_mode: BlendMode) {
1909 self.push_blended_primitive(
1910 DrawPrimitive::Image {
1911 rect: Rect::from_size(self.size),
1912 image,
1913 alpha: 1.0,
1914 color_filter: None,
1915 sampling: ImageSampling::Nearest,
1916 src_rect: None,
1917 },
1918 blend_mode,
1919 );
1920 }
1921
1922 fn draw_image_at(
1923 &mut self,
1924 rect: Rect,
1925 image: ImageBitmap,
1926 alpha: f32,
1927 color_filter: Option<ColorFilter>,
1928 ) {
1929 self.draw_image_at_sampled(rect, image, alpha, color_filter, ImageSampling::Nearest);
1930 }
1931
1932 fn draw_image_at_sampled(
1933 &mut self,
1934 rect: Rect,
1935 image: ImageBitmap,
1936 alpha: f32,
1937 color_filter: Option<ColorFilter>,
1938 sampling: ImageSampling,
1939 ) {
1940 self.push_blended_primitive(
1941 DrawPrimitive::Image {
1942 rect,
1943 image,
1944 alpha: alpha.clamp(0.0, 1.0),
1945 color_filter,
1946 sampling,
1947 src_rect: None,
1948 },
1949 BlendMode::SrcOver,
1950 );
1951 }
1952
1953 fn draw_image_at_blend(
1954 &mut self,
1955 rect: Rect,
1956 image: ImageBitmap,
1957 alpha: f32,
1958 color_filter: Option<ColorFilter>,
1959 blend_mode: BlendMode,
1960 ) {
1961 self.push_blended_primitive(
1962 DrawPrimitive::Image {
1963 rect,
1964 image,
1965 alpha: alpha.clamp(0.0, 1.0),
1966 color_filter,
1967 sampling: ImageSampling::Nearest,
1968 src_rect: None,
1969 },
1970 blend_mode,
1971 );
1972 }
1973
1974 fn draw_image_src(
1975 &mut self,
1976 image: ImageBitmap,
1977 src_rect: Rect,
1978 dst_rect: Rect,
1979 alpha: f32,
1980 color_filter: Option<ColorFilter>,
1981 ) {
1982 self.draw_image_src_blend(
1983 image,
1984 src_rect,
1985 dst_rect,
1986 alpha,
1987 color_filter,
1988 BlendMode::SrcOver,
1989 );
1990 }
1991
1992 fn draw_image_src_sampled(
1993 &mut self,
1994 image: ImageBitmap,
1995 src_rect: Rect,
1996 dst_rect: Rect,
1997 alpha: f32,
1998 color_filter: Option<ColorFilter>,
1999 sampling: ImageSampling,
2000 ) {
2001 self.push_blended_primitive(
2002 DrawPrimitive::Image {
2003 rect: dst_rect,
2004 image,
2005 alpha: alpha.clamp(0.0, 1.0),
2006 color_filter,
2007 sampling,
2008 src_rect: Some(src_rect),
2009 },
2010 BlendMode::SrcOver,
2011 );
2012 }
2013
2014 fn draw_image_src_blend(
2015 &mut self,
2016 image: ImageBitmap,
2017 src_rect: Rect,
2018 dst_rect: Rect,
2019 alpha: f32,
2020 color_filter: Option<ColorFilter>,
2021 blend_mode: BlendMode,
2022 ) {
2023 self.push_blended_primitive(
2024 DrawPrimitive::Image {
2025 rect: dst_rect,
2026 image,
2027 alpha: alpha.clamp(0.0, 1.0),
2028 color_filter,
2029 sampling: ImageSampling::Nearest,
2030 src_rect: Some(src_rect),
2031 },
2032 blend_mode,
2033 );
2034 }
2035
2036 fn draw_vector_path(&mut self, path: &crate::VectorPath, brush: Brush) {
2037 self.push_vector_path(path, &brush, BlendMode::SrcOver);
2038 }
2039
2040 fn measure_text(&self, text: &str, style: &DrawTextStyle) -> TextMeasurement {
2041 match &self.text_measurer {
2042 Some(measurer) => measurer.measure_text(text, style),
2043 None => estimate_text_measurement(text, style),
2044 }
2045 }
2046
2047 fn draw_text_at(&mut self, rect: Rect, brush: Brush, text: &str, style: &DrawTextStyle) {
2048 if text.is_empty() {
2049 return;
2050 }
2051 let Some(color) = solid_fill_color(&brush) else {
2052 return;
2053 };
2054 if color.3 <= 0.0 {
2055 return;
2056 }
2057 let measurement = self.measure_text(text, style);
2058 if !(measurement.size.width > 0.0 && measurement.size.height > 0.0) {
2059 return;
2060 }
2061 let origin = align_text_block(rect, measurement, style);
2062 if !origin.x.is_finite() || !origin.y.is_finite() {
2063 return;
2064 }
2065 self.recording
2066 .push_other(DrawPrimitive::Text(Box::new(TextPrimitive {
2067 rect: Rect::from_origin_size(origin, measurement.size),
2068 text: shared_text_str(text),
2069 style: style.clone(),
2070 color,
2071 })));
2072 }
2073
2074 fn into_primitives(self) -> Vec<DrawPrimitive> {
2075 self.finish().into_primitives_with_markers()
2076 }
2077}
2078
2079fn solid_fill_color(brush: &Brush) -> Option<Color> {
2084 match brush {
2085 Brush::Solid(color) => Some(*color),
2086 Brush::LinearGradient { colors, .. }
2087 | Brush::RadialGradient { colors, .. }
2088 | Brush::SweepGradient { colors, .. } => colors.first().copied(),
2089 }
2090}
2091
2092#[cfg(test)]
2093#[path = "tests/geometry_tests.rs"]
2094mod tests;