Skip to content

Commit

Permalink
Rewrite hsl/hwb converters
Browse files Browse the repository at this point in the history
  • Loading branch information
GPHemsley committed Dec 30, 2022
1 parent 4e231ea commit c13859b
Showing 1 changed file with 34 additions and 50 deletions.
84 changes: 34 additions & 50 deletions src/color.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,40 @@ pub enum SrgbColor {
},
}

/// https://w3c.github.io/csswg-drafts/css-color-4/#hsl-to-rgb
#[inline]
pub fn hsl_to_rgb(hue: f32, saturation: f32, lightness: f32) -> (f32, f32, f32) {
let mut hue = hue;

if hue < 0. {
hue += 1.;
}

let f = |n: f32| -> f32 {
let k = (hue.mul_add(12., n)) % 12.;
let a = saturation * lightness.min(1. - lightness);

a.mul_add(-(-1f32).max((k - 3.).min(9. - k).min(1.)), lightness)
};

(f(0.), f(8.), f(4.))
}

/// https://w3c.github.io/csswg-drafts/css-color-4/#hwb-to-rgb
#[inline]
pub fn hwb_to_rgb(hue: f32, whiteness: f32, blackness: f32) -> (f32, f32, f32) {
if whiteness + blackness >= 1. {
let gray = whiteness / (whiteness + blackness);
return (gray, gray, gray);
}

let (red, green, blue) = hsl_to_rgb(hue, 1., 0.5);

let f = |c: f32| -> f32 { c.mul_add(1. - whiteness - blackness, whiteness) };

(f(red), f(green), f(blue))
}

impl SrgbColor {
/// Construct an sRGB color from its component channels.
pub fn new(
Expand Down Expand Up @@ -1539,56 +1573,6 @@ where
)))
}

/// https://w3c.github.io/csswg-drafts/css-color-4/#hwb-to-rgb
#[inline]
pub fn hwb_to_rgb(h: f32, w: f32, b: f32) -> (f32, f32, f32) {
if w + b >= 1.0 {
let gray = w / (w + b);
return (gray, gray, gray);
}

let (mut red, mut green, mut blue) = hsl_to_rgb(h, 1.0, 0.5);
let x = 1.0 - w - b;
red = red * x + w;
green = green * x + w;
blue = blue * x + w;
(red, green, blue)
}

/// https://w3c.github.io/csswg-drafts/css-color-4/#hsl-to-rgb
/// except with h pre-multiplied by 3, to avoid some rounding errors.
#[inline]
pub fn hsl_to_rgb(hue: f32, saturation: f32, lightness: f32) -> (f32, f32, f32) {
fn hue_to_rgb(m1: f32, m2: f32, mut h3: f32) -> f32 {
if h3 < 0. {
h3 += 3.
}
if h3 > 3. {
h3 -= 3.
}
if h3 * 2. < 1. {
m1 + (m2 - m1) * h3 * 2.
} else if h3 * 2. < 3. {
m2
} else if h3 < 2. {
m1 + (m2 - m1) * (2. - h3) * 2.
} else {
m1
}
}
let m2 = if lightness <= 0.5 {
lightness * (saturation + 1.)
} else {
lightness + saturation - lightness * saturation
};
let m1 = lightness * 2. - m2;
let hue_times_3 = hue * 3.;
let red = hue_to_rgb(m1, m2, hue_times_3 + 1.);
let green = hue_to_rgb(m1, m2, hue_times_3);
let blue = hue_to_rgb(m1, m2, hue_times_3 - 1.);
(red, green, blue)
}

#[cfg(test)]
mod tests {
use crate::ParserInput;
Expand Down

0 comments on commit c13859b

Please sign in to comment.