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