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 stroke::{LineGeometry, Stroke},
11 typography::{
12 DrawTextMeasurer, DrawTextStyle, TextAlign, TextMeasurement, TextVerticalAlign,
13 estimate_text_measurement,
14 },
15};
16
17const VECTOR_PATH_MASK_CACHE_ENTRIES: usize = 96;
18const VECTOR_PATH_MASK_CACHE_BYTES: usize = 8 * 1024 * 1024;
19
20pub(crate) struct VectorPathMaskCache {
21 entries: Vec<(u64, ImageBitmap)>,
22 bytes: usize,
23}
24
25impl VectorPathMaskCache {
26 pub(crate) const fn new() -> Self {
27 Self {
28 entries: Vec::new(),
29 bytes: 0,
30 }
31 }
32
33 fn get(&mut self, key: u64) -> Option<ImageBitmap> {
34 let index = self.entries.iter().position(|(seen, _)| *seen == key)?;
35 let entry = self.entries.remove(index);
36 let image = entry.1.clone();
37 self.entries.push(entry);
38 Some(image)
39 }
40
41 fn put(&mut self, key: u64, image: ImageBitmap) {
42 let bytes = image.pixels().len();
43 if bytes > VECTOR_PATH_MASK_CACHE_BYTES {
44 return;
45 }
46 self.bytes += bytes;
47 self.entries.push((key, image));
48 while self.entries.len() > VECTOR_PATH_MASK_CACHE_ENTRIES
49 || self.bytes > VECTOR_PATH_MASK_CACHE_BYTES
50 {
51 let (_, dropped) = self.entries.remove(0);
52 self.bytes = self.bytes.saturating_sub(dropped.pixels().len());
53 }
54 }
55}
56
57#[derive(Clone, Copy, PartialEq, Eq)]
62enum PathSide {
63 Inside,
64 Outside,
65}
66
67fn vector_path_mask_key(
68 path: &crate::VectorPath,
69 origin: Point,
70 mask_size: (usize, usize),
71 brush: &Brush,
72 brush_rect: Rect,
73 side: PathSide,
74) -> u64 {
75 use std::hash::Hasher;
76 let mut hasher = crate::fx_hash::FxHasher::default();
77 hasher.write_u8(path.fill_rule() as u8);
78 hasher.write_u8(side as u8);
79 hasher.write_u32(origin.x.to_bits());
80 hasher.write_u32(origin.y.to_bits());
81 hasher.write_usize(mask_size.0);
82 hasher.write_usize(mask_size.1);
83 crate::render_hash::hash_brush(brush, &mut hasher);
84 if !matches!(brush, Brush::Solid(_)) {
85 for value in [
86 brush_rect.x,
87 brush_rect.y,
88 brush_rect.width,
89 brush_rect.height,
90 ] {
91 hasher.write_u32(value.to_bits());
92 }
93 }
94 for subpath in path.subpaths() {
95 hasher.write_usize(subpath.len());
96 for point in subpath {
97 hasher.write_u32(point.x.to_bits());
98 hasher.write_u32(point.y.to_bits());
99 }
100 }
101 hasher.finish()
102}
103
104const VECTOR_PATH_SUPERSAMPLE: f32 = 2.0;
107
108fn rasterize_vector_path(
113 path: &crate::VectorPath,
114 brush: &Brush,
115 brush_rect: Rect,
116 origin: Point,
117 (width, height): (usize, usize),
118 side: PathSide,
119) -> Option<ImageBitmap> {
120 let byte = |value: f32| (value.clamp(0.0, 1.0) * 255.0 + 0.5) as u8;
121 let covered = |opacity: f32| {
122 let mut mask = path.coverage_mask(width, height, origin, VECTOR_PATH_SUPERSAMPLE, 1.0);
123 for value in &mut mask {
124 let share = match side {
125 PathSide::Inside => *value,
126 PathSide::Outside => u8::MAX - *value,
127 };
128 *value = (f32::from(share) * opacity + 0.5) as u8;
129 }
130 mask
131 };
132 if let Brush::Solid(color) = brush {
133 let mask = match side {
134 PathSide::Inside => path.coverage_mask(
135 width,
136 height,
137 origin,
138 VECTOR_PATH_SUPERSAMPLE,
139 color.3.clamp(0.0, 1.0),
140 ),
141 PathSide::Outside => covered(color.3.clamp(0.0, 1.0)),
142 };
143 let rgb = [byte(color.0), byte(color.1), byte(color.2)];
144 return ImageBitmap::from_alpha8(width as u32, height as u32, rgb, mask).ok();
145 }
146 let coverage = covered(1.0);
147 let mut pixels = Vec::with_capacity(coverage.len() * 4);
148 for (row, line) in coverage.chunks_exact(width).enumerate() {
149 let y = origin.y + (row as f32 + 0.5) / VECTOR_PATH_SUPERSAMPLE;
150 for (column, &covered) in line.iter().enumerate() {
151 if covered == 0 {
152 pixels.extend_from_slice(&[0; 4]);
153 continue;
154 }
155 let x = origin.x + (column as f32 + 0.5) / VECTOR_PATH_SUPERSAMPLE;
156 let color = brush.color_at(brush_rect, Point::new(x, y));
157 let alpha = (color.3.clamp(0.0, 1.0) * f32::from(covered) + 0.5) as u8;
158 pixels.extend_from_slice(&[byte(color.0), byte(color.1), byte(color.2), alpha]);
159 }
160 }
161 ImageBitmap::from_rgba8(width as u32, height as u32, pixels).ok()
162}
163
164fn vector_path_mask_cache_get(key: u64) -> Option<ImageBitmap> {
165 crate::thread_state::with(|state| state.vector_path_masks.borrow_mut().get(key))
166}
167
168fn vector_path_mask_cache_put(key: u64, image: ImageBitmap) {
169 crate::thread_state::with(|state| state.vector_path_masks.borrow_mut().put(key, image));
170}
171
172#[derive(Clone, Copy, Debug, PartialEq, Default)]
174pub struct Point {
175 pub x: f32,
177 pub y: f32,
179}
180
181impl Point {
182 pub const fn new(x: f32, y: f32) -> Self {
184 Self { x, y }
185 }
186
187 pub const ZERO: Point = Point { x: 0.0, y: 0.0 };
189}
190
191#[derive(Clone, Copy, Debug, PartialEq, Default)]
193pub struct Size {
194 pub width: f32,
196 pub height: f32,
198}
199
200impl Size {
201 pub const fn new(width: f32, height: f32) -> Self {
203 Self { width, height }
204 }
205
206 pub const ZERO: Size = Size {
208 width: 0.0,
209 height: 0.0,
210 };
211}
212
213#[derive(Clone, Copy, Debug, PartialEq)]
215pub struct Rect {
216 pub x: f32,
218 pub y: f32,
220 pub width: f32,
222 pub height: f32,
224}
225
226impl Rect {
227 pub const EMPTY: Rect = Rect {
231 x: 0.0,
232 y: 0.0,
233 width: 0.0,
234 height: 0.0,
235 };
236
237 pub fn from_origin_size(origin: Point, size: Size) -> Self {
239 Self {
240 x: origin.x,
241 y: origin.y,
242 width: size.width,
243 height: size.height,
244 }
245 }
246
247 pub fn from_size(size: Size) -> Self {
249 Self {
250 x: 0.0,
251 y: 0.0,
252 width: size.width,
253 height: size.height,
254 }
255 }
256
257 pub fn translate(&self, dx: f32, dy: f32) -> Self {
259 Self {
260 x: self.x + dx,
261 y: self.y + dy,
262 width: self.width,
263 height: self.height,
264 }
265 }
266
267 pub fn contains(&self, x: f32, y: f32) -> bool {
269 x >= self.x && y >= self.y && x <= self.x + self.width && y <= self.y + self.height
270 }
271
272 pub fn is_empty(&self) -> bool {
274 self.width <= 0.0 || self.height <= 0.0
275 }
276
277 pub fn intersect(&self, other: Rect) -> Option<Rect> {
279 let left = self.x.max(other.x);
280 let top = self.y.max(other.y);
281 let right = (self.x + self.width).min(other.x + other.width);
282 let bottom = (self.y + self.height).min(other.y + other.height);
283 let width = right - left;
284 let height = bottom - top;
285 if width <= 0.0 || height <= 0.0 {
286 None
287 } else {
288 Some(Rect {
289 x: left,
290 y: top,
291 width,
292 height,
293 })
294 }
295 }
296
297 pub fn union(&self, other: Rect) -> Rect {
299 let left = self.x.min(other.x);
300 let top = self.y.min(other.y);
301 let right = (self.x + self.width).max(other.x + other.width);
302 let bottom = (self.y + self.height).max(other.y + other.height);
303 Rect {
304 x: left,
305 y: top,
306 width: (right - left).max(0.0),
307 height: (bottom - top).max(0.0),
308 }
309 }
310}
311
312#[derive(Clone, Copy, Debug, Default, PartialEq)]
314pub struct EdgeInsets {
315 pub left: f32,
317 pub top: f32,
319 pub right: f32,
321 pub bottom: f32,
323}
324
325impl EdgeInsets {
326 pub fn uniform(all: f32) -> Self {
328 Self {
329 left: all,
330 top: all,
331 right: all,
332 bottom: all,
333 }
334 }
335
336 pub fn horizontal(horizontal: f32) -> Self {
338 Self {
339 left: horizontal,
340 right: horizontal,
341 ..Self::default()
342 }
343 }
344
345 pub fn vertical(vertical: f32) -> Self {
347 Self {
348 top: vertical,
349 bottom: vertical,
350 ..Self::default()
351 }
352 }
353
354 pub fn symmetric(horizontal: f32, vertical: f32) -> Self {
356 Self {
357 left: horizontal,
358 right: horizontal,
359 top: vertical,
360 bottom: vertical,
361 }
362 }
363
364 pub fn from_components(left: f32, top: f32, right: f32, bottom: f32) -> Self {
366 Self {
367 left,
368 top,
369 right,
370 bottom,
371 }
372 }
373
374 pub fn is_zero(&self) -> bool {
376 self.left == 0.0 && self.top == 0.0 && self.right == 0.0 && self.bottom == 0.0
377 }
378
379 pub fn horizontal_sum(&self) -> f32 {
381 self.left + self.right
382 }
383
384 pub fn vertical_sum(&self) -> f32 {
386 self.top + self.bottom
387 }
388
389 pub fn inset_rect(&self, size: Size) -> Rect {
392 Rect {
393 x: self.left,
394 y: self.top,
395 width: (size.width - self.horizontal_sum()).max(0.0),
396 height: (size.height - self.vertical_sum()).max(0.0),
397 }
398 }
399}
400
401impl AddAssign for EdgeInsets {
402 fn add_assign(&mut self, rhs: Self) {
403 self.left += rhs.left;
404 self.top += rhs.top;
405 self.right += rhs.right;
406 self.bottom += rhs.bottom;
407 }
408}
409
410#[derive(Clone, Copy, Debug, Default, PartialEq)]
412pub struct CornerRadii {
413 pub top_left: f32,
415 pub top_right: f32,
417 pub bottom_right: f32,
419 pub bottom_left: f32,
421}
422
423impl CornerRadii {
424 pub fn uniform(radius: f32) -> Self {
426 Self {
427 top_left: radius,
428 top_right: radius,
429 bottom_right: radius,
430 bottom_left: radius,
431 }
432 }
433}
434
435#[derive(Clone, Copy, Debug, PartialEq)]
437pub struct RoundedCornerShape {
438 radii: CornerRadii,
439}
440
441impl RoundedCornerShape {
442 pub fn new(top_left: f32, top_right: f32, bottom_right: f32, bottom_left: f32) -> Self {
444 Self {
445 radii: CornerRadii {
446 top_left,
447 top_right,
448 bottom_right,
449 bottom_left,
450 },
451 }
452 }
453
454 pub fn uniform(radius: f32) -> Self {
456 Self {
457 radii: CornerRadii::uniform(radius),
458 }
459 }
460
461 pub fn with_radii(radii: CornerRadii) -> Self {
463 Self { radii }
464 }
465
466 pub fn resolve(&self, width: f32, height: f32) -> CornerRadii {
469 let mut resolved = self.radii;
470 let max_width = (width / 2.0).max(0.0);
471 let max_height = (height / 2.0).max(0.0);
472 resolved.top_left = resolved.top_left.clamp(0.0, max_width).min(max_height);
473 resolved.top_right = resolved.top_right.clamp(0.0, max_width).min(max_height);
474 resolved.bottom_right = resolved.bottom_right.clamp(0.0, max_width).min(max_height);
475 resolved.bottom_left = resolved.bottom_left.clamp(0.0, max_width).min(max_height);
476 resolved
477 }
478
479 pub fn radii(&self) -> CornerRadii {
481 self.radii
482 }
483}
484
485#[derive(Clone, Copy, Debug, PartialEq)]
488pub struct TransformOrigin {
489 pub pivot_fraction_x: f32,
491 pub pivot_fraction_y: f32,
493}
494
495impl TransformOrigin {
496 pub const fn new(pivot_fraction_x: f32, pivot_fraction_y: f32) -> Self {
498 Self {
499 pivot_fraction_x,
500 pivot_fraction_y,
501 }
502 }
503
504 pub const CENTER: TransformOrigin = TransformOrigin::new(0.5, 0.5);
506}
507
508impl Default for TransformOrigin {
509 fn default() -> Self {
510 Self::CENTER
511 }
512}
513
514#[derive(Clone, Copy, Debug, Default, PartialEq)]
516pub enum LayerShape {
517 #[default]
519 Rectangle,
520 Rounded(RoundedCornerShape),
522}
523
524#[derive(Clone, Debug, PartialEq)]
527pub struct GraphicsLayer {
528 pub alpha: f32,
530 pub scale: f32,
532 pub scale_x: f32,
534 pub scale_y: f32,
536 pub rotation_x: f32,
538 pub rotation_y: f32,
540 pub rotation_z: f32,
542 pub camera_distance: f32,
545 pub transform_origin: TransformOrigin,
547 pub translation_x: f32,
549 pub translation_y: f32,
551 pub space: Option<std::sync::Arc<crate::space::LayerSpace>>,
556 pub shadow_elevation: f32,
558 pub ambient_shadow_color: Color,
560 pub spot_shadow_color: Color,
562 pub shape: LayerShape,
564 pub clip: bool,
566 pub compositing_strategy: CompositingStrategy,
568 pub blend_mode: BlendMode,
570 pub color_filter: Option<ColorFilter>,
572 pub render_effect: Option<crate::render_effect::RenderEffect>,
575 pub backdrop_effect: Option<crate::render_effect::RenderEffect>,
579}
580
581impl GraphicsLayer {
582 pub const DEFAULT: Self = Self {
584 alpha: 1.0,
585 scale: 1.0,
586 scale_x: 1.0,
587 scale_y: 1.0,
588 rotation_x: 0.0,
589 rotation_y: 0.0,
590 rotation_z: 0.0,
591 camera_distance: 8.0,
592 transform_origin: TransformOrigin::CENTER,
593 translation_x: 0.0,
594 translation_y: 0.0,
595 space: None,
596 shadow_elevation: 0.0,
597 ambient_shadow_color: Color::BLACK,
598 spot_shadow_color: Color::BLACK,
599 shape: LayerShape::Rectangle,
600 clip: false,
601 compositing_strategy: CompositingStrategy::Auto,
602 blend_mode: BlendMode::SrcOver,
603 color_filter: None,
604 render_effect: None,
605 backdrop_effect: None,
606 };
607
608 pub fn composite_alpha_8bit(alpha: f32) -> f32 {
632 (alpha.clamp(0.0, 1.0) * 255.0).floor() / 255.0
633 }
634}
635
636impl Default for GraphicsLayer {
637 fn default() -> Self {
638 Self::DEFAULT
639 }
640}
641
642#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
647pub enum BlendMode {
648 Clear,
650 Src,
652 Dst,
654 #[default]
656 SrcOver,
657 DstOver,
659 SrcIn,
661 DstIn,
663 SrcOut,
665 DstOut,
667 SrcAtop,
669 DstAtop,
671 Xor,
673 Plus,
675 Modulate,
677 Screen,
679 Overlay,
681 Darken,
683 Lighten,
685 ColorDodge,
687 ColorBurn,
689 HardLight,
691 SoftLight,
693 Difference,
695 Exclusion,
697 Multiply,
699 Hue,
701 Saturation,
703 Color,
705 Luminosity,
707}
708
709impl BlendMode {
710 pub const ALL: [BlendMode; 29] = [
712 BlendMode::Clear,
713 BlendMode::Src,
714 BlendMode::Dst,
715 BlendMode::SrcOver,
716 BlendMode::DstOver,
717 BlendMode::SrcIn,
718 BlendMode::DstIn,
719 BlendMode::SrcOut,
720 BlendMode::DstOut,
721 BlendMode::SrcAtop,
722 BlendMode::DstAtop,
723 BlendMode::Xor,
724 BlendMode::Plus,
725 BlendMode::Modulate,
726 BlendMode::Screen,
727 BlendMode::Overlay,
728 BlendMode::Darken,
729 BlendMode::Lighten,
730 BlendMode::ColorDodge,
731 BlendMode::ColorBurn,
732 BlendMode::HardLight,
733 BlendMode::SoftLight,
734 BlendMode::Difference,
735 BlendMode::Exclusion,
736 BlendMode::Multiply,
737 BlendMode::Hue,
738 BlendMode::Saturation,
739 BlendMode::Color,
740 BlendMode::Luminosity,
741 ];
742}
743
744#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
746pub enum CompositingStrategy {
747 #[default]
749 Auto,
750 Offscreen,
752 ModulateAlpha,
754}
755
756#[derive(Clone, Debug, PartialEq)]
758pub enum DrawPrimitive {
759 Content,
762 Blend {
764 primitive: Box<DrawPrimitive>,
766 blend_mode: BlendMode,
768 },
769 Rect {
771 rect: Rect,
773 brush: Brush,
775 stroke: Option<Stroke>,
778 },
779 RoundRect {
781 rect: Rect,
783 brush: Brush,
785 radii: CornerRadii,
787 stroke: Option<Stroke>,
790 },
791 Arc {
801 rect: Rect,
805 brush: Brush,
807 center: Point,
809 radius: f32,
812 start_angle: f32,
815 sweep_angle: f32,
817 stroke: Option<Stroke>,
819 inner_radius: f32,
821 },
822 Line {
825 rect: Rect,
828 brush: Brush,
830 start: Point,
832 end: Point,
834 stroke: Stroke,
836 },
837 Trapezoid {
840 rect: Rect,
843 brush: Brush,
845 trapezoid: Trapezoid,
847 },
848 Image {
850 rect: Rect,
852 image: ImageBitmap,
854 alpha: f32,
856 color_filter: Option<ColorFilter>,
858 sampling: ImageSampling,
860 src_rect: Option<Rect>,
864 },
865 Text(Box<TextPrimitive>),
867 Shadow(ShadowPrimitive),
870 Group(GroupMarker),
873}
874
875#[derive(Clone, Debug, PartialEq)]
884pub struct TextPrimitive {
885 pub rect: Rect,
888 pub text: std::rc::Rc<str>,
891 pub style: DrawTextStyle,
893 pub brush: Brush,
896}
897
898fn shared_text_str(text: &str) -> Rc<str> {
908 use std::hash::{Hash, Hasher};
909
910 const POOL_CAPACITY: usize = 256;
911
912 let mut hasher = crate::FxHasher::default();
913 text.hash(&mut hasher);
914 let key = hasher.finish();
915
916 crate::thread_state::with(|state| {
917 let mut pool = state.shared_texts.borrow_mut();
918 if let Some(shared) = pool.get(&key)
919 && &**shared == text
920 {
921 return Rc::clone(shared);
922 }
923 let shared: Rc<str> = Rc::from(text);
924 if pool.len() >= POOL_CAPACITY {
925 pool.clear();
926 }
927 pool.insert(key, Rc::clone(&shared));
928 shared
929 })
930}
931
932impl DrawPrimitive {
933 pub fn translate(self, dx: f32, dy: f32) -> Self {
936 match self {
937 DrawPrimitive::Content => DrawPrimitive::Content,
938 DrawPrimitive::Blend {
939 primitive,
940 blend_mode,
941 } => DrawPrimitive::Blend {
942 primitive: Box::new(primitive.translate(dx, dy)),
943 blend_mode,
944 },
945 DrawPrimitive::Rect {
946 rect,
947 brush,
948 stroke,
949 } => DrawPrimitive::Rect {
950 rect: rect.translate(dx, dy),
951 brush,
952 stroke,
953 },
954 DrawPrimitive::RoundRect {
955 rect,
956 brush,
957 radii,
958 stroke,
959 } => DrawPrimitive::RoundRect {
960 rect: rect.translate(dx, dy),
961 brush,
962 radii,
963 stroke,
964 },
965 DrawPrimitive::Arc {
966 rect,
967 brush,
968 center,
969 radius,
970 start_angle,
971 sweep_angle,
972 stroke,
973 inner_radius,
974 } => DrawPrimitive::Arc {
975 rect: rect.translate(dx, dy),
976 brush,
977 center: Point::new(center.x + dx, center.y + dy),
978 radius,
979 start_angle,
980 sweep_angle,
981 stroke,
982 inner_radius,
983 },
984 DrawPrimitive::Line {
985 rect,
986 brush,
987 start,
988 end,
989 stroke,
990 } => DrawPrimitive::Line {
991 rect: rect.translate(dx, dy),
992 brush,
993 start: Point::new(start.x + dx, start.y + dy),
994 end: Point::new(end.x + dx, end.y + dy),
995 stroke,
996 },
997 DrawPrimitive::Trapezoid {
998 rect,
999 brush,
1000 trapezoid,
1001 } => DrawPrimitive::Trapezoid {
1002 rect: rect.translate(dx, dy),
1003 brush,
1004 trapezoid: trapezoid.translate(dx, dy),
1005 },
1006 DrawPrimitive::Image {
1007 rect,
1008 image,
1009 alpha,
1010 color_filter,
1011 sampling,
1012 src_rect,
1013 } => DrawPrimitive::Image {
1014 rect: rect.translate(dx, dy),
1015 image,
1016 alpha,
1017 color_filter,
1018 sampling,
1019 src_rect,
1020 },
1021 DrawPrimitive::Text(mut text) => {
1022 text.rect = text.rect.translate(dx, dy);
1023 DrawPrimitive::Text(text)
1024 }
1025 DrawPrimitive::Shadow(ShadowPrimitive::Drop {
1026 shape,
1027 cutout,
1028 blur_radius,
1029 blend_mode,
1030 }) => DrawPrimitive::Shadow(ShadowPrimitive::Drop {
1031 shape: Box::new(shape.translate(dx, dy)),
1032 cutout: cutout.map(|cutout| Box::new(cutout.translate(dx, dy))),
1033 blur_radius,
1034 blend_mode,
1035 }),
1036 DrawPrimitive::Shadow(ShadowPrimitive::Inner {
1037 fill,
1038 cutout,
1039 blur_radius,
1040 blend_mode,
1041 clip_rect,
1042 }) => DrawPrimitive::Shadow(ShadowPrimitive::Inner {
1043 fill: Box::new(fill.translate(dx, dy)),
1044 cutout: Box::new(cutout.translate(dx, dy)),
1045 blur_radius,
1046 blend_mode,
1047 clip_rect: clip_rect.translate(dx, dy),
1048 }),
1049 DrawPrimitive::Group(GroupMarker::Start(group)) => {
1050 DrawPrimitive::Group(GroupMarker::Start(group.translate(dx, dy)))
1051 }
1052 DrawPrimitive::Group(GroupMarker::End) => DrawPrimitive::Group(GroupMarker::End),
1053 }
1054 }
1055}
1056
1057#[derive(Clone, Debug, PartialEq)]
1060pub enum ShadowPrimitive {
1061 Drop {
1065 shape: Box<DrawPrimitive>,
1067 cutout: Option<Box<DrawPrimitive>>,
1069 blur_radius: f32,
1071 blend_mode: BlendMode,
1073 },
1074 Inner {
1076 fill: Box<DrawPrimitive>,
1078 cutout: Box<DrawPrimitive>,
1080 blur_radius: f32,
1082 blend_mode: BlendMode,
1084 clip_rect: Rect,
1086 },
1087}
1088
1089pub trait DrawScope {
1093 fn size(&self) -> Size;
1095 fn draw_content(&mut self);
1099 fn draw_rect(&mut self, brush: Brush);
1101 fn draw_rect_blend(&mut self, brush: Brush, blend_mode: BlendMode);
1103 fn draw_rect_at(&mut self, rect: Rect, brush: Brush);
1105 fn draw_rect_at_blend(&mut self, rect: Rect, brush: Brush, blend_mode: BlendMode);
1107 fn draw_round_rect(&mut self, brush: Brush, radii: CornerRadii);
1109 fn draw_round_rect_blend(&mut self, brush: Brush, radii: CornerRadii, blend_mode: BlendMode);
1111 fn draw_round_rect_at(&mut self, rect: Rect, brush: Brush, radii: CornerRadii);
1113 fn draw_circle(&mut self, brush: Brush, center: Point, radius: f32);
1115 fn draw_circle_blend(
1117 &mut self,
1118 brush: Brush,
1119 center: Point,
1120 radius: f32,
1121 blend_mode: BlendMode,
1122 );
1123
1124 fn draw_rect_stroked(&mut self, brush: Brush, stroke: Stroke);
1126 fn draw_rect_stroked_blend(&mut self, brush: Brush, stroke: Stroke, blend_mode: BlendMode);
1128 fn draw_rect_at_stroked(&mut self, rect: Rect, brush: Brush, stroke: Stroke);
1130 fn draw_rect_at_stroked_blend(
1132 &mut self,
1133 rect: Rect,
1134 brush: Brush,
1135 stroke: Stroke,
1136 blend_mode: BlendMode,
1137 );
1138 fn draw_round_rect_stroked(&mut self, brush: Brush, radii: CornerRadii, stroke: Stroke);
1140 fn draw_round_rect_stroked_blend(
1142 &mut self,
1143 brush: Brush,
1144 radii: CornerRadii,
1145 stroke: Stroke,
1146 blend_mode: BlendMode,
1147 );
1148 fn draw_round_rect_at_stroked(
1150 &mut self,
1151 rect: Rect,
1152 brush: Brush,
1153 radii: CornerRadii,
1154 stroke: Stroke,
1155 );
1156 fn draw_round_rect_at_stroked_blend(
1158 &mut self,
1159 rect: Rect,
1160 brush: Brush,
1161 radii: CornerRadii,
1162 stroke: Stroke,
1163 blend_mode: BlendMode,
1164 );
1165 fn draw_circle_stroked(&mut self, brush: Brush, center: Point, radius: f32, stroke: Stroke);
1168 fn draw_circle_stroked_blend(
1170 &mut self,
1171 brush: Brush,
1172 center: Point,
1173 radius: f32,
1174 stroke: Stroke,
1175 blend_mode: BlendMode,
1176 );
1177
1178 fn draw_arc(
1191 &mut self,
1192 brush: Brush,
1193 center: Point,
1194 radius: f32,
1195 start_angle: f32,
1196 sweep_angle: f32,
1197 stroke: Stroke,
1198 );
1199 #[expect(clippy::too_many_arguments)]
1201 fn draw_arc_blend(
1202 &mut self,
1203 brush: Brush,
1204 center: Point,
1205 radius: f32,
1206 start_angle: f32,
1207 sweep_angle: f32,
1208 stroke: Stroke,
1209 blend_mode: BlendMode,
1210 );
1211
1212 fn draw_line(&mut self, brush: Brush, start: Point, end: Point, stroke: Stroke);
1218 fn draw_line_blend(
1220 &mut self,
1221 brush: Brush,
1222 start: Point,
1223 end: Point,
1224 stroke: Stroke,
1225 blend_mode: BlendMode,
1226 );
1227
1228 fn draw_path(&mut self, path: &Path, brush: Brush, style: DrawStyle);
1237 fn draw_path_blend(
1239 &mut self,
1240 path: &Path,
1241 brush: Brush,
1242 style: DrawStyle,
1243 blend_mode: BlendMode,
1244 );
1245
1246 fn draw_annular_sector(
1255 &mut self,
1256 brush: Brush,
1257 center: Point,
1258 inner_radius: f32,
1259 outer_radius: f32,
1260 start_angle: f32,
1261 sweep_angle: f32,
1262 );
1263 #[expect(clippy::too_many_arguments)]
1265 fn draw_annular_sector_blend(
1266 &mut self,
1267 brush: Brush,
1268 center: Point,
1269 inner_radius: f32,
1270 outer_radius: f32,
1271 start_angle: f32,
1272 sweep_angle: f32,
1273 blend_mode: BlendMode,
1274 );
1275
1276 fn draw_image(&mut self, image: ImageBitmap);
1278 fn draw_image_blend(&mut self, image: ImageBitmap, blend_mode: BlendMode);
1280 fn draw_image_at(
1283 &mut self,
1284 rect: Rect,
1285 image: ImageBitmap,
1286 alpha: f32,
1287 color_filter: Option<ColorFilter>,
1288 );
1289 fn draw_image_at_sampled(
1291 &mut self,
1292 rect: Rect,
1293 image: ImageBitmap,
1294 alpha: f32,
1295 color_filter: Option<ColorFilter>,
1296 sampling: ImageSampling,
1297 );
1298 fn draw_image_at_blend(
1300 &mut self,
1301 rect: Rect,
1302 image: ImageBitmap,
1303 alpha: f32,
1304 color_filter: Option<ColorFilter>,
1305 blend_mode: BlendMode,
1306 );
1307 fn draw_image_src(
1310 &mut self,
1311 image: ImageBitmap,
1312 src_rect: Rect,
1313 dst_rect: Rect,
1314 alpha: f32,
1315 color_filter: Option<ColorFilter>,
1316 );
1317 fn draw_image_src_sampled(
1319 &mut self,
1320 image: ImageBitmap,
1321 src_rect: Rect,
1322 dst_rect: Rect,
1323 alpha: f32,
1324 color_filter: Option<ColorFilter>,
1325 sampling: ImageSampling,
1326 );
1327 fn draw_image_src_blend(
1329 &mut self,
1330 image: ImageBitmap,
1331 src_rect: Rect,
1332 dst_rect: Rect,
1333 alpha: f32,
1334 color_filter: Option<ColorFilter>,
1335 blend_mode: BlendMode,
1336 );
1337 fn draw_vector_path(&mut self, path: &crate::VectorPath, brush: Brush);
1345 fn draw_svg_path(&mut self, d: &str, brush: Brush) {
1351 if let Ok(path) = crate::VectorPath::parse(d) {
1352 self.draw_vector_path(&path, brush);
1353 }
1354 }
1355
1356 fn measure_text(&self, text: &str, style: &DrawTextStyle) -> TextMeasurement;
1363
1364 fn draw_text(&mut self, brush: Brush, text: &str, style: &DrawTextStyle) {
1367 self.draw_text_at(Rect::from_size(self.size()), brush, text, style);
1368 }
1369
1370 fn draw_text_at(&mut self, rect: Rect, brush: Brush, text: &str, style: &DrawTextStyle);
1377
1378 fn draw_text_from(&mut self, top_left: Point, brush: Brush, text: &str, style: &DrawTextStyle) {
1384 if text.is_empty() {
1385 return;
1386 }
1387 let measurement = self.measure_text(text, style);
1388 self.draw_text_at(
1389 Rect::from_origin_size(top_left, measurement.size),
1390 brush,
1391 text,
1392 &DrawTextStyle {
1393 align: TextAlign::Left,
1394 vertical_align: TextVerticalAlign::Top,
1395 ..style.clone()
1396 },
1397 );
1398 }
1399
1400 fn with_transform(
1406 &mut self,
1407 transform: &[DrawTransform<'_>],
1408 block: &mut dyn FnMut(&mut dyn DrawScope),
1409 );
1410
1411 fn translate(&mut self, dx: f32, dy: f32, block: &mut dyn FnMut(&mut dyn DrawScope)) {
1413 self.with_transform(&[DrawTransform::Translate { dx, dy }], block);
1414 }
1415
1416 fn scale(
1419 &mut self,
1420 scale_x: f32,
1421 scale_y: f32,
1422 pivot: Point,
1423 block: &mut dyn FnMut(&mut dyn DrawScope),
1424 ) {
1425 self.with_transform(
1426 &[DrawTransform::Scale {
1427 scale_x,
1428 scale_y,
1429 pivot,
1430 }],
1431 block,
1432 );
1433 }
1434
1435 fn rotate(&mut self, degrees: f32, pivot: Point, block: &mut dyn FnMut(&mut dyn DrawScope)) {
1438 self.with_transform(&[DrawTransform::Rotate { degrees, pivot }], block);
1439 }
1440
1441 fn inset(
1444 &mut self,
1445 left: f32,
1446 top: f32,
1447 right: f32,
1448 bottom: f32,
1449 block: &mut dyn FnMut(&mut dyn DrawScope),
1450 ) {
1451 self.with_transform(
1452 &[DrawTransform::Inset {
1453 left,
1454 top,
1455 right,
1456 bottom,
1457 }],
1458 block,
1459 );
1460 }
1461
1462 fn clip_rect(&mut self, rect: Rect, block: &mut dyn FnMut(&mut dyn DrawScope)) {
1464 self.with_transform(&[DrawTransform::ClipRect(rect)], block);
1465 }
1466
1467 fn clip_path(&mut self, path: &Path, block: &mut dyn FnMut(&mut dyn DrawScope)) {
1470 self.with_transform(&[DrawTransform::ClipPath(path)], block);
1471 }
1472
1473 fn into_primitives(self) -> Vec<DrawPrimitive>;
1476}
1477
1478pub fn align_text_block(rect: Rect, measurement: TextMeasurement, style: &DrawTextStyle) -> Point {
1484 let x = match style.align {
1485 TextAlign::Left => rect.x,
1486 TextAlign::Center => rect.x + (rect.width - measurement.size.width) * 0.5,
1487 TextAlign::Right => rect.x + rect.width - measurement.size.width,
1488 };
1489 let y = match style.vertical_align {
1490 TextVerticalAlign::Top => rect.y,
1491 TextVerticalAlign::Center => rect.y + (rect.height - measurement.size.height) * 0.5,
1492 TextVerticalAlign::Bottom => rect.y + rect.height - measurement.size.height,
1493 TextVerticalAlign::Baseline => rect.y - measurement.first_baseline,
1494 };
1495 Point::new(x, y)
1496}
1497
1498fn vector_mask_rect(bounds: Rect) -> Rect {
1501 let x = bounds.x.floor() - 1.0;
1502 let y = bounds.y.floor() - 1.0;
1503 Rect {
1504 x,
1505 y,
1506 width: (bounds.x + bounds.width).ceil() - x + 1.0,
1507 height: (bounds.y + bounds.height).ceil() - y + 1.0,
1508 }
1509}
1510
1511pub struct DrawScopeDefault<'a> {
1515 size: Size,
1516 recording: CommandRecorder<'a>,
1517 text_measurer: Option<Rc<dyn DrawTextMeasurer>>,
1518}
1519
1520impl<'a> DrawScopeDefault<'a> {
1521 pub fn new(size: Size, recording: &'a mut CommandRecording) -> Self {
1524 Self::with_storage(size, None, recording)
1525 }
1526
1527 pub fn with_text_measurer(
1532 size: Size,
1533 text_measurer: Rc<dyn DrawTextMeasurer>,
1534 recording: &'a mut CommandRecording,
1535 ) -> Self {
1536 Self::with_storage(size, Some(text_measurer), recording)
1537 }
1538
1539 fn with_storage(
1540 size: Size,
1541 text_measurer: Option<Rc<dyn DrawTextMeasurer>>,
1542 recording: &'a mut CommandRecording,
1543 ) -> Self {
1544 Self {
1545 size,
1546 recording: recording.record(),
1547 text_measurer,
1548 }
1549 }
1550
1551 pub fn content_marker_count(&self) -> u32 {
1553 self.recording.content_markers()
1554 }
1555
1556 pub fn draw_inset(&mut self, insets: EdgeInsets, draw: impl FnOnce(&mut DrawScopeDefault<'_>)) {
1560 let inner = insets.inset_rect(self.size);
1561 let mut recording = CommandRecording::default();
1562 let mut scope = DrawScopeDefault::with_storage(
1563 Size {
1564 width: inner.width,
1565 height: inner.height,
1566 },
1567 self.text_measurer.clone(),
1568 &mut recording,
1569 );
1570 draw(&mut scope);
1571 scope.finish();
1572 self.push_recorded(crate::translate_recorded(
1573 recording.primitives_with_markers(),
1574 inner.x,
1575 inner.y,
1576 ));
1577 }
1578
1579 pub fn push_recorded(&mut self, primitives: impl IntoIterator<Item = DrawPrimitive>) {
1581 for primitive in primitives {
1582 self.recording.push_primitive(primitive);
1583 }
1584 }
1585
1586 pub fn finish(self) {
1588 self.recording.finish();
1589 }
1590
1591 fn push_blended_primitive(&mut self, primitive: DrawPrimitive, blend_mode: BlendMode) {
1592 if blend_mode != BlendMode::SrcOver {
1593 self.recording.push_other(DrawPrimitive::Blend {
1594 primitive: Box::new(primitive),
1595 blend_mode,
1596 });
1597 return;
1598 }
1599 self.recording.push_other(primitive);
1600 }
1601
1602 #[expect(clippy::too_many_arguments)]
1603 #[inline]
1604 fn push_arc(
1605 &mut self,
1606 brush: Brush,
1607 center: Point,
1608 radius: f32,
1609 start_angle: f32,
1610 sweep_angle: f32,
1611 stroke: Option<Stroke>,
1612 inner_radius: f32,
1613 blend_mode: BlendMode,
1614 ) {
1615 let args = ArcRecordArgs {
1616 brush: &brush,
1617 center,
1618 radius,
1619 start_angle,
1620 sweep_angle,
1621 stroke,
1622 inner_radius,
1623 blend_mode,
1624 };
1625 let geometry = normalized_band(&args);
1626 if geometry.is_degenerate() {
1627 return;
1628 }
1629 self.recording.push_scope_arc(&args, &geometry);
1630 }
1631
1632 fn push_path_fill(&mut self, path: &Path, brush: &Brush, blend_mode: BlendMode) {
1636 let brush_rect = Rect::from_size(self.size);
1637 let recording = &mut self.recording;
1638 let sliced = crate::thread_state::with(|state| {
1639 let mut slicer = state.path_slicer.borrow_mut();
1640 let contours = path
1641 .contours()
1642 .iter()
1643 .map(|contour| contour.points.as_slice());
1644 slicer
1645 .slice(contours, PathFillRule::NonZero)
1646 .map(|slices| recording.push_trapezoids(brush_rect, slices, brush, blend_mode))
1647 .is_some()
1648 });
1649 if !sliced {
1650 self.push_vector_path(
1651 &path.to_vector_path(PathFillRule::NonZero),
1652 brush,
1653 brush_rect,
1654 blend_mode,
1655 PathSide::Inside,
1656 );
1657 }
1658 }
1659
1660 fn with_path_mask(
1665 &mut self,
1666 mask: &crate::VectorPath,
1667 rest: &[DrawTransform<'_>],
1668 block: &mut dyn FnMut(&mut dyn DrawScope),
1669 ) {
1670 let rect = vector_mask_rect(mask.bounds());
1671 self.recording.push_group_start(DrawGroup::masked(rect));
1672 self.with_transform(rest, block);
1673 self.push_vector_path(
1674 mask,
1675 &Brush::solid(Color::BLACK),
1676 rect,
1677 BlendMode::DstOut,
1678 PathSide::Outside,
1679 );
1680 self.recording.push_group_end();
1681 }
1682
1683 fn push_vector_path(
1687 &mut self,
1688 path: &crate::VectorPath,
1689 brush: &Brush,
1690 brush_rect: Rect,
1691 blend_mode: BlendMode,
1692 side: PathSide,
1693 ) {
1694 const MAX_MASK_PIXELS: f32 = 4096.0;
1695
1696 if path.is_empty() || !brush.paints() {
1697 return;
1698 }
1699 let bounds = path.bounds();
1700 if bounds.width <= 0.0 || bounds.height <= 0.0 {
1701 return;
1702 }
1703
1704 let rect = vector_mask_rect(bounds);
1705 let origin = Point::new(rect.x, rect.y);
1706 let mask_width = (rect.width * VECTOR_PATH_SUPERSAMPLE)
1707 .ceil()
1708 .clamp(1.0, MAX_MASK_PIXELS) as usize;
1709 let mask_height = (rect.height * VECTOR_PATH_SUPERSAMPLE)
1710 .ceil()
1711 .clamp(1.0, MAX_MASK_PIXELS) as usize;
1712 let mask_size = (mask_width, mask_height);
1713
1714 let key = vector_path_mask_key(path, origin, mask_size, brush, brush_rect, side);
1715 let image = match vector_path_mask_cache_get(key) {
1716 Some(image) => image,
1717 None => {
1718 let Some(image) =
1719 rasterize_vector_path(path, brush, brush_rect, origin, mask_size, side)
1720 else {
1721 return;
1722 };
1723 vector_path_mask_cache_put(key, image.clone());
1724 image
1725 }
1726 };
1727
1728 let image = DrawPrimitive::Image {
1729 rect,
1730 image,
1731 alpha: 1.0,
1732 color_filter: None,
1733 sampling: ImageSampling::Linear,
1734 src_rect: None,
1735 };
1736 self.recording
1737 .push_other(if blend_mode == BlendMode::SrcOver {
1738 image
1739 } else {
1740 DrawPrimitive::Blend {
1741 primitive: Box::new(image),
1742 blend_mode,
1743 }
1744 });
1745 }
1746}
1747
1748impl DrawScope for DrawScopeDefault<'_> {
1749 fn size(&self) -> Size {
1750 self.size
1751 }
1752
1753 fn with_transform(
1754 &mut self,
1755 transform: &[DrawTransform<'_>],
1756 block: &mut dyn FnMut(&mut dyn DrawScope),
1757 ) {
1758 let Some((step, rest)) = transform.split_first() else {
1759 block(self);
1760 return;
1761 };
1762 let outer = self.size;
1763 let (group, size) = match *step {
1764 DrawTransform::Translate { dx, dy } => (DrawGroup::moved(dx, dy, outer), outer),
1765 DrawTransform::Scale {
1766 scale_x,
1767 scale_y,
1768 pivot,
1769 } => (DrawGroup::about(pivot, scale_x, scale_y, 0.0, outer), outer),
1770 DrawTransform::Rotate { degrees, pivot } => {
1771 (DrawGroup::about(pivot, 1.0, 1.0, degrees, outer), outer)
1772 }
1773 DrawTransform::Inset {
1774 left,
1775 top,
1776 right,
1777 bottom,
1778 } => {
1779 let inner = Size::new(
1780 (outer.width - left - right).max(0.0),
1781 (outer.height - top - bottom).max(0.0),
1782 );
1783 (DrawGroup::moved(left, top, inner), inner)
1784 }
1785 DrawTransform::ClipRect(rect) => (DrawGroup::plain(rect, true), outer),
1786 DrawTransform::ClipPath(path) => {
1787 let mask = path.to_vector_path(PathFillRule::NonZero);
1788 if !mask.is_empty() {
1789 self.with_path_mask(&mask, rest, block);
1790 return;
1791 }
1792 (DrawGroup::plain(Rect::EMPTY, true), outer)
1794 }
1795 };
1796 self.recording.push_group_start(group);
1797 self.size = size;
1798 self.with_transform(rest, block);
1799 self.size = outer;
1800 self.recording.push_group_end();
1801 }
1802
1803 fn draw_content(&mut self) {
1804 self.recording.push_content();
1805 }
1806
1807 fn draw_rect(&mut self, brush: Brush) {
1808 self.draw_rect_blend(brush, BlendMode::SrcOver);
1809 }
1810
1811 fn draw_rect_blend(&mut self, brush: Brush, blend_mode: BlendMode) {
1812 self.recording
1813 .push_rect(Rect::from_size(self.size), &brush, None, blend_mode);
1814 }
1815
1816 fn draw_rect_at(&mut self, rect: Rect, brush: Brush) {
1817 self.draw_rect_at_blend(rect, brush, BlendMode::SrcOver);
1818 }
1819
1820 fn draw_rect_at_blend(&mut self, rect: Rect, brush: Brush, blend_mode: BlendMode) {
1821 self.recording.push_rect(rect, &brush, None, blend_mode);
1822 }
1823
1824 fn draw_round_rect(&mut self, brush: Brush, radii: CornerRadii) {
1825 self.draw_round_rect_blend(brush, radii, BlendMode::SrcOver);
1826 }
1827
1828 fn draw_round_rect_blend(&mut self, brush: Brush, radii: CornerRadii, blend_mode: BlendMode) {
1829 self.recording
1830 .push_round_rect(Rect::from_size(self.size), &brush, radii, None, blend_mode);
1831 }
1832
1833 fn draw_round_rect_at(&mut self, rect: Rect, brush: Brush, radii: CornerRadii) {
1834 self.recording
1835 .push_round_rect(rect, &brush, radii, None, BlendMode::SrcOver);
1836 }
1837
1838 fn draw_rect_stroked(&mut self, brush: Brush, stroke: Stroke) {
1839 self.draw_rect_stroked_blend(brush, stroke, BlendMode::SrcOver);
1840 }
1841
1842 fn draw_rect_stroked_blend(&mut self, brush: Brush, stroke: Stroke, blend_mode: BlendMode) {
1843 self.draw_rect_at_stroked_blend(Rect::from_size(self.size), brush, stroke, blend_mode);
1844 }
1845
1846 fn draw_rect_at_stroked(&mut self, rect: Rect, brush: Brush, stroke: Stroke) {
1847 self.draw_rect_at_stroked_blend(rect, brush, stroke, BlendMode::SrcOver);
1848 }
1849
1850 fn draw_rect_at_stroked_blend(
1851 &mut self,
1852 rect: Rect,
1853 brush: Brush,
1854 stroke: Stroke,
1855 blend_mode: BlendMode,
1856 ) {
1857 if !stroke.is_visible() {
1858 return;
1859 }
1860 self.recording
1861 .push_rect(rect, &brush, Some(stroke), blend_mode);
1862 }
1863
1864 fn draw_round_rect_stroked(&mut self, brush: Brush, radii: CornerRadii, stroke: Stroke) {
1865 self.draw_round_rect_stroked_blend(brush, radii, stroke, BlendMode::SrcOver);
1866 }
1867
1868 fn draw_round_rect_stroked_blend(
1869 &mut self,
1870 brush: Brush,
1871 radii: CornerRadii,
1872 stroke: Stroke,
1873 blend_mode: BlendMode,
1874 ) {
1875 self.draw_round_rect_at_stroked_blend(
1876 Rect::from_size(self.size),
1877 brush,
1878 radii,
1879 stroke,
1880 blend_mode,
1881 );
1882 }
1883
1884 fn draw_round_rect_at_stroked(
1885 &mut self,
1886 rect: Rect,
1887 brush: Brush,
1888 radii: CornerRadii,
1889 stroke: Stroke,
1890 ) {
1891 self.draw_round_rect_at_stroked_blend(rect, brush, radii, stroke, BlendMode::SrcOver);
1892 }
1893
1894 fn draw_round_rect_at_stroked_blend(
1895 &mut self,
1896 rect: Rect,
1897 brush: Brush,
1898 radii: CornerRadii,
1899 stroke: Stroke,
1900 blend_mode: BlendMode,
1901 ) {
1902 if !stroke.is_visible() {
1903 return;
1904 }
1905 self.recording
1906 .push_round_rect(rect, &brush, radii, Some(stroke), blend_mode);
1907 }
1908
1909 fn draw_circle_stroked(&mut self, brush: Brush, center: Point, radius: f32, stroke: Stroke) {
1910 self.draw_circle_stroked_blend(brush, center, radius, stroke, BlendMode::SrcOver);
1911 }
1912
1913 fn draw_circle_stroked_blend(
1914 &mut self,
1915 brush: Brush,
1916 center: Point,
1917 radius: f32,
1918 stroke: Stroke,
1919 blend_mode: BlendMode,
1920 ) {
1921 if !stroke.is_visible() || !radius.is_finite() {
1922 return;
1923 }
1924 let radius = radius.max(0.0);
1925 let diameter = radius * 2.0;
1926 self.draw_round_rect_at_stroked_blend(
1927 Rect {
1928 x: center.x - radius,
1929 y: center.y - radius,
1930 width: diameter,
1931 height: diameter,
1932 },
1933 brush,
1934 CornerRadii::uniform(radius),
1935 stroke,
1936 blend_mode,
1937 );
1938 }
1939
1940 fn draw_arc(
1941 &mut self,
1942 brush: Brush,
1943 center: Point,
1944 radius: f32,
1945 start_angle: f32,
1946 sweep_angle: f32,
1947 stroke: Stroke,
1948 ) {
1949 self.draw_arc_blend(
1950 brush,
1951 center,
1952 radius,
1953 start_angle,
1954 sweep_angle,
1955 stroke,
1956 BlendMode::SrcOver,
1957 );
1958 }
1959
1960 fn draw_arc_blend(
1961 &mut self,
1962 brush: Brush,
1963 center: Point,
1964 radius: f32,
1965 start_angle: f32,
1966 sweep_angle: f32,
1967 stroke: Stroke,
1968 blend_mode: BlendMode,
1969 ) {
1970 if !stroke.is_visible() {
1971 return;
1972 }
1973 self.push_arc(
1974 brush,
1975 center,
1976 radius,
1977 start_angle,
1978 sweep_angle,
1979 Some(stroke),
1980 0.0,
1981 blend_mode,
1982 );
1983 }
1984
1985 fn draw_line(&mut self, brush: Brush, start: Point, end: Point, stroke: Stroke) {
1986 self.draw_line_blend(brush, start, end, stroke, BlendMode::SrcOver);
1987 }
1988
1989 fn draw_line_blend(
1990 &mut self,
1991 brush: Brush,
1992 start: Point,
1993 end: Point,
1994 stroke: Stroke,
1995 blend_mode: BlendMode,
1996 ) {
1997 self.recording
1998 .line_run(&brush, stroke, blend_mode)
1999 .push(start, end, stroke.cap);
2000 }
2001
2002 fn draw_path(&mut self, path: &Path, brush: Brush, style: DrawStyle) {
2003 self.draw_path_blend(path, brush, style, BlendMode::SrcOver);
2004 }
2005
2006 fn draw_path_blend(
2007 &mut self,
2008 path: &Path,
2009 brush: Brush,
2010 style: DrawStyle,
2011 blend_mode: BlendMode,
2012 ) {
2013 let (stroke, dash) = match style {
2014 DrawStyle::Fill => {
2015 self.push_path_fill(path, &brush, blend_mode);
2016 return;
2017 }
2018 DrawStyle::Stroke(stroke) => (stroke, None),
2019 DrawStyle::DashedStroke(stroke, dash) => (stroke, Some(dash)),
2020 };
2021 if !stroke.is_visible() {
2022 return;
2023 }
2024 let mut run = self.recording.line_run(&brush, stroke, blend_mode);
2025 let mut push = |line: LineGeometry| run.push(line.start, line.end, line.cap);
2026 for contour in path.contours() {
2027 let Some(dash) = dash else {
2028 crate::for_each_stroke_line(&contour.points, contour.closed, stroke, &mut push);
2029 continue;
2030 };
2031 let closing = contour
2032 .closed
2033 .then(|| contour.points.first().copied())
2034 .flatten();
2035 let points: std::borrow::Cow<'_, [Point]> = match closing {
2036 Some(first) => {
2037 std::borrow::Cow::Owned(contour.points.iter().copied().chain([first]).collect())
2038 }
2039 None => std::borrow::Cow::Borrowed(&contour.points),
2040 };
2041 dash.for_each_dash(&points, |piece| {
2042 crate::for_each_stroke_line(piece, false, stroke, &mut push);
2043 });
2044 }
2045 }
2046
2047 fn draw_annular_sector(
2048 &mut self,
2049 brush: Brush,
2050 center: Point,
2051 inner_radius: f32,
2052 outer_radius: f32,
2053 start_angle: f32,
2054 sweep_angle: f32,
2055 ) {
2056 self.draw_annular_sector_blend(
2057 brush,
2058 center,
2059 inner_radius,
2060 outer_radius,
2061 start_angle,
2062 sweep_angle,
2063 BlendMode::SrcOver,
2064 );
2065 }
2066
2067 fn draw_annular_sector_blend(
2068 &mut self,
2069 brush: Brush,
2070 center: Point,
2071 inner_radius: f32,
2072 outer_radius: f32,
2073 start_angle: f32,
2074 sweep_angle: f32,
2075 blend_mode: BlendMode,
2076 ) {
2077 self.push_arc(
2078 brush,
2079 center,
2080 outer_radius,
2081 start_angle,
2082 sweep_angle,
2083 None,
2084 inner_radius,
2085 blend_mode,
2086 );
2087 }
2088
2089 fn draw_circle(&mut self, brush: Brush, center: Point, radius: f32) {
2090 self.draw_circle_blend(brush, center, radius, BlendMode::SrcOver);
2091 }
2092
2093 fn draw_circle_blend(
2094 &mut self,
2095 brush: Brush,
2096 center: Point,
2097 radius: f32,
2098 blend_mode: BlendMode,
2099 ) {
2100 let radius = radius.max(0.0);
2101 let diameter = radius * 2.0;
2102 self.recording.push_round_rect(
2103 Rect {
2104 x: center.x - radius,
2105 y: center.y - radius,
2106 width: diameter,
2107 height: diameter,
2108 },
2109 &brush,
2110 CornerRadii::uniform(radius),
2111 None,
2112 blend_mode,
2113 );
2114 }
2115
2116 fn draw_image(&mut self, image: ImageBitmap) {
2117 self.draw_image_blend(image, BlendMode::SrcOver);
2118 }
2119
2120 fn draw_image_blend(&mut self, image: ImageBitmap, blend_mode: BlendMode) {
2121 self.push_blended_primitive(
2122 DrawPrimitive::Image {
2123 rect: Rect::from_size(self.size),
2124 image,
2125 alpha: 1.0,
2126 color_filter: None,
2127 sampling: ImageSampling::Nearest,
2128 src_rect: None,
2129 },
2130 blend_mode,
2131 );
2132 }
2133
2134 fn draw_image_at(
2135 &mut self,
2136 rect: Rect,
2137 image: ImageBitmap,
2138 alpha: f32,
2139 color_filter: Option<ColorFilter>,
2140 ) {
2141 self.draw_image_at_sampled(rect, image, alpha, color_filter, ImageSampling::Nearest);
2142 }
2143
2144 fn draw_image_at_sampled(
2145 &mut self,
2146 rect: Rect,
2147 image: ImageBitmap,
2148 alpha: f32,
2149 color_filter: Option<ColorFilter>,
2150 sampling: ImageSampling,
2151 ) {
2152 self.push_blended_primitive(
2153 DrawPrimitive::Image {
2154 rect,
2155 image,
2156 alpha: alpha.clamp(0.0, 1.0),
2157 color_filter,
2158 sampling,
2159 src_rect: None,
2160 },
2161 BlendMode::SrcOver,
2162 );
2163 }
2164
2165 fn draw_image_at_blend(
2166 &mut self,
2167 rect: Rect,
2168 image: ImageBitmap,
2169 alpha: f32,
2170 color_filter: Option<ColorFilter>,
2171 blend_mode: BlendMode,
2172 ) {
2173 self.push_blended_primitive(
2174 DrawPrimitive::Image {
2175 rect,
2176 image,
2177 alpha: alpha.clamp(0.0, 1.0),
2178 color_filter,
2179 sampling: ImageSampling::Nearest,
2180 src_rect: None,
2181 },
2182 blend_mode,
2183 );
2184 }
2185
2186 fn draw_image_src(
2187 &mut self,
2188 image: ImageBitmap,
2189 src_rect: Rect,
2190 dst_rect: Rect,
2191 alpha: f32,
2192 color_filter: Option<ColorFilter>,
2193 ) {
2194 self.draw_image_src_blend(
2195 image,
2196 src_rect,
2197 dst_rect,
2198 alpha,
2199 color_filter,
2200 BlendMode::SrcOver,
2201 );
2202 }
2203
2204 fn draw_image_src_sampled(
2205 &mut self,
2206 image: ImageBitmap,
2207 src_rect: Rect,
2208 dst_rect: Rect,
2209 alpha: f32,
2210 color_filter: Option<ColorFilter>,
2211 sampling: ImageSampling,
2212 ) {
2213 self.push_blended_primitive(
2214 DrawPrimitive::Image {
2215 rect: dst_rect,
2216 image,
2217 alpha: alpha.clamp(0.0, 1.0),
2218 color_filter,
2219 sampling,
2220 src_rect: Some(src_rect),
2221 },
2222 BlendMode::SrcOver,
2223 );
2224 }
2225
2226 fn draw_image_src_blend(
2227 &mut self,
2228 image: ImageBitmap,
2229 src_rect: Rect,
2230 dst_rect: Rect,
2231 alpha: f32,
2232 color_filter: Option<ColorFilter>,
2233 blend_mode: BlendMode,
2234 ) {
2235 self.push_blended_primitive(
2236 DrawPrimitive::Image {
2237 rect: dst_rect,
2238 image,
2239 alpha: alpha.clamp(0.0, 1.0),
2240 color_filter,
2241 sampling: ImageSampling::Nearest,
2242 src_rect: Some(src_rect),
2243 },
2244 blend_mode,
2245 );
2246 }
2247
2248 fn draw_vector_path(&mut self, path: &crate::VectorPath, brush: Brush) {
2249 self.push_vector_path(
2250 path,
2251 &brush,
2252 Rect::from_size(self.size),
2253 BlendMode::SrcOver,
2254 PathSide::Inside,
2255 );
2256 }
2257
2258 fn measure_text(&self, text: &str, style: &DrawTextStyle) -> TextMeasurement {
2259 match &self.text_measurer {
2260 Some(measurer) => measurer.measure_text(text, style),
2261 None => estimate_text_measurement(text, style),
2262 }
2263 }
2264
2265 fn draw_text_at(&mut self, rect: Rect, brush: Brush, text: &str, style: &DrawTextStyle) {
2266 if text.is_empty() || !brush.paints() {
2267 return;
2268 }
2269 let measurement = self.measure_text(text, style);
2270 if !(measurement.size.width > 0.0 && measurement.size.height > 0.0) {
2271 return;
2272 }
2273 let origin = align_text_block(rect, measurement, style);
2274 if !origin.x.is_finite() || !origin.y.is_finite() {
2275 return;
2276 }
2277 self.recording
2278 .push_other(DrawPrimitive::Text(Box::new(TextPrimitive {
2279 rect: Rect::from_origin_size(origin, measurement.size),
2280 text: shared_text_str(text),
2281 style: style.clone(),
2282 brush,
2283 })));
2284 }
2285
2286 fn into_primitives(self) -> Vec<DrawPrimitive> {
2287 self.recording.into_primitives()
2288 }
2289}
2290
2291#[cfg(test)]
2292#[path = "tests/geometry_tests.rs"]
2293mod tests;