Skip to content

Commit 62d63fe

Browse files
devongovettemilio
authored andcommitted
Implement support for hwb() color syntax
1 parent 64eb9e3 commit 62d63fe

File tree

4 files changed

+7884
-4
lines changed

4 files changed

+7884
-4
lines changed

src/color.rs

Lines changed: 64 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,7 @@ where
565565
let (red, green, blue, uses_commas) = match_ignore_ascii_case! { name,
566566
"rgb" | "rgba" => parse_rgb_components_rgb(component_parser, arguments)?,
567567
"hsl" | "hsla" => parse_rgb_components_hsl(component_parser, arguments)?,
568+
"hwb" => parse_rgb_components_hwb(component_parser, arguments)?,
568569
_ => return Err(arguments.new_unexpected_token_error(Token::Ident(name.to_owned().into()))),
569570
};
570571

@@ -653,6 +654,65 @@ where
653654
let lightness = component_parser.parse_percentage(arguments)?;
654655
let lightness = lightness.max(0.).min(1.);
655656

657+
let (red, green, blue) = hsl_to_rgb(hue, saturation, lightness);
658+
let red = clamp_unit_f32(red);
659+
let green = clamp_unit_f32(green);
660+
let blue = clamp_unit_f32(blue);
661+
Ok((red, green, blue, uses_commas))
662+
}
663+
664+
#[inline]
665+
fn parse_rgb_components_hwb<'i, 't, ComponentParser>(
666+
component_parser: &ComponentParser,
667+
arguments: &mut Parser<'i, 't>,
668+
) -> Result<(u8, u8, u8, bool), ParseError<'i, ComponentParser::Error>>
669+
where
670+
ComponentParser: ColorComponentParser<'i>,
671+
{
672+
let hue_degrees = component_parser.parse_angle_or_number(arguments)?.degrees();
673+
674+
// Subtract an integer before rounding, to avoid some rounding errors:
675+
let hue_normalized_degrees = hue_degrees - 360. * (hue_degrees / 360.).floor();
676+
let hue = hue_normalized_degrees / 360.;
677+
678+
let uses_commas = arguments.try_parse(|i| i.expect_comma()).is_ok();
679+
680+
let whiteness = component_parser.parse_percentage(arguments)?;
681+
let whiteness = whiteness.max(0.).min(1.);
682+
683+
if uses_commas {
684+
arguments.expect_comma()?;
685+
}
686+
687+
let blackness = component_parser.parse_percentage(arguments)?;
688+
let blackness = blackness.max(0.).min(1.);
689+
690+
let (red, green, blue) = hwb_to_rgb(hue, whiteness, blackness);
691+
692+
let red = clamp_unit_f32(red);
693+
let green = clamp_unit_f32(green);
694+
let blue = clamp_unit_f32(blue);
695+
Ok((red, green, blue, uses_commas))
696+
}
697+
698+
/// https://drafts.csswg.org/css-color-4/#hwb-to-rgb
699+
#[inline]
700+
fn hwb_to_rgb(h: f32, w: f32, b: f32) -> (f32, f32, f32) {
701+
if w + b >= 1.0 {
702+
let gray = w / (w + b);
703+
return (gray, gray, gray);
704+
}
705+
706+
let (mut red, mut green, mut blue) = hsl_to_rgb(h, 1.0, 0.5);
707+
let x = 1.0 - w - b;
708+
red = red * x + w;
709+
green = green * x + w;
710+
blue = blue * x + w;
711+
(red, green, blue)
712+
}
713+
714+
#[inline]
715+
fn hsl_to_rgb(hue: f32, saturation: f32, lightness: f32) -> (f32, f32, f32) {
656716
// https://drafts.csswg.org/css-color/#hsl-color
657717
// except with h pre-multiplied by 3, to avoid some rounding errors.
658718
fn hue_to_rgb(m1: f32, m2: f32, mut h3: f32) -> f32 {
@@ -680,8 +740,8 @@ where
680740
};
681741
let m1 = lightness * 2. - m2;
682742
let hue_times_3 = hue * 3.;
683-
let red = clamp_unit_f32(hue_to_rgb(m1, m2, hue_times_3 + 1.));
684-
let green = clamp_unit_f32(hue_to_rgb(m1, m2, hue_times_3));
685-
let blue = clamp_unit_f32(hue_to_rgb(m1, m2, hue_times_3 - 1.));
686-
return Ok((red, green, blue, uses_commas));
743+
let red = hue_to_rgb(m1, m2, hue_times_3 + 1.);
744+
let green = hue_to_rgb(m1, m2, hue_times_3);
745+
let blue = hue_to_rgb(m1, m2, hue_times_3 - 1.);
746+
(red, green, blue)
687747
}

0 commit comments

Comments
 (0)