1use crate::Point;
8
9use crate::fixed_point::{fdot16, fdot6, FDot16, FDot6};
10use crate::math::left_shift;
11
12const MAX_COEFF_SHIFT: i32 = 6;
18
19#[derive(Clone, Debug)]
20pub enum Edge {
21 Line(LineEdge),
22 Quadratic(QuadraticEdge),
23 Cubic(CubicEdge),
24}
25
26impl Edge {
27 pub fn as_line(&self) -> &LineEdge {
28 match self {
29 Edge::Line(line) => line,
30 Edge::Quadratic(quad) => &quad.line,
31 Edge::Cubic(cubic) => &cubic.line,
32 }
33 }
34
35 pub fn as_line_mut(&mut self) -> &mut LineEdge {
36 match self {
37 Edge::Line(line) => line,
38 Edge::Quadratic(quad) => &mut quad.line,
39 Edge::Cubic(cubic) => &mut cubic.line,
40 }
41 }
42}
43
44impl core::ops::Deref for Edge {
45 type Target = LineEdge;
46
47 fn deref(&self) -> &Self::Target {
48 self.as_line()
49 }
50}
51
52impl core::ops::DerefMut for Edge {
53 fn deref_mut(&mut self) -> &mut Self::Target {
54 self.as_line_mut()
55 }
56}
57
58#[derive(Clone, Default, Debug)]
59pub struct LineEdge {
60 pub prev: Option<u32>,
62 pub next: Option<u32>,
63
64 pub x: FDot16,
65 pub dx: FDot16,
66 pub first_y: i32,
67 pub last_y: i32,
68 pub winding: i8, }
70
71impl LineEdge {
72 pub fn new(p0: Point, p1: Point, shift: i32) -> Option<Self> {
73 let scale = (1 << (shift + 6)) as f32;
74 let mut x0 = (p0.x * scale) as i32;
75 let mut y0 = (p0.y * scale) as i32;
76 let mut x1 = (p1.x * scale) as i32;
77 let mut y1 = (p1.y * scale) as i32;
78
79 let mut winding = 1;
80
81 if y0 > y1 {
82 core::mem::swap(&mut x0, &mut x1);
83 core::mem::swap(&mut y0, &mut y1);
84 winding = -1;
85 }
86
87 let top = fdot6::round(y0);
88 let bottom = fdot6::round(y1);
89
90 if top == bottom {
92 return None;
93 }
94
95 let slope = fdot6::div(x1 - x0, y1 - y0);
96 let dy = compute_dy(top, y0);
97
98 Some(LineEdge {
99 next: None,
100 prev: None,
101 x: fdot6::to_fdot16(x0 + fdot16::mul(slope, dy)),
102 dx: slope,
103 first_y: top,
104 last_y: bottom - 1,
105 winding,
106 })
107 }
108
109 pub fn is_vertical(&self) -> bool {
110 self.dx == 0
111 }
112
113 fn update(&mut self, mut x0: FDot16, mut y0: FDot16, mut x1: FDot16, mut y1: FDot16) -> bool {
114 debug_assert!(self.winding == 1 || self.winding == -1);
115
116 y0 >>= 10;
117 y1 >>= 10;
118
119 debug_assert!(y0 <= y1);
120
121 let top = fdot6::round(y0);
122 let bottom = fdot6::round(y1);
123
124 if top == bottom {
126 return false;
127 }
128
129 x0 >>= 10;
130 x1 >>= 10;
131
132 let slope = fdot6::div(x1 - x0, y1 - y0);
133 let dy = compute_dy(top, y0);
134
135 self.x = fdot6::to_fdot16(x0 + fdot16::mul(slope, dy));
136 self.dx = slope;
137 self.first_y = top;
138 self.last_y = bottom - 1;
139
140 true
141 }
142}
143
144#[derive(Clone, Debug)]
145pub struct QuadraticEdge {
146 pub line: LineEdge,
147 pub curve_count: i8,
148 curve_shift: u8, qx: FDot16,
150 qy: FDot16,
151 qdx: FDot16,
152 qdy: FDot16,
153 qddx: FDot16,
154 qddy: FDot16,
155 q_last_x: FDot16,
156 q_last_y: FDot16,
157}
158
159impl QuadraticEdge {
160 pub fn new(points: &[Point], shift: i32) -> Option<Self> {
161 let mut quad = Self::new2(points, shift)?;
162 if quad.update() {
163 Some(quad)
164 } else {
165 None
166 }
167 }
168
169 fn new2(points: &[Point], mut shift: i32) -> Option<Self> {
170 let scale = (1 << (shift + 6)) as f32;
171 let mut x0 = (points[0].x * scale) as i32;
172 let mut y0 = (points[0].y * scale) as i32;
173 let x1 = (points[1].x * scale) as i32;
174 let y1 = (points[1].y * scale) as i32;
175 let mut x2 = (points[2].x * scale) as i32;
176 let mut y2 = (points[2].y * scale) as i32;
177
178 let mut winding = 1;
179 if y0 > y2 {
180 core::mem::swap(&mut x0, &mut x2);
181 core::mem::swap(&mut y0, &mut y2);
182 winding = -1;
183 }
184 debug_assert!(y0 <= y1 && y1 <= y2);
185
186 let top = fdot6::round(y0);
187 let bottom = fdot6::round(y2);
188
189 if top == bottom {
191 return None;
192 }
193
194 {
196 let dx = (left_shift(x1, 1) - x0 - x2) >> 2;
197 let dy = (left_shift(y1, 1) - y0 - y2) >> 2;
198 shift = diff_to_shift(dx, dy, shift);
202 debug_assert!(shift >= 0);
203 }
204
205 if shift == 0 {
207 shift = 1;
208 } else if shift > MAX_COEFF_SHIFT {
209 shift = MAX_COEFF_SHIFT;
210 }
211
212 let curve_count = (1 << shift) as i8;
213
214 let curve_shift = (shift - 1) as u8;
231
232 let mut a = fdot6_to_fixed_div2(x0 - x1 - x1 + x2); let mut b = fdot6::to_fdot16(x1 - x0); let qx = fdot6::to_fdot16(x0);
236 let qdx = b + (a >> shift); let qddx = a >> (shift - 1); a = fdot6_to_fixed_div2(y0 - y1 - y1 + y2); b = fdot6::to_fdot16(y1 - y0); let qy = fdot6::to_fdot16(y0);
243 let qdy = b + (a >> shift); let qddy = a >> (shift - 1); let q_last_x = fdot6::to_fdot16(x2);
247 let q_last_y = fdot6::to_fdot16(y2);
248
249 Some(QuadraticEdge {
250 line: LineEdge {
251 next: None,
252 prev: None,
253 x: 0,
254 dx: 0,
255 first_y: 0,
256 last_y: 0,
257 winding,
258 },
259 curve_count,
260 curve_shift,
261 qx,
262 qy,
263 qdx,
264 qdy,
265 qddx,
266 qddy,
267 q_last_x,
268 q_last_y,
269 })
270 }
271
272 pub fn update(&mut self) -> bool {
273 let mut success;
274 let mut count = self.curve_count;
275 let mut oldx = self.qx;
276 let mut oldy = self.qy;
277 let mut dx = self.qdx;
278 let mut dy = self.qdy;
279 let mut newx;
280 let mut newy;
281 let shift = self.curve_shift;
282
283 debug_assert!(count > 0);
284
285 loop {
286 count -= 1;
287 if count > 0 {
288 newx = oldx + (dx >> shift);
289 dx += self.qddx;
290 newy = oldy + (dy >> shift);
291 dy += self.qddy;
292 } else {
293 newx = self.q_last_x;
295 newy = self.q_last_y;
296 }
297 success = self.line.update(oldx, oldy, newx, newy);
298 oldx = newx;
299 oldy = newy;
300
301 if count == 0 || success {
302 break;
303 }
304 }
305
306 self.qx = newx;
307 self.qy = newy;
308 self.qdx = dx;
309 self.qdy = dy;
310 self.curve_count = count;
311
312 success
313 }
314}
315
316#[derive(Clone, Debug)]
317pub struct CubicEdge {
318 pub line: LineEdge,
319 pub curve_count: i8,
320 curve_shift: u8, dshift: u8, cx: FDot16,
323 cy: FDot16,
324 cdx: FDot16,
325 cdy: FDot16,
326 cddx: FDot16,
327 cddy: FDot16,
328 cdddx: FDot16,
329 cdddy: FDot16,
330 c_last_x: FDot16,
331 c_last_y: FDot16,
332}
333
334impl CubicEdge {
335 pub fn new(points: &[Point], shift: i32) -> Option<Self> {
336 let mut cubic = Self::new2(points, shift, true)?;
337 if cubic.update() {
338 Some(cubic)
339 } else {
340 None
341 }
342 }
343
344 fn new2(points: &[Point], mut shift: i32, sort_y: bool) -> Option<Self> {
345 let scale = (1 << (shift + 6)) as f32;
346 let mut x0 = (points[0].x * scale) as i32;
347 let mut y0 = (points[0].y * scale) as i32;
348 let mut x1 = (points[1].x * scale) as i32;
349 let mut y1 = (points[1].y * scale) as i32;
350 let mut x2 = (points[2].x * scale) as i32;
351 let mut y2 = (points[2].y * scale) as i32;
352 let mut x3 = (points[3].x * scale) as i32;
353 let mut y3 = (points[3].y * scale) as i32;
354
355 let mut winding = 1;
356 if sort_y && y0 > y3 {
357 core::mem::swap(&mut x0, &mut x3);
358 core::mem::swap(&mut x1, &mut x2);
359 core::mem::swap(&mut y0, &mut y3);
360 core::mem::swap(&mut y1, &mut y2);
361 winding = -1;
362 }
363
364 let top = fdot6::round(y0);
365 let bot = fdot6::round(y3);
366
367 if sort_y && top == bot {
369 return None;
370 }
371
372 {
374 let dx = cubic_delta_from_line(x0, x1, x2, x3);
378 let dy = cubic_delta_from_line(y0, y1, y2, y3);
379 shift = diff_to_shift(dx, dy, 2) + 1;
381 }
382 debug_assert!(shift > 0);
384 if shift > MAX_COEFF_SHIFT {
385 shift = MAX_COEFF_SHIFT;
386 }
387
388 let mut up_shift = 6; let mut down_shift = shift + up_shift - 10;
393 if down_shift < 0 {
394 down_shift = 0;
395 up_shift = 10 - shift;
396 }
397
398 let curve_count = left_shift(-1, shift) as i8;
399 let curve_shift = shift as u8;
400 let dshift = down_shift as u8;
401
402 let mut b = fdot6_up_shift(3 * (x1 - x0), up_shift);
403 let mut c = fdot6_up_shift(3 * (x0 - x1 - x1 + x2), up_shift);
404 let mut d = fdot6_up_shift(x3 + 3 * (x1 - x2) - x0, up_shift);
405
406 let cx = fdot6::to_fdot16(x0);
407 let cdx = b + (c >> shift) + (d >> (2 * shift)); let cddx = 2 * c + ((3 * d) >> (shift - 1)); let cdddx = (3 * d) >> (shift - 1); b = fdot6_up_shift(3 * (y1 - y0), up_shift);
412 c = fdot6_up_shift(3 * (y0 - y1 - y1 + y2), up_shift);
413 d = fdot6_up_shift(y3 + 3 * (y1 - y2) - y0, up_shift);
414
415 let cy = fdot6::to_fdot16(y0);
416 let cdy = b + (c >> shift) + (d >> (2 * shift)); let cddy = 2 * c + ((3 * d) >> (shift - 1)); let cdddy = (3 * d) >> (shift - 1); let c_last_x = fdot6::to_fdot16(x3);
421 let c_last_y = fdot6::to_fdot16(y3);
422
423 Some(CubicEdge {
424 line: LineEdge {
425 next: None,
426 prev: None,
427 x: 0,
428 dx: 0,
429 first_y: 0,
430 last_y: 0,
431 winding,
432 },
433 curve_count,
434 curve_shift,
435 dshift,
436 cx,
437 cy,
438 cdx,
439 cdy,
440 cddx,
441 cddy,
442 cdddx,
443 cdddy,
444 c_last_x,
445 c_last_y,
446 })
447 }
448
449 pub fn update(&mut self) -> bool {
450 let mut success;
451 let mut count = self.curve_count;
452 let mut oldx = self.cx;
453 let mut oldy = self.cy;
454 let mut newx;
455 let mut newy;
456 let ddshift = self.curve_shift;
457 let dshift = self.dshift;
458
459 debug_assert!(count < 0);
460
461 loop {
462 count += 1;
463 if count < 0 {
464 newx = oldx + (self.cdx >> dshift);
465 self.cdx += self.cddx >> ddshift;
466 self.cddx += self.cdddx;
467
468 newy = oldy + (self.cdy >> dshift);
469 self.cdy += self.cddy >> ddshift;
470 self.cddy += self.cdddy;
471 } else {
472 newx = self.c_last_x;
474 newy = self.c_last_y;
475 }
476
477 if newy < oldy {
480 newy = oldy;
481 }
482
483 success = self.line.update(oldx, oldy, newx, newy);
484 oldx = newx;
485 oldy = newy;
486
487 if count == 0 || success {
488 break;
489 }
490 }
491
492 self.cx = newx;
493 self.cy = newy;
494 self.curve_count = count;
495
496 success
497 }
498}
499
500fn compute_dy(top: FDot6, y0: FDot6) -> FDot6 {
502 left_shift(top, 6) + 32 - y0
503}
504
505fn diff_to_shift(dx: FDot6, dy: FDot6, shift_aa: i32) -> i32 {
506 let mut dist = cheap_distance(dx, dy);
508
509 dist = (dist + (1 << 4)) >> (3 + shift_aa);
516
517 (32 - dist.leading_zeros() as i32) >> 1
519}
520
521fn cheap_distance(mut dx: FDot6, mut dy: FDot6) -> FDot6 {
522 dx = dx.abs();
523 dy = dy.abs();
524 if dx > dy {
526 dx + (dy >> 1)
527 } else {
528 dy + (dx >> 1)
529 }
530}
531
532fn fdot6_to_fixed_div2(value: FDot6) -> FDot16 {
542 left_shift(value, 16 - 6 - 1)
545}
546
547fn fdot6_up_shift(x: FDot6, up_shift: i32) -> i32 {
548 debug_assert!((left_shift(x, up_shift) >> up_shift) == x);
549 left_shift(x, up_shift)
550}
551
552fn cubic_delta_from_line(a: FDot6, b: FDot6, c: FDot6, d: FDot6) -> FDot6 {
560 let one_third = ((a * 8 - b * 15 + 6 * c + d) * 19) >> 9;
562 let two_third = ((a + 6 * b - c * 15 + d * 8) * 19) >> 9;
563
564 one_third.abs().max(two_third.abs())
565}