Skip to content

Commit 7c58008

Browse files
authored
Add parsing of color() (#317)
1 parent 722b30d commit 7c58008

File tree

5 files changed

+432
-3
lines changed

5 files changed

+432
-3
lines changed

src/color.rs

+166-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use std::f32::consts::PI;
66
use std::fmt;
7+
use std::str::FromStr;
78

89
use super::{BasicParseError, ParseError, Parser, ToCss, Token};
910

@@ -350,6 +351,122 @@ macro_rules! impl_lch_like {
350351
impl_lch_like!(Lch, "lch");
351352
impl_lch_like!(Oklch, "oklch");
352353

354+
/// A Predefined color space specified in:
355+
/// https://w3c.github.io/csswg-drafts/css-color-4/#predefined
356+
#[derive(Clone, Copy, PartialEq, Debug)]
357+
pub enum PredefinedColorSpace {
358+
/// https://w3c.github.io/csswg-drafts/css-color-4/#predefined-sRGB
359+
Srgb,
360+
/// https://w3c.github.io/csswg-drafts/css-color-4/#predefined-sRGB-linear
361+
SrgbLinear,
362+
/// https://w3c.github.io/csswg-drafts/css-color-4/#predefined-display-p3
363+
DisplayP3,
364+
/// https://w3c.github.io/csswg-drafts/css-color-4/#predefined-a98-rgb
365+
A98Rgb,
366+
/// https://w3c.github.io/csswg-drafts/css-color-4/#predefined-prophoto-rgb
367+
ProphotoRgb,
368+
/// https://w3c.github.io/csswg-drafts/css-color-4/#predefined-rec2020
369+
Rec2020,
370+
/// https://w3c.github.io/csswg-drafts/css-color-4/#predefined-xyz
371+
XyzD50,
372+
/// https://w3c.github.io/csswg-drafts/css-color-4/#predefined-xyz
373+
XyzD65,
374+
}
375+
376+
impl PredefinedColorSpace {
377+
/// Returns the string value of the predefined color space.
378+
pub fn as_str(&self) -> &str {
379+
match self {
380+
PredefinedColorSpace::Srgb => "srgb",
381+
PredefinedColorSpace::SrgbLinear => "srgb-linear",
382+
PredefinedColorSpace::DisplayP3 => "display-p3",
383+
PredefinedColorSpace::A98Rgb => "a98-rgb",
384+
PredefinedColorSpace::ProphotoRgb => "prophoto-rgb",
385+
PredefinedColorSpace::Rec2020 => "rec2020",
386+
PredefinedColorSpace::XyzD50 => "xyz-d50",
387+
PredefinedColorSpace::XyzD65 => "xyz-d65",
388+
}
389+
}
390+
}
391+
392+
impl FromStr for PredefinedColorSpace {
393+
type Err = ();
394+
395+
fn from_str(s: &str) -> Result<Self, Self::Err> {
396+
Ok(match_ignore_ascii_case! { s,
397+
"srgb" => PredefinedColorSpace::Srgb,
398+
"srgb-linear" => PredefinedColorSpace::SrgbLinear,
399+
"display-p3" => PredefinedColorSpace::DisplayP3,
400+
"a98-rgb" => PredefinedColorSpace::A98Rgb,
401+
"prophoto-rgb" => PredefinedColorSpace::ProphotoRgb,
402+
"rec2020" => PredefinedColorSpace::Rec2020,
403+
"xyz-d50" => PredefinedColorSpace::XyzD50,
404+
"xyz" | "xyz-d65" => PredefinedColorSpace::XyzD65,
405+
406+
_ => return Err(()),
407+
})
408+
}
409+
}
410+
411+
impl ToCss for PredefinedColorSpace {
412+
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
413+
where
414+
W: fmt::Write,
415+
{
416+
dest.write_str(self.as_str())
417+
}
418+
}
419+
420+
/// A color specified by the color() function.
421+
/// https://w3c.github.io/csswg-drafts/css-color-4/#color-function
422+
#[derive(Clone, Copy, PartialEq, Debug)]
423+
pub struct ColorFunction {
424+
/// The color space for this color.
425+
pub color_space: PredefinedColorSpace,
426+
/// The first component of the color. Either red or x.
427+
pub c1: f32,
428+
/// The second component of the color. Either green or y.
429+
pub c2: f32,
430+
/// The third component of the color. Either blue or z.
431+
pub c3: f32,
432+
/// The alpha component of the color.
433+
pub alpha: f32,
434+
}
435+
436+
impl ColorFunction {
437+
/// Construct a new color function definition with the given color space and
438+
/// color components.
439+
pub fn new(color_space: PredefinedColorSpace, c1: f32, c2: f32, c3: f32, alpha: f32) -> Self {
440+
Self {
441+
color_space,
442+
c1,
443+
c2,
444+
c3,
445+
alpha,
446+
}
447+
}
448+
}
449+
450+
impl ToCss for ColorFunction {
451+
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
452+
where
453+
W: fmt::Write,
454+
{
455+
dest.write_str("color(")?;
456+
self.color_space.to_css(dest)?;
457+
dest.write_char(' ')?;
458+
self.c1.to_css(dest)?;
459+
dest.write_char(' ')?;
460+
self.c2.to_css(dest)?;
461+
dest.write_char(' ')?;
462+
self.c3.to_css(dest)?;
463+
464+
serialize_alpha(dest, self.alpha, false)?;
465+
466+
dest.write_char(')')
467+
}
468+
}
469+
353470
/// An absolutely specified color.
354471
/// https://w3c.github.io/csswg-drafts/css-color-4/#typedef-absolute-color-base
355472
#[derive(Clone, Copy, PartialEq, Debug)]
@@ -370,6 +487,8 @@ pub enum AbsoluteColor {
370487
/// Specifies an Oklab color by Oklab Lightness, Chroma, and hue using
371488
/// the OKLCH cylindrical coordinate model.
372489
Oklch(Oklch),
490+
/// Specifies a color in a predefined color space.
491+
ColorFunction(ColorFunction),
373492
}
374493

375494
impl AbsoluteColor {
@@ -381,6 +500,7 @@ impl AbsoluteColor {
381500
Self::Lch(c) => c.alpha,
382501
Self::Oklab(c) => c.alpha,
383502
Self::Oklch(c) => c.alpha,
503+
Self::ColorFunction(c) => c.alpha,
384504
}
385505
}
386506
}
@@ -396,6 +516,7 @@ impl ToCss for AbsoluteColor {
396516
Self::Lch(lch) => lch.to_css(dest),
397517
Self::Oklab(lab) => lab.to_css(dest),
398518
Self::Oklch(lch) => lch.to_css(dest),
519+
Self::ColorFunction(color_function) => color_function.to_css(dest),
399520
}
400521
}
401522
}
@@ -571,7 +692,7 @@ impl Color {
571692
Token::Function(ref name) => {
572693
let name = name.clone();
573694
return input.parse_nested_block(|arguments| {
574-
parse_color_function(component_parser, &*name, arguments)
695+
parse_color(component_parser, &*name, arguments)
575696
});
576697
}
577698
_ => Err(()),
@@ -785,7 +906,7 @@ fn clamp_floor_256_f32(val: f32) -> u8 {
785906
}
786907

787908
#[inline]
788-
fn parse_color_function<'i, 't, ComponentParser>(
909+
fn parse_color<'i, 't, ComponentParser>(
789910
component_parser: &ComponentParser,
790911
name: &str,
791912
arguments: &mut Parser<'i, 't>,
@@ -827,6 +948,8 @@ where
827948
Color::Absolute(AbsoluteColor::Oklch(Oklch::new(l.max(0.), c.max(0.), h, alpha)))
828949
}),
829950

951+
"color" => parse_color_function(component_parser, arguments),
952+
830953
_ => return Err(arguments.new_unexpected_token_error(Token::Ident(name.to_owned().into()))),
831954
}?;
832955

