1use std::default::Default;
6use std::f64::consts::{FRAC_PI_2, PI};
7use std::str::FromStr;
8
9use euclid::Angle;
10use euclid::approxeq::ApproxEq;
11use euclid::default::{Point2D, Rect, Size2D, Transform2D};
12use fonts_traits::{FontDataAndIndex, FontIdentifier};
13use kurbo::{BezPath, ParamCurveNearest as _, PathEl, Point, Shape, Triangle};
14use malloc_size_of::MallocSizeOf;
15use malloc_size_of_derive::MallocSizeOf;
16use pixels::SharedSnapshot;
17use serde::{Deserialize, Serialize};
18use servo_base::Epoch;
19use servo_base::generic_channel::GenericSender;
20use strum::{Display, EnumString};
21use style::color::AbsoluteColor;
22use webrender_api::ImageKey;
23
24#[derive(Clone, Debug, Default, Deserialize, Serialize)]
25pub struct Path(pub BezPath);
26
27impl MallocSizeOf for Path {
28 fn size_of(&self, _ops: &mut malloc_size_of::MallocSizeOfOps) -> usize {
29 std::mem::size_of_val(self.0.elements())
30 }
31}
32
33pub struct IndexSizeError;
34
35#[derive(Clone, Copy, Debug)]
36pub struct RoundRectRadius {
37 pub x: f64,
38 pub y: f64,
39}
40
41pub enum RangeError {
42 InvalidSize,
44 NegativeRadius,
46}
47
48impl Path {
49 pub fn new() -> Self {
50 Self(BezPath::new())
51 }
52
53 pub fn from_svg(s: &str) -> Self {
54 Self(BezPath::from_svg(s).unwrap_or_default())
55 }
56
57 pub fn transform(&mut self, transform: Transform2D<f64>) {
58 self.0.apply_affine(transform.into());
59 }
60
61 pub fn ensure_there_is_a_subpath(&mut self, x: f64, y: f64) {
63 if self.0.elements().is_empty() {
65 self.0.move_to((x, y));
70 }
71 }
72
73 pub fn close_path(&mut self) {
75 if matches!(self.0.elements().last(), None | Some(PathEl::ClosePath)) {
77 return;
78 }
79 self.0.close_path();
83 }
84
85 pub fn move_to(&mut self, x: f64, y: f64) {
87 if !(x.is_finite() && y.is_finite()) {
89 return;
90 }
91
92 self.0.move_to((x, y));
94 }
95
96 pub fn line_to(&mut self, x: f64, y: f64) {
98 if !(x.is_finite() && y.is_finite()) {
100 return;
101 }
102
103 self.ensure_there_is_a_subpath(x, y);
105
106 self.0.line_to((x, y));
109 }
110
111 pub fn quadratic_curve_to(&mut self, cpx: f64, cpy: f64, x: f64, y: f64) {
113 if !(cpx.is_finite() && cpy.is_finite() && x.is_finite() && y.is_finite()) {
115 return;
116 }
117
118 self.ensure_there_is_a_subpath(cpx, cpy);
120
121 self.0.quad_to((cpx, cpy), (x, y));
125 }
126
127 pub fn bezier_curve_to(&mut self, cp1x: f64, cp1y: f64, cp2x: f64, cp2y: f64, x: f64, y: f64) {
129 if !(cp1x.is_finite() &&
131 cp1y.is_finite() &&
132 cp2x.is_finite() &&
133 cp2y.is_finite() &&
134 x.is_finite() &&
135 y.is_finite())
136 {
137 return;
138 }
139
140 self.ensure_there_is_a_subpath(cp1x, cp1y);
142
143 self.0.curve_to((cp1x, cp1y), (cp2x, cp2y), (x, y));
147 }
148
149 pub fn arc_to(
151 &mut self,
152 x1: f64,
153 y1: f64,
154 x2: f64,
155 y2: f64,
156 radius: f64,
157 ) -> Result<(), IndexSizeError> {
158 if !(x1.is_finite() &&
160 y1.is_finite() &&
161 x2.is_finite() &&
162 y2.is_finite() &&
163 radius.is_finite())
164 {
165 return Ok(());
166 }
167
168 self.ensure_there_is_a_subpath(x1, y1);
170
171 if radius < 0.0 {
173 return Err(IndexSizeError);
174 }
175
176 let Point { x: x0, y: y0 } = self.last_point().unwrap();
178
179 if ((x0, y0) == (x1, y1)) || ((x1, y1) == (x2, y2)) || radius.approx_eq(&0.0) {
184 self.0.line_to((x1, y1));
185 return Ok(());
186 }
187
188 let direction = Triangle::from_coords((x0, y0), (x1, y1), (x2, y2)).area();
192 if direction.approx_eq(&0.0) {
193 self.0.line_to((x1, y1));
194 return Ok(());
195 }
196
197 let a2 = (x0 - x1).powi(2) + (y0 - y1).powi(2);
210 let b2 = (x1 - x2).powi(2) + (y1 - y2).powi(2);
211 let d = {
212 let c2 = (x0 - x2).powi(2) + (y0 - y2).powi(2);
213 let cosx = (a2 + b2 - c2) / (2.0 * (a2 * b2).sqrt());
214 let sinx = (1.0 - cosx.powi(2)).sqrt();
215 radius / ((1.0 - cosx) / sinx)
216 };
217
218 let anx = (x1 - x0) / a2.sqrt();
220 let any = (y1 - y0) / a2.sqrt();
221 let tp1 = Point2D::new(x1 - anx * d, y1 - any * d);
222
223 let bnx = (x1 - x2) / b2.sqrt();
225 let bny = (y1 - y2) / b2.sqrt();
226 let tp2 = Point2D::new(x1 - bnx * d, y1 - bny * d);
227
228 let anticlockwise = direction < 0.0;
230 let cx = tp1.x + any * radius * if anticlockwise { 1.0 } else { -1.0 };
231 let cy = tp1.y - anx * radius * if anticlockwise { 1.0 } else { -1.0 };
232 let angle_start = (tp1.y - cy).atan2(tp1.x - cx);
233 let angle_end = (tp2.y - cy).atan2(tp2.x - cx);
234
235 self.0.line_to((tp1.x, tp1.y));
236
237 self.arc(cx, cy, radius, angle_start, angle_end, anticlockwise)
238 }
239
240 pub fn last_point(&mut self) -> Option<Point> {
241 self.0.current_position()
242 }
243
244 pub fn arc(
246 &mut self,
247 x: f64,
248 y: f64,
249 radius: f64,
250 start_angle: f64,
251 end_angle: f64,
252 counterclockwise: bool,
253 ) -> Result<(), IndexSizeError> {
254 self.ellipse(
256 x,
257 y,
258 radius,
259 radius,
260 0.,
261 start_angle,
262 end_angle,
263 counterclockwise,
264 )
265 }
266
267 #[expect(clippy::too_many_arguments)]
268 pub fn ellipse(
270 &mut self,
271 x: f64,
272 y: f64,
273 radius_x: f64,
274 radius_y: f64,
275 rotation_angle: f64,
276 start_angle: f64,
277 end_angle: f64,
278 counterclockwise: bool,
279 ) -> Result<(), IndexSizeError> {
280 if !(x.is_finite() &&
282 y.is_finite() &&
283 radius_x.is_finite() &&
284 radius_y.is_finite() &&
285 rotation_angle.is_finite() &&
286 start_angle.is_finite() &&
287 end_angle.is_finite())
288 {
289 return Ok(());
290 }
291
292 if radius_x < 0.0 || radius_y < 0.0 {
294 return Err(IndexSizeError);
295 }
296
297 let mut start = Angle::radians(start_angle);
298 let mut end = Angle::radians(end_angle);
299
300 if !counterclockwise && start > end + Angle::two_pi() ||
302 counterclockwise && end > start + Angle::two_pi()
303 {
304 start = start.positive();
305 end = end.positive();
306 }
307
308 let sweep = match counterclockwise {
310 true => {
311 if end - start == Angle::two_pi() {
312 -Angle::two_pi()
313 } else if end > start {
314 -(Angle::two_pi() - (end - start))
315 } else {
316 -(start - end)
317 }
318 },
319 false => {
320 if start - end == Angle::two_pi() {
321 Angle::two_pi()
322 } else if start > end {
323 Angle::two_pi() - (start - end)
324 } else {
325 end - start
326 }
327 },
328 };
329
330 self.append_ellipse_arc(
331 (x, y),
332 (radius_x, radius_y),
333 start.radians,
334 sweep.radians,
335 rotation_angle,
336 );
337
338 Ok(())
339 }
340
341 pub fn rect(&mut self, x: f64, y: f64, w: f64, h: f64) {
343 if !(x.is_finite() && y.is_finite() && w.is_finite() && h.is_finite()) {
345 return;
346 }
347
348 self.0.move_to((x, y));
352 self.0.line_to((x + w, y));
353 self.0.line_to((x + w, y + h));
354 self.0.line_to((x, y + h));
355
356 self.0.close_path();
358
359 self.0.move_to((x, y));
361 }
362
363 pub fn round_rect(
365 &mut self,
366 mut x: f64,
367 mut y: f64,
368 mut w: f64,
369 mut h: f64,
370 radii: &[RoundRectRadius],
371 ) -> Result<(), RangeError> {
372 if !(x.is_finite() && y.is_finite() && w.is_finite() && h.is_finite()) {
374 return Ok(());
375 }
376
377 if radii.is_empty() || radii.len() > 4 {
380 return Err(RangeError::InvalidSize);
381 }
382
383 for radius in radii {
386 if !(radius.x.is_finite() && radius.y.is_finite()) {
387 return Ok(());
388 }
389 if radius.x < 0.0 || radius.y < 0.0 {
390 return Err(RangeError::NegativeRadius);
391 }
392 }
393 let normalized_radii = radii;
395 let (mut upper_left, mut upper_right, mut lower_right, mut lower_left) =
397 match normalized_radii {
398 [a] => (*a, *a, *a, *a),
401 [a, b] => (*a, *b, *a, *b),
404 [a, b, c] => (*a, *b, *c, *b),
408 [a, b, c, d] => (*a, *b, *c, *d),
412 _ => unreachable!(),
413 };
414
415 let (orig_x, orig_y) = (x, y);
421 let counterclockwise = (w < 0.0) != (h < 0.0);
422 use std::mem::swap;
423 if w < 0.0 {
424 swap(&mut upper_left, &mut upper_right);
425 swap(&mut lower_left, &mut lower_right);
426 x += w;
427 w = -w;
428 }
429 if h < 0.0 {
430 swap(&mut upper_left, &mut lower_left);
431 swap(&mut upper_right, &mut lower_right);
432 y += h;
433 h = -h;
434 }
435
436 let top = upper_left.x + upper_right.x;
438 let right = upper_right.y + lower_right.y;
439 let bottom = lower_right.x + lower_left.x;
440 let left = upper_left.y + lower_left.y;
441 let scale = (w / top).min(h / right).min(w / bottom).min(h / left);
442 if scale < 1.0 {
443 upper_left.x *= scale;
444 upper_left.y *= scale;
445 upper_right.x *= scale;
446 upper_right.y *= scale;
447 lower_right.x *= scale;
448 lower_right.y *= scale;
449 lower_left.x *= scale;
450 lower_left.y *= scale;
451 }
452
453 let mut subpath = Path::new();
455 subpath.0.move_to((x + upper_left.x, y));
457 subpath.0.line_to((x + w - upper_right.x, y));
459 subpath.round_rect_arc(
461 x + w - upper_right.x,
462 y + upper_right.y,
463 upper_right.x,
464 upper_right.y,
465 -FRAC_PI_2,
466 );
467 subpath.0.line_to((x + w, y + h - lower_right.y));
469 subpath.round_rect_arc(
471 x + w - lower_right.x,
472 y + h - lower_right.y,
473 lower_right.x,
474 lower_right.y,
475 0.0,
476 );
477 subpath.0.line_to((x + lower_left.x, y + h));
479 subpath.round_rect_arc(
481 x + lower_left.x,
482 y + h - lower_left.y,
483 lower_left.x,
484 lower_left.y,
485 FRAC_PI_2,
486 );
487 subpath.0.line_to((x, y + upper_left.y));
489 subpath.round_rect_arc(
491 x + upper_left.x,
492 y + upper_left.y,
493 upper_left.x,
494 upper_left.y,
495 PI,
496 );
497
498 subpath.0.close_path();
500
501 if counterclockwise {
502 subpath.0 = subpath.0.reverse_subpaths();
503 }
504 self.0.extend(subpath.0.elements().iter().cloned());
505
506 self.0.move_to((orig_x, orig_y));
509
510 Ok(())
511 }
512
513 fn append_ellipse_arc(
514 &mut self,
515 center: (f64, f64),
516 radii: (f64, f64),
517 start_angle: f64,
518 sweep: f64,
519 rotation: f64,
520 ) {
521 let arc = kurbo::Arc::new(center, radii, start_angle, sweep, rotation);
522 let mut iter = arc.path_elements(0.01);
523
524 let Some(PathEl::MoveTo(start_point)) = iter.next() else {
525 unreachable!()
526 };
527 self.line_to(start_point.x, start_point.y);
528 if sweep.abs() > 1e-3 {
529 self.0.extend(iter);
530 }
531 }
532
533 #[inline]
535 fn round_rect_arc(&mut self, cx: f64, cy: f64, rx: f64, ry: f64, start_angle: f64) {
536 self.append_ellipse_arc((cx, cy), (rx, ry), start_angle, FRAC_PI_2, 0.0);
537 }
538
539 pub fn is_point_in_path(&self, x: f64, y: f64, fill_rule: FillRule) -> bool {
541 let p = Point::new(x, y);
542 if !p.is_finite() {
544 return false;
545 }
546
547 let mut path = self.clone();
554 path.close_path();
555 let winding = path.0.winding(p);
556 let is_inside = match fill_rule {
557 FillRule::Nonzero => winding != 0,
558 FillRule::Evenodd => (winding % 2) != 0,
559 };
560 if is_inside {
561 return true;
562 }
563 path.0
565 .segments()
566 .any(|seg| seg.nearest(p, 0.00001).distance_sq < 0.00001)
567 }
568
569 pub fn bounding_box(&self) -> Rect<f64> {
570 self.0.control_box().into()
571 }
572}
573
574#[derive(Clone, Debug, Deserialize, Serialize, MallocSizeOf)]
575pub enum FillRule {
576 Nonzero,
577 Evenodd,
578}
579
580#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
581pub struct CanvasId(pub u64);
582
583#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, Serialize)]
584pub struct CompositionOptions {
585 pub alpha: f64,
586 pub composition_operation: CompositionOrBlending,
587}
588
589#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
590pub struct ShadowOptions {
591 pub offset_x: f64,
592 pub offset_y: f64,
593 pub blur: f64,
594 pub color: AbsoluteColor,
595}
596
597impl ShadowOptions {
598 pub fn need_to_draw_shadow(&self) -> bool {
600 self.color.alpha != 0.0 &&
602 (self.offset_x != 0.0 ||
604 self.offset_y != 0.0 ||
605 self.blur != 0.0)
606 }
607}
608
609#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
610pub struct LineOptions {
611 pub width: f64,
612 pub cap_style: LineCapStyle,
613 pub join_style: LineJoinStyle,
614 pub miter_limit: f64,
615 pub dash: Vec<f32>,
616 pub dash_offset: f64,
617}
618
619pub type CanvasMsg = (CanvasId, CanvasCommand);
620
621#[derive(Debug, Deserialize, Serialize, Display, MallocSizeOf)]
622pub enum CanvasCommand {
623 Recreate(Option<Size2D<u64>>),
625 Destroy,
627 SetImageKey(ImageKey),
628 DrawImage(
629 SharedSnapshot,
630 Rect<f64>,
631 Rect<f64>,
632 bool,
633 ShadowOptions,
634 CompositionOptions,
635 Transform2D<f64>,
636 ),
637 DrawEmptyImage(
638 Size2D<u32>,
639 Rect<f64>,
640 Rect<f64>,
641 ShadowOptions,
642 CompositionOptions,
643 Transform2D<f64>,
644 ),
645 DrawImageInOther(
646 CanvasId,
647 Rect<f64>,
648 Rect<f64>,
649 bool,
650 ShadowOptions,
651 CompositionOptions,
652 Transform2D<f64>,
653 ),
654 ClearRect(Rect<f32>, Transform2D<f64>),
655 ClipPath(Path, FillRule, Transform2D<f64>),
656 PopClips(usize),
657 FillPath(
658 FillOrStrokeStyle,
659 Path,
660 FillRule,
661 ShadowOptions,
662 CompositionOptions,
663 Transform2D<f64>,
664 ),
665 FillText(
666 Rect<f64>,
667 Vec<TextRun>,
668 FillOrStrokeStyle,
669 ShadowOptions,
670 CompositionOptions,
671 Transform2D<f64>,
672 ),
673 StrokeText(
674 Rect<f64>,
675 Vec<TextRun>,
676 FillOrStrokeStyle,
677 LineOptions,
678 ShadowOptions,
679 CompositionOptions,
680 Transform2D<f64>,
681 ),
682 FillRect(
683 Rect<f32>,
684 FillOrStrokeStyle,
685 ShadowOptions,
686 CompositionOptions,
687 Transform2D<f64>,
688 ),
689 GetImageData(Option<Rect<u32>>, GenericSender<SharedSnapshot>),
690 PutImageData(Rect<u32>, SharedSnapshot),
691 StrokeRect(
692 Rect<f32>,
693 FillOrStrokeStyle,
694 LineOptions,
695 ShadowOptions,
696 CompositionOptions,
697 Transform2D<f64>,
698 ),
699 StrokePath(
700 Path,
701 FillOrStrokeStyle,
702 LineOptions,
703 ShadowOptions,
704 CompositionOptions,
705 Transform2D<f64>,
706 ),
707 UpdateImage(Option<Epoch>),
708 ProcessBatchMessages(Vec<CanvasCommand>),
709}
710
711#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
712pub struct CanvasGradientStop {
713 pub offset: f64,
714 pub color: AbsoluteColor,
715}
716
717#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
718pub struct LinearGradientStyle {
719 pub x0: f64,
720 pub y0: f64,
721 pub x1: f64,
722 pub y1: f64,
723 pub stops: Vec<CanvasGradientStop>,
724}
725
726impl LinearGradientStyle {
727 pub fn new(
728 x0: f64,
729 y0: f64,
730 x1: f64,
731 y1: f64,
732 stops: Vec<CanvasGradientStop>,
733 ) -> LinearGradientStyle {
734 LinearGradientStyle {
735 x0,
736 y0,
737 x1,
738 y1,
739 stops,
740 }
741 }
742}
743
744#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
745pub struct RadialGradientStyle {
746 pub x0: f64,
747 pub y0: f64,
748 pub r0: f64,
749 pub x1: f64,
750 pub y1: f64,
751 pub r1: f64,
752 pub stops: Vec<CanvasGradientStop>,
753}
754
755impl RadialGradientStyle {
756 pub fn new(
757 x0: f64,
758 y0: f64,
759 r0: f64,
760 x1: f64,
761 y1: f64,
762 r1: f64,
763 stops: Vec<CanvasGradientStop>,
764 ) -> RadialGradientStyle {
765 RadialGradientStyle {
766 x0,
767 y0,
768 r0,
769 x1,
770 y1,
771 r1,
772 stops,
773 }
774 }
775}
776
777#[derive(Clone, Debug, Deserialize, Serialize, MallocSizeOf)]
778pub struct SurfaceStyle {
779 pub surface_data: SharedSnapshot,
780 pub surface_size: Size2D<u32>,
781 pub repeat_x: bool,
782 pub repeat_y: bool,
783 pub transform: Transform2D<f32>,
784}
785
786impl SurfaceStyle {
787 pub fn new(
788 surface_data: SharedSnapshot,
789 surface_size: Size2D<u32>,
790 repeat_x: bool,
791 repeat_y: bool,
792 transform: Transform2D<f32>,
793 ) -> Self {
794 Self {
795 surface_data,
796 surface_size,
797 repeat_x,
798 repeat_y,
799 transform,
800 }
801 }
802}
803
804#[derive(Clone, Debug, Deserialize, Serialize, MallocSizeOf)]
805pub enum FillOrStrokeStyle {
806 Color(AbsoluteColor),
807 LinearGradient(LinearGradientStyle),
808 RadialGradient(RadialGradientStyle),
809 Surface(SurfaceStyle),
810}
811
812impl FillOrStrokeStyle {
813 pub fn is_zero_size_gradient(&self) -> bool {
814 match self {
815 Self::RadialGradient(pattern) => {
816 let centers_equal = (pattern.x0, pattern.y0) == (pattern.x1, pattern.y1);
817 let radii_equal = pattern.r0 == pattern.r1;
818 (centers_equal && radii_equal) || pattern.stops.is_empty()
819 },
820 Self::LinearGradient(pattern) => {
821 (pattern.x0, pattern.y0) == (pattern.x1, pattern.y1) || pattern.stops.is_empty()
822 },
823 Self::Color(..) | Self::Surface(..) => false,
824 }
825 }
826
827 pub fn x_bound(&self) -> Option<u32> {
828 match self {
829 Self::Surface(pattern) => {
830 if pattern.repeat_x {
831 None
832 } else {
833 Some(pattern.surface_size.width)
834 }
835 },
836 Self::Color(..) | Self::LinearGradient(..) | Self::RadialGradient(..) => None,
837 }
838 }
839
840 pub fn y_bound(&self) -> Option<u32> {
841 match self {
842 Self::Surface(pattern) => {
843 if pattern.repeat_y {
844 None
845 } else {
846 Some(pattern.surface_size.height)
847 }
848 },
849 Self::Color(..) | Self::LinearGradient(..) | Self::RadialGradient(..) => None,
850 }
851 }
852}
853
854#[derive(
855 Clone, Copy, Debug, Display, Deserialize, EnumString, MallocSizeOf, PartialEq, Serialize,
856)]
857pub enum LineCapStyle {
858 Butt = 0,
859 Round = 1,
860 Square = 2,
861}
862
863#[derive(
864 Clone, Copy, Debug, Deserialize, Display, EnumString, MallocSizeOf, PartialEq, Serialize,
865)]
866pub enum LineJoinStyle {
867 Round = 0,
868 Bevel = 1,
869 Miter = 2,
870}
871
872#[derive(Clone, Copy, Debug, Deserialize, Display, EnumString, PartialEq, Serialize)]
873#[strum(serialize_all = "kebab-case")]
874pub enum RepetitionStyle {
875 Repeat,
876 RepeatX,
877 RepeatY,
878 NoRepeat,
879}
880
881#[derive(
883 Clone, Copy, Debug, Deserialize, Display, EnumString, MallocSizeOf, PartialEq, Serialize,
884)]
885#[strum(serialize_all = "kebab-case")]
886pub enum CompositionStyle {
887 Clear,
888 Copy,
889 SourceOver,
890 DestinationOver,
891 SourceIn,
892 DestinationIn,
893 SourceOut,
894 DestinationOut,
895 SourceAtop,
896 DestinationAtop,
897 Xor,
898 Lighter,
899 }
902
903#[derive(
905 Clone, Copy, Debug, Deserialize, Display, EnumString, MallocSizeOf, PartialEq, Serialize,
906)]
907#[strum(serialize_all = "kebab-case")]
908pub enum BlendingStyle {
909 Multiply,
911 Screen,
912 Overlay,
913 Darken,
914 Lighten,
915 ColorDodge,
916 ColorBurn,
917 HardLight,
918 SoftLight,
919 Difference,
920 Exclusion,
921 Hue,
922 Saturation,
923 Color,
924 Luminosity,
925}
926
927#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
928pub enum CompositionOrBlending {
929 Composition(CompositionStyle),
930 Blending(BlendingStyle),
931}
932
933impl Default for CompositionOrBlending {
934 fn default() -> CompositionOrBlending {
935 CompositionOrBlending::Composition(CompositionStyle::SourceOver)
936 }
937}
938
939impl FromStr for CompositionOrBlending {
940 type Err = ();
941
942 fn from_str(string: &str) -> Result<CompositionOrBlending, ()> {
943 if let Ok(op) = CompositionStyle::from_str(string) {
944 return Ok(CompositionOrBlending::Composition(op));
945 }
946
947 if let Ok(op) = BlendingStyle::from_str(string) {
948 return Ok(CompositionOrBlending::Blending(op));
949 }
950
951 Err(())
952 }
953}
954
955#[derive(Debug, Deserialize, Serialize, MallocSizeOf)]
956pub struct GlyphAndPosition {
957 pub id: u32,
958 pub point: Point2D<f32>,
959}
960
961#[derive(Deserialize, Serialize, MallocSizeOf)]
962pub struct CanvasFont {
963 pub identifier: FontIdentifier,
965 pub data: Option<FontDataAndIndex>,
968}
969
970impl CanvasFont {
971 pub fn font_data_and_index(&self) -> Option<FontDataAndIndex> {
972 match &self.identifier {
973 FontIdentifier::Local(local_font_identifier) => {
974 local_font_identifier.font_data_and_index()
975 },
976 FontIdentifier::Web(_) => self.data.clone(),
977 FontIdentifier::ArrayBuffer(_) => self.data.clone(),
978 }
979 }
980}
981
982#[derive(Deserialize, Serialize, MallocSizeOf)]
983pub struct TextRun {
984 pub font: CanvasFont,
985 pub pt_size: f32,
986 pub glyphs_and_positions: Vec<GlyphAndPosition>,
987 pub advance: f32,
988 pub bounds: Rect<f64>,
989}
990
991impl std::fmt::Debug for TextRun {
992 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
993 f.debug_struct("TextRun")
994 .field("glyphs_and_positions", &self.glyphs_and_positions)
995 .field("size", &self.bounds)
996 .finish()
997 }
998}