style/color/
mod.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5//! Color support functions.
6
7/// cbindgen:ignore
8pub mod convert;
9
10mod color_function;
11pub mod component;
12pub mod mix;
13pub mod parsing;
14mod to_css;
15
16use self::parsing::ChannelKeyword;
17use crate::derives::*;
18pub use color_function::*;
19use component::ColorComponent;
20use cssparser::color::PredefinedColorSpace;
21
22/// The 3 components that make up a color.  (Does not include the alpha component)
23#[derive(Copy, Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
24#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
25#[repr(C)]
26pub struct ColorComponents(pub f32, pub f32, pub f32);
27
28impl ColorComponents {
29    /// Apply a function to each of the 3 components of the color.
30    #[must_use]
31    pub fn map(self, f: impl Fn(f32) -> f32) -> Self {
32        Self(f(self.0), f(self.1), f(self.2))
33    }
34}
35
36impl std::ops::Mul for ColorComponents {
37    type Output = Self;
38
39    fn mul(self, rhs: Self) -> Self::Output {
40        Self(self.0 * rhs.0, self.1 * rhs.1, self.2 * rhs.2)
41    }
42}
43
44impl std::ops::Div for ColorComponents {
45    type Output = Self;
46
47    fn div(self, rhs: Self) -> Self::Output {
48        Self(self.0 / rhs.0, self.1 / rhs.1, self.2 / rhs.2)
49    }
50}
51
52/// A color space representation in the CSS specification.
53///
54/// https://drafts.csswg.org/css-color-4/#typedef-color-space
55#[derive(
56    Clone,
57    Copy,
58    Debug,
59    Eq,
60    MallocSizeOf,
61    Parse,
62    PartialEq,
63    ToAnimatedValue,
64    ToComputedValue,
65    ToCss,
66    ToResolvedValue,
67    ToShmem,
68)]
69#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
70#[repr(u8)]
71pub enum ColorSpace {
72    /// A color specified in the sRGB color space with either the rgb/rgba(..)
73    /// functions or the newer color(srgb ..) function. If the color(..)
74    /// function is used, the AS_COLOR_FUNCTION flag will be set. Examples:
75    /// "color(srgb 0.691 0.139 0.259)", "rgb(176, 35, 66)"
76    Srgb = 0,
77    /// A color specified in the Hsl notation in the sRGB color space, e.g.
78    /// "hsl(289.18 93.136% 65.531%)"
79    /// https://drafts.csswg.org/css-color-4/#the-hsl-notation
80    Hsl,
81    /// A color specified in the Hwb notation in the sRGB color space, e.g.
82    /// "hwb(740deg 20% 30%)"
83    /// https://drafts.csswg.org/css-color-4/#the-hwb-notation
84    Hwb,
85    /// A color specified in the Lab color format, e.g.
86    /// "lab(29.2345% 39.3825 20.0664)".
87    /// https://w3c.github.io/csswg-drafts/css-color-4/#lab-colors
88    Lab,
89    /// A color specified in the Lch color format, e.g.
90    /// "lch(29.2345% 44.2 27)".
91    /// https://w3c.github.io/csswg-drafts/css-color-4/#lch-colors
92    Lch,
93    /// A color specified in the Oklab color format, e.g.
94    /// "oklab(40.101% 0.1147 0.0453)".
95    /// https://w3c.github.io/csswg-drafts/css-color-4/#lab-colors
96    Oklab,
97    /// A color specified in the Oklch color format, e.g.
98    /// "oklch(40.101% 0.12332 21.555)".
99    /// https://w3c.github.io/csswg-drafts/css-color-4/#lch-colors
100    Oklch,
101    /// A color specified with the color(..) function and the "srgb-linear"
102    /// color space, e.g. "color(srgb-linear 0.435 0.017 0.055)".
103    SrgbLinear,
104    /// A color specified with the color(..) function and the "display-p3"
105    /// color space, e.g. "color(display-p3 0.84 0.19 0.72)".
106    DisplayP3,
107    /// A color specified with the color(..) function and the "display-p3-linear"
108    /// color space.
109    DisplayP3Linear,
110    /// A color specified with the color(..) function and the "a98-rgb" color
111    /// space, e.g. "color(a98-rgb 0.44091 0.49971 0.37408)".
112    A98Rgb,
113    /// A color specified with the color(..) function and the "prophoto-rgb"
114    /// color space, e.g. "color(prophoto-rgb 0.36589 0.41717 0.31333)".
115    ProphotoRgb,
116    /// A color specified with the color(..) function and the "rec2020" color
117    /// space, e.g. "color(rec2020 0.42210 0.47580 0.35605)".
118    Rec2020,
119    /// A color specified with the color(..) function and the "xyz-d50" color
120    /// space, e.g. "color(xyz-d50 0.2005 0.14089 0.4472)".
121    XyzD50,
122    /// A color specified with the color(..) function and the "xyz-d65" or "xyz"
123    /// color space, e.g. "color(xyz-d65 0.21661 0.14602 0.59452)".
124    /// NOTE: https://drafts.csswg.org/css-color-4/#resolving-color-function-values
125    ///       specifies that `xyz` is an alias for the `xyz-d65` color space.
126    #[parse(aliases = "xyz")]
127    XyzD65,
128}
129
130impl ColorSpace {
131    /// Returns whether this is a `<rectangular-color-space>`.
132    #[inline]
133    pub fn is_rectangular(&self) -> bool {
134        !self.is_polar()
135    }
136
137    /// Returns whether this is a `<polar-color-space>`.
138    #[inline]
139    pub fn is_polar(&self) -> bool {
140        matches!(self, Self::Hsl | Self::Hwb | Self::Lch | Self::Oklch)
141    }
142
143    /// Returns true if the color has RGB or XYZ components.
144    #[inline]
145    pub fn is_rgb_or_xyz_like(&self) -> bool {
146        match self {
147            Self::Srgb
148            | Self::SrgbLinear
149            | Self::DisplayP3
150            | Self::DisplayP3Linear
151            | Self::A98Rgb
152            | Self::ProphotoRgb
153            | Self::Rec2020
154            | Self::XyzD50
155            | Self::XyzD65 => true,
156            _ => false,
157        }
158    }
159
160    /// Returns an index of the hue component in the color space, otherwise
161    /// `None`.
162    #[inline]
163    pub fn hue_index(&self) -> Option<usize> {
164        match self {
165            Self::Hsl | Self::Hwb => Some(0),
166            Self::Lch | Self::Oklch => Some(2),
167
168            _ => {
169                debug_assert!(!self.is_polar());
170                None
171            },
172        }
173    }
174}
175
176/// Flags used when serializing colors.
177#[derive(Clone, Copy, Debug, Default, MallocSizeOf, PartialEq, ToShmem)]
178#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
179#[repr(C)]
180pub struct ColorFlags(u8);
181bitflags! {
182    impl ColorFlags : u8 {
183        /// Whether the 1st color component is `none`.
184        const C0_IS_NONE = 1 << 0;
185        /// Whether the 2nd color component is `none`.
186        const C1_IS_NONE = 1 << 1;
187        /// Whether the 3rd color component is `none`.
188        const C2_IS_NONE = 1 << 2;
189        /// Whether the alpha component is `none`.
190        const ALPHA_IS_NONE = 1 << 3;
191        /// Marks that this color is in the legacy color format. This flag is
192        /// only valid for the `Srgb` color space.
193        const IS_LEGACY_SRGB = 1 << 4;
194    }
195}
196
197/// An absolutely specified color, using either rgb(), rgba(), lab(), lch(),
198/// oklab(), oklch() or color().
199#[derive(Copy, Clone, Debug, MallocSizeOf, PartialEq, ToShmem, ToTyped)]
200#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
201#[repr(C)]
202pub struct AbsoluteColor {
203    /// The 3 components that make up colors in any color space.
204    pub components: ColorComponents,
205    /// The alpha component of the color.
206    pub alpha: f32,
207    /// The current color space that the components represent.
208    pub color_space: ColorSpace,
209    /// Extra flags used durring serialization of this color.
210    pub flags: ColorFlags,
211}
212
213/// Given an [`AbsoluteColor`], return the 4 float components as the type given,
214/// e.g.:
215///
216/// ```rust
217/// let srgb = AbsoluteColor::new(ColorSpace::Srgb, 1.0, 0.0, 0.0, 0.0);
218/// let floats = color_components_as!(&srgb, [f32; 4]); // [1.0, 0.0, 0.0, 0.0]
219/// ```
220macro_rules! color_components_as {
221    ($c:expr, $t:ty) => {{
222        // This macro is not an inline function, because we can't use the
223        // generic  type ($t) in a constant expression as per:
224        // https://github.com/rust-lang/rust/issues/76560
225        const_assert_eq!(std::mem::size_of::<$t>(), std::mem::size_of::<[f32; 4]>());
226        const_assert_eq!(std::mem::align_of::<$t>(), std::mem::align_of::<[f32; 4]>());
227        const_assert!(std::mem::size_of::<AbsoluteColor>() >= std::mem::size_of::<$t>());
228        const_assert_eq!(
229            std::mem::align_of::<AbsoluteColor>(),
230            std::mem::align_of::<$t>()
231        );
232
233        std::mem::transmute::<&ColorComponents, &$t>(&$c.components)
234    }};
235}
236
237/// Holds details about each component passed into creating a new [`AbsoluteColor`].
238pub struct ComponentDetails {
239    value: f32,
240    is_none: bool,
241}
242
243impl From<f32> for ComponentDetails {
244    fn from(value: f32) -> Self {
245        Self {
246            value,
247            is_none: false,
248        }
249    }
250}
251
252impl From<u8> for ComponentDetails {
253    fn from(value: u8) -> Self {
254        Self {
255            value: value as f32 / 255.0,
256            is_none: false,
257        }
258    }
259}
260
261impl From<Option<f32>> for ComponentDetails {
262    fn from(value: Option<f32>) -> Self {
263        if let Some(value) = value {
264            Self {
265                value,
266                is_none: false,
267            }
268        } else {
269            Self {
270                value: 0.0,
271                is_none: true,
272            }
273        }
274    }
275}
276
277impl From<ColorComponent<f32>> for ComponentDetails {
278    fn from(value: ColorComponent<f32>) -> Self {
279        if let ColorComponent::Value(value) = value {
280            Self {
281                value,
282                is_none: false,
283            }
284        } else {
285            Self {
286                value: 0.0,
287                is_none: true,
288            }
289        }
290    }
291}
292
293impl AbsoluteColor {
294    /// A fully transparent color in the legacy syntax.
295    pub const TRANSPARENT_BLACK: Self = Self {
296        components: ColorComponents(0.0, 0.0, 0.0),
297        alpha: 0.0,
298        color_space: ColorSpace::Srgb,
299        flags: ColorFlags::IS_LEGACY_SRGB,
300    };
301
302    /// An opaque black color in the legacy syntax.
303    pub const BLACK: Self = Self {
304        components: ColorComponents(0.0, 0.0, 0.0),
305        alpha: 1.0,
306        color_space: ColorSpace::Srgb,
307        flags: ColorFlags::IS_LEGACY_SRGB,
308    };
309
310    /// An opaque white color in the legacy syntax.
311    pub const WHITE: Self = Self {
312        components: ColorComponents(1.0, 1.0, 1.0),
313        alpha: 1.0,
314        color_space: ColorSpace::Srgb,
315        flags: ColorFlags::IS_LEGACY_SRGB,
316    };
317
318    /// Create a new [`AbsoluteColor`] with the given [`ColorSpace`] and
319    /// components.
320    pub fn new(
321        color_space: ColorSpace,
322        c1: impl Into<ComponentDetails>,
323        c2: impl Into<ComponentDetails>,
324        c3: impl Into<ComponentDetails>,
325        alpha: impl Into<ComponentDetails>,
326    ) -> Self {
327        let mut flags = ColorFlags::empty();
328
329        macro_rules! cd {
330            ($c:expr,$flag:expr) => {{
331                let component_details = $c.into();
332                if component_details.is_none {
333                    flags |= $flag;
334                }
335                component_details.value
336            }};
337        }
338
339        let mut components = ColorComponents(
340            cd!(c1, ColorFlags::C0_IS_NONE),
341            cd!(c2, ColorFlags::C1_IS_NONE),
342            cd!(c3, ColorFlags::C2_IS_NONE),
343        );
344
345        let alpha = cd!(alpha, ColorFlags::ALPHA_IS_NONE);
346
347        // Lightness for Lab and Lch is clamped to [0..100].
348        if matches!(color_space, ColorSpace::Lab | ColorSpace::Lch) {
349            components.0 = components.0.clamp(0.0, 100.0);
350        }
351
352        // Lightness for Oklab and Oklch is clamped to [0..1].
353        if matches!(color_space, ColorSpace::Oklab | ColorSpace::Oklch) {
354            components.0 = components.0.clamp(0.0, 1.0);
355        }
356
357        // Chroma must not be less than 0.
358        if matches!(color_space, ColorSpace::Lch | ColorSpace::Oklch) {
359            components.1 = components.1.max(0.0);
360        }
361
362        // Alpha is always clamped to [0..1].
363        let alpha = alpha.clamp(0.0, 1.0);
364
365        Self {
366            components,
367            alpha,
368            color_space,
369            flags,
370        }
371    }
372
373    /// Convert this color into the sRGB color space and set it to the legacy
374    /// syntax.
375    #[inline]
376    #[must_use]
377    pub fn into_srgb_legacy(self) -> Self {
378        let mut result = if !matches!(self.color_space, ColorSpace::Srgb) {
379            self.to_color_space(ColorSpace::Srgb)
380        } else {
381            self
382        };
383
384        // Explicitly set the flags to IS_LEGACY_SRGB only to clear out the
385        // *_IS_NONE flags, because the legacy syntax doesn't allow "none".
386        result.flags = ColorFlags::IS_LEGACY_SRGB;
387
388        result
389    }
390
391    /// Create a new [`AbsoluteColor`] from rgba legacy syntax values in the sRGB color space.
392    pub fn srgb_legacy(red: u8, green: u8, blue: u8, alpha: f32) -> Self {
393        let mut result = Self::new(ColorSpace::Srgb, red, green, blue, alpha);
394        result.flags = ColorFlags::IS_LEGACY_SRGB;
395        result
396    }
397
398    /// Return all the components of the color in an array.  (Includes alpha)
399    #[inline]
400    pub fn raw_components(&self) -> &[f32; 4] {
401        unsafe { color_components_as!(self, [f32; 4]) }
402    }
403
404    /// Returns true if this color is in the legacy color syntax.
405    #[inline]
406    pub fn is_legacy_syntax(&self) -> bool {
407        // rgb(), rgba(), hsl(), hsla(), hwb(), hwba()
408        match self.color_space {
409            ColorSpace::Srgb => self.flags.contains(ColorFlags::IS_LEGACY_SRGB),
410            ColorSpace::Hsl | ColorSpace::Hwb => true,
411            _ => false,
412        }
413    }
414
415    /// Returns true if this color is fully transparent.
416    #[inline]
417    pub fn is_transparent(&self) -> bool {
418        self.flags.contains(ColorFlags::ALPHA_IS_NONE) || self.alpha == 0.0
419    }
420
421    /// Return an optional first component.
422    #[inline]
423    pub fn c0(&self) -> Option<f32> {
424        if self.flags.contains(ColorFlags::C0_IS_NONE) {
425            None
426        } else {
427            Some(self.components.0)
428        }
429    }
430
431    /// Return an optional second component.
432    #[inline]
433    pub fn c1(&self) -> Option<f32> {
434        if self.flags.contains(ColorFlags::C1_IS_NONE) {
435            None
436        } else {
437            Some(self.components.1)
438        }
439    }
440
441    /// Return an optional second component.
442    #[inline]
443    pub fn c2(&self) -> Option<f32> {
444        if self.flags.contains(ColorFlags::C2_IS_NONE) {
445            None
446        } else {
447            Some(self.components.2)
448        }
449    }
450
451    /// Return an optional alpha component.
452    #[inline]
453    pub fn alpha(&self) -> Option<f32> {
454        if self.flags.contains(ColorFlags::ALPHA_IS_NONE) {
455            None
456        } else {
457            Some(self.alpha)
458        }
459    }
460
461    /// Return the value of a component by its channel keyword.
462    pub fn get_component_by_channel_keyword(
463        &self,
464        channel_keyword: ChannelKeyword,
465    ) -> Result<Option<f32>, ()> {
466        if channel_keyword == ChannelKeyword::Alpha {
467            return Ok(self.alpha());
468        }
469
470        Ok(match self.color_space {
471            ColorSpace::Srgb => {
472                if self.flags.contains(ColorFlags::IS_LEGACY_SRGB) {
473                    match channel_keyword {
474                        ChannelKeyword::R => self.c0().map(|v| v * 255.0),
475                        ChannelKeyword::G => self.c1().map(|v| v * 255.0),
476                        ChannelKeyword::B => self.c2().map(|v| v * 255.0),
477                        _ => return Err(()),
478                    }
479                } else {
480                    match channel_keyword {
481                        ChannelKeyword::R => self.c0(),
482                        ChannelKeyword::G => self.c1(),
483                        ChannelKeyword::B => self.c2(),
484                        _ => return Err(()),
485                    }
486                }
487            },
488            ColorSpace::Hsl => match channel_keyword {
489                ChannelKeyword::H => self.c0(),
490                ChannelKeyword::S => self.c1(),
491                ChannelKeyword::L => self.c2(),
492                _ => return Err(()),
493            },
494            ColorSpace::Hwb => match channel_keyword {
495                ChannelKeyword::H => self.c0(),
496                ChannelKeyword::W => self.c1(),
497                ChannelKeyword::B => self.c2(),
498                _ => return Err(()),
499            },
500            ColorSpace::Lab | ColorSpace::Oklab => match channel_keyword {
501                ChannelKeyword::L => self.c0(),
502                ChannelKeyword::A => self.c1(),
503                ChannelKeyword::B => self.c2(),
504                _ => return Err(()),
505            },
506            ColorSpace::Lch | ColorSpace::Oklch => match channel_keyword {
507                ChannelKeyword::L => self.c0(),
508                ChannelKeyword::C => self.c1(),
509                ChannelKeyword::H => self.c2(),
510                _ => return Err(()),
511            },
512            ColorSpace::SrgbLinear
513            | ColorSpace::DisplayP3
514            | ColorSpace::DisplayP3Linear
515            | ColorSpace::A98Rgb
516            | ColorSpace::ProphotoRgb
517            | ColorSpace::Rec2020 => match channel_keyword {
518                ChannelKeyword::R => self.c0(),
519                ChannelKeyword::G => self.c1(),
520                ChannelKeyword::B => self.c2(),
521                _ => return Err(()),
522            },
523            ColorSpace::XyzD50 | ColorSpace::XyzD65 => match channel_keyword {
524                ChannelKeyword::X => self.c0(),
525                ChannelKeyword::Y => self.c1(),
526                ChannelKeyword::Z => self.c2(),
527                _ => return Err(()),
528            },
529        })
530    }
531
532    /// Convert this color to the specified color space.
533    pub fn to_color_space(&self, color_space: ColorSpace) -> Self {
534        use ColorSpace::*;
535
536        if self.color_space == color_space {
537            return self.clone();
538        }
539
540        // Conversion functions doesn't handle NAN component values, so they are
541        // converted to 0.0. They do however need to know if a component is
542        // missing, so we use NAN as the marker for that.
543        macro_rules! missing_to_nan {
544            ($c:expr) => {{
545                if let Some(v) = $c {
546                    crate::values::normalize(v)
547                } else {
548                    f32::NAN
549                }
550            }};
551        }
552
553        let components = ColorComponents(
554            missing_to_nan!(self.c0()),
555            missing_to_nan!(self.c1()),
556            missing_to_nan!(self.c2()),
557        );
558
559        let result = match (self.color_space, color_space) {
560            // We have simplified conversions that do not need to convert to XYZ
561            // first. This improves performance, because it skips at least 2
562            // matrix multiplications and reduces float rounding errors.
563            (Srgb, Hsl) => convert::rgb_to_hsl(&components),
564            (Srgb, Hwb) => convert::rgb_to_hwb(&components),
565            (Hsl, Srgb) => convert::hsl_to_rgb(&components),
566            (Hwb, Srgb) => convert::hwb_to_rgb(&components),
567            (Lab, Lch) | (Oklab, Oklch) => convert::orthogonal_to_polar(
568                &components,
569                convert::epsilon_for_range(0.0, if color_space == Lch { 100.0 } else { 1.0 }),
570            ),
571            (Lch, Lab) | (Oklch, Oklab) => convert::polar_to_orthogonal(&components),
572
573            // All other conversions need to convert to XYZ first.
574            _ => {
575                let (xyz, white_point) = match self.color_space {
576                    Lab => convert::to_xyz::<convert::Lab>(&components),
577                    Lch => convert::to_xyz::<convert::Lch>(&components),
578                    Oklab => convert::to_xyz::<convert::Oklab>(&components),
579                    Oklch => convert::to_xyz::<convert::Oklch>(&components),
580                    Srgb => convert::to_xyz::<convert::Srgb>(&components),
581                    Hsl => convert::to_xyz::<convert::Hsl>(&components),
582                    Hwb => convert::to_xyz::<convert::Hwb>(&components),
583                    SrgbLinear => convert::to_xyz::<convert::SrgbLinear>(&components),
584                    DisplayP3 => convert::to_xyz::<convert::DisplayP3>(&components),
585                    DisplayP3Linear => convert::to_xyz::<convert::DisplayP3Linear>(&components),
586                    A98Rgb => convert::to_xyz::<convert::A98Rgb>(&components),
587                    ProphotoRgb => convert::to_xyz::<convert::ProphotoRgb>(&components),
588                    Rec2020 => convert::to_xyz::<convert::Rec2020>(&components),
589                    XyzD50 => convert::to_xyz::<convert::XyzD50>(&components),
590                    XyzD65 => convert::to_xyz::<convert::XyzD65>(&components),
591                };
592
593                match color_space {
594                    Lab => convert::from_xyz::<convert::Lab>(&xyz, white_point),
595                    Lch => convert::from_xyz::<convert::Lch>(&xyz, white_point),
596                    Oklab => convert::from_xyz::<convert::Oklab>(&xyz, white_point),
597                    Oklch => convert::from_xyz::<convert::Oklch>(&xyz, white_point),
598                    Srgb => convert::from_xyz::<convert::Srgb>(&xyz, white_point),
599                    Hsl => convert::from_xyz::<convert::Hsl>(&xyz, white_point),
600                    Hwb => convert::from_xyz::<convert::Hwb>(&xyz, white_point),
601                    SrgbLinear => convert::from_xyz::<convert::SrgbLinear>(&xyz, white_point),
602                    DisplayP3 => convert::from_xyz::<convert::DisplayP3>(&xyz, white_point),
603                    DisplayP3Linear => {
604                        convert::from_xyz::<convert::DisplayP3Linear>(&xyz, white_point)
605                    },
606                    A98Rgb => convert::from_xyz::<convert::A98Rgb>(&xyz, white_point),
607                    ProphotoRgb => convert::from_xyz::<convert::ProphotoRgb>(&xyz, white_point),
608                    Rec2020 => convert::from_xyz::<convert::Rec2020>(&xyz, white_point),
609                    XyzD50 => convert::from_xyz::<convert::XyzD50>(&xyz, white_point),
610                    XyzD65 => convert::from_xyz::<convert::XyzD65>(&xyz, white_point),
611                }
612            },
613        };
614
615        // A NAN value coming from a conversion function means the the component
616        // is missing, so we convert it to None.
617        macro_rules! nan_to_missing {
618            ($v:expr) => {{
619                if $v.is_nan() {
620                    None
621                } else {
622                    Some($v)
623                }
624            }};
625        }
626
627        Self::new(
628            color_space,
629            nan_to_missing!(result.0),
630            nan_to_missing!(result.1),
631            nan_to_missing!(result.2),
632            self.alpha(),
633        )
634    }
635}
636
637impl From<PredefinedColorSpace> for ColorSpace {
638    fn from(value: PredefinedColorSpace) -> Self {
639        match value {
640            PredefinedColorSpace::Srgb => ColorSpace::Srgb,
641            PredefinedColorSpace::SrgbLinear => ColorSpace::SrgbLinear,
642            PredefinedColorSpace::DisplayP3 => ColorSpace::DisplayP3,
643            PredefinedColorSpace::DisplayP3Linear => ColorSpace::DisplayP3Linear,
644            PredefinedColorSpace::A98Rgb => ColorSpace::A98Rgb,
645            PredefinedColorSpace::ProphotoRgb => ColorSpace::ProphotoRgb,
646            PredefinedColorSpace::Rec2020 => ColorSpace::Rec2020,
647            PredefinedColorSpace::XyzD50 => ColorSpace::XyzD50,
648            PredefinedColorSpace::XyzD65 => ColorSpace::XyzD65,
649        }
650    }
651}