re(ui): put a number on the render-vs-capture tone difference

Closes an observation I left dangling last iteration ("the capture is ~4x
darker than the render") and puts a figure on the  INDEX's texture row
already carried: exact gamma/sRGB fidelity untested because a hue
comparison cannot see it.

Geometry first: cross-correlating the main-menu capture against our
render over +/-6 px puts the best alignment at exactly dy=0 dx=0,
correlation 0.9466. Only the tone differs.

Two methods failed before one worked, and both failures are recorded.
Three dark patches gave "4x darker" -- the whole-frame best linear scale
is 0.914, so three patches from one region are not a transfer curve. A
pixel-wise fit over 854,685 pixels then produced a NON-MONOTONIC transfer
(render 96-127 mapping brighter than render 128-159) with mean abs error
10-14 for every candidate model. That is edge misalignment, not a tone
curve: at correlation 0.947 a bright pixel routinely lands on a dark one.

Flat patches fix it -- 16x16 blocks with std < 8 in BOTH images, a
threshold chosen from the counts (0/83/404/1055/1788 at std<3/5/8/12/20):

  main menu  404 patches  gamma 1.491  err 0.28   (best linear 0.276, 0.34)
  EXTRAS     382 patches  gamma 1.493  err 0.22   (best linear 0.273, 0.28)
  title      506 patches  gamma 1.338  err 1.08   (best linear 0.842, 9.02)

Reach, stated because it is narrow: those patches span only render values
~0-60, where gamma and a plain scale are nearly indistinguishable -- the
two menus decide nothing (0.28 vs 0.34, 0.22 vs 0.28) and only the title
separates them. Nothing constrains midtones or highlights.

The held-out control FAILED TO DISCRIMINATE and is reported as such: the
splash's 2918 flat patches are pure black (render 0-4), so every model
scores ~0.00. That is a test with no power, not corroboration.

Confound left open: this compares our composite to what canary DISPLAYS,
and canary applies kernel_display_gamma_type = 2 (BT.709). The exponent
may be its output stage. The discriminating run -- set it to 0, recapture,
refit -- needs one emulator session reaching the main menu and was not
done.

Classified measured, not decoded; HANDOFF says plainly that a port
applying it is authoring.
This commit is contained in:
Sylpheed RE agent
2026-08-29 01:05:25 +00:00
parent 2b4ec20f00
commit ee73de50be
4 changed files with 113 additions and 1 deletions

View File

@@ -219,6 +219,22 @@ authored version can be deleted.
⚠️ An earlier version of this bullet said "the title screen loops at ≈ 2.2 s"
and attributed it to `ptloop01/02`. Both halves were wrong: it is the **plate**,
and it is a different build.
* 🟡 **Our composite is brighter than the emulator's frame — measured, and you
would be *authoring* if you apply it.** Alignment is exact (best offset
dy=0 dx=0, correlation **0.9466**), so only the tone differs. Fitting on 16×16
patches that are flat in **both** images: `capture ≈ 255·(render/255)^γ` with
γ = **1.491** (main menu), **1.493** (`EXTRAS`), **1.338** (title).
⚠️ **Narrow reach.** Those flat patches span only render values ~060, where a
gamma and a plain scale are nearly indistinguishable — on both menus the errors
are 0.28 vs 0.34 and 0.22 vs 0.28. Only the title separates them (1.08 vs
9.02). Nothing here constrains midtones or highlights.
🔴 The held-out control **failed to discriminate**: the splash's 2 918 flat
patches are pure black (render 04), so every model scores ≈ 0.
**It may be the emulator, not the game.** Canary applies
`kernel_display_gamma_type = 2` (BT.709) on output. The discriminating run —
set it to `0` and re-fit — was not done.
[`structures/ui-render-tone-curve.md`](../re/structures/ui-render-tone-curve.md)
* 🟡 **`screen render` silently drops one full-screen element per screen — and
you must NOT simply draw it.** Auditing what the composer omits on your five
screens: everything is accounted for (`kind & 0x4` ghost instances, `.prm`