css_ast/functions/
color_function.rs

1use super::prelude::*;
2use crate::{AngleOrNumber, NoneOr, NumberOrPercentage};
3
4#[derive(Parse, Peek, IntoCursor, ToCursors, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
5#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
6#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
7pub enum ColorSpace {
8	#[atom(CssAtomSet::Srgb)]
9	Srgb(T![Ident]),
10	#[atom(CssAtomSet::SrgbLinear)]
11	SrgbLinear(T![Ident]),
12	#[atom(CssAtomSet::DisplayP3)]
13	DisplayP3(T![Ident]),
14	#[atom(CssAtomSet::A98Rgb)]
15	A98Rgb(T![Ident]),
16	#[atom(CssAtomSet::ProphotoRgb)]
17	ProphotoRgb(T![Ident]),
18	#[atom(CssAtomSet::Rec2020)]
19	Rec2020(T![Ident]),
20	#[atom(CssAtomSet::Xyz)]
21	Xyz(T![Ident]),
22	#[atom(CssAtomSet::XyzD50)]
23	XyzD50(T![Ident]),
24	#[atom(CssAtomSet::XyzD65)]
25	XyzD65(T![Ident]),
26}
27
28#[derive(IntoCursor, ToCursors, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
29#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
30#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
31pub struct CommaOrSlash(Cursor);
32
33impl<'a> Peek<'a> for CommaOrSlash {
34	fn peek<I>(_: &Parser<'a, I>, c: Cursor) -> bool
35	where
36		I: Iterator<Item = Cursor> + Clone,
37	{
38		c == ',' || c == '/'
39	}
40}
41
42impl<'a> Parse<'a> for CommaOrSlash {
43	fn parse<I>(p: &mut Parser<'a, I>) -> ParserResult<Self>
44	where
45		I: Iterator<Item = Cursor> + Clone,
46	{
47		if !p.peek::<Self>() {
48			Err(Diagnostic::new(p.next(), Diagnostic::unexpected))?
49		}
50		Ok(Self(p.next()))
51	}
52}
53
54/// <https://drafts.csswg.org/css-color/#typedef-color-function>
55#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
56#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
57#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
58pub enum ColorFunction {
59	Color(ColorFunctionColor),
60	Rgb(RgbFunction),
61	Rgba(RgbaFunction),
62	Hsl(HslFunction),
63	Hsla(HslaFunction),
64	Hwb(HwbFunction),
65	Lab(LabFunction),
66	Lch(LchFunction),
67	Oklab(OklabFunction),
68	Oklch(OklchFunction),
69}
70
71#[cfg(feature = "chromashift")]
72impl crate::ToChromashift for ColorFunction {
73	fn to_chromashift(&self) -> Option<chromashift::Color> {
74		match self {
75			Self::Color(c) => c.to_chromashift(),
76			Self::Rgb(c) => c.to_chromashift(),
77			Self::Rgba(c) => c.to_chromashift(),
78			Self::Hsl(c) => c.to_chromashift(),
79			Self::Hsla(c) => c.to_chromashift(),
80			Self::Hwb(c) => c.to_chromashift(),
81			Self::Lab(c) => c.to_chromashift(),
82			Self::Lch(c) => c.to_chromashift(),
83			Self::Oklab(c) => c.to_chromashift(),
84			Self::Oklch(c) => c.to_chromashift(),
85		}
86	}
87}
88
89/// <https://drafts.csswg.org/css-color/#funcdef-color>
90///
91/// ```text,ignore
92/// color() = color( <colorspace-params> [ / [ <alpha-value> | none ] ]? )
93/// <colorspace-params> = [ <predefined-rgb-params> | <xyz-params>]
94/// <predefined-rgb-params> = <predefined-rgb> [ <number> | <percentage> | none ]{3}
95/// <predefined-rgb> = srgb | srgb-linear | display-p3 | a98-rgb | prophoto-rgb | rec2020
96/// <xyz-params> = <xyz-space> [ <number> | <percentage> | none ]{3}
97/// <xyz-space> = xyz | xyz-d50 | xyz-d65
98/// ```
99#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
100#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
101#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
102pub struct ColorFunctionColor {
103	#[atom(CssAtomSet::Color)]
104	pub name: T![Function],
105	pub params: ColorFunctionColorParams,
106	pub close: T![')'],
107}
108
109#[cfg(feature = "chromashift")]
110impl crate::ToChromashift for ColorFunctionColor {
111	fn to_chromashift(&self) -> Option<chromashift::Color> {
112		todo!();
113	}
114}
115
116#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
117#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
118#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
119pub struct ColorFunctionColorParams(
120	pub ColorSpace,
121	pub NoneOr<NumberOrPercentage>,
122	pub NoneOr<NumberOrPercentage>,
123	pub NoneOr<NumberOrPercentage>,
124	pub Option<T![/]>,
125	pub Option<NoneOr<NumberOrPercentage>>,
126);
127
128/// <https://drafts.csswg.org/css-color/#funcdef-rgb>
129///
130/// ```text,ignore
131/// rgb() = [ <legacy-rgb-syntax> | <modern-rgb-syntax> ]
132/// rgba() = [ <legacy-rgba-syntax> | <modern-rgba-syntax> ]
133/// <legacy-rgb-syntax> =   rgb( <percentage>#{3} , <alpha-value>? ) |
134///                   rgb( <number>#{3} , <alpha-value>? )
135/// <legacy-rgba-syntax> = rgba( <percentage>#{3} , <alpha-value>? ) |
136///                   rgba( <number>#{3} , <alpha-value>? )
137/// <modern-rgb-syntax> = rgb(
138///   [ <number> | <percentage> | none]{3}
139///   [ / [<alpha-value> | none] ]?  )
140/// <modern-rgba-syntax> = rgba(
141///   [ <number> | <percentage> | none]{3}
142///   [ / [<alpha-value> | none] ]?  )
143/// ```
144#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
145#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
146#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
147pub struct RgbFunction {
148	#[atom(CssAtomSet::Rgb)]
149	pub name: T![Function],
150	pub params: RgbFunctionParams,
151	pub close: T![')'],
152}
153
154#[cfg(feature = "chromashift")]
155impl crate::ToChromashift for RgbFunction {
156	fn to_chromashift(&self) -> Option<chromashift::Color> {
157		self.params.to_chromashift()
158	}
159}
160
161#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
162#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
163#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
164pub struct RgbaFunction {
165	#[atom(CssAtomSet::Rgba)]
166	pub name: T![Function],
167	pub params: RgbFunctionParams,
168	pub close: T![')'],
169}
170
171#[cfg(feature = "chromashift")]
172impl crate::ToChromashift for RgbaFunction {
173	fn to_chromashift(&self) -> Option<chromashift::Color> {
174		self.params.to_chromashift()
175	}
176}
177
178#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
179#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
180#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
181pub struct RgbFunctionParams(
182	pub NoneOr<NumberOrPercentage>,
183	pub Option<T![,]>,
184	pub NoneOr<NumberOrPercentage>,
185	pub Option<T![,]>,
186	pub NoneOr<NumberOrPercentage>,
187	pub Option<CommaOrSlash>,
188	pub Option<NoneOr<NumberOrPercentage>>,
189);
190
191#[cfg(feature = "chromashift")]
192impl crate::ToChromashift for RgbFunctionParams {
193	fn to_chromashift(&self) -> Option<chromashift::Color> {
194		use chromashift::Srgb;
195		let Self(red, _, green, _, blue, _, alpha) = &self;
196		let alpha = match alpha {
197			Some(NoneOr::None(_)) => 0.0,
198			Some(NoneOr::Some(NumberOrPercentage::Number(t))) => t.value() * 100.0,
199			Some(NoneOr::Some(NumberOrPercentage::Percentage(t))) => t.value(),
200			None => 100.0,
201		};
202		let red = match red {
203			NoneOr::None(_) => {
204				return None;
205			}
206			NoneOr::Some(NumberOrPercentage::Number(red)) => red.value(),
207			NoneOr::Some(NumberOrPercentage::Percentage(red)) => red.value() / 100.0 * 255.0,
208		} as u8;
209		let green = match green {
210			NoneOr::None(_) => {
211				return None;
212			}
213			NoneOr::Some(NumberOrPercentage::Number(green)) => green.value(),
214			NoneOr::Some(NumberOrPercentage::Percentage(green)) => green.value() / 100.0 * 255.0,
215		} as u8;
216		let blue = match blue {
217			NoneOr::None(_) => {
218				return None;
219			}
220			NoneOr::Some(NumberOrPercentage::Number(blue)) => blue.value(),
221			NoneOr::Some(NumberOrPercentage::Percentage(blue)) => blue.value() / 100.0 * 255.0,
222		} as u8;
223		Some(chromashift::Color::Srgb(Srgb::new(red, green, blue, alpha)))
224	}
225}
226
227/// <https://drafts.csswg.org/css-color/#funcdef-hsl>
228///
229/// ```text,ignore
230/// hsl() = [ <legacy-hsl-syntax> | <modern-hsl-syntax> ]
231/// hsla() = [ <legacy-hsla-syntax> | <modern-hsla-syntax> ]
232/// <modern-hsl-syntax> = hsl(
233///     [<hue> | none]
234///     [<percentage> | <number> | none]
235///     [<percentage> | <number> | none]
236///     [ / [<alpha-value> | none] ]? )
237/// <modern-hsla-syntax> = hsla(
238///     [<hue> | none]
239///     [<percentage> | <number> | none]
240///     [<percentage> | <number> | none]
241///     [ / [<alpha-value> | none] ]? )
242/// <legacy-hsl-syntax> = hsl( <hue>, <percentage>, <percentage>, <alpha-value>? )
243/// <legacy-hsla-syntax> = hsla( <hue>, <percentage>, <percentage>, <alpha-value>? )
244/// ```
245#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
246#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
247#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
248pub struct HslFunction {
249	#[atom(CssAtomSet::Hsl)]
250	pub name: T![Function],
251	pub params: HslFunctionParams,
252	pub close: T![')'],
253}
254
255#[cfg(feature = "chromashift")]
256impl crate::ToChromashift for HslFunction {
257	fn to_chromashift(&self) -> Option<chromashift::Color> {
258		self.params.to_chromashift()
259	}
260}
261
262#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
263#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
264#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
265pub struct HslaFunction {
266	#[atom(CssAtomSet::Hsla)]
267	pub name: T![Function],
268	pub params: HslFunctionParams,
269	pub close: T![')'],
270}
271
272#[cfg(feature = "chromashift")]
273impl crate::ToChromashift for HslaFunction {
274	fn to_chromashift(&self) -> Option<chromashift::Color> {
275		self.params.to_chromashift()
276	}
277}
278
279#[derive(Parse, 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))]
282pub struct HslFunctionParams(
283	pub NoneOr<AngleOrNumber>,
284	pub Option<T![,]>,
285	pub NoneOr<NumberOrPercentage>,
286	pub Option<T![,]>,
287	pub NoneOr<NumberOrPercentage>,
288	pub Option<CommaOrSlash>,
289	pub Option<NoneOr<NumberOrPercentage>>,
290);
291
292#[cfg(feature = "chromashift")]
293impl crate::ToChromashift for HslFunctionParams {
294	fn to_chromashift(&self) -> Option<chromashift::Color> {
295		use chromashift::Hsl;
296		let Self(hue, _, saturation, _, lightness, _, alpha) = &self;
297		let hue = match hue {
298			NoneOr::None(_) => {
299				return None;
300			}
301			NoneOr::Some(AngleOrNumber::Number(hue)) => hue.value(),
302			NoneOr::Some(AngleOrNumber::Angle(d)) => d.as_degrees(),
303		};
304		let saturation = match saturation {
305			NoneOr::None(_) => {
306				return None;
307			}
308			NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
309			NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value(),
310		};
311		let lightness = match lightness {
312			NoneOr::None(_) => {
313				return None;
314			}
315			NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
316			NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value(),
317		};
318		let alpha = match alpha {
319			Some(NoneOr::None(_)) => 0.0,
320			Some(NoneOr::Some(NumberOrPercentage::Number(t))) => t.value() * 100.0,
321			Some(NoneOr::Some(NumberOrPercentage::Percentage(t))) => t.value(),
322			None => 100.0,
323		};
324		Some(chromashift::Color::Hsl(Hsl::new(hue, saturation, lightness, alpha)))
325	}
326}
327
328// https://drafts.csswg.org/css-color/#funcdef-hwb
329// hwb() = hwb(
330//  [<hue> | none]
331//  [<percentage> | <number> | none]
332//  [<percentage> | <number> | none]
333//  [ / [<alpha-value> | none] ]? )
334#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
335#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
336#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
337pub struct HwbFunction {
338	#[atom(CssAtomSet::Hwb)]
339	pub name: T![Function],
340	pub params: HwbFunctionParams,
341	pub close: T![')'],
342}
343
344#[cfg(feature = "chromashift")]
345impl crate::ToChromashift for HwbFunction {
346	fn to_chromashift(&self) -> Option<chromashift::Color> {
347		use chromashift::Hwb;
348		let HwbFunctionParams(hue, whiteness, blackness, _, alpha) = &self.params;
349		let hue = match hue {
350			NoneOr::None(_) => {
351				return None;
352			}
353			NoneOr::Some(AngleOrNumber::Number(hue)) => hue.value(),
354			NoneOr::Some(AngleOrNumber::Angle(d)) => d.as_degrees(),
355		};
356		let whiteness = match whiteness {
357			NoneOr::None(_) => {
358				return None;
359			}
360			NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
361			NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value(),
362		};
363		let blackness = match blackness {
364			NoneOr::None(_) => {
365				return None;
366			}
367			NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
368			NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value(),
369		};
370		let alpha = match alpha {
371			Some(NoneOr::None(_)) => 0.0,
372			Some(NoneOr::Some(NumberOrPercentage::Number(t))) => t.value() * 100.0,
373			Some(NoneOr::Some(NumberOrPercentage::Percentage(t))) => t.value(),
374			None => 100.0,
375		};
376		Some(chromashift::Color::Hwb(Hwb::new(hue, whiteness, blackness, alpha)))
377	}
378}
379
380#[derive(Parse, 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))]
383pub struct HwbFunctionParams(
384	pub NoneOr<AngleOrNumber>,
385	pub NoneOr<NumberOrPercentage>,
386	pub NoneOr<NumberOrPercentage>,
387	pub Option<T![/]>,
388	pub Option<NoneOr<NumberOrPercentage>>,
389);
390
391/// <https://drafts.csswg.org/css-color/#funcdef-lab>
392///
393/// ```text,ignore
394/// lab() = lab( [<percentage> | <number> | none]
395///  [ <percentage> | <number> | none]
396///  [ <percentage> | <number> | none]
397///  [ / [<alpha-value> | none] ]? )
398/// ```
399#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
400#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
401#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
402pub struct LabFunction {
403	#[atom(CssAtomSet::Lab)]
404	pub name: T![Function],
405	pub params: LabFunctionParams,
406	pub close: T![')'],
407}
408
409#[cfg(feature = "chromashift")]
410impl crate::ToChromashift for LabFunction {
411	fn to_chromashift(&self) -> Option<chromashift::Color> {
412		use chromashift::Lab;
413		let LabFunctionParams(l, a, b, _, alpha) = &self.params;
414		let l = match l {
415			NoneOr::None(_) => {
416				return None;
417			}
418			NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
419			NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value(),
420		} as f64;
421		let a = match a {
422			NoneOr::None(_) => {
423				return None;
424			}
425			NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
426			NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value() / 100.0 * 125.0,
427		} as f64;
428		let b = match b {
429			NoneOr::None(_) => {
430				return None;
431			}
432			NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
433			NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value() / 100.0 * 125.0,
434		} as f64;
435		let alpha = match alpha {
436			Some(NoneOr::None(_)) => 0.0,
437			Some(NoneOr::Some(NumberOrPercentage::Number(t))) => t.value() * 100.0,
438			Some(NoneOr::Some(NumberOrPercentage::Percentage(t))) => t.value(),
439			None => 100.0,
440		};
441		Some(chromashift::Color::Lab(Lab::new(l, a, b, alpha)))
442	}
443}
444
445#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
446#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
447#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
448pub struct LabFunctionParams(
449	pub NoneOr<NumberOrPercentage>,
450	pub NoneOr<NumberOrPercentage>,
451	pub NoneOr<NumberOrPercentage>,
452	pub Option<T![/]>,
453	pub Option<NoneOr<NumberOrPercentage>>,
454);
455
456/// <https://drafts.csswg.org/css-color/#funcdef-lch>
457///
458/// ```text,ignore
459/// lch() = lch( [<percentage> | <number> | none]
460///  [ <percentage> | <number> | none]
461///  [ <hue> | none]
462///  [ / [<alpha-value> | none] ]? )
463/// ```
464#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
465#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
466#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
467pub struct LchFunction {
468	#[atom(CssAtomSet::Lch)]
469	pub name: T![Function],
470	pub params: LchFunctionParams,
471	pub close: T![')'],
472}
473
474#[cfg(feature = "chromashift")]
475impl crate::ToChromashift for LchFunction {
476	fn to_chromashift(&self) -> Option<chromashift::Color> {
477		use chromashift::Lch;
478		let LchFunctionParams(lightness, chroma, hue, _, alpha) = &self.params;
479		let lightness = match lightness {
480			NoneOr::None(_) => {
481				return None;
482			}
483			NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
484			NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value(),
485		} as f64;
486		let chroma = match chroma {
487			NoneOr::None(_) => {
488				return None;
489			}
490			NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
491			NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value() / 100.0 * 150.0,
492		} as f64;
493		let hue = match hue {
494			NoneOr::None(_) => {
495				return None;
496			}
497			NoneOr::Some(AngleOrNumber::Number(hue)) => hue.value(),
498			NoneOr::Some(AngleOrNumber::Angle(d)) => d.as_degrees(),
499		} as f64;
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::Lch(Lch::new(lightness, chroma, hue, alpha)))
507	}
508}
509
510#[derive(Parse, Peek, 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))]
513pub struct LchFunctionParams(
514	pub NoneOr<NumberOrPercentage>,
515	pub NoneOr<NumberOrPercentage>,
516	pub NoneOr<AngleOrNumber>,
517	pub Option<T![/]>,
518	pub Option<NoneOr<NumberOrPercentage>>,
519);
520
521/// <https://drafts.csswg.org/css-color/#funcdef-oklab>
522///
523/// ```text,ignore
524/// oklab() = oklab( [ <percentage> | <number> | none]
525///  [ <percentage> | <number> | none]
526///  [ <percentage> | <number> | none]
527///  [ / [<alpha-value> | none] ]? )
528///  ```
529#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
530#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
531#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
532pub struct OklabFunction {
533	#[atom(CssAtomSet::Oklab)]
534	pub name: T![Function],
535	pub params: LabFunctionParams,
536	pub close: T![')'],
537}
538
539#[cfg(feature = "chromashift")]
540impl crate::ToChromashift for OklabFunction {
541	fn to_chromashift(&self) -> Option<chromashift::Color> {
542		use chromashift::Oklab;
543		let LabFunctionParams(l, a, b, _, alpha) = &self.params;
544		let alpha = match alpha {
545			Some(NoneOr::None(_)) => 0.0,
546			Some(NoneOr::Some(NumberOrPercentage::Number(t))) => t.value() * 100.0,
547			Some(NoneOr::Some(NumberOrPercentage::Percentage(t))) => t.value(),
548			None => 100.0,
549		};
550		let l = match l {
551			NoneOr::None(_) => {
552				return None;
553			}
554			NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
555			NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value() / 100.0,
556		} as f64;
557		let a = match a {
558			NoneOr::None(_) => {
559				return None;
560			}
561			NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
562			NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value() / 100.0 * 0.4,
563		} as f64;
564		let b = match b {
565			NoneOr::None(_) => {
566				return None;
567			}
568			NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
569			NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value() / 100.0 * 0.4,
570		} as f64;
571		Some(chromashift::Color::Oklab(Oklab::new(l, a, b, alpha)))
572	}
573}
574
575/// <https://drafts.csswg.org/css-color/#funcdef-oklch>
576///
577/// ```text,ignore
578/// oklab() = oklab( [ <percentage> | <number> | none]
579///  [ <percentage> | <number> | none]
580///  [ <percentage> | <number> | none]
581///  [ / [<alpha-value> | none] ]? )
582///  ```
583#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
584#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
585#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
586pub struct OklchFunction {
587	#[atom(CssAtomSet::Oklch)]
588	pub name: T![Function],
589	pub params: LchFunctionParams,
590	pub close: T![')'],
591}
592
593#[cfg(feature = "chromashift")]
594impl crate::ToChromashift for OklchFunction {
595	fn to_chromashift(&self) -> Option<chromashift::Color> {
596		use chromashift::Oklch;
597		let LchFunctionParams(lightness, chroma, hue, _, alpha) = &self.params;
598		let lightness = match lightness {
599			NoneOr::None(_) => {
600				return None;
601			}
602			NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
603			NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value(),
604		} as f64;
605		let chroma = match chroma {
606			NoneOr::None(_) => {
607				return None;
608			}
609			NoneOr::Some(NumberOrPercentage::Number(n)) => n.value(),
610			NoneOr::Some(NumberOrPercentage::Percentage(p)) => p.value() / 100.0 * 150.0,
611		} as f64;
612		let hue = match hue {
613			NoneOr::None(_) => {
614				return None;
615			}
616			NoneOr::Some(AngleOrNumber::Number(hue)) => hue.value(),
617			NoneOr::Some(AngleOrNumber::Angle(d)) => d.as_degrees(),
618		} as f64;
619		let alpha = match alpha {
620			Some(NoneOr::None(_)) => 0.0,
621			Some(NoneOr::Some(NumberOrPercentage::Number(t))) => t.value() * 100.0,
622			Some(NoneOr::Some(NumberOrPercentage::Percentage(t))) => t.value(),
623			None => 100.0,
624		};
625		Some(chromashift::Color::Oklch(Oklch::new(lightness, chroma, hue, alpha)))
626	}
627}
628
629#[cfg(test)]
630mod tests {
631	use super::*;
632
633	#[test]
634	fn size_test() {
635		assert_eq!(std::mem::size_of::<ColorFunction>(), 140);
636		assert_eq!(std::mem::size_of::<ColorFunctionColor>(), 120);
637		assert_eq!(std::mem::size_of::<RgbFunction>(), 136);
638		assert_eq!(std::mem::size_of::<RgbaFunction>(), 136);
639		assert_eq!(std::mem::size_of::<HslFunction>(), 136);
640		assert_eq!(std::mem::size_of::<HslaFunction>(), 136);
641		assert_eq!(std::mem::size_of::<HwbFunction>(), 104);
642		assert_eq!(std::mem::size_of::<LabFunction>(), 104);
643		assert_eq!(std::mem::size_of::<LchFunction>(), 104);
644		assert_eq!(std::mem::size_of::<OklabFunction>(), 104);
645		assert_eq!(std::mem::size_of::<OklchFunction>(), 104);
646	}
647}