alpha2_geometry/raster/
dense.rs1use 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#[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#[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}