Develop a linear DNG from windows and reduced copies of it
DemosaicedImage::linear_rgb16_window uploads part of a linear DNG, or a box-reduced copy of it, and says where it sits in the frame; size() now reports the frame and texture_size() the texels, and the fused pass writes the window into the shader's uniforms. EditGraph::source_region finds the part of the source a view reads, and tiles::plan cuts a render too large for one texture into halo-grown, grid-aligned tiles. The GPU test renders frames a tile at a time from their own windows and compares them with the whole: identical for point operations, within one code value when straightened with clarity on.
This commit is contained in:
@@ -83,6 +83,13 @@ impl ParamCapability {
|
||||
}
|
||||
}
|
||||
|
||||
/// TRACES: FR-DSP-2
|
||||
/// How far past the framing's own footprint [`EditGraph::source_region`]
|
||||
/// reaches when a lens warp is active, as a fraction of the frame on each
|
||||
/// side. Distortion profiles move a corner by a few per cent of the frame; a
|
||||
/// window short of what the warp reads would render the missing strip black.
|
||||
pub const WARP_MARGIN: f32 = 0.04;
|
||||
|
||||
/// An ordered pipeline of operations, plus how the result is framed.
|
||||
pub struct EditGraph {
|
||||
ops: Vec<Box<dyn Operation>>,
|
||||
@@ -292,6 +299,57 @@ impl EditGraph {
|
||||
self.framing.output_size(width, height)
|
||||
}
|
||||
|
||||
/// TRACES: FR-DSP-2 | NFR-RES-2
|
||||
/// The part of the source the visible region reads, as a rectangle in
|
||||
/// normalised source coordinates, clamped to the frame.
|
||||
///
|
||||
/// For a photograph larger than one texture: a render of part of it —
|
||||
/// the canvas zoomed in, one tile of an export — binds only this window
|
||||
/// of the source (see `dr_pipeline::SOURCE_WINDOW_UNIFORM_FIELDS`).
|
||||
///
|
||||
/// The framing is walked on the CPU with [`Framing::source_at`], along
|
||||
/// the border and across the interior, so a straightened or keystoned
|
||||
/// view gets the box around the quadrilateral it actually reads. The lens
|
||||
/// warps have no CPU mirror, so when one is active the box is widened
|
||||
/// by [`WARP_MARGIN`] of the frame on each side: a distortion profile
|
||||
/// moves a corner by a few per cent of the frame at most. `halo`, in
|
||||
/// source pixels, is added on top — the detail stage's reach, which reads
|
||||
/// beyond the pixels it writes.
|
||||
pub fn source_region(&self, source: (u32, u32), halo: u32) -> crate::framing::CropRect {
|
||||
const STEPS: usize = 16;
|
||||
let (sw, sh) = (source.0.max(1), source.1.max(1));
|
||||
let (mut x0, mut y0, mut x1, mut y1) = (f32::MAX, f32::MAX, f32::MIN, f32::MIN);
|
||||
for j in 0..=STEPS {
|
||||
for i in 0..=STEPS {
|
||||
let out = (i as f32 / STEPS as f32, j as f32 / STEPS as f32);
|
||||
let (x, y) = self.framing.source_at(out, sw, sh);
|
||||
x0 = x0.min(x);
|
||||
y0 = y0.min(y);
|
||||
x1 = x1.max(x);
|
||||
y1 = y1.max(y);
|
||||
}
|
||||
}
|
||||
let warp = if crate::lens::compose_warps(&self.warps).is_active() {
|
||||
WARP_MARGIN
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
// Two pixels beyond the halo: the bilinear tap's second texel, and
|
||||
// the rounding of the box to whole pixels by the caller.
|
||||
let px = (halo as f32 + 2.0) / sw as f32;
|
||||
let py = (halo as f32 + 2.0) / sh as f32;
|
||||
let x0 = (x0 - warp - px).clamp(0.0, 1.0);
|
||||
let y0 = (y0 - warp - py).clamp(0.0, 1.0);
|
||||
let x1 = (x1 + warp + px).clamp(0.0, 1.0);
|
||||
let y1 = (y1 + warp + py).clamp(0.0, 1.0);
|
||||
crate::framing::CropRect {
|
||||
x: x0,
|
||||
y: y0,
|
||||
width: (x1 - x0).max(0.0),
|
||||
height: (y1 - y0).max(0.0),
|
||||
}
|
||||
}
|
||||
|
||||
/// Descriptors for every operation, in order.
|
||||
///
|
||||
/// Operations only — framing is not one, and is reached through
|
||||
|
||||
Reference in New Issue
Block a user