pub fn dilate(bitmap: &[u8], width: usize, height: usize, radius: u8) -> Vec { if radius == 0 || bitmap.is_empty() { return bitmap.to_vec(); } let r = radius as isize; let mut out = vec![0u8; width * height]; for y in 0..height { for x in 0..width { let mut max_val = bitmap[y * width + x]; for dy in -r..=r { for dx in -r..=r { let ny = (y as isize + dy).clamp(0, height as isize - 1) as usize; let nx = (x as isize + dx).clamp(0, width as isize - 1) as usize; max_val = max_val.max(bitmap[ny * width + nx]); } } out[y * width + x] = max_val; } } out } pub fn thicken_light(bitmap: &[u8], width: usize, height: usize, strength: f32) -> Vec { if strength <= 0.0 || bitmap.is_empty() { return bitmap.to_vec(); } let mut out = bitmap.to_vec(); let threshold = 64u8; let strength_byte = (strength * 255.0).clamp(0.0, 255.0) as u8; for y in 0..height { for x in 0..width { let idx = y * width + x; if bitmap[idx] >= threshold { continue; } let mut neighbor_max = 0u8; for dy in -1..=1isize { for dx in -1..=1isize { if dy == 0 && dx == 0 { continue; } let ny = (y as isize + dy).clamp(0, height as isize - 1) as usize; let nx = (x as isize + dx).clamp(0, width as isize - 1) as usize; neighbor_max = neighbor_max.max(bitmap[ny * width + nx]); } } if neighbor_max > threshold { out[idx] = out[idx].saturating_add(strength_byte); } } } out } pub fn r8_to_rgb(r8: &[u8]) -> Vec { let mut rgb = Vec::with_capacity(r8.len() * 3); for &v in r8 { rgb.push(v); rgb.push(v); rgb.push(v); } rgb } pub fn r8_to_rgba(r8: &[u8]) -> Vec { let mut rgba = Vec::with_capacity(r8.len() * 4); for &v in r8 { rgba.push(v); rgba.push(v); rgba.push(v); rgba.push(v); } rgba }