44
55use std:: f32:: consts:: PI ;
66use std:: fmt;
7+ use std:: str:: FromStr ;
78
89use super :: { BasicParseError , ParseError , Parser , ToCss , Token } ;
910
@@ -350,6 +351,122 @@ macro_rules! impl_lch_like {
350351impl_lch_like ! ( Lch , "lch" ) ;
351352impl_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
375494impl 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