Files
lightcraft/crates/engine/src/upright_frames.rs
T
Alex ChambersandClaude Opus 5.5 1c37bda98f LR-META-XMP: Upright corrections move between photos in each photo's frame; Synchronize Folder reads sidecars in it (#205)
Second review round.

- Paste, Paste from Previous, Sync and Auto Sync copied a stored Upright correction as it was:
  a virtual copy turned right got the original's matrix (15.4 px mean, 23.2 px max at
  3200 x 4800), and so did a file with another EXIF orientation. They now re-express it from the
  source photo's frame into each target's through the stored image, as Lightroom keeps it
  (upright_frames). A patch that turns a photo without bringing a correction (a crop paste, a
  preset) turns the correction it keeps. Import records the file's orientation in the catalog
  (Photo::file_orientation, optional); photos imported earlier are taken as stored the way they
  are shown.
- Synchronize Folder read sidecars without the photo's frame: a sidecar whose only change was its
  time undid import's crop conversion and cleared the kept Upright, silently. Its scan and its read
  now use the recorded frame (else the file's), and its report carries the sidecars' warnings.
  Without any frame an existing photo keeps its crop, and the report says so.
- The Lightroom catalog import's XMP column dropped its diagnostics (Guided guides vanished); they
  now go into the per-image report, once alongside the develop settings' ones.
- The release note says what isn't covered yet.

The new regressions fail on the previous commit and pass here.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 17:09:50 +02:00

62 lines
3.2 KiB
Rust

//! Upright corrections belong to the frame of the photo they are stored with
//! (`Geometry::upright_transform`: centred coordinates of the photo as shown, after its file's
//! orientation and its own Rotate Left / Right). Settings moved between photos (Paste, Paste from
//! Previous, Sync, Auto Sync) re-express a correction in the target's frame through the stored
//! image, which is how Lightroom keeps it, and a settings patch that turns a photo turns the
//! correction it keeps (issue #205).
use lightcraft_catalog::Photo;
use lightcraft_develop::DevelopSettings;
use lightcraft_geom::{Homography, Orientation};
use serde_json::Value;
use crate::sidecar::StoredFrame;
/// A photo's frame for its Upright correction: its file's stored image, then its own rotation.
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct PhotoFrame {
pub stored: StoredFrame,
pub user: Orientation,
}
/// The frame of photo `p` with settings `s`. A photo imported before the file's orientation was
/// recorded is taken as stored the way it is shown.
pub fn frame_of(p: &Photo, s: &DevelopSettings) -> Option<PhotoFrame> {
let stored = crate::sidecar::recorded_frame(p).or_else(|| StoredFrame::of_shown(Orientation::Normal, f64::from(p.width), f64::from(p.height)))?;
Some(PhotoFrame { stored, user: s.orientation })
}
/// Correction `h`, made in frame `from`, as the same correction of the stored image in frame `to`.
pub fn reexpress(h: &Homography, from: &PhotoFrame, to: &PhotoFrame) -> Option<Homography> {
let on_stored = crate::crs::upright_on_stored(h, from.stored.orientation.then(from.user), from.stored.width, from.stored.height)?;
let saved = crate::crs::SavedUpright { mode: lightcraft_develop::Upright::Auto, matrix: on_stored };
crate::crs::upright_in_frame(&saved, to.stored.orientation.then(to.user), to.stored.width, to.stored.height)
}
/// Settings `after` of photo `p` (which had `before`) once `patch` is applied: a correction the
/// patch brought from photo frame `source` is put into `p`'s frame; a correction `p` keeps is
/// turned when the patch turned the photo. A patch from nowhere in particular (a preset) leaves a
/// correction it brings as it is.
pub fn settle(p: &Photo, before: &DevelopSettings, after: &mut DevelopSettings, patch: &Value, source: Option<&PhotoFrame>) {
let Some(target) = frame_of(p, after) else { return };
let (from, h) = if patch.pointer("/geometry/upright_transform").is_some() {
(source.copied(), after.geometry.upright_transform)
} else if after.orientation != before.orientation {
(frame_of(p, before), after.geometry.upright_transform)
} else {
return;
};
if let (Some(from), Some(h)) = (from, h) {
// a frame it can't be put in (no size) leaves it to be solved again
after.geometry.upright_transform = reexpress(&h, &from, &target);
}
}
/// `patch` applied to settings `before` of photo `p` for a transfer from photo frame `source`,
/// with the Upright correction in `p`'s frame ([`settle`]).
pub fn transferred(p: &Photo, before: &DevelopSettings, patch: &Value, source: Option<&PhotoFrame>) -> DevelopSettings {
let mut after = lightcraft_develop::apply_partial(before, patch, 1.0);
settle(p, before, &mut after, patch, source);
after
}