Skip to main content

alpha2_geometry/raster/
dense.rs

1//! The reference rasterizer, accumulating every pixel of the clip. [`super::rasterize`] must
2//! produce the same bytes.
3
4use super::accumulate::{accumulate_line, clip_line};
5use super::{TILE, TILE_BYTES, TileCoverage, TileState, coverage_byte, flatten, tile_aligned};
6use crate::IRect;
7use alpha2_model::FillRule;
8use kurbo::{BezPath, Point};
9
10/// Row-major coverage bytes of `path` in `clip` grown to whole tiles, with width and height.
11#[must_use]
12pub fn coverage_bytes(path: &BezPath, rule: FillRule, clip: IRect) -> (Vec<u8>, usize, usize) {
13    let clip = tile_aligned(clip);
14    let (w, h) = (clip.width() as usize, clip.height() as usize);
15    if w == 0 || h == 0 {
16        return (Vec::new(), w, h);
17    }
18    let mut lines = Vec::new();
19    flatten::flatten(
20        path,
21        Point::new(f64::from(clip.x0), f64::from(clip.y0)),
22        &mut lines,
23    );
24    let stride = w + 2;
25    let mut acc = vec![0.0f32; stride * h];
26    #[allow(clippy::cast_precision_loss)]
27    let (wf, hf, wf32) = (w as f64, h as f64, w as f32);
28    for line in lines {
29        clip_line(line, wf, hf, |p0, p1| {
30            accumulate_line(&mut acc, stride, 0, (0, h), wf32, p0, p1, |_, _, _| {});
31        });
32    }
33    let mut data = vec![0u8; w * h];
34    for y in 0..h {
35        let mut winding = 0.0f32;
36        let row = &acc[y * stride..y * stride + w];
37        let out = &mut data[y * w..y * w + w];
38        for (value, byte) in row.iter().zip(out.iter_mut()) {
39            winding += *value;
40            *byte = coverage_byte(rule, winding);
41        }
42    }
43    (data, w, h)
44}
45
46/// [`coverage_bytes`] cut into tiles as [`super::rasterize`] cuts them.
47#[must_use]
48pub fn rasterize_dense(path: &BezPath, rule: FillRule, clip: IRect) -> TileCoverage {
49    let aligned = tile_aligned(clip);
50    let (data, w, h) = coverage_bytes(path, rule, clip);
51    let tile = i32::try_from(TILE).unwrap_or(16);
52    let (tiles_x, tiles_y) = (w / TILE, h / TILE);
53    let mut out = TileCoverage {
54        origin: (aligned.x0.div_euclid(tile), aligned.y0.div_euclid(tile)),
55        size: (
56            u32::try_from(tiles_x).unwrap_or(0),
57            u32::try_from(tiles_y).unwrap_or(0),
58        ),
59        tiles: Vec::with_capacity(tiles_x * tiles_y),
60        partial: Vec::new(),
61    };
62    for ty in 0..tiles_y {
63        for tx in 0..tiles_x {
64            let mut bytes = [0u8; TILE_BYTES];
65            for r in 0..TILE {
66                let start = (ty * TILE + r) * w + tx * TILE;
67                bytes[r * TILE..r * TILE + TILE].copy_from_slice(&data[start..start + TILE]);
68            }
69            let state = if bytes.iter().all(|byte| *byte == 255) {
70                TileState::Full
71            } else if bytes.iter().all(|byte| *byte == 0) {
72                TileState::Empty
73            } else {
74                out.partial.push(bytes);
75                TileState::Partial(u32::try_from(out.partial.len() - 1).unwrap_or(u32::MAX))
76            };
77            out.tiles.push(state);
78        }
79    }
80    out
81}