@@ -1069,3 +1192,44 @@ where
10691192

10701193
Ok(into_color(lightness, chroma, hue, alpha))
10711194
}
1195+
1196+
#[inline]
1197+
fn parse_color_function<'i, 't, ComponentParser>(
1198+
component_parser: &ComponentParser,
1199+
arguments: &mut Parser<'i, 't>,
1200+
) -> Result<Color, ParseError<'i, ComponentParser::Error>>
1201+
where
1202+
ComponentParser: ColorComponentParser<'i>,
1203+
{
1204+
let color_space = {
1205+
let location = arguments.current_source_location();
1206+
1207+
let ident = arguments.expect_ident()?;
1208+
PredefinedColorSpace::from_str(ident)
1209+
.map_err(|_| location.new_unexpected_token_error(Token::Ident(ident.clone())))?
1210+
};
1211+
1212+
macro_rules! parse_component {
1213+
() => {{
1214+
component_parser
1215+
.parse_number_or_percentage(arguments)?
1216+
.unit_value()
1217+
}};
1218+
}
1219+
1220+
let c1 = parse_component!();
1221+
let c2 = parse_component!();
1222+
let c3 = parse_component!();
1223+
1224+
let alpha = parse_alpha(component_parser, arguments, false)?;
1225+
1226+
Ok(Color::Absolute(AbsoluteColor::ColorFunction(
1227+
ColorFunction {
1228+
color_space,
1229+
c1,
1230+
c2,
1231+
c3,
1232+
alpha,
1233+
},
1234+
)))
1235+
}

0 commit comments

Comments
 (0)