use super::*;
fn deterministic_pixels(width: u32, height: u32, stride: u32) -> Vec<u8> {
let mut pixels = vec![0u8; (stride * height) as usize];
for y in 0..height {
for x in 0..width {
let offset = (y * stride) as usize + (x as usize * 4);
let opaque = (x * 7 + y * 13) % 5 < 2;
pixels[offset + 3] = if opaque { 255 } else { 0 };
}
}
pixels
}
fn all_opaque_pixels(width: u32, height: u32, stride: u32) -> Vec<u8> {
let mut pixels = vec![0u8; (stride * height) as usize];
for y in 0..height {
for x in 0..width {
let offset = (y * stride) as usize + (x as usize * 4);
pixels[offset + 3] = 255;
}
}
pixels
}
fn all_transparent_pixels(height: u32, stride: u32) -> Vec<u8> {
vec![0u8; (stride * height) as usize]
}
const SHAPES: &[(u32, u32, u32)] = &[
(1, 1, 4),
(2, 2, 8),
(7, 3, 28),
(8, 3, 32),
(9, 3, 36),
(16, 1, 64),
(17, 5, 68),
(32, 32, 128),
(2, 2, 12), (9, 4, 44), (0, 0, 0), (4, 0, 16), ];
fn assert_masks_identical(actual: &AlphaMask, expected: &AlphaMask, ctx: &str) {
assert_eq!(
actual, expected,
"{ctx}: マスク全体(data/width/height)が不一致"
);
assert_eq!(actual.width(), expected.width(), "{ctx}: width 不一致");
assert_eq!(actual.height(), expected.height(), "{ctx}: height 不一致");
for y in 0..=expected.height() {
for x in 0..=expected.width() {
assert_eq!(
actual.is_hit(x, y),
expected.is_hit(x, y),
"{ctx}: is_hit({x},{y}) が不一致"
);
}
}
}
#[test]
fn regenerate_from_empty_equals_from_pbgra32_for_all_shapes() {
for &(w, h, stride) in SHAPES {
let pixels = deterministic_pixels(w, h, stride);
let expected = AlphaMask::from_pbgra32(&pixels, w, h, stride);
let mut actual = AlphaMask::from_pbgra32(&[], 0, 0, 0);
actual.regenerate_from_pbgra32(&pixels, w, h, stride);
assert_masks_identical(&actual, &expected, &format!("空→{w}x{h} stride={stride}"));
}
}
#[test]
fn regenerate_shrink_leaves_no_stale_content() {
let big = all_opaque_pixels(32, 32, 128);
let mut mask = AlphaMask::from_pbgra32(&big, 32, 32, 128);
assert!(mask.is_hit(31, 31), "前提: 前状態は全不透明");
let small = all_transparent_pixels(8, 32);
let expected = AlphaMask::from_pbgra32(&small, 8, 8, 32);
mask.regenerate_from_pbgra32(&small, 8, 8, 32);
assert_masks_identical(&mask, &expected, "32x32(全不透明) → 8x8(全透明)");
for y in 0..8 {
for x in 0..8 {
assert!(!mask.is_hit(x, y), "縮小後に残留ヒット ({x},{y})");
}
}
}
#[test]
fn regenerate_shrink_with_mixed_pattern_equals_fresh() {
let big = all_opaque_pixels(17, 9, 68);
let mut mask = AlphaMask::from_pbgra32(&big, 17, 9, 68);
let small = deterministic_pixels(9, 3, 36);
let expected = AlphaMask::from_pbgra32(&small, 9, 3, 36);
mask.regenerate_from_pbgra32(&small, 9, 3, 36);
assert_masks_identical(&mask, &expected, "17x9 → 9x3(混在)");
}
#[test]
fn regenerate_grow_equals_fresh() {
let small = all_opaque_pixels(4, 4, 16);
let mut mask = AlphaMask::from_pbgra32(&small, 4, 4, 16);
let big = deterministic_pixels(33, 17, 132);
let expected = AlphaMask::from_pbgra32(&big, 33, 17, 132);
mask.regenerate_from_pbgra32(&big, 33, 17, 132);
assert_masks_identical(&mask, &expected, "4x4 → 33x17");
}
#[test]
fn regenerate_sequence_stays_equivalent_at_every_step() {
let mut mask = AlphaMask::from_pbgra32(&[], 0, 0, 0);
let sequence: &[(u32, u32, u32)] = &[
(32, 32, 128),
(9, 3, 36),
(0, 0, 0),
(17, 5, 68),
(17, 5, 68),
(1, 1, 4),
(16, 1, 64),
(2, 2, 12),
(4, 0, 16),
(8, 3, 32),
];
for &(w, h, stride) in sequence {
let pixels = deterministic_pixels(w, h, stride);
let expected = AlphaMask::from_pbgra32(&pixels, w, h, stride);
mask.regenerate_from_pbgra32(&pixels, w, h, stride);
assert_masks_identical(&mask, &expected, &format!("系列 {w}x{h} stride={stride}"));
}
}
#[test]
fn regenerate_same_size_clears_previous_content() {
let opaque = all_opaque_pixels(9, 4, 36);
let mut mask = AlphaMask::from_pbgra32(&opaque, 9, 4, 36);
let transparent = all_transparent_pixels(4, 36);
let expected = AlphaMask::from_pbgra32(&transparent, 9, 4, 36);
mask.regenerate_from_pbgra32(&transparent, 9, 4, 36);
assert_masks_identical(&mask, &expected, "9x4 同寸(全不透明→全透明)");
}
#[test]
fn regenerate_reuses_buffer_on_shrink() {
let big = deterministic_pixels(32, 32, 128);
let mut mask = AlphaMask::from_pbgra32(&big, 32, 32, 128);
let cap_after_big = mask.data.capacity();
let ptr_after_big = mask.data.as_ptr();
assert!(cap_after_big >= 128, "前提: 大きい方の容量は 128 以上");
let small = deterministic_pixels(8, 8, 32);
mask.regenerate_from_pbgra32(&small, 8, 8, 32);
assert_eq!(
mask.data.capacity(),
cap_after_big,
"縮小方向の再生成で容量が変化した(=確保し直している)"
);
assert_eq!(
mask.data.as_ptr(),
ptr_after_big,
"縮小方向の再生成で確保位置が動いた(=確保し直している)"
);
assert_eq!(mask.data.len(), 8, "縮小後の長さは row_bytes*height");
}
#[test]
fn regenerate_reuses_buffer_when_growing_back_within_capacity() {
let big = deterministic_pixels(32, 32, 128);
let mut mask = AlphaMask::from_pbgra32(&big, 32, 32, 128);
let cap = mask.data.capacity();
let ptr = mask.data.as_ptr();
let small = deterministic_pixels(8, 8, 32);
mask.regenerate_from_pbgra32(&small, 8, 8, 32);
mask.regenerate_from_pbgra32(&big, 32, 32, 128);
assert_eq!(mask.data.capacity(), cap, "容量内の再拡大で容量が変化した");
assert_eq!(mask.data.as_ptr(), ptr, "容量内の再拡大で確保位置が動いた");
assert_eq!(mask.data.len(), 128);
}
#[test]
fn regenerate_alternating_shapes_does_not_grow_capacity() {
let a = deterministic_pixels(32, 32, 128);
let b = deterministic_pixels(9, 3, 36);
let mut mask = AlphaMask::from_pbgra32(&a, 32, 32, 128);
mask.regenerate_from_pbgra32(&b, 9, 3, 36);
let cap = mask.data.capacity();
let ptr = mask.data.as_ptr();
for i in 0..8 {
mask.regenerate_from_pbgra32(&a, 32, 32, 128);
mask.regenerate_from_pbgra32(&b, 9, 3, 36);
assert_eq!(mask.data.capacity(), cap, "反復 {i} で容量が増えた");
assert_eq!(mask.data.as_ptr(), ptr, "反復 {i} で確保位置が動いた");
}
}
#[test]
fn partial_eq_detects_content_and_dimension_differences() {
let pixels = deterministic_pixels(9, 3, 36);
let same_a = AlphaMask::from_pbgra32(&pixels, 9, 3, 36);
let same_b = AlphaMask::from_pbgra32(&pixels, 9, 3, 36);
assert_eq!(same_a, same_b, "同一入力のマスクは等しい");
let mut flipped = pixels.clone();
let offset = (1 * 36usize) + (2 * 4) + 3;
flipped[offset] = 255 - flipped[offset];
let content_diff = AlphaMask::from_pbgra32(&flipped, 9, 3, 36);
assert_ne!(same_a, content_diff, "1 画素の差を PartialEq が見落とした");
let dim_diff = AlphaMask::from_pbgra32(&pixels, 9, 2, 36);
assert_ne!(same_a, dim_diff, "寸法差を PartialEq が見落とした");
}