1use super::{AbsoluteColor, ColorFlags, ColorSpace};
8use crate::values::normalize;
9use cssparser::color::{clamp_unit_f32, serialize_color_alpha, OPAQUE};
10use std::fmt::{self, Write};
11use style_traits::{CssWriter, ToCss};
12
13struct ModernComponent<'a>(&'a Option<f32>);
16
17impl<'a> ToCss for ModernComponent<'a> {
18 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
19 where
20 W: fmt::Write,
21 {
22 if let Some(value) = self.0 {
23 if value.is_finite() {
24 value.to_css(dest)
25 } else if value.is_nan() {
26 dest.write_str("calc(NaN)")
27 } else {
28 debug_assert!(value.is_infinite());
29 if value.is_sign_negative() {
30 dest.write_str("calc(-infinity)")
31 } else {
32 dest.write_str("calc(infinity)")
33 }
34 }
35 } else {
36 dest.write_str("none")
37 }
38 }
39}
40
41impl ToCss for AbsoluteColor {
42 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
43 where
44 W: Write,
45 {
46 match self.color_space {
47 ColorSpace::Srgb if self.flags.contains(ColorFlags::IS_LEGACY_SRGB) => {
48 let has_alpha = self.alpha != OPAQUE;
50
51 dest.write_str(if has_alpha { "rgba(" } else { "rgb(" })?;
52 clamp_unit_f32(self.components.0).to_css(dest)?;
53 dest.write_str(", ")?;
54 clamp_unit_f32(self.components.1).to_css(dest)?;
55 dest.write_str(", ")?;
56 clamp_unit_f32(self.components.2).to_css(dest)?;
57
58 serialize_color_alpha(dest, Some(self.alpha), true)?;
60
61 dest.write_char(')')
62 },
63 ColorSpace::Hsl | ColorSpace::Hwb => {
64 if self.flags.contains(ColorFlags::IS_LEGACY_SRGB) {
65 self.into_srgb_legacy().to_css(dest)
66 } else {
67 self.to_color_space(ColorSpace::Srgb).to_css(dest)
68 }
69 },
70 ColorSpace::Oklab | ColorSpace::Lab | ColorSpace::Oklch | ColorSpace::Lch => {
71 if let ColorSpace::Oklab | ColorSpace::Oklch = self.color_space {
72 dest.write_str("ok")?;
73 }
74 if let ColorSpace::Oklab | ColorSpace::Lab = self.color_space {
75 dest.write_str("lab(")?;
76 } else {
77 dest.write_str("lch(")?;
78 }
79 ModernComponent(&self.c0()).to_css(dest)?;
80 dest.write_char(' ')?;
81 ModernComponent(&self.c1()).to_css(dest)?;
82 dest.write_char(' ')?;
83 ModernComponent(&self.c2()).to_css(dest)?;
84 serialize_color_alpha(dest, self.alpha(), false)?;
85 dest.write_char(')')
86 },
87 _ => {
88 #[cfg(debug_assertions)]
89 match self.color_space {
90 ColorSpace::Srgb => {
91 debug_assert!(
92 !self.flags.contains(ColorFlags::IS_LEGACY_SRGB),
93 "legacy srgb is not a color function"
94 );
95 },
96 ColorSpace::SrgbLinear |
97 ColorSpace::DisplayP3 |
98 ColorSpace::A98Rgb |
99 ColorSpace::ProphotoRgb |
100 ColorSpace::Rec2020 |
101 ColorSpace::XyzD50 |
102 ColorSpace::XyzD65 => {
103 },
105 _ => {
106 unreachable!("other color spaces do not support color() syntax")
107 },
108 };
109
110 dest.write_str("color(")?;
111 self.color_space.to_css(dest)?;
112 dest.write_char(' ')?;
113 ModernComponent(&self.c0()).to_css(dest)?;
114 dest.write_char(' ')?;
115 ModernComponent(&self.c1()).to_css(dest)?;
116 dest.write_char(' ')?;
117 ModernComponent(&self.c2()).to_css(dest)?;
118
119 serialize_color_alpha(dest, self.alpha(), false)?;
120
121 dest.write_char(')')
122 },
123 }
124 }
125}
126
127impl AbsoluteColor {
128 pub fn write_author_preferred_value<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
134 where
135 W: Write,
136 {
137 macro_rules! precision {
138 ($v:expr) => {{
139 ($v * 100.0).round() / 100.0
140 }};
141 }
142 macro_rules! number {
143 ($c:expr) => {{
144 if let Some(v) = $c.map(normalize) {
145 precision!(v).to_css(dest)?;
146 } else {
147 write!(dest, "none")?;
148 }
149 }};
150 }
151 macro_rules! percentage {
152 ($c:expr) => {{
153 if let Some(v) = $c.map(normalize) {
154 precision!(v).to_css(dest)?;
155 dest.write_char('%')?;
156 } else {
157 write!(dest, "none")?;
158 }
159 }};
160 }
161 macro_rules! unit_percentage {
162 ($c:expr) => {{
163 if let Some(v) = $c.map(normalize) {
164 precision!(v * 100.0).to_css(dest)?;
165 dest.write_char('%')?;
166 } else {
167 write!(dest, "none")?;
168 }
169 }};
170 }
171 macro_rules! angle {
172 ($c:expr) => {{
173 if let Some(v) = $c.map(normalize) {
174 precision!(v).to_css(dest)?;
175 dest.write_str("deg")?;
176 } else {
177 write!(dest, "none")?;
178 }
179 }};
180 }
181
182 match self.color_space {
183 ColorSpace::Srgb => {
184 write!(dest, "rgb(")?;
185 unit_percentage!(self.c0());
186 dest.write_char(' ')?;
187 unit_percentage!(self.c1());
188 dest.write_char(' ')?;
189 unit_percentage!(self.c2());
190 serialize_color_alpha(dest, self.alpha(), false)?;
191 dest.write_char(')')
192 },
193 ColorSpace::Hsl | ColorSpace::Hwb => {
194 dest.write_str(if self.color_space == ColorSpace::Hsl {
195 "hsl("
196 } else {
197 "hwb("
198 })?;
199 angle!(self.c0());
200 dest.write_char(' ')?;
201 percentage!(self.c1());
202 dest.write_char(' ')?;
203 percentage!(self.c2());
204 serialize_color_alpha(dest, self.alpha(), false)?;
205 dest.write_char(')')
206 },
207 ColorSpace::Lab | ColorSpace::Oklab => {
208 if self.color_space == ColorSpace::Oklab {
209 dest.write_str("ok")?;
210 }
211 dest.write_str("lab(")?;
212 if self.color_space == ColorSpace::Lab {
213 percentage!(self.c0())
214 } else {
215 unit_percentage!(self.c0())
216 }
217 dest.write_char(' ')?;
218 number!(self.c1());
219 dest.write_char(' ')?;
220 number!(self.c2());
221 serialize_color_alpha(dest, self.alpha(), false)?;
222 dest.write_char(')')
223 },
224 ColorSpace::Lch | ColorSpace::Oklch => {
225 if self.color_space == ColorSpace::Oklch {
226 dest.write_str("ok")?;
227 }
228 dest.write_str("lch(")?;
229 number!(self.c0());
230 dest.write_char(' ')?;
231 number!(self.c1());
232 dest.write_char(' ')?;
233 angle!(self.c2());
234 serialize_color_alpha(dest, self.alpha(), false)?;
235 dest.write_char(')')
236 },
237 ColorSpace::SrgbLinear |
238 ColorSpace::DisplayP3 |
239 ColorSpace::A98Rgb |
240 ColorSpace::ProphotoRgb |
241 ColorSpace::Rec2020 |
242 ColorSpace::XyzD50 |
243 ColorSpace::XyzD65 => {
244 dest.write_str("color(")?;
245 self.color_space.to_css(dest)?;
246 dest.write_char(' ')?;
247 number!(self.c0());
248 dest.write_char(' ')?;
249 number!(self.c1());
250 dest.write_char(' ')?;
251 number!(self.c2());
252 serialize_color_alpha(dest, self.alpha(), false)?;
253 dest.write_char(')')
254 },
255 }
256 }
257}