Keep the film stock when an edit is saved
`Version::update` is the write path an automatic save goes through. It copied the parameters and the masks and said nothing about the film, so a photograph developed on a stock was written back without it and opened the next time without its emulsion. Nothing reported a failure — the line was simply not there. It is the third of the three routines that captured "the edit" and the only one that got it wrong, which is the argument for not having three. All of them now destructure one `EditState`, so `from_graph`, `update` and `apply` cannot disagree about what an edit consists of, and the next part of one cannot be lost by anybody writing a line too few. Two tests, both of which fail without the fix: the field survives `update`, and the stock survives the round trip through the file. `apply` returns the `FilmRebake` it always implicitly owed, so `apply_version` now reads the debt off the call rather than off `version.film` — and pays it in both directions, since a version with no film has to clear the adjust pass too or it keeps textures bound that nothing will sample. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+26
-6
@@ -3148,18 +3148,14 @@ impl DevelopSession {
|
||||
/// rather than leaving the previous image's exposure standing. The file's
|
||||
/// orientation survives it, since that was never an edit.
|
||||
pub fn apply_version(&mut self, version: &dr_pipeline::Version) {
|
||||
version.apply(&mut self.graph);
|
||||
// TRACES: FR-DEV-3f
|
||||
// The film, which `apply` cleared and could not restore: a sidecar
|
||||
// names a stock, and turning a name into tables needs the profile
|
||||
// database that `dr-pipeline` deliberately does not link. So it is
|
||||
// re-baked here, after the parameters, because the bake reads the
|
||||
// film's own exposure sliders and they have just arrived.
|
||||
if let Some(film) = &version.film {
|
||||
let stock = film.stock.clone();
|
||||
let print = film.print.is_some();
|
||||
self.choose_film(Some(&stock), print);
|
||||
}
|
||||
let rebake = version.apply(&mut self.graph);
|
||||
self.pay_film_debt(&rebake);
|
||||
// The stored edit becomes the floor rather than a step. It is not
|
||||
// something the user did in this sitting, and an undo that reached
|
||||
// behind it would discard a previous session's work in one press —
|
||||
@@ -3167,6 +3163,30 @@ impl DevelopSession {
|
||||
self.history.reset(&self.graph);
|
||||
}
|
||||
|
||||
/// TRACES: FR-DEV-3f
|
||||
/// Pay what a restored edit owes the picture.
|
||||
///
|
||||
/// `dr-pipeline` restores a stock's *name* and clears its tables, because
|
||||
/// baking needs the profile database it does not link (ARCH §6.5a). This
|
||||
/// side of the seam has it, so this is where the photograph gets its film
|
||||
/// back.
|
||||
///
|
||||
/// Both outcomes go through [`Self::set_film`], and the empty one is not
|
||||
/// a no-op: `set_state` cleared the *graph*, and the adjust pass would go
|
||||
/// on holding textures that nothing will sample. That is the two halves
|
||||
/// disagreeing, which is the failure `set_film` exists to make
|
||||
/// impossible — and it is silent in this direction, which is worse.
|
||||
fn pay_film_debt(&mut self, rebake: &dr_pipeline::FilmRebake) {
|
||||
match rebake.wanted() {
|
||||
Some(film) => {
|
||||
let stock = film.stock.clone();
|
||||
let print = film.print.is_some();
|
||||
self.choose_film(Some(&stock), print);
|
||||
}
|
||||
None => self.set_film(None),
|
||||
}
|
||||
}
|
||||
|
||||
/// TRACES: FR-DEV-5
|
||||
/// Step the edit back one, returning whether anything moved.
|
||||
///
|
||||
|
||||
Reference in New Issue
Block a user