Emit floats an f32 can hold, and drop the format! that formats nothing

CI runs cargo fmt --check and clippy -D warnings, and this branch had
never been through either. Both would have failed it.

The bulk was the generated colour tables: eight significant figures where
an f32 carries about 7.2, so the eighth is noise that rounds away at
compile time and clippy's excessive_precision says so 109 times over.
Fixed in the generator rather than only in the file, so it stays fixed --
and the file is trimmed in place rather than re-derived, because
regenerating it needs a colour-science stack that has nothing to do with
the defect.

The format! in the composer is mine too, from extracting the rendering
tail: the braces in it were escaped because the text used to live inside a
larger template, and once extracted the escapes are noise and the call
formats nothing.

Also here, and clearly not mine: an unused import and a shadowed binding
in dr-gpu, and an unused import in a test. They are pre-existing --
clippy has been failing on master before this branch existed, on lints
like is_multiple_of that arrived with a toolchain rather than with
anyone's code. Fixed because CI cannot go green around them, and called
out because a merge commit is a bad place to quietly edit someone else's
crate.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-25 22:28:14 +02:00
co-authored by Claude Opus 5
parent 6925aa2a86
commit 3b5952769b
18 changed files with 490 additions and 315 deletions
+50 -13
View File
@@ -65,7 +65,12 @@ impl<'a> Recipe<'a> {
/// The straightforward reading of a stock: reversal viewed directly,
/// negative printed on the paper its datasheet names.
pub fn new(film: &'a Profile, print: Option<&'a Profile>) -> Self {
Self { film, print, exposure_ev: 0.0, print_exposure_ev: 0.0 }
Self {
film,
print,
exposure_ev: 0.0,
print_exposure_ev: 0.0,
}
}
}
@@ -140,8 +145,7 @@ impl Baked {
let w = if dx == 0 { 1.0 - frac[0] } else { frac[0] }
* if dy == 0 { 1.0 - frac[1] } else { frac[1] }
* if dz == 0 { 1.0 - frac[2] } else { frac[2] };
let e = self.lut
[((base[2] + dz) * n + base[1] + dy) * n + base[0] + dx];
let e = self.lut[((base[2] + dz) * n + base[1] + dy) * n + base[0] + dx];
for c in 0..3 {
out[c] += w * e[c];
}
@@ -192,7 +196,12 @@ pub fn exposure_matrix(film: &Profile) -> [[f32; 3]; 3] {
/// goes: the mask is a fixed density, so balancing mid-grey to neutral cancels
/// it — which is why a printed negative looks like a photograph while a scanned
/// one looks orange.
fn print_balance(film: &Profile, paper: &Profile, exposure_ev: f32, print_exposure_ev: f32) -> [f32; 3] {
fn print_balance(
film: &Profile,
paper: &Profile,
exposure_ev: f32,
print_exposure_ev: f32,
) -> [f32; 3] {
let matrix = exposure_matrix(film);
let scene = MID_GREY * 2f32.powf(exposure_ev);
let mut log_exposure = [0.0f32; 3];
@@ -320,7 +329,11 @@ fn invert_mean_curve(paper: &Profile, density: f32) -> f32 {
let ascending = at(last) >= at(0);
for i in 0..last {
let (lo, hi) = (at(i), at(i + 1));
let brackets = if ascending { lo <= density && density <= hi } else { hi <= density && density <= lo };
let brackets = if ascending {
lo <= density && density <= hi
} else {
hi <= density && density <= lo
};
if brackets && (hi - lo).abs() > f32::EPSILON {
let f = (density - lo) / (hi - lo);
return log_at(i) + (log_at(i + 1) - log_at(i)) * f;
@@ -329,7 +342,11 @@ fn invert_mean_curve(paper: &Profile, density: f32) -> f32 {
// Off the end of the curve: the nearest end is the honest answer, and it
// keeps a badly-scaled contributed profile from producing a NaN that would
// propagate silently through the whole LUT.
if (density <= at(0)) == ascending { paper.log_exposure_min } else { paper.log_exposure_max }
if (density <= at(0)) == ascending {
paper.log_exposure_min
} else {
paper.log_exposure_max
}
}
#[cfg(test)]
@@ -386,7 +403,10 @@ mod tests {
let shadow = baked.apply([0.02; 3]);
let mid = baked.apply([MID_GREY; 3]);
let highlight = baked.apply([0.8; 3]);
assert!(shadow[1] < mid[1] && mid[1] < highlight[1], "{shadow:?} {mid:?} {highlight:?}");
assert!(
shadow[1] < mid[1] && mid[1] < highlight[1],
"{shadow:?} {mid:?} {highlight:?}"
);
}
#[test]
@@ -397,7 +417,10 @@ mod tests {
let baked = bake(&Recipe::new(&film, None));
let shadow = baked.apply([0.02; 3]);
let highlight = baked.apply([0.8; 3]);
assert!(shadow[1] > highlight[1], "not inverted: {shadow:?} -> {highlight:?}");
assert!(
shadow[1] > highlight[1],
"not inverted: {shadow:?} -> {highlight:?}"
);
let mid = baked.apply([MID_GREY; 3]);
assert!(mid[0] > mid[2] * 4.0, "no orange mask: {mid:?}");
}
@@ -418,7 +441,10 @@ mod tests {
"print is not a positive: {shadow:?} {mid:?} {highlight:?}"
);
for grey in [shadow, mid, highlight] {
assert!(spread(grey) < 0.06, "print of a neutral is not neutral: {grey:?}");
assert!(
spread(grey) < 0.06,
"print of a neutral is not neutral: {grey:?}"
);
}
}
@@ -426,7 +452,10 @@ mod tests {
fn exposure_moves_the_print_the_way_it_moves_a_photograph() {
let film = portra();
let paper = endura();
let brighter = bake(&Recipe { exposure_ev: 1.0, ..Recipe::new(&film, Some(&paper)) });
let brighter = bake(&Recipe {
exposure_ev: 1.0,
..Recipe::new(&film, Some(&paper))
});
let base = bake(&Recipe::new(&film, Some(&paper)));
assert!(brighter.apply([MID_GREY; 3])[1] > base.apply([MID_GREY; 3])[1]);
}
@@ -443,11 +472,16 @@ mod tests {
let mut worst = 0.0f32;
for i in 0..40 {
for j in 0..40 {
let rgb = [i as f32 / 39.0, j as f32 / 39.0, ((i + j) % 40) as f32 / 39.0];
let rgb = [
i as f32 / 39.0,
j as f32 / 39.0,
((i + j) % 40) as f32 / 39.0,
];
let mut log_exposure = [0.0f32; 3];
for (l, slot) in log_exposure.iter_mut().enumerate() {
let m = baked.exposure_matrix[l];
*slot = ((m[0] * rgb[0] + m[1] * rgb[1] + m[2] * rgb[2]).max(0.0) + 1e-10).log10();
*slot =
((m[0] * rgb[0] + m[1] * rgb[1] + m[2] * rgb[2]).max(0.0) + 1e-10).log10();
}
let exact = viewing.to_srgb(&film.transmittance(film.density_at(log_exposure)));
let approx = baked.apply(rgb);
@@ -456,7 +490,10 @@ mod tests {
}
}
}
assert!(worst < 1.0 / 255.0, "worst LUT error {worst} exceeds one code value");
assert!(
worst < 1.0 / 255.0,
"worst LUT error {worst} exceeds one code value"
);
}
#[test]