Measure the white balance probe in camera RGB, where the gains multiply
Pressing "pick" and clicking a near-neutral wall on a Canon 6D frame set tint to -77 and turned the whole photograph green. The white balance operation runs first in the chain, on camera RGB, before the body's base curve and colour matrix; the probe was read off a display render after all three, and the solve treated that sRGB triple as if the gains multiplied it directly. On a JPEG the two spaces coincide, which is why the existing tests passed while the picker was broken on every raw file. The probe now reads the camera-space tap a merge stitches from, composed under the edit's own framing so a fraction of the canvas is a fraction of the probe, and puts the as-shot balance on itself - exactly the value the operation's gains are about to multiply. No operations run in the tap, so nothing has to be stripped and restored, and the display target is left alone, so a sample that found nothing usable no longer needs a redraw. A raw-frame test with the 6D's matrix and a typical as-shot balance samples a warm grey and asserts the rendered pixel comes back neutral; it fails on the previous probe.
This commit is contained in:
@@ -857,6 +857,17 @@ impl EditGraph {
|
||||
crate::operation::compose_camera_linear(&self.warps, self.framing.baseline(), view)
|
||||
}
|
||||
|
||||
/// TRACES: FR-DEV-3
|
||||
/// The camera-space tap under this edit's own framing — crop, view,
|
||||
/// rotation and all — so a fraction of what is on the canvas is a
|
||||
/// fraction of what this renders. Nothing else of the edit: no
|
||||
/// operation, no mask, no repair. See
|
||||
/// [`crate::operation::compose_camera_probe`] for why the white balance
|
||||
/// picker reads from here and not from the display.
|
||||
pub fn compose_camera_probe(&self) -> ComposedShader {
|
||||
crate::operation::compose_camera_probe(&self.warps, &self.framing)
|
||||
}
|
||||
|
||||
/// TRACES: FR-DEV-19c
|
||||
/// [`Self::compose_for`], with one layer's mask drawn over the picture.
|
||||
///
|
||||
|
||||
@@ -79,10 +79,14 @@ const FLOOR: f32 = 1e-4;
|
||||
/// TRACES: FR-DEV-3
|
||||
/// Move the graph so that `sample` renders neutral.
|
||||
///
|
||||
/// `sample` is linear RGB, as the operation's own gains multiply it — that is,
|
||||
/// measured with the sampling operation at its defaults. Returns whether the
|
||||
/// graph was moved: `false` where the chain offers no white point widget, or
|
||||
/// where the colour has no balance in it to correct.
|
||||
/// `sample` is the linear triple the operation's own gains multiply — camera
|
||||
/// RGB with the camera's as-shot balance on, *before* the body's base curve
|
||||
/// and matrix, and with the sampling operation at its defaults. Not the
|
||||
/// pixel on the screen: the matrix mixes the channels on the way there, so
|
||||
/// a colour read after it does not answer to these gains, and a solve over
|
||||
/// one lands somewhere no sample asked for. Returns whether the graph was
|
||||
/// moved: `false` where the chain offers no white point widget, or where the
|
||||
/// colour has no balance in it to correct.
|
||||
///
|
||||
/// **Absolute, not relative.** The values written depend on the colour and not
|
||||
/// on where the sliders happened to be, so sampling the same wall twice lands
|
||||
|
||||
@@ -431,9 +431,11 @@ pub enum OutputMode {
|
||||
/// no operations, and the caller fills the reserved uniforms neutral —
|
||||
/// unit white balance, identity matrix, base curve off — so what is
|
||||
/// stored is the sensor's own numbers, demosaiced and undistorted. Only
|
||||
/// [`compose_camera_linear`] produces it, and only
|
||||
/// `AdjustPass::render_camera_linear` accepts it, so the neutral
|
||||
/// uniforms cannot be forgotten by a caller that composed it by mistake.
|
||||
/// [`compose_camera_probe`] produces it — for a merge through
|
||||
/// [`compose_camera_linear`], and for the white balance picker under the
|
||||
/// edit's own framing — and only `AdjustPass::render_camera_linear`
|
||||
/// accepts it, so the neutral uniforms cannot be forgotten by a caller
|
||||
/// that composed it by mistake.
|
||||
///
|
||||
/// Thirty-two bits rather than sixteen because the composite is written
|
||||
/// back as a RAW at the sensor's scale (FR-MRG-3): a 14-bit sensor has
|
||||
@@ -614,9 +616,26 @@ pub fn compose_camera_linear(
|
||||
// upright too, and `view` is a fraction of the upright frame.
|
||||
framing.set_baseline(baseline);
|
||||
framing.set_view(view);
|
||||
compose_camera_probe(warps, &framing)
|
||||
}
|
||||
|
||||
/// TRACES: FR-DEV-3
|
||||
/// The same tap under a framing the caller chose: the white balance probe.
|
||||
///
|
||||
/// A neutral picked off the canvas has to be measured in the space the
|
||||
/// white balance gains multiply, and that is camera RGB — the operation
|
||||
/// runs before the body's matrix, and a probe read after the matrix would
|
||||
/// be solving the wrong equation on any body whose matrix mixes the
|
||||
/// channels, which is every body. It also has to be measured at the pixel
|
||||
/// the canvas is showing, which is why this takes the edit's own framing
|
||||
/// where a merge passes the file's orientation and a tile.
|
||||
pub fn compose_camera_probe(
|
||||
warps: &[Box<dyn crate::lens::Warp>],
|
||||
framing: &Framing,
|
||||
) -> ComposedShader {
|
||||
compose_inner(
|
||||
&[],
|
||||
&framing,
|
||||
framing,
|
||||
ColourSpace::Srgb,
|
||||
&MaskStack::new(),
|
||||
&crate::spot::SpotSet::new(),
|
||||
@@ -668,7 +687,7 @@ fn compose_inner(
|
||||
// twice and be bound to a texture of the wrong format.
|
||||
//
|
||||
// `forced` is the one exception, and it is not a caller flag in the
|
||||
// sense above: `compose_camera_linear` is the only function that passes
|
||||
// sense above: `compose_camera_probe` is the only function that passes
|
||||
// it, with an empty operation list, and the mode it forces has its own
|
||||
// storage format and its own render entry on the GPU side.
|
||||
let output_mode = forced.unwrap_or(
|
||||
|
||||
Reference in New Issue
Block a user