mirror of
https://github.com/storytold/lightcraft.git
synced 2026-11-01 05:57:09 -06:00
LR-EDIT-COLOR-TEMP: Kelvin white balance for raws whose camera has colour matrices (#730)
Raws without matrices in the file (ARW, NEF, RW2, RAF, CR2, CR3, PEF, SRW,
ORF) were all edited on the relative white-balance scale (6500 K / 0 = as
shot), even when lightcraft_raw::spectral has measured matrices for the
camera. Those 52 bodies (ILCE-7M3, ILCE-7M4, D850, EOS 5D Mark III...) now
edit in Kelvin like a DNG and like Lightroom.
Design:
- One decision on both sides: spectral::find_camera("Make Model") backs
catalog::relative_wb_camera (Photo::relative_wb); files::WbScaleOf decides
the same from the decoded file. A corpus test asserts they agree.
Photo::fitted_look keeps the old predicate for smart previews.
- The matrices only convert: As Shot is the file's multipliers read through
the model's ColorMatrix1/2 (spectral::as_shot_white_through). The look
(JPEG fit / profile / spectral fallback) is unchanged, and the slider
adapts from that white with Bradford. The camera-space rebalance stays
reserved for files with their own matrices.
- Presets resolve to their Kelvin values (WbMode::preset, sources cited).
- The Color panel shows the white balance in effect (effective_wb).
- XMP: crs::Target::for_file takes the camera; matrix cameras read
crs:Temperature as Kelvin (marked "scale": "kelvin"), others keep the
#510 as-shot shift.
Migration (no look changes):
- WhiteBalance.scale: WbScale { Legacy (absent in JSON), Kelvin }. A
white balance without a scale keeps its old meaning. On a spectral-Kelvin
raw (SourceInfo.legacy_relative_wb) the pipeline composes the old
adaptation (6500 -> value) with the new one (as shot -> ...) in Bradford
cone space (local::legacy_white). Von Kries scalings compose exactly, so
the matrix matches to f32 and the pixels are identical
(pipeline legacy_relative_white_balance_renders_as_before_on_a_kelvin_raw,
media old_relative_white_balance_on_a_kelvin_camera_keeps_its_look...).
- Every white-balance writer (develop.set/adjust/quickAdjust/wb/wbPick/
reset) first runs local::normalize_wb: a legacy value becomes the Custom
Kelvin value with the same look and is marked Kelvin. Partials without a
scale (old presets, develop.merge) stay legacy.
- Catalog format 7: Op::SetAsShotWb replaces the 6500/0 as-shot recorded
by older imports when the file is next decoded or reloaded (system op,
not an undo step). Format-version test updated.
- The web wire carries legacy_relative_wb.
Docs: parity (LR-EDIT-COLOR-TEMP), camera-preview-colour, xmp-interop,
lightroom-catalog-import, control-protocol, mcp, whats-new.
This commit is contained in:
@@ -168,9 +168,12 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
}
|
||||
let label = if vals.len() == 1 { controls::find(&vals[0].0).map(|c| c.label).unwrap_or("Edit").to_string() } else { "Edit".into() };
|
||||
let apply = |d: &mut DevelopSettings, info: &lightcraft_pipeline::SourceInfo| {
|
||||
if d.wb.mode == WbMode::AsShot && vals.iter().any(|(k, _)| k == "wb.temp" || k == "wb.tint") {
|
||||
d.wb.temp = info.as_shot_temp;
|
||||
d.wb.tint = info.as_shot_tint;
|
||||
if vals.iter().any(|(k, _)| k == "wb.temp" || k == "wb.tint") {
|
||||
lightcraft_pipeline::local::normalize_wb(d, info);
|
||||
if d.wb.mode == WbMode::AsShot {
|
||||
d.wb.temp = info.as_shot_temp;
|
||||
d.wb.tint = info.as_shot_tint;
|
||||
}
|
||||
}
|
||||
for (k, v) in &vals {
|
||||
controls::set(d, k, *v);
|
||||
@@ -209,6 +212,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
let info = s.source_info(active(s, "develop.adjust")?);
|
||||
edit(s, "develop.adjust", spec.label, |d| {
|
||||
if c == "wb.temp" || c == "wb.tint" {
|
||||
lightcraft_pipeline::local::normalize_wb(d, &info);
|
||||
if d.wb.mode == WbMode::AsShot {
|
||||
d.wb.temp = info.as_shot_temp;
|
||||
d.wb.tint = info.as_shot_tint;
|
||||
@@ -240,10 +244,17 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
Some(l) => (*l).clone(),
|
||||
None => {
|
||||
let info = s.source_info(id);
|
||||
DevelopSettings {
|
||||
wb: lightcraft_develop::WhiteBalance { mode: WbMode::AsShot, temp: info.as_shot_temp, tint: info.as_shot_tint },
|
||||
let mut fresh = DevelopSettings {
|
||||
wb: lightcraft_develop::WhiteBalance {
|
||||
mode: WbMode::AsShot,
|
||||
temp: info.as_shot_temp,
|
||||
tint: info.as_shot_tint,
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
}
|
||||
};
|
||||
lightcraft_pipeline::local::normalize_wb(&mut fresh, &info);
|
||||
fresh
|
||||
}
|
||||
};
|
||||
// a fresh start renders with the current process (a section or slider reset doesn't change it)
|
||||
@@ -377,7 +388,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
m => m
|
||||
.preset()
|
||||
.map(|(t, ti)| {
|
||||
// presets are relative to daylight for rendered files
|
||||
// presets are Kelvin on raws with that scale, relative to daylight (6500 K = as shot) elsewhere
|
||||
if info.raw && !info.relative_wb { (t, ti) } else { (6500.0 * t / 5500.0, ti) }
|
||||
})
|
||||
.unwrap_or((info.as_shot_temp, info.as_shot_tint)),
|
||||
@@ -387,6 +398,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
// The resolved temperature/tint is stored, so rendering never depends on the mode; the
|
||||
// mode is kept for the UI's WB dropdown.
|
||||
edit(s, "develop.wb", "White Balance", |d| {
|
||||
lightcraft_pipeline::local::normalize_wb(d, &info);
|
||||
d.wb.mode = mode;
|
||||
d.wb.temp = t.clamp(2000.0, 50000.0);
|
||||
d.wb.tint = tint.clamp(-150.0, 150.0);
|
||||
@@ -416,6 +428,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
let patch = lightcraft_raster::Rgb32f::filled(4, 4, acc);
|
||||
let (t, tint) = lightcraft_pipeline::auto::auto_wb(&patch, &info);
|
||||
edit(s, "develop.wbPick", "White Balance", |d| {
|
||||
lightcraft_pipeline::local::normalize_wb(d, &info);
|
||||
d.wb.mode = WbMode::Custom;
|
||||
d.wb.temp = t;
|
||||
d.wb.tint = tint;
|
||||
@@ -847,10 +860,14 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
.filter_map(|id| s.develop_of(id).map(|d| (id, d)))
|
||||
.filter_map(|(id, d)| {
|
||||
let mut nd = (*d).clone();
|
||||
if nd.wb.mode == WbMode::AsShot && (ctl == "wb.temp" || ctl == "wb.tint") {
|
||||
if ctl == "wb.temp" || ctl == "wb.tint" {
|
||||
// each photo on its own scale: Kelvin (an old relative value converted first) or relative
|
||||
let info = s.source_info(id);
|
||||
nd.wb.temp = info.as_shot_temp;
|
||||
nd.wb.tint = info.as_shot_tint;
|
||||
lightcraft_pipeline::local::normalize_wb(&mut nd, &info);
|
||||
if nd.wb.mode == WbMode::AsShot {
|
||||
nd.wb.temp = info.as_shot_temp;
|
||||
nd.wb.tint = info.as_shot_tint;
|
||||
}
|
||||
}
|
||||
let v = controls::get(&nd, &ctl).unwrap_or(spec.default);
|
||||
controls::set(&mut nd, &ctl, v + delta);
|
||||
|
||||
Reference in New Issue
Block a user