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)]
422#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
423#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
424#[derive(csskit_derives::NodeWithMetadata)]
425pub struct HwbFunction {
426 #[atom(CssAtomSet::Hwb)]
427 pub name: T![Function],
428 pub params: HwbFunctionParams,
429 pub close: T![')'],
430}
431
432#[cfg(feature = "chromashift")]
433impl crate::ToChromashift for HwbFunction {
434 fn to_chromashift(&self) -> Option<chromashift::Color> {
435 use chromashift::Hwb;
436 let HwbFunctionParams(hue, whiteness, blackness, _, alpha) = &self.params;
437 let hue = match hue {
438 NoneOr::None(_) => {
439 return None;
440 }
441 NoneOr::Some(AngleOrNumber::Number(hue)) => hue.value(),
442 NoneOr::Some(AngleOrNumber::Angle(d)) => d.as_degrees(),
443 };
444 let whiteness = match whiteness {
445 NoneOr::None(_) => {
446 return None;
447 }
448 NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
449 NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value(),
450 };
451 let blackness = match blackness {
452 NoneOr::None(_) => {
453 return None;
454 }
455 NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
456 NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value(),
457 };
458 let alpha = match alpha {
459 Some(NoneOr::None(_)) => 0.0,
460 Some(NoneOr::Some(NumberOrPercentage::Number(t))) => t.value() * 100.0,
461 Some(NoneOr::Some(NumberOrPercentage::Percentage(t))) => t.value(),
462 None => 100.0,
463 };
464 Some(chromashift::Color::Hwb(Hwb::new(hue, whiteness, blackness, alpha)))
465 }
466}
467
468#[derive(Parse, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
469#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
470#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
471#[derive(csskit_derives::NodeWithMetadata)]
472pub struct HwbFunctionParams(
473 pub NoneOr<AngleOrNumber>,
474 pub NoneOr<NumberOrPercentage>,
475 pub NoneOr<NumberOrPercentage>,
476 pub Option<T![/]>,
477 pub Option<NoneOr<NumberOrPercentage>>,
478);
479
480#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
489#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
490#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
491#[derive(csskit_derives::NodeWithMetadata)]
492pub struct LabFunction {
493 #[atom(CssAtomSet::Lab)]
494 pub name: T![Function],
495 pub params: LabFunctionParams,
496 pub close: T![')'],
497}
498
499#[cfg(feature = "chromashift")]
500impl crate::ToChromashift for LabFunction {
501 fn to_chromashift(&self) -> Option<chromashift::Color> {
502 use chromashift::Lab;
503 let LabFunctionParams(l, a, b, _, alpha) = &self.params;
504 let l = match l {
505 NoneOr::None(_) => {
506 return None;
507 }
508 NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
509 NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value(),
510 } as f64;
511 let a = match a {
512 NoneOr::None(_) => {
513 return None;
514 }
515 NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
516 NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value() / 100.0 * 125.0,
517 } as f64;
518 let b = match b {
519 NoneOr::None(_) => {
520 return None;
521 }
522 NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
523 NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value() / 100.0 * 125.0,
524 } as f64;
525 let alpha = match alpha {
526 Some(NoneOr::None(_)) => 0.0,
527 Some(NoneOr::Some(NumberOrPercentage::Number(t))) => t.value() * 100.0,
528 Some(NoneOr::Some(NumberOrPercentage::Percentage(t))) => t.value(),
529 None => 100.0,
530 };
531 Some(chromashift::Color::Lab(Lab::new(l, a, b, alpha)))
532 }
533}
534
535#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
536#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
537#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
538#[derive(csskit_derives::NodeWithMetadata)]
539pub struct LabFunctionParams(
540 pub NoneOr<NumberOrPercentage>,
541 pub NoneOr<NumberOrPercentage>,
542 pub NoneOr<NumberOrPercentage>,
543 pub Option<T![/]>,
544 pub Option<NoneOr<NumberOrPercentage>>,
545);
546
547#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
556#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
557#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
558#[derive(csskit_derives::NodeWithMetadata)]
559pub struct LchFunction {
560 #[atom(CssAtomSet::Lch)]
561 pub name: T![Function],
562 pub params: LchFunctionParams,
563 pub close: T![')'],
564}
565
566#[cfg(feature = "chromashift")]
567impl crate::ToChromashift for LchFunction {
568 fn to_chromashift(&self) -> Option<chromashift::Color> {
569 use chromashift::Lch;
570 let LchFunctionParams(lightness, chroma, hue, _, alpha) = &self.params;
571 let lightness = match lightness {
572 NoneOr::None(_) => {
573 return None;
574 }
575 NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
576 NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value(),
577 } as f64;
578 let chroma = match chroma {
579 NoneOr::None(_) => {
580 return None;
581 }
582 NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
583 NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value() / 100.0 * 150.0,
584 } as f64;
585 let hue = match hue {
586 NoneOr::None(_) => {
587 return None;
588 }
589 NoneOr::Some(AngleOrNumber::Number(hue)) => hue.value(),
590 NoneOr::Some(AngleOrNumber::Angle(d)) => d.as_degrees(),
591 } as f64;
592 let alpha = match alpha {
593 Some(NoneOr::None(_)) => 0.0,
594 Some(NoneOr::Some(NumberOrPercentage::Number(t))) => t.value() * 100.0,
595 Some(NoneOr::Some(NumberOrPercentage::Percentage(t))) => t.value(),
596 None => 100.0,
597 };
598 Some(chromashift::Color::Lch(Lch::new(lightness, chroma, hue, alpha)))
599 }
600}
601
602#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
603#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
604#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
605#[derive(csskit_derives::NodeWithMetadata)]
606pub struct LchFunctionParams(
607 pub NoneOr<NumberOrPercentage>,
608 pub NoneOr<NumberOrPercentage>,
609 pub NoneOr<AngleOrNumber>,
610 pub Option<T![/]>,
611 pub Option<NoneOr<NumberOrPercentage>>,
612);
613
614#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
623#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
624#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
625#[derive(csskit_derives::NodeWithMetadata)]
626pub struct OklabFunction {
627 #[atom(CssAtomSet::Oklab)]
628 pub name: T![Function],
629 pub params: LabFunctionParams,
630 pub close: T![')'],
631}
632
633#[cfg(feature = "chromashift")]
634impl crate::ToChromashift for OklabFunction {
635 fn to_chromashift(&self) -> Option<chromashift::Color> {
636 use chromashift::Oklab;
637 let LabFunctionParams(l, a, b, _, alpha) = &self.params;
638 let alpha = match alpha {
639 Some(NoneOr::None(_)) => 0.0,
640 Some(NoneOr::Some(NumberOrPercentage::Number(t))) => t.value() * 100.0,
641 Some(NoneOr::Some(NumberOrPercentage::Percentage(t))) => t.value(),
642 None => 100.0,
643 };
644 let l = match l {
645 NoneOr::None(_) => {
646 return None;
647 }
648 NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
649 NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value() / 100.0,
650 } as f64;
651 let a = match a {
652 NoneOr::None(_) => {
653 return None;
654 }
655 NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
656 NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value() / 100.0 * 0.4,
657 } as f64;
658 let b = match b {
659 NoneOr::None(_) => {
660 return None;
661 }
662 NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
663 NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value() / 100.0 * 0.4,
664 } as f64;
665 Some(chromashift::Color::Oklab(Oklab::new(l, a, b, alpha)))
666 }
667}
668
669#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
678#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
679#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
680#[derive(csskit_derives::NodeWithMetadata)]
681pub struct OklchFunction {
682 #[atom(CssAtomSet::Oklch)]
683 pub name: T![Function],
684 pub params: LchFunctionParams,
685 pub close: T![')'],
686}
687
688#[cfg(feature = "chromashift")]
689impl crate::ToChromashift for OklchFunction {
690 fn to_chromashift(&self) -> Option<chromashift::Color> {
691 use chromashift::Oklch;
692 let LchFunctionParams(lightness, chroma, hue, _, alpha) = &self.params;
693 let lightness = match lightness {
694 NoneOr::None(_) => {
695 return None;
696 }
697 NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
698 NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value(),
699 } as f64;
700 let chroma = match chroma {
701 NoneOr::None(_) => {
702 return None;
703 }
704 NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
705 NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value() / 100.0 * 150.0,
706 } as f64;
707 let hue = match hue {
708 NoneOr::None(_) => {
709 return None;
710 }
711 NoneOr::Some(AngleOrNumber::Number(hue)) => hue.value(),
712 NoneOr::Some(AngleOrNumber::Angle(d)) => d.as_degrees(),
713 } as f64;
714 let alpha = match alpha {
715 Some(NoneOr::None(_)) => 0.0,
716 Some(NoneOr::Some(NumberOrPercentage::Number(t))) => t.value() * 100.0,
717 Some(NoneOr::Some(NumberOrPercentage::Percentage(t))) => t.value(),
718 None => 100.0,
719 };
720 Some(chromashift::Color::Oklch(Oklch::new(lightness, chroma, hue, alpha)))
721 }
722}
723
724#[cfg(test)]
725mod tests {
726 use super::*;
727
728 #[test]
729 fn size_test() {
730 assert_eq!(std::mem::size_of::<ColorFunction<'_>>(), 144);
731 assert_eq!(std::mem::size_of::<ColorFunctionColor>(), 120);
732 assert_eq!(std::mem::size_of::<RgbFunction>(), 136);
733 assert_eq!(std::mem::size_of::<RgbaFunction>(), 136);
734 assert_eq!(std::mem::size_of::<HslFunction>(), 136);
735 assert_eq!(std::mem::size_of::<HslaFunction>(), 136);
736 assert_eq!(std::mem::size_of::<HwbFunction>(), 104);
737 assert_eq!(std::mem::size_of::<LabFunction>(), 104);
738 assert_eq!(std::mem::size_of::<LchFunction>(), 104);
739 assert_eq!(std::mem::size_of::<OklabFunction>(), 104);
740 assert_eq!(std::mem::size_of::<OklchFunction>(), 104);
741 }
742}