1use crate::geometry_impl::{try_get_geometry_impl, FillPath, VelloPath};
2use crate::simple_geometry_impl::SimpleGeometryImpl;
3use crate::vello_extensions::{to_kurbo_point, PATH_TOLERANCE};
4use ferroui_base::media::{FillRule, SweepDirection};
5use ferroui_base::platform::{IGeometryContext, IGeometryImpl, IStreamGeometryBuilder};
6use ferroui_base::{Point, Size};
7use kurbo::{BezPath, SvgArc, Vec2};
8use std::sync::Arc;
9
10#[derive(Clone)]
15struct PathBuilder {
16 path: BezPath,
17 fill_rule: FillRule,
18}
19
20impl PathBuilder {
21 fn new() -> Self {
23 Self { path: BezPath::new(), fill_rule: FillRule::NonZero }
24 }
25
26 fn from_path(path: &VelloPath) -> Self {
27 Self { path: path.path().clone(), fill_rule: path.fill_rule() }
28 }
29
30 fn snapshot(&self) -> VelloPath {
31 VelloPath::new(self.path.clone(), self.fill_rule)
32 }
33
34 fn current_point(&mut self) -> kurbo::Point {
36 if self.path.elements().is_empty() {
39 self.path.move_to((0.0, 0.0));
40 }
41 self.path.current_position().unwrap_or_default()
42 }
43
44 fn move_to(&mut self, point: kurbo::Point) {
45 self.path.move_to(point);
46 }
47
48 fn line_to(&mut self, point: kurbo::Point) {
49 self.current_point();
50 self.path.line_to(point);
51 }
52
53 fn quad_to(&mut self, p1: kurbo::Point, p2: kurbo::Point) {
54 self.current_point();
55 self.path.quad_to(p1, p2);
56 }
57
58 fn cubic_to(&mut self, p1: kurbo::Point, p2: kurbo::Point, p3: kurbo::Point) {
59 self.current_point();
60 self.path.curve_to(p1, p2, p3);
61 }
62
63 fn close(&mut self) {
64 if !self.path.elements().is_empty() {
65 self.path.close_path();
66 }
67 }
68
69 fn arc_to(&mut self, radii: Vec2, rotation_angle: f64, is_large_arc: bool, clockwise: bool, end: kurbo::Point) {
75 let from = self.current_point();
76 let arc = SvgArc {
77 from,
78 to: end,
79 radii,
80 x_rotation: rotation_angle.to_radians(),
81 large_arc: is_large_arc,
82 sweep: clockwise,
85 };
86
87 match kurbo::Arc::from_svg_arc(&arc) {
88 Some(arc) => {
89 let mut curves = Vec::new();
90 arc.to_cubic_beziers(PATH_TOLERANCE, |p1, p2, p3| curves.push((p1, p2, p3)));
91 if let Some(last) = curves.last_mut() {
95 last.2 = end;
96 }
97 for (p1, p2, p3) in curves {
98 self.path.curve_to(p1, p2, p3);
99 }
100 }
101 None => self.path.line_to(end),
102 }
103 }
104}
105
106enum FillBuilder {
108 None,
110 SameAsStroke,
112 Separate(PathBuilder),
114}
115
116pub struct StreamGeometryBuilder {
122 stroke: PathBuilder,
123 fill: FillBuilder,
124 is_filled: bool,
125 start_point: Point,
126 is_figure_broken: bool,
127 built: bool,
128}
129
130impl StreamGeometryBuilder {
131 pub fn new(source: Option<&dyn IGeometryImpl>) -> Self {
136 let (stroke, fill) = match source {
137 None => (PathBuilder::from_path(&VelloPath::empty()), FillBuilder::SameAsStroke),
138 Some(source) => {
139 let geometry = try_get_geometry_impl(source)
140 .unwrap_or_else(|| panic!("The geometry wasn't created by the Vello render interface."));
141 let stroke_path = geometry.stroke_path();
142 let fill = match geometry.fill() {
143 FillPath::SameAsStroke => FillBuilder::SameAsStroke,
144 FillPath::Separate(fill_path) => FillBuilder::Separate(PathBuilder::from_path(&fill_path)),
145 FillPath::None if stroke_path.is_none() => FillBuilder::SameAsStroke,
147 FillPath::None => FillBuilder::None,
148 };
149 let stroke = PathBuilder::from_path(&stroke_path.unwrap_or_else(VelloPath::empty));
150 (stroke, fill)
151 }
152 };
153
154 Self { stroke, fill, is_filled: false, start_point: Point::default(), is_figure_broken: false, built: false }
155 }
156
157 fn fill(&mut self) -> &mut PathBuilder {
159 if matches!(self.fill, FillBuilder::None) {
160 self.fill = FillBuilder::Separate(PathBuilder::new());
161 }
162
163 match &mut self.fill {
164 FillBuilder::SameAsStroke => &mut self.stroke,
165 FillBuilder::Separate(fill) => fill,
166 FillBuilder::None => unreachable!("the fill builder was just created"),
167 }
168 }
169
170 fn duplicate(&self) -> bool {
172 self.is_filled && !matches!(self.fill, FillBuilder::SameAsStroke)
173 }
174
175 fn ensure_separate_fill_path(&mut self) {
176 if matches!(self.fill, FillBuilder::SameAsStroke) {
177 self.fill = FillBuilder::Separate(self.stroke.clone());
178 }
179 }
180
181 fn break_figure(&mut self) {
182 if !self.is_figure_broken {
183 self.is_figure_broken = true;
184 self.ensure_separate_fill_path();
185 }
186 }
187}
188
189impl IStreamGeometryBuilder for StreamGeometryBuilder {
190 fn to_geometry(&mut self) -> Arc<dyn IGeometryImpl> {
191 if self.built {
192 panic!("The geometry has already been built.");
193 }
194
195 self.built = true;
196
197 let stroke = self.stroke.snapshot();
198 let fill = match &self.fill {
199 FillBuilder::None => FillPath::None,
200 FillBuilder::SameAsStroke => FillPath::SameAsStroke,
201 FillBuilder::Separate(fill) => FillPath::Separate(fill.snapshot()),
202 };
203
204 SimpleGeometryImpl::new(stroke, fill, None)
205 }
206}
207
208impl IGeometryContext for StreamGeometryBuilder {
209 fn arc_to(
210 &mut self,
211 point: Point,
212 size: Size,
213 rotation_angle: f64,
214 is_large_arc: bool,
215 sweep_direction: SweepDirection,
216 is_stroked: bool,
217 ) {
218 let clockwise = sweep_direction == SweepDirection::Clockwise;
219 let radii = Vec2::new(size.width, size.height);
220 let end = to_kurbo_point(point);
221
222 if is_stroked {
223 self.stroke.arc_to(radii, rotation_angle, is_large_arc, clockwise, end);
224 } else {
225 self.break_figure();
226 self.stroke.move_to(end);
227 }
228
229 if self.duplicate() {
230 self.fill().arc_to(radii, rotation_angle, is_large_arc, clockwise, end);
231 }
232 }
233
234 fn begin_figure(&mut self, start_point: Point, is_filled: bool) {
235 if !is_filled {
236 self.ensure_separate_fill_path();
237 }
238
239 self.is_filled = is_filled;
240 self.start_point = start_point;
241 self.is_figure_broken = false;
242 self.stroke.move_to(to_kurbo_point(start_point));
243
244 if self.duplicate() {
245 self.fill().move_to(to_kurbo_point(start_point));
246 }
247 }
248
249 fn cubic_bezier_to(&mut self, control_point1: Point, control_point2: Point, end_point: Point, is_stroked: bool) {
250 let (p1, p2, p3) =
251 (to_kurbo_point(control_point1), to_kurbo_point(control_point2), to_kurbo_point(end_point));
252
253 if is_stroked {
254 self.stroke.cubic_to(p1, p2, p3);
255 } else {
256 self.break_figure();
257 self.stroke.move_to(p3);
258 }
259
260 if self.duplicate() {
261 self.fill().cubic_to(p1, p2, p3);
262 }
263 }
264
265 fn quadratic_bezier_to(&mut self, control_point: Point, end_point: Point, is_stroked: bool) {
266 let (p1, p2) = (to_kurbo_point(control_point), to_kurbo_point(end_point));
267
268 if is_stroked {
269 self.stroke.quad_to(p1, p2);
270 } else {
271 self.break_figure();
272 self.stroke.move_to(p2);
273 }
274
275 if self.duplicate() {
276 self.fill().quad_to(p1, p2);
277 }
278 }
279
280 fn line_to(&mut self, point: Point, is_stroked: bool) {
281 let point = to_kurbo_point(point);
282
283 if is_stroked {
284 self.stroke.line_to(point);
285 } else {
286 self.break_figure();
287 self.stroke.move_to(point);
288 }
289
290 if self.duplicate() {
291 self.fill().line_to(point);
292 }
293 }
294
295 fn end_figure(&mut self, is_closed: bool) {
296 if is_closed {
297 if self.is_figure_broken {
298 self.stroke.line_to(to_kurbo_point(self.start_point));
299 self.is_figure_broken = false;
300 } else {
301 self.stroke.close();
302 }
303
304 if self.duplicate() {
305 self.fill().close();
306 }
307 }
308 }
309
310 fn set_fill_rule(&mut self, fill_rule: FillRule) {
311 self.fill().fill_rule = fill_rule;
312 }
313
314 fn dispose(&mut self) {}
315}