diff --git a/crates/gc-formats/src/texture.rs b/crates/gc-formats/src/texture.rs index a6f4240..a8c45c8 100644 --- a/crates/gc-formats/src/texture.rs +++ b/crates/gc-formats/src/texture.rs @@ -18,6 +18,7 @@ //! anything above it clamps to opaque. use crate::buf::{Error, ErrorKind, Result}; +use crate::gs; use crate::level_core::TextureEntry; /// PS2 CSM1 CLUT order to linear: within each 32-entry group, swap the two @@ -30,13 +31,20 @@ pub fn clut_index(i: u32) -> u32 { } } -/// PS2 alpha 0..=0x80 to 0..=0xff. `0x80` is opaque; above that clamps. +/// PS2 alpha `0x00..=0x80` to `0x00..=0xff`. `0x80` is opaque; above clamps. +/// +/// Delegates to [`crate::gs::alpha_to_8`] rather than computing here. This used +/// to be `if a < 0x80 { a * 2 } else { 0xff }`, which is `a / 128 * 256` -- one +/// too high at the top of the range, so `0x7F` decoded to 254 where the renderer's +/// own alpha path and gs::alpha_to_8 both give 253. Three expressions of one rule, +/// two of them right, and the wrong one was the one decoding actual pixels. +/// +/// The visual difference is at most 1/255 on a single palette entry and nothing +/// anyone would ever see. That is exactly why it survived: the bug class here is +/// not "big wrong", it is "right to within a rounding step", in the code path +/// that nobody instruments. pub fn scale_alpha(a: u8) -> u8 { - if a < 0x80 { - a * 2 - } else { - 0xff - } + gs::alpha_to_8(a) } /// A decoded RGBA8 image.