1use super::angle::Angle;
4use super::number::CSSNumber;
5use super::percentage::Percentage;
6use crate::compat::Feature;
7use crate::error::{ParserError, PrinterError};
8use crate::macros::enum_property;
9use crate::printer::Printer;
10use crate::rules::supports::SupportsCondition;
11use crate::targets::Browsers;
12use crate::traits::{FallbackValues, Parse, ToCss};
13use bitflags::bitflags;
14use cssparser::*;
15use std::any::TypeId;
16use std::f32::consts::PI;
17use std::fmt::Write;
18
19#[derive(Debug, Clone, PartialEq)]
29#[cfg_attr(
30 feature = "serde",
31 derive(serde::Serialize, serde::Deserialize),
32 serde(tag = "type", content = "value", rename_all = "lowercase")
33)]
34pub enum CssColor {
35 CurrentColor,
37 RGBA(RGBA),
39 LAB(Box<LABColor>),
41 Predefined(Box<PredefinedColor>),
43 Float(Box<FloatColor>),
45}
46
47#[derive(Debug, Clone, Copy, PartialEq)]
49#[cfg_attr(
50 feature = "serde",
51 derive(serde::Serialize, serde::Deserialize),
52 serde(tag = "type", content = "value", rename_all = "lowercase")
53)]
54pub enum LABColor {
55 LAB(LAB),
57 LCH(LCH),
59 OKLAB(OKLAB),
61 OKLCH(OKLCH),
63}
64
65#[derive(Debug, Clone, Copy, PartialEq)]
67#[cfg_attr(
68 feature = "serde",
69 derive(serde::Serialize, serde::Deserialize),
70 serde(tag = "type", content = "value")
71)]
72pub enum PredefinedColor {
73 #[cfg_attr(feature = "serde", serde(rename = "srgb"))]
75 SRGB(SRGB),
76 #[cfg_attr(feature = "serde", serde(rename = "srgb-linear"))]
78 SRGBLinear(SRGBLinear),
79 #[cfg_attr(feature = "serde", serde(rename = "display-p3"))]
81 DisplayP3(P3),
82 #[cfg_attr(feature = "serde", serde(rename = "a98-rgb"))]
84 A98(A98),
85 #[cfg_attr(feature = "serde", serde(rename = "prophoto-rgb"))]
87 ProPhoto(ProPhoto),
88 #[cfg_attr(feature = "serde", serde(rename = "rec2020"))]
90 Rec2020(Rec2020),
91 #[cfg_attr(feature = "serde", serde(rename = "xyz-d50"))]
93 XYZd50(XYZd50),
94 #[cfg_attr(feature = "serde", serde(rename = "xyz-d65"))]
96 XYZd65(XYZd65),
97}
98
99#[derive(Debug, Clone, Copy, PartialEq)]
103#[cfg_attr(
104 feature = "serde",
105 derive(serde::Serialize, serde::Deserialize),
106 serde(tag = "type", content = "value", rename_all = "lowercase")
107)]
108pub enum FloatColor {
109 RGB(SRGB),
111 HSL(HSL),
113 HWB(HWB),
115}
116
117bitflags! {
118 pub struct ColorFallbackKind: u8 {
120 const RGB = 0b01;
122 const P3 = 0b10;
124 const LAB = 0b100;
126 const OKLAB = 0b1000;
128 }
129}
130
131enum_property! {
132 enum ColorSpace {
135 "srgb": SRGB,
136 "srgb-linear": SRGBLinear,
137 "lab": LAB,
138 "oklab": OKLAB,
139 "xyz": XYZ,
140 "xyz-d50": XYZd50,
141 "xyz-d65": XYZd65,
142 "hsl": Hsl,
143 "hwb": Hwb,
144 "lch": LCH,
145 "oklch": OKLCH,
146 }
147}
148
149enum_property! {
150 pub enum HueInterpolationMethod {
153 Shorter,
155 Longer,
157 Increasing,
159 Decreasing,
161 Specified,
163 }
164}
165
166impl ColorFallbackKind {
167 pub(crate) fn lowest(&self) -> ColorFallbackKind {
168 *self & ColorFallbackKind::from_bits_truncate(self.bits().wrapping_neg())
170 }
171
172 pub(crate) fn highest(&self) -> ColorFallbackKind {
173 if self.is_empty() {
175 return ColorFallbackKind::empty();
176 }
177
178 let zeros = 7 - self.bits().leading_zeros();
179 ColorFallbackKind::from_bits_truncate(1 << zeros)
180 }
181
182 pub(crate) fn and_below(&self) -> ColorFallbackKind {
183 if self.is_empty() {
184 return ColorFallbackKind::empty();
185 }
186
187 *self | ColorFallbackKind::from_bits_truncate(self.bits() - 1)
188 }
189
190 pub(crate) fn supports_condition<'i>(&self) -> SupportsCondition<'i> {
191 let s = match *self {
192 ColorFallbackKind::P3 => "color: color(display-p3 0 0 0)",
193 ColorFallbackKind::LAB => "color: lab(0% 0 0)",
194 _ => unreachable!(),
195 };
196
197 SupportsCondition::Declaration(s.into())
198 }
199}
200
201impl CssColor {
202 pub fn current_color() -> CssColor {
204 CssColor::CurrentColor
205 }
206
207 pub fn transparent() -> CssColor {
209 CssColor::RGBA(RGBA::transparent())
210 }
211
212 pub fn to_rgb(&self) -> CssColor {
214 RGBA::from(self).into()
215 }
216
217 pub fn to_lab(&self) -> CssColor {
219 LAB::from(self).into()
220 }
221
222 pub fn to_p3(&self) -> CssColor {
224 P3::from(self).into()
225 }
226
227 pub(crate) fn get_possible_fallbacks(&self, targets: Browsers) -> ColorFallbackKind {
228 let mut fallbacks = match self {
232 CssColor::CurrentColor | CssColor::RGBA(_) | CssColor::Float(..) => return ColorFallbackKind::empty(),
233 CssColor::LAB(lab) => match &**lab {
234 LABColor::LAB(..) | LABColor::LCH(..) => ColorFallbackKind::LAB.and_below(),
235 LABColor::OKLAB(..) | LABColor::OKLCH(..) => ColorFallbackKind::OKLAB.and_below(),
236 },
237 CssColor::Predefined(predefined) => match &**predefined {
238 PredefinedColor::DisplayP3(..) => ColorFallbackKind::P3.and_below(),
239 _ => {
240 if Feature::ColorFunction.is_compatible(targets) {
241 return ColorFallbackKind::empty();
242 }
243
244 ColorFallbackKind::LAB.and_below()
245 }
246 },
247 };
248
249 if fallbacks.contains(ColorFallbackKind::OKLAB) {
250 if Feature::OklabColors.is_compatible(targets) {
251 fallbacks.remove(ColorFallbackKind::LAB.and_below());
252 }
253 }
254
255 if fallbacks.contains(ColorFallbackKind::LAB) {
256 if Feature::LabColors.is_compatible(targets) {
257 fallbacks.remove(ColorFallbackKind::P3.and_below());
258 } else if Feature::LabColors.is_partially_compatible(targets) {
259 fallbacks.remove(ColorFallbackKind::P3);
262 }
263 }
264
265 if fallbacks.contains(ColorFallbackKind::P3) {
266 if Feature::P3Colors.is_compatible(targets) {
267 fallbacks.remove(ColorFallbackKind::RGB);
268 } else if fallbacks.highest() != ColorFallbackKind::P3 && !Feature::P3Colors.is_partially_compatible(targets)
269 {
270 fallbacks.remove(ColorFallbackKind::P3);
273 }
274 }
275
276 fallbacks
277 }
278
279 pub fn get_necessary_fallbacks(&self, targets: Browsers) -> ColorFallbackKind {
281 let fallbacks = self.get_possible_fallbacks(targets);
284 fallbacks - fallbacks.highest()
285 }
286
287 pub fn get_fallback(&self, kind: ColorFallbackKind) -> CssColor {
289 if matches!(self, CssColor::RGBA(_)) {
290 return self.clone();
291 }
292
293 match kind {
294 ColorFallbackKind::RGB => self.to_rgb(),
295 ColorFallbackKind::P3 => self.to_p3(),
296 ColorFallbackKind::LAB => self.to_lab(),
297 _ => unreachable!(),
298 }
299 }
300}
301
302impl FallbackValues for CssColor {
303 fn get_fallbacks(&mut self, targets: Browsers) -> Vec<CssColor> {
304 let fallbacks = self.get_necessary_fallbacks(targets);
305
306 let mut res = Vec::new();
307 if fallbacks.contains(ColorFallbackKind::RGB) {
308 res.push(self.to_rgb());
309 }
310
311 if fallbacks.contains(ColorFallbackKind::P3) {
312 res.push(self.to_p3());
313 }
314
315 if fallbacks.contains(ColorFallbackKind::LAB) {
316 *self = self.to_lab();
317 }
318
319 res
320 }
321}
322
323impl Default for CssColor {
324 fn default() -> CssColor {
325 CssColor::transparent()
326 }
327}
328
329impl From<Color> for CssColor {
330 fn from(color: Color) -> Self {
331 match color {
332 Color::CurrentColor => CssColor::CurrentColor,
333 Color::RGBA(rgba) => CssColor::RGBA(rgba),
334 }
335 }
336}
337
338impl<'i> Parse<'i> for CssColor {
339 fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
340 let parser = ComponentParser { allow_none: false };
341 if let Ok(color) = input.try_parse(|input| Color::parse_with(&parser, input)) {
342 return Ok(color.into());
343 }
344
345 parse_color_function(input)
346 }
347}
348
349impl ToCss for CssColor {
350 fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
351 where
352 W: std::fmt::Write,
353 {
354 match self {
355 CssColor::CurrentColor => dest.write_str("currentColor"),
356 CssColor::RGBA(color) => {
357 if color.alpha == 255 {
358 let hex: u32 = ((color.red as u32) << 16) | ((color.green as u32) << 8) | (color.blue as u32);
359 if let Some(name) = short_color_name(hex) {
360 return dest.write_str(name);
361 }
362
363 let compact = compact_hex(hex);
364 if hex == expand_hex(compact) {
365 write!(dest, "#{:03x}", compact)?;
366 } else {
367 write!(dest, "#{:06x}", hex)?;
368 }
369 } else {
370 if let Some(targets) = dest.targets {
372 if !Feature::CssRrggbbaa.is_compatible(targets) {
373 dest.write_str("rgba(")?;
374 write!(dest, "{}", color.red)?;
375 dest.delim(',', false)?;
376 write!(dest, "{}", color.green)?;
377 dest.delim(',', false)?;
378 write!(dest, "{}", color.blue)?;
379 dest.delim(',', false)?;
380
381 let mut rounded_alpha = (color.alpha_f32() * 100.0).round() / 100.0;
383 let clamped = (rounded_alpha * 255.0).round().max(0.).min(255.0) as u8;
384 if clamped != color.alpha {
385 rounded_alpha = (color.alpha_f32() * 1000.).round() / 1000.;
386 }
387
388 rounded_alpha.to_css(dest)?;
389 dest.write_char(')')?;
390 return Ok(());
391 }
392 }
393
394 let hex: u32 = ((color.red as u32) << 24)
395 | ((color.green as u32) << 16)
396 | ((color.blue as u32) << 8)
397 | (color.alpha as u32);
398 let compact = compact_hex(hex);
399 if hex == expand_hex(compact) {
400 write!(dest, "#{:04x}", compact)?;
401 } else {
402 write!(dest, "#{:08x}", hex)?;
403 }
404 }
405 Ok(())
406 }
407 CssColor::LAB(lab) => match &**lab {
408 LABColor::LAB(lab) => write_components("lab", lab.l, lab.a, lab.b, lab.alpha, dest),
409 LABColor::LCH(lch) => write_components("lch", lch.l, lch.c, lch.h, lch.alpha, dest),
410 LABColor::OKLAB(lab) => write_components("oklab", lab.l, lab.a, lab.b, lab.alpha, dest),
411 LABColor::OKLCH(lch) => write_components("oklch", lch.l, lch.c, lch.h, lch.alpha, dest),
412 },
413 CssColor::Predefined(predefined) => write_predefined(predefined, dest),
414 CssColor::Float(float) => {
415 let srgb = SRGB::from(**float);
417 CssColor::from(srgb).to_css(dest)
418 }
419 }
420 }
421}
422
423fn compact_hex(v: u32) -> u32 {
426 return ((v & 0x0FF00000) >> 12) | ((v & 0x00000FF0) >> 4);
427}
428
429fn expand_hex(v: u32) -> u32 {
431 return ((v & 0xF000) << 16) | ((v & 0xFF00) << 12) | ((v & 0x0FF0) << 8) | ((v & 0x00FF) << 4) | (v & 0x000F);
432}
433
434fn short_color_name(v: u32) -> Option<&'static str> {
435 let s = match v {
437 0x000080 => "navy",
438 0x008000 => "green",
439 0x008080 => "teal",
440 0x4b0082 => "indigo",
441 0x800000 => "maroon",
442 0x800080 => "purple",
443 0x808000 => "olive",
444 0x808080 => "gray",
445 0xa0522d => "sienna",
446 0xa52a2a => "brown",
447 0xc0c0c0 => "silver",
448 0xcd853f => "peru",
449 0xd2b48c => "tan",
450 0xda70d6 => "orchid",
451 0xdda0dd => "plum",
452 0xee82ee => "violet",
453 0xf0e68c => "khaki",
454 0xf0ffff => "azure",
455 0xf5deb3 => "wheat",
456 0xf5f5dc => "beige",
457 0xfa8072 => "salmon",
458 0xfaf0e6 => "linen",
459 0xff0000 => "red",
460 0xff6347 => "tomato",
461 0xff7f50 => "coral",
462 0xffa500 => "orange",
463 0xffc0cb => "pink",
464 0xffd700 => "gold",
465 0xffe4c4 => "bisque",
466 0xfffafa => "snow",
467 0xfffff0 => "ivory",
468 _ => return None,
469 };
470
471 Some(s)
472}
473
474pub(crate) struct ComponentParser {
475 pub allow_none: bool,
476}
477
478impl<'i> ColorComponentParser<'i> for ComponentParser {
479 type Error = ParserError<'i>;
480
481 fn parse_angle_or_number<'t>(
482 &self,
483 input: &mut Parser<'i, 't>,
484 ) -> Result<AngleOrNumber, ParseError<'i, Self::Error>> {
485 if let Ok(angle) = input.try_parse(Angle::parse) {
486 Ok(AngleOrNumber::Angle {
487 degrees: angle.to_degrees(),
488 })
489 } else if let Ok(value) = input.try_parse(CSSNumber::parse) {
490 Ok(AngleOrNumber::Number { value })
491 } else if self.allow_none {
492 input.expect_ident_matching("none")?;
493 Ok(AngleOrNumber::Number { value: f32::NAN })
494 } else {
495 Err(input.new_custom_error(ParserError::InvalidValue))
496 }
497 }
498
499 fn parse_number<'t>(&self, input: &mut Parser<'i, 't>) -> Result<f32, ParseError<'i, Self::Error>> {
500 if let Ok(val) = input.try_parse(CSSNumber::parse) {
501 return Ok(val);
502 } else if self.allow_none {
503 input.expect_ident_matching("none")?;
504 Ok(f32::NAN)
505 } else {
506 Err(input.new_custom_error(ParserError::InvalidValue))
507 }
508 }
509
510 fn parse_percentage<'t>(&self, input: &mut Parser<'i, 't>) -> Result<f32, ParseError<'i, Self::Error>> {
511 if let Ok(val) = input.try_parse(Percentage::parse) {
512 return Ok(val.0);
513 } else if self.allow_none {
514 input.expect_ident_matching("none")?;
515 Ok(f32::NAN)
516 } else {
517 Err(input.new_custom_error(ParserError::InvalidValue))
518 }
519 }
520
521 fn parse_number_or_percentage<'t>(
522 &self,
523 input: &mut Parser<'i, 't>,
524 ) -> Result<NumberOrPercentage, ParseError<'i, Self::Error>> {
525 if let Ok(value) = input.try_parse(CSSNumber::parse) {
526 Ok(NumberOrPercentage::Number { value })
527 } else if let Ok(value) = input.try_parse(Percentage::parse) {
528 Ok(NumberOrPercentage::Percentage { unit_value: value.0 })
529 } else if self.allow_none {
530 input.expect_ident_matching("none")?;
531 Ok(NumberOrPercentage::Number { value: f32::NAN })
532 } else {
533 Err(input.new_custom_error(ParserError::InvalidValue))
534 }
535 }
536}
537
538fn parse_color_function<'i, 't>(input: &mut Parser<'i, 't>) -> Result<CssColor, ParseError<'i, ParserError<'i>>> {
540 let location = input.current_source_location();
541 let function = input.expect_function()?;
542 let parser = ComponentParser { allow_none: true };
543
544 match_ignore_ascii_case! {&*function,
545 "lab" => {
546 let (l, a, b, alpha) = parse_lab(input, &parser)?;
547 let lab = LABColor::LAB(LAB { l, a, b, alpha });
548 Ok(CssColor::LAB(Box::new(lab)))
549 },
550 "oklab" => {
551 let (l, a, b, alpha) = parse_lab(input, &parser)?;
552 let lab = LABColor::OKLAB(OKLAB { l, a, b, alpha });
553 Ok(CssColor::LAB(Box::new(lab)))
554 },
555 "lch" => {
556 let (l, c, h, alpha) = parse_lch(input, &parser)?;
557 let lab = LABColor::LCH(LCH { l, c, h, alpha });
558 Ok(CssColor::LAB(Box::new(lab)))
559 },
560 "oklch" => {
561 let (l, c, h, alpha) = parse_lch(input, &parser)?;
562 let lab = LABColor::OKLCH(OKLCH { l, c, h, alpha });
563 Ok(CssColor::LAB(Box::new(lab)))
564 },
565 "color" => {
566 let predefined = parse_predefined(input, &parser)?;
567 Ok(CssColor::Predefined(Box::new(predefined)))
568 },
569 "hsl" => {
570 let (h, s, l, a) = parse_hsl_hwb(input, &parser)?;
571 Ok(CssColor::Float(Box::new(FloatColor::HSL(HSL { h, s, l, alpha: a }))))
572 },
573 "hwb" => {
574 let (h, w, b, a) = parse_hsl_hwb(input, &parser)?;
575 Ok(CssColor::Float(Box::new(FloatColor::HWB(HWB { h, w, b, alpha: a }))))
576 },
577 "rgb" => {
578 let (r, g, b, a) = parse_rgb(input, &parser)?;
579 Ok(CssColor::Float(Box::new(FloatColor::RGB(SRGB { r, g, b, alpha: a }))))
580 },
581 "color-mix" => {
582 input.parse_nested_block(parse_color_mix)
583 },
584 _ => Err(location.new_unexpected_token_error(
585 cssparser::Token::Ident(function.clone())
586 ))
587 }
588}
589
590#[inline]
592fn parse_lab<'i, 't>(
593 input: &mut Parser<'i, 't>,
594 parser: &ComponentParser,
595) -> Result<(f32, f32, f32, f32), ParseError<'i, ParserError<'i>>> {
596 let res = input.parse_nested_block(|input| {
598 let l = parser.parse_percentage(input)?.clamp(0.0, f32::MAX);
600 let a = parser.parse_number(input)?;
601 let b = parser.parse_number(input)?;
602 let alpha = parse_alpha(input, parser)?;
603
604 Ok((l, a, b, alpha))
605 })?;
606
607 Ok(res)
608}
609
610#[inline]
612fn parse_lch<'i, 't>(
613 input: &mut Parser<'i, 't>,
614 parser: &ComponentParser,
615) -> Result<(f32, f32, f32, f32), ParseError<'i, ParserError<'i>>> {
616 let res = input.parse_nested_block(|input| {
618 let l = parser.parse_percentage(input)?.clamp(0.0, f32::MAX);
619 let c = parser.parse_number(input)?.clamp(0.0, f32::MAX);
620 let h = parse_angle_or_number(input, parser)?;
621 let alpha = parse_alpha(input, parser)?;
622
623 Ok((l, c, h, alpha))
624 })?;
625
626 Ok(res)
627}
628
629#[inline]
630fn parse_predefined<'i, 't>(
631 input: &mut Parser<'i, 't>,
632 parser: &ComponentParser,
633) -> Result<PredefinedColor, ParseError<'i, ParserError<'i>>> {
634 let res = input.parse_nested_block(|input| {
636 let location = input.current_source_location();
637 let colorspace = input.expect_ident_cloned()?;
638
639 let a = input
642 .try_parse(|input| parse_number_or_percentage(input, parser))
643 .unwrap_or(0.0);
644 let b = input
645 .try_parse(|input| parse_number_or_percentage(input, parser))
646 .unwrap_or(0.0);
647 let c = input
648 .try_parse(|input| parse_number_or_percentage(input, parser))
649 .unwrap_or(0.0);
650 let alpha = parse_alpha(input, parser)?;
651
652 let res = match_ignore_ascii_case! { &*&colorspace,
653 "srgb" => PredefinedColor::SRGB(SRGB { r: a, g: b, b: c, alpha }),
654 "srgb-linear" => PredefinedColor::SRGBLinear(SRGBLinear { r: a, g: b, b: c, alpha }),
655 "display-p3" => PredefinedColor::DisplayP3(P3 { r: a, g: b, b: c, alpha }),
656 "a98-rgb" => PredefinedColor::A98(A98 { r: a, g: b, b: c, alpha }),
657 "prophoto-rgb" => PredefinedColor::ProPhoto(ProPhoto { r: a, g: b, b: c, alpha }),
658 "rec2020" => PredefinedColor::Rec2020(Rec2020 { r: a, g: b, b: c, alpha }),
659 "xyz-d50" => PredefinedColor::XYZd50(XYZd50 { x: a, y: b, z: c, alpha}),
660 "xyz" | "xyz-d65" => PredefinedColor::XYZd65(XYZd65 { x: a, y: b, z: c, alpha }),
661 _ => return Err(location.new_unexpected_token_error(
662 cssparser::Token::Ident(colorspace.clone())
663 ))
664 };
665
666 Ok(res)
667 })?;
668
669 Ok(res)
670}
671
672#[inline]
676fn parse_hsl_hwb<'i, 't>(
677 input: &mut Parser<'i, 't>,
678 parser: &ComponentParser,
679) -> Result<(f32, f32, f32, f32), ParseError<'i, ParserError<'i>>> {
680 let res = input.parse_nested_block(|input| {
682 let (h, a, b) = parse_hsl_hwb_components(input, parser)?;
683 let alpha = parse_alpha(input, parser)?;
684
685 Ok((h, a, b, alpha))
686 })?;
687
688 Ok(res)
689}
690
691#[inline]
692pub(crate) fn parse_hsl_hwb_components<'i, 't>(
693 input: &mut Parser<'i, 't>,
694 parser: &ComponentParser,
695) -> Result<(f32, f32, f32), ParseError<'i, ParserError<'i>>> {
696 let h = parse_angle_or_number(input, parser)?;
697 let a = parser.parse_percentage(input)?.clamp(0.0, 1.0);
698 let b = parser.parse_percentage(input)?.clamp(0.0, 1.0);
699 Ok((h, a, b))
700}
701
702#[inline]
703fn parse_rgb<'i, 't>(
704 input: &mut Parser<'i, 't>,
705 parser: &ComponentParser,
706) -> Result<(f32, f32, f32, f32), ParseError<'i, ParserError<'i>>> {
707 let res = input.parse_nested_block(|input| {
709 let (r, g, b) = parse_rgb_components(input, parser)?;
710 let alpha = parse_alpha(input, parser)?;
711 Ok((r, g, b, alpha))
712 })?;
713
714 Ok(res)
715}
716
717#[inline]
718pub(crate) fn parse_rgb_components<'i, 't>(
719 input: &mut Parser<'i, 't>,
720 parser: &ComponentParser,
721) -> Result<(f32, f32, f32), ParseError<'i, ParserError<'i>>> {
722 #[derive(PartialEq)]
725 enum Kind {
726 Unknown,
727 Number,
728 Percentage,
729 }
730
731 #[inline]
732 fn parse_component<'i, 't>(
733 input: &mut Parser<'i, 't>,
734 parser: &ComponentParser,
735 kind: Kind,
736 ) -> Result<(f32, Kind), ParseError<'i, ParserError<'i>>> {
737 Ok(match parser.parse_number_or_percentage(input)? {
738 NumberOrPercentage::Number { value } if value.is_nan() => (value, kind),
739 NumberOrPercentage::Number { value } if kind != Kind::Percentage => {
740 (value.round().clamp(0.0, 255.0) / 255.0, Kind::Number)
741 }
742 NumberOrPercentage::Percentage { unit_value } if kind != Kind::Number => {
743 (unit_value.clamp(0.0, 1.0), Kind::Percentage)
744 }
745 _ => return Err(input.new_custom_error(ParserError::InvalidValue)),
746 })
747 }
748
749 let (r, kind) = parse_component(input, parser, Kind::Unknown)?;
750 let (g, kind) = parse_component(input, parser, kind)?;
751 let (b, _) = parse_component(input, parser, kind)?;
752 Ok((r, g, b))
753}
754
755#[inline]
756fn parse_angle_or_number<'i, 't>(
757 input: &mut Parser<'i, 't>,
758 parser: &ComponentParser,
759) -> Result<f32, ParseError<'i, ParserError<'i>>> {
760 Ok(match parser.parse_angle_or_number(input)? {
761 AngleOrNumber::Number { value } => value,
762 AngleOrNumber::Angle { degrees } => degrees,
763 })
764}
765
766#[inline]
767fn parse_number_or_percentage<'i, 't>(
768 input: &mut Parser<'i, 't>,
769 parser: &ComponentParser,
770) -> Result<f32, ParseError<'i, ParserError<'i>>> {
771 Ok(match parser.parse_number_or_percentage(input)? {
772 NumberOrPercentage::Number { value } => value,
773 NumberOrPercentage::Percentage { unit_value } => unit_value,
774 })
775}
776
777#[inline]
778fn parse_alpha<'i, 't>(
779 input: &mut Parser<'i, 't>,
780 parser: &ComponentParser,
781) -> Result<f32, ParseError<'i, ParserError<'i>>> {
782 let res = if input.try_parse(|input| input.expect_delim('/')).is_ok() {
783 parse_number_or_percentage(input, parser)?.clamp(0.0, 1.0)
784 } else {
785 1.0
786 };
787 Ok(res)
788}
789
790#[inline]
791fn write_components<W>(
792 name: &str,
793 a: f32,
794 b: f32,
795 c: f32,
796 alpha: f32,
797 dest: &mut Printer<W>,
798) -> Result<(), PrinterError>
799where
800 W: std::fmt::Write,
801{
802 dest.write_str(name)?;
803 dest.write_char('(')?;
804 if a.is_nan() {
805 dest.write_str("none")?;
806 } else {
807 Percentage(a).to_css(dest)?;
808 }
809 dest.write_char(' ')?;
810 write_component(b, dest)?;
811 dest.write_char(' ')?;
812 write_component(c, dest)?;
813 if alpha.is_nan() || (alpha - 1.0).abs() > f32::EPSILON {
814 dest.delim('/', true)?;
815 write_component(alpha, dest)?;
816 }
817
818 dest.write_char(')')
819}
820
821#[inline]
822fn write_component<W>(c: f32, dest: &mut Printer<W>) -> Result<(), PrinterError>
823where
824 W: std::fmt::Write,
825{
826 if c.is_nan() {
827 dest.write_str("none")?;
828 } else {
829 c.to_css(dest)?;
830 }
831 Ok(())
832}
833
834#[inline]
835fn write_predefined<W>(predefined: &PredefinedColor, dest: &mut Printer<W>) -> Result<(), PrinterError>
836where
837 W: std::fmt::Write,
838{
839 use PredefinedColor::*;
840
841 let (name, a, b, c, alpha) = match predefined {
842 SRGB(rgb) => ("srgb", rgb.r, rgb.g, rgb.b, rgb.alpha),
843 SRGBLinear(rgb) => ("srgb-linear", rgb.r, rgb.g, rgb.b, rgb.alpha),
844 DisplayP3(rgb) => ("display-p3", rgb.r, rgb.g, rgb.b, rgb.alpha),
845 A98(rgb) => ("a98-rgb", rgb.r, rgb.g, rgb.b, rgb.alpha),
846 ProPhoto(rgb) => ("prophoto-rgb", rgb.r, rgb.g, rgb.b, rgb.alpha),
847 Rec2020(rgb) => ("rec2020", rgb.r, rgb.g, rgb.b, rgb.alpha),
848 XYZd50(xyz) => ("xyz-d50", xyz.x, xyz.y, xyz.z, xyz.alpha),
849 XYZd65(xyz) => ("xyz", xyz.x, xyz.y, xyz.z, xyz.alpha),
851 };
852
853 dest.write_str("color(")?;
854 dest.write_str(name)?;
855 if !dest.minify || a != 0.0 || b != 0.0 || c != 0.0 {
856 dest.write_char(' ')?;
857 write_component(a, dest)?;
858 if !dest.minify || b != 0.0 || c != 0.0 {
859 dest.write_char(' ')?;
860 write_component(b, dest)?;
861 if !dest.minify || c != 0.0 {
862 dest.write_char(' ')?;
863 write_component(c, dest)?;
864 }
865 }
866 }
867
868 if alpha.is_nan() || (alpha - 1.0).abs() > f32::EPSILON {
869 dest.delim('/', true)?;
870 write_component(alpha, dest)?;
871 }
872
873 dest.write_char(')')
874}
875
876macro_rules! define_colorspace {
877 (
878 $(#[$outer:meta])*
879 $vis:vis struct $name:ident {
880 $(#[$a_meta: meta])*
881 $a: ident,
882 $(#[$b_meta: meta])*
883 $b: ident,
884 $(#[$c_meta: meta])*
885 $c: ident
886 }
887 ) => {
888 $(#[$outer])*
889 #[derive(Debug, Clone, Copy, PartialEq)]
890 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
891 pub struct $name {
892 $(#[$a_meta])*
893 pub $a: f32,
894 $(#[$b_meta])*
895 pub $b: f32,
896 $(#[$c_meta])*
897 pub $c: f32,
898 pub alpha: f32,
900 }
901
902 impl $name {
903 #[inline]
904 fn resolve_missing(&self) -> Self {
905 Self {
906 $a: if self.$a.is_nan() { 0.0 } else { self.$a },
907 $b: if self.$b.is_nan() { 0.0 } else { self.$b },
908 $c: if self.$c.is_nan() { 0.0 } else { self.$c },
909 alpha: if self.alpha.is_nan() { 0.0 } else { self.alpha },
910 }
911 }
912
913 #[inline]
916 pub fn resolve(&self) -> Self {
917 let mut resolved = self.resolve_missing();
918 if !resolved.in_gamut() {
919 resolved = map_gamut(resolved);
920 }
921 resolved
922 }
923 }
924 };
925}
926
927define_colorspace! {
928 pub struct SRGB {
930 r,
932 g,
934 b
936 }
937}
938
939define_colorspace! {
940 pub struct SRGBLinear {
942 r,
944 g,
946 b
948 }
949}
950
951define_colorspace! {
952 pub struct P3 {
954 r,
956 g,
958 b
960 }
961}
962
963define_colorspace! {
964 pub struct A98 {
966 r,
968 g,
970 b
972 }
973}
974
975define_colorspace! {
976 pub struct ProPhoto {
978 r,
980 g,
982 b
984 }
985}
986
987define_colorspace! {
988 pub struct Rec2020 {
990 r,
992 g,
994 b
996 }
997}
998
999define_colorspace! {
1000 pub struct LAB {
1002 l,
1004 a,
1006 b
1008 }
1009}
1010
1011define_colorspace! {
1012 pub struct LCH {
1014 l,
1016 c,
1018 h
1020 }
1021}
1022
1023define_colorspace! {
1024 pub struct OKLAB {
1026 l,
1028 a,
1030 b
1032 }
1033}
1034
1035define_colorspace! {
1036 pub struct OKLCH {
1038 l,
1040 c,
1042 h
1044 }
1045}
1046
1047define_colorspace! {
1048 pub struct XYZd50 {
1050 x,
1052 y,
1054 z
1056 }
1057}
1058
1059define_colorspace! {
1060 pub struct XYZd65 {
1062 x,
1064 y,
1066 z
1068 }
1069}
1070
1071define_colorspace! {
1072 pub struct HSL {
1074 h,
1076 s,
1078 l
1080 }
1081}
1082
1083define_colorspace! {
1084 pub struct HWB {
1086 h,
1088 w,
1090 b
1092 }
1093}
1094
1095macro_rules! via {
1096 ($t: ident -> $u: ident -> $v: ident) => {
1097 impl From<$t> for $v {
1098 #[inline]
1099 fn from(t: $t) -> $v {
1100 let xyz: $u = t.into();
1101 xyz.into()
1102 }
1103 }
1104
1105 impl From<$v> for $t {
1106 #[inline]
1107 fn from(t: $v) -> $t {
1108 let xyz: $u = t.into();
1109 xyz.into()
1110 }
1111 }
1112 };
1113}
1114
1115#[inline]
1116fn rectangular_to_polar(l: f32, a: f32, b: f32) -> (f32, f32, f32) {
1117 let mut h = b.atan2(a) * 180.0 / PI;
1119 if h < 0.0 {
1120 h += 360.0;
1121 }
1122 let c = (a.powi(2) + b.powi(2)).sqrt();
1123 h = h % 360.0;
1124 (l, c, h)
1125}
1126
1127#[inline]
1128fn polar_to_rectangular(l: f32, c: f32, h: f32) -> (f32, f32, f32) {
1129 let a = c * (h * PI / 180.0).cos();
1131 let b = c * (h * PI / 180.0).sin();
1132 (l, a, b)
1133}
1134
1135impl From<LCH> for LAB {
1136 fn from(lch: LCH) -> LAB {
1137 let lch = lch.resolve_missing();
1138 let (l, a, b) = polar_to_rectangular(lch.l, lch.c, lch.h);
1139 LAB {
1140 l,
1141 a,
1142 b,
1143 alpha: lch.alpha,
1144 }
1145 }
1146}
1147
1148impl From<LAB> for LCH {
1149 fn from(lab: LAB) -> LCH {
1150 let lab = lab.resolve_missing();
1151 let (l, c, h) = rectangular_to_polar(lab.l, lab.a, lab.b);
1152 LCH {
1153 l,
1154 c,
1155 h,
1156 alpha: lab.alpha,
1157 }
1158 }
1159}
1160
1161impl From<OKLCH> for OKLAB {
1162 fn from(lch: OKLCH) -> OKLAB {
1163 let lch = lch.resolve_missing();
1164 let (l, a, b) = polar_to_rectangular(lch.l, lch.c, lch.h);
1165 OKLAB {
1166 l,
1167 a,
1168 b,
1169 alpha: lch.alpha,
1170 }
1171 }
1172}
1173
1174impl From<OKLAB> for OKLCH {
1175 fn from(lab: OKLAB) -> OKLCH {
1176 let lab = lab.resolve_missing();
1177 let (l, c, h) = rectangular_to_polar(lab.l, lab.a, lab.b);
1178 OKLCH {
1179 l,
1180 c,
1181 h,
1182 alpha: lab.alpha,
1183 }
1184 }
1185}
1186
1187const D50: &[f32] = &[0.3457 / 0.3585, 1.00000, (1.0 - 0.3457 - 0.3585) / 0.3585];
1188
1189impl From<LAB> for XYZd50 {
1190 fn from(lab: LAB) -> XYZd50 {
1191 const K: f32 = 24389.0 / 27.0; const E: f32 = 216.0 / 24389.0; let lab = lab.resolve_missing();
1196 let l = lab.l * 100.0;
1197 let a = lab.a;
1198 let b = lab.b;
1199
1200 let f1 = (l + 16.0) / 116.0;
1202 let f0 = a / 500.0 + f1;
1203 let f2 = f1 - b / 200.0;
1204
1205 let x = if f0.powi(3) > E {
1207 f0.powi(3)
1208 } else {
1209 (116.0 * f0 - 16.0) / K
1210 };
1211
1212 let y = if l > K * E { ((l + 16.0) / 116.0).powi(3) } else { l / K };
1213
1214 let z = if f2.powi(3) > E {
1215 f2.powi(3)
1216 } else {
1217 (116.0 * f2 - 16.0) / K
1218 };
1219
1220 XYZd50 {
1222 x: x * D50[0],
1223 y: y * D50[1],
1224 z: z * D50[2],
1225 alpha: lab.alpha,
1226 }
1227 }
1228}
1229
1230impl From<XYZd50> for XYZd65 {
1231 fn from(xyz: XYZd50) -> XYZd65 {
1232 const MATRIX: &[f32] = &[
1234 0.9554734527042182,
1235 -0.023098536874261423,
1236 0.0632593086610217,
1237 -0.028369706963208136,
1238 1.0099954580058226,
1239 0.021041398966943008,
1240 0.012314001688319899,
1241 -0.020507696433477912,
1242 1.3303659366080753,
1243 ];
1244
1245 let xyz = xyz.resolve_missing();
1246 let (x, y, z) = multiply_matrix(MATRIX, xyz.x, xyz.y, xyz.z);
1247 XYZd65 {
1248 x,
1249 y,
1250 z,
1251 alpha: xyz.alpha,
1252 }
1253 }
1254}
1255
1256impl From<XYZd65> for XYZd50 {
1257 fn from(xyz: XYZd65) -> XYZd50 {
1258 const MATRIX: &[f32] = &[
1260 1.0479298208405488,
1261 0.022946793341019088,
1262 -0.05019222954313557,
1263 0.029627815688159344,
1264 0.990434484573249,
1265 -0.01707382502938514,
1266 -0.009243058152591178,
1267 0.015055144896577895,
1268 0.7518742899580008,
1269 ];
1270
1271 let xyz = xyz.resolve_missing();
1272 let (x, y, z) = multiply_matrix(MATRIX, xyz.x, xyz.y, xyz.z);
1273 XYZd50 {
1274 x,
1275 y,
1276 z,
1277 alpha: xyz.alpha,
1278 }
1279 }
1280}
1281
1282impl From<XYZd65> for SRGBLinear {
1283 fn from(xyz: XYZd65) -> SRGBLinear {
1284 const MATRIX: &[f32] = &[
1286 3.2409699419045226,
1287 -1.537383177570094,
1288 -0.4986107602930034,
1289 -0.9692436362808796,
1290 1.8759675015077202,
1291 0.04155505740717559,
1292 0.05563007969699366,
1293 -0.20397695888897652,
1294 1.0569715142428786,
1295 ];
1296
1297 let xyz = xyz.resolve_missing();
1298 let (r, g, b) = multiply_matrix(MATRIX, xyz.x, xyz.y, xyz.z);
1299 SRGBLinear {
1300 r,
1301 g,
1302 b,
1303 alpha: xyz.alpha,
1304 }
1305 }
1306}
1307
1308#[inline]
1309fn multiply_matrix(m: &[f32], x: f32, y: f32, z: f32) -> (f32, f32, f32) {
1310 let a = m[0] * x + m[1] * y + m[2] * z;
1311 let b = m[3] * x + m[4] * y + m[5] * z;
1312 let c = m[6] * x + m[7] * y + m[8] * z;
1313 (a, b, c)
1314}
1315
1316impl From<SRGBLinear> for SRGB {
1317 #[inline]
1318 fn from(rgb: SRGBLinear) -> SRGB {
1319 let rgb = rgb.resolve_missing();
1320 let (r, g, b) = gam_srgb(rgb.r, rgb.g, rgb.b);
1321 SRGB {
1322 r,
1323 g,
1324 b,
1325 alpha: rgb.alpha,
1326 }
1327 }
1328}
1329
1330fn gam_srgb(r: f32, g: f32, b: f32) -> (f32, f32, f32) {
1331 #[inline]
1340 fn gam_srgb_component(c: f32) -> f32 {
1341 let abs = c.abs();
1342 if abs > 0.0031308 {
1343 let sign = if c < 0.0 { -1.0 } else { 1.0 };
1344 return sign * (1.055 * abs.powf(1.0 / 2.4) - 0.055);
1345 }
1346
1347 return 12.92 * c;
1348 }
1349
1350 let r = gam_srgb_component(r);
1351 let g = gam_srgb_component(g);
1352 let b = gam_srgb_component(b);
1353 (r, g, b)
1354}
1355
1356impl From<OKLAB> for XYZd65 {
1357 fn from(lab: OKLAB) -> XYZd65 {
1358 const LMS_TO_XYZ: &[f32] = &[
1360 1.2268798733741557,
1361 -0.5578149965554813,
1362 0.28139105017721583,
1363 -0.04057576262431372,
1364 1.1122868293970594,
1365 -0.07171106666151701,
1366 -0.07637294974672142,
1367 -0.4214933239627914,
1368 1.5869240244272418,
1369 ];
1370
1371 const OKLAB_TO_LMS: &[f32] = &[
1372 0.99999999845051981432,
1373 0.39633779217376785678,
1374 0.21580375806075880339,
1375 1.0000000088817607767,
1376 -0.1055613423236563494,
1377 -0.063854174771705903402,
1378 1.0000000546724109177,
1379 -0.089484182094965759684,
1380 -1.2914855378640917399,
1381 ];
1382
1383 let lab = lab.resolve_missing();
1384 let (a, b, c) = multiply_matrix(OKLAB_TO_LMS, lab.l, lab.a, lab.b);
1385 let (x, y, z) = multiply_matrix(LMS_TO_XYZ, a.powi(3), b.powi(3), c.powi(3));
1386 XYZd65 {
1387 x,
1388 y,
1389 z,
1390 alpha: lab.alpha,
1391 }
1392 }
1393}
1394
1395impl From<XYZd65> for OKLAB {
1396 fn from(xyz: XYZd65) -> OKLAB {
1397 const XYZ_TO_LMS: &[f32] = &[
1399 0.8190224432164319,
1400 0.3619062562801221,
1401 -0.12887378261216414,
1402 0.0329836671980271,
1403 0.9292868468965546,
1404 0.03614466816999844,
1405 0.048177199566046255,
1406 0.26423952494422764,
1407 0.6335478258136937,
1408 ];
1409
1410 const LMS_TO_OKLAB: &[f32] = &[
1411 0.2104542553,
1412 0.7936177850,
1413 -0.0040720468,
1414 1.9779984951,
1415 -2.4285922050,
1416 0.4505937099,
1417 0.0259040371,
1418 0.7827717662,
1419 -0.8086757660,
1420 ];
1421
1422 let xyz = xyz.resolve_missing();
1423 let (a, b, c) = multiply_matrix(XYZ_TO_LMS, xyz.x, xyz.y, xyz.z);
1424 let (l, a, b) = multiply_matrix(LMS_TO_OKLAB, a.cbrt(), b.cbrt(), c.cbrt());
1425 OKLAB {
1426 l,
1427 a,
1428 b,
1429 alpha: xyz.alpha,
1430 }
1431 }
1432}
1433
1434impl From<XYZd50> for LAB {
1435 fn from(xyz: XYZd50) -> LAB {
1436 const E: f32 = 216.0 / 24389.0; const K: f32 = 24389.0 / 27.0; let xyz = xyz.resolve_missing();
1444 let x = xyz.x / D50[0];
1445 let y = xyz.y / D50[1];
1446 let z = xyz.z / D50[2];
1447
1448 let f0 = if x > E { x.cbrt() } else { (K * x + 16.0) / 116.0 };
1450
1451 let f1 = if y > E { y.cbrt() } else { (K * y + 16.0) / 116.0 };
1452
1453 let f2 = if z > E { z.cbrt() } else { (K * z + 16.0) / 116.0 };
1454
1455 let l = ((116.0 * f1) - 16.0) / 100.0;
1456 let a = 500.0 * (f0 - f1);
1457 let b = 200.0 * (f1 - f2);
1458 LAB {
1459 l,
1460 a,
1461 b,
1462 alpha: xyz.alpha,
1463 }
1464 }
1465}
1466
1467impl From<SRGB> for SRGBLinear {
1468 fn from(rgb: SRGB) -> SRGBLinear {
1469 let rgb = rgb.resolve_missing();
1470 let (r, g, b) = lin_srgb(rgb.r, rgb.g, rgb.b);
1471 SRGBLinear {
1472 r,
1473 g,
1474 b,
1475 alpha: rgb.alpha,
1476 }
1477 }
1478}
1479
1480fn lin_srgb(r: f32, g: f32, b: f32) -> (f32, f32, f32) {
1481 #[inline]
1490 fn lin_srgb_component(c: f32) -> f32 {
1491 let abs = c.abs();
1492 if abs < 0.04045 {
1493 return c / 12.92;
1494 }
1495
1496 let sign = if c < 0.0 { -1.0 } else { 1.0 };
1497 sign * ((abs + 0.055) / 1.055).powf(2.4)
1498 }
1499
1500 let r = lin_srgb_component(r);
1501 let g = lin_srgb_component(g);
1502 let b = lin_srgb_component(b);
1503 (r, g, b)
1504}
1505
1506impl From<SRGBLinear> for XYZd65 {
1507 fn from(rgb: SRGBLinear) -> XYZd65 {
1508 const MATRIX: &[f32] = &[
1512 0.41239079926595934,
1513 0.357584339383878,
1514 0.1804807884018343,
1515 0.21263900587151027,
1516 0.715168678767756,
1517 0.07219231536073371,
1518 0.01933081871559182,
1519 0.11919477979462598,
1520 0.9505321522496607,
1521 ];
1522
1523 let rgb = rgb.resolve_missing();
1524 let (x, y, z) = multiply_matrix(MATRIX, rgb.r, rgb.g, rgb.b);
1525 XYZd65 {
1526 x,
1527 y,
1528 z,
1529 alpha: rgb.alpha,
1530 }
1531 }
1532}
1533
1534impl From<XYZd65> for P3 {
1535 fn from(xyz: XYZd65) -> P3 {
1536 const MATRIX: &[f32] = &[
1538 2.493496911941425,
1539 -0.9313836179191239,
1540 -0.40271078445071684,
1541 -0.8294889695615747,
1542 1.7626640603183463,
1543 0.023624685841943577,
1544 0.03584583024378447,
1545 -0.07617238926804182,
1546 0.9568845240076872,
1547 ];
1548
1549 let xyz = xyz.resolve_missing();
1550 let (r, g, b) = multiply_matrix(MATRIX, xyz.x, xyz.y, xyz.z);
1551 let (r, g, b) = gam_srgb(r, g, b); P3 {
1553 r,
1554 g,
1555 b,
1556 alpha: xyz.alpha,
1557 }
1558 }
1559}
1560
1561impl From<P3> for XYZd65 {
1562 fn from(p3: P3) -> XYZd65 {
1563 const MATRIX: &[f32] = &[
1568 0.4865709486482162,
1569 0.26566769316909306,
1570 0.1982172852343625,
1571 0.2289745640697488,
1572 0.6917385218365064,
1573 0.079286914093745,
1574 0.0000000000000000,
1575 0.04511338185890264,
1576 1.043944368900976,
1577 ];
1578
1579 let p3 = p3.resolve_missing();
1580 let (r, g, b) = lin_srgb(p3.r, p3.g, p3.b);
1581 let (x, y, z) = multiply_matrix(MATRIX, r, g, b);
1582 XYZd65 {
1583 x,
1584 y,
1585 z,
1586 alpha: p3.alpha,
1587 }
1588 }
1589}
1590
1591impl From<A98> for XYZd65 {
1592 fn from(a98: A98) -> XYZd65 {
1593 #[inline]
1595 fn lin_a98rgb_component(c: f32) -> f32 {
1596 let sign = if c < 0.0 { -1.0 } else { 1.0 };
1597 sign * c.abs().powf(563.0 / 256.0)
1598 }
1599
1600 let a98 = a98.resolve_missing();
1604 let r = lin_a98rgb_component(a98.r);
1605 let g = lin_a98rgb_component(a98.g);
1606 let b = lin_a98rgb_component(a98.b);
1607
1608 const MATRIX: &[f32] = &[
1616 0.5766690429101305,
1617 0.1855582379065463,
1618 0.1882286462349947,
1619 0.29734497525053605,
1620 0.6273635662554661,
1621 0.07529145849399788,
1622 0.02703136138641234,
1623 0.07068885253582723,
1624 0.9913375368376388,
1625 ];
1626
1627 let (x, y, z) = multiply_matrix(MATRIX, r, g, b);
1628 XYZd65 {
1629 x,
1630 y,
1631 z,
1632 alpha: a98.alpha,
1633 }
1634 }
1635}
1636
1637impl From<XYZd65> for A98 {
1638 fn from(xyz: XYZd65) -> A98 {
1639 const MATRIX: &[f32] = &[
1642 2.0415879038107465,
1643 -0.5650069742788596,
1644 -0.34473135077832956,
1645 -0.9692436362808795,
1646 1.8759675015077202,
1647 0.04155505740717557,
1648 0.013444280632031142,
1649 -0.11836239223101838,
1650 1.0151749943912054,
1651 ];
1652
1653 #[inline]
1654 fn gam_a98_component(c: f32) -> f32 {
1655 let sign = if c < 0.0 { -1.0 } else { 1.0 };
1660 sign * c.abs().powf(256.0 / 563.0)
1661 }
1662
1663 let xyz = xyz.resolve_missing();
1664 let (r, g, b) = multiply_matrix(MATRIX, xyz.x, xyz.y, xyz.z);
1665 let r = gam_a98_component(r);
1666 let g = gam_a98_component(g);
1667 let b = gam_a98_component(b);
1668 A98 {
1669 r,
1670 g,
1671 b,
1672 alpha: xyz.alpha,
1673 }
1674 }
1675}
1676
1677impl From<ProPhoto> for XYZd50 {
1678 fn from(prophoto: ProPhoto) -> XYZd50 {
1679 #[inline]
1687 fn lin_prophoto_component(c: f32) -> f32 {
1688 const ET2: f32 = 16.0 / 512.0;
1689 let abs = c.abs();
1690 if abs <= ET2 {
1691 return c / 16.0;
1692 }
1693
1694 let sign = if c < 0.0 { -1.0 } else { 1.0 };
1695 sign * c.powf(1.8)
1696 }
1697
1698 let prophoto = prophoto.resolve_missing();
1699 let r = lin_prophoto_component(prophoto.r);
1700 let g = lin_prophoto_component(prophoto.g);
1701 let b = lin_prophoto_component(prophoto.b);
1702
1703 const MATRIX: &[f32] = &[
1708 0.7977604896723027,
1709 0.13518583717574031,
1710 0.0313493495815248,
1711 0.2880711282292934,
1712 0.7118432178101014,
1713 0.00008565396060525902,
1714 0.0,
1715 0.0,
1716 0.8251046025104601,
1717 ];
1718
1719 let (x, y, z) = multiply_matrix(MATRIX, r, g, b);
1720 XYZd50 {
1721 x,
1722 y,
1723 z,
1724 alpha: prophoto.alpha,
1725 }
1726 }
1727}
1728
1729impl From<XYZd50> for ProPhoto {
1730 fn from(xyz: XYZd50) -> ProPhoto {
1731 const MATRIX: &[f32] = &[
1733 1.3457989731028281,
1734 -0.25558010007997534,
1735 -0.05110628506753401,
1736 -0.5446224939028347,
1737 1.5082327413132781,
1738 0.02053603239147973,
1739 0.0,
1740 0.0,
1741 1.2119675456389454,
1742 ];
1743
1744 #[inline]
1745 fn gam_prophoto_component(c: f32) -> f32 {
1746 const ET: f32 = 1.0 / 512.0;
1752 let abs = c.abs();
1753 if abs >= ET {
1754 let sign = if c < 0.0 { -1.0 } else { 1.0 };
1755 return sign * abs.powf(1.0 / 1.8);
1756 }
1757
1758 16.0 * c
1759 }
1760
1761 let xyz = xyz.resolve_missing();
1762 let (r, g, b) = multiply_matrix(MATRIX, xyz.x, xyz.y, xyz.z);
1763 let r = gam_prophoto_component(r);
1764 let g = gam_prophoto_component(g);
1765 let b = gam_prophoto_component(b);
1766 ProPhoto {
1767 r,
1768 g,
1769 b,
1770 alpha: xyz.alpha,
1771 }
1772 }
1773}
1774
1775impl From<Rec2020> for XYZd65 {
1776 fn from(rec2020: Rec2020) -> XYZd65 {
1777 #[inline]
1783 fn lin_rec2020_component(c: f32) -> f32 {
1784 const A: f32 = 1.09929682680944;
1785 const B: f32 = 0.018053968510807;
1786
1787 let abs = c.abs();
1788 if abs < B * 4.5 {
1789 return c / 4.5;
1790 }
1791
1792 let sign = if c < 0.0 { -1.0 } else { 1.0 };
1793 sign * ((abs + A - 1.0) / A).powf(1.0 / 0.45)
1794 }
1795
1796 let rec2020 = rec2020.resolve_missing();
1797 let r = lin_rec2020_component(rec2020.r);
1798 let g = lin_rec2020_component(rec2020.g);
1799 let b = lin_rec2020_component(rec2020.b);
1800
1801 const MATRIX: &[f32] = &[
1806 0.6369580483012914,
1807 0.14461690358620832,
1808 0.1688809751641721,
1809 0.2627002120112671,
1810 0.6779980715188708,
1811 0.05930171646986196,
1812 0.000000000000000,
1813 0.028072693049087428,
1814 1.060985057710791,
1815 ];
1816
1817 let (x, y, z) = multiply_matrix(MATRIX, r, g, b);
1818 XYZd65 {
1819 x,
1820 y,
1821 z,
1822 alpha: rec2020.alpha,
1823 }
1824 }
1825}
1826
1827impl From<XYZd65> for Rec2020 {
1828 fn from(xyz: XYZd65) -> Rec2020 {
1829 const MATRIX: &[f32] = &[
1831 1.7166511879712674,
1832 -0.35567078377639233,
1833 -0.25336628137365974,
1834 -0.6666843518324892,
1835 1.6164812366349395,
1836 0.01576854581391113,
1837 0.017639857445310783,
1838 -0.042770613257808524,
1839 0.9421031212354738,
1840 ];
1841
1842 #[inline]
1843 fn gam_rec2020_component(c: f32) -> f32 {
1844 const A: f32 = 1.09929682680944;
1849 const B: f32 = 0.018053968510807;
1850
1851 let abs = c.abs();
1852 if abs > B {
1853 let sign = if c < 0.0 { -1.0 } else { 1.0 };
1854 return sign * (A * abs.powf(0.45) - (A - 1.0));
1855 }
1856
1857 4.5 * c
1858 }
1859
1860 let xyz = xyz.resolve_missing();
1861 let (r, g, b) = multiply_matrix(MATRIX, xyz.x, xyz.y, xyz.z);
1862 let r = gam_rec2020_component(r);
1863 let g = gam_rec2020_component(g);
1864 let b = gam_rec2020_component(b);
1865 Rec2020 {
1866 r,
1867 g,
1868 b,
1869 alpha: xyz.alpha,
1870 }
1871 }
1872}
1873
1874impl From<SRGB> for HSL {
1875 fn from(rgb: SRGB) -> HSL {
1876 let rgb = rgb.resolve();
1878 let r = rgb.r;
1879 let g = rgb.g;
1880 let b = rgb.b;
1881 let max = r.max(g).max(b);
1882 let min = r.min(g).min(b);
1883 let mut h = f32::NAN;
1884 let mut s: f32 = 0.0;
1885 let l = (min + max) / 2.0;
1886 let d = max - min;
1887
1888 if d != 0.0 {
1889 s = if l == 0.0 || l == 1.0 {
1890 0.0
1891 } else {
1892 (max - l) / l.min(1.0 - l)
1893 };
1894
1895 if max == r {
1896 h = (g - b) / d + (if g < b { 6.0 } else { 0.0 });
1897 } else if max == g {
1898 h = (b - r) / d + 2.0;
1899 } else if max == b {
1900 h = (r - g) / d + 4.0;
1901 }
1902
1903 h = h * 60.0;
1904 }
1905
1906 HSL {
1907 h,
1908 s,
1909 l,
1910 alpha: rgb.alpha,
1911 }
1912 }
1913}
1914
1915impl From<HSL> for SRGB {
1916 fn from(hsl: HSL) -> SRGB {
1917 let hsl = hsl.resolve_missing();
1919 let mut h = hsl.h % 360.0;
1920 if h < 0.0 {
1921 h += 360.0;
1922 }
1923
1924 #[inline]
1925 fn hue_to_rgb(n: f32, h: f32, s: f32, l: f32) -> f32 {
1926 let k = (n + h / 30.0) % 12.0;
1927 let a = s * l.min(1.0 - l);
1928 l - a * (k - 3.0).min(9.0 - k).clamp(-1.0, 1.0)
1929 }
1930
1931 let r = hue_to_rgb(0.0, h, hsl.s, hsl.l);
1932 let g = hue_to_rgb(8.0, h, hsl.s, hsl.l);
1933 let b = hue_to_rgb(4.0, h, hsl.s, hsl.l);
1934 SRGB {
1935 r,
1936 g,
1937 b,
1938 alpha: hsl.alpha,
1939 }
1940 }
1941}
1942
1943impl From<SRGB> for HWB {
1944 fn from(rgb: SRGB) -> HWB {
1945 let rgb = rgb.resolve();
1946 let hsl = HSL::from(rgb);
1947 let r = rgb.r;
1948 let g = rgb.g;
1949 let b = rgb.b;
1950 let w = r.min(g).min(b);
1951 let b = 1.0 - r.max(g).max(b);
1952 HWB {
1953 h: hsl.h,
1954 w,
1955 b,
1956 alpha: rgb.alpha,
1957 }
1958 }
1959}
1960
1961impl From<HWB> for SRGB {
1962 fn from(hwb: HWB) -> SRGB {
1963 let hwb = hwb.resolve_missing();
1965 let h = hwb.h;
1966 let w = hwb.w;
1967 let b = hwb.b;
1968
1969 if w + b >= 1.0 {
1970 let gray = w / (w + b);
1971 return SRGB {
1972 r: gray,
1973 g: gray,
1974 b: gray,
1975 alpha: hwb.alpha,
1976 };
1977 }
1978
1979 let mut rgba = SRGB::from(HSL {
1980 h,
1981 s: 1.0,
1982 l: 0.5,
1983 alpha: hwb.alpha,
1984 });
1985 let x = 1.0 - w - b;
1986 rgba.r = rgba.r * x + w;
1987 rgba.g = rgba.g * x + w;
1988 rgba.b = rgba.b * x + w;
1989 rgba
1990 }
1991}
1992
1993impl From<RGBA> for SRGB {
1994 fn from(rgb: RGBA) -> SRGB {
1995 SRGB {
1996 r: rgb.red_f32(),
1997 g: rgb.green_f32(),
1998 b: rgb.blue_f32(),
1999 alpha: rgb.alpha_f32(),
2000 }
2001 }
2002}
2003
2004impl From<SRGB> for RGBA {
2005 fn from(rgb: SRGB) -> RGBA {
2006 let rgb = rgb.resolve();
2007 RGBA::from_floats(rgb.r, rgb.g, rgb.b, rgb.alpha)
2008 }
2009}
2010
2011via!(LAB -> XYZd50 -> XYZd65);
2013via!(ProPhoto -> XYZd50 -> XYZd65);
2014via!(OKLCH -> OKLAB -> XYZd65);
2015
2016via!(LAB -> XYZd65 -> OKLAB);
2017via!(LAB -> XYZd65 -> OKLCH);
2018via!(LAB -> XYZd65 -> SRGB);
2019via!(LAB -> XYZd65 -> SRGBLinear);
2020via!(LAB -> XYZd65 -> P3);
2021via!(LAB -> XYZd65 -> A98);
2022via!(LAB -> XYZd65 -> ProPhoto);
2023via!(LAB -> XYZd65 -> Rec2020);
2024via!(LAB -> XYZd65 -> HSL);
2025via!(LAB -> XYZd65 -> HWB);
2026
2027via!(LCH -> LAB -> XYZd65);
2028via!(LCH -> XYZd65 -> OKLAB);
2029via!(LCH -> XYZd65 -> OKLCH);
2030via!(LCH -> XYZd65 -> SRGB);
2031via!(LCH -> XYZd65 -> SRGBLinear);
2032via!(LCH -> XYZd65 -> P3);
2033via!(LCH -> XYZd65 -> A98);
2034via!(LCH -> XYZd65 -> ProPhoto);
2035via!(LCH -> XYZd65 -> Rec2020);
2036via!(LCH -> XYZd65 -> XYZd50);
2037via!(LCH -> XYZd65 -> HSL);
2038via!(LCH -> XYZd65 -> HWB);
2039
2040via!(SRGB -> SRGBLinear -> XYZd65);
2041via!(SRGB -> XYZd65 -> OKLAB);
2042via!(SRGB -> XYZd65 -> OKLCH);
2043via!(SRGB -> XYZd65 -> P3);
2044via!(SRGB -> XYZd65 -> A98);
2045via!(SRGB -> XYZd65 -> ProPhoto);
2046via!(SRGB -> XYZd65 -> Rec2020);
2047via!(SRGB -> XYZd65 -> XYZd50);
2048
2049via!(P3 -> XYZd65 -> SRGBLinear);
2050via!(P3 -> XYZd65 -> OKLAB);
2051via!(P3 -> XYZd65 -> OKLCH);
2052via!(P3 -> XYZd65 -> A98);
2053via!(P3 -> XYZd65 -> ProPhoto);
2054via!(P3 -> XYZd65 -> Rec2020);
2055via!(P3 -> XYZd65 -> XYZd50);
2056via!(P3 -> XYZd65 -> HSL);
2057via!(P3 -> XYZd65 -> HWB);
2058
2059via!(SRGBLinear -> XYZd65 -> OKLAB);
2060via!(SRGBLinear -> XYZd65 -> OKLCH);
2061via!(SRGBLinear -> XYZd65 -> A98);
2062via!(SRGBLinear -> XYZd65 -> ProPhoto);
2063via!(SRGBLinear -> XYZd65 -> Rec2020);
2064via!(SRGBLinear -> XYZd65 -> XYZd50);
2065via!(SRGBLinear -> XYZd65 -> HSL);
2066via!(SRGBLinear -> XYZd65 -> HWB);
2067
2068via!(A98 -> XYZd65 -> OKLAB);
2069via!(A98 -> XYZd65 -> OKLCH);
2070via!(A98 -> XYZd65 -> ProPhoto);
2071via!(A98 -> XYZd65 -> Rec2020);
2072via!(A98 -> XYZd65 -> XYZd50);
2073via!(A98 -> XYZd65 -> HSL);
2074via!(A98 -> XYZd65 -> HWB);
2075
2076via!(ProPhoto -> XYZd65 -> OKLAB);
2077via!(ProPhoto -> XYZd65 -> OKLCH);
2078via!(ProPhoto -> XYZd65 -> Rec2020);
2079via!(ProPhoto -> XYZd65 -> HSL);
2080via!(ProPhoto -> XYZd65 -> HWB);
2081
2082via!(XYZd50 -> XYZd65 -> OKLAB);
2083via!(XYZd50 -> XYZd65 -> OKLCH);
2084via!(XYZd50 -> XYZd65 -> Rec2020);
2085via!(XYZd50 -> XYZd65 -> HSL);
2086via!(XYZd50 -> XYZd65 -> HWB);
2087
2088via!(Rec2020 -> XYZd65 -> OKLAB);
2089via!(Rec2020 -> XYZd65 -> OKLCH);
2090via!(Rec2020 -> XYZd65 -> HSL);
2091via!(Rec2020 -> XYZd65 -> HWB);
2092
2093via!(HSL -> XYZd65 -> OKLAB);
2094via!(HSL -> XYZd65 -> OKLCH);
2095via!(HSL -> SRGB -> XYZd65);
2096via!(HSL -> SRGB -> HWB);
2097
2098via!(HWB -> SRGB -> XYZd65);
2099via!(HWB -> XYZd65 -> OKLAB);
2100via!(HWB -> XYZd65 -> OKLCH);
2101
2102via!(RGBA -> SRGB -> LAB);
2105via!(RGBA -> SRGB -> LCH);
2106via!(RGBA -> SRGB -> OKLAB);
2107via!(RGBA -> SRGB -> OKLCH);
2108via!(RGBA -> SRGB -> P3);
2109via!(RGBA -> SRGB -> SRGBLinear);
2110via!(RGBA -> SRGB -> A98);
2111via!(RGBA -> SRGB -> ProPhoto);
2112via!(RGBA -> SRGB -> XYZd50);
2113via!(RGBA -> SRGB -> XYZd65);
2114via!(RGBA -> SRGB -> Rec2020);
2115via!(RGBA -> SRGB -> HSL);
2116via!(RGBA -> SRGB -> HWB);
2117
2118macro_rules! color_space {
2119 ($space: ty) => {
2120 impl From<LABColor> for $space {
2121 fn from(color: LABColor) -> $space {
2122 use LABColor::*;
2123
2124 match color {
2125 LAB(v) => v.into(),
2126 LCH(v) => v.into(),
2127 OKLAB(v) => v.into(),
2128 OKLCH(v) => v.into(),
2129 }
2130 }
2131 }
2132
2133 impl From<PredefinedColor> for $space {
2134 fn from(color: PredefinedColor) -> $space {
2135 use PredefinedColor::*;
2136
2137 match color {
2138 SRGB(v) => v.into(),
2139 SRGBLinear(v) => v.into(),
2140 DisplayP3(v) => v.into(),
2141 A98(v) => v.into(),
2142 ProPhoto(v) => v.into(),
2143 Rec2020(v) => v.into(),
2144 XYZd50(v) => v.into(),
2145 XYZd65(v) => v.into(),
2146 }
2147 }
2148 }
2149
2150 impl From<FloatColor> for $space {
2151 fn from(color: FloatColor) -> $space {
2152 use FloatColor::*;
2153
2154 match color {
2155 RGB(v) => v.into(),
2156 HSL(v) => v.into(),
2157 HWB(v) => v.into(),
2158 }
2159 }
2160 }
2161
2162 impl From<&CssColor> for $space {
2163 fn from(color: &CssColor) -> $space {
2164 match color {
2165 CssColor::RGBA(rgba) => (*rgba).into(),
2166 CssColor::LAB(lab) => (**lab).into(),
2167 CssColor::Predefined(predefined) => (**predefined).into(),
2168 CssColor::Float(float) => (**float).into(),
2169 CssColor::CurrentColor => unreachable!(),
2170 }
2171 }
2172 }
2173 };
2174}
2175
2176color_space!(LAB);
2177color_space!(LCH);
2178color_space!(OKLAB);
2179color_space!(OKLCH);
2180color_space!(SRGB);
2181color_space!(SRGBLinear);
2182color_space!(XYZd50);
2183color_space!(XYZd65);
2184color_space!(P3);
2185color_space!(A98);
2186color_space!(ProPhoto);
2187color_space!(Rec2020);
2188color_space!(HSL);
2189color_space!(HWB);
2190color_space!(RGBA);
2191
2192macro_rules! predefined {
2193 ($key: ident, $t: ty) => {
2194 impl From<$t> for PredefinedColor {
2195 fn from(color: $t) -> PredefinedColor {
2196 PredefinedColor::$key(color)
2197 }
2198 }
2199
2200 impl From<$t> for CssColor {
2201 fn from(color: $t) -> CssColor {
2202 CssColor::Predefined(Box::new(PredefinedColor::$key(color)))
2203 }
2204 }
2205 };
2206}
2207
2208predefined!(SRGBLinear, SRGBLinear);
2209predefined!(XYZd50, XYZd50);
2210predefined!(XYZd65, XYZd65);
2211predefined!(DisplayP3, P3);
2212predefined!(A98, A98);
2213predefined!(ProPhoto, ProPhoto);
2214predefined!(Rec2020, Rec2020);
2215
2216macro_rules! lab {
2217 ($key: ident, $t: ty) => {
2218 impl From<$t> for LABColor {
2219 fn from(color: $t) -> LABColor {
2220 LABColor::$key(color)
2221 }
2222 }
2223
2224 impl From<$t> for CssColor {
2225 fn from(color: $t) -> CssColor {
2226 CssColor::LAB(Box::new(LABColor::$key(color)))
2227 }
2228 }
2229 };
2230}
2231
2232lab!(LAB, LAB);
2233lab!(LCH, LCH);
2234lab!(OKLAB, OKLAB);
2235lab!(OKLCH, OKLCH);
2236
2237macro_rules! rgb {
2238 ($t: ty) => {
2239 impl From<$t> for CssColor {
2240 fn from(color: $t) -> CssColor {
2241 CssColor::RGBA(color.into())
2244 }
2245 }
2246 };
2247}
2248
2249rgb!(SRGB);
2250rgb!(HSL);
2251rgb!(HWB);
2252
2253impl From<RGBA> for CssColor {
2254 fn from(color: RGBA) -> CssColor {
2255 CssColor::RGBA(color)
2256 }
2257}
2258
2259pub trait ColorGamut {
2261 fn in_gamut(&self) -> bool;
2263 fn clip(&self) -> Self;
2265}
2266
2267macro_rules! bounded_color_gamut {
2268 ($t: ty, $a: ident, $b: ident, $c: ident) => {
2269 impl ColorGamut for $t {
2270 #[inline]
2271 fn in_gamut(&self) -> bool {
2272 self.$a >= 0.0 && self.$a <= 1.0 && self.$b >= 0.0 && self.$b <= 1.0 && self.$c >= 0.0 && self.$c <= 1.0
2273 }
2274
2275 #[inline]
2276 fn clip(&self) -> Self {
2277 Self {
2278 $a: self.$a.clamp(0.0, 1.0),
2279 $b: self.$b.clamp(0.0, 1.0),
2280 $c: self.$c.clamp(0.0, 1.0),
2281 alpha: self.alpha.clamp(0.0, 1.0),
2282 }
2283 }
2284 }
2285 };
2286}
2287
2288macro_rules! unbounded_color_gamut {
2289 ($t: ty, $a: ident, $b: ident, $c: ident) => {
2290 impl ColorGamut for $t {
2291 #[inline]
2292 fn in_gamut(&self) -> bool {
2293 true
2294 }
2295
2296 #[inline]
2297 fn clip(&self) -> Self {
2298 *self
2299 }
2300 }
2301 };
2302}
2303
2304macro_rules! hsl_hwb_color_gamut {
2305 ($t: ty, $a: ident, $b: ident) => {
2306 impl ColorGamut for $t {
2307 #[inline]
2308 fn in_gamut(&self) -> bool {
2309 self.$a >= 0.0 && self.$a <= 1.0 && self.$b >= 0.0 && self.$b <= 1.0
2310 }
2311
2312 #[inline]
2313 fn clip(&self) -> Self {
2314 Self {
2315 h: self.h % 360.0,
2316 $a: self.$a.clamp(0.0, 1.0),
2317 $b: self.$b.clamp(0.0, 1.0),
2318 alpha: self.alpha.clamp(0.0, 1.0),
2319 }
2320 }
2321 }
2322 };
2323}
2324
2325bounded_color_gamut!(SRGB, r, g, b);
2326bounded_color_gamut!(SRGBLinear, r, g, b);
2327bounded_color_gamut!(P3, r, g, b);
2328bounded_color_gamut!(A98, r, g, b);
2329bounded_color_gamut!(ProPhoto, r, g, b);
2330bounded_color_gamut!(Rec2020, r, g, b);
2331unbounded_color_gamut!(LAB, l, a, b);
2332unbounded_color_gamut!(OKLAB, l, a, b);
2333unbounded_color_gamut!(XYZd50, x, y, z);
2334unbounded_color_gamut!(XYZd65, x, y, z);
2335unbounded_color_gamut!(LCH, l, c, h);
2336unbounded_color_gamut!(OKLCH, l, c, h);
2337hsl_hwb_color_gamut!(HSL, s, l);
2338hsl_hwb_color_gamut!(HWB, w, b);
2339
2340fn delta_eok<T: Into<OKLAB>>(a: T, b: OKLCH) -> f32 {
2341 let a: OKLAB = a.into();
2343 let b: OKLAB = b.into();
2344 let delta_l = a.l - b.l;
2345 let delta_a = a.a - b.a;
2346 let delta_b = a.b - b.b;
2347
2348 (delta_l.powi(2) + delta_a.powi(2) + delta_b.powi(2)).sqrt()
2349}
2350
2351fn map_gamut<T>(color: T) -> T
2352where
2353 T: Into<OKLCH> + ColorGamut + Into<OKLAB> + From<OKLCH> + Copy,
2354{
2355 const JND: f32 = 0.02;
2356 const EPSILON: f32 = 0.00001;
2357
2358 let mut current: OKLCH = color.into();
2360
2361 if (current.l - 1.0).abs() < EPSILON || current.l > 1.0 {
2363 return OKLCH {
2364 l: 1.0,
2365 c: 0.0,
2366 h: 0.0,
2367 alpha: current.alpha,
2368 }
2369 .into();
2370 }
2371
2372 if current.l < EPSILON {
2374 return OKLCH {
2375 l: 0.0,
2376 c: 0.0,
2377 h: 0.0,
2378 alpha: current.alpha,
2379 }
2380 .into();
2381 }
2382
2383 let mut min = 0.0;
2384 let mut max = current.c;
2385
2386 while min < max {
2387 let chroma = (min + max) / 2.0;
2388 current.c = chroma;
2389
2390 let converted = T::from(current);
2391 if converted.in_gamut() {
2392 min = chroma;
2393 continue;
2394 }
2395
2396 let clipped = converted.clip();
2397 let delta_e = delta_eok(clipped, current);
2398 if delta_e < JND {
2399 return clipped;
2400 }
2401
2402 max = chroma;
2403 }
2404
2405 current.into()
2406}
2407
2408fn parse_color_mix<'i, 't>(input: &mut Parser<'i, 't>) -> Result<CssColor, ParseError<'i, ParserError<'i>>> {
2409 input.expect_ident_matching("in")?;
2410 let method = ColorSpace::parse(input)?;
2411
2412 let hue_method = if matches!(
2413 method,
2414 ColorSpace::Hsl | ColorSpace::Hwb | ColorSpace::LCH | ColorSpace::OKLCH
2415 ) {
2416 let hue_method = input.try_parse(HueInterpolationMethod::parse);
2417 if hue_method.is_ok() {
2418 input.expect_ident_matching("hue")?;
2419 }
2420 hue_method
2421 } else {
2422 Ok(HueInterpolationMethod::Shorter)
2423 };
2424
2425 let hue_method = hue_method.unwrap_or(HueInterpolationMethod::Shorter);
2426 input.expect_comma()?;
2427
2428 let first_percent = input.try_parse(|input| input.expect_percentage());
2429 let first_color = CssColor::parse(input)?;
2430 let first_percent = first_percent
2431 .or_else(|_| input.try_parse(|input| input.expect_percentage()))
2432 .ok();
2433 input.expect_comma()?;
2434
2435 let second_percent = input.try_parse(|input| input.expect_percentage());
2436 let second_color = CssColor::parse(input)?;
2437 let second_percent = second_percent
2438 .or_else(|_| input.try_parse(|input| input.expect_percentage()))
2439 .ok();
2440
2441 let (p1, p2) = if first_percent.is_none() && second_percent.is_none() {
2443 (0.5, 0.5)
2444 } else {
2445 let p2 = second_percent.unwrap_or_else(|| 1.0 - first_percent.unwrap());
2446 let p1 = first_percent.unwrap_or_else(|| 1.0 - second_percent.unwrap());
2447 (p1, p2)
2448 };
2449
2450 if (p1 + p2) == 0.0 {
2451 return Err(input.new_custom_error(ParserError::InvalidValue));
2452 }
2453
2454 Ok(match method {
2455 ColorSpace::SRGB => first_color.interpolate::<SRGB>(p1, &second_color, p2, hue_method),
2456 ColorSpace::SRGBLinear => first_color.interpolate::<SRGBLinear>(p1, &second_color, p2, hue_method),
2457 ColorSpace::Hsl => first_color.interpolate::<HSL>(p1, &second_color, p2, hue_method),
2458 ColorSpace::Hwb => first_color.interpolate::<HWB>(p1, &second_color, p2, hue_method),
2459 ColorSpace::LAB => first_color.interpolate::<LAB>(p1, &second_color, p2, hue_method),
2460 ColorSpace::LCH => first_color.interpolate::<LCH>(p1, &second_color, p2, hue_method),
2461 ColorSpace::OKLAB => first_color.interpolate::<OKLAB>(p1, &second_color, p2, hue_method),
2462 ColorSpace::OKLCH => first_color.interpolate::<OKLCH>(p1, &second_color, p2, hue_method),
2463 ColorSpace::XYZ | ColorSpace::XYZd65 => first_color.interpolate::<XYZd65>(p1, &second_color, p2, hue_method),
2464 ColorSpace::XYZd50 => first_color.interpolate::<XYZd50>(p1, &second_color, p2, hue_method),
2465 })
2466}
2467
2468impl CssColor {
2469 fn get_type_id(&self) -> TypeId {
2470 match self {
2471 CssColor::RGBA(..) => TypeId::of::<SRGB>(),
2472 CssColor::LAB(lab) => match &**lab {
2473 LABColor::LAB(..) => TypeId::of::<LAB>(),
2474 LABColor::LCH(..) => TypeId::of::<LCH>(),
2475 LABColor::OKLAB(..) => TypeId::of::<OKLAB>(),
2476 LABColor::OKLCH(..) => TypeId::of::<OKLCH>(),
2477 },
2478 CssColor::Predefined(predefined) => match &**predefined {
2479 PredefinedColor::SRGB(..) => TypeId::of::<SRGB>(),
2480 PredefinedColor::SRGBLinear(..) => TypeId::of::<SRGBLinear>(),
2481 PredefinedColor::DisplayP3(..) => TypeId::of::<P3>(),
2482 PredefinedColor::A98(..) => TypeId::of::<A98>(),
2483 PredefinedColor::ProPhoto(..) => TypeId::of::<ProPhoto>(),
2484 PredefinedColor::Rec2020(..) => TypeId::of::<Rec2020>(),
2485 PredefinedColor::XYZd50(..) => TypeId::of::<XYZd50>(),
2486 PredefinedColor::XYZd65(..) => TypeId::of::<XYZd65>(),
2487 },
2488 CssColor::Float(float) => match &**float {
2489 FloatColor::RGB(..) => TypeId::of::<SRGB>(),
2490 FloatColor::HSL(..) => TypeId::of::<HSL>(),
2491 FloatColor::HWB(..) => TypeId::of::<HWB>(),
2492 },
2493 _ => unreachable!(),
2494 }
2495 }
2496
2497 pub fn interpolate<'a, T>(
2500 &'a self,
2501 mut p1: f32,
2502 other: &'a CssColor,
2503 mut p2: f32,
2504 method: HueInterpolationMethod,
2505 ) -> CssColor
2506 where
2507 T: 'static
2508 + From<&'a CssColor>
2509 + Interpolate
2510 + Into<CssColor>
2511 + Into<OKLCH>
2512 + ColorGamut
2513 + Into<OKLAB>
2514 + From<OKLCH>
2515 + Copy,
2516 {
2517 let type_id = TypeId::of::<T>();
2518 let converted_first = self.get_type_id() != type_id;
2519 let converted_second = other.get_type_id() != type_id;
2520
2521 let mut first_color = T::from(self);
2523 let mut second_color = T::from(other);
2524
2525 if converted_first && !first_color.in_gamut() {
2526 first_color = map_gamut(first_color);
2527 }
2528
2529 if converted_second && !second_color.in_gamut() {
2530 second_color = map_gamut(second_color);
2531 }
2532
2533 if converted_first {
2535 first_color.adjust_powerless_components();
2536 }
2537
2538 if converted_second {
2539 second_color.adjust_powerless_components();
2540 }
2541
2542 first_color.fill_missing_components(&second_color);
2544 second_color.fill_missing_components(&first_color);
2545
2546 first_color.adjust_hue(&mut second_color, method);
2548
2549 first_color.premultiply();
2551 second_color.premultiply();
2552
2553 let mut alpha_multiplier = p1 + p2;
2555 if alpha_multiplier != 1.0 {
2556 p1 = p1 / alpha_multiplier;
2557 p2 = p2 / alpha_multiplier;
2558 if alpha_multiplier > 1.0 {
2559 alpha_multiplier = 1.0;
2560 }
2561 }
2562
2563 let mut result_color = first_color.interpolate(p1, &second_color, p2);
2564 result_color.unpremultiply(alpha_multiplier);
2565
2566 result_color.into()
2567 }
2568}
2569
2570pub trait Interpolate {
2572 fn adjust_powerless_components(&mut self) {}
2574 fn fill_missing_components(&mut self, other: &Self);
2576 fn adjust_hue(&mut self, _: &mut Self, _: HueInterpolationMethod) {}
2578 fn premultiply(&mut self);
2580 fn unpremultiply(&mut self, alpha_multiplier: f32);
2582 fn interpolate(&self, p1: f32, other: &Self, p2: f32) -> Self;
2584}
2585
2586macro_rules! interpolate {
2587 ($a: ident, $b: ident, $c: ident) => {
2588 fn fill_missing_components(&mut self, other: &Self) {
2589 if self.$a.is_nan() {
2590 self.$a = other.$a;
2591 }
2592
2593 if self.$b.is_nan() {
2594 self.$b = other.$b;
2595 }
2596
2597 if self.$c.is_nan() {
2598 self.$c = other.$c;
2599 }
2600
2601 if self.alpha.is_nan() {
2602 self.alpha = other.alpha;
2603 }
2604 }
2605
2606 fn interpolate(&self, p1: f32, other: &Self, p2: f32) -> Self {
2607 Self {
2608 $a: self.$a * p1 + other.$a * p2,
2609 $b: self.$b * p1 + other.$b * p2,
2610 $c: self.$c * p1 + other.$c * p2,
2611 alpha: self.alpha * p1 + other.alpha * p2,
2612 }
2613 }
2614 };
2615}
2616
2617macro_rules! rectangular_premultiply {
2618 ($a: ident, $b: ident, $c: ident) => {
2619 fn premultiply(&mut self) {
2620 if !self.alpha.is_nan() {
2621 self.$a *= self.alpha;
2622 self.$b *= self.alpha;
2623 self.$c *= self.alpha;
2624 }
2625 }
2626
2627 fn unpremultiply(&mut self, alpha_multiplier: f32) {
2628 if !self.alpha.is_nan() && self.alpha != 0.0 {
2629 self.$a /= self.alpha;
2630 self.$b /= self.alpha;
2631 self.$c /= self.alpha;
2632 self.alpha *= alpha_multiplier;
2633 }
2634 }
2635 };
2636}
2637
2638macro_rules! polar_premultiply {
2639 ($a: ident, $b: ident) => {
2640 fn premultiply(&mut self) {
2641 if !self.alpha.is_nan() {
2642 self.$a *= self.alpha;
2643 self.$b *= self.alpha;
2644 }
2645 }
2646
2647 fn unpremultiply(&mut self, alpha_multiplier: f32) {
2648 self.h %= 360.0;
2649 if !self.alpha.is_nan() {
2650 self.$a /= self.alpha;
2651 self.$b /= self.alpha;
2652 self.alpha *= alpha_multiplier;
2653 }
2654 }
2655 };
2656}
2657
2658macro_rules! adjust_powerless_lab {
2659 () => {
2660 fn adjust_powerless_components(&mut self) {
2661 if self.l.abs() < f32::EPSILON {
2663 self.a = f32::NAN;
2664 self.b = f32::NAN;
2665 }
2666 }
2667 };
2668}
2669
2670macro_rules! adjust_powerless_lch {
2671 () => {
2672 fn adjust_powerless_components(&mut self) {
2673 if self.c.abs() < f32::EPSILON {
2676 self.h = f32::NAN;
2677 }
2678
2679 if self.l.abs() < f32::EPSILON {
2680 self.c = f32::NAN;
2681 self.h = f32::NAN;
2682 }
2683 }
2684
2685 fn adjust_hue(&mut self, other: &mut Self, method: HueInterpolationMethod) {
2686 method.interpolate(&mut self.h, &mut other.h);
2687 }
2688 };
2689}
2690
2691impl Interpolate for SRGB {
2692 rectangular_premultiply!(r, g, b);
2693 interpolate!(r, g, b);
2694}
2695
2696impl Interpolate for SRGBLinear {
2697 rectangular_premultiply!(r, g, b);
2698 interpolate!(r, g, b);
2699}
2700
2701impl Interpolate for XYZd50 {
2702 rectangular_premultiply!(x, y, z);
2703 interpolate!(x, y, z);
2704}
2705
2706impl Interpolate for XYZd65 {
2707 rectangular_premultiply!(x, y, z);
2708 interpolate!(x, y, z);
2709}
2710
2711impl Interpolate for LAB {
2712 adjust_powerless_lab!();
2713 rectangular_premultiply!(l, a, b);
2714 interpolate!(l, a, b);
2715}
2716
2717impl Interpolate for OKLAB {
2718 adjust_powerless_lab!();
2719 rectangular_premultiply!(l, a, b);
2720 interpolate!(l, a, b);
2721}
2722
2723impl Interpolate for LCH {
2724 adjust_powerless_lch!();
2725 polar_premultiply!(l, c);
2726 interpolate!(l, c, h);
2727}
2728
2729impl Interpolate for OKLCH {
2730 adjust_powerless_lch!();
2731 polar_premultiply!(l, c);
2732 interpolate!(l, c, h);
2733}
2734
2735impl Interpolate for HSL {
2736 polar_premultiply!(s, l);
2737
2738 fn adjust_powerless_components(&mut self) {
2739 if self.s.abs() < f32::EPSILON {
2742 self.h = f32::NAN;
2743 }
2744
2745 if self.l.abs() < f32::EPSILON || (self.l - 1.0).abs() < f32::EPSILON {
2746 self.h = f32::NAN;
2747 self.s = f32::NAN;
2748 }
2749 }
2750
2751 fn adjust_hue(&mut self, other: &mut Self, method: HueInterpolationMethod) {
2752 method.interpolate(&mut self.h, &mut other.h);
2753 }
2754
2755 interpolate!(h, s, l);
2756}
2757
2758impl Interpolate for HWB {
2759 polar_premultiply!(w, b);
2760
2761 fn adjust_powerless_components(&mut self) {
2762 if (self.w + self.b - 1.0).abs() < f32::EPSILON {
2765 self.h = f32::NAN;
2766 }
2767 }
2768
2769 fn adjust_hue(&mut self, other: &mut Self, method: HueInterpolationMethod) {
2770 method.interpolate(&mut self.h, &mut other.h);
2771 }
2772
2773 interpolate!(h, w, b);
2774}
2775
2776impl HueInterpolationMethod {
2777 fn interpolate(&self, a: &mut f32, b: &mut f32) {
2778 if *self != HueInterpolationMethod::Specified {
2780 *a = ((*a % 360.0) + 360.0) % 360.0;
2781 *b = ((*b % 360.0) + 360.0) % 360.0;
2782 }
2783
2784 match self {
2785 HueInterpolationMethod::Shorter => {
2786 let delta = *b - *a;
2788 if delta > 180.0 {
2789 *a += 360.0;
2790 } else if delta < -180.0 {
2791 *b += 360.0;
2792 }
2793 }
2794 HueInterpolationMethod::Longer => {
2795 let delta = *b - *a;
2797 if 0.0 < delta && delta < 180.0 {
2798 *a += 360.0;
2799 } else if -180.0 < delta && delta < 0.0 {
2800 *b += 360.0;
2801 }
2802 }
2803 HueInterpolationMethod::Increasing => {
2804 if *b < *a {
2806 *b += 360.0;
2807 }
2808 }
2809 HueInterpolationMethod::Decreasing => {
2810 if *a < *b {
2812 *a += 360.0;
2813 }
2814 }
2815 HueInterpolationMethod::Specified => {}
2816 }
2817 }
2818}