Ask which frame this was taken on, because grain is enlargement
Build and test / Desktop (Linux) (push) Successful in 19m6s
Build and test / Layer separation (push) Successful in 28s
Traceability / Requirement traces (push) Failing after 26s
🐳 Android image / Build and push (push) Successful in 2s
Build and test / android-image (push) Successful in 3s
Build and test / Android (aarch64) (push) Failing after 33m7s
Build and test / Desktop (Linux) (push) Successful in 19m6s
Build and test / Layer separation (push) Successful in 28s
Traceability / Requirement traces (push) Failing after 26s
🐳 Android image / Build and push (push) Successful in 2s
Build and test / android-image (push) Successful in 3s
Build and test / Android (aarch64) (push) Failing after 33m7s
A crystal is a fixed size in micrometres. How grainy a photograph looks is therefore not a property of the emulsion alone -- it is film size against output size, and the frame is the half a digital file cannot supply. This assumed 35 mm for everything. The same emulsion on 4x5 averages about 3,800 crystals into the pixel that holds 300 on 35 mm, so it renders roughly 3.5 times smoother at the same print; every large-format photograph was being rendered as grainy as a half-frame. `Format` now carries the real image widths -- the gate, not the nominal inches, since a "4x5" exposes about 121 mm -- and the film node asks for it. It is a genuinely fixed list, unlike the stocks, so it is a declared `enum` parameter and gets its control, its sidecar entry and its undo step for nothing. It is also the first enum in the develop chain, and it broke two tests by being one. A row has to compare equal to itself across two builds or `sync_rows` replaces it on every parameter event -- destroying the elements built from it, including whichever TouchArea holds the current gesture, so the format picker would have fought every slider drag in the panel. `ModelRc` compares by identity and the row built a fresh choices model each call. `no_choices` already shares one empty model for exactly this reason, and the build site already said "see no_choices for why the identity matters". The fix follows it: memoise the model per variant list. Curve rows solve the same problem the other way, writing values through the existing model, which is not needed here -- a variant list is fixed at compile time, so one model can serve forever. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -37,6 +37,23 @@ pub const ID: OpId = OpId("film_sim");
|
||||
pub const EXPOSURE: ParamId = ParamId("exposure");
|
||||
pub const PRINT_EXPOSURE: ParamId = ParamId("print_exposure");
|
||||
pub const PUSH: ParamId = ParamId("push");
|
||||
pub const FORMAT: ParamId = ParamId("format");
|
||||
|
||||
/// TRACES: FR-DEV-3f
|
||||
/// The frames a photograph can be simulated on, smallest first.
|
||||
///
|
||||
/// A genuinely fixed list, unlike the stocks: nobody invents a film format, so
|
||||
/// this is a declared `enum` parameter and gets its control, its place in the
|
||||
/// sidecar and its undo step for free. The *sizes* live in `dr_film::Format`;
|
||||
/// this crate carries only the names, in the same order.
|
||||
static FORMATS: [LocalizedKey; 6] = [
|
||||
LocalizedKey("param.film_sim.format.35mm"),
|
||||
LocalizedKey("param.film_sim.format.645"),
|
||||
LocalizedKey("param.film_sim.format.6x6"),
|
||||
LocalizedKey("param.film_sim.format.6x7"),
|
||||
LocalizedKey("param.film_sim.format.4x5"),
|
||||
LocalizedKey("param.film_sim.format.8x10"),
|
||||
];
|
||||
|
||||
/// How many samples a characteristic curve carries.
|
||||
///
|
||||
@@ -70,6 +87,13 @@ static DESCRIPTOR: OpDescriptor = OpDescriptor {
|
||||
// actually published: Double-X's measured axis spans about -1 to +2,
|
||||
// and beyond a range like that a curve would have to be invented.
|
||||
ParamDescriptor::stops("push", "param.film_sim.push", -1.0, 3.0),
|
||||
// TRACES: FR-DEV-3f
|
||||
// Which frame this was taken on — the half of the enlargement a
|
||||
// photograph cannot supply. A crystal is a fixed size in micrometres,
|
||||
// so how grainy a picture looks is film size against output size, and
|
||||
// the same emulsion on 4x5 renders about three times smoother than on
|
||||
// 35mm at the same print.
|
||||
ParamDescriptor::choice("format", "param.film_sim.format", &FORMATS),
|
||||
],
|
||||
};
|
||||
|
||||
@@ -129,6 +153,8 @@ pub struct FilmSim {
|
||||
exposure: f32,
|
||||
print_exposure: f32,
|
||||
push: f32,
|
||||
/// Index into `FORMATS`. Zero is 35 mm, which is the neutral choice.
|
||||
format: f32,
|
||||
tables: Option<FilmTables>,
|
||||
}
|
||||
|
||||
@@ -173,6 +199,7 @@ impl Operation for FilmSim {
|
||||
EXPOSURE => self.exposure = value,
|
||||
PRINT_EXPOSURE => self.print_exposure = value,
|
||||
PUSH => self.push = value,
|
||||
FORMAT => self.format = value,
|
||||
_ => log::warn!("film_sim: unknown parameter {id}"),
|
||||
}
|
||||
}
|
||||
@@ -182,6 +209,7 @@ impl Operation for FilmSim {
|
||||
EXPOSURE => self.exposure,
|
||||
PRINT_EXPOSURE => self.print_exposure,
|
||||
PUSH => self.push,
|
||||
FORMAT => self.format,
|
||||
_ => 0.0,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user