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-29screen 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 5204 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.