const ALPHA_THRESHOLD: u8 = 128;
#[derive(Debug, Clone, PartialEq)]
pub struct AlphaMask {
data: Vec<u8>,
width: u32,
height: u32,
}
fn pack_pbgra32_alpha(
data: &mut [u8],
pixels: &[u8],
width: u32,
height: u32,
stride: u32,
row_bytes: usize,
) {
for y in 0..height {
let row_start = (y * stride) as usize;
for x in 0..width {
let pixel_offset = row_start + (x as usize * 4);
let alpha = pixels.get(pixel_offset + 3).copied().unwrap_or(0);
if alpha >= ALPHA_THRESHOLD {
let byte_index = (y as usize * row_bytes) + (x as usize / 8);
let bit_index = 7 - (x % 8);
data[byte_index] |= 1 << bit_index;
}
}
}
}
impl AlphaMask {
pub fn from_pbgra32(pixels: &[u8], width: u32, height: u32, stride: u32) -> Self {
let row_bytes = width.div_ceil(8) as usize;
let mut data = vec![0u8; row_bytes * height as usize];
pack_pbgra32_alpha(&mut data, pixels, width, height, stride, row_bytes);
Self {
data,
width,
height,
}
}
pub fn regenerate_from_pbgra32(&mut self, pixels: &[u8], width: u32, height: u32, stride: u32) {
let row_bytes = width.div_ceil(8) as usize;
let len = row_bytes * height as usize;
self.data.clear();
self.data.resize(len, 0);
pack_pbgra32_alpha(&mut self.data, pixels, width, height, stride, row_bytes);
self.width = width;
self.height = height;
}
pub fn is_hit(&self, x: u32, y: u32) -> bool {
if x >= self.width || y >= self.height {
return false;
}
let row_bytes = self.width.div_ceil(8) as usize;
let byte_index = (y as usize * row_bytes) + (x as usize / 8);
let bit_index = 7 - (x % 8);
debug_assert!(
byte_index < self.data.len(),
"AlphaMask invariant violated: byte_index {} out of bounds (data.len()={}, width={}, height={})",
byte_index,
self.data.len(),
self.width,
self.height
);
(self.data[byte_index] >> bit_index) & 1 == 1
}
pub fn width(&self) -> u32 {
self.width
}
pub fn height(&self) -> u32 {
self.height
}
pub fn packed_capacity(&self) -> usize {
self.data.capacity()
}
}
#[cfg(test)]
#[path = "alpha_mask_regenerate_tests.rs"]
mod regenerate_tests;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_transparent_pixel_not_hit() {
let pixels = vec![
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ];
let mask = AlphaMask::from_pbgra32(&pixels, 2, 2, 8);
assert!(!mask.is_hit(0, 0));
assert!(!mask.is_hit(1, 0));
assert!(!mask.is_hit(0, 1));
assert!(!mask.is_hit(1, 1));
}
#[test]
fn test_opaque_pixel_hit() {
let pixels = vec![
0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, ];
let mask = AlphaMask::from_pbgra32(&pixels, 2, 2, 8);
assert!(mask.is_hit(0, 0));
assert!(mask.is_hit(1, 0));
assert!(mask.is_hit(0, 1));
assert!(mask.is_hit(1, 1));
}
#[test]
fn test_threshold_boundary_127() {
let pixels = vec![0, 0, 0, 127]; let mask = AlphaMask::from_pbgra32(&pixels, 1, 1, 4);
assert!(!mask.is_hit(0, 0));
}
#[test]
fn test_threshold_boundary_128() {
let pixels = vec![0, 0, 0, 128]; let mask = AlphaMask::from_pbgra32(&pixels, 1, 1, 4);
assert!(mask.is_hit(0, 0));
}
#[test]
fn test_out_of_bounds() {
let pixels = vec![0, 0, 0, 255];
let mask = AlphaMask::from_pbgra32(&pixels, 1, 1, 4);
assert!(!mask.is_hit(1, 0)); assert!(!mask.is_hit(0, 1)); assert!(!mask.is_hit(100, 100));
}
#[test]
fn test_mixed_pattern() {
let pixels = vec![
0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, ];
let mask = AlphaMask::from_pbgra32(&pixels, 2, 2, 8);
assert!(mask.is_hit(0, 0));
assert!(!mask.is_hit(1, 0));
assert!(!mask.is_hit(0, 1));
assert!(mask.is_hit(1, 1));
}
#[test]
fn test_wide_image_bit_packing() {
let mut pixels = vec![0u8; 10 * 4];
pixels[3] = 255; pixels[9 * 4 + 3] = 255;
let mask = AlphaMask::from_pbgra32(&pixels, 10, 1, 40);
assert!(mask.is_hit(0, 0));
for x in 1..9 {
assert!(!mask.is_hit(x, 0), "x={} should not hit", x);
}
assert!(mask.is_hit(9, 0));
}
#[test]
fn test_size_accessors() {
let pixels = vec![0u8; 100 * 50 * 4];
let mask = AlphaMask::from_pbgra32(&pixels, 100, 50, 400);
assert_eq!(mask.width(), 100);
assert_eq!(mask.height(), 50);
}
#[test]
fn test_zero_width_produces_empty_mask() {
let pixels: Vec<u8> = Vec::new();
let mask = AlphaMask::from_pbgra32(&pixels, 0, 0, 0);
assert_eq!(mask.width(), 0);
assert_eq!(mask.height(), 0);
assert!(!mask.is_hit(0, 0));
assert!(!mask.is_hit(5, 5));
}
#[test]
fn test_zero_height_produces_empty_mask() {
let pixels: Vec<u8> = Vec::new();
let mask = AlphaMask::from_pbgra32(&pixels, 4, 0, 16);
assert_eq!(mask.width(), 4);
assert_eq!(mask.height(), 0);
assert!(!mask.is_hit(0, 0));
assert!(!mask.is_hit(3, 0));
}
#[test]
fn test_padded_stride_reads_correct_rows() {
let stride = 12u32;
let mut pixels = vec![0u8; (stride * 2) as usize];
pixels[3] = 255;
pixels[(1 * stride as usize) + (1 * 4) + 3] = 255;
let mask = AlphaMask::from_pbgra32(&pixels, 2, 2, stride);
assert!(mask.is_hit(0, 0), "(0,0) should hit");
assert!(!mask.is_hit(1, 0), "(1,0) should not hit");
assert!(!mask.is_hit(0, 1), "(0,1) should not hit");
assert!(
mask.is_hit(1, 1),
"(1,1) should hit (padded stride row offset)"
);
}
#[test]
fn test_bit_packing_across_byte_boundary() {
let mut pixels = vec![0u8; 16 * 4];
pixels[7 * 4 + 3] = 255; pixels[8 * 4 + 3] = 255;
let mask = AlphaMask::from_pbgra32(&pixels, 16, 1, 64);
for x in 0..16 {
let expected = x == 7 || x == 8;
assert_eq!(
mask.is_hit(x, 0),
expected,
"x={x} hit mismatch (boundary packing)"
);
}
}
#[test]
fn test_is_hit_at_last_valid_pixel() {
let w = 3u32;
let h = 2u32;
let stride = w * 4;
let mut pixels = vec![0u8; (stride * h) as usize];
pixels[((h - 1) * stride + (w - 1) * 4 + 3) as usize] = 255;
let mask = AlphaMask::from_pbgra32(&pixels, w, h, stride);
assert!(mask.is_hit(2, 1), "last valid pixel should hit");
assert!(!mask.is_hit(3, 1), "x == width is out of bounds");
assert!(!mask.is_hit(2, 2), "y == height is out of bounds");
}
#[test]
fn test_is_hit_out_of_bounds_returns_before_indexing() {
let w = 9u32;
let h = 3u32;
let stride = w * 4;
let pixels = vec![0u8; (stride * h) as usize];
let mask = AlphaMask::from_pbgra32(&pixels, w, h, stride);
assert!(!mask.is_hit(w, 0));
assert!(!mask.is_hit(0, h));
assert!(!mask.is_hit(w, h));
assert!(!mask.is_hit(u32::MAX, u32::MAX));
assert!(!mask.is_hit(u32::MAX, 0));
assert!(!mask.is_hit(0, u32::MAX));
}
#[test]
fn test_is_hit_all_valid_coords_satisfy_index_invariant() {
let w = 17u32;
let h = 5u32;
let stride = w * 4;
let mut pixels = vec![0u8; (stride * h) as usize];
pixels[((h - 1) * stride + (w - 1) * 4 + 3) as usize] = 255;
let mask = AlphaMask::from_pbgra32(&pixels, w, h, stride);
let mut hit_count = 0u32;
for y in 0..h {
for x in 0..w {
if mask.is_hit(x, y) {
hit_count += 1;
}
}
}
assert_eq!(hit_count, 1, "only the bottom-right pixel should hit");
assert!(mask.is_hit(w - 1, h - 1), "max-index pixel should hit");
}
}