Look the lens up and say plainly whether one was found
`dr-lens` has held a complete Lensfun lookup — distortion, TCA and vignetting coefficients from a lens name, a focal length and an aperture — with no dependents anywhere in the workspace. The three corrections it feeds now exist in the graph, so this connects the two and finishes the chain. The coefficient structs stay duplicated. `dr-pipeline` is organised around having no dependencies so its codegen is testable without a device or a database (ARCH §6.5a), and `dr-lens` carries an XML parser and 5.5 MB of profile data. Neither crate can convert to the other, so the conversion goes above both, in `develop.rs`, which is the only place that sees them together. Both traits grow the same defaulted door. The optical corrections do not sit on the same side of the fetch — distortion and CA rewrite coordinates and are `Warp`s, vignetting applies a gain to the pixel already there and is an ordinary node — and fanning a profile out by which trait each happens to implement would make the caller reason about that distinction. Each correction takes its own share of the whole profile instead, and `set_lens_profile` walks both lists identically. The lookup happens in `set_source_metadata` rather than in its caller, because that is the one place a session is told which file it came from. Doing it there makes it unforgettable, in the shape `FilmRebake` already uses for the other derived thing — and, more to the point, makes *clearing* unforgettable: a session that opened a second photograph while still holding the first one's profile would correct it for the wrong optics, invisibly, in a way that looks exactly like the lens. It needs the whole shot and not just a name. Distortion is interpolated across a zoom's focal range and vignetting depends strongly on aperture — a fast prime can be two stops down in the corners wide open and clean by f/8 — so a lookup missing either returns coefficients measured for a shot nobody took. Missing any of the three refuses rather than guesses. A profile is derived, not persisted: it comes from the file's EXIF and a database, so it is not a parameter, not in the sidecar and not undoable. What is an edit is the manual trim beside it, which each correction composes with the measurement — so a photographer can lean on it, override it, or work without one. `InfoPanel` gains a lens line, and it distinguishes three cases rather than two. `dr-lens` states the rule it exists for: an automatic correction that silently did nothing is worse than one the user can see is unavailable. A session with no header draws nothing, a header naming no lens reads "Lens not recorded", and a lens the database has never heard of reads "· no profile". Collapsing the last two would send somebody hunting for a profile that was never missing — which, for third-party and adapted glass, is the ordinary case. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -64,6 +64,32 @@ use std::sync::Arc;
|
||||
use crate::descriptor::{OpDescriptor, ParamId};
|
||||
use crate::operation::{Helper, Uniform};
|
||||
|
||||
/// Lateral chromatic aberration, as a per-channel radial scale.
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct Tca {
|
||||
pub red_scale: f32,
|
||||
pub blue_scale: f32,
|
||||
}
|
||||
|
||||
/// What a lens profile says about one shot, in the pipeline's own types.
|
||||
///
|
||||
/// **Deliberately a mirror of `dr_lens::LensProfile` rather than that type
|
||||
/// itself.** The dependency would have to run the wrong way: `dr-lens` carries
|
||||
/// an XML parser and 5.5 MB of Lensfun data, and this crate is organised around
|
||||
/// having no dependencies so that its codegen is testable without a GPU or a
|
||||
/// database (ARCH §6.5a). Whatever sits above both does the conversion; it is
|
||||
/// nine fields and a `match`.
|
||||
///
|
||||
/// Every field is independently optional because the database is: it commonly
|
||||
/// carries distortion for a lens and no vignetting, or covers only part of a
|
||||
/// zoom. A partial profile is useful and must not be discarded wholesale.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Default)]
|
||||
pub struct LensProfile {
|
||||
pub distortion: Option<crate::ops::distortion::PtLens>,
|
||||
pub tca: Option<Tca>,
|
||||
pub vignetting: Option<crate::ops::vignetting::Pa>,
|
||||
}
|
||||
|
||||
/// A coordinate-domain operation, applied before the source is sampled.
|
||||
///
|
||||
/// Object-safe for the same reason [`crate::operation::Operation`] is: the
|
||||
@@ -118,6 +144,20 @@ pub trait Warp: Send + Sync {
|
||||
fn helpers(&self) -> &'static [Helper] {
|
||||
&[]
|
||||
}
|
||||
|
||||
/// Take whatever this warp needs from a lens profile.
|
||||
///
|
||||
/// Handed the *whole* profile rather than its own slice of it, so that the
|
||||
/// graph fanning one out does not have to know which correction wants
|
||||
/// which coefficients — the same reason an operation is handed a
|
||||
/// [`ParamId`] rather than a field. `None` clears any profile in place,
|
||||
/// which is what opening a photograph from an unrecognised lens must do:
|
||||
/// leaving the previous one standing would correct this frame for the
|
||||
/// optics of the last one.
|
||||
///
|
||||
/// Defaulted, because a warp need not be profile-driven. Nothing about the
|
||||
/// coordinate stage requires a database behind it.
|
||||
fn set_profile(&mut self, _profile: Option<&LensProfile>) {}
|
||||
}
|
||||
|
||||
/// The composed geometry stage: WGSL, uniforms, and what it needs from the
|
||||
|
||||
Reference in New Issue
Block a user