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(
59 path: &crate::VectorPath,
60 origin: Point,
61 mask_size: (usize, usize),
62 brush: &Brush,
63 brush_rect: Rect,
64) -> u64 {
65 use std::hash::Hasher;
66 let mut hasher = crate::fx_hash::FxHasher::default();
67 hasher.write_u8(path.fill_rule() as u8);
68 hasher.write_u32(origin.x.to_bits());
69 hasher.write_u32(origin.y.to_bits());
70 hasher.write_usize(mask_size.0);
71 hasher.write_usize(mask_size.1);
72 crate::render_hash::hash_brush(brush, &mut hasher);
73 if !matches!(brush, Brush::Solid(_)) {
74 for value in [
75 brush_rect.x,
76 brush_rect.y,
77 brush_rect.width,
78 brush_rect.height,
79 ] {
80 hasher.write_u32(value.to_bits());
81 }
82 }
83 for subpath in path.subpaths() {
84 hasher.write_usize(subpath.len());
85 for point in subpath {
86 hasher.write_u32(point.x.to_bits());
87 hasher.write_u32(point.y.to_bits());
88 }
89 }
90 hasher.finish()
91}
92
93const VECTOR_PATH_SUPERSAMPLE: f32 = 2.0;
96
97fn paints(brush: &Brush) -> bool {
99 match brush {
100 Brush::Solid(color) => color.3 > 0.0,
101 Brush::LinearGradient { colors, .. }
102 | Brush::RadialGradient { colors, .. }
103 | Brush::SweepGradient { colors, .. } => colors.iter().any(|color| color.3 > 0.0),
104 }
105}
106
107fn rasterize_vector_path(
111 path: &crate::VectorPath,
112 brush: &Brush,
113 brush_rect: Rect,
114 origin: Point,
115 (width, height): (usize, usize),
116) -> Option<ImageBitmap> {
117 let byte = |value: f32| (value.clamp(0.0, 1.0) * 255.0 + 0.5) as u8;
118 if let Brush::Solid(color) = brush {
119 let opacity = color.3.clamp(0.0, 1.0);
120 let mask = path.coverage_mask(width, height, origin, VECTOR_PATH_SUPERSAMPLE, opacity);
121 let rgb = [byte(color.0), byte(color.1), byte(color.2)];
122 return ImageBitmap::from_alpha8(width as u32, height as u32, rgb, mask).ok();
123 }
124 let coverage = path.coverage_mask(width, height, origin, VECTOR_PATH_SUPERSAMPLE, 1.0);
125 let mut pixels = Vec::with_capacity(coverage.len() * 4);
126 for (row, line) in coverage.chunks_exact(width).enumerate() {
127 let y = origin.y + (row as f32 + 0.5) / VECTOR_PATH_SUPERSAMPLE;
128 for (column, &covered) in line.iter().enumerate() {
129 if covered == 0 {
130 pixels.extend_from_slice(&[0; 4]);
131 continue;
132 }
133 let x = origin.x + (column as f32 + 0.5) / VECTOR_PATH_SUPERSAMPLE;
134 let color = brush.color_at(brush_rect, Point::new(x, y));
135 let alpha = (color.3.clamp(0.0, 1.0) * f32::from(covered) + 0.5) as u8;
136 pixels.extend_from_slice(&[byte(color.0), byte(color.1), byte(color.2), alpha]);
137 }
138 }
139 ImageBitmap::from_rgba8(width as u32, height as u32, pixels).ok()
140}
141
142fn vector_path_mask_cache_get(key: u64) -> Option<ImageBitmap> {
143 crate::thread_state::with(|state| state.vector_path_masks.borrow_mut().get(key))
144}
145
146fn vector_path_mask_cache_put(key: u64, image: ImageBitmap) {
147 crate::thread_state::with(|state| state.vector_path_masks.borrow_mut().put(key, image));
148}
149
150#[derive(Clone, Copy, Debug, PartialEq, Default)]
152pub struct Point {
153 pub x: f32,
155 pub y: f32,
157}
158
159impl Point {
160 pub const fn new(x: f32, y: f32) -> Self {
162 Self { x, y }
163 }
164
165 pub const ZERO: Point = Point { x: 0.0, y: 0.0 };
167}
168
169#[derive(Clone, Copy, Debug, PartialEq, Default)]
171pub struct Size {
172 pub width: f32,
174 pub height: f32,
176}
177
178impl Size {
179 pub const fn new(width: f32, height: f32) -> Self {
181 Self { width, height }
182 }
183
184 pub const ZERO: Size = Size {
186 width: 0.0,
187 height: 0.0,
188 };
189}
190
191#[derive(Clone, Copy, Debug, PartialEq)]
193pub struct Rect {
194 pub x: f32,
196 pub y: f32,
198 pub width: f32,
200 pub height: f32,
202}
203
204impl Rect {
205 pub const EMPTY: Rect = Rect {
209 x: 0.0,
210 y: 0.0,
211 width: 0.0,
212 height: 0.0,
213 };
214
215 pub fn from_origin_size(origin: Point, size: Size) -> Self {
217 Self {
218 x: origin.x,
219 y: origin.y,
220 width: size.width,
221 height: size.height,
222 }
223 }
224
225 pub fn from_size(size: Size) -> Self {
227 Self {
228 x: 0.0,
229 y: 0.0,
230 width: size.width,
231 height: size.height,
232 }
233 }
234
235 pub fn translate(&self, dx: f32, dy: f32) -> Self {
237 Self {
238 x: self.x + dx,
239 y: self.y + dy,
240 width: self.width,
241 height: self.height,
242 }
243 }
244
245 pub fn contains(&self, x: f32, y: f32) -> bool {
247 x >= self.x && y >= self.y && x <= self.x + self.width && y <= self.y + self.height
248 }
249
250 pub fn is_empty(&self) -> bool {
252 self.width <= 0.0 || self.height <= 0.0
253 }
254
255 pub fn intersect(&self, other: Rect) -> Option<Rect> {
257 let left = self.x.max(other.x);
258 let top = self.y.max(other.y);
259 let right = (self.x + self.width).min(other.x + other.width);
260 let bottom = (self.y + self.height).min(other.y + other.height);
261 let width = right - left;
262 let height = bottom - top;
263 if width <= 0.0 || height <= 0.0 {
264 None
265 } else {
266 Some(Rect {
267 x: left,
268 y: top,
269 width,
270 height,
271 })
272 }
273 }
274
275 pub fn union(&self, other: Rect) -> Rect {
277 let left = self.x.min(other.x);
278 let top = self.y.min(other.y);
279 let right = (self.x + self.width).max(other.x + other.width);
280 let bottom = (self.y + self.height).max(other.y + other.height);
281 Rect {
282 x: left,
283 y: top,
284 width: (right - left).max(0.0),
285 height: (bottom - top).max(0.0),
286 }
287 }
288}
289
290#[derive(Clone, Copy, Debug, Default, PartialEq)]
292pub struct EdgeInsets {
293 pub left: f32,
295 pub top: f32,
297 pub right: f32,
299 pub bottom: f32,
301}
302
303impl EdgeInsets {
304 pub fn uniform(all: f32) -> Self {
306 Self {
307 left: all,
308 top: all,
309 right: all,
310 bottom: all,
311 }
312 }
313
314 pub fn horizontal(horizontal: f32) -> Self {
316 Self {
317 left: horizontal,
318 right: horizontal,
319 ..Self::default()
320 }
321 }
322
323 pub fn vertical(vertical: f32) -> Self {
325 Self {
326 top: vertical,
327 bottom: vertical,
328 ..Self::default()
329 }
330 }
331
332 pub fn symmetric(horizontal: f32, vertical: f32) -> Self {
334 Self {
335 left: horizontal,
336 right: horizontal,
337 top: vertical,
338 bottom: vertical,
339 }
340 }
341
342 pub fn from_components(left: f32, top: f32, right: f32, bottom: f32) -> Self {
344 Self {
345 left,
346 top,
347 right,
348 bottom,
349 }
350 }
351
352 pub fn is_zero(&self) -> bool {
354 self.left == 0.0 && self.top == 0.0 && self.right == 0.0 && self.bottom == 0.0
355 }
356
357 pub fn horizontal_sum(&self) -> f32 {
359 self.left + self.right
360 }
361
362 pub fn vertical_sum(&self) -> f32 {
364 self.top + self.bottom
365 }
366
367 pub fn inset_rect(&self, size: Size) -> Rect {
370 Rect {
371 x: self.left,
372 y: self.top,
373 width: (size.width - self.horizontal_sum()).max(0.0),
374 height: (size.height - self.vertical_sum()).max(0.0),
375 }
376 }
377}
378
379impl AddAssign for EdgeInsets {
380 fn add_assign(&mut self, rhs: Self) {
381 self.left += rhs.left;
382 self.top += rhs.top;
383 self.right += rhs.right;
384 self.bottom += rhs.bottom;
385 }
386}
387
388#[derive(Clone, Copy, Debug, Default, PartialEq)]
390pub struct CornerRadii {
391 pub top_left: f32,
393 pub top_right: f32,
395 pub bottom_right: f32,
397 pub bottom_left: f32,
399}
400
401impl CornerRadii {
402 pub fn uniform(radius: f32) -> Self {
404 Self {
405 top_left: radius,
406 top_right: radius,
407 bottom_right: radius,
408 bottom_left: radius,
409 }
410 }
411}
412
413#[derive(Clone, Copy, Debug, PartialEq)]
415pub struct RoundedCornerShape {
416 radii: CornerRadii,
417}
418
419impl RoundedCornerShape {
420 pub fn new(top_left: f32, top_right: f32, bottom_right: f32, bottom_left: f32) -> Self {
422 Self {
423 radii: CornerRadii {
424 top_left,
425 top_right,
426 bottom_right,
427 bottom_left,
428 },
429 }
430 }
431
432 pub fn uniform(radius: f32) -> Self {
434 Self {
435 radii: CornerRadii::uniform(radius),
436 }
437 }
438
439 pub fn with_radii(radii: CornerRadii) -> Self {
441 Self { radii }
442 }
443
444 pub fn resolve(&self, width: f32, height: f32) -> CornerRadii {
447 let mut resolved = self.radii;
448 let max_width = (width / 2.0).max(0.0);
449 let max_height = (height / 2.0).max(0.0);
450 resolved.top_left = resolved.top_left.clamp(0.0, max_width).min(max_height);
451 resolved.top_right = resolved.top_right.clamp(0.0, max_width).min(max_height);
452 resolved.bottom_right = resolved.bottom_right.clamp(0.0, max_width).min(max_height);
453 resolved.bottom_left = resolved.bottom_left.clamp(0.0, max_width).min(max_height);
454 resolved
455 }
456
457 pub fn radii(&self) -> CornerRadii {
459 self.radii
460 }
461}
462
463#[derive(Clone, Copy, Debug, PartialEq)]
466pub struct TransformOrigin {
467 pub pivot_fraction_x: f32,
469 pub pivot_fraction_y: f32,
471}
472
473impl TransformOrigin {
474 pub const fn new(pivot_fraction_x: f32, pivot_fraction_y: f32) -> Self {
476 Self {
477 pivot_fraction_x,
478 pivot_fraction_y,
479 }
480 }
481
482 pub const CENTER: TransformOrigin = TransformOrigin::new(0.5, 0.5);
484}
485
486impl Default for TransformOrigin {
487 fn default() -> Self {
488 Self::CENTER
489 }
490}
491
492#[derive(Clone, Copy, Debug, Default, PartialEq)]
494pub enum LayerShape {
495 #[default]
497 Rectangle,
498 Rounded(RoundedCornerShape),
500}
501
502#[derive(Clone, Debug, PartialEq)]
505pub struct GraphicsLayer {
506 pub alpha: f32,
508 pub scale: f32,
510 pub scale_x: f32,
512 pub scale_y: f32,
514 pub rotation_x: f32,
516 pub rotation_y: f32,
518 pub rotation_z: f32,
520 pub camera_distance: f32,
523 pub transform_origin: TransformOrigin,
525 pub translation_x: f32,
527 pub translation_y: f32,
529 pub shadow_elevation: f32,
531 pub ambient_shadow_color: Color,
533 pub spot_shadow_color: Color,
535 pub shape: LayerShape,
537 pub clip: bool,
539 pub compositing_strategy: CompositingStrategy,
541 pub blend_mode: BlendMode,
543 pub color_filter: Option<ColorFilter>,
545 pub render_effect: Option<crate::render_effect::RenderEffect>,
548 pub backdrop_effect: Option<crate::render_effect::RenderEffect>,
552}
553
554impl GraphicsLayer {
555 pub const DEFAULT: Self = Self {
557 alpha: 1.0,
558 scale: 1.0,
559 scale_x: 1.0,
560 scale_y: 1.0,
561 rotation_x: 0.0,
562 rotation_y: 0.0,
563 rotation_z: 0.0,
564 camera_distance: 8.0,
565 transform_origin: TransformOrigin::CENTER,
566 translation_x: 0.0,
567 translation_y: 0.0,
568 shadow_elevation: 0.0,
569 ambient_shadow_color: Color::BLACK,
570 spot_shadow_color: Color::BLACK,
571 shape: LayerShape::Rectangle,
572 clip: false,
573 compositing_strategy: CompositingStrategy::Auto,
574 blend_mode: BlendMode::SrcOver,
575 color_filter: None,
576 render_effect: None,
577 backdrop_effect: None,
578 };
579
580 pub fn composite_alpha_8bit(alpha: f32) -> f32 {
604 (alpha.clamp(0.0, 1.0) * 255.0).floor() / 255.0
605 }
606}
607
608impl Default for GraphicsLayer {
609 fn default() -> Self {
610 Self::DEFAULT
611 }
612}
613
614#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
619pub enum BlendMode {
620 Clear,
622 Src,
624 Dst,
626 #[default]
628 SrcOver,
629 DstOver,
631 SrcIn,
633 DstIn,
635 SrcOut,
637 DstOut,
639 SrcAtop,
641 DstAtop,
643 Xor,
645 Plus,
647 Modulate,
649 Screen,
651 Overlay,
653 Darken,
655 Lighten,
657 ColorDodge,
659 ColorBurn,
661 HardLight,
663 SoftLight,
665 Difference,
667 Exclusion,
669 Multiply,
671 Hue,
673 Saturation,
675 Color,
677 Luminosity,
679}
680
681impl BlendMode {
682 pub const ALL: [BlendMode; 29] = [
684 BlendMode::Clear,
685 BlendMode::Src,
686 BlendMode::Dst,
687 BlendMode::SrcOver,
688 BlendMode::DstOver,
689 BlendMode::SrcIn,
690 BlendMode::DstIn,
691 BlendMode::SrcOut,
692 BlendMode::DstOut,
693 BlendMode::SrcAtop,
694 BlendMode::DstAtop,
695 BlendMode::Xor,
696 BlendMode::Plus,
697 BlendMode::Modulate,
698 BlendMode::Screen,
699 BlendMode::Overlay,
700 BlendMode::Darken,
701 BlendMode::Lighten,
702 BlendMode::ColorDodge,
703 BlendMode::ColorBurn,
704 BlendMode::HardLight,
705 BlendMode::SoftLight,
706 BlendMode::Difference,
707 BlendMode::Exclusion,
708 BlendMode::Multiply,
709 BlendMode::Hue,
710 BlendMode::Saturation,
711 BlendMode::Color,
712 BlendMode::Luminosity,
713 ];
714}
715
716#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
718pub enum CompositingStrategy {
719 #[default]
721 Auto,
722 Offscreen,
724 ModulateAlpha,
726}
727
728#[derive(Clone, Debug, PartialEq)]
730pub enum DrawPrimitive {
731 Content,
734 Blend {
736 primitive: Box<DrawPrimitive>,
738 blend_mode: BlendMode,
740 },
741 Rect {
743 rect: Rect,
745 brush: Brush,
747 stroke: Option<Stroke>,
750 },
751 RoundRect {
753 rect: Rect,
755 brush: Brush,
757 radii: CornerRadii,
759 stroke: Option<Stroke>,
762 },
763 Arc {
773 rect: Rect,
777 brush: Brush,
779 center: Point,
781 radius: f32,
784 start_angle: f32,
787 sweep_angle: f32,
789 stroke: Option<Stroke>,
791 inner_radius: f32,
793 },
794 Line {
797 rect: Rect,
800 brush: Brush,
802 start: Point,
804 end: Point,
806 stroke: Stroke,
808 },
809 Trapezoid {
812 rect: Rect,
815 brush: Brush,
817 trapezoid: Trapezoid,
819 },
820 Image {
822 rect: Rect,
824 image: ImageBitmap,
826 alpha: f32,
828 color_filter: Option<ColorFilter>,
830 sampling: ImageSampling,
832 src_rect: Option<Rect>,
836 },
837 Text(Box<TextPrimitive>),
839 Shadow(ShadowPrimitive),
842}
843
844#[derive(Clone, Debug, PartialEq)]
853pub struct TextPrimitive {
854 pub rect: Rect,
857 pub text: std::rc::Rc<str>,
860 pub style: DrawTextStyle,
862 pub color: Color,
866}
867
868fn shared_text_str(text: &str) -> Rc<str> {
878 use std::hash::{Hash, Hasher};
879
880 const POOL_CAPACITY: usize = 256;
881
882 let mut hasher = crate::FxHasher::default();
883 text.hash(&mut hasher);
884 let key = hasher.finish();
885
886 crate::thread_state::with(|state| {
887 let mut pool = state.shared_texts.borrow_mut();
888 if let Some(shared) = pool.get(&key)
889 && &**shared == text
890 {
891 return Rc::clone(shared);
892 }
893 let shared: Rc<str> = Rc::from(text);
894 if pool.len() >= POOL_CAPACITY {
895 pool.clear();
896 }
897 pool.insert(key, Rc::clone(&shared));
898 shared
899 })
900}
901
902impl DrawPrimitive {
903 pub fn translate(self, dx: f32, dy: f32) -> Self {
906 match self {
907 DrawPrimitive::Content => DrawPrimitive::Content,
908 DrawPrimitive::Blend {
909 primitive,
910 blend_mode,
911 } => DrawPrimitive::Blend {
912 primitive: Box::new(primitive.translate(dx, dy)),
913 blend_mode,
914 },
915 DrawPrimitive::Rect {
916 rect,
917 brush,
918 stroke,
919 } => DrawPrimitive::Rect {
920 rect: rect.translate(dx, dy),
921 brush,
922 stroke,
923 },
924 DrawPrimitive::RoundRect {
925 rect,
926 brush,
927 radii,
928 stroke,
929 } => DrawPrimitive::RoundRect {
930 rect: rect.translate(dx, dy),
931 brush,
932 radii,
933 stroke,
934 },
935 DrawPrimitive::Arc {
936 rect,
937 brush,
938 center,
939 radius,
940 start_angle,
941 sweep_angle,
942 stroke,
943 inner_radius,
944 } => DrawPrimitive::Arc {
945 rect: rect.translate(dx, dy),
946 brush,
947 center: Point::new(center.x + dx, center.y + dy),
948 radius,
949 start_angle,
950 sweep_angle,
951 stroke,
952 inner_radius,
953 },
954 DrawPrimitive::Line {
955 rect,
956 brush,
957 start,
958 end,
959 stroke,
960 } => DrawPrimitive::Line {
961 rect: rect.translate(dx, dy),
962 brush,
963 start: Point::new(start.x + dx, start.y + dy),
964 end: Point::new(end.x + dx, end.y + dy),
965 stroke,
966 },
967 DrawPrimitive::Trapezoid {
968 rect,
969 brush,
970 trapezoid,
971 } => DrawPrimitive::Trapezoid {
972 rect: rect.translate(dx, dy),
973 brush,
974 trapezoid: trapezoid.translate(dx, dy),
975 },
976 DrawPrimitive::Image {
977 rect,
978 image,
979 alpha,
980 color_filter,
981 sampling,
982 src_rect,
983 } => DrawPrimitive::Image {
984 rect: rect.translate(dx, dy),
985 image,
986 alpha,
987 color_filter,
988 sampling,
989 src_rect,
990 },
991 DrawPrimitive::Text(mut text) => {
992 text.rect = text.rect.translate(dx, dy);
993 DrawPrimitive::Text(text)
994 }
995 DrawPrimitive::Shadow(ShadowPrimitive::Drop {
996 shape,
997 cutout,
998 blur_radius,
999 blend_mode,
1000 }) => DrawPrimitive::Shadow(ShadowPrimitive::Drop {
1001 shape: Box::new(shape.translate(dx, dy)),
1002 cutout: cutout.map(|cutout| Box::new(cutout.translate(dx, dy))),
1003 blur_radius,
1004 blend_mode,
1005 }),
1006 DrawPrimitive::Shadow(ShadowPrimitive::Inner {
1007 fill,
1008 cutout,
1009 blur_radius,
1010 blend_mode,
1011 clip_rect,
1012 }) => DrawPrimitive::Shadow(ShadowPrimitive::Inner {
1013 fill: Box::new(fill.translate(dx, dy)),
1014 cutout: Box::new(cutout.translate(dx, dy)),
1015 blur_radius,
1016 blend_mode,
1017 clip_rect: clip_rect.translate(dx, dy),
1018 }),
1019 }
1020 }
1021}
1022
1023#[derive(Clone, Debug, PartialEq)]
1026pub enum ShadowPrimitive {
1027 Drop {
1031 shape: Box<DrawPrimitive>,
1033 cutout: Option<Box<DrawPrimitive>>,
1035 blur_radius: f32,
1037 blend_mode: BlendMode,
1039 },
1040 Inner {
1042 fill: Box<DrawPrimitive>,
1044 cutout: Box<DrawPrimitive>,
1046 blur_radius: f32,
1048 blend_mode: BlendMode,
1050 clip_rect: Rect,
1052 },
1053}
1054
1055pub trait DrawScope {
1059 fn size(&self) -> Size;
1061 fn draw_content(&mut self);
1065 fn draw_rect(&mut self, brush: Brush);
1067 fn draw_rect_blend(&mut self, brush: Brush, blend_mode: BlendMode);
1069 fn draw_rect_at(&mut self, rect: Rect, brush: Brush);
1071 fn draw_rect_at_blend(&mut self, rect: Rect, brush: Brush, blend_mode: BlendMode);
1073 fn draw_round_rect(&mut self, brush: Brush, radii: CornerRadii);
1075 fn draw_round_rect_blend(&mut self, brush: Brush, radii: CornerRadii, blend_mode: BlendMode);
1077 fn draw_round_rect_at(&mut self, rect: Rect, brush: Brush, radii: CornerRadii);
1079 fn draw_circle(&mut self, brush: Brush, center: Point, radius: f32);
1081 fn draw_circle_blend(
1083 &mut self,
1084 brush: Brush,
1085 center: Point,
1086 radius: f32,
1087 blend_mode: BlendMode,
1088 );
1089
1090 fn draw_rect_stroked(&mut self, brush: Brush, stroke: Stroke);
1092 fn draw_rect_stroked_blend(&mut self, brush: Brush, stroke: Stroke, blend_mode: BlendMode);
1094 fn draw_rect_at_stroked(&mut self, rect: Rect, brush: Brush, stroke: Stroke);
1096 fn draw_rect_at_stroked_blend(
1098 &mut self,
1099 rect: Rect,
1100 brush: Brush,
1101 stroke: Stroke,
1102 blend_mode: BlendMode,
1103 );
1104 fn draw_round_rect_stroked(&mut self, brush: Brush, radii: CornerRadii, stroke: Stroke);
1106 fn draw_round_rect_stroked_blend(
1108 &mut self,
1109 brush: Brush,
1110 radii: CornerRadii,
1111 stroke: Stroke,
1112 blend_mode: BlendMode,
1113 );
1114 fn draw_round_rect_at_stroked(
1116 &mut self,
1117 rect: Rect,
1118 brush: Brush,
1119 radii: CornerRadii,
1120 stroke: Stroke,
1121 );
1122 fn draw_round_rect_at_stroked_blend(
1124 &mut self,
1125 rect: Rect,
1126 brush: Brush,
1127 radii: CornerRadii,
1128 stroke: Stroke,
1129 blend_mode: BlendMode,
1130 );
1131 fn draw_circle_stroked(&mut self, brush: Brush, center: Point, radius: f32, stroke: Stroke);
1134 fn draw_circle_stroked_blend(
1136 &mut self,
1137 brush: Brush,
1138 center: Point,
1139 radius: f32,
1140 stroke: Stroke,
1141 blend_mode: BlendMode,
1142 );
1143
1144 fn draw_arc(
1157 &mut self,
1158 brush: Brush,
1159 center: Point,
1160 radius: f32,
1161 start_angle: f32,
1162 sweep_angle: f32,
1163 stroke: Stroke,
1164 );
1165 #[expect(clippy::too_many_arguments)]
1167 fn draw_arc_blend(
1168 &mut self,
1169 brush: Brush,
1170 center: Point,
1171 radius: f32,
1172 start_angle: f32,
1173 sweep_angle: f32,
1174 stroke: Stroke,
1175 blend_mode: BlendMode,
1176 );
1177
1178 fn draw_line(&mut self, brush: Brush, start: Point, end: Point, stroke: Stroke);
1184 fn draw_line_blend(
1186 &mut self,
1187 brush: Brush,
1188 start: Point,
1189 end: Point,
1190 stroke: Stroke,
1191 blend_mode: BlendMode,
1192 );
1193
1194 fn draw_path(&mut self, path: &Path, brush: Brush, style: DrawStyle);
1203 fn draw_path_blend(
1205 &mut self,
1206 path: &Path,
1207 brush: Brush,
1208 style: DrawStyle,
1209 blend_mode: BlendMode,
1210 );
1211
1212 fn draw_annular_sector(
1221 &mut self,
1222 brush: Brush,
1223 center: Point,
1224 inner_radius: f32,
1225 outer_radius: f32,
1226 start_angle: f32,
1227 sweep_angle: f32,
1228 );
1229 #[expect(clippy::too_many_arguments)]
1231 fn draw_annular_sector_blend(
1232 &mut self,
1233 brush: Brush,
1234 center: Point,
1235 inner_radius: f32,
1236 outer_radius: f32,
1237 start_angle: f32,
1238 sweep_angle: f32,
1239 blend_mode: BlendMode,
1240 );
1241
1242 fn draw_image(&mut self, image: ImageBitmap);
1244 fn draw_image_blend(&mut self, image: ImageBitmap, blend_mode: BlendMode);
1246 fn draw_image_at(
1249 &mut self,
1250 rect: Rect,
1251 image: ImageBitmap,
1252 alpha: f32,
1253 color_filter: Option<ColorFilter>,
1254 );
1255 fn draw_image_at_sampled(
1257 &mut self,
1258 rect: Rect,
1259 image: ImageBitmap,
1260 alpha: f32,
1261 color_filter: Option<ColorFilter>,
1262 sampling: ImageSampling,
1263 );
1264 fn draw_image_at_blend(
1266 &mut self,
1267 rect: Rect,
1268 image: ImageBitmap,
1269 alpha: f32,
1270 color_filter: Option<ColorFilter>,
1271 blend_mode: BlendMode,
1272 );
1273 fn draw_image_src(
1276 &mut self,
1277 image: ImageBitmap,
1278 src_rect: Rect,
1279 dst_rect: Rect,
1280 alpha: f32,
1281 color_filter: Option<ColorFilter>,
1282 );
1283 fn draw_image_src_sampled(
1285 &mut self,
1286 image: ImageBitmap,
1287 src_rect: Rect,
1288 dst_rect: Rect,
1289 alpha: f32,
1290 color_filter: Option<ColorFilter>,
1291 sampling: ImageSampling,
1292 );
1293 fn draw_image_src_blend(
1295 &mut self,
1296 image: ImageBitmap,
1297 src_rect: Rect,
1298 dst_rect: Rect,
1299 alpha: f32,
1300 color_filter: Option<ColorFilter>,
1301 blend_mode: BlendMode,
1302 );
1303 fn draw_vector_path(&mut self, path: &crate::VectorPath, brush: Brush);
1311 fn draw_svg_path(&mut self, d: &str, brush: Brush) {
1317 if let Ok(path) = crate::VectorPath::parse(d) {
1318 self.draw_vector_path(&path, brush);
1319 }
1320 }
1321
1322 fn measure_text(&self, text: &str, style: &DrawTextStyle) -> TextMeasurement;
1329
1330 fn draw_text(&mut self, brush: Brush, text: &str, style: &DrawTextStyle) {
1333 self.draw_text_at(Rect::from_size(self.size()), brush, text, style);
1334 }
1335
1336 fn draw_text_at(&mut self, rect: Rect, brush: Brush, text: &str, style: &DrawTextStyle);
1343
1344 fn draw_text_from(&mut self, top_left: Point, brush: Brush, text: &str, style: &DrawTextStyle) {
1350 if text.is_empty() {
1351 return;
1352 }
1353 let measurement = self.measure_text(text, style);
1354 self.draw_text_at(
1355 Rect::from_origin_size(top_left, measurement.size),
1356 brush,
1357 text,
1358 &DrawTextStyle {
1359 align: TextAlign::Left,
1360 vertical_align: TextVerticalAlign::Top,
1361 ..style.clone()
1362 },
1363 );
1364 }
1365
1366 fn into_primitives(self) -> Vec<DrawPrimitive>;
1369}
1370
1371pub fn align_text_block(rect: Rect, measurement: TextMeasurement, style: &DrawTextStyle) -> Point {
1377 let x = match style.align {
1378 TextAlign::Left => rect.x,
1379 TextAlign::Center => rect.x + (rect.width - measurement.size.width) * 0.5,
1380 TextAlign::Right => rect.x + rect.width - measurement.size.width,
1381 };
1382 let y = match style.vertical_align {
1383 TextVerticalAlign::Top => rect.y,
1384 TextVerticalAlign::Center => rect.y + (rect.height - measurement.size.height) * 0.5,
1385 TextVerticalAlign::Bottom => rect.y + rect.height - measurement.size.height,
1386 TextVerticalAlign::Baseline => rect.y - measurement.first_baseline,
1387 };
1388 Point::new(x, y)
1389}
1390
1391#[derive(Default)]
1394pub struct DrawScopeDefault {
1395 size: Size,
1396 recording: CommandRecorder,
1397 text_measurer: Option<Rc<dyn DrawTextMeasurer>>,
1398}
1399
1400impl DrawScopeDefault {
1401 pub fn new(size: Size) -> Self {
1403 Self::with_storage(size, None, CommandRecording::default())
1404 }
1405
1406 pub fn with_text_measurer(size: Size, text_measurer: Rc<dyn DrawTextMeasurer>) -> Self {
1411 Self::with_storage(size, Some(text_measurer), CommandRecording::default())
1412 }
1413
1414 pub fn with_text_measurer_reusing(
1417 size: Size,
1418 text_measurer: Rc<dyn DrawTextMeasurer>,
1419 storage: CommandRecording,
1420 ) -> Self {
1421 Self::with_storage(size, Some(text_measurer), storage)
1422 }
1423
1424 fn with_storage(
1425 size: Size,
1426 text_measurer: Option<Rc<dyn DrawTextMeasurer>>,
1427 recording: CommandRecording,
1428 ) -> Self {
1429 let recording = CommandRecorder::reusing(recording);
1430 Self {
1431 size,
1432 recording,
1433 text_measurer,
1434 }
1435 }
1436
1437 pub fn content_marker_count(&self) -> u32 {
1439 self.recording.content_markers()
1440 }
1441
1442 pub fn inset(&mut self, insets: EdgeInsets, draw: impl FnOnce(&mut DrawScopeDefault)) {
1446 let inner = insets.inset_rect(self.size);
1447 let mut scope = Self::with_storage(
1448 Size {
1449 width: inner.width,
1450 height: inner.height,
1451 },
1452 self.text_measurer.clone(),
1453 CommandRecording::default(),
1454 );
1455 draw(&mut scope);
1456 let recording = scope.finish();
1457 self.push_recorded(
1458 recording
1459 .primitives_with_markers()
1460 .map(|primitive| primitive.translate(inner.x, inner.y)),
1461 );
1462 }
1463
1464 pub fn push_recorded(&mut self, primitives: impl IntoIterator<Item = DrawPrimitive>) {
1466 for primitive in primitives {
1467 self.recording.push_primitive(primitive);
1468 }
1469 }
1470
1471 pub fn finish(self) -> CommandRecording {
1474 self.recording.finish()
1475 }
1476
1477 fn push_blended_primitive(&mut self, primitive: DrawPrimitive, blend_mode: BlendMode) {
1478 if blend_mode != BlendMode::SrcOver {
1479 self.recording.push_other(DrawPrimitive::Blend {
1480 primitive: Box::new(primitive),
1481 blend_mode,
1482 });
1483 return;
1484 }
1485 self.recording.push_other(primitive);
1486 }
1487
1488 #[expect(clippy::too_many_arguments)]
1489 #[inline]
1490 fn push_arc(
1491 &mut self,
1492 brush: Brush,
1493 center: Point,
1494 radius: f32,
1495 start_angle: f32,
1496 sweep_angle: f32,
1497 stroke: Option<Stroke>,
1498 inner_radius: f32,
1499 blend_mode: BlendMode,
1500 ) {
1501 let args = ArcRecordArgs {
1502 brush: &brush,
1503 center,
1504 radius,
1505 start_angle,
1506 sweep_angle,
1507 stroke,
1508 inner_radius,
1509 blend_mode,
1510 };
1511 let geometry = normalized_band(&args);
1512 if geometry.is_degenerate() {
1513 return;
1514 }
1515 self.recording.push_scope_arc(&args, &geometry);
1516 }
1517
1518 fn push_path_fill(&mut self, path: &Path, brush: &Brush, blend_mode: BlendMode) {
1522 let brush_rect = Rect::from_size(self.size);
1523 let recording = &mut self.recording;
1524 let sliced = crate::thread_state::with(|state| {
1525 let mut slicer = state.path_slicer.borrow_mut();
1526 let contours = path
1527 .contours()
1528 .iter()
1529 .map(|contour| contour.points.as_slice());
1530 slicer
1531 .slice(contours, PathFillRule::NonZero)
1532 .map(|slices| recording.push_trapezoids(brush_rect, slices, brush, blend_mode))
1533 .is_some()
1534 });
1535 if !sliced {
1536 self.push_vector_path(
1537 &path.to_vector_path(PathFillRule::NonZero),
1538 brush,
1539 brush_rect,
1540 blend_mode,
1541 );
1542 }
1543 }
1544
1545 fn push_vector_path(
1549 &mut self,
1550 path: &crate::VectorPath,
1551 brush: &Brush,
1552 brush_rect: Rect,
1553 blend_mode: BlendMode,
1554 ) {
1555 const MAX_MASK_PIXELS: f32 = 4096.0;
1556
1557 if path.is_empty() || !paints(brush) {
1558 return;
1559 }
1560 let bounds = path.bounds();
1561 if bounds.width <= 0.0 || bounds.height <= 0.0 {
1562 return;
1563 }
1564
1565 let origin = Point::new(bounds.x.floor() - 1.0, bounds.y.floor() - 1.0);
1566 let rect_width = (bounds.x + bounds.width).ceil() - origin.x + 1.0;
1567 let rect_height = (bounds.y + bounds.height).ceil() - origin.y + 1.0;
1568 let mask_width = (rect_width * VECTOR_PATH_SUPERSAMPLE)
1569 .ceil()
1570 .clamp(1.0, MAX_MASK_PIXELS) as usize;
1571 let mask_height = (rect_height * VECTOR_PATH_SUPERSAMPLE)
1572 .ceil()
1573 .clamp(1.0, MAX_MASK_PIXELS) as usize;
1574 let mask_size = (mask_width, mask_height);
1575
1576 let key = vector_path_mask_key(path, origin, mask_size, brush, brush_rect);
1577 let image = match vector_path_mask_cache_get(key) {
1578 Some(image) => image,
1579 None => {
1580 let Some(image) = rasterize_vector_path(path, brush, brush_rect, origin, mask_size)
1581 else {
1582 return;
1583 };
1584 vector_path_mask_cache_put(key, image.clone());
1585 image
1586 }
1587 };
1588
1589 let image = DrawPrimitive::Image {
1590 rect: Rect {
1591 x: origin.x,
1592 y: origin.y,
1593 width: rect_width,
1594 height: rect_height,
1595 },
1596 image,
1597 alpha: 1.0,
1598 color_filter: None,
1599 sampling: ImageSampling::Linear,
1600 src_rect: None,
1601 };
1602 self.recording
1603 .push_other(if blend_mode == BlendMode::SrcOver {
1604 image
1605 } else {
1606 DrawPrimitive::Blend {
1607 primitive: Box::new(image),
1608 blend_mode,
1609 }
1610 });
1611 }
1612}
1613
1614impl DrawScope for DrawScopeDefault {
1615 fn size(&self) -> Size {
1616 self.size
1617 }
1618
1619 fn draw_content(&mut self) {
1620 self.recording.push_content();
1621 }
1622
1623 fn draw_rect(&mut self, brush: Brush) {
1624 self.draw_rect_blend(brush, BlendMode::SrcOver);
1625 }
1626
1627 fn draw_rect_blend(&mut self, brush: Brush, blend_mode: BlendMode) {
1628 self.recording
1629 .push_rect(Rect::from_size(self.size), &brush, None, blend_mode);
1630 }
1631
1632 fn draw_rect_at(&mut self, rect: Rect, brush: Brush) {
1633 self.draw_rect_at_blend(rect, brush, BlendMode::SrcOver);
1634 }
1635
1636 fn draw_rect_at_blend(&mut self, rect: Rect, brush: Brush, blend_mode: BlendMode) {
1637 self.recording.push_rect(rect, &brush, None, blend_mode);
1638 }
1639
1640 fn draw_round_rect(&mut self, brush: Brush, radii: CornerRadii) {
1641 self.draw_round_rect_blend(brush, radii, BlendMode::SrcOver);
1642 }
1643
1644 fn draw_round_rect_blend(&mut self, brush: Brush, radii: CornerRadii, blend_mode: BlendMode) {
1645 self.recording
1646 .push_round_rect(Rect::from_size(self.size), &brush, radii, None, blend_mode);
1647 }
1648
1649 fn draw_round_rect_at(&mut self, rect: Rect, brush: Brush, radii: CornerRadii) {
1650 self.recording
1651 .push_round_rect(rect, &brush, radii, None, BlendMode::SrcOver);
1652 }
1653
1654 fn draw_rect_stroked(&mut self, brush: Brush, stroke: Stroke) {
1655 self.draw_rect_stroked_blend(brush, stroke, BlendMode::SrcOver);
1656 }
1657
1658 fn draw_rect_stroked_blend(&mut self, brush: Brush, stroke: Stroke, blend_mode: BlendMode) {
1659 self.draw_rect_at_stroked_blend(Rect::from_size(self.size), brush, stroke, blend_mode);
1660 }
1661
1662 fn draw_rect_at_stroked(&mut self, rect: Rect, brush: Brush, stroke: Stroke) {
1663 self.draw_rect_at_stroked_blend(rect, brush, stroke, BlendMode::SrcOver);
1664 }
1665
1666 fn draw_rect_at_stroked_blend(
1667 &mut self,
1668 rect: Rect,
1669 brush: Brush,
1670 stroke: Stroke,
1671 blend_mode: BlendMode,
1672 ) {
1673 if !stroke.is_visible() {
1674 return;
1675 }
1676 self.recording
1677 .push_rect(rect, &brush, Some(stroke), blend_mode);
1678 }
1679
1680 fn draw_round_rect_stroked(&mut self, brush: Brush, radii: CornerRadii, stroke: Stroke) {
1681 self.draw_round_rect_stroked_blend(brush, radii, stroke, BlendMode::SrcOver);
1682 }
1683
1684 fn draw_round_rect_stroked_blend(
1685 &mut self,
1686 brush: Brush,
1687 radii: CornerRadii,
1688 stroke: Stroke,
1689 blend_mode: BlendMode,
1690 ) {
1691 self.draw_round_rect_at_stroked_blend(
1692 Rect::from_size(self.size),
1693 brush,
1694 radii,
1695 stroke,
1696 blend_mode,
1697 );
1698 }
1699
1700 fn draw_round_rect_at_stroked(
1701 &mut self,
1702 rect: Rect,
1703 brush: Brush,
1704 radii: CornerRadii,
1705 stroke: Stroke,
1706 ) {
1707 self.draw_round_rect_at_stroked_blend(rect, brush, radii, stroke, BlendMode::SrcOver);
1708 }
1709
1710 fn draw_round_rect_at_stroked_blend(
1711 &mut self,
1712 rect: Rect,
1713 brush: Brush,
1714 radii: CornerRadii,
1715 stroke: Stroke,
1716 blend_mode: BlendMode,
1717 ) {
1718 if !stroke.is_visible() {
1719 return;
1720 }
1721 self.recording
1722 .push_round_rect(rect, &brush, radii, Some(stroke), blend_mode);
1723 }
1724
1725 fn draw_circle_stroked(&mut self, brush: Brush, center: Point, radius: f32, stroke: Stroke) {
1726 self.draw_circle_stroked_blend(brush, center, radius, stroke, BlendMode::SrcOver);
1727 }
1728
1729 fn draw_circle_stroked_blend(
1730 &mut self,
1731 brush: Brush,
1732 center: Point,
1733 radius: f32,
1734 stroke: Stroke,
1735 blend_mode: BlendMode,
1736 ) {
1737 if !stroke.is_visible() || !radius.is_finite() {
1738 return;
1739 }
1740 let radius = radius.max(0.0);
1741 let diameter = radius * 2.0;
1742 self.draw_round_rect_at_stroked_blend(
1743 Rect {
1744 x: center.x - radius,
1745 y: center.y - radius,
1746 width: diameter,
1747 height: diameter,
1748 },
1749 brush,
1750 CornerRadii::uniform(radius),
1751 stroke,
1752 blend_mode,
1753 );
1754 }
1755
1756 fn draw_arc(
1757 &mut self,
1758 brush: Brush,
1759 center: Point,
1760 radius: f32,
1761 start_angle: f32,
1762 sweep_angle: f32,
1763 stroke: Stroke,
1764 ) {
1765 self.draw_arc_blend(
1766 brush,
1767 center,
1768 radius,
1769 start_angle,
1770 sweep_angle,
1771 stroke,
1772 BlendMode::SrcOver,
1773 );
1774 }
1775
1776 fn draw_arc_blend(
1777 &mut self,
1778 brush: Brush,
1779 center: Point,
1780 radius: f32,
1781 start_angle: f32,
1782 sweep_angle: f32,
1783 stroke: Stroke,
1784 blend_mode: BlendMode,
1785 ) {
1786 if !stroke.is_visible() {
1787 return;
1788 }
1789 self.push_arc(
1790 brush,
1791 center,
1792 radius,
1793 start_angle,
1794 sweep_angle,
1795 Some(stroke),
1796 0.0,
1797 blend_mode,
1798 );
1799 }
1800
1801 fn draw_line(&mut self, brush: Brush, start: Point, end: Point, stroke: Stroke) {
1802 self.draw_line_blend(brush, start, end, stroke, BlendMode::SrcOver);
1803 }
1804
1805 fn draw_line_blend(
1806 &mut self,
1807 brush: Brush,
1808 start: Point,
1809 end: Point,
1810 stroke: Stroke,
1811 blend_mode: BlendMode,
1812 ) {
1813 let line = LineGeometry::new(start, end, stroke);
1814 if line.is_degenerate() {
1815 return;
1816 }
1817 self.recording.push_line(&line, &brush, stroke, blend_mode);
1818 }
1819
1820 fn draw_path(&mut self, path: &Path, brush: Brush, style: DrawStyle) {
1821 self.draw_path_blend(path, brush, style, BlendMode::SrcOver);
1822 }
1823
1824 fn draw_path_blend(
1825 &mut self,
1826 path: &Path,
1827 brush: Brush,
1828 style: DrawStyle,
1829 blend_mode: BlendMode,
1830 ) {
1831 let (stroke, dash) = match style {
1832 DrawStyle::Fill => {
1833 self.push_path_fill(path, &brush, blend_mode);
1834 return;
1835 }
1836 DrawStyle::Stroke(stroke) => (stroke, None),
1837 DrawStyle::DashedStroke(stroke, dash) => (stroke, Some(dash)),
1838 };
1839 if !stroke.is_visible() {
1840 return;
1841 }
1842 let recording = &mut self.recording;
1843 let mut push = |line: LineGeometry| {
1844 if !line.is_degenerate() {
1845 let stroke = Stroke {
1846 cap: line.cap,
1847 ..stroke
1848 };
1849 recording.push_line(&line, &brush, stroke, blend_mode);
1850 }
1851 };
1852 for contour in path.contours() {
1853 let Some(dash) = dash else {
1854 crate::for_each_stroke_line(&contour.points, contour.closed, stroke, &mut push);
1855 continue;
1856 };
1857 let closing = contour
1858 .closed
1859 .then(|| contour.points.first().copied())
1860 .flatten();
1861 let points: std::borrow::Cow<'_, [Point]> = match closing {
1862 Some(first) => {
1863 std::borrow::Cow::Owned(contour.points.iter().copied().chain([first]).collect())
1864 }
1865 None => std::borrow::Cow::Borrowed(&contour.points),
1866 };
1867 dash.for_each_dash(&points, |piece| {
1868 crate::for_each_stroke_line(piece, false, stroke, &mut push);
1869 });
1870 }
1871 }
1872
1873 fn draw_annular_sector(
1874 &mut self,
1875 brush: Brush,
1876 center: Point,
1877 inner_radius: f32,
1878 outer_radius: f32,
1879 start_angle: f32,
1880 sweep_angle: f32,
1881 ) {
1882 self.draw_annular_sector_blend(
1883 brush,
1884 center,
1885 inner_radius,
1886 outer_radius,
1887 start_angle,
1888 sweep_angle,
1889 BlendMode::SrcOver,
1890 );
1891 }
1892
1893 fn draw_annular_sector_blend(
1894 &mut self,
1895 brush: Brush,
1896 center: Point,
1897 inner_radius: f32,
1898 outer_radius: f32,
1899 start_angle: f32,
1900 sweep_angle: f32,
1901 blend_mode: BlendMode,
1902 ) {
1903 self.push_arc(
1904 brush,
1905 center,
1906 outer_radius,
1907 start_angle,
1908 sweep_angle,
1909 None,
1910 inner_radius,
1911 blend_mode,
1912 );
1913 }
1914
1915 fn draw_circle(&mut self, brush: Brush, center: Point, radius: f32) {
1916 self.draw_circle_blend(brush, center, radius, BlendMode::SrcOver);
1917 }
1918
1919 fn draw_circle_blend(
1920 &mut self,
1921 brush: Brush,
1922 center: Point,
1923 radius: f32,
1924 blend_mode: BlendMode,
1925 ) {
1926 let radius = radius.max(0.0);
1927 let diameter = radius * 2.0;
1928 self.recording.push_round_rect(
1929 Rect {
1930 x: center.x - radius,
1931 y: center.y - radius,
1932 width: diameter,
1933 height: diameter,
1934 },
1935 &brush,
1936 CornerRadii::uniform(radius),
1937 None,
1938 blend_mode,
1939 );
1940 }
1941
1942 fn draw_image(&mut self, image: ImageBitmap) {
1943 self.draw_image_blend(image, BlendMode::SrcOver);
1944 }
1945
1946 fn draw_image_blend(&mut self, image: ImageBitmap, blend_mode: BlendMode) {
1947 self.push_blended_primitive(
1948 DrawPrimitive::Image {
1949 rect: Rect::from_size(self.size),
1950 image,
1951 alpha: 1.0,
1952 color_filter: None,
1953 sampling: ImageSampling::Nearest,
1954 src_rect: None,
1955 },
1956 blend_mode,
1957 );
1958 }
1959
1960 fn draw_image_at(
1961 &mut self,
1962 rect: Rect,
1963 image: ImageBitmap,
1964 alpha: f32,
1965 color_filter: Option<ColorFilter>,
1966 ) {
1967 self.draw_image_at_sampled(rect, image, alpha, color_filter, ImageSampling::Nearest);
1968 }
1969
1970 fn draw_image_at_sampled(
1971 &mut self,
1972 rect: Rect,
1973 image: ImageBitmap,
1974 alpha: f32,
1975 color_filter: Option<ColorFilter>,
1976 sampling: ImageSampling,
1977 ) {
1978 self.push_blended_primitive(
1979 DrawPrimitive::Image {
1980 rect,
1981 image,
1982 alpha: alpha.clamp(0.0, 1.0),
1983 color_filter,
1984 sampling,
1985 src_rect: None,
1986 },
1987 BlendMode::SrcOver,
1988 );
1989 }
1990
1991 fn draw_image_at_blend(
1992 &mut self,
1993 rect: Rect,
1994 image: ImageBitmap,
1995 alpha: f32,
1996 color_filter: Option<ColorFilter>,
1997 blend_mode: BlendMode,
1998 ) {
1999 self.push_blended_primitive(
2000 DrawPrimitive::Image {
2001 rect,
2002 image,
2003 alpha: alpha.clamp(0.0, 1.0),
2004 color_filter,
2005 sampling: ImageSampling::Nearest,
2006 src_rect: None,
2007 },
2008 blend_mode,
2009 );
2010 }
2011
2012 fn draw_image_src(
2013 &mut self,
2014 image: ImageBitmap,
2015 src_rect: Rect,
2016 dst_rect: Rect,
2017 alpha: f32,
2018 color_filter: Option<ColorFilter>,
2019 ) {
2020 self.draw_image_src_blend(
2021 image,
2022 src_rect,
2023 dst_rect,
2024 alpha,
2025 color_filter,
2026 BlendMode::SrcOver,
2027 );
2028 }
2029
2030 fn draw_image_src_sampled(
2031 &mut self,
2032 image: ImageBitmap,
2033 src_rect: Rect,
2034 dst_rect: Rect,
2035 alpha: f32,
2036 color_filter: Option<ColorFilter>,
2037 sampling: ImageSampling,
2038 ) {
2039 self.push_blended_primitive(
2040 DrawPrimitive::Image {
2041 rect: dst_rect,
2042 image,
2043 alpha: alpha.clamp(0.0, 1.0),
2044 color_filter,
2045 sampling,
2046 src_rect: Some(src_rect),
2047 },
2048 BlendMode::SrcOver,
2049 );
2050 }
2051
2052 fn draw_image_src_blend(
2053 &mut self,
2054 image: ImageBitmap,
2055 src_rect: Rect,
2056 dst_rect: Rect,
2057 alpha: f32,
2058 color_filter: Option<ColorFilter>,
2059 blend_mode: BlendMode,
2060 ) {
2061 self.push_blended_primitive(
2062 DrawPrimitive::Image {
2063 rect: dst_rect,
2064 image,
2065 alpha: alpha.clamp(0.0, 1.0),
2066 color_filter,
2067 sampling: ImageSampling::Nearest,
2068 src_rect: Some(src_rect),
2069 },
2070 blend_mode,
2071 );
2072 }
2073
2074 fn draw_vector_path(&mut self, path: &crate::VectorPath, brush: Brush) {
2075 self.push_vector_path(path, &brush, Rect::from_size(self.size), BlendMode::SrcOver);
2076 }
2077
2078 fn measure_text(&self, text: &str, style: &DrawTextStyle) -> TextMeasurement {
2079 match &self.text_measurer {
2080 Some(measurer) => measurer.measure_text(text, style),
2081 None => estimate_text_measurement(text, style),
2082 }
2083 }
2084
2085 fn draw_text_at(&mut self, rect: Rect, brush: Brush, text: &str, style: &DrawTextStyle) {
2086 if text.is_empty() {
2087 return;
2088 }
2089 let Some(color) = solid_fill_color(&brush) else {
2090 return;
2091 };
2092 if color.3 <= 0.0 {
2093 return;
2094 }
2095 let measurement = self.measure_text(text, style);
2096 if !(measurement.size.width > 0.0 && measurement.size.height > 0.0) {
2097 return;
2098 }
2099 let origin = align_text_block(rect, measurement, style);
2100 if !origin.x.is_finite() || !origin.y.is_finite() {
2101 return;
2102 }
2103 self.recording
2104 .push_other(DrawPrimitive::Text(Box::new(TextPrimitive {
2105 rect: Rect::from_origin_size(origin, measurement.size),
2106 text: shared_text_str(text),
2107 style: style.clone(),
2108 color,
2109 })));
2110 }
2111
2112 fn into_primitives(self) -> Vec<DrawPrimitive> {
2113 self.finish().into_primitives_with_markers()
2114 }
2115}
2116
2117fn solid_fill_color(brush: &Brush) -> Option<Color> {
2122 match brush {
2123 Brush::Solid(color) => Some(*color),
2124 Brush::LinearGradient { colors, .. }
2125 | Brush::RadialGradient { colors, .. }
2126 | Brush::SweepGradient { colors, .. } => colors.first().copied(),
2127 }
2128}
2129
2130#[cfg(test)]
2131#[path = "tests/geometry_tests.rs"]
2132mod tests;