zeno/
raster.rs

1//! Path rasterizer.
2
3#![allow(clippy::too_many_arguments)]
4
5use super::geometry::{Point, Vector};
6use super::path_builder::PathBuilder;
7use super::style::Fill;
8
9use crate::lib::Vec;
10use core::fmt;
11
12#[inline(always)]
13fn coverage(fill: Fill, mut coverage: i32) -> u8 {
14    coverage >>= PIXEL_BITS * 2 + 1 - 8;
15    if fill == Fill::EvenOdd {
16        coverage &= 511;
17        if coverage >= 256 {
18            coverage = 511i32.wrapping_sub(coverage);
19        }
20    } else {
21        if coverage < 0 {
22            coverage = !coverage;
23        }
24        if coverage >= 256 {
25            coverage = 255;
26        }
27    }
28    coverage as u8
29}
30
31pub struct Rasterizer<'a, S: RasterStorage> {
32    storage: &'a mut S,
33    xmin: i32,
34    xmax: i32,
35    ymin: i32,
36    ymax: i32,
37    height: i32,
38    shift: Vector,
39    start: FixedPoint,
40    closed: bool,
41    current: Point,
42    x: i32,
43    y: i32,
44    px: i32,
45    py: i32,
46    cover: i32,
47    area: i32,
48    invalid: bool,
49}
50
51impl<'a, S: RasterStorage> Rasterizer<'a, S> {
52    pub fn new(storage: &'a mut S) -> Self {
53        Self {
54            storage,
55            xmin: 0,
56            xmax: 0,
57            ymin: 0,
58            ymax: 0,
59            height: 0,
60            shift: Vector::ZERO,
61            start: FixedPoint::default(),
62            closed: false,
63            current: Point::ZERO,
64            x: 0,
65            y: 0,
66            px: 0,
67            py: 0,
68            cover: 0,
69            area: 0,
70            invalid: false,
71        }
72    }
73
74    pub fn rasterize(
75        &mut self,
76        shift: Vector,
77        width: u32,
78        height: u32,
79        apply: &mut impl FnMut(&mut Self),
80        fill: Fill,
81        buffer: &mut [u8],
82        pitch: usize,
83        y_up: bool,
84    ) {
85        let w = width as i32;
86        let h = height as i32;
87        self.storage
88            .reset(FixedPoint { x: 0, y: 0 }, FixedPoint { x: w, y: h });
89        self.shift = shift;
90        self.start = FixedPoint::default();
91        self.closed = true;
92        self.current = Point::ZERO;
93        self.xmin = 0;
94        self.ymin = 0;
95        self.xmax = w;
96        self.ymax = h;
97        self.height = h;
98        self.x = 0;
99        self.y = 0;
100        self.px = 0;
101        self.py = 0;
102        self.invalid = true;
103        apply(self);
104        if !self.closed {
105            self.line_to(self.start);
106        }
107        if !self.invalid {
108            self.storage.set(self.x, self.y, self.area, self.cover);
109        }
110        let indices = self.storage.indices();
111        let cells = self.storage.cells();
112        let min = FixedPoint::new(self.xmin, self.ymin);
113        let max = FixedPoint::new(self.xmax, self.ymax);
114        let height = height as usize;
115        for (i, &index) in indices.iter().enumerate() {
116            if index != -1 {
117                let y = ((i as i32) - min.y) as usize;
118                let row_offset = if y_up {
119                    pitch * (height - 1 - y)
120                } else {
121                    pitch * y
122                };
123                let row = &mut buffer[row_offset..];
124                let mut x = min.x;
125                let mut cover = 0;
126                let mut area;
127                let mut index = index;
128                loop {
129                    let cell = &cells[index as usize];
130                    if cover != 0 && cell.x > x {
131                        let count = (cell.x - x) as usize;
132                        let c = coverage(fill, cover);
133                        let xi = x as usize;
134                        for b in &mut row[xi..xi + count] {
135                            *b = c;
136                        }
137                    }
138                    cover = cover.wrapping_add(cell.cover.wrapping_mul(ONE_PIXEL * 2));
139                    area = cover.wrapping_sub(cell.area);
140                    if area != 0 && cell.x >= min.x {
141                        let count = 1;
142                        let c = coverage(fill, area);
143                        let xi = cell.x as usize;
144                        for b in &mut row[xi..xi + count] {
145                            *b = c;
146                        }
147                    }
148                    x = cell.x + 1;
149                    index = cell.next;
150                    if index == -1 {
151                        break;
152                    }
153                }
154                if cover != 0 {
155                    let count = (max.x - x) as usize;
156                    let c = coverage(fill, cover);
157                    let xi = x as usize;
158                    for b in &mut row[xi..xi + count] {
159                        *b = c;
160                    }
161                }
162            }
163        }
164    }
165
166    pub fn rasterize_write(
167        &mut self,
168        shift: Vector,
169        width: u32,
170        height: u32,
171        apply: &mut impl FnMut(&mut Self),
172        fill: Fill,
173        pitch: usize,
174        y_up: bool,
175        write: &mut impl FnMut(usize, usize, usize, u8),
176    ) {
177        let w = width as i32;
178        let h = height as i32;
179        self.storage
180            .reset(FixedPoint { x: 0, y: 0 }, FixedPoint { x: w, y: h });
181        self.shift = shift;
182        self.start = FixedPoint::default();
183        self.closed = true;
184        self.current = Point::ZERO;
185        self.xmin = 0;
186        self.ymin = 0;
187        self.xmax = w;
188        self.ymax = h;
189        self.height = h;
190        self.x = 0;
191        self.y = 0;
192        self.px = 0;
193        self.py = 0;
194        self.invalid = true;
195        apply(self);
196        if !self.closed {
197            self.line_to(self.start);
198        }
199        if !self.invalid {
200            self.storage.set(self.x, self.y, self.area, self.cover);
201        }
202        let indices = self.storage.indices();
203        let cells = self.storage.cells();
204        let min = FixedPoint::new(self.xmin, self.ymin);
205        let max = FixedPoint::new(self.xmax, self.ymax);
206        let height = height as usize;
207        for (i, &index) in indices.iter().enumerate() {
208            if index != -1 {
209                let y = ((i as i32) - min.y) as usize;
210                let row_offset = if y_up {
211                    pitch * (height - 1 - y)
212                } else {
213                    pitch * y
214                };
215                let mut x = min.x;
216                let mut cover = 0;
217                let mut area;
218                let mut index = index;
219                loop {
220                    let cell = &cells[index as usize];
221                    if cover != 0 && cell.x > x {
222                        let count = (cell.x - x) as usize;
223                        let c = coverage(fill, cover);
224                        let xi = x as usize;
225                        write(row_offset, xi, count, c);
226                    }
227                    cover = cover.wrapping_add(cell.cover.wrapping_mul(ONE_PIXEL * 2));
228                    area = cover.wrapping_sub(cell.area);
229                    if area != 0 && cell.x >= min.x {
230                        let count = 1;
231                        let c = coverage(fill, area);
232                        let xi = cell.x as usize;
233                        write(row_offset, xi, count, c);
234                    }
235                    x = cell.x + 1;
236                    index = cell.next;
237                    if index == -1 {
238                        break;
239                    }
240                }
241                if cover != 0 {
242                    let count = (max.x - x) as usize;
243                    let c = coverage(fill, cover);
244                    let xi = x as usize;
245                    write(row_offset, xi, count, c);
246                }
247            }
248        }
249    }
250
251    #[inline(always)]
252    fn set_cell(&mut self, x: i32, y: i32) {
253        if !self.invalid && (self.area != 0 || self.cover != 0) {
254            self.storage.set(self.x, self.y, self.area, self.cover);
255        }
256        self.area = 0;
257        self.cover = 0;
258        self.x = if x > (self.xmin - 1) {
259            x
260        } else {
261            self.xmin - 1
262        };
263        self.y = y;
264        self.invalid = y >= self.ymax || y < self.ymin || x >= self.xmax;
265    }
266
267    fn move_to(&mut self, to: FixedPoint) {
268        self.set_cell(trunc(to.x), trunc(to.y));
269        self.px = to.x;
270        self.py = to.y;
271    }
272
273    fn line_to(&mut self, to: FixedPoint) {
274        let to_x = to.x;
275        let to_y = to.y;
276        let mut ey1 = trunc(self.py);
277        let ey2 = trunc(to_y);
278        if (ey1 >= self.ymax && ey2 >= self.ymax) || (ey1 < self.ymin && ey2 < self.ymin) {
279            self.px = to_x;
280            self.py = to_y;
281            return;
282        }
283        let mut ex1 = trunc(self.px);
284        let ex2 = trunc(to_x);
285        let mut fx1 = fract(self.px);
286        let mut fy1 = fract(self.py);
287        let dx = to_x - self.px;
288        let dy = to_y - self.py;
289        if ex1 == ex2 && ey1 == ey2 {
290            // empty
291        } else if dy == 0 {
292            self.set_cell(ex2, ey2);
293            self.px = to_x;
294            self.py = to_y;
295            return;
296        } else if dx == 0 {
297            if dy > 0 {
298                loop {
299                    let fy2 = ONE_PIXEL;
300                    self.cover += fy2 - fy1;
301                    self.area += (fy2 - fy1) * fx1 * 2;
302                    fy1 = 0;
303                    ey1 += 1;
304                    self.set_cell(ex1, ey1);
305                    if ey1 == ey2 {
306                        break;
307                    }
308                }
309            } else {
310                loop {
311                    let fy2 = 0;
312                    self.cover += fy2 - fy1;
313                    self.area += (fy2 - fy1) * fx1 * 2;
314                    fy1 = ONE_PIXEL;
315                    ey1 -= 1;
316                    self.set_cell(ex1, ey1);
317                    if ey1 == ey2 {
318                        break;
319                    }
320                }
321            }
322        } else {
323            let mut prod = dx * fy1 - dy * fx1;
324            let dx_r = if ex1 != ex2 { (0x00FFFFFF) / dx } else { 0 };
325            let dy_r = if ey1 != ey2 { (0x00FFFFFF) / dy } else { 0 };
326            fn udiv(a: i32, b: i32) -> i32 {
327                ((a as u64 * b as u64) >> (4 * 8 - PIXEL_BITS)) as i32
328            }
329            loop {
330                if prod <= 0 && prod - dx * ONE_PIXEL > 0 {
331                    let fx2 = 0;
332                    let fy2 = udiv(-prod, -dx_r);
333                    prod -= dy * ONE_PIXEL;
334                    self.cover += fy2 - fy1;
335                    self.area += (fy2 - fy1) * (fx1 + fx2);
336                    fx1 = ONE_PIXEL;
337                    fy1 = fy2;
338                    ex1 -= 1;
339                } else if prod - dx * ONE_PIXEL <= 0 && prod - dx * ONE_PIXEL + dy * ONE_PIXEL > 0 {
340                    prod -= dx * ONE_PIXEL;
341                    let fx2 = udiv(-prod, dy_r);
342                    let fy2 = ONE_PIXEL;
343                    self.cover += fy2 - fy1;
344                    self.area += (fy2 - fy1) * (fx1 + fx2);
345                    fx1 = fx2;
346                    fy1 = 0;
347                    ey1 += 1;
348                } else if prod - dx * ONE_PIXEL + dy * ONE_PIXEL <= 0 && prod + dy * ONE_PIXEL >= 0
349                {
350                    prod += dy * ONE_PIXEL;
351                    let fx2 = ONE_PIXEL;
352                    let fy2 = udiv(prod, dx_r);
353                    self.cover += fy2 - fy1;
354                    self.area += (fy2 - fy1) * (fx1 + fx2);
355                    fx1 = 0;
356                    fy1 = fy2;
357                    ex1 += 1;
358                } else {
359                    let fx2 = udiv(prod, -dy_r);
360                    let fy2 = 0;
361                    prod += dx * ONE_PIXEL;
362                    self.cover += fy2 - fy1;
363                    self.area += (fy2 - fy1) * (fx1 + fx2);
364                    fx1 = fx2;
365                    fy1 = ONE_PIXEL;
366                    ey1 -= 1;
367                }
368                self.set_cell(ex1, ey1);
369                if ex1 == ex2 && ey1 == ey2 {
370                    break;
371                }
372            }
373        }
374        let fx2 = fract(to_x);
375        let fy2 = fract(to_y);
376        self.cover += fy2 - fy1;
377        self.area += (fy2 - fy1) * (fx1 + fx2);
378        self.px = to_x;
379        self.py = to_y;
380    }
381
382    #[allow(clippy::uninit_assumed_init, invalid_value)]
383    fn quad_to(&mut self, control: FixedPoint, to: FixedPoint) {
384        let mut arc = [FixedPoint::default(); 16 * 2 + 1];
385        arc[0].x = to.x;
386        arc[0].y = to.y;
387        arc[1].x = control.x;
388        arc[1].y = control.y;
389        arc[2].x = self.px;
390        arc[2].y = self.py;
391        if (trunc(arc[0].y) >= self.ymax
392            && trunc(arc[1].y) >= self.ymax
393            && trunc(arc[2].y) >= self.ymax)
394            || (trunc(arc[0].y) < self.ymin
395                && trunc(arc[1].y) < self.ymin
396                && trunc(arc[2].y) < self.ymin)
397        {
398            self.px = arc[0].x;
399            self.py = arc[0].y;
400            return;
401        }
402        let mut dx = (arc[2].x + arc[0].x - 2 * arc[1].x).abs();
403        let dy = (arc[2].y + arc[0].y - 2 * arc[1].y).abs();
404        if dx < dy {
405            dx = dy;
406        }
407        let mut draw = 1;
408        while dx > ONE_PIXEL / 4 {
409            dx >>= 2;
410            draw <<= 1;
411        }
412        let mut a = 0;
413        loop {
414            let mut split = draw & (-draw);
415            loop {
416                split >>= 1;
417                if split == 0 {
418                    break;
419                }
420                split_quad(&mut arc[a..]);
421                a += 2;
422            }
423            let p = arc[a];
424            self.line_to(p);
425            draw -= 1;
426            if draw == 0 {
427                break;
428            }
429            a -= 2;
430        }
431    }
432
433    #[allow(clippy::uninit_assumed_init, invalid_value)]
434    fn curve_to(&mut self, control1: FixedPoint, control2: FixedPoint, to: FixedPoint) {
435        let mut arc = [FixedPoint::default(); 16 * 8 + 1];
436        arc[0].x = to.x;
437        arc[0].y = to.y;
438        arc[1].x = control2.x;
439        arc[1].y = control2.y;
440        arc[2].x = control1.x;
441        arc[2].y = control1.y;
442        arc[3].x = self.px;
443        arc[3].y = self.py;
444        if (trunc(arc[0].y) >= self.ymax
445            && trunc(arc[1].y) >= self.ymax
446            && trunc(arc[2].y) >= self.ymax
447            && trunc(arc[3].y) >= self.ymax)
448            || (trunc(arc[0].y) < self.ymin
449                && trunc(arc[1].y) < self.ymin
450                && trunc(arc[2].y) < self.ymin
451                && trunc(arc[3].y) < self.ymin)
452        {
453            self.px = arc[0].x;
454            self.py = arc[0].y;
455            return;
456        }
457        let mut a = 0;
458        loop {
459            if (2 * arc[a].x - 3 * arc[a + 1].x + arc[a + 3].x).abs() > ONE_PIXEL / 2
460                || (2 * arc[a].y - 3 * arc[a + 1].y + arc[a + 3].y).abs() > ONE_PIXEL / 2
461                || (arc[a].x - 3 * arc[a + 2].x + 2 * arc[a + 3].x).abs() > ONE_PIXEL / 2
462                || (arc[a].y - 3 * arc[a + 2].y + 2 * arc[a + 3].y).abs() > ONE_PIXEL / 2
463            {
464                let buf = &mut arc[a..];
465                // if buf.len() < 7 {
466                //     return;
467                // }
468                if buf.len() >= 7 {
469                    split_cubic(buf);
470                    a += 3;
471                    continue;
472                } else {
473                    self.line_to(to);
474                    return;
475                }
476            }
477            let p = arc[a];
478            self.line_to(p);
479            if a == 0 {
480                return;
481            }
482            a -= 3;
483        }
484    }
485}
486
487impl<S: RasterStorage> PathBuilder for Rasterizer<'_, S> {
488    fn current_point(&self) -> Point {
489        self.current + self.shift
490    }
491
492    #[inline(always)]
493    fn move_to(&mut self, to: impl Into<Point>) -> &mut Self {
494        if !self.closed {
495            self.line_to(self.start);
496        }
497        let to = to.into();
498        let p = FixedPoint::from_point(to + self.shift);
499        self.move_to(p);
500        self.closed = false;
501        self.start = p;
502        self.current = to;
503        self
504    }
505
506    #[inline(always)]
507    fn line_to(&mut self, to: impl Into<Point>) -> &mut Self {
508        let to = to.into();
509        self.current = to;
510        self.closed = false;
511        self.line_to(FixedPoint::from_point(to + self.shift));
512        self
513    }
514
515    #[inline(always)]
516    fn quad_to(&mut self, control: impl Into<Point>, to: impl Into<Point>) -> &mut Self {
517        let to = to.into();
518        self.current = to;
519        self.closed = false;
520        self.quad_to(
521            FixedPoint::from_point(control.into() + self.shift),
522            FixedPoint::from_point(to + self.shift),
523        );
524        self
525    }
526
527    #[inline(always)]
528    fn curve_to(
529        &mut self,
530        control1: impl Into<Point>,
531        control2: impl Into<Point>,
532        to: impl Into<Point>,
533    ) -> &mut Self {
534        let to = to.into();
535        self.current = to;
536        self.closed = false;
537        self.curve_to(
538            FixedPoint::from_point(control1.into() + self.shift),
539            FixedPoint::from_point(control2.into() + self.shift),
540            FixedPoint::from_point(to + self.shift),
541        );
542        self
543    }
544
545    #[inline(always)]
546    fn close(&mut self) -> &mut Self {
547        self.line_to(self.start);
548        self.closed = true;
549        self
550    }
551}
552
553#[derive(Copy, Clone, Default)]
554pub struct Cell {
555    x: i32,
556    cover: i32,
557    area: i32,
558    next: i32,
559}
560
561pub trait RasterStorage {
562    fn reset(&mut self, min: FixedPoint, max: FixedPoint);
563    fn cells(&self) -> &[Cell];
564    fn indices(&self) -> &[i32];
565    fn set(&mut self, x: i32, y: i32, area: i32, cover: i32);
566}
567
568#[derive(Default)]
569pub struct HeapStorage {
570    min: FixedPoint,
571    max: FixedPoint,
572    cells: Vec<Cell>,
573    indices: Vec<i32>,
574}
575
576impl RasterStorage for HeapStorage {
577    fn reset(&mut self, min: FixedPoint, max: FixedPoint) {
578        self.min = min;
579        self.max = max;
580        self.cells.clear();
581        self.indices.clear();
582        self.indices.resize((max.y - min.y) as usize, -1);
583    }
584
585    fn cells(&self) -> &[Cell] {
586        &self.cells
587    }
588
589    fn indices(&self) -> &[i32] {
590        &self.indices
591    }
592
593    #[inline(always)]
594    #[allow(clippy::comparison_chain)]
595    fn set(&mut self, x: i32, y: i32, area: i32, cover: i32) {
596        let yindex = (y - self.min.y) as usize;
597        let mut cell_index = self.indices[yindex];
598        let mut last_index = -1;
599        while cell_index != -1 {
600            let cell = &mut self.cells[cell_index as usize];
601            if cell.x > x {
602                break;
603            } else if cell.x == x {
604                cell.area = cell.area.wrapping_add(area);
605                cell.cover = cell.cover.wrapping_add(cover);
606                return;
607            }
608            last_index = cell_index;
609            cell_index = cell.next;
610        }
611        let new_index = self.cells.len();
612        let cell = Cell {
613            x,
614            area,
615            cover,
616            next: cell_index,
617        };
618        if last_index != -1 {
619            self.cells[last_index as usize].next = new_index as i32;
620        } else {
621            self.indices[yindex] = new_index as i32;
622        }
623        self.cells.push(cell);
624    }
625}
626
627const MAX_CELLS: usize = 1024;
628const MAX_BAND: usize = 512;
629
630pub struct AdaptiveStorage {
631    min: FixedPoint,
632    max: FixedPoint,
633    height: usize,
634    cell_count: usize,
635    cells: [Cell; MAX_CELLS],
636    heap_cells: Vec<Cell>,
637    indices: [i32; MAX_BAND],
638    heap_indices: Vec<i32>,
639}
640
641impl AdaptiveStorage {
642    #[allow(clippy::uninit_assumed_init, invalid_value)]
643    pub fn new() -> Self {
644        Self {
645            min: FixedPoint::default(),
646            max: FixedPoint::default(),
647            height: 0,
648            cell_count: 0,
649            cells: [Default::default(); MAX_CELLS],
650            heap_cells: Vec::new(),
651            indices: [Default::default(); MAX_BAND],
652            heap_indices: Vec::new(),
653        }
654    }
655}
656
657impl RasterStorage for AdaptiveStorage {
658    fn reset(&mut self, min: FixedPoint, max: FixedPoint) {
659        self.min = min;
660        self.max = max;
661        self.height = (max.y - min.y) as usize;
662        self.cell_count = 0;
663        self.heap_cells.clear();
664        self.heap_indices.clear();
665        if self.height > MAX_BAND {
666            self.heap_indices.resize((max.y - min.y) as usize, -1);
667        } else {
668            for i in 0..self.height {
669                self.indices[i] = -1;
670            }
671        }
672    }
673
674    fn cells(&self) -> &[Cell] {
675        if self.cell_count > MAX_CELLS {
676            &self.heap_cells
677        } else {
678            &self.cells
679        }
680    }
681
682    fn indices(&self) -> &[i32] {
683        if self.height > MAX_BAND {
684            &self.heap_indices
685        } else {
686            &self.indices[..self.height]
687        }
688    }
689
690    #[inline(always)]
691    #[allow(clippy::comparison_chain)]
692    fn set(&mut self, x: i32, y: i32, area: i32, cover: i32) {
693        let yindex = (y - self.min.y) as usize;
694        let indices = if self.height > MAX_BAND {
695            &mut self.heap_indices[..]
696        } else {
697            &mut self.indices[..]
698        };
699        let cells = if !self.heap_cells.is_empty() {
700            &mut self.heap_cells[..]
701        } else {
702            &mut self.cells[..]
703        };
704        let mut cell_index = indices[yindex];
705        let mut last_index = -1;
706        while cell_index != -1 {
707            let cell = &mut cells[cell_index as usize];
708            if cell.x > x {
709                break;
710            } else if cell.x == x {
711                cell.area = cell.area.wrapping_add(area);
712                cell.cover = cell.cover.wrapping_add(cover);
713                return;
714            }
715            last_index = cell_index;
716            cell_index = cell.next;
717        }
718        let new_index = self.cell_count;
719        self.cell_count += 1;
720        let cell = Cell {
721            x,
722            area,
723            cover,
724            next: cell_index,
725        };
726        if last_index != -1 {
727            cells[last_index as usize].next = new_index as i32;
728        } else {
729            indices[yindex] = new_index as i32;
730        }
731        if new_index < MAX_CELLS {
732            cells[new_index] = cell;
733        } else {
734            if self.heap_cells.is_empty() {
735                self.heap_cells.extend_from_slice(&self.cells);
736            }
737            self.heap_cells.push(cell);
738        }
739    }
740}
741
742const _MAX_DIM: u32 = i16::MAX as u32;
743
744fn split_quad(base: &mut [FixedPoint]) {
745    let mut a;
746    let mut b;
747    base[4].x = base[2].x;
748    a = base[0].x + base[1].x;
749    b = base[1].x + base[2].x;
750    base[3].x = b >> 1;
751    base[2].x = (a + b) >> 2;
752    base[1].x = a >> 1;
753    base[4].y = base[2].y;
754    a = base[0].y + base[1].y;
755    b = base[1].y + base[2].y;
756    base[3].y = b >> 1;
757    base[2].y = (a + b) >> 2;
758    base[1].y = a >> 1;
759}
760
761fn split_cubic(base: &mut [FixedPoint]) {
762    let mut a;
763    let mut b;
764    let mut c;
765    base[6].x = base[3].x;
766    a = base[0].x + base[1].x;
767    b = base[1].x + base[2].x;
768    c = base[2].x + base[3].x;
769    base[5].x = c >> 1;
770    c += b;
771    base[4].x = c >> 2;
772    base[1].x = a >> 1;
773    a += b;
774    base[2].x = a >> 2;
775    base[3].x = (a + c) >> 3;
776    base[6].y = base[3].y;
777    a = base[0].y + base[1].y;
778    b = base[1].y + base[2].y;
779    c = base[2].y + base[3].y;
780    base[5].y = c >> 1;
781    c += b;
782    base[4].y = c >> 2;
783    base[1].y = a >> 1;
784    a += b;
785    base[2].y = a >> 2;
786    base[3].y = (a + c) >> 3;
787}
788
789#[derive(Copy, Clone, Default)]
790pub struct FixedPoint {
791    pub x: i32,
792    pub y: i32,
793}
794
795impl fmt::Debug for FixedPoint {
796    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
797        write!(f, "({}, {})", self.x, self.y)
798    }
799}
800
801impl FixedPoint {
802    pub fn new(x: i32, y: i32) -> Self {
803        Self { x, y }
804    }
805
806    #[inline(always)]
807    pub fn from_point(p: Point) -> Self {
808        Self {
809            x: to_fixed(p.x),
810            y: to_fixed(p.y),
811        }
812    }
813}
814
815#[inline(always)]
816fn to_fixed(v: f32) -> i32 {
817    unsafe { (v * 256.).to_int_unchecked() }
818}
819
820const PIXEL_BITS: i32 = 8;
821const ONE_PIXEL: i32 = 1 << PIXEL_BITS;
822
823#[inline(always)]
824fn trunc(x: i32) -> i32 {
825    x >> PIXEL_BITS
826}
827
828#[inline(always)]
829fn fract(x: i32) -> i32 {
830    x & (ONE_PIXEL - 1)
831}