1use std::cell::RefCell;
6
7use dom_struct::dom_struct;
8use js::context::JSContext;
9use js::rust::HandleObject;
10use script_bindings::codegen::GenericBindings::DOMMatrixBinding::DOMMatrix2DInit;
11use script_bindings::error::ErrorResult;
12use script_bindings::reflector::{Reflector, reflect_dom_object_with_proto};
13use script_bindings::str::DOMString;
14use servo_canvas_traits::canvas::Path;
15
16use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::Path2DMethods;
17use crate::dom::bindings::codegen::UnionTypes::UnrestrictedDoubleOrDOMPointInitOrUnrestrictedDoubleOrDOMPointInitSequence;
18use crate::dom::bindings::error::{Error, Fallible};
19use crate::dom::bindings::root::DomRoot;
20use crate::dom::dommatrixreadonly::dommatrix2dinit_to_matrix;
21use crate::dom::globalscope::GlobalScope;
22
23#[dom_struct]
24pub(crate) struct Path2D {
25 reflector_: Reflector,
26 #[ignore_malloc_size_of = "Defined in kurbo."]
27 #[no_trace]
28 path: RefCell<Path>,
29}
30
31impl Path2D {
32 pub(crate) fn new() -> Path2D {
33 Self {
34 reflector_: Reflector::new(),
35 path: RefCell::new(Path::new()),
36 }
37 }
38 pub(crate) fn new_with_path(other: &Path2D) -> Path2D {
39 Self {
40 reflector_: Reflector::new(),
41 path: other.path.clone(),
42 }
43 }
44
45 pub(crate) fn new_with_str(path: &str) -> Path2D {
46 Self {
47 reflector_: Reflector::new(),
48 path: RefCell::new(Path::from_svg(path)),
49 }
50 }
51
52 pub(crate) fn segments(&self) -> Path {
53 self.path.borrow().clone()
54 }
55
56 pub(crate) fn is_path_empty(&self) -> bool {
57 self.path.borrow().0.is_empty()
58 }
59
60 fn add_path(&self, other: &Path2D, transform: &DOMMatrix2DInit) -> ErrorResult {
61 if other.is_path_empty() {
63 return Ok(());
64 }
65
66 let matrix = dommatrix2dinit_to_matrix(transform)?;
68
69 if !matrix.m11.is_finite() ||
73 !matrix.m12.is_finite() ||
74 !matrix.m21.is_finite() ||
75 !matrix.m22.is_finite() ||
76 !matrix.m31.is_finite() ||
77 !matrix.m32.is_finite()
78 {
79 return Ok(());
80 }
81
82 let mut c = other.segments();
84
85 c.transform(matrix);
87
88 let mut path = self.path.borrow_mut();
89
90 let last_point = path.last_point();
92
93 path.0.extend(c.0);
95
96 if let Some(last_point) = last_point {
98 path.move_to(last_point.x, last_point.y);
99 }
100
101 Ok(())
102 }
103}
104
105impl Path2DMethods<crate::DomTypeHolder> for Path2D {
106 fn AddPath(&self, other: &Path2D, transform: &DOMMatrix2DInit) -> ErrorResult {
108 self.add_path(other, transform)
109 }
110
111 fn ClosePath(&self) {
113 self.path.borrow_mut().close_path();
114 }
115
116 fn MoveTo(&self, x: f64, y: f64) {
118 self.path.borrow_mut().move_to(x, y);
119 }
120
121 fn LineTo(&self, x: f64, y: f64) {
123 self.path.borrow_mut().line_to(x, y);
124 }
125
126 fn QuadraticCurveTo(&self, cpx: f64, cpy: f64, x: f64, y: f64) {
128 self.path.borrow_mut().quadratic_curve_to(cpx, cpy, x, y);
129 }
130
131 fn BezierCurveTo(&self, cp1x: f64, cp1y: f64, cp2x: f64, cp2y: f64, x: f64, y: f64) {
133 self.path
134 .borrow_mut()
135 .bezier_curve_to(cp1x, cp1y, cp2x, cp2y, x, y);
136 }
137
138 fn ArcTo(&self, x1: f64, y1: f64, x2: f64, y2: f64, radius: f64) -> Fallible<()> {
140 self.path
141 .borrow_mut()
142 .arc_to(x1, y1, x2, y2, radius)
143 .map_err(|_| Error::IndexSize(None))
144 }
145
146 fn Rect(&self, x: f64, y: f64, w: f64, h: f64) {
148 self.path.borrow_mut().rect(x, y, w, h);
149 }
150
151 fn RoundRect(
153 &self,
154 x: f64,
155 y: f64,
156 w: f64,
157 h: f64,
158 radii: UnrestrictedDoubleOrDOMPointInitOrUnrestrictedDoubleOrDOMPointInitSequence,
159 ) -> Fallible<()> {
160 let radii = super::canvas_state::round_rect_radii(&radii);
161 self.path
162 .borrow_mut()
163 .round_rect(x, y, w, h, &radii)
164 .map_err(super::canvas_state::round_rect_error)
165 }
166
167 fn Arc(
169 &self,
170 x: f64,
171 y: f64,
172 radius: f64,
173 start_angle: f64,
174 end_angle: f64,
175 counterclockwise: bool,
176 ) -> Fallible<()> {
177 self.path
178 .borrow_mut()
179 .arc(x, y, radius, start_angle, end_angle, counterclockwise)
180 .map_err(|_| Error::IndexSize(None))
181 }
182
183 fn Ellipse(
185 &self,
186 x: f64,
187 y: f64,
188 radius_x: f64,
189 radius_y: f64,
190 rotation_angle: f64,
191 start_angle: f64,
192 end_angle: f64,
193 counterclockwise: bool,
194 ) -> Fallible<()> {
195 self.path
196 .borrow_mut()
197 .ellipse(
198 x,
199 y,
200 radius_x,
201 radius_y,
202 rotation_angle,
203 start_angle,
204 end_angle,
205 counterclockwise,
206 )
207 .map_err(|_| Error::IndexSize(None))
208 }
209
210 fn Constructor(
212 cx: &mut JSContext,
213 global: &GlobalScope,
214 proto: Option<HandleObject>,
215 ) -> DomRoot<Path2D> {
216 reflect_dom_object_with_proto(cx, Box::new(Self::new()), global, proto)
217 }
218
219 fn Constructor_(
221 cx: &mut JSContext,
222 global: &GlobalScope,
223 proto: Option<HandleObject>,
224 other: &Path2D,
225 ) -> DomRoot<Path2D> {
226 reflect_dom_object_with_proto(cx, Box::new(Self::new_with_path(other)), global, proto)
227 }
228
229 fn Constructor__(
231 cx: &mut JSContext,
232 global: &GlobalScope,
233 proto: Option<HandleObject>,
234 path_string: DOMString,
235 ) -> DomRoot<Path2D> {
236 reflect_dom_object_with_proto(
237 cx,
238 Box::new(Self::new_with_str(&path_string.str())),
239 global,
240 proto,
241 )
242 }
243}