diff --git a/crates/sylpheed-formats/src/t8ad.rs b/crates/sylpheed-formats/src/t8ad.rs index 32c973b..9a595fd 100644 --- a/crates/sylpheed-formats/src/t8ad.rs +++ b/crates/sylpheed-formats/src/t8ad.rs @@ -38,6 +38,18 @@ pub struct T8adImage { pub width: u32, pub height: u32, pub rgba: Vec, + /// 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 { } } Some(T8adImage { + flags: be32(bytes, 4), width: width as u32, height: height as u32, rgba, diff --git a/crates/sylpheed-formats/src/ui_layout.rs b/crates/sylpheed-formats/src/ui_layout.rs index 6a8593e..c455636 100644 --- a/crates/sylpheed-formats/src/ui_layout.rs +++ b/crates/sylpheed-formats/src/ui_layout.rs @@ -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], diff --git a/docs/re/METHOD.md b/docs/re/METHOD.md index 38133bd..106b940 100644 --- a/docs/re/METHOD.md +++ b/docs/re/METHOD.md @@ -294,3 +294,8 @@ agent's loop prompt, i.e. nowhere durable. See [`README.md`](README.md) for the first one in the file. It failed its own control instantly: different sprites reported identical dimensions. Match on something the header itself carries (here width × height against the decoded PNGs) rather than on proximity. +* **`cargo build` passing does not mean `cargo test` compiles.** Adding a field to + `T8adImage` built the library fine in 1.48 s — and broke two *test-only* struct + literals, so `cargo test` failed to compile with exit 101. A green build on the + binary is not a gate for a struct change; run the tests before believing a + data-structure edit is safe. diff --git a/docs/re/structures/ui-paint-order-key.md b/docs/re/structures/ui-paint-order-key.md index 0a391bf..cd86488 100644 --- a/docs/re/structures/ui-paint-order-key.md +++ b/docs/re/structures/ui-paint-order-key.md @@ -477,10 +477,22 @@ and the texture itself is blue-leaning mode** is what is left, and this is the only per-sprite field found that distinguishes the elements involved. -⚠️ **This is a correlation, not a decode.** Nothing here shows the bit *means* -additive. The test: blend bit-`0x02` sprites additively and re-correlate the title -render against +### 🔴 Tested — it does NOT select an additive blend + +Blending bit-`0x02` sprites additively and re-correlating against the title +capture moved **every** measure the wrong way: + +| | alpha-over | additive | +|---|---|---| +| whole-frame mean diff | **+0.55** | +1.04 | +| swoosh-band mean diff | **+1.83** | +3.98 | +| swoosh-band edge-corr | **0.6971** | 0.5578 | + +So the bit is real and independent, but **additive is refuted**. `T8adImage` now +carries the word as `flags`, documented and *not* acted on. + +⚠️ **What remains is a correlation, not a decode.** The original test was against [`../captures/title-builds/live-title-build4-no-plate.png`](../captures/title-builds/live-title-build4-no-plate.png). Worth noting `ptlogo_back2eff` carries `0x8830` **despite** having `eff` in its -name — so the split is the field's, not a naming pattern's, which is the one thing -that makes it more than a guess. +name — so the split is the field's, not a naming pattern's. What the bit *does* +mean is still unknown. diff --git a/docs/re/ui-title-build-map.md b/docs/re/ui-title-build-map.md index 4da540c..5345b23 100644 --- a/docs/re/ui-title-build-map.md +++ b/docs/re/ui-title-build-map.md @@ -500,3 +500,44 @@ The obvious guess — *our dim is applied over the whole frame instead of beneat the UI, where its layer key puts it* — is **wrong**. If it were, the logo would render too dark; it reads **+2.36** against a background of **−0.74**. The compositor honours the paint order here. + + +## 🔴 The swoosh is not displaced either — and the residual is restated + +Two more candidates eliminated, and the residual is smaller than earlier sections +implied. + +**Not a displacement.** Shifting the render's swoosh band over ±80 px × ±8 px and +re-correlating peaks **sharply at (0, 0)** — 0.7342, falling to 0.22 at ±24 px and +0.10 at ±48. The swoosh is where it should be. + +**Not additive blending.** See +[`structures/ui-paint-order-key.md`](structures/ui-paint-order-key.md): every +measure worsens. + +**And the residual, restated with the current best render** (`--black +--primitives`, `rest()` fixed): + +| | | +|---|---| +| whole-frame mean diff | **+0.55** | +| swoosh-band mean diff | **+1.83** | +| swoosh-band edge-correlation | **0.6971** (vs ≈ 0.92 frame-wide) | + +⚠️ Earlier sections quoted band tiles at **+16 … +34**. Those were measured on a +render **without** `--primitives`. With the dim drawn the band's *average* is +nearly right; what is wrong is its **structure** — the tiles run −38.6 then +33.8 +across the band and cancel. So the defect is neither brightness, nor position, nor +additive blending: it is a shape difference in one band, and it is **not +diagnosed**. Six candidates eliminated: pivot (twice — inert at scale 100, and no +measured displacement), `fade`, `tint`, texture colour, additive blend. + +### ⚠️ A caveat on the plate-free capture + +It was taken at **t ≈ 4.0 s**, about **2.9 s** (≈ 174 keyframe units) after the +screen begins. Several title elements have keyframes running to **t = 600** +(10 s) — the `ptloop` sweeps among them. "Settled" was judged from mean +luminance, which cannot see a thin sprite still in motion. The capture is settled +for the bulk of the screen; it is **not proven settled for every element**, and +that is a live alternative explanation for a structural difference in exactly the +band those sweeps cross.