Give the projector lamp a name instead of a warning

Every bake of a Vision3 stock logged:

    unknown illuminant "K75P", falling back to D55

K75P is a cinema xenon short-arc lamp, and Kodak 2383 and 2393 -- the
projection print films those stocks print onto -- name it as the light their
result is looked at under. It was never implemented, so it fell through to
the unknown branch.

The fallback was the right family: a xenon arc sits near 6000 K, close to
daylight and nothing like the tungsten enlarger above it. So the pixels do not
move. What changes is that D55 is now a documented choice rather than the
consolation prize for an unrecognised string, with the approximation stated --
an arc has line structure a Planckian curve cannot express, and the residue of
that is small here because the viewing step adapts the white point out either
way.

The test is the point of the commit. A profile naming a light nobody
implemented should fail the suite, not whisper into a log that only gets read
when somebody happens to be looking for something else -- which is how this
was found.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-26 17:41:34 +02:00
co-authored by Claude Opus 5
parent 4b50648870
commit f14176de29
+27
View File
@@ -58,6 +58,18 @@ pub fn illuminant(name: &str) -> Spectrum {
// the filter's effect is a colour shift, and a colour shift ahead of
// the balance step is by construction invisible.
"TH-KG3" | "TH-KG3-L" | "T" => blackbody(3400.0),
// A cinema projector's xenon short-arc lamp, which is what a release
// print is *looked at* under — Kodak 2383 and 2393 name it.
//
// Approximated by D55, and named here rather than left to fall through
// to the unknown branch, which logged a warning on every bake of a
// Vision3 stock and implied something was broken. A xenon arc sits near
// 6000 K, close to daylight and nothing like the tungsten above, so
// daylight is the right family to stand in for it. It is not a
// blackbody — the arc has line structure a Planckian curve has no way
// to express — and the residue of that is small here because the
// viewing step adapts the white point out either way.
"K75P" => ILLUMINANT_D55,
other => {
if let Some(kelvin) = other.strip_prefix("BB").and_then(|k| k.parse::<f32>().ok()) {
return blackbody(kelvin);
@@ -216,6 +228,21 @@ mod tests {
}
}
#[test]
fn every_illuminant_a_shipped_profile_names_is_known() {
// The warning that found this said "unknown illuminant K75P, falling
// back to D55" on every bake of a Vision3 stock. The fallback was
// reasonable and the silence was not: a profile naming a light nobody
// implemented should be a failing test rather than a line in a log.
for name in ["D50", "D55", "D65", "TH-KG3", "TH-KG3-L", "T", "K75P"] {
let rgb = Viewing::new(name).to_srgb(&[1.0; SPECTRUM]);
assert!(
rgb.iter().all(|c| (c - 1.0).abs() < 0.01),
"{name} is not a working illuminant: clear frame gave {rgb:?}"
);
}
}
#[test]
fn a_hotter_blackbody_is_bluer() {
// Cheap, but it is the one property that catches Planck's law written