re: additive blending refuted, the swoosh is not displaced, and the

residual is smaller than I said

Testing the candidate I raised last iteration rather than carrying it.
Blending bit-0x02 sprites additively moves every measure the wrong way --
whole-frame mean diff +0.55 to +1.04, swoosh-band mean +1.83 to +3.98,
band edge-correlation 0.6971 down to 0.5578. So the bit is real and
independent but does not select an additive blend. I reverted the
experiment and kept the word as T8adImage::flags, documented and not
acted on; the render is byte-identical to before.

Second refutation: the swoosh is not displaced. Shifting the band over
plus or minus 80 by 8 pixels peaks sharply at zero, 0.7342, falling to
0.22 at 24 px. So the pivot story is dead twice over -- inert at scale
100, and no displacement to explain anyway.

And I have restated the residual, because earlier sections overstated it.
The +16 to +34 band tiles I quoted were measured WITHOUT --primitives.
With the dim drawn the band's average is nearly right at +1.83; what is
wrong is its structure, tiles running -38.6 then +33.8 and cancelling.
Six candidates eliminated now and none confirmed.

One caveat I owe the port agent about the capture I gave them: it is at
t=4.0s, roughly 174 keyframe units into a screen whose elements have
keyframes out to t=600. I judged "settled" from mean luminance, which
cannot see a thin sprite still moving. It is settled for the bulk of the
screen and not proven settled for every element -- which is a live
alternative explanation for a structural difference in exactly the band
the sweeps cross.

METHOD: cargo build passing does not mean cargo test compiles. Adding the
field built the library in 1.48s and broke two test-only struct literals;
cargo test failed with exit 101.
This commit is contained in:
Sylpheed RE agent
2026-08-28 22:00:55 +00:00
parent e0d02dce71
commit 5d02d5cd60
5 changed files with 82 additions and 5 deletions

View File

@@ -38,6 +38,18 @@ pub struct T8adImage {
pub width: u32,
pub height: u32,
pub rgba: Vec<u8>,
/// The header word at `+0x04`. A flag word; **bit `0x02`** is a candidate
/// blend selector — it separates the title's effect sprites from its ordinary
/// ones exactly, and disc-wide it toggles independently of the rest of the
/// word in 27.1 % of 19 216 sprites. See
/// `docs/re/structures/ui-paint-order-key.md`.
///
/// ⚠️ **Additive was tested and REFUTED.** Blending bit-`0x02` sprites
/// additively moved every measure against the title capture the wrong way:
/// whole-frame mean diff +0.55 → +1.04, swoosh-band mean +1.83 → +3.98, band
/// edge-correlation 0.6971 → 0.5578. The bit is real and independent, but it
/// does not select an additive blend. Carried, not acted on.
pub flags: u32,
}
/// Whether `bytes` starts with the T8aD magic.
@@ -108,6 +120,7 @@ pub fn parse(bytes: &[u8]) -> Option<T8adImage> {
}
}
Some(T8adImage {
flags: be32(bytes, 4),
width: width as u32,
height: height as u32,
rgba,

View File

@@ -1049,6 +1049,10 @@ fn blit(
continue;
}
let di = ((ty as u32 * cw + tx as u32) * 4) as usize;
// Straight alpha-over. `T8aD +0x04` bit 0x02 was tested as an
// ADDITIVE selector and REFUTED — it moved every metric against the
// title capture the wrong way (see the doc comment on
// `T8adImage::flags`), so the bit is carried but not acted on.
for (k, sc) in [sr, sg, sb].into_iter().enumerate() {
let dc = canvas[di + k] as u32;
canvas[di + k] = ((sc * sa + dc * (255 - sa)) / 255) as u8;
@@ -1214,6 +1218,7 @@ mod tests {
// leaves the top-left quadrant bare. See
// `docs/re/structures/ui-rat-layout.md`.
let img = t8ad::T8adImage {
flags: 0,
width: 640,
height: 360,
rgba: vec![255u8; 640 * 360 * 4],
@@ -1247,6 +1252,7 @@ mod tests {
// `ptcopyright.t32` is 694x20 at (293,655), and the capture's glyph run
// starts at x = 295 — inside that rect, not offset by a pivot.
let img = t8ad::T8adImage {
flags: 0,
width: 694,
height: 20,
rgba: vec![255u8; 694 * 20 * 4],