@@ -16,16 +16,6 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer};
16
16
17
17
const OPAQUE : f32 = 1.0 ;
18
18
19
- fn serialize_none_or < T > ( dest : & mut impl fmt:: Write , value : & Option < T > ) -> fmt:: Result
20
- where
21
- T : ToCss ,
22
- {
23
- match value {
24
- Some ( v) => v. to_css ( dest) ,
25
- None => dest. write_str ( "none" ) ,
26
- }
27
- }
28
-
29
19
/// Serialize the alpha copmonent of a color according to the specification.
30
20
/// <https://drafts.csswg.org/css-color-4/#serializing-alpha-values>
31
21
#[ inline]
@@ -55,6 +45,33 @@ pub fn serialize_color_alpha(
55
45
rounded_alpha. to_css ( dest)
56
46
}
57
47
48
+ /// A [`ModernComponent`] can serialize to `none`, `nan`, `infinity` and
49
+ /// floating point values.
50
+ struct ModernComponent < ' a > ( & ' a Option < f32 > ) ;
51
+
52
+ impl < ' a > ToCss for ModernComponent < ' a > {
53
+ fn to_css < W > ( & self , dest : & mut W ) -> fmt:: Result
54
+ where
55
+ W : fmt:: Write ,
56
+ {
57
+ if let Some ( value) = self . 0 {
58
+ if value. is_infinite ( ) {
59
+ if value. is_sign_negative ( ) {
60
+ dest. write_str ( "calc(-infinity)" )
61
+ } else {
62
+ dest. write_str ( "calc(infinity)" )
63
+ }
64
+ } else if value. is_nan ( ) {
65
+ dest. write_str ( "calc(NaN)" )
66
+ } else {
67
+ value. to_css ( dest)
68
+ }
69
+ } else {
70
+ dest. write_str ( "none" )
71
+ }
72
+ }
73
+ }
74
+
58
75
// Guaratees hue in [0..360)
59
76
fn normalize_hue ( hue : f32 ) -> f32 {
60
77
// <https://drafts.csswg.org/css-values/#angles>
@@ -363,11 +380,11 @@ macro_rules! impl_lab_like {
363
380
{
364
381
dest. write_str( $fname) ?;
365
382
dest. write_str( "(" ) ?;
366
- serialize_none_or ( dest , & self . lightness) ?;
383
+ ModernComponent ( & self . lightness) . to_css ( dest ) ?;
367
384
dest. write_char( ' ' ) ?;
368
- serialize_none_or ( dest , & self . a) ?;
385
+ ModernComponent ( & self . a) . to_css ( dest ) ?;
369
386
dest. write_char( ' ' ) ?;
370
- serialize_none_or ( dest , & self . b) ?;
387
+ ModernComponent ( & self . b) . to_css ( dest ) ?;
371
388
serialize_color_alpha( dest, self . alpha, false ) ?;
372
389
dest. write_char( ')' )
373
390
}
@@ -454,11 +471,11 @@ macro_rules! impl_lch_like {
454
471
{
455
472
dest. write_str( $fname) ?;
456
473
dest. write_str( "(" ) ?;
457
- serialize_none_or ( dest , & self . lightness) ?;
474
+ ModernComponent ( & self . lightness) . to_css ( dest ) ?;
458
475
dest. write_char( ' ' ) ?;
459
- serialize_none_or ( dest , & self . chroma) ?;
476
+ ModernComponent ( & self . chroma) . to_css ( dest ) ?;
460
477
dest. write_char( ' ' ) ?;
461
- serialize_none_or ( dest , & self . hue) ?;
478
+ ModernComponent ( & self . hue) . to_css ( dest ) ?;
462
479
serialize_color_alpha( dest, self . alpha, false ) ?;
463
480
dest. write_char( ')' )
464
481
}
@@ -579,11 +596,11 @@ impl ToCss for ColorFunction {
579
596
dest. write_str ( "color(" ) ?;
580
597
self . color_space . to_css ( dest) ?;
581
598
dest. write_char ( ' ' ) ?;
582
- serialize_none_or ( dest , & self . c1 ) ?;
599
+ ModernComponent ( & self . c1 ) . to_css ( dest ) ?;
583
600
dest. write_char ( ' ' ) ?;
584
- serialize_none_or ( dest , & self . c2 ) ?;
601
+ ModernComponent ( & self . c2 ) . to_css ( dest ) ?;
585
602
dest. write_char ( ' ' ) ?;
586
- serialize_none_or ( dest , & self . c3 ) ?;
603
+ ModernComponent ( & self . c3 ) . to_css ( dest ) ?;
587
604
588
605
serialize_color_alpha ( dest, self . alpha , false ) ?;
589
606
@@ -1473,6 +1490,15 @@ pub fn hsl_to_rgb(hue: f32, saturation: f32, lightness: f32) -> (f32, f32, f32)
1473
1490
( red, green, blue)
1474
1491
}
1475
1492
1493
+ #[ inline]
1494
+ fn max_preserve_nan ( value : f32 , max : f32 ) -> f32 {
1495
+ if value. is_nan ( ) {
1496
+ value
1497
+ } else {
1498
+ value. max ( max)
1499
+ }
1500
+ }
1501
+
1476
1502
type IntoColorFn < Output > =
1477
1503
fn ( l : Option < f32 > , a : Option < f32 > , b : Option < f32 > , alpha : Option < f32 > ) -> Output ;
1478
1504
@@ -1495,7 +1521,7 @@ where
1495
1521
P :: parse_number_or_percentage,
1496
1522
) ?;
1497
1523
1498
- let lightness = lightness. map ( |l| l. value ( lightness_range) . max ( 0.0 ) ) ;
1524
+ let lightness = lightness. map ( |l| max_preserve_nan ( l. value ( lightness_range) , 0.0 ) ) ;
1499
1525
let a = a. map ( |a| a. value ( a_b_range) ) ;
1500
1526
let b = b. map ( |b| b. value ( a_b_range) ) ;
1501
1527
@@ -1521,8 +1547,8 @@ where
1521
1547
P :: parse_angle_or_number,
1522
1548
) ?;
1523
1549
1524
- let lightness = lightness. map ( |l| l. value ( lightness_range) . max ( 0.0 ) ) ;
1525
- let chroma = chroma. map ( |c| c. value ( chroma_range) . max ( 0.0 ) ) ;
1550
+ let lightness = lightness. map ( |l| max_preserve_nan ( l. value ( lightness_range) , 0.0 ) ) ;
1551
+ let chroma = chroma. map ( |c| max_preserve_nan ( c. value ( chroma_range) , 0.0 ) ) ;
1526
1552
let hue = hue. map ( |h| normalize_hue ( h. degrees ( ) ) ) ;
1527
1553
1528
1554
Ok ( into_color ( lightness, chroma, hue, alpha) )
@@ -1591,3 +1617,55 @@ where
1591
1617
1592
1618
Ok ( ( r1, r2, r3, alpha) )
1593
1619
}
1620
+
1621
+ #[ cfg( test) ]
1622
+ mod tests {
1623
+ use super :: * ;
1624
+
1625
+ #[ test]
1626
+ fn serialize_modern_components ( ) {
1627
+ // None.
1628
+ assert_eq ! ( ModernComponent ( & None ) . to_css_string( ) , "none" . to_string( ) ) ;
1629
+
1630
+ // Finite values.
1631
+ assert_eq ! (
1632
+ ModernComponent ( & Some ( 10.0 ) ) . to_css_string( ) ,
1633
+ "10" . to_string( )
1634
+ ) ;
1635
+ assert_eq ! (
1636
+ ModernComponent ( & Some ( -10.0 ) ) . to_css_string( ) ,
1637
+ "-10" . to_string( )
1638
+ ) ;
1639
+ assert_eq ! ( ModernComponent ( & Some ( 0.0 ) ) . to_css_string( ) , "0" . to_string( ) ) ;
1640
+ assert_eq ! (
1641
+ ModernComponent ( & Some ( -0.0 ) ) . to_css_string( ) ,
1642
+ "0" . to_string( )
1643
+ ) ;
1644
+
1645
+ // Infinite values.
1646
+ assert_eq ! (
1647
+ ModernComponent ( & Some ( f32 :: INFINITY ) ) . to_css_string( ) ,
1648
+ "calc(infinity)" . to_string( )
1649
+ ) ;
1650
+ assert_eq ! (
1651
+ ModernComponent ( & Some ( f32 :: NEG_INFINITY ) ) . to_css_string( ) ,
1652
+ "calc(-infinity)" . to_string( )
1653
+ ) ;
1654
+
1655
+ // NaN.
1656
+ assert_eq ! (
1657
+ ModernComponent ( & Some ( f32 :: NAN ) ) . to_css_string( ) ,
1658
+ "calc(NaN)" . to_string( )
1659
+ ) ;
1660
+ }
1661
+
1662
+ #[ test]
1663
+ fn max_preserve_nan_preserves_nan ( ) {
1664
+ assert ! ( max_preserve_nan( f32 :: NAN , 0.0 ) . is_nan( ) ) ;
1665
+ assert_eq ! ( max_preserve_nan( 10.0 , 0.0 ) , 10.0 ) ;
1666
+ assert_eq ! ( max_preserve_nan( -10.0 , 0.0 ) , 0.0 ) ;
1667
+ assert_eq ! ( max_preserve_nan( -10.0 , -5.0 ) , -5.0 ) ;
1668
+ assert_eq ! ( max_preserve_nan( f32 :: INFINITY , 0.0 ) , f32 :: INFINITY ) ;
1669
+ assert_eq ! ( max_preserve_nan( f32 :: NEG_INFINITY , 0.0 ) , 0.0 ) ;
1670
+ }
1671
+ }
0 commit comments