pub fn dilate(mask: &[bool], width: u32, height: u32, radius: u32) -> Vec<bool> {
if radius == 0 {
return mask.to_vec();
}
let w = width as usize;
let h = height as usize;
let r = radius as usize;
let mut horiz = vec![false; w * h];
for y in 0..h {
let row_start = y * w;
let mut count = 0usize;
for wx in 0..=(r.min(w - 1)) {
if mask[row_start + wx] {
count += 1;
}
}
for x in 0..w {
if count > 0 {
horiz[row_start + x] = true;
}
let add_x = x + r + 1;
if add_x < w && mask[row_start + add_x] {
count += 1;
}
if x >= r {
let remove_x = x - r;
if mask[row_start + remove_x] {
count -= 1;
}
}
}
}
let mut result = vec![false; w * h];
for x in 0..w {
let mut count = 0usize;
for wy in 0..=(r.min(h - 1)) {
if horiz[wy * w + x] {
count += 1;
}
}
for y in 0..h {
if count > 0 {
result[y * w + x] = true;
}
let add_y = y + r + 1;
if add_y < h && horiz[add_y * w + x] {
count += 1;
}
if y >= r {
let remove_y = y - r;
if horiz[remove_y * w + x] {
count -= 1;
}
}
}
}
result
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn radius_zero_is_noop() {
let mask = vec![false, true, false, false];
let result = dilate(&mask, 2, 2, 0);
assert_eq!(result, mask);
}
#[test]
fn single_pixel_expands() {
let mut mask = vec![false; 25];
mask[2 * 5 + 2] = true;
let result = dilate(&mask, 5, 5, 1);
assert!(result[2 * 5 + 2]); assert!(result[1 * 5 + 2]); assert!(result[3 * 5 + 2]); assert!(result[2 * 5 + 1]); assert!(result[2 * 5 + 3]); assert!(result[1 * 5 + 1]); assert!(result[1 * 5 + 3]); assert!(result[3 * 5 + 1]); assert!(result[3 * 5 + 3]); }
#[test]
fn nearby_pixels_merge() {
let mask = vec![true, false, false, false, true];
let result = dilate(&mask, 5, 1, 2);
assert!(result.iter().all(|&v| v));
}
#[test]
fn distant_pixels_stay_separate() {
let mut mask = vec![false; 10];
mask[0] = true;
mask[9] = true;
let result = dilate(&mask, 10, 1, 2);
assert!(!result[4]);
assert!(!result[5]);
}
}