mirror of
https://github.com/storytold/lightcraft.git
synced 2026-10-28 12:08:10 -06:00
M13.2: Photo Merge commands, dialog with live preview, CLI
- engine: `merge.hdr`, `merge.panorama`, `merge.hdrPanorama` (bracket size from EXIF); MergeJob (Send) with progress/cancel, preview renders through the develop pipeline (deghost overlay, auto-crop frame); finish writes <name>-HDR/-Pano.dng next to the first source, imports, selects, applies Auto Settings / auto crop - ui: Photo Merge dialog (HDR / Panorama / HDR Panorama) with live preview on a worker thread, background final merge with progress toast; context menu and Ctrl+H / Ctrl+M (Ctrl now means the physical Control key on macOS) - cli: `merge hdr|panorama|hdr-panorama`, `synth-merge` for synthetic inputs - merge::synth: procedural brackets (DNG) and panorama views; docs/merge.md Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
543fdde41e
commit
bc2cda97cf
@@ -0,0 +1,72 @@
|
||||
//! Photo Merge commands: `merge.hdr`, `merge.panorama`, `merge.hdrPanorama`.
|
||||
//!
|
||||
//! Run synchronously here (CLI, MCP, control channel); the desktop UI plans the same job with
|
||||
//! [`Session::plan_merge`] and runs it on a worker thread. `preview: true` merges ≤ 1024 px frames
|
||||
//! and (with `previewPath`) writes the rendered preview as PNG instead of adding a photo.
|
||||
|
||||
use lightcraft_catalog::PhotoId;
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use super::{CommandSpec, bad, bool_or, cmd, str_param};
|
||||
use crate::{Result, Session};
|
||||
|
||||
fn can_merge(s: &Session) -> std::result::Result<(), String> {
|
||||
if cfg!(target_arch = "wasm32") && s.media.file_bytes.is_none() {
|
||||
return Err("Photo Merge needs access to the photo files".into());
|
||||
}
|
||||
// the photo count is checked when run (an explicit `ids` parameter may name them)
|
||||
if s.catalog.photos().nth(1).is_none() { Err("the library has fewer than 2 photos".into()) } else { Ok(()) }
|
||||
}
|
||||
|
||||
fn run(s: &mut Session, id: &str, p: &Value) -> Result<Value> {
|
||||
let (kind, finish) = crate::merge::parse(id, p)?;
|
||||
let ids: Vec<PhotoId> = s.targets(p);
|
||||
if !bool_or(p, "preview", false) {
|
||||
return s.merge_now(kind, finish, &ids);
|
||||
}
|
||||
let job = s.plan_merge(kind, finish, &ids, true)?;
|
||||
let out = job.run(&|_, _| true).map_err(|e| bad(id, e))?;
|
||||
let mut info = out.info;
|
||||
if let (Some(path), Some(img)) = (str_param(p, "previewPath"), &out.preview) {
|
||||
let png = lightcraft_codecs::encode_png(&lightcraft_codecs::EncodeImage::rgba8(img), &lightcraft_codecs::EncodeMeta::default())
|
||||
.map_err(|e| bad(id, e.to_string()))?;
|
||||
std::fs::write(path, png).map_err(|e| bad(id, format!("{path}: {e}")))?;
|
||||
info["previewPath"] = json!(path);
|
||||
}
|
||||
if let Some(img) = &out.preview {
|
||||
info["previewSize"] = json!([img.width, img.height]);
|
||||
}
|
||||
Ok(info)
|
||||
}
|
||||
|
||||
pub fn specs() -> Vec<CommandSpec> {
|
||||
vec![
|
||||
cmd!(
|
||||
"merge.hdr",
|
||||
"HDR Merge",
|
||||
[],
|
||||
None,
|
||||
"{ids?, align=true, deghost=none|low|medium|high, autoSettings=true, stack=false, preview=false, showOverlay=false, previewPath?}",
|
||||
can_merge,
|
||||
|s, p| run(s, "merge.hdr", p)
|
||||
),
|
||||
cmd!(
|
||||
"merge.panorama",
|
||||
"Panorama Merge",
|
||||
[],
|
||||
None,
|
||||
"{ids?, projection=auto|spherical|cylindrical|perspective, boundaryWarp=0..100, autoCrop=false, fillEdges=false, autoSettings=true, stack=false, maxMegapixels=40, preview=false, previewPath?}",
|
||||
can_merge,
|
||||
|s, p| run(s, "merge.panorama", p)
|
||||
),
|
||||
cmd!(
|
||||
"merge.hdrPanorama",
|
||||
"HDR Panorama Merge",
|
||||
[],
|
||||
None,
|
||||
"{ids?, bracket=0 (auto from EXIF), align, deghost, projection, boundaryWarp, autoCrop, fillEdges, autoSettings, stack, preview, previewPath?}",
|
||||
can_merge,
|
||||
|s, p| run(s, "merge.hdrPanorama", p)
|
||||
),
|
||||
]
|
||||
}
|
||||
@@ -8,6 +8,7 @@ mod develop;
|
||||
mod edit;
|
||||
mod library;
|
||||
mod masks;
|
||||
mod merge;
|
||||
mod preset_files;
|
||||
mod query;
|
||||
mod xmp;
|
||||
@@ -102,6 +103,7 @@ pub fn command_specs() -> &'static [CommandSpec] {
|
||||
v.extend(library::specs());
|
||||
v.extend(develop::specs());
|
||||
v.extend(masks::specs());
|
||||
v.extend(merge::specs());
|
||||
v.extend(query::specs());
|
||||
v.extend(xmp::specs());
|
||||
v.extend(preset_files::specs());
|
||||
|
||||
Reference in New Issue
Block a user