Open and export a linear DNG too large for one texture
A 22927×8966 Lightroom panorama opened as its embedded preview with develop withheld, because no texture could hold it. DevelopSession now opens a linear DNG past PROXY_EDGE (8192) on a box-reduced copy, and keeps the full resolution on the CPU. The canvas at fit, the thumbnail, the histograms and the masks work from the copy; a render finer than it — the canvas zoomed in, a tile of the export — samples a window cut from the full resolution, kept while the view stays inside it. The export renders in halo-grown tiles of 4096 and assembles them. On the panorama: decode 1.6 s, open 210 ms, canvas 37 ms, a zoomed window 130 ms, the full-size export 5.9 s.
This commit is contained in:
@@ -112,6 +112,19 @@ pub struct DevelopSession {
|
||||
/// history still stands on that crop. See [`super::framing::CropNotice`].
|
||||
pub(super) crop_notice: Option<super::framing::CropNotice>,
|
||||
pub(super) demosaiced: Arc<DemosaicedImage>,
|
||||
/// TRACES: FR-DSP-2 | NFR-RES-2
|
||||
/// The photograph at full resolution, when it is too large to hold in one
|
||||
/// texture. `demosaiced` is then a reduced copy of it, which is all the
|
||||
/// canvas needs at fit and all the histograms, masks and probes ever
|
||||
/// read; a render that wants more detail than the copy has — the canvas
|
||||
/// zoomed in, a tile of the export — cuts a window from this instead
|
||||
/// (see `source_for`). `None` for every photograph that fits, which is
|
||||
/// nearly all of them.
|
||||
pub(super) full: Option<Arc<RawImage>>,
|
||||
/// The last window cut from `full`: the region it covers in normalised
|
||||
/// source coordinates, the reduction it was cut at, and the texture. Kept
|
||||
/// so panning a zoomed canvas does not re-upload what is already there.
|
||||
pub(super) window: Option<(CropRect, u32, Arc<DemosaicedImage>)>,
|
||||
pub(super) adjust: AdjustPass,
|
||||
/// TRACES: FR-DSP-7
|
||||
/// Optional, because a session that cannot count its frames is still a
|
||||
@@ -312,6 +325,16 @@ pub struct DevelopSession {
|
||||
pub(super) display_space: dr_types::ColourSpace,
|
||||
}
|
||||
|
||||
/// TRACES: FR-DSP-2 | NFR-RES-2
|
||||
/// The longest edge a photograph is developed at whole; one larger is
|
||||
/// developed from a reduced copy and full-resolution windows.
|
||||
///
|
||||
/// Below the device's own limit on purpose. A 16384-texel texture of
|
||||
/// half floats is two gigabytes, and the canvas never shows more than a few
|
||||
/// thousand pixels of it; 8192 is four times a 4K long edge, the same bound
|
||||
/// the JPEG path fits a film scan to.
|
||||
pub const PROXY_EDGE: u32 = 8192;
|
||||
|
||||
impl DevelopSession {
|
||||
/// Demosaic an image and prepare its edit graph.
|
||||
///
|
||||
@@ -329,6 +352,35 @@ impl DevelopSession {
|
||||
Ok(Self::with_source(ctx, demosaiced, orientation))
|
||||
}
|
||||
|
||||
/// TRACES: FR-DSP-2 | NFR-RES-2
|
||||
/// Open a decoded photograph, whatever its size.
|
||||
///
|
||||
/// A photograph that fits the device goes through [`Self::open`] as it
|
||||
/// always did. A linear DNG larger than [`PROXY_EDGE`] — a stitched
|
||||
/// panorama — is instead held at full resolution on the CPU and opened
|
||||
/// on a copy reduced to fit, from which the canvas, the histograms and
|
||||
/// the masks work; the full resolution is cut into windows only where a
|
||||
/// render needs it. Without this the file opened as its embedded preview
|
||||
/// with develop withheld.
|
||||
pub fn open_owned(
|
||||
ctx: &GpuContext,
|
||||
raw: RawImage,
|
||||
orientation: dr_types::Orientation,
|
||||
) -> Result<Self, String> {
|
||||
let (w, h) = (raw.crop.width.max(1), raw.crop.height.max(1));
|
||||
let edge = PROXY_EDGE.min(DemosaicedImage::max_dimension(ctx));
|
||||
if raw.samples_per_pixel != 3 || w.max(h) <= edge {
|
||||
return Self::open(ctx, &raw, orientation);
|
||||
}
|
||||
let reduce = w.max(h).div_ceil(edge);
|
||||
log::info!("{w}×{h} is larger than one texture; developing from a 1/{reduce} copy");
|
||||
let proxy = DemosaicedImage::linear_rgb16_window(ctx, &raw, [0, 0, w, h], reduce)
|
||||
.map_err(|e| e.to_string())?;
|
||||
let mut session = Self::with_source(ctx, proxy, orientation);
|
||||
session.full = Some(Arc::new(raw));
|
||||
Ok(session)
|
||||
}
|
||||
|
||||
/// Prepare an edit graph over an already-processed RGB image.
|
||||
///
|
||||
/// The JPEG path. A JPEG is already demosaiced, so there is no sensor
|
||||
@@ -379,6 +431,8 @@ impl DevelopSession {
|
||||
compared_snapshot: None,
|
||||
crop_notice: None,
|
||||
demosaiced: Arc::new(demosaiced),
|
||||
full: None,
|
||||
window: None,
|
||||
adjust: AdjustPass::new(ctx),
|
||||
histogram: HistogramPass::new(ctx)
|
||||
.inspect_err(|e| log::warn!("no histogram on this device: {e}"))
|
||||
|
||||
Reference in New Issue
Block a user