1use super::prelude::*;
2use crate::functions::color_mix_function::ColorMixFunction;
3use crate::{AngleOrNumber, NoneOr, NumberOrPercentage};
4use css_parse::BumpBox;
5
6#[derive(Parse, Peek, IntoCursor, ToCursors, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
7#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
8#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
9#[derive(csskit_derives::NodeWithMetadata)]
10pub enum ColorSpace {
11 #[atom(CssAtomSet::Srgb)]
12 Srgb(T![Ident]),
13 #[atom(CssAtomSet::SrgbLinear)]
14 SrgbLinear(T![Ident]),
15 #[atom(CssAtomSet::DisplayP3)]
16 DisplayP3(T![Ident]),
17 #[atom(CssAtomSet::A98Rgb)]
18 A98Rgb(T![Ident]),
19 #[atom(CssAtomSet::ProphotoRgb)]
20 ProphotoRgb(T![Ident]),
21 #[atom(CssAtomSet::Rec2020)]
22 Rec2020(T![Ident]),
23 #[atom(CssAtomSet::Xyz)]
24 Xyz(T![Ident]),
25 #[atom(CssAtomSet::XyzD50)]
26 XyzD50(T![Ident]),
27 #[atom(CssAtomSet::XyzD65)]
28 XyzD65(T![Ident]),
29}
30
31#[derive(IntoCursor, ToCursors, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
32#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
33#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
34#[derive(csskit_derives::NodeWithMetadata)]
35pub struct CommaOrSlash(Cursor);
36
37impl<'a> Peek<'a> for CommaOrSlash {
38 fn peek<I>(_: &Parser<'a, I>, c: Cursor) -> bool
39 where
40 I: Iterator<Item = Cursor> + Clone,
41 {
42 c == ',' || c == '/'
43 }
44}
45
46impl<'a> Parse<'a> for CommaOrSlash {
47 fn parse<I>(p: &mut Parser<'a, I>) -> ParserResult<Self>
48 where
49 I: Iterator<Item = Cursor> + Clone,
50 {
51 if !p.peek::<Self>() {
52 Err(Diagnostic::new(p.next(), Diagnostic::unexpected))?
53 }
54 Ok(Self(p.next()))
55 }
56}
57
58#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
60#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
61#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(all))]
62#[derive(csskit_derives::NodeWithMetadata)]
63pub enum ColorFunction<'a> {
64 Color(BumpBox<'a, ColorFunctionColor>),
65 ColorMix(BumpBox<'a, ColorMixFunction<'a>>),
66 Rgb(RgbFunction),
67 Rgba(RgbaFunction),
68 Hsl(HslFunction),
69 Hsla(HslaFunction),
70 Hwb(HwbFunction),
71 Lab(LabFunction),
72 Lch(LchFunction),
73 Oklab(OklabFunction),
74 Oklch(OklchFunction),
75}
76
77#[cfg(feature = "chromashift")]
78impl crate::ToChromashift for ColorFunction<'_> {
79 fn to_chromashift(&self) -> Option<chromashift::Color> {
80 match self {
81 Self::Color(c) => c.to_chromashift(),
82 Self::ColorMix(c) => c.to_chromashift(),
83 Self::Rgb(c) => c.to_chromashift(),
84 Self::Rgba(c) => c.to_chromashift(),
85 Self::Hsl(c) => c.to_chromashift(),
86 Self::Hsla(c) => c.to_chromashift(),
87 Self::Hwb(c) => c.to_chromashift(),
88 Self::Lab(c) => c.to_chromashift(),
89 Self::Lch(c) => c.to_chromashift(),
90 Self::Oklab(c) => c.to_chromashift(),
91 Self::Oklch(c) => c.to_chromashift(),
92 }
93 }
94}
95
96#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
107#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
108#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
109#[derive(csskit_derives::NodeWithMetadata)]
110pub struct ColorFunctionColor {
111 #[atom(CssAtomSet::Color)]
112 pub name: T![Function],
113 pub params: ColorFunctionColorParams,
114 pub close: T![')'],
115}
116
117#[cfg(feature = "chromashift")]
118impl crate::ToChromashift for ColorFunctionColor {
119 fn to_chromashift(&self) -> Option<chromashift::Color> {
120 use chromashift::{A98Rgb, DisplayP3, LinearRgb, ProphotoRgb, Rec2020, Srgb, XyzD50, XyzD65};
121
122 let ColorFunctionColorParams(space, c1, c2, c3, _, alpha) = &self.params;
123
124 let alpha = match alpha {
125 Some(NoneOr::None(_)) => 0.0,
126 Some(NoneOr::Some(NumberOrPercentage::Number(t))) => t.value() * 100.0,
127 Some(NoneOr::Some(NumberOrPercentage::Percentage(t))) => t.value(),
128 None => 100.0,
129 };
130
131 let channel_unit = |c: &NoneOr<NumberOrPercentage>| -> Option<f64> {
133 match c {
134 NoneOr::None(_) => None,
135 NoneOr::Some(NumberOrPercentage::Number(n)) => Some(n.value() as f64),
136 NoneOr::Some(NumberOrPercentage::Percentage(p)) => Some(p.value() as f64 / 100.0),
137 }
138 };
139
140 match space {
141 ColorSpace::Srgb(_) => {
142 let r = (channel_unit(c1)? * 255.0) as u8;
143 let g = (channel_unit(c2)? * 255.0) as u8;
144 let b = (channel_unit(c3)? * 255.0) as u8;
145 Some(chromashift::Color::Srgb(Srgb::new(r, g, b, alpha)))
146 }
147 ColorSpace::SrgbLinear(_) => {
148 let r = channel_unit(c1)?;
149 let g = channel_unit(c2)?;
150 let b = channel_unit(c3)?;
151 Some(chromashift::Color::LinearRgb(LinearRgb::new(r, g, b, alpha)))
152 }
153 ColorSpace::DisplayP3(_) => {
154 let r = channel_unit(c1)?;
155 let g = channel_unit(c2)?;
156 let b = channel_unit(c3)?;
157 Some(chromashift::Color::DisplayP3(DisplayP3::new(r, g, b, alpha)))
158 }
159 ColorSpace::A98Rgb(_) => {
160 let r = channel_unit(c1)?;
161 let g = channel_unit(c2)?;
162 let b = channel_unit(c3)?;
163 Some(chromashift::Color::A98Rgb(A98Rgb::new(r, g, b, alpha)))
164 }
165 ColorSpace::ProphotoRgb(_) => {
166 let r = channel_unit(c1)?;
167 let g = channel_unit(c2)?;
168 let b = channel_unit(c3)?;
169 Some(chromashift::Color::ProphotoRgb(ProphotoRgb::new(r, g, b, alpha)))
170 }
171 ColorSpace::Rec2020(_) => {
172 let r = channel_unit(c1)?;
173 let g = channel_unit(c2)?;
174 let b = channel_unit(c3)?;
175 Some(chromashift::Color::Rec2020(Rec2020::new(r, g, b, alpha)))
176 }
177 ColorSpace::Xyz(_) | ColorSpace::XyzD65(_) => {
178 let x = channel_unit(c1)? * 100.0;
179 let y = channel_unit(c2)? * 100.0;
180 let z = channel_unit(c3)? * 100.0;
181 Some(chromashift::Color::XyzD65(XyzD65::new(x, y, z, alpha)))
182 }
183 ColorSpace::XyzD50(_) => {
184 let x = channel_unit(c1)? * 100.0;
185 let y = channel_unit(c2)? * 100.0;
186 let z = channel_unit(c3)? * 100.0;
187 Some(chromashift::Color::XyzD50(XyzD50::new(x, y, z, alpha)))
188 }
189 }
190 }
191}
192
193#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
194#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
195#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
196#[derive(csskit_derives::NodeWithMetadata)]
197pub struct ColorFunctionColorParams(
198 pub ColorSpace,
199 pub NoneOr<NumberOrPercentage>,
200 pub NoneOr<NumberOrPercentage>,
201 pub NoneOr<NumberOrPercentage>,
202 pub Option<T![/]>,
203 pub Option<NoneOr<NumberOrPercentage>>,
204);
205
206#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
223#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
224#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
225#[derive(csskit_derives::NodeWithMetadata)]
226pub struct RgbFunction {
227 #[atom(CssAtomSet::Rgb)]
228 pub name: T![Function],
229 pub params: RgbFunctionParams,
230 pub close: T![')'],
231}
232
233#[cfg(feature = "chromashift")]
234impl crate::ToChromashift for RgbFunction {
235 fn to_chromashift(&self) -> Option<chromashift::Color> {
236 self.params.to_chromashift()
237 }
238}
239
240#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
241#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
242#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
243#[derive(csskit_derives::NodeWithMetadata)]
244pub struct RgbaFunction {
245 #[atom(CssAtomSet::Rgba)]
246 pub name: T![Function],
247 pub params: RgbFunctionParams,
248 pub close: T![')'],
249}
250
251#[cfg(feature = "chromashift")]
252impl crate::ToChromashift for RgbaFunction {
253 fn to_chromashift(&self) -> Option<chromashift::Color> {
254 self.params.to_chromashift()
255 }
256}
257
258#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
259#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
260#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
261#[derive(csskit_derives::NodeWithMetadata)]
262pub struct RgbFunctionParams(
263 pub NoneOr<NumberOrPercentage>,
264 pub Option<T![,]>,
265 pub NoneOr<NumberOrPercentage>,
266 pub Option<T![,]>,
267 pub NoneOr<NumberOrPercentage>,
268 pub Option<CommaOrSlash>,
269 pub Option<NoneOr<NumberOrPercentage>>,
270);
271
272#[cfg(feature = "chromashift")]
273impl crate::ToChromashift for RgbFunctionParams {
274 fn to_chromashift(&self) -> Option<chromashift::Color> {
275 use chromashift::Srgb;
276 let Self(red, _, green, _, blue, _, alpha) = &self;
277 let alpha = match alpha {
278 Some(NoneOr::None(_)) => 0.0,
279 Some(NoneOr::Some(NumberOrPercentage::Number(t))) => t.value() * 100.0,
280 Some(NoneOr::Some(NumberOrPercentage::Percentage(t))) => t.value(),
281 None => 100.0,
282 };
283 let red = match red {
284 NoneOr::None(_) => {
285 return None;
286 }
287 NoneOr::Some(NumberOrPercentage::Number(red)) => red.value(),
288 NoneOr::Some(NumberOrPercentage::Percentage(red)) => red.value() / 100.0 * 255.0,
289 } as u8;
290 let green = match green {
291 NoneOr::None(_) => {
292 return None;
293 }
294 NoneOr::Some(NumberOrPercentage::Number(green)) => green.value(),
295 NoneOr::Some(NumberOrPercentage::Percentage(green)) => green.value() / 100.0 * 255.0,
296 } as u8;
297 let blue = match blue {
298 NoneOr::None(_) => {
299 return None;
300 }
301 NoneOr::Some(NumberOrPercentage::Number(blue)) => blue.value(),
302 NoneOr::Some(NumberOrPercentage::Percentage(blue)) => blue.value() / 100.0 * 255.0,
303 } as u8;
304 Some(chromashift::Color::Srgb(Srgb::new(red, green, blue, alpha)))
305 }
306}
307
308#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
327#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
328#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
329#[derive(csskit_derives::NodeWithMetadata)]
330pub struct HslFunction {
331 #[atom(CssAtomSet::Hsl)]
332 pub name: T![Function],
333 pub params: HslFunctionParams,
334 pub close: T![')'],
335}
336
337#[cfg(feature = "chromashift")]
338impl crate::ToChromashift for HslFunction {
339 fn to_chromashift(&self) -> Option<chromashift::Color> {
340 self.params.to_chromashift()
341 }
342}
343
344#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
345#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
346#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
347#[derive(csskit_derives::NodeWithMetadata)]
348pub struct HslaFunction {
349 #[atom(CssAtomSet::Hsla)]
350 pub name: T![Function],
351 pub params: HslFunctionParams,
352 pub close: T![')'],
353}
354
355#[cfg(feature = "chromashift")]
356impl crate::ToChromashift for HslaFunction {
357 fn to_chromashift(&self) -> Option<chromashift::Color> {
358 self.params.to_chromashift()
359 }
360}
361
362#[derive(Parse, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
363#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
364#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
365#[derive(csskit_derives::NodeWithMetadata)]
366pub struct HslFunctionParams(
367 pub NoneOr<AngleOrNumber>,
368 pub Option<T![,]>,
369 pub NoneOr<NumberOrPercentage>,
370 pub Option<T![,]>,
371 pub NoneOr<NumberOrPercentage>,
372 pub Option<CommaOrSlash>,
373 pub Option<NoneOr<NumberOrPercentage>>,
374);
375
376#[cfg(feature = "chromashift")]
377impl crate::ToChromashift for HslFunctionParams {
378 fn to_chromashift(&self) -> Option<chromashift::Color> {
379 use chromashift::Hsl;
380 let Self(hue, _, saturation, _, lightness, _, alpha) = &self;
381 let hue = match hue {
382 NoneOr::None(_) => {
383 return None;
384 }
385 NoneOr::Some(AngleOrNumber::Number(hue)) => hue.value(),
386 NoneOr::Some(AngleOrNumber::Angle(d)) => d.as_degrees(),
387 };
388 let saturation = match saturation {
389 NoneOr::None(_) => {
390 return None;
391 }
392 NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
393 NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value(),
394 };
395 let lightness = match lightness {
396 NoneOr::None(_) => {
397 return None;
398 }
399 NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
400 NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value(),
401 };
402 let alpha = match alpha {
403 Some(NoneOr::None(_)) => 0.0,
404 Some(NoneOr::Some(NumberOrPercentage::Number(t))) => t.value() * 100.0,
405 Some(NoneOr::Some(NumberOrPercentage::Percentage(t))) => t.value(),
406 None => 100.0,
407 };
408 Some(chromashift::Color::Hsl(Hsl::new(hue, saturation, lightness, alpha)))
409 }
410}
411
412#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
419#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
420#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
421#[derive(csskit_derives::NodeWithMetadata)]
422pub struct HwbFunction {
423 #[atom(CssAtomSet::Hwb)]
424 pub name: T![Function],
425 pub params: HwbFunctionParams,
426 pub close: T![')'],
427}
428
429#[cfg(feature = "chromashift")]
430impl crate::ToChromashift for HwbFunction {
431 fn to_chromashift(&self) -> Option<chromashift::Color> {
432 use chromashift::Hwb;
433 let HwbFunctionParams(hue, whiteness, blackness, _, alpha) = &self.params;
434 let hue = match hue {
435 NoneOr::None(_) => {
436 return None;
437 }
438 NoneOr::Some(AngleOrNumber::Number(hue)) => hue.value(),
439 NoneOr::Some(AngleOrNumber::Angle(d)) => d.as_degrees(),
440 };
441 let whiteness = match whiteness {
442 NoneOr::None(_) => {
443 return None;
444 }
445 NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
446 NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value(),
447 };
448 let blackness = match blackness {
449 NoneOr::None(_) => {
450 return None;
451 }
452 NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
453 NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value(),
454 };
455 let alpha = match alpha {
456 Some(NoneOr::None(_)) => 0.0,
457 Some(NoneOr::Some(NumberOrPercentage::Number(t))) => t.value() * 100.0,
458 Some(NoneOr::Some(NumberOrPercentage::Percentage(t))) => t.value(),
459 None => 100.0,
460 };
461 Some(chromashift::Color::Hwb(Hwb::new(hue, whiteness, blackness, alpha)))
462 }
463}
464
465#[derive(Parse, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
466#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
467#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
468#[derive(csskit_derives::NodeWithMetadata)]
469pub struct HwbFunctionParams(
470 pub NoneOr<AngleOrNumber>,
471 pub NoneOr<NumberOrPercentage>,
472 pub NoneOr<NumberOrPercentage>,
473 pub Option<T![/]>,
474 pub Option<NoneOr<NumberOrPercentage>>,
475);
476
477#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
486#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
487#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
488#[derive(csskit_derives::NodeWithMetadata)]
489pub struct LabFunction {
490 #[atom(CssAtomSet::Lab)]
491 pub name: T![Function],
492 pub params: LabFunctionParams,
493 pub close: T![')'],
494}
495
496#[cfg(feature = "chromashift")]
497impl crate::ToChromashift for LabFunction {
498 fn to_chromashift(&self) -> Option<chromashift::Color> {
499 use chromashift::Lab;
500 let LabFunctionParams(l, a, b, _, alpha) = &self.params;
501 let l = match l {
502 NoneOr::None(_) => {
503 return None;
504 }
505 NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
506 NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value(),
507 } as f64;
508 let a = match a {
509 NoneOr::None(_) => {
510 return None;
511 }
512 NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
513 NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value() / 100.0 * 125.0,
514 } as f64;
515 let b = match b {
516 NoneOr::None(_) => {
517 return None;
518 }
519 NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
520 NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value() / 100.0 * 125.0,
521 } as f64;
522 let alpha = match alpha {
523 Some(NoneOr::None(_)) => 0.0,
524 Some(NoneOr::Some(NumberOrPercentage::Number(t))) => t.value() * 100.0,
525 Some(NoneOr::Some(NumberOrPercentage::Percentage(t))) => t.value(),
526 None => 100.0,
527 };
528 Some(chromashift::Color::Lab(Lab::new(l, a, b, alpha)))
529 }
530}
531
532#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
533#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
534#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
535#[derive(csskit_derives::NodeWithMetadata)]
536pub struct LabFunctionParams(
537 pub NoneOr<NumberOrPercentage>,
538 pub NoneOr<NumberOrPercentage>,
539 pub NoneOr<NumberOrPercentage>,
540 pub Option<T![/]>,
541 pub Option<NoneOr<NumberOrPercentage>>,
542);
543
544#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
553#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
554#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
555#[derive(csskit_derives::NodeWithMetadata)]
556pub struct LchFunction {
557 #[atom(CssAtomSet::Lch)]
558 pub name: T![Function],
559 pub params: LchFunctionParams,
560 pub close: T![')'],
561}
562
563#[cfg(feature = "chromashift")]
564impl crate::ToChromashift for LchFunction {
565 fn to_chromashift(&self) -> Option<chromashift::Color> {
566 use chromashift::Lch;
567 let LchFunctionParams(lightness, chroma, hue, _, alpha) = &self.params;
568 let lightness = match lightness {
569 NoneOr::None(_) => {
570 return None;
571 }
572 NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
573 NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value(),
574 } as f64;
575 let chroma = match chroma {
576 NoneOr::None(_) => {
577 return None;
578 }
579 NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
580 NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value() / 100.0 * 150.0,
581 } as f64;
582 let hue = match hue {
583 NoneOr::None(_) => {
584 return None;
585 }
586 NoneOr::Some(AngleOrNumber::Number(hue)) => hue.value(),
587 NoneOr::Some(AngleOrNumber::Angle(d)) => d.as_degrees(),
588 } as f64;
589 let alpha = match alpha {
590 Some(NoneOr::None(_)) => 0.0,
591 Some(NoneOr::Some(NumberOrPercentage::Number(t))) => t.value() * 100.0,
592 Some(NoneOr::Some(NumberOrPercentage::Percentage(t))) => t.value(),
593 None => 100.0,
594 };
595 Some(chromashift::Color::Lch(Lch::new(lightness, chroma, hue, alpha)))
596 }
597}
598
599#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
600#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
601#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
602#[derive(csskit_derives::NodeWithMetadata)]
603pub struct LchFunctionParams(
604 pub NoneOr<NumberOrPercentage>,
605 pub NoneOr<NumberOrPercentage>,
606 pub NoneOr<AngleOrNumber>,
607 pub Option<T![/]>,
608 pub Option<NoneOr<NumberOrPercentage>>,
609);
610
611#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
620#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
621#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
622#[derive(csskit_derives::NodeWithMetadata)]
623pub struct OklabFunction {
624 #[atom(CssAtomSet::Oklab)]
625 pub name: T![Function],
626 pub params: LabFunctionParams,
627 pub close: T![')'],
628}
629
630#[cfg(feature = "chromashift")]
631impl crate::ToChromashift for OklabFunction {
632 fn to_chromashift(&self) -> Option<chromashift::Color> {
633 use chromashift::Oklab;
634 let LabFunctionParams(l, a, b, _, alpha) = &self.params;
635 let alpha = match alpha {
636 Some(NoneOr::None(_)) => 0.0,
637 Some(NoneOr::Some(NumberOrPercentage::Number(t))) => t.value() * 100.0,
638 Some(NoneOr::Some(NumberOrPercentage::Percentage(t))) => t.value(),
639 None => 100.0,
640 };
641 let l = match l {
642 NoneOr::None(_) => {
643 return None;
644 }
645 NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
646 NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value() / 100.0,
647 } as f64;
648 let a = match a {
649 NoneOr::None(_) => {
650 return None;
651 }
652 NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
653 NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value() / 100.0 * 0.4,
654 } as f64;
655 let b = match b {
656 NoneOr::None(_) => {
657 return None;
658 }
659 NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
660 NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value() / 100.0 * 0.4,
661 } as f64;
662 Some(chromashift::Color::Oklab(Oklab::new(l, a, b, alpha)))
663 }
664}
665
666#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
675#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
676#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
677#[derive(csskit_derives::NodeWithMetadata)]
678pub struct OklchFunction {
679 #[atom(CssAtomSet::Oklch)]
680 pub name: T![Function],
681 pub params: LchFunctionParams,
682 pub close: T![')'],
683}
684
685#[cfg(feature = "chromashift")]
686impl crate::ToChromashift for OklchFunction {
687 fn to_chromashift(&self) -> Option<chromashift::Color> {
688 use chromashift::Oklch;
689 let LchFunctionParams(lightness, chroma, hue, _, alpha) = &self.params;
690 let lightness = match lightness {
691 NoneOr::None(_) => {
692 return None;
693 }
694 NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
695 NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value(),
696 } as f64;
697 let chroma = match chroma {
698 NoneOr::None(_) => {
699 return None;
700 }
701 NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
702 NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value() / 100.0 * 150.0,
703 } as f64;
704 let hue = match hue {
705 NoneOr::None(_) => {
706 return None;
707 }
708 NoneOr::Some(AngleOrNumber::Number(hue)) => hue.value(),
709 NoneOr::Some(AngleOrNumber::Angle(d)) => d.as_degrees(),
710 } as f64;
711 let alpha = match alpha {
712 Some(NoneOr::None(_)) => 0.0,
713 Some(NoneOr::Some(NumberOrPercentage::Number(t))) => t.value() * 100.0,
714 Some(NoneOr::Some(NumberOrPercentage::Percentage(t))) => t.value(),
715 None => 100.0,
716 };
717 Some(chromashift::Color::Oklch(Oklch::new(lightness, chroma, hue, alpha)))
718 }
719}
720
721#[cfg(test)]
722mod tests {
723 use super::*;
724
725 #[test]
726 fn size_test() {
727 assert_eq!(std::mem::size_of::<ColorFunction<'_>>(), 144);
728 assert_eq!(std::mem::size_of::<ColorFunctionColor>(), 120);
729 assert_eq!(std::mem::size_of::<RgbFunction>(), 136);
730 assert_eq!(std::mem::size_of::<RgbaFunction>(), 136);
731 assert_eq!(std::mem::size_of::<HslFunction>(), 136);
732 assert_eq!(std::mem::size_of::<HslaFunction>(), 136);
733 assert_eq!(std::mem::size_of::<HwbFunction>(), 104);
734 assert_eq!(std::mem::size_of::<LabFunction>(), 104);
735 assert_eq!(std::mem::size_of::<LchFunction>(), 104);
736 assert_eq!(std::mem::size_of::<OklabFunction>(), 104);
737 assert_eq!(std::mem::size_of::<OklchFunction>(), 104);
738 }
739}