Average a patch under the white balance picker, not one photosite
The probe's comment said a 192px render "averages a small neighbourhood into each of its pixels". It does not: the composed shader fetches the source at one position per output pixel - nearest for an unrotated frame, four photosites blended otherwise - so the probe was a point sample of a noisy sensor, and two painted-white air conditioners on the same wall answered +37 and -50. The tap is now narrowed to the patch of the canvas around the click, a couple of percent of its width and square on screen, and rendered at 64x64 with interpolation forced on, which puts a sample on every sensor pixel under it at any ordinary zoom. The samples are averaged, with the void and clipped ones left out rather than allowed to pull the mean, and fewer than half surviving is refused. compose_camera_probe takes the patch; the merge's compose_camera_linear keeps its nearest sampling. The readback shrinks from six megabytes to sixty-four kilobytes. A frame of alternating warm and cool columns, averaging neutral, moves the controls by at most two units; a point sample swung them to sixty.
This commit is contained in:
@@ -604,7 +604,9 @@ pub fn compose_full_revealing(
|
||||
warps: &[Box<dyn crate::lens::Warp>],
|
||||
reveal: Option<&crate::mask::Reveal>,
|
||||
) -> ComposedShader {
|
||||
compose_inner(ops, framing, output, masks, spots, warps, reveal, None)
|
||||
compose_inner(
|
||||
ops, framing, output, masks, spots, warps, reveal, None, false,
|
||||
)
|
||||
}
|
||||
|
||||
/// TRACES: FR-MRG-2
|
||||
@@ -629,7 +631,7 @@ 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)
|
||||
compose_camera_tap(warps, &framing, false)
|
||||
}
|
||||
|
||||
/// TRACES: FR-DEV-3
|
||||
@@ -642,9 +644,26 @@ pub fn compose_camera_linear(
|
||||
/// 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.
|
||||
///
|
||||
/// **Interpolated whatever the framing says.** The point of rendering a
|
||||
/// patch is to average what is under it, and the nearest sampling an
|
||||
/// unrotated frame otherwise gets is a comb: at two source pixels per
|
||||
/// probe pixel it lands on the same column of any pattern every time, and
|
||||
/// the average of a thousand samples is then the average of nothing.
|
||||
pub fn compose_camera_probe(
|
||||
warps: &[Box<dyn crate::lens::Warp>],
|
||||
framing: &Framing,
|
||||
) -> ComposedShader {
|
||||
compose_camera_tap(warps, framing, true)
|
||||
}
|
||||
|
||||
/// The camera-space tap proper: no operations, `rgba32float`, and the
|
||||
/// profile uniforms left for the GPU side to fill neutral. `smooth` forces
|
||||
/// the interpolating sampler; see the two callers for who wants it and why.
|
||||
fn compose_camera_tap(
|
||||
warps: &[Box<dyn crate::lens::Warp>],
|
||||
framing: &Framing,
|
||||
smooth: bool,
|
||||
) -> ComposedShader {
|
||||
compose_inner(
|
||||
&[],
|
||||
@@ -655,6 +674,7 @@ pub fn compose_camera_probe(
|
||||
warps,
|
||||
None,
|
||||
Some(OutputMode::CameraLinear),
|
||||
smooth,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -668,6 +688,7 @@ fn compose_inner(
|
||||
warps: &[Box<dyn crate::lens::Warp>],
|
||||
reveal: Option<&crate::mask::Reveal>,
|
||||
forced: Option<OutputMode>,
|
||||
smooth: bool,
|
||||
) -> ComposedShader {
|
||||
// The lens corrections, composed into one coordinate transform. Beside
|
||||
// `framing` because they are the other half of the same stage: framing
|
||||
@@ -874,7 +895,10 @@ fn compose_inner(
|
||||
// framing alone — which is what this did before the warps existed — would
|
||||
// have nearest-neighboured a distortion correction on an unstraightened
|
||||
// frame, and the aliasing would have looked like a bad profile.
|
||||
let interpolate = framing.needs_interpolation() || warp.is_active();
|
||||
// `smooth` is the third reason, and the only one a caller states: the
|
||||
// white balance probe averages a patch and cannot do that through a
|
||||
// nearest-neighbour comb (see `compose_camera_probe`).
|
||||
let interpolate = smooth || framing.needs_interpolation() || warp.is_active();
|
||||
|
||||
// Declared ahead of the warp block, which assigns to them. They enter
|
||||
// equal to `p` so that a chain mixing a splitting warp with a
|
||||
|
||||
Reference in New Issue
Block a user