4
4
5
5
use std:: f32:: consts:: PI ;
6
6
use std:: fmt;
7
+ use std:: str:: FromStr ;
7
8
8
9
use super :: { BasicParseError , ParseError , Parser , ToCss , Token } ;
9
10
@@ -350,6 +351,122 @@ macro_rules! impl_lch_like {
350
351
impl_lch_like ! ( Lch , "lch" ) ;
351
352
impl_lch_like ! ( Oklch , "oklch" ) ;
352
353
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
+
353
470
/// An absolutely specified color.
354
471
/// https://w3c.github.io/csswg-drafts/css-color-4/#typedef-absolute-color-base
355
472
#[ derive( Clone , Copy , PartialEq , Debug ) ]
@@ -370,6 +487,8 @@ pub enum AbsoluteColor {
370
487
/// Specifies an Oklab color by Oklab Lightness, Chroma, and hue using
371
488
/// the OKLCH cylindrical coordinate model.
372
489
Oklch ( Oklch ) ,
490
+ /// Specifies a color in a predefined color space.
491
+ ColorFunction ( ColorFunction ) ,
373
492
}
374
493
375
494
impl AbsoluteColor {
@@ -381,6 +500,7 @@ impl AbsoluteColor {
381
500
Self :: Lch ( c) => c. alpha ,
382
501
Self :: Oklab ( c) => c. alpha ,
383
502
Self :: Oklch ( c) => c. alpha ,
503
+ Self :: ColorFunction ( c) => c. alpha ,
384
504
}
385
505
}
386
506
}
@@ -396,6 +516,7 @@ impl ToCss for AbsoluteColor {
396
516
Self :: Lch ( lch) => lch. to_css ( dest) ,
397
517
Self :: Oklab ( lab) => lab. to_css ( dest) ,
398
518
Self :: Oklch ( lch) => lch. to_css ( dest) ,
519
+ Self :: ColorFunction ( color_function) => color_function. to_css ( dest) ,
399
520
}
400
521
}
401
522
}
@@ -571,7 +692,7 @@ impl Color {
571
692
Token :: Function ( ref name) => {
572
693
let name = name. clone ( ) ;
573
694
return input. parse_nested_block ( |arguments| {
574
- parse_color_function ( component_parser, & * name, arguments)
695
+ parse_color ( component_parser, & * name, arguments)
575
696
} ) ;
576
697
}
577
698
_ => Err ( ( ) ) ,
@@ -785,7 +906,7 @@ fn clamp_floor_256_f32(val: f32) -> u8 {
785
906
}
786
907
787
908
#[ inline]
788
- fn parse_color_function < ' i , ' t , ComponentParser > (
909
+ fn parse_color < ' i , ' t , ComponentParser > (
789
910
component_parser : & ComponentParser ,
790
911
name : & str ,
791
912
arguments : & mut Parser < ' i , ' t > ,
@@ -827,6 +948,8 @@ where
827
948
Color :: Absolute ( AbsoluteColor :: Oklch ( Oklch :: new( l. max( 0. ) , c. max( 0. ) , h, alpha) ) )
828
949
} ) ,
829
950
951
+ "color" => parse_color_function( component_parser, arguments) ,
952
+
830
953
_ => return Err ( arguments. new_unexpected_token_error( Token :: Ident ( name. to_owned( ) . into( ) ) ) ) ,
831
954
} ?;
832
955
@@ -1069,3 +1192,44 @@ where
1069
1192
1070
1193
Ok ( into_color ( lightness, chroma, hue, alpha) )
1071
1194
}
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