style/values/computed/
color.rs1use crate::color::AbsoluteColor;
8use crate::typed_om::{KeywordValue, ToTyped, TypedValue};
9use crate::values::animated::ToAnimatedZero;
10use crate::values::computed::percentage::Percentage;
11use crate::values::generics::color::{
12 GenericCaretColor, GenericColor, GenericColorMix, GenericColorOrAuto,
13};
14use std::fmt::{self, Write};
15use style_traits::{CssString, CssWriter, ToCss};
16use thin_vec::ThinVec;
17
18pub use crate::values::specified::color::{ColorScheme, ForcedColorAdjust, PrintColorAdjust};
19
20pub type ColorPropertyValue = AbsoluteColor;
22
23pub type Color = GenericColor<Percentage>;
25
26pub type ColorMix = GenericColorMix<Color, Percentage>;
28
29impl ToCss for Color {
30 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
31 where
32 W: fmt::Write,
33 {
34 match *self {
35 Self::Absolute(ref c) => c.to_css(dest),
36 Self::ColorFunction(ref color_function) => color_function.to_css(dest),
37 Self::CurrentColor => dest.write_str("currentcolor"),
38 Self::ColorMix(ref m) => m.to_css(dest),
39 Self::ContrastColor(ref c) => {
40 dest.write_str("contrast-color(")?;
41 c.to_css(dest)?;
42 dest.write_char(')')
43 },
44 }
45 }
46}
47
48impl ToTyped for Color {
49 fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
50 match *self {
51 Self::CurrentColor => {
52 dest.push(TypedValue::Keyword(KeywordValue(CssString::from(
53 "currentcolor",
54 ))));
55 Ok(())
56 },
57 _ => Err(()),
58 }
59 }
60}
61
62impl Color {
63 pub const TRANSPARENT_BLACK: Self = Self::Absolute(AbsoluteColor::TRANSPARENT_BLACK);
65
66 pub const BLACK: Self = Self::Absolute(AbsoluteColor::BLACK);
68
69 pub const WHITE: Self = Self::Absolute(AbsoluteColor::WHITE);
71
72 pub fn from_color_mix(color_mix: ColorMix) -> Self {
75 if let Some(absolute) = color_mix.mix_to_absolute() {
76 Self::Absolute(absolute)
77 } else {
78 Self::ColorMix(Box::new(color_mix))
79 }
80 }
81
82 pub fn resolve_to_absolute(&self, current_color: &AbsoluteColor) -> AbsoluteColor {
84 match *self {
85 Self::Absolute(c) => c,
86 Self::ColorFunction(ref color_function) => {
87 color_function.resolve_to_absolute(current_color)
88 },
89 Self::CurrentColor => *current_color,
90 Self::ColorMix(ref mix) => {
91 use crate::color::mix;
92
93 let fill = mix.omitted_weight().unwrap_or(0.0);
94
95 mix::mix_many(
96 mix.interpolation,
97 mix.items.iter().map(|item| {
98 mix::ColorMixItem::new(
99 item.color.resolve_to_absolute(current_color),
100 item.percentage.as_ref().map_or(fill, |p| p.0),
101 )
102 }),
103 mix.flags,
104 )
105 },
106 Self::ContrastColor(ref c) => {
107 let bg_color = c.resolve_to_absolute(current_color);
108 Self::resolve_contrast_color(&bg_color)
109 },
110 }
111 }
112
113 pub fn resolve_contrast_color(bg_color: &AbsoluteColor) -> AbsoluteColor {
115 if Self::contrast_ratio(bg_color, &AbsoluteColor::BLACK)
116 > Self::contrast_ratio(bg_color, &AbsoluteColor::WHITE)
117 {
118 AbsoluteColor::BLACK
119 } else {
120 AbsoluteColor::WHITE
121 }
122 }
123
124 fn contrast_ratio(a: &AbsoluteColor, b: &AbsoluteColor) -> f32 {
125 let compute = |c| -> f32 {
130 if c <= 0.04045 {
131 c / 12.92
132 } else {
133 f32::powf((c + 0.055) / 1.055, 2.4)
134 }
135 };
136 let luminance = |r, g, b| -> f32 { 0.2126 * r + 0.7152 * g + 0.0722 * b };
137 let a = a.into_srgb_legacy();
138 let b = b.into_srgb_legacy();
139 let a = a.raw_components();
140 let b = b.raw_components();
141 let la = luminance(compute(a[0]), compute(a[1]), compute(a[2])) + 0.05;
142 let lb = luminance(compute(b[0]), compute(b[1]), compute(b[2])) + 0.05;
143 if la > lb {
144 la / lb
145 } else {
146 lb / la
147 }
148 }
149}
150
151impl ToAnimatedZero for AbsoluteColor {
152 fn to_animated_zero(&self) -> Result<Self, ()> {
153 Ok(Self::TRANSPARENT_BLACK)
154 }
155}
156
157pub type ColorOrAuto = GenericColorOrAuto<Color>;
159
160pub type CaretColor = GenericCaretColor<Color>;