From 31aa638383fa489e0f3df3fe51e362cf74ca27da Mon Sep 17 00:00:00 2001 From: Exalted5373 <247056922+Exalted5373@users.noreply.github.com> Date: Thu, 8 Oct 2026 15:53:36 -0700 Subject: [PATCH] LR-IMP-PROFILES: red / green / blue curves only with the master curve; neutral test values Lightroom Classic reads `ToneCurvePV2012Red/Green/Blue` only together with `ToneCurvePV2012`: a packet with a channel curve and no master curve leaves the photo unchanged there (rendered on a chart: the channel curve alone has no effect, with an identity master curve it applies). Imports now do the same. The preset-packet test helper uses made-up as-shot values. Co-Authored-By: Claude Opus 5.5 --- crates/engine/src/crs.rs | 33 +++++++++++++++++++++++++++------ docs/whats-new.md | 1 + docs/xmp-interop.md | 2 +- 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/crates/engine/src/crs.rs b/crates/engine/src/crs.rs index 0d9b7a771..0b0c83b94 100644 --- a/crates/engine/src/crs.rs +++ b/crates/engine/src/crs.rs @@ -244,11 +244,14 @@ pub fn to_partial(props: &Props, raw: Option) -> Value { put(o, "curve.master", c); } } - for (crs, ch) in - [("ToneCurvePV2012", "master"), ("ToneCurvePV2012Red", "red"), ("ToneCurvePV2012Green", "green"), ("ToneCurvePV2012Blue", "blue")] - { - if let Some(c) = curve(props, &format!("crs:{crs}")) { - put(o, &format!("curve.{ch}"), c); + // Lightroom Classic reads the red / green / blue curves only together with the master 2012 + // curve: a channel curve without `ToneCurvePV2012` leaves the photo unchanged + if let Some(c) = curve(props, "crs:ToneCurvePV2012") { + put(o, "curve.master", c); + for (crs, ch) in [("ToneCurvePV2012Red", "red"), ("ToneCurvePV2012Green", "green"), ("ToneCurvePV2012Blue", "blue")] { + if let Some(c) = curve(props, &format!("crs:{crs}")) { + put(o, &format!("curve.{ch}"), c); + } } } @@ -572,7 +575,7 @@ mod tests { + crs:CropConstrainToWarp="0" crs:AsShotTemperature="5500" crs:AsShotTint="10" {extra_attrs}> {extra_elems} @@ -580,6 +583,24 @@ mod tests { ) } + #[test] + fn channel_curves_need_the_master_curve() { + // Lightroom ignores a red / green / blue curve without `ToneCurvePV2012` + let packet = |master: &str| { + format!( + r#" + {master} + 0, 064, 40255, 255 + "# + ) + }; + let alone = to_partial(&props(&packet("")), Some(true)); + assert!(alone.pointer("/curve/green").is_none(), "{alone}"); + let master = "0, 0255, 255"; + let with = to_partial(&props(&packet(master)), Some(true)); + assert!(with.pointer("/curve/green").is_some() && with.pointer("/curve/master").is_some(), "{with}"); + } + fn unmapped(x: &str) -> Vec { to_partial_report(&props(x), None, None, 1.5).1 } diff --git a/docs/whats-new.md b/docs/whats-new.md index f128c9dbb..9e884c14c 100644 --- a/docs/whats-new.md +++ b/docs/whats-new.md @@ -49,6 +49,7 @@ white, empty Point Color slots…) as settings that couldn't be carried over. - A preset whose lens-profile switch is off no longer turns off the lens corrections built into a DNG (iPhone ProRAW and other files with embedded distortion / vignetting corrections), matching what the preset does elsewhere. +- A red / green / blue curve in an XMP preset without the master curve is ignored, as Lightroom ignores it. - Imported `.cube` LUT profiles appear in the Profile menu and the profile browser, grouped by their folder, and stay favourites across restarts (issue #328). diff --git a/docs/xmp-interop.md b/docs/xmp-interop.md index 85b41a958..735432312 100644 --- a/docs/xmp-interop.md +++ b/docs/xmp-interop.md @@ -117,7 +117,7 @@ names are read (e.g. `Exposure2012`, not the older `Exposure`). | `ParametricShadows`, `ParametricDarks`, `ParametricLights`, `ParametricHighlights` | `curve.shadows/darks/lights/highlights` | | | `ParametricShadowSplit`, `ParametricMidtoneSplit`, `ParametricHighlightSplit` | `curve.split_shadows/split_mid/split_highlights` | | | `CurveRefineSaturation` | `curve.refine_saturation` | 0..100 (100 = curve saturation unchanged) | -| `ToneCurvePV2012`, `ToneCurvePV2012Red/Green/Blue` | `curve.master/red/green/blue` | `"x, y"` points in 0..255 → 0..1; a straight 0→255 line = no curve | +| `ToneCurvePV2012`, `ToneCurvePV2012Red/Green/Blue` | `curve.master/red/green/blue` | `"x, y"` points in 0..255 → 0..1; a straight 0→255 line = no curve; the red / green / blue curves are read only with `ToneCurvePV2012` (Lightroom Classic ignores them without it) | | `SplitToningShadowHue/Saturation`, `SplitToningHighlightHue/Saturation` | `grading.shadows/highlights.hue/sat` | | | `ColorGradeShadowLum`, `ColorGradeHighlightLum` | `grading.shadows/highlights.lum` | | | `ColorGradeMidtoneHue/Sat/Lum`, `ColorGradeGlobalHue/Sat/Lum` | `grading.midtones/global.*` | |