diff --git a/crates/gc-extract/tests/disc.rs b/crates/gc-extract/tests/disc.rs index f3bdab5..f8d1a56 100644 --- a/crates/gc-extract/tests/disc.rs +++ b/crates/gc-extract/tests/disc.rs @@ -748,6 +748,7 @@ fn tfrag_textures_decode_to_plausible_images() { } let mut decoded = 0usize; + let mut alphas: std::collections::BTreeMap = std::collections::BTreeMap::new(); let mut opaque = 0usize; let mut multi_colour = 0usize; let mut flat = 0usize; @@ -801,6 +802,9 @@ fn tfrag_textures_decode_to_plausible_images() { if op == texels as usize { opaque += 1; } + for px in t.rgba.chunks_exact(4) { + *alphas.entry(px[3]).or_insert(0usize) += 1; + } } } @@ -822,6 +826,30 @@ fn tfrag_textures_decode_to_plausible_images() { opaque_pct * 100.0, detail_pct * 100.0 ); + + // The 0x80 convention, measured on the disc rather than asserted in prose. + // Every decoded alpha must be a value gs::alpha_to_8 can produce -- and 254 + // cannot be produced by it, since (v*255+64)/128 never lands on 254 for an + // integer v. 254 is precisely what the old `a * 2` produced for a raw 0x7F, + // so this line fails if that bug is ever reintroduced or the two expressions + // of the rule drift apart again. + let legal: std::collections::HashSet = + (0..=255u8).map(gc_formats::gs::alpha_to_8).collect(); + let illegal: Vec<(u8, usize)> = alphas + .iter() + .filter(|(a, _)| !legal.contains(a)) + .map(|(a, n)| (*a, *n)) + .collect(); + eprintln!("decoded alpha histogram: {alphas:?}"); + assert!( + illegal.is_empty(), + "{illegal:?} are not producible by gs::alpha_to_8 -- the CLUT alpha \ + conversion and the GS rule have diverged again" + ); + assert!( + alphas.contains_key(&0xff), + "no fully opaque texel decoded; the 0x80 -> opaque rule is not exercised" + ); } /// LOD linkage: every LOD-01 and LOD-0 vertex-info entry must resolve to a