Render each body through the profile its own files describe
Colour came from whichever matrix rawler happened to key `D65`, the second one was discarded, and the rendering was left linear. That is the dcraw default, and FR-DEV-3e names it as the reason people abandon a converter in the first hour: correct in the abstract, flat and poor on skin in practice. The decoder now builds a camera profile. - `ColorMatrix1/2` and `CalibrationIlluminant1/2`. rawler surfaces these as an illuminant-keyed map — for DNGs from the tags, and for native formats from its own camera database — so a Canon CR2 arrives with a tungsten matrix and a daylight matrix exactly as an Adobe DNG of the same frame would. Dual-illuminant support is therefore not a DNG feature here. - `ForwardMatrix1/2`, read straight from the root IFD, because rawler parses them and never surfaces them. Where a file carries both, they replace the inverted colour matrix: the same relationship measured in the direction rendering actually wants, rather than an inversion that amplifies the measurement error exactly where skin lives. - `AsShotNeutral`, used to estimate what the scene was lit by and to interpolate between the two calibrations in mireds. The estimate is circular — the temperature needs a matrix and the matrix needs the temperature — so it is a fixed point, three rounds, as Adobe's SDK does it. Bodies calibrated at neither D65 nor A stopped rendering uncalibrated as a side effect: a Phase One IQ3 carries D55 and D75 and used to get no matrix at all. And a base curve, applied per channel in camera RGB between the last adjustment and the conversion out of camera space — a toe, a steep midtone and a shoulder, which is the difference between a photograph and a scan of one. It is not an edit: no slider, nothing in the sidecar, because it belongs to the body rather than to anything anyone decided, and a sidecar is shared between bodies. It is not a develop node either, and `ops/README.md` now records why. It evaluates on the tone curve's own spline rather than a second copy, so a profile author placing a control point and a photographer dragging one mean the same thing by it. The curves are data. `core/dr-decode/profiles/base_curves.yaml` ships inside the binary as a floor and is superseded by any copy on disk carrying a higher `version:`, so a body can be added and distributed without a release — and, under the GPL, contributed. The comparison runs both ways: a stale pack cannot hold an upgraded binary back at last year's rendering. Canon EOS 6D and R6, Nikon Z 6 and D750, Sony A7 III and Fujifilm X-T3 ship with their own curves. Every other body gets a conservative default, which is much closer to right than the identity is for any of them. A JPEG gets none — it has already been rendered once, by the camera. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -216,6 +216,44 @@ carries lens-profile coefficients that are not parameters. `distortion` and
|
||||
`aberration` are `Warp`s rather than operations: they rewrite coordinates
|
||||
before sampling rather than transforming a colour after it.
|
||||
|
||||
## What is not a node, and why
|
||||
|
||||
Three things act on every pixel and are deliberately not in this directory:
|
||||
the as-shot white balance, the camera matrix, and the **base curve**
|
||||
(FR-DEV-3e). They are emitted by [`../src/operation.rs`](../src/operation.rs)
|
||||
into the composed shader's fixed preamble, around the block of nodes.
|
||||
|
||||
The test is not "does it transform a colour" — all three do. It is **whose
|
||||
decision is it**. A node is something a photographer chose: it has parameters,
|
||||
it moves off a neutral, it lands in the sidecar, it can be undone. These three
|
||||
are properties of the *file*, at the same standing as the masked-photosite crop
|
||||
(FR-RAW-3) and the stored orientation (FR-DEV-3h). Nobody chose the sensor's
|
||||
green sensitivity or the body's rendering; they are what reading the file
|
||||
correctly means.
|
||||
|
||||
Making the base curve a node would have said the opposite in four places at
|
||||
once. It would have appeared in the develop panel as a control, so an
|
||||
unprofiled body would show a slider that does nothing. Its values would have
|
||||
gone into the sidecar, and sidecars are shared between devices and bodies
|
||||
(FR-NC-9) — one camera's rendering would follow an edit onto another camera's
|
||||
file. Its neutral would have had to be "the identity", so a profiled body would
|
||||
open reporting itself modified. And there is no seam through which a node could
|
||||
learn which camera took the frame: the profile arrives on the decoded image,
|
||||
travels through `DemosaicedImage` beside the matrix it belongs with, and is
|
||||
written into the uniform block by the same three lines in `dr-gpu` — which is
|
||||
exactly the path the matrix already took, because it is exactly the same kind
|
||||
of thing.
|
||||
|
||||
What it *does* share with the tone curve node is the spline. The composer asks
|
||||
`ToneCurve` for its `curve_span`/`curve_eval` helpers rather than emitting a
|
||||
second copy, so a profile author placing a control point and a photographer
|
||||
dragging one mean the same thing by it.
|
||||
|
||||
The order still reads correctly from this directory: the base curve runs after
|
||||
every node in the chain and before the conversion out of camera space. That is
|
||||
the same reasoning `exposure` records under `placement:` — corrections to
|
||||
capture are only meaningful on linear values, so the rendering goes last.
|
||||
|
||||
## Errors
|
||||
|
||||
The build script reports failures by naming the key you got wrong, and exits
|
||||
|
||||
Reference in New Issue
Block a user