style/values/computed/
color.rs1use crate::color::AbsoluteColor;
8use crate::values::animated::ToAnimatedZero;
9use crate::values::computed::percentage::Percentage;
10use crate::values::generics::color::{
11 GenericCaretColor, GenericColor, GenericColorMix, GenericColorOrAuto,
12};
13use std::fmt::{self, Write};
14use style_traits::{CssWriter, ToCss};
15
16pub use crate::values::specified::color::{ColorScheme, ForcedColorAdjust, PrintColorAdjust};
17
18pub type ColorPropertyValue = AbsoluteColor;
20
21pub type Color = GenericColor<Percentage>;
23
24pub type ColorMix = GenericColorMix<Color, Percentage>;
26
27impl ToCss for Color {
28 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
29 where
30 W: fmt::Write,
31 {
32 match *self {
33 Self::Absolute(ref c) => c.to_css(dest),
34 Self::ColorFunction(ref color_function) => color_function.to_css(dest),
35 Self::CurrentColor => dest.write_str("currentcolor"),
36 Self::ColorMix(ref m) => m.to_css(dest),
37 Self::ContrastColor(ref c) => {
38 dest.write_str("contrast-color(")?;
39 c.to_css(dest)?;
40 dest.write_char(')')
41 },
42 }
43 }
44}
45
46impl Color {
47 pub const TRANSPARENT_BLACK: Self = Self::Absolute(AbsoluteColor::TRANSPARENT_BLACK);
49
50 pub const BLACK: Self = Self::Absolute(AbsoluteColor::BLACK);
52
53 pub const WHITE: Self = Self::Absolute(AbsoluteColor::WHITE);
55
56 pub fn from_color_mix(color_mix: ColorMix) -> Self {
59 if let Some(absolute) = color_mix.mix_to_absolute() {
60 Self::Absolute(absolute)
61 } else {
62 Self::ColorMix(Box::new(color_mix))
63 }
64 }
65
66 pub fn resolve_to_absolute(&self, current_color: &AbsoluteColor) -> AbsoluteColor {
69 use crate::values::specified::percentage::ToPercentage;
70
71 match *self {
72 Self::Absolute(c) => c,
73 Self::ColorFunction(ref color_function) => {
74 color_function.resolve_to_absolute(current_color)
75 },
76 Self::CurrentColor => *current_color,
77 Self::ColorMix(ref mix) => {
78 let left = mix.left.resolve_to_absolute(current_color);
79 let right = mix.right.resolve_to_absolute(current_color);
80 crate::color::mix::mix(
81 mix.interpolation,
82 &left,
83 mix.left_percentage.to_percentage(),
84 &right,
85 mix.right_percentage.to_percentage(),
86 mix.flags,
87 )
88 },
89 Self::ContrastColor(ref c) => {
90 let bg_color = c.resolve_to_absolute(current_color);
91 if Self::contrast_ratio(&bg_color, &AbsoluteColor::BLACK)
92 > Self::contrast_ratio(&bg_color, &AbsoluteColor::WHITE)
93 {
94 AbsoluteColor::BLACK
95 } else {
96 AbsoluteColor::WHITE
97 }
98 },
99 }
100 }
101
102 fn contrast_ratio(a: &AbsoluteColor, b: &AbsoluteColor) -> f32 {
103 let compute = |c| -> f32 {
108 if c <= 0.04045 {
109 c / 12.92
110 } else {
111 f32::powf((c + 0.055) / 1.055, 2.4)
112 }
113 };
114 let luminance = |r, g, b| -> f32 { 0.2126 * r + 0.7152 * g + 0.0722 * b };
115 let a = a.into_srgb_legacy();
116 let b = b.into_srgb_legacy();
117 let a = a.raw_components();
118 let b = b.raw_components();
119 let la = luminance(compute(a[0]), compute(a[1]), compute(a[2])) + 0.05;
120 let lb = luminance(compute(b[0]), compute(b[1]), compute(b[2])) + 0.05;
121 if la > lb {
122 la / lb
123 } else {
124 lb / la
125 }
126 }
127}
128
129impl ToAnimatedZero for AbsoluteColor {
130 fn to_animated_zero(&self) -> Result<Self, ()> {
131 Ok(Self::TRANSPARENT_BLACK)
132 }
133}
134
135pub type ColorOrAuto = GenericColorOrAuto<Color>;
137
138pub type CaretColor = GenericCaretColor<Color>;