mirror of
https://github.com/storytold/lightcraft.git
synced 2026-10-24 10:09:01 -06:00
This commit is contained in:
@@ -404,10 +404,12 @@ pub fn parse(id: &str, p: &Value) -> Result<(MergeKind, MergeFinish)> {
|
||||
}
|
||||
|
||||
impl Session {
|
||||
/// Plan a merge of `ids` (catalog order of the given ids is kept).
|
||||
/// Plan a merge of `ids` (catalog order of the given ids is kept). A photo named more than once is merged once
|
||||
/// (issue #1947): it adds nothing to the merge, and each copy would be decoded and held in memory.
|
||||
pub fn plan_merge(&self, kind: MergeKind, finish: MergeFinish, ids: &[PhotoId], preview: bool) -> Result<MergeJob> {
|
||||
let mut sources = Vec::new();
|
||||
for id in ids {
|
||||
let mut seen = std::collections::HashSet::new();
|
||||
for id in ids.iter().filter(|id| seen.insert(**id)) {
|
||||
let p = self.catalog.photo(*id).ok_or(lightcraft_catalog::CatalogError::NoPhoto(*id))?;
|
||||
match &p.source {
|
||||
Source::File { path } => sources.push((*id, path.clone())),
|
||||
|
||||
@@ -226,3 +226,27 @@ fn panorama_size_is_held_to_memory() {
|
||||
assert_eq!(mp(fit_in_memory(&asked, photos, gb(16))), 20.0, "a size asked for is kept when it fits");
|
||||
assert_eq!(fit_in_memory(&full, 1e6, None), fit_in_memory(&full, 1e6, gb(8)), "memory unknown: taken as 8 GB");
|
||||
}
|
||||
|
||||
/// A photo named twice is merged once (issue #1947): `[a, a, a]` is one photo, too few to merge, and `[a, b, a, b]`
|
||||
/// merges two frames rather than holding four in memory.
|
||||
#[test]
|
||||
fn a_photo_named_twice_is_merged_once() {
|
||||
let dir = temp_dir("dup-ids");
|
||||
let mut paths = Vec::new();
|
||||
for (i, b) in lightcraft_merge::synth::bracket_dngs(96, 64, &[-1.0, 1.0]).unwrap().into_iter().enumerate() {
|
||||
let p = dir.join(format!("IMG_{i}.dng"));
|
||||
std::fs::write(&p, b).unwrap();
|
||||
paths.push(p.to_string_lossy().to_string());
|
||||
}
|
||||
let mut s = Session::new().with_fs();
|
||||
let ids = import(&mut s, paths);
|
||||
let (a, b) = (ids[0], ids[1]);
|
||||
for cmd in ["merge.hdr", "merge.panorama"] {
|
||||
let e = s.execute(cmd, &json!({"ids": [a, a, a], "preview": true})).unwrap_err();
|
||||
assert!(e.to_string().contains("at least 2"), "{cmd}: {e}");
|
||||
}
|
||||
let (kind, finish) = crate::merge::parse("merge.hdr", &json!({})).unwrap();
|
||||
let job = s.plan_merge(kind, finish, &[a, b, a, b, a].map(lightcraft_catalog::PhotoId), true).unwrap();
|
||||
assert_eq!(job.sources.iter().map(|(p, _)| p.0).collect::<Vec<_>>(), vec![a, b]);
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
|
||||
@@ -26,6 +26,11 @@
|
||||
|
||||
## 2026-10-15
|
||||
|
||||
### Photo Merge takes each photo once
|
||||
- A merge that names the same photo more than once (through `ids` on the control channel, MCP or the CLI) now merges
|
||||
it once (issue #1947). Naming one photo three times no longer "merges" it with itself, and a long repeated list no
|
||||
longer decodes the photo again for every entry.
|
||||
|
||||
### Constrain Crop tightens the crop you see
|
||||
- Geometry ▸ Constrain Crop now changes the crop itself, as in Lightroom (issue #1927). Ticking it, or moving a
|
||||
Geometry slider, Upright or a lens correction while it is ticked, shrinks the crop about its centre until the blank
|
||||
|
||||
Reference in New Issue
Block a user