diff --git a/crates/bevy_color/crates/gen_tests/src/main.rs b/crates/bevy_color/crates/gen_tests/src/main.rs index c58c2d850138d..80aa88b288683 100644 --- a/crates/bevy_color/crates/gen_tests/src/main.rs +++ b/crates/bevy_color/crates/gen_tests/src/main.rs @@ -1,4 +1,4 @@ -use palette::{Hsl, Hsv, Hwb, IntoColor, Lab, Lch, LinSrgb, Oklab, Srgb, Xyz}; +use palette::{Hsl, Hsv, Hwb, IntoColor, Lab, Lch, LinSrgb, Oklab, Oklch, Srgb, Xyz}; const TEST_COLORS: &[(f32, f32, f32, &str)] = &[ (0., 0., 0., "black"), @@ -25,7 +25,7 @@ fn main() { println!( "// Generated by gen_tests. Do not edit. #[cfg(test)] -use crate::{{Hsla, Hsva, Hwba, Srgba, LinearRgba, Oklaba, Laba, Lcha, Xyza}}; +use crate::{{Hsla, Hsva, Hwba, Srgba, LinearRgba, Oklaba, Oklcha, Laba, Lcha, Xyza}}; #[cfg(test)] pub struct TestColor {{ @@ -38,6 +38,7 @@ pub struct TestColor {{ pub lab: Laba, pub lch: Lcha, pub oklab: Oklaba, + pub oklch: Oklcha, pub xyz: Xyza, }} " @@ -55,6 +56,7 @@ pub struct TestColor {{ let lab: Lab = srgb.into_color(); let lch: Lch = srgb.into_color(); let oklab: Oklab = srgb.into_color(); + let oklch: Oklch = srgb.into_color(); let xyz: Xyz = srgb.into_color(); println!(" // {name}"); println!( @@ -68,6 +70,7 @@ pub struct TestColor {{ lab: Laba::new({}, {}, {}, 1.0), lch: Lcha::new({}, {}, {}, 1.0), oklab: Oklaba::new({}, {}, {}, 1.0), + oklch: Oklcha::new({}, {}, {}, 1.0), xyz: Xyza::new({}, {}, {}, 1.0), }},", VariablePrecision(srgb.red), @@ -94,6 +97,9 @@ pub struct TestColor {{ VariablePrecision(oklab.l), VariablePrecision(oklab.a), VariablePrecision(oklab.b), + VariablePrecision(oklch.l), + VariablePrecision(oklch.chroma), + VariablePrecision(oklch.hue.into_positive_degrees()), VariablePrecision(xyz.x), VariablePrecision(xyz.y), VariablePrecision(xyz.z), diff --git a/crates/bevy_color/src/color.rs b/crates/bevy_color/src/color.rs index 39b3a931127b7..3a460761b176c 100644 --- a/crates/bevy_color/src/color.rs +++ b/crates/bevy_color/src/color.rs @@ -1,4 +1,6 @@ -use crate::{Alpha, Hsla, Hsva, Hwba, Laba, Lcha, LinearRgba, Oklaba, Srgba, StandardColor, Xyza}; +use crate::{ + Alpha, Hsla, Hsva, Hwba, Laba, Lcha, LinearRgba, Oklaba, Oklcha, Srgba, StandardColor, Xyza, +}; use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize}; use serde::{Deserialize, Serialize}; @@ -29,6 +31,8 @@ pub enum Color { Lcha(Lcha), /// A color in the Oklaba color space with alpha. Oklaba(Oklaba), + /// A color in the Oklcha color space with alpha. + Oklcha(Oklcha), /// A color in the XYZ color space with alpha. Xyza(Xyza), } @@ -196,6 +200,26 @@ impl Color { }) } + /// Creates a new [`Color`] object storing a [`Oklcha`] color. + pub const fn oklcha(lightness: f32, chroma: f32, hue: f32, alpha: f32) -> Self { + Self::Oklcha(Oklcha { + lightness, + chroma, + hue, + alpha, + }) + } + + /// Creates a new [`Color`] object storing a [`Oklcha`] color with an alpha of 1.0. + pub const fn oklch(lightness: f32, chroma: f32, hue: f32) -> Self { + Self::Oklcha(Oklcha { + lightness, + chroma, + hue, + alpha: 1.0, + }) + } + /// Creates a new [`Color`] object storing a [`Xyza`] color. pub const fn xyza(x: f32, y: f32, z: f32, alpha: f32) -> Self { Self::Xyza(Xyza { x, y, z, alpha }) @@ -241,6 +265,7 @@ impl Alpha for Color { Color::Laba(x) => *x = x.with_alpha(alpha), Color::Lcha(x) => *x = x.with_alpha(alpha), Color::Oklaba(x) => *x = x.with_alpha(alpha), + Color::Oklcha(x) => *x = x.with_alpha(alpha), Color::Xyza(x) => *x = x.with_alpha(alpha), } @@ -257,6 +282,7 @@ impl Alpha for Color { Color::Laba(x) => x.alpha(), Color::Lcha(x) => x.alpha(), Color::Oklaba(x) => x.alpha(), + Color::Oklcha(x) => x.alpha(), Color::Xyza(x) => x.alpha(), } } @@ -298,6 +324,12 @@ impl From for Color { } } +impl From for Color { + fn from(value: Oklcha) -> Self { + Self::Oklcha(value) + } +} + impl From for Color { fn from(value: Lcha) -> Self { Self::Lcha(value) @@ -327,6 +359,7 @@ impl From for Srgba { Color::Laba(laba) => laba.into(), Color::Lcha(lcha) => lcha.into(), Color::Oklaba(oklab) => oklab.into(), + Color::Oklcha(oklch) => oklch.into(), Color::Xyza(xyza) => xyza.into(), } } @@ -343,6 +376,7 @@ impl From for LinearRgba { Color::Laba(laba) => laba.into(), Color::Lcha(lcha) => lcha.into(), Color::Oklaba(oklab) => oklab.into(), + Color::Oklcha(oklch) => oklch.into(), Color::Xyza(xyza) => xyza.into(), } } @@ -359,6 +393,7 @@ impl From for Hsla { Color::Laba(laba) => laba.into(), Color::Lcha(lcha) => lcha.into(), Color::Oklaba(oklab) => oklab.into(), + Color::Oklcha(oklch) => oklch.into(), Color::Xyza(xyza) => xyza.into(), } } @@ -375,6 +410,7 @@ impl From for Hsva { Color::Laba(laba) => laba.into(), Color::Lcha(lcha) => lcha.into(), Color::Oklaba(oklab) => oklab.into(), + Color::Oklcha(oklch) => oklch.into(), Color::Xyza(xyza) => xyza.into(), } } @@ -391,6 +427,7 @@ impl From for Hwba { Color::Laba(laba) => laba.into(), Color::Lcha(lcha) => lcha.into(), Color::Oklaba(oklab) => oklab.into(), + Color::Oklcha(oklch) => oklch.into(), Color::Xyza(xyza) => xyza.into(), } } @@ -407,6 +444,7 @@ impl From for Laba { Color::Laba(laba) => laba, Color::Lcha(lcha) => lcha.into(), Color::Oklaba(oklab) => oklab.into(), + Color::Oklcha(oklch) => oklch.into(), Color::Xyza(xyza) => xyza.into(), } } @@ -423,6 +461,7 @@ impl From for Lcha { Color::Laba(laba) => laba.into(), Color::Lcha(lcha) => lcha, Color::Oklaba(oklab) => oklab.into(), + Color::Oklcha(oklch) => oklch.into(), Color::Xyza(xyza) => xyza.into(), } } @@ -439,6 +478,24 @@ impl From for Oklaba { Color::Laba(laba) => laba.into(), Color::Lcha(lcha) => lcha.into(), Color::Oklaba(oklab) => oklab, + Color::Oklcha(oklch) => oklch.into(), + Color::Xyza(xyza) => xyza.into(), + } + } +} + +impl From for Oklcha { + fn from(value: Color) -> Self { + match value { + Color::Srgba(srgba) => srgba.into(), + Color::LinearRgba(linear) => linear.into(), + Color::Hsla(hsla) => hsla.into(), + Color::Hsva(hsva) => hsva.into(), + Color::Hwba(hwba) => hwba.into(), + Color::Laba(laba) => laba.into(), + Color::Lcha(lcha) => lcha.into(), + Color::Oklaba(oklab) => oklab.into(), + Color::Oklcha(oklch) => oklch, Color::Xyza(xyza) => xyza.into(), } } @@ -455,6 +512,7 @@ impl From for Xyza { Color::Laba(laba) => laba.into(), Color::Lcha(x) => x.into(), Color::Oklaba(x) => x.into(), + Color::Oklcha(oklch) => oklch.into(), Color::Xyza(xyza) => xyza, } } diff --git a/crates/bevy_color/src/lib.rs b/crates/bevy_color/src/lib.rs index 6148a5a27a84e..da8b46c066686 100644 --- a/crates/bevy_color/src/lib.rs +++ b/crates/bevy_color/src/lib.rs @@ -39,8 +39,8 @@ //! and an analog of lightness in the form of value. In contrast, HWB instead uses whiteness and blackness //! parameters, which can be used to lighten and darken a particular hue respectively. //! -//! Oklab is a perceptually uniform color space that is designed to be used for tasks such -//! as image processing. It is not as widely used as the other color spaces, but it is useful +//! Oklab and Okclch are perceptually uniform color spaces that are designed to be used for tasks such +//! as image processing. They are not as widely used as the other color spaces, but are useful //! for tasks such as color correction and image analysis, where it is important to be able //! to do things like change color saturation without causing hue shifts. //! @@ -87,6 +87,7 @@ mod laba; mod lcha; mod linear_rgba; mod oklaba; +mod oklcha; pub mod palettes; mod srgba; #[cfg(test)] @@ -106,6 +107,7 @@ pub mod prelude { pub use crate::lcha::*; pub use crate::linear_rgba::*; pub use crate::oklaba::*; + pub use crate::oklcha::*; pub use crate::srgba::*; pub use crate::xyza::*; } @@ -120,6 +122,7 @@ pub use laba::*; pub use lcha::*; pub use linear_rgba::*; pub use oklaba::*; +pub use oklcha::*; pub use srgba::*; pub use xyza::*; @@ -142,6 +145,7 @@ where Self: From + Into, Self: From + Into, Self: From + Into, + Self: From + Into, Self: From + Into, Self: Alpha, { diff --git a/crates/bevy_color/src/oklcha.rs b/crates/bevy_color/src/oklcha.rs new file mode 100644 index 0000000000000..8fd2512b5a940 --- /dev/null +++ b/crates/bevy_color/src/oklcha.rs @@ -0,0 +1,318 @@ +use crate::{ + color_difference::EuclideanDistance, Alpha, Hsla, Hsva, Hwba, Laba, Lcha, LinearRgba, + Luminance, Mix, Oklaba, Srgba, StandardColor, Xyza, +}; +use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize}; +use serde::{Deserialize, Serialize}; + +/// Color in Oklch color space, with alpha +#[doc = include_str!("../docs/conversion.md")] +///
+#[doc = include_str!("../docs/diagrams/model_graph.svg")] +///
+#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Reflect)] +#[reflect(PartialEq, Serialize, Deserialize)] +pub struct Oklcha { + /// The 'lightness' channel. [0.0, 1.0] + pub lightness: f32, + /// The 'chroma' channel. [0.0, 1.0] + pub chroma: f32, + /// The 'hue' channel. [0.0, 360.0] + pub hue: f32, + /// The alpha channel. [0.0, 1.0] + pub alpha: f32, +} + +impl StandardColor for Oklcha {} + +impl Oklcha { + /// Construct a new [`Oklcha`] color from components. + /// + /// # Arguments + /// + /// * `lightness` - Lightness channel. [0.0, 1.0] + /// * `chroma` - Chroma channel. [0.0, 1.0] + /// * `hue` - Hue channel. [0.0, 360.0] + /// * `alpha` - Alpha channel. [0.0, 1.0] + pub const fn new(lightness: f32, chroma: f32, hue: f32, alpha: f32) -> Self { + Self { + lightness, + chroma, + hue, + alpha, + } + } + + /// Construct a new [`Oklcha`] color from (l, c, h) components, with the default alpha (1.0). + /// + /// # Arguments + /// + /// * `lightness` - Lightness channel. [0.0, 1.0] + /// * `chroma` - Chroma channel. [0.0, 1.0] + /// * `hue` - Hue channel. [0.0, 360.0] + /// * `alpha` - Alpha channel. [0.0, 1.0] + pub const fn lch(lightness: f32, chroma: f32, hue: f32) -> Self { + Self::new(lightness, chroma, hue, 1.0) + } + + /// Return a copy of this color with the 'lightness' channel set to the given value. + pub const fn with_l(self, lightness: f32) -> Self { + Self { lightness, ..self } + } + + /// Return a copy of this color with the 'chroma' channel set to the given value. + pub const fn with_c(self, chroma: f32) -> Self { + Self { chroma, ..self } + } + + /// Return a copy of this color with the 'hue' channel set to the given value. + pub const fn with_h(self, hue: f32) -> Self { + Self { hue, ..self } + } +} + +impl Default for Oklcha { + fn default() -> Self { + Self::new(1., 0., 0., 1.) + } +} + +impl Mix for Oklcha { + #[inline] + fn mix(&self, other: &Self, factor: f32) -> Self { + let n_factor = 1.0 - factor; + Self { + lightness: self.lightness * n_factor + other.lightness * factor, + chroma: self.chroma * n_factor + other.chroma * factor, + hue: self.hue * n_factor + other.hue * factor, + alpha: self.alpha * n_factor + other.alpha * factor, + } + } +} + +impl Alpha for Oklcha { + #[inline] + fn with_alpha(&self, alpha: f32) -> Self { + Self { alpha, ..*self } + } + + #[inline] + fn alpha(&self) -> f32 { + self.alpha + } +} + +impl Luminance for Oklcha { + #[inline] + fn with_luminance(&self, lightness: f32) -> Self { + Self { lightness, ..*self } + } + + fn luminance(&self) -> f32 { + self.lightness + } + + fn darker(&self, amount: f32) -> Self { + Self::new( + (self.lightness - amount).max(0.), + self.chroma, + self.hue, + self.alpha, + ) + } + + fn lighter(&self, amount: f32) -> Self { + Self::new( + (self.lightness + amount).min(1.), + self.chroma, + self.hue, + self.alpha, + ) + } +} + +impl EuclideanDistance for Oklcha { + #[inline] + fn distance_squared(&self, other: &Self) -> f32 { + (self.lightness - other.lightness).powi(2) + + (self.chroma - other.chroma).powi(2) + + (self.hue - other.hue).powi(2) + } +} + +impl From for Oklcha { + fn from(Oklaba { l, a, b, alpha }: Oklaba) -> Self { + let lightness = l; + let chroma = a.hypot(b); + let hue = b.atan2(a).to_degrees(); + + let hue = if hue < 0.0 { hue + 360.0 } else { hue }; + + Oklcha::new(lightness, chroma, hue, alpha) + } +} + +impl From for Oklaba { + fn from( + Oklcha { + lightness, + chroma, + hue, + alpha, + }: Oklcha, + ) -> Self { + let l = lightness; + let a = chroma * hue.to_radians().cos(); + let b = chroma * hue.to_radians().sin(); + + Oklaba::new(l, a, b, alpha) + } +} + +// Derived Conversions + +impl From for Oklcha { + fn from(value: Hsla) -> Self { + Oklaba::from(value).into() + } +} + +impl From for Hsla { + fn from(value: Oklcha) -> Self { + Oklaba::from(value).into() + } +} + +impl From for Oklcha { + fn from(value: Hsva) -> Self { + Oklaba::from(value).into() + } +} + +impl From for Hsva { + fn from(value: Oklcha) -> Self { + Oklaba::from(value).into() + } +} + +impl From for Oklcha { + fn from(value: Hwba) -> Self { + Oklaba::from(value).into() + } +} + +impl From for Hwba { + fn from(value: Oklcha) -> Self { + Oklaba::from(value).into() + } +} + +impl From for Oklcha { + fn from(value: Laba) -> Self { + Oklaba::from(value).into() + } +} + +impl From for Laba { + fn from(value: Oklcha) -> Self { + Oklaba::from(value).into() + } +} + +impl From for Oklcha { + fn from(value: Lcha) -> Self { + Oklaba::from(value).into() + } +} + +impl From for Lcha { + fn from(value: Oklcha) -> Self { + Oklaba::from(value).into() + } +} + +impl From for Oklcha { + fn from(value: LinearRgba) -> Self { + Oklaba::from(value).into() + } +} + +impl From for LinearRgba { + fn from(value: Oklcha) -> Self { + Oklaba::from(value).into() + } +} + +impl From for Oklcha { + fn from(value: Srgba) -> Self { + Oklaba::from(value).into() + } +} + +impl From for Srgba { + fn from(value: Oklcha) -> Self { + Oklaba::from(value).into() + } +} + +impl From for Oklcha { + fn from(value: Xyza) -> Self { + Oklaba::from(value).into() + } +} + +impl From for Xyza { + fn from(value: Oklcha) -> Self { + Oklaba::from(value).into() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{test_colors::TEST_COLORS, testing::assert_approx_eq, Srgba}; + + #[test] + fn test_to_from_srgba() { + let oklcha = Oklcha::new(0.5, 0.5, 180.0, 1.0); + let srgba: Srgba = oklcha.into(); + let oklcha2: Oklcha = srgba.into(); + assert_approx_eq!(oklcha.lightness, oklcha2.lightness, 0.001); + assert_approx_eq!(oklcha.chroma, oklcha2.chroma, 0.001); + assert_approx_eq!(oklcha.hue, oklcha2.hue, 0.001); + assert_approx_eq!(oklcha.alpha, oklcha2.alpha, 0.001); + } + + #[test] + fn test_to_from_srgba_2() { + for color in TEST_COLORS.iter() { + let rgb2: Srgba = (color.oklch).into(); + let oklch: Oklcha = (color.rgb).into(); + assert!( + color.rgb.distance(&rgb2) < 0.0001, + "{}: {:?} != {:?}", + color.name, + color.rgb, + rgb2 + ); + assert!( + color.oklch.distance(&oklch) < 0.0001, + "{}: {:?} != {:?}", + color.name, + color.oklch, + oklch + ); + } + } + + #[test] + fn test_to_from_linear() { + let oklcha = Oklcha::new(0.5, 0.5, 0.5, 1.0); + let linear: LinearRgba = oklcha.into(); + let oklcha2: Oklcha = linear.into(); + assert_approx_eq!(oklcha.lightness, oklcha2.lightness, 0.001); + assert_approx_eq!(oklcha.chroma, oklcha2.chroma, 0.001); + assert_approx_eq!(oklcha.hue, oklcha2.hue, 0.001); + assert_approx_eq!(oklcha.alpha, oklcha2.alpha, 0.001); + } +} diff --git a/crates/bevy_color/src/test_colors.rs b/crates/bevy_color/src/test_colors.rs index 027eb5e3a7612..9fd479c0af5e3 100644 --- a/crates/bevy_color/src/test_colors.rs +++ b/crates/bevy_color/src/test_colors.rs @@ -1,6 +1,6 @@ // Generated by gen_tests. Do not edit. #[cfg(test)] -use crate::{Hsla, Hsva, Hwba, Laba, Lcha, LinearRgba, Oklaba, Srgba, Xyza}; +use crate::{Hsla, Hsva, Hwba, Laba, Lcha, LinearRgba, Oklaba, Oklcha, Srgba, Xyza}; #[cfg(test)] pub struct TestColor { @@ -13,6 +13,7 @@ pub struct TestColor { pub lab: Laba, pub lch: Lcha, pub oklab: Oklaba, + pub oklch: Oklcha, pub xyz: Xyza, } @@ -30,6 +31,7 @@ pub const TEST_COLORS: &[TestColor] = &[ hwb: Hwba::new(0.0, 0.0, 1.0, 1.0), lab: Laba::new(0.0, 0.0, 0.0, 1.0), oklab: Oklaba::new(0.0, 0.0, 0.0, 1.0), + oklch: Oklcha::new(0.0, 0.0, 0.0, 1.0), xyz: Xyza::new(0.0, 0.0, 0.0, 1.0), }, // white @@ -43,6 +45,7 @@ pub const TEST_COLORS: &[TestColor] = &[ hwb: Hwba::new(0.0, 1.0, 0.0, 1.0), lab: Laba::new(1.0, 0.0, 0.0, 1.0), oklab: Oklaba::new(1.0, 0.0, 0.000000059604645, 1.0), + oklch: Oklcha::new(1.0, 0.000000059604645, 90.0, 1.0), xyz: Xyza::new(0.95047, 1.0, 1.08883, 1.0), }, // red @@ -56,6 +59,7 @@ pub const TEST_COLORS: &[TestColor] = &[ hsv: Hsva::new(0.0, 1.0, 1.0, 1.0), hwb: Hwba::new(0.0, 0.0, 0.0, 1.0), lab: Laba::new(0.532408, 0.8009243, 0.6720321, 1.0), + oklch: Oklcha::new(0.6279554, 0.2576833, 29.233892, 1.0), xyz: Xyza::new(0.4124564, 0.2126729, 0.0193339, 1.0), }, // green @@ -69,6 +73,7 @@ pub const TEST_COLORS: &[TestColor] = &[ hwb: Hwba::new(120.0, 0.0, 0.0, 1.0), lab: Laba::new(0.8773472, -0.86182654, 0.8317931, 1.0), oklab: Oklaba::new(0.8664396, -0.2338874, 0.1794985, 1.0), + oklch: Oklcha::new(0.8664396, 0.2948271, 142.49532, 1.0), xyz: Xyza::new(0.3575761, 0.7151522, 0.119192, 1.0), }, // blue @@ -82,6 +87,7 @@ pub const TEST_COLORS: &[TestColor] = &[ hsv: Hsva::new(240.0, 1.0, 1.0, 1.0), hwb: Hwba::new(240.0, 0.0, 0.0, 1.0), lab: Laba::new(0.32297015, 0.7918751, -1.0786015, 1.0), + oklch: Oklcha::new(0.45201376, 0.31321433, 264.05203, 1.0), xyz: Xyza::new(0.1804375, 0.072175, 0.9503041, 1.0), }, // yellow @@ -95,6 +101,7 @@ pub const TEST_COLORS: &[TestColor] = &[ hsv: Hsva::new(60.0, 1.0, 1.0, 1.0), hwb: Hwba::new(60.0, 0.0, 0.0, 1.0), lab: Laba::new(0.9713927, -0.21553725, 0.94477975, 1.0), + oklch: Oklcha::new(0.9679827, 0.21100593, 109.76923, 1.0), xyz: Xyza::new(0.7700325, 0.9278251, 0.1385259, 1.0), }, // magenta @@ -108,6 +115,7 @@ pub const TEST_COLORS: &[TestColor] = &[ lab: Laba::new(0.6032421, 0.9823433, -0.60824895, 1.0), lch: Lcha::new(0.6032421, 1.1554068, 328.23495, 1.0), oklab: Oklaba::new(0.7016738, 0.27456632, -0.16915613, 1.0), + oklch: Oklcha::new(0.7016738, 0.32249108, 328.36343, 1.0), xyz: Xyza::new(0.5928939, 0.28484792, 0.969638, 1.0), }, // cyan @@ -121,6 +129,7 @@ pub const TEST_COLORS: &[TestColor] = &[ hsv: Hsva::new(180.0, 1.0, 1.0, 1.0), hwb: Hwba::new(180.0, 0.0, 0.0, 1.0), lab: Laba::new(0.9111321, -0.4808751, -0.14131188, 1.0), + oklch: Oklcha::new(0.9053992, 0.15454963, 194.76901, 1.0), xyz: Xyza::new(0.5380136, 0.78732723, 1.069496, 1.0), }, // gray @@ -134,6 +143,7 @@ pub const TEST_COLORS: &[TestColor] = &[ hsv: Hsva::new(0.0, 0.0, 0.5, 1.0), hwb: Hwba::new(0.0, 0.5, 0.5, 1.0), lab: Laba::new(0.5338897, 0.0, 0.0, 1.0), + oklch: Oklcha::new(0.5981808, 0.00000023841858, 0.0, 1.0), xyz: Xyza::new(0.2034397, 0.21404117, 0.23305441, 1.0), }, // olive @@ -147,6 +157,7 @@ pub const TEST_COLORS: &[TestColor] = &[ hwb: Hwba::new(60.0, 0.0, 0.5, 1.0), lab: Laba::new(0.51677734, -0.1289308, 0.5651491, 1.0), oklab: Oklaba::new(0.57902855, -0.042691574, 0.11878061, 1.0), + oklch: Oklcha::new(0.57902855, 0.12621966, 109.76922, 1.0), xyz: Xyza::new(0.16481864, 0.19859275, 0.029650241, 1.0), }, // purple @@ -160,6 +171,7 @@ pub const TEST_COLORS: &[TestColor] = &[ hwb: Hwba::new(300.0, 0.0, 0.5, 1.0), lab: Laba::new(0.29655674, 0.58761847, -0.3638428, 1.0), oklab: Oklaba::new(0.41972777, 0.1642403, -0.10118592, 1.0), + oklch: Oklcha::new(0.41972777, 0.19290791, 328.36343, 1.0), xyz: Xyza::new(0.12690368, 0.060969174, 0.20754242, 1.0), }, // teal @@ -173,6 +185,7 @@ pub const TEST_COLORS: &[TestColor] = &[ hsv: Hsva::new(180.0, 1.0, 0.5, 1.0), hwb: Hwba::new(180.0, 0.0, 0.5, 1.0), lab: Laba::new(0.4807306, -0.28765023, -0.084530115, 1.0), + oklch: Oklcha::new(0.54159236, 0.092448615, 194.76903, 1.0), xyz: Xyza::new(0.11515705, 0.16852042, 0.22891617, 1.0), }, // maroon @@ -186,6 +199,7 @@ pub const TEST_COLORS: &[TestColor] = &[ lab: Laba::new(0.2541851, 0.47909766, 0.37905872, 1.0), lch: Lcha::new(0.2541851, 0.61091745, 38.350803, 1.0), oklab: Oklaba::new(0.3756308, 0.13450874, 0.07527886, 1.0), + oklch: Oklcha::new(0.3756308, 0.1541412, 29.233906, 1.0), xyz: Xyza::new(0.08828264, 0.045520753, 0.0041382504, 1.0), }, // lime @@ -199,6 +213,7 @@ pub const TEST_COLORS: &[TestColor] = &[ lab: Laba::new(0.46052113, -0.5155285, 0.4975627, 1.0), lch: Lcha::new(0.46052113, 0.71647626, 136.01596, 1.0), oklab: Oklaba::new(0.5182875, -0.13990697, 0.10737252, 1.0), + oklch: Oklcha::new(0.5182875, 0.17635989, 142.49535, 1.0), xyz: Xyza::new(0.076536, 0.153072, 0.025511991, 1.0), }, // navy @@ -212,6 +227,7 @@ pub const TEST_COLORS: &[TestColor] = &[ hwb: Hwba::new(240.0, 0.0, 0.5, 1.0), lab: Laba::new(0.12890343, 0.4736844, -0.64519864, 1.0), oklab: Oklaba::new(0.27038592, -0.01941514, -0.18635012, 1.0), + oklch: Oklcha::new(0.27038592, 0.18735878, 264.05203, 1.0), xyz: Xyza::new(0.03862105, 0.01544842, 0.20340417, 1.0), }, // orange @@ -225,6 +241,7 @@ pub const TEST_COLORS: &[TestColor] = &[ hwb: Hwba::new(60.0, 0.0, 0.5, 1.0), lab: Laba::new(0.51677734, -0.1289308, 0.5651491, 1.0), oklab: Oklaba::new(0.57902855, -0.042691574, 0.11878061, 1.0), + oklch: Oklcha::new(0.57902855, 0.12621966, 109.76922, 1.0), xyz: Xyza::new(0.16481864, 0.19859275, 0.029650241, 1.0), }, // fuchsia @@ -238,6 +255,7 @@ pub const TEST_COLORS: &[TestColor] = &[ hwb: Hwba::new(300.0, 0.0, 0.5, 1.0), lab: Laba::new(0.29655674, 0.58761847, -0.3638428, 1.0), oklab: Oklaba::new(0.41972777, 0.1642403, -0.10118592, 1.0), + oklch: Oklcha::new(0.41972777, 0.19290791, 328.36343, 1.0), xyz: Xyza::new(0.12690368, 0.060969174, 0.20754242, 1.0), }, // aqua @@ -251,6 +269,7 @@ pub const TEST_COLORS: &[TestColor] = &[ hsv: Hsva::new(180.0, 1.0, 0.5, 1.0), hwb: Hwba::new(180.0, 0.0, 0.5, 1.0), lab: Laba::new(0.4807306, -0.28765023, -0.084530115, 1.0), + oklch: Oklcha::new(0.54159236, 0.092448615, 194.76903, 1.0), xyz: Xyza::new(0.11515705, 0.16852042, 0.22891617, 1.0), }, ]; diff --git a/crates/bevy_render/src/color/mod.rs b/crates/bevy_render/src/color/mod.rs index b669abb878823..c1f38f8e2eea4 100644 --- a/crates/bevy_render/src/color/mod.rs +++ b/crates/bevy_render/src/color/mod.rs @@ -1,5 +1,5 @@ use bevy_color::{ - Color, HexColorError, Hsla, Hsva, Hwba, Laba, Lcha, LinearRgba, Oklaba, Srgba, Xyza, + Color, HexColorError, Hsla, Hsva, Hwba, Laba, Lcha, LinearRgba, Oklaba, Oklcha, Srgba, Xyza, }; use bevy_math::{Vec3, Vec4}; @@ -929,6 +929,7 @@ impl From for LegacyColor { Color::Laba(x) => x.into(), Color::Lcha(x) => x.into(), Color::Oklaba(x) => x.into(), + Color::Oklcha(x) => x.into(), Color::Xyza(x) => x.into(), } } @@ -1083,6 +1084,18 @@ impl From for LegacyColor { } } +impl From for Oklcha { + fn from(value: LegacyColor) -> Self { + LinearRgba::from(value).into() + } +} + +impl From for LegacyColor { + fn from(value: Oklcha) -> Self { + LinearRgba::from(value).into() + } +} + impl From for wgpu::Color { fn from(color: LegacyColor) -> Self { if let LegacyColor::RgbaLinear {