Projection chips redraw the preview; auto-crop as the DNG default crop

Picking a chip stored the choice for the merge and changed nothing on
screen — the chip did not even highlight, since the selected property
was never written back. Now the pick is reflected, and the job, waiting
for its decision, takes a Preview request, draws the alignment on the
chosen surface at proxy cost and reports again; the drain puts the new
picture and its size up. Auto is the surface the field of view suggests.

Also:
The largest rectangle inside the frames' coverage is found a row at a
time — a histogram of consecutive covered rows and a stack pass per row —
so the composite is never held to be measured (FR-MRG-11). It is written
as DefaultCropOrigin/DefaultCropSize (FR-MRG-4): the file opens on the
picture, the border is still in it, and resetting the crop shows it.
rawler reports the crop as the picture, which the test checks.

FR-MRG-4 records the question raised the same day — fill the border
rather than crop it — as open: a non-generative fill through the heal,
or a generative inpainter with its licence and weights. Neither decided.
This commit is contained in:
2026-09-19 15:24:20 +02:00
parent 30bd276d0b
commit 57ed51c1c5
7 changed files with 301 additions and 47 deletions
+21 -6
View File
@@ -224,12 +224,7 @@ pub fn wire<S, C, F>(
let ctl = ctl.clone();
window.on_merge_confirm(move || {
let Some(w) = weak.upgrade() else { return };
let projection = match ctl.projection.get() {
1 => Some(dr_pano::Projection::Perspective),
2 => Some(dr_pano::Projection::Cylindrical),
3 => Some(dr_pano::Projection::Spherical),
_ => None,
};
let projection = chip_projection(ctl.projection.get());
if let Some(job) = ctl.job.borrow().as_ref() {
let _ = job.decide.send(Decision::Merge { projection });
job.activity.detail("merging");
@@ -240,9 +235,19 @@ pub fn wire<S, C, F>(
});
}
{
let weak = window.as_weak();
let ctl = ctl.clone();
window.on_merge_projection_picked(move |i| {
let Some(w) = weak.upgrade() else { return };
ctl.projection.set(i);
w.set_merge_projection_selected(i);
// Show it: the job redraws the preview on that surface and
// reports again, and the drain puts the new picture up.
if let Some(job) = ctl.job.borrow().as_ref() {
let _ = job.decide.send(Decision::Preview {
projection: chip_projection(i),
});
}
});
}
@@ -348,6 +353,16 @@ fn start<Fetch>(
*ctl.timer.borrow_mut() = Some(timer);
}
/// The projection a chip index names; 0 is "Auto".
fn chip_projection(i: i32) -> Option<dr_pano::Projection> {
match i {
1 => Some(dr_pano::Projection::Perspective),
2 => Some(dr_pano::Projection::Cylindrical),
3 => Some(dr_pano::Projection::Spherical),
_ => None,
}
}
/// Take everything the job has said and reflect it on the page.
fn drain(window: &AppWindow, ctl: &Rc<MergeController>, on_done: &Rc<impl Fn(&AppWindow)>) {
let events = {