Files
lightcraft/crates
Alex ChambersandClaude Opus 5.5 baf079f659 LR-BEHAV-RENDER-FIDELITY: process V5 rolls raw tone curves off into white in stops, so raising Exposure keeps highlights (#1632) (#3783)
* LR-BEHAV-RENDER-FIDELITY: V3 camera tone curve rolls off in stops past its last knot (#1632)

A raw's camera tone curve is fitted only to camera JPEG tones below 0.85, so its last knot sits
1 to 3 stops below the sensor's white. Past it, CameraTone::apply closes on white exponentially in
linear light: the sensor's white renders at 8-bit 246-254 at Exposure 0 and 254-255 from +1, and
raising Exposure turns the top stop the sensor recorded white.

Process V3 extends the curve with CameraTone::apply_rolloff: same value and slope at the knot,
then gap / (1 + c (2^t - 1)) at t stops past it, the gap halving every stop in the end. On 11 CC0
corpus files with a fitted look, unclipped pixels rendering white drop from up to 22.5% to 0 at
+1 EV and from up to 35% to 0 at +2 EV; Exposure 0 renders change by at most 1 code on average.
V1 and V2, the fit itself, JPEGs and raws without a fitted look are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* LR-BEHAV-RENDER-FIDELITY: V3 generic raw curve rolls off in stops too; DRO-off, DNG and GPU checks (#1632)

Raws without a camera curve take ToneMap::hdr, which reached white about 1/4 stop past the
sensor's white and clipped there (o.min(1.0)). Process V3 now uses ToneMap::rolloff_hdr: from half
a stop below the white point (tone::GENERIC_KNEE_STOPS) the extended Reinhard curve hands over to
the same stop-based shoulder as the camera curve (tone::rolloff_past, shared with
CameraTone::apply_rolloff), with the same value and slope at the join. The sensor's white renders
at about 235 / 248 / 252 at Exposure 0 / +1 / +2 where V1 and V2 render 239 / 255 / 255. On 6 CC0
DNGs on the generic curve, unclipped pixels rendering white drop from up to 8.4% to 0 at +1 EV and
from up to 17.2% to 0 at +2 EV.

The DRO-off gate (look_fit::at_most) also checks the V3 shoulder; the rate bound already implies it,
so no fit changes and LOOK_VERSION stays. Tests: the generic shoulder (join, slope, monotone, below
white, no kink), a regression test that fails with the clipping curve, a DNG ProfileToneCurve under
V3, the DRO-off property tests on the V3 shoulder, and a per-process camera-curve GPU check.
Docs: process-versions, parity checklist (LR-BEHAV-RENDER-FIDELITY, LRC-DEV-PROCESS), gap 3.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* LR-BEHAV-RENDER-FIDELITY: what's new: process 3 keeps raw highlights when Exposure is raised (#1632)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* LR-BEHAV-RENDER-FIDELITY: a flat last camera-curve segment joins the V3 shoulder with its own slope (#1632)

The V3 camera shoulder took the last segment's slope with a floor of 0.1, so a flat or shallow
segment (valid for a fitted, DNG or saved curve) joined with a kink: slope 0 on the curve, about
0.1 just past it. The shoulder now keeps the segment's own slope at the knot. Below 0.1 a quadratic
term in 2^t - 1 joins the denominator: it adds no slope at the knot and makes up the 0.1 pace
further up, so a flat curve still closes on white. Segments at 0.1 or steeper are byte-identical,
and the generic curve passes no floor. Comments now say the shoulder approaches white in real
numbers and can round to it in f32. Found in an independent review of the branch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* LR-BEHAV-RENDER-FIDELITY: V4's GPU test pins V4; the corpus DRO photos keep their knots under the V5 bound (#1632)

The GPU light-operator test assumed the default process is V4; it now sets V4, since V5 is the
default. The corpus DRO test checks that dro_off's V5 bound leaves the second-last knot where
the rate bound puts it on the Sony DRO samples, so their V1 to V4 renders don't change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* LR-BEHAV-RENDER-FIDELITY: the DRO-off curve is main's again, so V1 to V4 keep it (#1632)

CodeRabbit found that the V5 bound in `dro_off` could move the second-last knot of a DRO
photo whose fitted last segment is flat, and V1 to V4 read that curve too. `dro_off` and
`at_most` are back to main's, so the camera-look fit is unchanged and LOOK_VERSION stays.
The DRO tests check the V5 shoulder where the lowered segment's slope over its squared gap
is at most the fitted one's (then it stays below), and on the Sony DRO corpus samples.
Also: `rolloff_hdr`'s doc names the HDR lift, and the release note names DNGs whose curve
already reaches white.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 20:50:13 +01:00
..
2026-10-31 02:22:11 +00:00