1use alloc::vec::Vec;
8
9use ruisa_path::{PathVerb, ScreenIntRect};
10
11use crate::{Path, Point};
12
13use crate::edge::{CubicEdge, Edge, LineEdge, QuadraticEdge};
14use crate::edge_clipper::EdgeClipperIter;
15use crate::path_geometry;
16
17#[derive(Copy, Clone, PartialEq, Debug)]
18enum Combine {
19 No,
20 Partial,
21 Total,
22}
23
24#[derive(Copy, Clone, Debug)]
25pub struct ShiftedIntRect {
26 shifted: ScreenIntRect,
27 shift: i32,
28}
29
30impl ShiftedIntRect {
31 pub fn new(rect: &ScreenIntRect, shift: i32) -> Option<Self> {
32 let shifted = ScreenIntRect::from_xywh(
33 rect.x() << shift,
34 rect.y() << shift,
35 rect.width() << shift,
36 rect.height() << shift,
37 )?;
38 Some(ShiftedIntRect { shifted, shift })
39 }
40
41 pub fn shifted(&self) -> &ScreenIntRect {
42 &self.shifted
43 }
44
45 pub fn recover(&self) -> ScreenIntRect {
46 ScreenIntRect::from_xywh(
47 self.shifted.x() >> self.shift,
48 self.shifted.y() >> self.shift,
49 self.shifted.width() >> self.shift,
50 self.shifted.height() >> self.shift,
51 )
52 .unwrap() }
54}
55
56pub struct BasicEdgeBuilder {
57 edges: Vec<Edge>,
58 clip_shift: i32,
59}
60
61impl BasicEdgeBuilder {
62 pub fn new(clip_shift: i32) -> Self {
63 BasicEdgeBuilder {
64 edges: Vec::with_capacity(64), clip_shift,
66 }
67 }
68
69 pub fn build_edges(
72 path: &Path,
73 clip: Option<&ShiftedIntRect>,
74 clip_shift: i32,
75 ) -> Option<Vec<Edge>> {
76 let can_cull_to_the_right = false; let mut builder = BasicEdgeBuilder::new(clip_shift);
81 if !builder.build(path, clip, can_cull_to_the_right) {
82 log::warn!("infinite or NaN segments detected during edges building");
83 return None;
84 }
85
86 if builder.edges.len() < 2 {
87 return None;
88 }
89
90 Some(builder.edges)
91 }
92
93 pub fn build(
95 &mut self,
96 path: &Path,
97 clip: Option<&ShiftedIntRect>,
98 can_cull_to_the_right: bool,
99 ) -> bool {
100 if let Some(clip) = clip {
101 let clip = clip.recover().to_rect();
102 for edges in EdgeClipperIter::new(path, clip, can_cull_to_the_right) {
103 for edge in edges {
104 match edge {
105 PathEdge::LineTo(p0, p1) => {
106 if !p0.is_finite() || !p1.is_finite() {
107 return false;
108 }
109
110 self.push_line(&[p0, p1])
111 }
112 PathEdge::QuadTo(p0, p1, p2) => {
113 if !p0.is_finite() || !p1.is_finite() || !p2.is_finite() {
114 return false;
115 }
116
117 self.push_quad(&[p0, p1, p2])
118 }
119 PathEdge::CubicTo(p0, p1, p2, p3) => {
120 if !p0.is_finite()
121 || !p1.is_finite()
122 || !p2.is_finite()
123 || !p3.is_finite()
124 {
125 return false;
126 }
127
128 self.push_cubic(&[p0, p1, p2, p3])
129 }
130 }
131 }
132 }
133 } else {
134 for edge in edge_iter(path) {
135 match edge {
136 PathEdge::LineTo(p0, p1) => {
137 self.push_line(&[p0, p1]);
138 }
139 PathEdge::QuadTo(p0, p1, p2) => {
140 let points = [p0, p1, p2];
141 let mut mono_x = [Point::zero(); 5];
142 let n = path_geometry::chop_quad_at_y_extrema(&points, &mut mono_x);
143 for i in 0..=n {
144 self.push_quad(&mono_x[i * 2..]);
145 }
146 }
147 PathEdge::CubicTo(p0, p1, p2, p3) => {
148 let points = [p0, p1, p2, p3];
149 let mut mono_y = [Point::zero(); 10];
150 let n = path_geometry::chop_cubic_at_y_extrema(&points, &mut mono_y);
151 for i in 0..=n {
152 self.push_cubic(&mono_y[i * 3..]);
153 }
154 }
155 }
156 }
157 }
158
159 true
160 }
161
162 fn push_line(&mut self, points: &[Point; 2]) {
163 if let Some(edge) = LineEdge::new(points[0], points[1], self.clip_shift) {
164 let combine = if edge.is_vertical() && !self.edges.is_empty() {
165 if let Some(Edge::Line(last)) = self.edges.last_mut() {
166 combine_vertical(&edge, last)
167 } else {
168 Combine::No
169 }
170 } else {
171 Combine::No
172 };
173
174 match combine {
175 Combine::Total => {
176 self.edges.pop();
177 }
178 Combine::Partial => {}
179 Combine::No => self.edges.push(Edge::Line(edge)),
180 }
181 }
182 }
183
184 fn push_quad(&mut self, points: &[Point]) {
185 if let Some(edge) = QuadraticEdge::new(points, self.clip_shift) {
186 self.edges.push(Edge::Quadratic(edge));
187 }
188 }
189
190 fn push_cubic(&mut self, points: &[Point]) {
191 if let Some(edge) = CubicEdge::new(points, self.clip_shift) {
192 self.edges.push(Edge::Cubic(edge));
193 }
194 }
195}
196
197fn combine_vertical(edge: &LineEdge, last: &mut LineEdge) -> Combine {
198 if last.dx != 0 || edge.x != last.x {
199 return Combine::No;
200 }
201
202 if edge.winding == last.winding {
203 return if edge.last_y + 1 == last.first_y {
204 last.first_y = edge.first_y;
205 Combine::Partial
206 } else if edge.first_y == last.last_y + 1 {
207 last.last_y = edge.last_y;
208 Combine::Partial
209 } else {
210 Combine::No
211 };
212 }
213
214 if edge.first_y == last.first_y {
215 return if edge.last_y == last.last_y {
216 Combine::Total
217 } else if edge.last_y < last.last_y {
218 last.first_y = edge.last_y + 1;
219 Combine::Partial
220 } else {
221 last.first_y = last.last_y + 1;
222 last.last_y = edge.last_y;
223 last.winding = edge.winding;
224 Combine::Partial
225 };
226 }
227
228 if edge.last_y == last.last_y {
229 if edge.first_y > last.first_y {
230 last.last_y = edge.first_y - 1;
231 } else {
232 last.last_y = last.first_y - 1;
233 last.first_y = edge.first_y;
234 last.winding = edge.winding;
235 }
236
237 return Combine::Partial;
238 }
239
240 Combine::No
241}
242
243pub fn edge_iter(path: &Path) -> PathEdgeIter {
244 PathEdgeIter {
245 path,
246 verb_index: 0,
247 points_index: 0,
248 move_to: Point::zero(),
249 needs_close_line: false,
250 }
251}
252
253#[derive(Copy, Clone, PartialEq, Debug)]
254pub enum PathEdge {
255 LineTo(Point, Point),
256 QuadTo(Point, Point, Point),
257 CubicTo(Point, Point, Point, Point),
258}
259
260pub struct PathEdgeIter<'a> {
264 path: &'a Path,
265 verb_index: usize,
266 points_index: usize,
267 move_to: Point,
268 needs_close_line: bool,
269}
270
271impl<'a> PathEdgeIter<'a> {
272 fn close_line(&mut self) -> Option<PathEdge> {
273 self.needs_close_line = false;
274
275 let edge = PathEdge::LineTo(self.path.points()[self.points_index - 1], self.move_to);
276 Some(edge)
277 }
278}
279
280impl<'a> Iterator for PathEdgeIter<'a> {
281 type Item = PathEdge;
282
283 fn next(&mut self) -> Option<Self::Item> {
284 if self.verb_index < self.path.verbs().len() {
285 let verb = self.path.verbs()[self.verb_index];
286 self.verb_index += 1;
287
288 match verb {
289 PathVerb::Move => {
290 if self.needs_close_line {
291 let res = self.close_line();
292 self.move_to = self.path.points()[self.points_index];
293 self.points_index += 1;
294 return res;
295 }
296
297 self.move_to = self.path.points()[self.points_index];
298 self.points_index += 1;
299 self.next()
300 }
301 PathVerb::Close => {
302 if self.needs_close_line {
303 return self.close_line();
304 }
305
306 self.next()
307 }
308 _ => {
309 self.needs_close_line = true;
311
312 let edge;
313 match verb {
314 PathVerb::Line => {
315 edge = PathEdge::LineTo(
316 self.path.points()[self.points_index - 1],
317 self.path.points()[self.points_index + 0],
318 );
319 self.points_index += 1;
320 }
321 PathVerb::Quad => {
322 edge = PathEdge::QuadTo(
323 self.path.points()[self.points_index - 1],
324 self.path.points()[self.points_index + 0],
325 self.path.points()[self.points_index + 1],
326 );
327 self.points_index += 2;
328 }
329 PathVerb::Cubic => {
330 edge = PathEdge::CubicTo(
331 self.path.points()[self.points_index - 1],
332 self.path.points()[self.points_index + 0],
333 self.path.points()[self.points_index + 1],
334 self.path.points()[self.points_index + 2],
335 );
336 self.points_index += 3;
337 }
338 _ => unreachable!(),
339 };
340
341 Some(edge)
342 }
343 }
344 } else if self.needs_close_line {
345 self.close_line()
346 } else {
347 None
348 }
349 }
350}