From 4d325eb3f04de081983ad6313482830f0276ae9a Mon Sep 17 00:00:00 2001 From: Zachary Harrold Date: Tue, 27 Feb 2024 09:30:50 +1100 Subject: [PATCH] `bevy_color`: Add `Laba` to support Lab Color Model (#12115) # Objective - Improve compatibility with CSS Module 4 - Simplify `Lcha` conversion functions ## Solution - Added `Laba` which implements the Lab color model. - Updated `Color` and `LegacyColor` accordingly. ## Migration Guide - Convert `Laba` to either `Xyza` or `Lcha` using the provided `From` implementations and then handle accordingly. ## Notes The Lab color space is a required stepping stone when converting between XYZ and Lch, therefore we already use the Lab color model, just in an nameless fashion prone to errors. This PR also includes a slightly broader refactor of the `From` implementations between the various colour spaces to better reflect the graph of definitions. My goal was to keep domain specific knowledge of each colour space contained to their respective files (e.g., the `From for LinearRgba` definition was in `linear_rgba.rs` when it probably belongs in `oklaba.rs`, since Linear sRGB is a fundamental space and Oklab is defined in its relation to it) --- .../bevy_color/crates/gen_tests/src/main.rs | 10 +- crates/bevy_color/src/color.rs | 36 +- crates/bevy_color/src/hsla.rs | 38 +- crates/bevy_color/src/hsva.rs | 16 +- crates/bevy_color/src/hwba.rs | 16 +- crates/bevy_color/src/laba.rs | 347 ++++++++++++++++++ crates/bevy_color/src/lcha.rs | 117 ++---- crates/bevy_color/src/lib.rs | 4 + crates/bevy_color/src/linear_rgba.rs | 39 +- crates/bevy_color/src/oklaba.rs | 97 ++++- crates/bevy_color/src/srgba.rs | 23 +- crates/bevy_color/src/test_colors.rs | 21 +- crates/bevy_color/src/xyza.rs | 38 +- crates/bevy_render/src/color/mod.rs | 17 +- 14 files changed, 599 insertions(+), 220 deletions(-) create mode 100644 crates/bevy_color/src/laba.rs diff --git a/crates/bevy_color/crates/gen_tests/src/main.rs b/crates/bevy_color/crates/gen_tests/src/main.rs index bd26e6921c..c58c2d8501 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, Lch, LinSrgb, Oklab, Srgb, Xyz}; +use palette::{Hsl, Hsv, Hwb, IntoColor, Lab, Lch, LinSrgb, Oklab, 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, Lcha, Xyza}}; +use crate::{{Hsla, Hsva, Hwba, Srgba, LinearRgba, Oklaba, Laba, Lcha, Xyza}}; #[cfg(test)] pub struct TestColor {{ @@ -35,6 +35,7 @@ pub struct TestColor {{ pub hsl: Hsla, pub hsv: Hsva, pub hwb: Hwba, + pub lab: Laba, pub lch: Lcha, pub oklab: Oklaba, pub xyz: Xyza, @@ -51,6 +52,7 @@ pub struct TestColor {{ let hsl: Hsl = srgb.into_color(); let hsv: Hsv = srgb.into_color(); let hwb: Hwb = srgb.into_color(); + let lab: Lab = srgb.into_color(); let lch: Lch = srgb.into_color(); let oklab: Oklab = srgb.into_color(); let xyz: Xyz = srgb.into_color(); @@ -63,6 +65,7 @@ pub struct TestColor {{ hsl: Hsla::new({}, {}, {}, 1.0), hsv: Hsva::new({}, {}, {}, 1.0), hwb: Hwba::new({}, {}, {}, 1.0), + lab: Laba::new({}, {}, {}, 1.0), lch: Lcha::new({}, {}, {}, 1.0), oklab: Oklaba::new({}, {}, {}, 1.0), xyz: Xyza::new({}, {}, {}, 1.0), @@ -82,6 +85,9 @@ pub struct TestColor {{ VariablePrecision(hwb.hue.into_positive_degrees()), VariablePrecision(hwb.whiteness), VariablePrecision(hwb.blackness), + VariablePrecision(lab.l / 100.0), + VariablePrecision(lab.a / 100.0), + VariablePrecision(lab.b / 100.0), VariablePrecision(lch.l / 100.0), VariablePrecision(lch.chroma / 100.0), VariablePrecision(lch.hue.into_positive_degrees()), diff --git a/crates/bevy_color/src/color.rs b/crates/bevy_color/src/color.rs index 8cde1b2282..d1d24e2833 100644 --- a/crates/bevy_color/src/color.rs +++ b/crates/bevy_color/src/color.rs @@ -1,4 +1,4 @@ -use crate::{Alpha, Hsla, Hsva, Hwba, Lcha, LinearRgba, Oklaba, Srgba, StandardColor, Xyza}; +use crate::{Alpha, Hsla, Hsva, Hwba, Laba, Lcha, LinearRgba, Oklaba, Srgba, StandardColor, Xyza}; use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize}; use serde::{Deserialize, Serialize}; @@ -19,6 +19,8 @@ pub enum Color { Hsva(Hsva), /// A color in the HWB color space with alpha. Hwba(Hwba), + /// A color in the LAB color space with alpha. + Laba(Laba), /// A color in the LCH color space with alpha. Lcha(Lcha), /// A color in the Oklaba color space with alpha. @@ -52,6 +54,7 @@ impl Alpha for Color { Color::Hsla(x) => *x = x.with_alpha(alpha), Color::Hsva(x) => *x = x.with_alpha(alpha), Color::Hwba(x) => *x = x.with_alpha(alpha), + 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::Xyza(x) => *x = x.with_alpha(alpha), @@ -67,6 +70,7 @@ impl Alpha for Color { Color::Hsla(x) => x.alpha(), Color::Hsva(x) => x.alpha(), Color::Hwba(x) => x.alpha(), + Color::Laba(x) => x.alpha(), Color::Lcha(x) => x.alpha(), Color::Oklaba(x) => x.alpha(), Color::Xyza(x) => x.alpha(), @@ -116,6 +120,12 @@ impl From for Color { } } +impl From for Color { + fn from(value: Laba) -> Self { + Self::Laba(value) + } +} + impl From for Color { fn from(value: Xyza) -> Self { Self::Xyza(value) @@ -130,6 +140,7 @@ impl From for Srgba { 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::Xyza(xyza) => xyza.into(), @@ -145,6 +156,7 @@ impl From for LinearRgba { 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::Xyza(xyza) => xyza.into(), @@ -160,6 +172,7 @@ impl From for Hsla { Color::Hsla(hsla) => hsla, 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::Xyza(xyza) => xyza.into(), @@ -175,6 +188,7 @@ impl From for Hsva { Color::Hsla(hsla) => hsla.into(), Color::Hsva(hsva) => hsva, Color::Hwba(hwba) => hwba.into(), + Color::Laba(laba) => laba.into(), Color::Lcha(lcha) => lcha.into(), Color::Oklaba(oklab) => oklab.into(), Color::Xyza(xyza) => xyza.into(), @@ -190,6 +204,23 @@ impl From for Hwba { Color::Hsla(hsla) => hsla.into(), Color::Hsva(hsva) => hsva.into(), Color::Hwba(hwba) => hwba, + Color::Laba(laba) => laba.into(), + Color::Lcha(lcha) => lcha.into(), + Color::Oklaba(oklab) => oklab.into(), + Color::Xyza(xyza) => xyza.into(), + } + } +} + +impl From for Laba { + 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, Color::Lcha(lcha) => lcha.into(), Color::Oklaba(oklab) => oklab.into(), Color::Xyza(xyza) => xyza.into(), @@ -205,6 +236,7 @@ impl From for Lcha { Color::Hsla(hsla) => hsla.into(), Color::Hsva(hsva) => hsva.into(), Color::Hwba(hwba) => hwba.into(), + Color::Laba(laba) => laba.into(), Color::Lcha(lcha) => lcha, Color::Oklaba(oklab) => oklab.into(), Color::Xyza(xyza) => xyza.into(), @@ -220,6 +252,7 @@ impl From for Oklaba { 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, Color::Xyza(xyza) => xyza.into(), @@ -235,6 +268,7 @@ impl From for Xyza { Color::Hsla(x) => x.into(), Color::Hsva(hsva) => hsva.into(), Color::Hwba(hwba) => hwba.into(), + Color::Laba(laba) => laba.into(), Color::Lcha(x) => x.into(), Color::Oklaba(x) => x.into(), Color::Xyza(xyza) => xyza, diff --git a/crates/bevy_color/src/hsla.rs b/crates/bevy_color/src/hsla.rs index 462562a2a3..3739c4b144 100644 --- a/crates/bevy_color/src/hsla.rs +++ b/crates/bevy_color/src/hsla.rs @@ -1,4 +1,4 @@ -use crate::{Alpha, Hsva, Hwba, Lcha, LinearRgba, Luminance, Mix, Oklaba, Srgba, StandardColor}; +use crate::{Alpha, Hsva, Hwba, Lcha, LinearRgba, Luminance, Mix, Srgba, StandardColor, Xyza}; use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize}; use serde::{Deserialize, Serialize}; @@ -170,12 +170,20 @@ impl From for Hsla { } } +// Derived Conversions + impl From for Hsla { fn from(value: Hwba) -> Self { Hsva::from(value).into() } } +impl From for Hwba { + fn from(value: Hsla) -> Self { + Hsva::from(value).into() + } +} + impl From for Hsla { fn from(value: Srgba) -> Self { Hsva::from(value).into() @@ -188,20 +196,14 @@ impl From for Srgba { } } -impl From for Hwba { - fn from(value: Hsla) -> Self { - Hsva::from(value).into() - } -} - impl From for Hsla { fn from(value: LinearRgba) -> Self { Hsva::from(value).into() } } -impl From for Hsla { - fn from(value: Oklaba) -> Self { +impl From for LinearRgba { + fn from(value: Hsla) -> Self { Hsva::from(value).into() } } @@ -212,6 +214,24 @@ impl From for Hsla { } } +impl From for Lcha { + fn from(value: Hsla) -> Self { + Hsva::from(value).into() + } +} + +impl From for Hsla { + fn from(value: Xyza) -> Self { + Hsva::from(value).into() + } +} + +impl From for Xyza { + fn from(value: Hsla) -> Self { + Hsva::from(value).into() + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/bevy_color/src/hsva.rs b/crates/bevy_color/src/hsva.rs index 30f204ed1d..0583491e68 100644 --- a/crates/bevy_color/src/hsva.rs +++ b/crates/bevy_color/src/hsva.rs @@ -1,4 +1,4 @@ -use crate::{Alpha, Hwba, Lcha, LinearRgba, Oklaba, Srgba, StandardColor, Xyza}; +use crate::{Alpha, Hwba, Lcha, LinearRgba, Srgba, StandardColor, Xyza}; use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize}; use serde::{Deserialize, Serialize}; @@ -116,6 +116,8 @@ impl From for Hsva { } } +// Derived Conversions + impl From for Hsva { fn from(value: Srgba) -> Self { Hwba::from(value).into() @@ -152,18 +154,6 @@ impl From for Lcha { } } -impl From for Hsva { - fn from(value: Oklaba) -> Self { - Hwba::from(value).into() - } -} - -impl From for Oklaba { - fn from(value: Hsva) -> Self { - Hwba::from(value).into() - } -} - impl From for Hsva { fn from(value: Xyza) -> Self { Hwba::from(value).into() diff --git a/crates/bevy_color/src/hwba.rs b/crates/bevy_color/src/hwba.rs index b6c7003a2a..5105e59d6a 100644 --- a/crates/bevy_color/src/hwba.rs +++ b/crates/bevy_color/src/hwba.rs @@ -2,7 +2,7 @@ //! in [_HWB - A More Intuitive Hue-Based Color Model_] by _Smith et al_. //! //! [_HWB - A More Intuitive Hue-Based Color Model_]: https://web.archive.org/web/20240226005220/http://alvyray.com/Papers/CG/HWB_JGTv208.pdf -use crate::{Alpha, Lcha, LinearRgba, Oklaba, Srgba, StandardColor, Xyza}; +use crate::{Alpha, Lcha, LinearRgba, Srgba, StandardColor, Xyza}; use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize}; use serde::{Deserialize, Serialize}; @@ -161,6 +161,8 @@ impl From for Srgba { } } +// Derived Conversions + impl From for Hwba { fn from(value: LinearRgba) -> Self { Srgba::from(value).into() @@ -185,18 +187,6 @@ impl From for Lcha { } } -impl From for Hwba { - fn from(value: Oklaba) -> Self { - Srgba::from(value).into() - } -} - -impl From for Oklaba { - fn from(value: Hwba) -> Self { - Srgba::from(value).into() - } -} - impl From for Hwba { fn from(value: Xyza) -> Self { Srgba::from(value).into() diff --git a/crates/bevy_color/src/laba.rs b/crates/bevy_color/src/laba.rs new file mode 100644 index 0000000000..4b4714a3a8 --- /dev/null +++ b/crates/bevy_color/src/laba.rs @@ -0,0 +1,347 @@ +use crate::{ + Alpha, Hsla, Hsva, Hwba, LinearRgba, Luminance, Mix, Oklaba, Srgba, StandardColor, Xyza, +}; +use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize}; +use serde::{Deserialize, Serialize}; + +/// Color in LAB color space, with alpha +#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Reflect)] +#[reflect(PartialEq, Serialize, Deserialize)] +pub struct Laba { + /// The lightness channel. [0.0, 1.5] + pub lightness: f32, + /// The a axis. [-1.5, 1.5] + pub a: f32, + /// The b axis. [-1.5, 1.5] + pub b: f32, + /// The alpha channel. [0.0, 1.0] + pub alpha: f32, +} + +impl StandardColor for Laba {} + +impl Laba { + /// Construct a new [`Laba`] color from components. + /// + /// # Arguments + /// + /// * `lightness` - Lightness channel. [0.0, 1.5] + /// * `a` - a axis. [-1.5, 1.5] + /// * `b` - b axis. [-1.5, 1.5] + /// * `alpha` - Alpha channel. [0.0, 1.0] + pub const fn new(lightness: f32, a: f32, b: f32, alpha: f32) -> Self { + Self { + lightness, + a, + b, + alpha, + } + } + + /// Construct a new [`Laba`] color from (l, a, b) components, with the default alpha (1.0). + /// + /// # Arguments + /// + /// * `lightness` - Lightness channel. [0.0, 1.5] + /// * `a` - a axis. [-1.5, 1.5] + /// * `b` - b axis. [-1.5, 1.5] + pub const fn lab(lightness: f32, a: f32, b: f32) -> Self { + Self { + lightness, + a, + b, + alpha: 1.0, + } + } + + /// Return a copy of this color with the lightness channel set to the given value. + pub const fn with_lightness(self, lightness: f32) -> Self { + Self { lightness, ..self } + } + + /// CIE Epsilon Constant + /// + /// See [Continuity (16) (17)](http://brucelindbloom.com/index.html?LContinuity.html) + pub const CIE_EPSILON: f32 = 216.0 / 24389.0; + + /// CIE Kappa Constant + /// + /// See [Continuity (16) (17)](http://brucelindbloom.com/index.html?LContinuity.html) + pub const CIE_KAPPA: f32 = 24389.0 / 27.0; +} + +impl Default for Laba { + fn default() -> Self { + Self::new(1., 0., 0., 1.) + } +} + +impl Mix for Laba { + #[inline] + fn mix(&self, other: &Self, factor: f32) -> Self { + let n_factor = 1.0 - factor; + Self { + lightness: self.lightness * n_factor + other.lightness * factor, + a: self.a * n_factor + other.a * factor, + b: self.b * n_factor + other.b * factor, + alpha: self.alpha * n_factor + other.alpha * factor, + } + } +} + +impl Alpha for Laba { + #[inline] + fn with_alpha(&self, alpha: f32) -> Self { + Self { alpha, ..*self } + } + + #[inline] + fn alpha(&self) -> f32 { + self.alpha + } +} + +impl Luminance for Laba { + #[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.a, + self.b, + self.alpha, + ) + } + + fn lighter(&self, amount: f32) -> Self { + Self::new( + (self.lightness + amount).min(1.), + self.a, + self.b, + self.alpha, + ) + } +} + +impl From for Xyza { + fn from( + Laba { + lightness, + a, + b, + alpha, + }: Laba, + ) -> Self { + // Based on http://www.brucelindbloom.com/index.html?Eqn_Lab_to_XYZ.html + let l = 100. * lightness; + let a = 100. * a; + let b = 100. * b; + + let fy = (l + 16.0) / 116.0; + let fx = a / 500.0 + fy; + let fz = fy - b / 200.0; + let xr = { + let fx3 = fx.powf(3.0); + + if fx3 > Laba::CIE_EPSILON { + fx3 + } else { + (116.0 * fx - 16.0) / Laba::CIE_KAPPA + } + }; + let yr = if l > Laba::CIE_EPSILON * Laba::CIE_KAPPA { + ((l + 16.0) / 116.0).powf(3.0) + } else { + l / Laba::CIE_KAPPA + }; + let zr = { + let fz3 = fz.powf(3.0); + + if fz3 > Laba::CIE_EPSILON { + fz3 + } else { + (116.0 * fz - 16.0) / Laba::CIE_KAPPA + } + }; + let x = xr * Xyza::D65_WHITE.x; + let y = yr * Xyza::D65_WHITE.y; + let z = zr * Xyza::D65_WHITE.z; + + Xyza::new(x, y, z, alpha) + } +} + +impl From for Laba { + fn from(Xyza { x, y, z, alpha }: Xyza) -> Self { + // Based on http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_Lab.html + let xr = x / Xyza::D65_WHITE.x; + let yr = y / Xyza::D65_WHITE.y; + let zr = z / Xyza::D65_WHITE.z; + let fx = if xr > Laba::CIE_EPSILON { + xr.cbrt() + } else { + (Laba::CIE_KAPPA * xr + 16.0) / 116.0 + }; + let fy = if yr > Laba::CIE_EPSILON { + yr.cbrt() + } else { + (Laba::CIE_KAPPA * yr + 16.0) / 116.0 + }; + let fz = if yr > Laba::CIE_EPSILON { + zr.cbrt() + } else { + (Laba::CIE_KAPPA * zr + 16.0) / 116.0 + }; + let l = 1.16 * fy - 0.16; + let a = 5.00 * (fx - fy); + let b = 2.00 * (fy - fz); + + Laba::new(l, a, b, alpha) + } +} + +// Derived Conversions + +impl From for Laba { + fn from(value: Srgba) -> Self { + Xyza::from(value).into() + } +} + +impl From for Srgba { + fn from(value: Laba) -> Self { + Xyza::from(value).into() + } +} + +impl From for Laba { + fn from(value: LinearRgba) -> Self { + Xyza::from(value).into() + } +} + +impl From for LinearRgba { + fn from(value: Laba) -> Self { + Xyza::from(value).into() + } +} + +impl From for Laba { + fn from(value: Hsla) -> Self { + Xyza::from(value).into() + } +} + +impl From for Hsla { + fn from(value: Laba) -> Self { + Xyza::from(value).into() + } +} + +impl From for Laba { + fn from(value: Hsva) -> Self { + Xyza::from(value).into() + } +} + +impl From for Hsva { + fn from(value: Laba) -> Self { + Xyza::from(value).into() + } +} + +impl From for Laba { + fn from(value: Hwba) -> Self { + Xyza::from(value).into() + } +} + +impl From for Hwba { + fn from(value: Laba) -> Self { + Xyza::from(value).into() + } +} + +impl From for Laba { + fn from(value: Oklaba) -> Self { + Xyza::from(value).into() + } +} + +impl From for Oklaba { + fn from(value: Laba) -> Self { + Xyza::from(value).into() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + color_difference::EuclideanDistance, test_colors::TEST_COLORS, testing::assert_approx_eq, + Srgba, + }; + + #[test] + fn test_to_from_srgba() { + for color in TEST_COLORS.iter() { + let rgb2: Srgba = (color.lab).into(); + let laba: Laba = (color.rgb).into(); + assert!( + color.rgb.distance(&rgb2) < 0.0001, + "{}: {:?} != {:?}", + color.name, + color.rgb, + rgb2 + ); + assert_approx_eq!(color.lab.lightness, laba.lightness, 0.001); + if laba.lightness > 0.01 { + assert_approx_eq!(color.lab.a, laba.a, 0.1); + } + if laba.lightness > 0.01 && laba.a > 0.01 { + assert!( + (color.lab.b - laba.b).abs() < 1.7, + "{:?} != {:?}", + color.lab, + laba + ); + } + assert_approx_eq!(color.lab.alpha, laba.alpha, 0.001); + } + } + + #[test] + fn test_to_from_linear() { + for color in TEST_COLORS.iter() { + let rgb2: LinearRgba = (color.lab).into(); + let laba: Laba = (color.linear_rgb).into(); + assert!( + color.linear_rgb.distance(&rgb2) < 0.0001, + "{}: {:?} != {:?}", + color.name, + color.linear_rgb, + rgb2 + ); + assert_approx_eq!(color.lab.lightness, laba.lightness, 0.001); + if laba.lightness > 0.01 { + assert_approx_eq!(color.lab.a, laba.a, 0.1); + } + if laba.lightness > 0.01 && laba.a > 0.01 { + assert!( + (color.lab.b - laba.b).abs() < 1.7, + "{:?} != {:?}", + color.lab, + laba + ); + } + assert_approx_eq!(color.lab.alpha, laba.alpha, 0.001); + } + } +} diff --git a/crates/bevy_color/src/lcha.rs b/crates/bevy_color/src/lcha.rs index c56995a397..2a01147a68 100644 --- a/crates/bevy_color/src/lcha.rs +++ b/crates/bevy_color/src/lcha.rs @@ -1,4 +1,4 @@ -use crate::{Alpha, Hsla, LinearRgba, Luminance, Mix, Oklaba, Srgba, StandardColor, Xyza}; +use crate::{Alpha, Laba, LinearRgba, Luminance, Mix, Srgba, StandardColor, Xyza}; use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize}; use serde::{Deserialize, Serialize}; @@ -66,16 +66,6 @@ impl Lcha { pub const fn with_lightness(self, lightness: f32) -> Self { Self { lightness, ..self } } - - /// CIE Epsilon Constant - /// - /// See [Continuity (16) (17)](http://brucelindbloom.com/index.html?LContinuity.html) - pub const CIE_EPSILON: f32 = 216.0 / 24389.0; - - /// CIE Kappa Constant - /// - /// See [Continuity (16) (17)](http://brucelindbloom.com/index.html?LContinuity.html) - pub const CIE_KAPPA: f32 = 24389.0 / 27.0; } impl Default for Lcha { @@ -138,7 +128,7 @@ impl Luminance for Lcha { } } -impl From for Xyza { +impl From for Laba { fn from( Lcha { lightness, @@ -147,79 +137,25 @@ impl From for Xyza { alpha, }: Lcha, ) -> Self { - let lightness = lightness * 100.0; - let chroma = chroma * 100.0; - - // convert LCH to Lab - // http://www.brucelindbloom.com/index.html?Eqn_LCH_to_Lab.html + // Based on http://www.brucelindbloom.com/index.html?Eqn_LCH_to_Lab.html let l = lightness; let a = chroma * hue.to_radians().cos(); let b = chroma * hue.to_radians().sin(); - // convert Lab to XYZ - // http://www.brucelindbloom.com/index.html?Eqn_Lab_to_XYZ.html - let fy = (l + 16.0) / 116.0; - let fx = a / 500.0 + fy; - let fz = fy - b / 200.0; - let xr = { - let fx3 = fx.powf(3.0); - - if fx3 > Lcha::CIE_EPSILON { - fx3 - } else { - (116.0 * fx - 16.0) / Lcha::CIE_KAPPA - } - }; - let yr = if l > Lcha::CIE_EPSILON * Lcha::CIE_KAPPA { - ((l + 16.0) / 116.0).powf(3.0) - } else { - l / Lcha::CIE_KAPPA - }; - let zr = { - let fz3 = fz.powf(3.0); - - if fz3 > Lcha::CIE_EPSILON { - fz3 - } else { - (116.0 * fz - 16.0) / Lcha::CIE_KAPPA - } - }; - let x = xr * Xyza::D65_WHITE.x; - let y = yr * Xyza::D65_WHITE.y; - let z = zr * Xyza::D65_WHITE.z; - - Xyza::new(x, y, z, alpha) + Laba::new(l, a, b, alpha) } } -impl From for Lcha { - fn from(Xyza { x, y, z, alpha }: Xyza) -> Self { - // XYZ to Lab - // http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_Lab.html - let xr = x / Xyza::D65_WHITE.x; - let yr = y / Xyza::D65_WHITE.y; - let zr = z / Xyza::D65_WHITE.z; - let fx = if xr > Lcha::CIE_EPSILON { - xr.cbrt() - } else { - (Lcha::CIE_KAPPA * xr + 16.0) / 116.0 - }; - let fy = if yr > Lcha::CIE_EPSILON { - yr.cbrt() - } else { - (Lcha::CIE_KAPPA * yr + 16.0) / 116.0 - }; - let fz = if yr > Lcha::CIE_EPSILON { - zr.cbrt() - } else { - (Lcha::CIE_KAPPA * zr + 16.0) / 116.0 - }; - let l = 116.0 * fy - 16.0; - let a = 500.0 * (fx - fy); - let b = 200.0 * (fy - fz); - - // Lab to LCH - // http://www.brucelindbloom.com/index.html?Eqn_Lab_to_LCH.html +impl From for Lcha { + fn from( + Laba { + lightness, + a, + b, + alpha, + }: Laba, + ) -> Self { + // Based on http://www.brucelindbloom.com/index.html?Eqn_Lab_to_LCH.html let c = (a.powf(2.0) + b.powf(2.0)).sqrt(); let h = { let h = b.to_radians().atan2(a.to_radians()).to_degrees(); @@ -231,47 +167,48 @@ impl From for Lcha { } }; - let lightness = (l / 100.0).clamp(0.0, 1.5); - let chroma = (c / 100.0).clamp(0.0, 1.5); + let chroma = c.clamp(0.0, 1.5); let hue = h; Lcha::new(lightness, chroma, hue, alpha) } } +// Derived Conversions + impl From for Lcha { fn from(value: Srgba) -> Self { - Xyza::from(value).into() + Laba::from(value).into() } } impl From for Srgba { fn from(value: Lcha) -> Self { - Xyza::from(value).into() + Laba::from(value).into() } } impl From for Lcha { fn from(value: LinearRgba) -> Self { - Srgba::from(value).into() + Laba::from(value).into() } } impl From for LinearRgba { fn from(value: Lcha) -> Self { - LinearRgba::from(Srgba::from(value)) + Laba::from(value).into() } } -impl From for Lcha { - fn from(value: Oklaba) -> Self { - Srgba::from(value).into() +impl From for Lcha { + fn from(value: Xyza) -> Self { + Laba::from(value).into() } } -impl From for Lcha { - fn from(value: Hsla) -> Self { - Srgba::from(value).into() +impl From for Xyza { + fn from(value: Lcha) -> Self { + Laba::from(value).into() } } diff --git a/crates/bevy_color/src/lib.rs b/crates/bevy_color/src/lib.rs index 3bdce2df24..a90c2fd627 100644 --- a/crates/bevy_color/src/lib.rs +++ b/crates/bevy_color/src/lib.rs @@ -7,6 +7,7 @@ //! - [`Hsla`] (hue, saturation, lightness, alpha) //! - [`Hsva`] (hue, saturation, value, alpha) //! - [`Hwba`] (hue, whiteness, blackness, alpha) +//! - [`Laba`] (lightness, a-axis, b-axis, alpha) //! - [`Lcha`] (lightness, chroma, hue, alpha) //! - [`Oklaba`] (lightness, a-axis, b-axis, alpha) //! - [`Xyza`] (x-axis, y-axis, z-axis, alpha) @@ -83,6 +84,7 @@ mod color_range; mod hsla; mod hsva; mod hwba; +mod laba; mod lcha; mod linear_rgba; mod oklaba; @@ -100,6 +102,7 @@ pub use color_range::*; pub use hsla::*; pub use hsva::*; pub use hwba::*; +pub use laba::*; pub use lcha::*; pub use linear_rgba::*; pub use oklaba::*; @@ -122,6 +125,7 @@ where Self: From + Into, Self: From + Into, Self: From + Into, + Self: From + Into, Self: From + Into, Self: From + Into, Self: From + Into, diff --git a/crates/bevy_color/src/linear_rgba.rs b/crates/bevy_color/src/linear_rgba.rs index 375d910c12..9a682b2ac9 100644 --- a/crates/bevy_color/src/linear_rgba.rs +++ b/crates/bevy_color/src/linear_rgba.rs @@ -1,7 +1,4 @@ -use crate::{ - color_difference::EuclideanDistance, oklaba::Oklaba, Alpha, Hsla, Luminance, Mix, Srgba, - StandardColor, -}; +use crate::{color_difference::EuclideanDistance, Alpha, Luminance, Mix, StandardColor}; use bevy_math::Vec4; use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize}; use serde::{Deserialize, Serialize}; @@ -202,40 +199,6 @@ impl From for Vec4 { } } -#[allow(clippy::excessive_precision)] -impl From for LinearRgba { - fn from(value: Oklaba) -> Self { - let Oklaba { l, a, b, alpha } = value; - - // From https://github.com/Ogeon/palette/blob/e75eab2fb21af579353f51f6229a510d0d50a311/palette/src/oklab.rs#L312-L332 - let l_ = l + 0.3963377774 * a + 0.2158037573 * b; - let m_ = l - 0.1055613458 * a - 0.0638541728 * b; - let s_ = l - 0.0894841775 * a - 1.2914855480 * b; - - let l = l_ * l_ * l_; - let m = m_ * m_ * m_; - let s = s_ * s_ * s_; - - let red = 4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s; - let green = -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s; - let blue = -0.0041960863 * l - 0.7034186147 * m + 1.7076147010 * s; - - Self { - red, - green, - blue, - alpha, - } - } -} - -impl From for LinearRgba { - #[inline] - fn from(value: Hsla) -> Self { - LinearRgba::from(Srgba::from(value)) - } -} - impl From for wgpu::Color { fn from(color: LinearRgba) -> Self { wgpu::Color { diff --git a/crates/bevy_color/src/oklaba.rs b/crates/bevy_color/src/oklaba.rs index dceac2157e..5913dcbfa5 100644 --- a/crates/bevy_color/src/oklaba.rs +++ b/crates/bevy_color/src/oklaba.rs @@ -1,6 +1,6 @@ use crate::{ - color_difference::EuclideanDistance, Alpha, Hsla, Lcha, LinearRgba, Luminance, Mix, Srgba, - StandardColor, + color_difference::EuclideanDistance, Alpha, Hsla, Hsva, Hwba, Lcha, LinearRgba, Luminance, Mix, + Srgba, StandardColor, Xyza, }; use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize}; use serde::{Deserialize, Serialize}; @@ -146,9 +146,68 @@ impl From for Oklaba { } } -impl From for Oklaba { - fn from(value: Srgba) -> Self { - Oklaba::from(LinearRgba::from(value)) +#[allow(clippy::excessive_precision)] +impl From for LinearRgba { + fn from(value: Oklaba) -> Self { + let Oklaba { l, a, b, alpha } = value; + + // From https://github.com/Ogeon/palette/blob/e75eab2fb21af579353f51f6229a510d0d50a311/palette/src/oklab.rs#L312-L332 + let l_ = l + 0.3963377774 * a + 0.2158037573 * b; + let m_ = l - 0.1055613458 * a - 0.0638541728 * b; + let s_ = l - 0.0894841775 * a - 1.2914855480 * b; + + let l = l_ * l_ * l_; + let m = m_ * m_ * m_; + let s = s_ * s_ * s_; + + let red = 4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s; + let green = -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s; + let blue = -0.0041960863 * l - 0.7034186147 * m + 1.7076147010 * s; + + Self { + red, + green, + blue, + alpha, + } + } +} + +// Derived Conversions + +impl From for Oklaba { + fn from(value: Hsla) -> Self { + LinearRgba::from(value).into() + } +} + +impl From for Hsla { + fn from(value: Oklaba) -> Self { + LinearRgba::from(value).into() + } +} + +impl From for Oklaba { + fn from(value: Hsva) -> Self { + LinearRgba::from(value).into() + } +} + +impl From for Hsva { + fn from(value: Oklaba) -> Self { + LinearRgba::from(value).into() + } +} + +impl From for Oklaba { + fn from(value: Hwba) -> Self { + LinearRgba::from(value).into() + } +} + +impl From for Hwba { + fn from(value: Oklaba) -> Self { + LinearRgba::from(value).into() } } @@ -158,8 +217,32 @@ impl From for Oklaba { } } -impl From for Oklaba { - fn from(value: Hsla) -> Self { +impl From for Lcha { + fn from(value: Oklaba) -> Self { + LinearRgba::from(value).into() + } +} + +impl From for Oklaba { + fn from(value: Srgba) -> Self { + LinearRgba::from(value).into() + } +} + +impl From for Srgba { + fn from(value: Oklaba) -> Self { + LinearRgba::from(value).into() + } +} + +impl From for Oklaba { + fn from(value: Xyza) -> Self { + LinearRgba::from(value).into() + } +} + +impl From for Xyza { + fn from(value: Oklaba) -> Self { LinearRgba::from(value).into() } } diff --git a/crates/bevy_color/src/srgba.rs b/crates/bevy_color/src/srgba.rs index 761496d334..696484e7ce 100644 --- a/crates/bevy_color/src/srgba.rs +++ b/crates/bevy_color/src/srgba.rs @@ -1,6 +1,5 @@ use crate::color_difference::EuclideanDistance; -use crate::oklaba::Oklaba; -use crate::{Alpha, LinearRgba, Luminance, Mix, StandardColor}; +use crate::{Alpha, LinearRgba, Luminance, Mix, StandardColor, Xyza}; use bevy_math::Vec4; use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize}; use serde::{Deserialize, Serialize}; @@ -297,12 +296,6 @@ impl From for LinearRgba { } } -impl From for Srgba { - fn from(value: Oklaba) -> Self { - Srgba::from(LinearRgba::from(value)) - } -} - impl From for [f32; 4] { fn from(color: Srgba) -> Self { [color.red, color.green, color.blue, color.alpha] @@ -315,6 +308,20 @@ impl From for Vec4 { } } +// Derived Conversions + +impl From for Srgba { + fn from(value: Xyza) -> Self { + LinearRgba::from(value).into() + } +} + +impl From for Xyza { + fn from(value: Srgba) -> Self { + LinearRgba::from(value).into() + } +} + /// Error returned if a hex string could not be parsed as a color. #[derive(Debug, Error, PartialEq, Eq)] pub enum HexColorError { diff --git a/crates/bevy_color/src/test_colors.rs b/crates/bevy_color/src/test_colors.rs index d1b1372241..027eb5e3a7 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, Lcha, LinearRgba, Oklaba, Srgba, Xyza}; +use crate::{Hsla, Hsva, Hwba, Laba, Lcha, LinearRgba, Oklaba, Srgba, Xyza}; #[cfg(test)] pub struct TestColor { @@ -10,6 +10,7 @@ pub struct TestColor { pub hsl: Hsla, pub hsv: Hsva, pub hwb: Hwba, + pub lab: Laba, pub lch: Lcha, pub oklab: Oklaba, pub xyz: Xyza, @@ -27,6 +28,7 @@ pub const TEST_COLORS: &[TestColor] = &[ lch: Lcha::new(0.0, 0.0, 0.0000136603785, 1.0), hsv: Hsva::new(0.0, 0.0, 0.0, 1.0), 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), xyz: Xyza::new(0.0, 0.0, 0.0, 1.0), }, @@ -39,6 +41,7 @@ pub const TEST_COLORS: &[TestColor] = &[ lch: Lcha::new(1.0, 0.0, 0.0000136603785, 1.0), hsv: Hsva::new(0.0, 0.0, 1.0, 1.0), 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), xyz: Xyza::new(0.95047, 1.0, 1.08883, 1.0), }, @@ -52,6 +55,7 @@ pub const TEST_COLORS: &[TestColor] = &[ oklab: Oklaba::new(0.6279554, 0.22486295, 0.1258463, 1.0), 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), xyz: Xyza::new(0.4124564, 0.2126729, 0.0193339, 1.0), }, // green @@ -63,6 +67,7 @@ pub const TEST_COLORS: &[TestColor] = &[ lch: Lcha::new(0.87734723, 1.1977587, 136.01595, 1.0), hsv: Hsva::new(120.0, 1.0, 1.0, 1.0), 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), xyz: Xyza::new(0.3575761, 0.7151522, 0.119192, 1.0), }, @@ -76,6 +81,7 @@ pub const TEST_COLORS: &[TestColor] = &[ oklab: Oklaba::new(0.4520137, -0.032456964, -0.31152815, 1.0), 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), xyz: Xyza::new(0.1804375, 0.072175, 0.9503041, 1.0), }, // yellow @@ -88,6 +94,7 @@ pub const TEST_COLORS: &[TestColor] = &[ oklab: Oklaba::new(0.9679827, -0.07136908, 0.19856972, 1.0), 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), xyz: Xyza::new(0.7700325, 0.9278251, 0.1385259, 1.0), }, // magenta @@ -98,6 +105,7 @@ pub const TEST_COLORS: &[TestColor] = &[ hsl: Hsla::new(300.0, 1.0, 0.5, 1.0), hsv: Hsva::new(300.0, 1.0, 1.0, 1.0), hwb: Hwba::new(300.0, 0.0, 0.0, 1.0), + 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), xyz: Xyza::new(0.5928939, 0.28484792, 0.969638, 1.0), @@ -112,6 +120,7 @@ pub const TEST_COLORS: &[TestColor] = &[ oklab: Oklaba::new(0.90539926, -0.1494439, -0.039398134, 1.0), 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), xyz: Xyza::new(0.5380136, 0.78732723, 1.069496, 1.0), }, // gray @@ -124,6 +133,7 @@ pub const TEST_COLORS: &[TestColor] = &[ oklab: Oklaba::new(0.5981807, 0.00000011920929, 0.0, 1.0), 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), xyz: Xyza::new(0.2034397, 0.21404117, 0.23305441, 1.0), }, // olive @@ -135,6 +145,7 @@ pub const TEST_COLORS: &[TestColor] = &[ lch: Lcha::new(0.51677734, 0.57966936, 102.851265, 1.0), hsv: Hsva::new(60.0, 1.0, 0.5, 1.0), 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), xyz: Xyza::new(0.16481864, 0.19859275, 0.029650241, 1.0), }, @@ -147,6 +158,7 @@ pub const TEST_COLORS: &[TestColor] = &[ lch: Lcha::new(0.29655674, 0.69114214, 328.23495, 1.0), hsv: Hsva::new(300.0, 1.0, 0.5, 1.0), 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), xyz: Xyza::new(0.12690368, 0.060969174, 0.20754242, 1.0), }, @@ -160,6 +172,7 @@ pub const TEST_COLORS: &[TestColor] = &[ oklab: Oklaba::new(0.54159236, -0.08939436, -0.02356726, 1.0), 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), xyz: Xyza::new(0.11515705, 0.16852042, 0.22891617, 1.0), }, // maroon @@ -170,6 +183,7 @@ pub const TEST_COLORS: &[TestColor] = &[ hsl: Hsla::new(0.0, 1.0, 0.25, 1.0), hsv: Hsva::new(0.0, 1.0, 0.5, 1.0), hwb: Hwba::new(0.0, 0.0, 0.5, 1.0), + 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), xyz: Xyza::new(0.08828264, 0.045520753, 0.0041382504, 1.0), @@ -182,6 +196,7 @@ pub const TEST_COLORS: &[TestColor] = &[ hsl: Hsla::new(120.0, 1.0, 0.25, 1.0), hsv: Hsva::new(120.0, 1.0, 0.5, 1.0), hwb: Hwba::new(120.0, 0.0, 0.5, 1.0), + 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), xyz: Xyza::new(0.076536, 0.153072, 0.025511991, 1.0), @@ -195,6 +210,7 @@ pub const TEST_COLORS: &[TestColor] = &[ lch: Lcha::new(0.12890343, 0.8004114, 306.28494, 1.0), hsv: Hsva::new(240.0, 1.0, 0.5, 1.0), 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), xyz: Xyza::new(0.03862105, 0.01544842, 0.20340417, 1.0), }, @@ -207,6 +223,7 @@ pub const TEST_COLORS: &[TestColor] = &[ lch: Lcha::new(0.51677734, 0.57966936, 102.851265, 1.0), hsv: Hsva::new(60.0, 1.0, 0.5, 1.0), 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), xyz: Xyza::new(0.16481864, 0.19859275, 0.029650241, 1.0), }, @@ -219,6 +236,7 @@ pub const TEST_COLORS: &[TestColor] = &[ lch: Lcha::new(0.29655674, 0.69114214, 328.23495, 1.0), hsv: Hsva::new(300.0, 1.0, 0.5, 1.0), 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), xyz: Xyza::new(0.12690368, 0.060969174, 0.20754242, 1.0), }, @@ -232,6 +250,7 @@ pub const TEST_COLORS: &[TestColor] = &[ oklab: Oklaba::new(0.54159236, -0.08939436, -0.02356726, 1.0), 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), xyz: Xyza::new(0.11515705, 0.16852042, 0.22891617, 1.0), }, ]; diff --git a/crates/bevy_color/src/xyza.rs b/crates/bevy_color/src/xyza.rs index 6a11749852..6694ee38e0 100644 --- a/crates/bevy_color/src/xyza.rs +++ b/crates/bevy_color/src/xyza.rs @@ -1,4 +1,4 @@ -use crate::{Alpha, Hsla, LinearRgba, Luminance, Mix, Oklaba, Srgba, StandardColor}; +use crate::{Alpha, LinearRgba, Luminance, Mix, StandardColor}; use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize}; use serde::{Deserialize, Serialize}; @@ -162,42 +162,6 @@ impl From for LinearRgba { } } -impl From for Xyza { - fn from(value: Srgba) -> Self { - LinearRgba::from(value).into() - } -} - -impl From for Srgba { - fn from(value: Xyza) -> Self { - LinearRgba::from(value).into() - } -} - -impl From for Xyza { - fn from(value: Hsla) -> Self { - LinearRgba::from(value).into() - } -} - -impl From for Hsla { - fn from(value: Xyza) -> Self { - LinearRgba::from(value).into() - } -} - -impl From for Xyza { - fn from(value: Oklaba) -> Self { - LinearRgba::from(value).into() - } -} - -impl From for Oklaba { - fn from(value: Xyza) -> Self { - LinearRgba::from(value).into() - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/crates/bevy_render/src/color/mod.rs b/crates/bevy_render/src/color/mod.rs index a68204d765..b669abb878 100644 --- a/crates/bevy_render/src/color/mod.rs +++ b/crates/bevy_render/src/color/mod.rs @@ -1,4 +1,6 @@ -use bevy_color::{Color, HexColorError, Hsla, Hsva, Hwba, Lcha, LinearRgba, Oklaba, Srgba, Xyza}; +use bevy_color::{ + Color, HexColorError, Hsla, Hsva, Hwba, Laba, Lcha, LinearRgba, Oklaba, Srgba, Xyza, +}; use bevy_math::{Vec3, Vec4}; use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize}; @@ -924,6 +926,7 @@ impl From for LegacyColor { Color::Hsla(x) => x.into(), Color::Hsva(x) => x.into(), Color::Hwba(x) => x.into(), + Color::Laba(x) => x.into(), Color::Lcha(x) => x.into(), Color::Oklaba(x) => x.into(), Color::Xyza(x) => x.into(), @@ -1032,6 +1035,12 @@ impl From for LegacyColor { } } +impl From for LegacyColor { + fn from(value: Laba) -> Self { + Lcha::from(value).into() + } +} + impl From for LegacyColor { fn from( Lcha { @@ -1056,6 +1065,12 @@ impl From for Lcha { } } +impl From for Laba { + fn from(value: LegacyColor) -> Self { + Color::from(value).into() + } +} + impl From for Oklaba { fn from(value: LegacyColor) -> Self { LinearRgba::from(value).into()