Offer the border fill on the merge page, experimental, with every knob on it
A Border choice beside the projection — crop to the picture, or fill it — that redraws the preview filled so the invented pixels are seen before they are confirmed (FR-MRG-1), greyed with the reason when the model is not there. The job fills at half the composite's resolution in a display-ish space (white balance, matrix, gamma; invertible) and samples the result back into the linear DNG wherever no frame reached; the sidecar's merge line says border filled and with which knobs. Experimental because the fill is right in thin borders and wrong in deep corners, where the model's Places2 prior puts clouds in sky and water under grass; so its six knobs — working scale, edge erosion, coarse pass, band width, mirror depth, seam feather — are sliders under the choice, each committing a redraw, until the defaults are right.
This commit is contained in:
+122
-3
@@ -19,7 +19,8 @@ use slint::ComponentHandle;
|
||||
|
||||
use crate::activity::{Activity, ActivityLog, Kind};
|
||||
use crate::merge::{
|
||||
self, AlignmentReport, Cancel, Decision, MergeDestination, MergeEvent, MergeInput, MergeRequest,
|
||||
self, AlignmentReport, Cancel, Decision, FillSettings, MergeDestination, MergeEvent,
|
||||
MergeInput, MergeRequest,
|
||||
};
|
||||
use crate::{AppWindow, MergeFrameRow};
|
||||
|
||||
@@ -41,9 +42,21 @@ pub struct MergeController {
|
||||
timer: RefCell<Option<slint::Timer>>,
|
||||
/// The projection chip: 0 auto, then the three in `Projection`'s order.
|
||||
projection: Cell<i32>,
|
||||
/// The border chip: 0 crop, 1 fill (FR-MRG-4).
|
||||
border: Cell<i32>,
|
||||
/// The fill's knobs, as the page's sliders have them.
|
||||
fill: Cell<FillSettings>,
|
||||
report: RefCell<Option<AlignmentReport>>,
|
||||
}
|
||||
|
||||
impl MergeController {
|
||||
/// The fill to ask for: the knobs if the border chip says fill, else
|
||||
/// none.
|
||||
fn fill_choice(&self) -> Option<FillSettings> {
|
||||
(self.border.get() == 1).then(|| self.fill.get())
|
||||
}
|
||||
}
|
||||
|
||||
impl MergeController {
|
||||
pub fn new(activity: Rc<ActivityLog>) -> Rc<Self> {
|
||||
Rc::new(MergeController {
|
||||
@@ -51,6 +64,8 @@ impl MergeController {
|
||||
job: RefCell::new(None),
|
||||
timer: RefCell::new(None),
|
||||
projection: Cell::new(0),
|
||||
border: Cell::new(0),
|
||||
fill: Cell::new(FillSettings::default()),
|
||||
report: RefCell::new(None),
|
||||
})
|
||||
}
|
||||
@@ -235,6 +250,19 @@ pub fn wire<S, C, F>(
|
||||
fetch,
|
||||
&on_done,
|
||||
);
|
||||
// `DARKROOM_START_MERGE_FILL=1` opens it with the fill chosen,
|
||||
// to look at the knobs; the preview fills once the alignment
|
||||
// is in, as a chip pick would have it.
|
||||
if std::env::var_os("DARKROOM_START_MERGE_FILL").is_some() {
|
||||
ctl.border.set(1);
|
||||
window.set_merge_border_selected(1);
|
||||
if let Some(job) = ctl.job.borrow().as_ref() {
|
||||
let _ = job.decide.send(Decision::Preview {
|
||||
projection: None,
|
||||
fill: ctl.fill_choice(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,8 +273,9 @@ pub fn wire<S, C, F>(
|
||||
window.on_merge_confirm(move || {
|
||||
let Some(w) = weak.upgrade() else { return };
|
||||
let projection = chip_projection(ctl.projection.get());
|
||||
let fill = ctl.fill_choice();
|
||||
if let Some(job) = ctl.job.borrow().as_ref() {
|
||||
let _ = job.decide.send(Decision::Merge { projection });
|
||||
let _ = job.decide.send(Decision::Merge { projection, fill });
|
||||
job.activity.detail("merging");
|
||||
}
|
||||
w.set_merge_running(true);
|
||||
@@ -266,10 +295,71 @@ pub fn wire<S, C, F>(
|
||||
if let Some(job) = ctl.job.borrow().as_ref() {
|
||||
let _ = job.decide.send(Decision::Preview {
|
||||
projection: chip_projection(i),
|
||||
fill: ctl.fill_choice(),
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
{
|
||||
let weak = window.as_weak();
|
||||
let ctl = ctl.clone();
|
||||
window.on_merge_border_picked(move |i| {
|
||||
let Some(w) = weak.upgrade() else { return };
|
||||
ctl.border.set(i);
|
||||
w.set_merge_border_selected(i);
|
||||
// The fill is shown before it is chosen for good: the job
|
||||
// redraws the preview with the border filled, or not.
|
||||
if let Some(job) = ctl.job.borrow().as_ref() {
|
||||
let _ = job.decide.send(Decision::Preview {
|
||||
projection: chip_projection(ctl.projection.get()),
|
||||
fill: ctl.fill_choice(),
|
||||
});
|
||||
}
|
||||
w.set_merge_running(true);
|
||||
w.set_merge_stage(
|
||||
if i == 1 {
|
||||
"Filling the preview"
|
||||
} else {
|
||||
"Drawing the preview"
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
let weak = window.as_weak();
|
||||
let ctl = ctl.clone();
|
||||
window.on_merge_fill_knob(move |name, value| {
|
||||
let Some(w) = weak.upgrade() else { return };
|
||||
let mut f = ctl.fill.get();
|
||||
let v = value.round().max(0.0) as usize;
|
||||
match name.as_str() {
|
||||
"scale" => f.scale = (v as u32).clamp(1, 8),
|
||||
"erosion" => f.erosion = v,
|
||||
"coarse" => f.params.coarse = v.max(1),
|
||||
"band" => f.params.band = v.max(8),
|
||||
"mirror" => f.params.mirror_depth = v.max(1),
|
||||
"feather" => f.params.feather = v,
|
||||
_ => return,
|
||||
}
|
||||
ctl.fill.set(f);
|
||||
show_fill_knobs(&w, &f);
|
||||
// A knob turned while the fill is chosen redraws the preview
|
||||
// with it; turned under the crop it waits for the chip.
|
||||
if ctl.border.get() != 1 {
|
||||
return;
|
||||
}
|
||||
if let Some(job) = ctl.job.borrow().as_ref() {
|
||||
let _ = job.decide.send(Decision::Preview {
|
||||
projection: chip_projection(ctl.projection.get()),
|
||||
fill: Some(f),
|
||||
});
|
||||
}
|
||||
w.set_merge_running(true);
|
||||
w.set_merge_stage("Filling the preview".into());
|
||||
});
|
||||
}
|
||||
|
||||
// --- stopping and leaving --------------------------------------------
|
||||
{
|
||||
@@ -332,7 +422,8 @@ fn start<Fetch>(
|
||||
let Some(frames) = fetch(&tx, &cancel) else {
|
||||
return;
|
||||
};
|
||||
let request = MergeRequest::new(frames, destination);
|
||||
let mut request = MergeRequest::new(frames, destination);
|
||||
request.inpaint_model = crate::library::inpaint_model();
|
||||
merge::run(gpu, request, tx, decision, cancel);
|
||||
});
|
||||
}
|
||||
@@ -346,6 +437,9 @@ fn start<Fetch>(
|
||||
});
|
||||
*ctl.report.borrow_mut() = None;
|
||||
ctl.projection.set(0);
|
||||
ctl.border.set(0);
|
||||
ctl.fill.set(FillSettings::default());
|
||||
show_fill_knobs(window, &FillSettings::default());
|
||||
|
||||
// The page, from a clean slate.
|
||||
window.set_merge_stage("Fetching".into());
|
||||
@@ -361,6 +455,10 @@ fn start<Fetch>(
|
||||
>::new())));
|
||||
window.set_merge_preview(slint::Image::default());
|
||||
window.set_merge_projection_selected(0);
|
||||
window.set_merge_border_selected(0);
|
||||
window.set_merge_fill_available(false);
|
||||
window.set_merge_fill_note("".into());
|
||||
window.set_merge_preview_filled(false);
|
||||
window.set_merge_done(false);
|
||||
window.set_show_merge(true);
|
||||
|
||||
@@ -530,4 +628,25 @@ fn show_report(window: &AppWindow, report: &AlignmentReport, names: &[String]) {
|
||||
let buffer = slint::SharedPixelBuffer::<slint::Rgba8Pixel>::clone_from_slice(rgba, *w, *h);
|
||||
window.set_merge_preview(slint::Image::from_rgba8(buffer));
|
||||
}
|
||||
window.set_merge_preview_filled(report.filled);
|
||||
match &report.filler {
|
||||
Ok(rung) => {
|
||||
window.set_merge_fill_available(true);
|
||||
window.set_merge_fill_note(format!("runs on {rung}").into());
|
||||
}
|
||||
Err(why) => {
|
||||
window.set_merge_fill_available(false);
|
||||
window.set_merge_fill_note(format!("The border can only be cropped: {why}.").into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The sliders, from the settings.
|
||||
fn show_fill_knobs(window: &AppWindow, f: &FillSettings) {
|
||||
window.set_merge_fill_scale(f.scale as f32);
|
||||
window.set_merge_fill_erosion(f.erosion as f32);
|
||||
window.set_merge_fill_coarse(f.params.coarse as f32);
|
||||
window.set_merge_fill_band(f.params.band as f32);
|
||||
window.set_merge_fill_mirror(f.params.mirror_depth as f32);
|
||||
window.set_merge_fill_feather(f.params.feather as f32);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user