1// Copyright 2006 The Android Open Source Project
2// Copyright 2020 Yevhenii Reizner
3//
4// Use of this source code is governed by a BSD-style license that can be
5// found in the LICENSE file.
6
7// NOTE: this is not SkPathBuilder, but rather a reimplementation of SkPath.
8
9use alloc::vec;
10use alloc::vec::Vec;
11
12use crate::{Path, Point, Rect};
13
14use crate::path::PathVerb;
15use crate::path_geometry;
16use crate::scalar::{Scalar, SCALAR_ROOT_2_OVER_2};
17
18#[derive(Copy, Clone, PartialEq, Debug)]
19pub(crate) enum PathDirection {
20 /// Clockwise direction for adding closed contours.
21 CW,
22 /// Counter-clockwise direction for adding closed contours.
23 CCW,
24}
25
26/// A path builder.
27#[derive(Clone, Default, Debug)]
28pub struct PathBuilder {
29 pub(crate) verbs: Vec<PathVerb>,
30 pub(crate) points: Vec<Point>,
31 pub(crate) last_move_to_index: usize,
32 pub(crate) move_to_required: bool,
33}
34
35impl PathBuilder {
36 /// Creates a new builder.
37 pub fn new() -> Self {
38 PathBuilder {
39 verbs: Vec::new(),
40 points: Vec::new(),
41 last_move_to_index: 0,
42 move_to_required: true,
43 }
44 }
45
46 /// Creates a new builder with a specified capacity.
47 ///
48 /// Number of points depends on a verb type:
49 ///
50 /// - Move - 1
51 /// - Line - 1
52 /// - Quad - 2
53 /// - Cubic - 3
54 /// - Close - 0
55 pub fn with_capacity(verbs_capacity: usize, points_capacity: usize) -> Self {
56 PathBuilder {
57 verbs: Vec::with_capacity(verbs_capacity),
58 points: Vec::with_capacity(points_capacity),
59 last_move_to_index: 0,
60 move_to_required: true,
61 }
62 }
63
64 /// Creates a new `Path` from `Rect`.
65 ///
66 /// Never fails since `Rect` is always valid.
67 ///
68 /// Segments are created clockwise: TopLeft -> TopRight -> BottomRight -> BottomLeft
69 ///
70 /// The contour is closed.
71 pub fn from_rect(rect: Rect) -> Path {
72 let verbs = vec![
73 PathVerb::Move,
74 PathVerb::Line,
75 PathVerb::Line,
76 PathVerb::Line,
77 PathVerb::Close,
78 ];
79
80 let points = vec![
81 Point::from_xy(rect.left(), rect.top()),
82 Point::from_xy(rect.right(), rect.top()),
83 Point::from_xy(rect.right(), rect.bottom()),
84 Point::from_xy(rect.left(), rect.bottom()),
85 ];
86
87 Path {
88 bounds: rect,
89 verbs,
90 points,
91 }
92 }
93
94 /// Creates a new `Path` from a circle.
95 ///
96 /// See [`PathBuilder::push_circle`] for details.
97 pub fn from_circle(cx: f32, cy: f32, radius: f32) -> Option<Path> {
98 let mut b = PathBuilder::new();
99 b.push_circle(cx, cy, radius);
100 b.finish()
101 }
102
103 /// Creates a new `Path` from an oval.
104 ///
105 /// See [`PathBuilder::push_oval`] for details.
106 pub fn from_oval(oval: Rect) -> Option<Path> {
107 let mut b = PathBuilder::new();
108 b.push_oval(oval);
109 b.finish()
110 }
111
112 pub(crate) fn reserve(&mut self, additional_verbs: usize, additional_points: usize) {
113 self.verbs.reserve(additional_verbs);
114 self.points.reserve(additional_points);
115 }
116
117 /// Returns the current number of segments in the builder.
118 pub fn len(&self) -> usize {
119 self.verbs.len()
120 }
121
122 /// Checks if the builder has any segments added.
123 pub fn is_empty(&self) -> bool {
124 self.verbs.is_empty()
125 }
126
127 /// Adds beginning of a contour.
128 ///
129 /// Multiple continuous MoveTo segments are not allowed.
130 /// If the previous segment was also MoveTo, it will be overwritten with the current one.
131 pub fn move_to(&mut self, x: f32, y: f32) {
132 if let Some(PathVerb::Move) = self.verbs.last() {
133 let last_idx = self.points.len() - 1;
134 self.points[last_idx] = Point::from_xy(x, y);
135 } else {
136 self.last_move_to_index = self.points.len();
137 self.move_to_required = false;
138
139 self.verbs.push(PathVerb::Move);
140 self.points.push(Point::from_xy(x, y));
141 }
142 }
143
144 fn inject_move_to_if_needed(&mut self) {
145 if self.move_to_required {
146 match self.points.get(self.last_move_to_index).cloned() {
147 Some(p) => self.move_to(p.x, p.y),
148 None => self.move_to(0.0, 0.0),
149 }
150 }
151 }
152
153 /// Adds a line from the last point.
154 ///
155 /// - If `Path` is empty - adds Move(0, 0) first.
156 /// - If `Path` ends with Close - adds Move(last_x, last_y) first.
157 pub fn line_to(&mut self, x: f32, y: f32) {
158 self.inject_move_to_if_needed();
159
160 self.verbs.push(PathVerb::Line);
161 self.points.push(Point::from_xy(x, y));
162 }
163
164 /// Adds a quad curve from the last point to `x`, `y`.
165 ///
166 /// - If `Path` is empty - adds Move(0, 0) first.
167 /// - If `Path` ends with Close - adds Move(last_x, last_y) first.
168 pub fn quad_to(&mut self, x1: f32, y1: f32, x: f32, y: f32) {
169 self.inject_move_to_if_needed();
170
171 self.verbs.push(PathVerb::Quad);
172 self.points.push(Point::from_xy(x1, y1));
173 self.points.push(Point::from_xy(x, y));
174 }
175
176 pub(crate) fn quad_to_pt(&mut self, p1: Point, p: Point) {
177 self.quad_to(p1.x, p1.y, p.x, p.y);
178 }
179
180 // We do not support conic segments, but Skia still relies on them from time to time.
181 // This method will simply convert the input data into quad segments.
182 pub(crate) fn conic_to(&mut self, x1: f32, y1: f32, x: f32, y: f32, weight: f32) {
183 // check for <= 0 or NaN with this test
184 if !(weight > 0.0) {
185 self.line_to(x, y);
186 } else if !weight.is_finite() {
187 self.line_to(x1, y1);
188 self.line_to(x, y);
189 } else if weight == 1.0 {
190 self.quad_to(x1, y1, x, y);
191 } else {
192 self.inject_move_to_if_needed();
193
194 let last = self.last_point().unwrap();
195 let quadder = path_geometry::AutoConicToQuads::compute(
196 last,
197 Point::from_xy(x1, y1),
198 Point::from_xy(x, y),
199 weight,
200 );
201 if let Some(quadder) = quadder {
202 // Points are ordered as: 0 - 1 2 - 3 4 - 5 6 - ..
203 // `count` is a number of pairs +1
204 let mut offset = 1;
205 for _ in 0..quadder.len {
206 let pt1 = quadder.points[offset + 0];
207 let pt2 = quadder.points[offset + 1];
208 self.quad_to(pt1.x, pt1.y, pt2.x, pt2.y);
209 offset += 2;
210 }
211 }
212 }
213 }
214
215 pub(crate) fn conic_points_to(&mut self, pt1: Point, pt2: Point, weight: f32) {
216 self.conic_to(pt1.x, pt1.y, pt2.x, pt2.y, weight);
217 }
218
219 /// Adds a cubic curve from the last point to `x`, `y`.
220 ///
221 /// - If `Path` is empty - adds Move(0, 0) first.
222 /// - If `Path` ends with Close - adds Move(last_x, last_y) first.
223 pub fn cubic_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, x: f32, y: f32) {
224 self.inject_move_to_if_needed();
225
226 self.verbs.push(PathVerb::Cubic);
227 self.points.push(Point::from_xy(x1, y1));
228 self.points.push(Point::from_xy(x2, y2));
229 self.points.push(Point::from_xy(x, y));
230 }
231
232 pub(crate) fn cubic_to_pt(&mut self, p1: Point, p2: Point, p: Point) {
233 self.cubic_to(p1.x, p1.y, p2.x, p2.y, p.x, p.y);
234 }
235
236 /// Closes the current contour.
237 ///
238 /// A closed contour connects the first and the last Point
239 /// with a line, forming a continuous loop.
240 ///
241 /// Does nothing when `Path` is empty or already closed.
242 ///
243 /// Open and closed contour will be filled the same way.
244 /// Stroking an open contour will add LineCap at contour's start and end.
245 /// Stroking an closed contour will add LineJoin at contour's start and end.
246 pub fn close(&mut self) {
247 // don't add a close if it's the first verb or a repeat
248 if !self.verbs.is_empty() {
249 if self.verbs.last().cloned() != Some(PathVerb::Close) {
250 self.verbs.push(PathVerb::Close);
251 }
252 }
253
254 self.move_to_required = true;
255 }
256
257 /// Returns the last point if any.
258 pub fn last_point(&self) -> Option<Point> {
259 self.points.last().cloned()
260 }
261
262 pub(crate) fn set_last_point(&mut self, pt: Point) {
263 match self.points.last_mut() {
264 Some(last) => *last = pt,
265 None => self.move_to(pt.x, pt.y),
266 }
267 }
268
269 pub(crate) fn is_zero_length_since_point(&self, start_pt_index: usize) -> bool {
270 let count = self.points.len() - start_pt_index;
271 if count < 2 {
272 return true;
273 }
274
275 let first = self.points[start_pt_index];
276 for i in 1..count {
277 if first != self.points[start_pt_index + i] {
278 return false;
279 }
280 }
281
282 true
283 }
284
285 /// Adds a rectangle contour.
286 ///
287 /// The contour is closed and has a clock-wise direction.
288 pub fn push_rect(&mut self, rect: Rect) {
289 self.move_to(rect.left(), rect.top());
290 self.line_to(rect.right(), rect.top());
291 self.line_to(rect.right(), rect.bottom());
292 self.line_to(rect.left(), rect.bottom());
293 self.close();
294 }
295
296 /// Adds an oval contour bounded by the provided rectangle.
297 ///
298 /// The contour is closed and has a clock-wise direction.
299 pub fn push_oval(&mut self, oval: Rect) {
300 let cx = oval.left().half() + oval.right().half();
301 let cy = oval.top().half() + oval.bottom().half();
302
303 let oval_points = [
304 Point::from_xy(cx, oval.bottom()),
305 Point::from_xy(oval.left(), cy),
306 Point::from_xy(cx, oval.top()),
307 Point::from_xy(oval.right(), cy),
308 ];
309
310 let rect_points = [
311 Point::from_xy(oval.right(), oval.bottom()),
312 Point::from_xy(oval.left(), oval.bottom()),
313 Point::from_xy(oval.left(), oval.top()),
314 Point::from_xy(oval.right(), oval.top()),
315 ];
316
317 let weight = SCALAR_ROOT_2_OVER_2;
318 self.move_to(oval_points[3].x, oval_points[3].y);
319 for (p1, p2) in rect_points.iter().zip(oval_points.iter()) {
320 self.conic_points_to(*p1, *p2, weight);
321 }
322 self.close();
323 }
324
325 /// Adds a circle contour.
326 ///
327 /// The contour is closed and has a clock-wise direction.
328 ///
329 /// Does nothing when:
330 /// - `radius` <= 0
331 /// - any value is not finite or really large
332 pub fn push_circle(&mut self, x: f32, y: f32, r: f32) {
333 if let Some(r) = Rect::from_xywh(x - r, y - r, r + r, r + r) {
334 self.push_oval(r);
335 }
336 }
337
338 /// Adds a path.
339 pub fn push_path(&mut self, other: &Path) {
340 self.last_move_to_index = self.points.len();
341
342 self.verbs.extend_from_slice(&other.verbs);
343 self.points.extend_from_slice(&other.points);
344 }
345
346 pub(crate) fn push_path_builder(&mut self, other: &PathBuilder) {
347 if other.is_empty() {
348 return;
349 }
350
351 if self.last_move_to_index != 0 {
352 self.last_move_to_index = self.points.len() + other.last_move_to_index;
353 }
354
355 self.verbs.extend_from_slice(&other.verbs);
356 self.points.extend_from_slice(&other.points);
357 }
358
359 /// Appends, in a reverse order, the first contour of path ignoring path's last point.
360 pub(crate) fn reverse_path_to(&mut self, other: &PathBuilder) {
361 if other.is_empty() {
362 return;
363 }
364
365 debug_assert_eq!(other.verbs[0], PathVerb::Move);
366
367 let mut points_offset = other.points.len() - 1;
368 for verb in other.verbs.iter().rev() {
369 match verb {
370 PathVerb::Move => {
371 // if the path has multiple contours, stop after reversing the last
372 break;
373 }
374 PathVerb::Line => {
375 // We're moving one point back manually, to prevent points_offset overflow.
376 let pt = other.points[points_offset - 1];
377 points_offset -= 1;
378 self.line_to(pt.x, pt.y);
379 }
380 PathVerb::Quad => {
381 let pt1 = other.points[points_offset - 1];
382 let pt2 = other.points[points_offset - 2];
383 points_offset -= 2;
384 self.quad_to(pt1.x, pt1.y, pt2.x, pt2.y);
385 }
386 PathVerb::Cubic => {
387 let pt1 = other.points[points_offset - 1];
388 let pt2 = other.points[points_offset - 2];
389 let pt3 = other.points[points_offset - 3];
390 points_offset -= 3;
391 self.cubic_to(pt1.x, pt1.y, pt2.x, pt2.y, pt3.x, pt3.y);
392 }
393 PathVerb::Close => {}
394 }
395 }
396 }
397
398 /// Reset the builder.
399 ///
400 /// Memory is not deallocated.
401 pub fn clear(&mut self) {
402 self.verbs.clear();
403 self.points.clear();
404 self.last_move_to_index = 0;
405 self.move_to_required = true;
406 }
407
408 /// Finishes the builder and returns a `Path`.
409 ///
410 /// Returns `None` when `Path` is empty or has invalid bounds.
411 pub fn finish(self) -> Option<Path> {
412 if self.is_empty() {
413 return None;
414 }
415
416 // Just a move to? Bail.
417 if self.verbs.len() == 1 {
418 return None;
419 }
420
421 let bounds = Rect::from_points(&self.points)?;
422
423 Some(Path {
424 bounds,
425 verbs: self.verbs,
426 points: self.points,
427 })
428 }
429}
430