1use super::prelude::*;
2use crate::functions::color_mix_function::ColorMixFunction;
3use crate::functions::light_dark_function::LightDarkFunction;
4use crate::functions::relative_color::RelativeColorFunction;
5use crate::{AngleOrNumber, NoneOr, NumberOrPercentage};
6use css_parse::BumpBox;
7
8#[derive(
9 Parse, Peek, IntoCursor, ToSpan, SemanticEq, ToCursors, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash,
10)]
11#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
12#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
13#[derive(csskit_derives::NodeWithMetadata)]
14pub enum ColorSpace {
15 #[atom(CssAtomSet::Srgb)]
16 Srgb(T![Ident]),
17 #[atom(CssAtomSet::SrgbLinear)]
18 SrgbLinear(T![Ident]),
19 #[atom(CssAtomSet::DisplayP3)]
20 DisplayP3(T![Ident]),
21 #[atom(CssAtomSet::A98Rgb)]
22 A98Rgb(T![Ident]),
23 #[atom(CssAtomSet::ProphotoRgb)]
24 ProphotoRgb(T![Ident]),
25 #[atom(CssAtomSet::Rec2020)]
26 Rec2020(T![Ident]),
27 #[atom(CssAtomSet::Xyz)]
28 Xyz(T![Ident]),
29 #[atom(CssAtomSet::XyzD50)]
30 XyzD50(T![Ident]),
31 #[atom(CssAtomSet::XyzD65)]
32 XyzD65(T![Ident]),
33}
34
35#[derive(IntoCursor, ToSpan, SemanticEq, ToCursors, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
36#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
37#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
38#[derive(csskit_derives::NodeWithMetadata)]
39pub struct CommaOrSlash(Cursor);
40
41impl<'a> Peek<'a> for CommaOrSlash {
42 const PEEK_KINDSET: KindSet = KindSet::new(&[Kind::Delim]);
43
44 #[inline(always)]
45 fn peek<I>(_: &Parser<'a, I>, c: Cursor) -> bool
46 where
47 I: Iterator<Item = Cursor> + Clone,
48 {
49 c == ',' || c == '/'
50 }
51}
52
53impl<'a> Parse<'a> for CommaOrSlash {
54 fn parse<I>(p: &mut Parser<'a, I>) -> ParserResult<Self>
55 where
56 I: Iterator<Item = Cursor> + Clone,
57 {
58 if !p.peek::<Self>() {
59 Err(Diagnostic::new(p.next(), Diagnostic::unexpected))?
60 }
61 Ok(Self(p.next()))
62 }
63}
64
65#[derive(Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
67#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
68#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(all))]
69#[derive(csskit_derives::NodeWithMetadata)]
70pub enum ColorFunction<'a> {
71 Relative(BumpBox<'a, RelativeColorFunction<'a>>),
72 Color(BumpBox<'a, ColorFunctionColor>),
73 ColorMix(BumpBox<'a, ColorMixFunction<'a>>),
74 LightDark(BumpBox<'a, LightDarkFunction<'a>>),
75 Rgb(RgbFunction),
76 Rgba(RgbaFunction),
77 Hsl(HslFunction),
78 Hsla(HslaFunction),
79 Hwb(HwbFunction),
80 Lab(LabFunction),
81 Lch(LchFunction),
82 Oklab(OklabFunction),
83 Oklch(OklchFunction),
84}
85
86impl<'a> Parse<'a> for ColorFunction<'a> {
87 fn parse<I>(p: &mut Parser<'a, I>) -> ParserResult<Self>
88 where
89 I: Iterator<Item = Cursor> + Clone,
90 {
91 if p.peek::<BumpBox<RelativeColorFunction>>() {
93 return Ok(Self::Relative(p.parse()?));
94 }
95 if p.peek::<BumpBox<ColorFunctionColor>>() {
96 return Ok(Self::Color(p.parse()?));
97 }
98 if p.peek::<BumpBox<ColorMixFunction>>() {
99 return Ok(Self::ColorMix(p.parse()?));
100 }
101 if p.peek::<BumpBox<LightDarkFunction>>() {
102 return Ok(Self::LightDark(p.parse()?));
103 }
104 if p.peek::<RgbFunction>() {
105 return Ok(Self::Rgb(p.parse()?));
106 }
107 if p.peek::<RgbaFunction>() {
108 return Ok(Self::Rgba(p.parse()?));
109 }
110 if p.peek::<HslFunction>() {
111 return Ok(Self::Hsl(p.parse()?));
112 }
113 if p.peek::<HslaFunction>() {
114 return Ok(Self::Hsla(p.parse()?));
115 }
116 if p.peek::<HwbFunction>() {
117 return Ok(Self::Hwb(p.parse()?));
118 }
119 if p.peek::<LabFunction>() {
120 return Ok(Self::Lab(p.parse()?));
121 }
122 if p.peek::<LchFunction>() {
123 return Ok(Self::Lch(p.parse()?));
124 }
125 if p.peek::<OklabFunction>() {
126 return Ok(Self::Oklab(p.parse()?));
127 }
128 Ok(Self::Oklch(p.parse()?))
129 }
130}
131
132#[cfg(feature = "chromashift")]
133impl crate::ToChromashift for ColorFunction<'_> {
134 fn to_chromashift(&self) -> Option<chromashift::Color> {
135 match self {
136 Self::Relative(r) => r.to_chromashift(),
137 Self::Color(c) => c.to_chromashift(),
138 Self::ColorMix(c) => c.to_chromashift(),
139 Self::LightDark(_c) => None,
140 Self::Rgb(c) => c.to_chromashift(),
141 Self::Rgba(c) => c.to_chromashift(),
142 Self::Hsl(c) => c.to_chromashift(),
143 Self::Hsla(c) => c.to_chromashift(),
144 Self::Hwb(c) => c.to_chromashift(),
145 Self::Lab(c) => c.to_chromashift(),
146 Self::Lch(c) => c.to_chromashift(),
147 Self::Oklab(c) => c.to_chromashift(),
148 Self::Oklch(c) => c.to_chromashift(),
149 }
150 }
151}
152
153#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
164#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
165#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
166#[derive(csskit_derives::NodeWithMetadata)]
167pub struct ColorFunctionColor {
168 #[atom(CssAtomSet::Color)]
169 pub name: T![Function],
170 pub params: ColorFunctionColorParams,
171 pub close: T![')'],
172}
173
174#[cfg(feature = "chromashift")]
175impl crate::ToChromashift for ColorFunctionColor {
176 fn to_chromashift(&self) -> Option<chromashift::Color> {
177 use chromashift::{A98Rgb, DisplayP3, LinearRgb, ProphotoRgb, Rec2020, Srgb, XyzD50, XyzD65};
178
179 let ColorFunctionColorParams(space, c1, c2, c3, _, alpha) = &self.params;
180
181 let alpha = match alpha {
182 Some(NoneOr::None(_)) => 0.0,
183 Some(NoneOr::Some(NumberOrPercentage::Number(t))) => t.value() * 100.0,
184 Some(NoneOr::Some(NumberOrPercentage::Percentage(t))) => t.value(),
185 None => 100.0,
186 };
187
188 let channel_unit = |c: &NoneOr<NumberOrPercentage>| -> f64 {
190 match c {
191 NoneOr::None(_) => 0.0,
192 NoneOr::Some(NumberOrPercentage::Number(n)) => n.value() as f64,
193 NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value() as f64 / 100.0,
194 }
195 };
196
197 match space {
198 ColorSpace::Srgb(_) => {
199 let r = (channel_unit(c1) * 255.0).round() as u8;
200 let g = (channel_unit(c2) * 255.0).round() as u8;
201 let b = (channel_unit(c3) * 255.0).round() as u8;
202 Some(chromashift::Color::Srgb(Srgb::new(r, g, b, alpha)))
203 }
204 ColorSpace::SrgbLinear(_) => {
205 let r = channel_unit(c1);
206 let g = channel_unit(c2);
207 let b = channel_unit(c3);
208 Some(chromashift::Color::LinearRgb(LinearRgb::new(r, g, b, alpha)))
209 }
210 ColorSpace::DisplayP3(_) => {
211 let r = channel_unit(c1);
212 let g = channel_unit(c2);
213 let b = channel_unit(c3);
214 Some(chromashift::Color::DisplayP3(DisplayP3::new(r, g, b, alpha)))
215 }
216 ColorSpace::A98Rgb(_) => {
217 let r = channel_unit(c1);
218 let g = channel_unit(c2);
219 let b = channel_unit(c3);
220 Some(chromashift::Color::A98Rgb(A98Rgb::new(r, g, b, alpha)))
221 }
222 ColorSpace::ProphotoRgb(_) => {
223 let r = channel_unit(c1);
224 let g = channel_unit(c2);
225 let b = channel_unit(c3);
226 Some(chromashift::Color::ProphotoRgb(ProphotoRgb::new(r, g, b, alpha)))
227 }
228 ColorSpace::Rec2020(_) => {
229 let r = channel_unit(c1);
230 let g = channel_unit(c2);
231 let b = channel_unit(c3);
232 Some(chromashift::Color::Rec2020(Rec2020::new(r, g, b, alpha)))
233 }
234 ColorSpace::Xyz(_) | ColorSpace::XyzD65(_) => {
235 let x = channel_unit(c1) * 100.0;
236 let y = channel_unit(c2) * 100.0;
237 let z = channel_unit(c3) * 100.0;
238 Some(chromashift::Color::XyzD65(XyzD65::new(x, y, z, alpha)))
239 }
240 ColorSpace::XyzD50(_) => {
241 let x = channel_unit(c1) * 100.0;
242 let y = channel_unit(c2) * 100.0;
243 let z = channel_unit(c3) * 100.0;
244 Some(chromashift::Color::XyzD50(XyzD50::new(x, y, z, alpha)))
245 }
246 }
247 }
248}
249
250#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
251#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
252#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
253#[derive(csskit_derives::NodeWithMetadata)]
254pub struct ColorFunctionColorParams(
255 pub ColorSpace,
256 pub NoneOr<NumberOrPercentage>,
257 pub NoneOr<NumberOrPercentage>,
258 pub NoneOr<NumberOrPercentage>,
259 pub Option<T![/]>,
260 pub Option<NoneOr<NumberOrPercentage>>,
261);
262
263#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
280#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
281#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
282#[derive(csskit_derives::NodeWithMetadata)]
283pub struct RgbFunction {
284 #[atom(CssAtomSet::Rgb)]
285 pub name: T![Function],
286 pub params: RgbFunctionParams,
287 pub close: T![')'],
288}
289
290#[cfg(feature = "chromashift")]
291impl crate::ToChromashift for RgbFunction {
292 fn to_chromashift(&self) -> Option<chromashift::Color> {
293 self.params.to_chromashift()
294 }
295}
296
297#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
298#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
299#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
300#[derive(csskit_derives::NodeWithMetadata)]
301pub struct RgbaFunction {
302 #[atom(CssAtomSet::Rgba)]
303 pub name: T![Function],
304 pub params: RgbFunctionParams,
305 pub close: T![')'],
306}
307
308#[cfg(feature = "chromashift")]
309impl crate::ToChromashift for RgbaFunction {
310 fn to_chromashift(&self) -> Option<chromashift::Color> {
311 self.params.to_chromashift()
312 }
313}
314
315#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
316#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
317#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
318#[derive(csskit_derives::NodeWithMetadata)]
319pub struct RgbFunctionParams(
320 pub NoneOr<NumberOrPercentage>,
321 pub Option<T![,]>,
322 pub NoneOr<NumberOrPercentage>,
323 pub Option<T![,]>,
324 pub NoneOr<NumberOrPercentage>,
325 pub Option<CommaOrSlash>,
326 pub Option<NoneOr<NumberOrPercentage>>,
327);
328
329#[cfg(feature = "chromashift")]
330impl crate::ToChromashift for RgbFunctionParams {
331 fn to_chromashift(&self) -> Option<chromashift::Color> {
332 use chromashift::Srgb;
333 let Self(red, _, green, _, blue, _, alpha) = &self;
334 let alpha = match alpha {
335 Some(NoneOr::None(_)) => 0.0,
336 Some(NoneOr::Some(NumberOrPercentage::Number(t))) => t.value() * 100.0,
337 Some(NoneOr::Some(NumberOrPercentage::Percentage(t))) => t.value(),
338 None => 100.0,
339 };
340 let red = (match red {
341 NoneOr::None(_) => 0.0,
342 NoneOr::Some(NumberOrPercentage::Number(red)) => red.value(),
343 NoneOr::Some(NumberOrPercentage::Percentage(red)) => red.value() / 100.0 * 255.0,
344 })
345 .round() as u8;
346 let green = (match green {
347 NoneOr::None(_) => 0.0,
348 NoneOr::Some(NumberOrPercentage::Number(green)) => green.value(),
349 NoneOr::Some(NumberOrPercentage::Percentage(green)) => green.value() / 100.0 * 255.0,
350 })
351 .round() as u8;
352 let blue = (match blue {
353 NoneOr::None(_) => 0.0,
354 NoneOr::Some(NumberOrPercentage::Number(blue)) => blue.value(),
355 NoneOr::Some(NumberOrPercentage::Percentage(blue)) => blue.value() / 100.0 * 255.0,
356 })
357 .round() as u8;
358 Some(chromashift::Color::Srgb(Srgb::new(red, green, blue, alpha)))
359 }
360}
361
362#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
381#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
382#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
383#[derive(csskit_derives::NodeWithMetadata)]
384pub struct HslFunction {
385 #[atom(CssAtomSet::Hsl)]
386 pub name: T![Function],
387 pub params: HslFunctionParams,
388 pub close: T![')'],
389}
390
391#[cfg(feature = "chromashift")]
392impl crate::ToChromashift for HslFunction {
393 fn to_chromashift(&self) -> Option<chromashift::Color> {
394 self.params.to_chromashift()
395 }
396}
397
398#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
399#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
400#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
401#[derive(csskit_derives::NodeWithMetadata)]
402pub struct HslaFunction {
403 #[atom(CssAtomSet::Hsla)]
404 pub name: T![Function],
405 pub params: HslFunctionParams,
406 pub close: T![')'],
407}
408
409#[cfg(feature = "chromashift")]
410impl crate::ToChromashift for HslaFunction {
411 fn to_chromashift(&self) -> Option<chromashift::Color> {
412 self.params.to_chromashift()
413 }
414}
415
416#[derive(Parse, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
417#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
418#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
419#[derive(csskit_derives::NodeWithMetadata)]
420pub struct HslFunctionParams(
421 pub NoneOr<AngleOrNumber>,
422 pub Option<T![,]>,
423 pub NoneOr<NumberOrPercentage>,
424 pub Option<T![,]>,
425 pub NoneOr<NumberOrPercentage>,
426 pub Option<CommaOrSlash>,
427 pub Option<NoneOr<NumberOrPercentage>>,
428);
429
430#[cfg(feature = "chromashift")]
431impl crate::ToChromashift for HslFunctionParams {
432 fn to_chromashift(&self) -> Option<chromashift::Color> {
433 use chromashift::Hsl;
434 let Self(hue, _, saturation, _, lightness, _, alpha) = &self;
435 let hue = match hue {
436 NoneOr::None(_) => 0.0,
437 NoneOr::Some(AngleOrNumber::Number(hue)) => hue.value(),
438 NoneOr::Some(AngleOrNumber::Angle(d)) => d.as_degrees(),
439 };
440 let saturation = match saturation {
441 NoneOr::None(_) => 0.0,
442 NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
443 NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value(),
444 };
445 let lightness = match lightness {
446 NoneOr::None(_) => 0.0,
447 NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
448 NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value(),
449 };
450 let alpha = match alpha {
451 Some(NoneOr::None(_)) => 0.0,
452 Some(NoneOr::Some(NumberOrPercentage::Number(t))) => t.value() * 100.0,
453 Some(NoneOr::Some(NumberOrPercentage::Percentage(t))) => t.value(),
454 None => 100.0,
455 };
456 Some(chromashift::Color::Hsl(Hsl::new(hue, saturation, lightness, alpha)))
457 }
458}
459
460#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
470#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
471#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
472#[derive(csskit_derives::NodeWithMetadata)]
473pub struct HwbFunction {
474 #[atom(CssAtomSet::Hwb)]
475 pub name: T![Function],
476 pub params: HwbFunctionParams,
477 pub close: T![')'],
478}
479
480#[cfg(feature = "chromashift")]
481impl crate::ToChromashift for HwbFunction {
482 fn to_chromashift(&self) -> Option<chromashift::Color> {
483 use chromashift::Hwb;
484 let HwbFunctionParams(hue, whiteness, blackness, _, alpha) = &self.params;
485 let hue = match hue {
486 NoneOr::None(_) => 0.0,
487 NoneOr::Some(AngleOrNumber::Number(hue)) => hue.value(),
488 NoneOr::Some(AngleOrNumber::Angle(d)) => d.as_degrees(),
489 };
490 let whiteness = match whiteness {
491 NoneOr::None(_) => 0.0,
492 NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
493 NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value(),
494 };
495 let blackness = match blackness {
496 NoneOr::None(_) => 0.0,
497 NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
498 NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value(),
499 };
500 let alpha = match alpha {
501 Some(NoneOr::None(_)) => 0.0,
502 Some(NoneOr::Some(NumberOrPercentage::Number(t))) => t.value() * 100.0,
503 Some(NoneOr::Some(NumberOrPercentage::Percentage(t))) => t.value(),
504 None => 100.0,
505 };
506 Some(chromashift::Color::Hwb(Hwb::new(hue, whiteness, blackness, alpha)))
507 }
508}
509
510#[derive(Parse, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
511#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
512#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
513#[derive(csskit_derives::NodeWithMetadata)]
514pub struct HwbFunctionParams(
515 pub NoneOr<AngleOrNumber>,
516 pub NoneOr<NumberOrPercentage>,
517 pub NoneOr<NumberOrPercentage>,
518 pub Option<T![/]>,
519 pub Option<NoneOr<NumberOrPercentage>>,
520);
521
522#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
531#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
532#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
533#[derive(csskit_derives::NodeWithMetadata)]
534pub struct LabFunction {
535 #[atom(CssAtomSet::Lab)]
536 pub name: T![Function],
537 pub params: LabFunctionParams,
538 pub close: T![')'],
539}
540
541#[cfg(feature = "chromashift")]
542impl crate::ToChromashift for LabFunction {
543 fn to_chromashift(&self) -> Option<chromashift::Color> {
544 use chromashift::Lab;
545 let LabFunctionParams(l, a, b, _, alpha) = &self.params;
546 let l = match l {
547 NoneOr::None(_) => 0.0,
548 NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
549 NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value(),
550 } as f64;
551 let a = match a {
552 NoneOr::None(_) => 0.0,
553 NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
554 NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value() / 100.0 * 125.0,
555 } as f64;
556 let b = match b {
557 NoneOr::None(_) => 0.0,
558 NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
559 NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value() / 100.0 * 125.0,
560 } as f64;
561 let alpha = match alpha {
562 Some(NoneOr::None(_)) => 0.0,
563 Some(NoneOr::Some(NumberOrPercentage::Number(t))) => t.value() * 100.0,
564 Some(NoneOr::Some(NumberOrPercentage::Percentage(t))) => t.value(),
565 None => 100.0,
566 };
567 Some(chromashift::Color::Lab(Lab::new(l, a, b, alpha)))
568 }
569}
570
571#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
572#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
573#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
574#[derive(csskit_derives::NodeWithMetadata)]
575pub struct LabFunctionParams(
576 pub NoneOr<NumberOrPercentage>,
577 pub NoneOr<NumberOrPercentage>,
578 pub NoneOr<NumberOrPercentage>,
579 pub Option<T![/]>,
580 pub Option<NoneOr<NumberOrPercentage>>,
581);
582
583#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
592#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
593#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
594#[derive(csskit_derives::NodeWithMetadata)]
595pub struct LchFunction {
596 #[atom(CssAtomSet::Lch)]
597 pub name: T![Function],
598 pub params: LchFunctionParams,
599 pub close: T![')'],
600}
601
602#[cfg(feature = "chromashift")]
603impl crate::ToChromashift for LchFunction {
604 fn to_chromashift(&self) -> Option<chromashift::Color> {
605 use chromashift::Lch;
606 let LchFunctionParams(lightness, chroma, hue, _, alpha) = &self.params;
607 let lightness = match lightness {
608 NoneOr::None(_) => 0.0,
609 NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
610 NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value(),
611 } as f64;
612 let chroma = match chroma {
613 NoneOr::None(_) => 0.0,
614 NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
615 NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value() / 100.0 * 150.0,
616 } as f64;
617 let hue = match hue {
618 NoneOr::None(_) => 0.0,
619 NoneOr::Some(AngleOrNumber::Number(hue)) => hue.value(),
620 NoneOr::Some(AngleOrNumber::Angle(d)) => d.as_degrees(),
621 } as f64;
622 let alpha = match alpha {
623 Some(NoneOr::None(_)) => 0.0,
624 Some(NoneOr::Some(NumberOrPercentage::Number(t))) => t.value() * 100.0,
625 Some(NoneOr::Some(NumberOrPercentage::Percentage(t))) => t.value(),
626 None => 100.0,
627 };
628 Some(chromashift::Color::Lch(Lch::new(lightness, chroma, hue, alpha)))
629 }
630}
631
632#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
633#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
634#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
635#[derive(csskit_derives::NodeWithMetadata)]
636pub struct LchFunctionParams(
637 pub NoneOr<NumberOrPercentage>,
638 pub NoneOr<NumberOrPercentage>,
639 pub NoneOr<AngleOrNumber>,
640 pub Option<T![/]>,
641 pub Option<NoneOr<NumberOrPercentage>>,
642);
643
644#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
653#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
654#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
655#[derive(csskit_derives::NodeWithMetadata)]
656pub struct OklabFunction {
657 #[atom(CssAtomSet::Oklab)]
658 pub name: T![Function],
659 pub params: LabFunctionParams,
660 pub close: T![')'],
661}
662
663#[cfg(feature = "chromashift")]
664impl crate::ToChromashift for OklabFunction {
665 fn to_chromashift(&self) -> Option<chromashift::Color> {
666 use chromashift::Oklab;
667 let LabFunctionParams(l, a, b, _, alpha) = &self.params;
668 let alpha = match alpha {
669 Some(NoneOr::None(_)) => 0.0,
670 Some(NoneOr::Some(NumberOrPercentage::Number(t))) => t.value() * 100.0,
671 Some(NoneOr::Some(NumberOrPercentage::Percentage(t))) => t.value(),
672 None => 100.0,
673 };
674 let l = match l {
675 NoneOr::None(_) => 0.0,
676 NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
677 NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value() / 100.0,
678 } as f64;
679 let a = match a {
680 NoneOr::None(_) => 0.0,
681 NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
682 NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value() / 100.0 * 0.4,
683 } as f64;
684 let b = match b {
685 NoneOr::None(_) => 0.0,
686 NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
687 NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value() / 100.0 * 0.4,
688 } as f64;
689 Some(chromashift::Color::Oklab(Oklab::new(l, a, b, alpha)))
690 }
691}
692
693#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
702#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
703#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
704#[derive(csskit_derives::NodeWithMetadata)]
705pub struct OklchFunction {
706 #[atom(CssAtomSet::Oklch)]
707 pub name: T![Function],
708 pub params: LchFunctionParams,
709 pub close: T![')'],
710}
711
712#[cfg(feature = "chromashift")]
713impl crate::ToChromashift for OklchFunction {
714 fn to_chromashift(&self) -> Option<chromashift::Color> {
715 use chromashift::Oklch;
716 let LchFunctionParams(lightness, chroma, hue, _, alpha) = &self.params;
717 let lightness = match lightness {
718 NoneOr::None(_) => 0.0,
719 NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
720 NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value(),
721 } as f64;
722 let chroma = match chroma {
723 NoneOr::None(_) => 0.0,
724 NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
725 NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value() / 100.0 * 150.0,
726 } as f64;
727 let hue = match hue {
728 NoneOr::None(_) => 0.0,
729 NoneOr::Some(AngleOrNumber::Number(hue)) => hue.value(),
730 NoneOr::Some(AngleOrNumber::Angle(d)) => d.as_degrees(),
731 } as f64;
732 let alpha = match alpha {
733 Some(NoneOr::None(_)) => 0.0,
734 Some(NoneOr::Some(NumberOrPercentage::Number(t))) => t.value() * 100.0,
735 Some(NoneOr::Some(NumberOrPercentage::Percentage(t))) => t.value(),
736 None => 100.0,
737 };
738 Some(chromashift::Color::Oklch(Oklch::new(lightness, chroma, hue, alpha)))
739 }
740}
741
742#[cfg(test)]
743mod tests {
744 use super::*;
745
746 #[test]
747 fn size_test() {
748 assert_eq!(std::mem::size_of::<ColorFunction<'_>>(), 144);
749 assert_eq!(std::mem::size_of::<ColorFunctionColor>(), 120);
750 assert_eq!(std::mem::size_of::<RgbFunction>(), 136);
751 assert_eq!(std::mem::size_of::<RgbaFunction>(), 136);
752 assert_eq!(std::mem::size_of::<HslFunction>(), 136);
753 assert_eq!(std::mem::size_of::<HslaFunction>(), 136);
754 assert_eq!(std::mem::size_of::<HwbFunction>(), 104);
755 assert_eq!(std::mem::size_of::<LabFunction>(), 104);
756 assert_eq!(std::mem::size_of::<LchFunction>(), 104);
757 assert_eq!(std::mem::size_of::<OklabFunction>(), 104);
758 assert_eq!(std::mem::size_of::<OklchFunction>(), 104);
759 }
760}