Files
Sylpheed/docs/re/structures/title-residual-tone-vs-geometry.md
sylph-decoder 36b81da9d9 re: the menu's edge residual is not global misregistration and not blur
sylpheed-port ran the signed-edge discriminator I proposed but could not execute --
it needs their render beside the capture. Recorded as their measurement, with their
tool and commit.

Controls against deliberately damaged copies of their own render: a known +1 px
shift reads back as +0.938 px, a known blur as r -0.896 on the laplacian, neither
leaking into the other's channel. The menu against the capture gives -0.010 px
horizontal, -0.009 px vertical, laplacian r +0.103 -- and the weak blur term is the
opposite sign to the blur control.

So global misregistration and blur are both excluded, and of the three candidates I
named the misplaced soft element is the only one left.

Reach is the whole reach: a whole-frame fit excludes a global translation, not a
local one, since one misplaced element is a small share of 38752 edge pixels. The
next test is local and needs the residual map, which is theirs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-31 04:57:38 +00:00

7.3 KiB
Raw Blame History

✅ At least two thirds of the title's disagreement is GEOMETRY, not tone

⚠️ The 10.92 title baseline here could not be reproduced on 2026-08-29 — screen render --build 4 --black against the same capture gives 14.07 both at formats-pin-2026-08-29c and today. The ratio this document argues for may well survive; the absolute is in doubt. Separately, part of the title's residual is now known to be neither tone nor rotation but rest() posing five transient flashes at their peaks — ui-settle-time.md closes the light-arc band from 33.22 to 11.79.

Classification: measured, on committed captures. Answers a question the pending rotation decision needs: how much would drawing the rotation actually buy?

Two separate effects are known to make our renders differ from the console:

  • the tone curve — the console renders midtones brighter than we do (ui-render-tone-curve.md), and its single-exponent model is refuted above render ≈ 40;
  • the missing rotation — our blit draws axis-aligned rectangles, so the title's two tilted light sweeps land as upright bars (ui-keyframe-rotation.md).

They are confounded in any whole-frame number. This separates them.

Method, and why the split is an upper bound on tone

A per-level lookup table fitted on a screen is the most general tone model there is: it maps every render level to whatever capture level minimises the error, with no functional form assumed. Whatever such a LUT cannot close is, by construction, not a per-pixel-level effect — it is spatial.

So fitting a LUT on the screen itself and measuring what remains gives an upper bound on the tone share and a lower bound on the geometry share.

The positive control

The method must be able to close a residual that really is tone. On the main menu it does:

🔴 THE PREMISE OF THIS CONTROL WAS WRONG, corrected 2026-08-31. It read "where the port measures only 0.06 % of pixels differing, so geometry is essentially right". That 0.06 % is verify-capture's, and it counts pixels surviving -threshold 25% — differing by more than ~64 levels. It is a gross-displacement detector by design, blind to sub-pixel offsets and antialiasing, which are exactly what a per-level LUT also cannot close. It says no gross displacement, not geometry is right, and I read one as the other.

sylpheed-port measured the screen directly (DECISIONS.md at 5738b83): after this LUT, the menu's residual is 6.94 on edge pixels against 2.20 on flat — 3.2×, with a known negative (a render against itself under a pure gamma) leaving exactly 0.00, since a per-level LUT inverts a per-level effect perfectly. So the menu carries spatial error and is not a geometry-free control. ⚠️ I have not reproduced that split — it needs their render beside the capture — and it is recorded as their measurement, not mine.

✅ The result below does not rest on this control. "Whatever a fitted per-level LUT cannot close is, by construction, not a per-pixel-level effect" is stated as the method's basis above and stands on its own. What the control actually shows is narrower than claimed: the LUT closes 70 % of the menu's residual — evidence it works on tone, not evidence the menu is tone-only.

mean abs difference closed
main menu, uncorrected 9.26 —
main menu + LUT fitted on the main menu 2.75 70.3 %

✅ The instrument works: a tone-dominated residual collapses by 70 %.

The result

mean abs difference closed
title, uncorrected 10.92 —
title + LUT fitted on the title 7.42 32.0 %

🔴 At most 32 % of the title's disagreement is a tone effect, so at least 68 % is spatial — content in the wrong place. That is where the rotation lives, and it makes the rotation the dominant term by roughly two to one.

⚠️ The same-screen LUT is fitted, so 32 % is generous to tone. The true tone share is smaller and the geometry share larger.

🔴 And a claim this refutes: there is no single transferable tone curve

Fitting on one screen and applying to the other:

closed
main menu + LUT fitted on the title +29.7 %
title + LUT fitted on the main menu −24.0 % ✗

The menu's curve makes the title worse. A curve fitted on a dark, flat screen is unconstrained at the bright end — the menu's populated range is levels 5–204 with few bright pixels — and extrapolating it onto the title's planet and wordmark actively harms.

⚠️ So "the tone curve" is not one thing that can be measured once and applied. This extends ui-render-tone-curve.md's refutation of the single exponent: even a full per-level LUT fails to transfer between screens. A consumer must not carry a global tone correction.

Reach

  • Two screens, one capture each; the title capture is the plate-free frame and our render is at rest, so they are the same screen but not the same instant — a looser pairing than the port's posed comparison, which is why the absolute numbers here (10.92) are larger than its 1.81 % of pixels. ⚠️ The ratio is what this page claims, not the absolute level.
  • Our render draws the sweeps' parent record only — the pre-leaf-fix state — so the geometry share measured here includes both the missing rotation and the missing leaf placement. Both are closed by the same decision.

✅ The menu's edge residual is NOT global misregistration, and not blur

Measured by sylpheed-port (tools/port/edge-residual-kind, branch auto/port-p6-audio at f4351b0), running the discriminator I proposed but could not execute — it needs their render beside the capture, which I do not have. Recorded as their measurement.

The test, made concrete: a shift by (dx,dy) makes the signed residual track the gradient, and the fitted slope is the shift in pixels; a blur makes it track the laplacian, symmetric and directionless.

control — a known +1 px shift reads back +0.938 px (r +0.789)
control — a known blur r −0.896 on the laplacian
neither leaked into the other's channel ✅
main_menu vs the capture −0.010 px horizontal, −0.009 px vertical; laplacian r +0.103

❌ Global misregistration is excluded: any whole-frame translation is under a hundredth of a pixel, against a control that reads a true 1 px at 0.938. ❌ Blur is excluded: the weak laplacian term is the opposite sign to the blur control, so the capture is not a softened render either.

⚠️ The reach is the whole reach: this is a WHOLE-FRAME fit. One misplaced element is a small share of 38 752 edge pixels and would not move these numbers. It excludes a global translation, not a local one.

📌 So of the three candidates I named — misregistration, antialiasing, a misplaced soft element — the third survives, and it is the only one left. The next test is necessarily local: where does the edge residual concentrate spatially, and does that region correspond to one element? ⚠️ That needs the residual map, which is on the port's side; I can state the test and cannot run it.