mod.rs (23230B)
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 8 pub mod convert; 9 10 mod color_function; 11 pub mod component; 12 pub mod mix; 13 pub mod parsing; 14 mod to_css; 15 16 use self::parsing::ChannelKeyword; 17 use crate::derives::*; 18 pub use color_function::*; 19 use component::ColorComponent; 20 use 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)] 26 pub struct ColorComponents(pub f32, pub f32, pub f32); 27 28 impl 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 36 impl 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 44 impl 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)] 71 pub 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 130 impl 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)] 180 pub struct ColorFlags(u8); 181 bitflags! { 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)] 202 pub 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 /// ``` 220 macro_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`]. 238 pub struct ComponentDetails { 239 value: f32, 240 is_none: bool, 241 } 242 243 impl From<f32> for ComponentDetails { 244 fn from(value: f32) -> Self { 245 Self { 246 value, 247 is_none: false, 248 } 249 } 250 } 251 252 impl 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 261 impl 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 277 impl 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 293 impl 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 637 impl 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 }