mirror of
https://github.com/storytold/lightcraft.git
synced 2026-10-24 10:09:01 -06:00
Merge PR #2177: LRC-DEV-AUTOSYNC: sync HDR state while retaining per-photo tuning
crates/engine/src/lib.rs: keep both explicit comparison leaves (camera_local_tone and hdr). crates/engine/src/tests.rs: keep both sides' tests.
This commit is contained in:
@@ -956,6 +956,9 @@ impl Session {
|
||||
// switching camera local tone mapping off is a change without copying untouched values.
|
||||
old_json["profile"]["camera_local_tone"] = Value::Bool(old.profile.camera_local_tone);
|
||||
new_json["profile"]["camera_local_tone"] = Value::Bool(new.profile.camera_local_tone);
|
||||
// Compare actual HDR leaves even when persisted defaults omit the whole HDR object.
|
||||
old_json["hdr"] = serde_json::json!(old.hdr);
|
||||
new_json["hdr"] = serde_json::json!(new.hdr);
|
||||
let Some(mut delta) = json_delta(&old_json, &new_json) else { return Vec::new() };
|
||||
if let Some(o) = delta.as_object_mut() {
|
||||
for k in ["spots", "red_eye", "version", "process"] {
|
||||
|
||||
@@ -1012,6 +1012,24 @@ fn auto_sync_camera_local_tone_changes_only_when_edited() {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn auto_sync_hdr_off_returns_selected_photos_to_sdr() {
|
||||
use lightcraft_catalog::PhotoId;
|
||||
let mut s = demo();
|
||||
let ids: Vec<u64> = s.catalog.photos().take(2).map(|p| p.id.0).collect();
|
||||
assert_eq!(ids.len(), 2);
|
||||
for id in &ids {
|
||||
assert_eq!(s.develop_of(PhotoId(*id)).unwrap().hdr, lightcraft_develop::Hdr::default(), "default-HDR fixture");
|
||||
}
|
||||
s.execute("library.select", &json!({"ids": ids, "active": ids[0]})).unwrap();
|
||||
s.execute("develop.autoSync", &json!({"on": true})).unwrap();
|
||||
let enabled = |s: &Session| ids.iter().map(|id| s.develop_of(PhotoId(*id)).unwrap().hdr.enabled).collect::<Vec<_>>();
|
||||
s.execute("develop.hdr", &json!({"enabled": true})).unwrap();
|
||||
assert_eq!(enabled(&s), vec![true, true], "HDR On must reach both selected photos");
|
||||
s.execute("develop.hdr", &json!({"enabled": false})).unwrap();
|
||||
assert_eq!(enabled(&s), vec![false, false], "HDR Off must reach both selected photos");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn json_delta_keeps_only_changes() {
|
||||
let a = json!({"light": {"exposure": 0, "contrast": 5}, "masks": [1], "x": 1});
|
||||
@@ -1777,3 +1795,46 @@ fn malformed_photo_targets_are_refused_not_widened() {
|
||||
let preset = s.presets.first().unwrap().id.clone();
|
||||
s.execute("preset.apply", &json!({"id": preset})).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn auto_sync_hdr_enabled_preserves_per_photo_tuning() {
|
||||
use lightcraft_develop::{DevelopSettings, Hdr, ProcessVersion};
|
||||
let mut s = demo();
|
||||
let ids: Vec<_> = s.catalog.photos().take(2).map(|p| p.id).collect();
|
||||
assert_eq!(ids.len(), 2);
|
||||
s.execute("develop.autoSync", &json!({"on": false})).unwrap();
|
||||
// Keep distinct stored processes explicit: the default is now V2; no rendering claim is made.
|
||||
let mut expected = [DevelopSettings::default(), DevelopSettings { process: ProcessVersion(2), ..DevelopSettings::default() }];
|
||||
expected[0].process = ProcessVersion(1);
|
||||
expected[0].light.exposure = 0.5;
|
||||
expected[0].light.contrast = 12.0;
|
||||
expected[1].light.exposure = -0.25;
|
||||
expected[1].light.contrast = -22.0;
|
||||
expected[1].hdr = Hdr {
|
||||
enabled: false,
|
||||
max_ev: 4.0,
|
||||
sdr_brightness: 11.0,
|
||||
sdr_contrast: -12.0,
|
||||
sdr_highlights: 13.0,
|
||||
sdr_shadows: -14.0,
|
||||
sdr_whites: 15.0,
|
||||
sdr_clarity: -16.0,
|
||||
};
|
||||
for (id, settings) in ids.iter().zip(&expected) {
|
||||
s.set_develop(*id, settings.clone(), "HDR tuning fixture").unwrap();
|
||||
}
|
||||
s.execute("library.select", &json!({"ids": ids.iter().map(|id| id.0).collect::<Vec<_>>(), "active": ids[0].0})).unwrap();
|
||||
s.execute("develop.autoSync", &json!({"on": true})).unwrap();
|
||||
let selection = s.selection.clone();
|
||||
for enabled in [true, false] {
|
||||
s.execute("develop.hdr", &json!({"enabled": enabled})).unwrap();
|
||||
for (id, settings) in ids.iter().zip(&mut expected) {
|
||||
settings.hdr.enabled = enabled;
|
||||
let actual = s.develop_of(*id).unwrap();
|
||||
assert_eq!(actual.hdr, settings.hdr, "HDR tuning must survive enabled={enabled} for photo {id:?}");
|
||||
assert_eq!(actual.as_ref(), &*settings, "other per-photo edits and process must survive enabled={enabled}");
|
||||
}
|
||||
assert_eq!(s.selection, selection, "selection and active photo must survive enabled={enabled}");
|
||||
assert!(s.auto_sync, "Auto Sync must remain enabled");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -842,6 +842,7 @@ Latin stays upright. Advanced Japanese composition remains open. Regression cove
|
||||
| 2026-10-12 | minor | LR-IMP-CAMERA-COVERAGE: Pentax PEFs without area tags lose their masked border lines (issue #1271); status unchanged (🟡) |
|
||||
| 2026-10-12 | minor | Camera-look deep shadows follow the JPEG toe (#1307); camera calibration remains partial |
|
||||
| 2026-10-11 | minor | LR-IMP-PREVIEW-READ: a preview reads the raw's header and its embedded JPEG instead of the whole file (a 56 MB RAF: 56.5 MB → 5.9 MB, ~10 s → 0.96 s over SMB, byte-identical pixels), and an unedited raw's grid thumbnail is that camera JPEG itself, so no sensor decode is queued behind it. A CR3's JPEG track and a TIFF raw whose preview tag sits past the prefix are read through `lightcraft_raw::sparse::preview_plan`, which names the ranges `embedded_preview` reads; the formats it cannot describe (CRW/MRW/X3F, NEF/ORF/PEF) keep the whole-file path |
|
||||
| 2026-10-11 | minor | LRC-DEV-AUTOSYNC: preserve per-photo HDR tuning when synchronizing HDR enabled state; status unchanged. |
|
||||
| 2026-10-12 | minor | LR-BEHAV-ACCESS: canvas handles by keyboard with AccessKit names and values; Tab order test; status stays 🟡 |
|
||||
| 2026-10-12 | minor | LR-BEHAV-ACCESS: AccessKit tree audit test; text fields named, sliders expose range and Increment / Decrement; status stays 🟡 |
|
||||
| 2026-10-12 | minor | LR-PREF-DISPLAY: on Linux, previews follow the display profile the desktop assigns in colord |
|
||||
|
||||
Reference in New Issue
Block a user