From a66361c85dc6ea118dbaa1c7edb0dd2e47d15cad Mon Sep 17 00:00:00 2001 From: bflatastic Date: Thu, 29 Oct 2026 10:21:52 +0700 Subject: [PATCH] Color: rgb_to_hsl keeps over-range saturation so 32 bpc colours round-trip Co-Authored-By: Claude Opus 5.5 --- crates/color/src/lib.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/crates/color/src/lib.rs b/crates/color/src/lib.rs index f59029e01..71eb43296 100644 --- a/crates/color/src/lib.rs +++ b/crates/color/src/lib.rs @@ -99,7 +99,7 @@ pub fn rgb_to_hsl(r: f32, g: f32, b: f32) -> (f32, f32, f32) { let d = max - min; let denom = if l > 0.5 { 2.0 - max - min } else { max + min }; // Over-range (HDR) or negative inputs can make the denominator zero or negative. - let s = if denom > 0.0 { (d / denom).min(1.0) } else { 1.0 }; + let s = if denom > 0.0 { d / denom } else { 1.0 }; let h = if max == r { (((g - b) / d + if g < b { 6.0 } else { 0.0 }) / 6.0).rem_euclid(1.0) } else if max == g { @@ -269,10 +269,13 @@ mod tests { for (r, g, b) in [(1.5, 0.5, 0.5), (2.0, 0.1, 0.3), (-0.5, 0.2, 0.9)] { let (h, s, l) = rgb_to_hsl(r, g, b); assert!(h.is_finite() && s.is_finite() && l.is_finite(), "{h} {s} {l}"); - assert!((0.0..=1.0).contains(&s), "saturation {s} for {r} {g} {b}"); let (r2, g2, b2) = hsl_to_rgb(h, s, l); assert!(r2.is_finite() && g2.is_finite() && b2.is_finite(), "{r2} {g2} {b2}"); } + // A 32 bpc colour whose denominator stays positive still round-trips exactly. + let (h, s, l) = rgb_to_hsl(1.2, 0.5, 0.5); + let (r, g, b) = hsl_to_rgb(h, s, l); + assert!((r - 1.2).abs() < 1e-5 && (g - 0.5).abs() < 1e-5 && (b - 0.5).abs() < 1e-5, "{r} {g} {b}"); } #[test]