bevy_color: Add Oklch Color Space (#12168)

# Objective

- Complete compatibility with CSS Module 4

## Solution

- Added `Oklcha` which implements the Oklch color model.
- Updated `Color` and `LegacyColor` accordingly.

## Migration Guide

- Convert `Oklcha` to `Oklaba` using the provided `From` implementations
and then handle accordingly.

## Notes

This is the _last_ color space missing from the CSS Module 4 standard,
and is also the one I believe we should recommend users actually work
with for hand-crafting colours. It has all the uniformity benefits of
Oklab combined with the intuition chroma and hue provide (when compared
to a-axis and b-axis parameters).
This commit is contained in:
Zachary Harrold 2024-02-28 12:22:55 +11:00 committed by GitHub
parent 6f849d32c8
commit 6774e042c7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 425 additions and 7 deletions

View file

@ -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),

View file

@ -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<Oklaba> for Color {
}
}
impl From<Oklcha> for Color {
fn from(value: Oklcha) -> Self {
Self::Oklcha(value)
}
}
impl From<Lcha> for Color {
fn from(value: Lcha) -> Self {
Self::Lcha(value)
@ -327,6 +359,7 @@ impl From<Color> 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<Color> 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<Color> 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<Color> 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<Color> 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<Color> 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<Color> 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<Color> 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<Color> 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<Color> 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,
}
}

View file

@ -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<Laba> + Into<Laba>,
Self: From<Lcha> + Into<Lcha>,
Self: From<Oklaba> + Into<Oklaba>,
Self: From<Oklcha> + Into<Oklcha>,
Self: From<Xyza> + Into<Xyza>,
Self: Alpha,
{

View file

@ -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")]
/// <div>
#[doc = include_str!("../docs/diagrams/model_graph.svg")]
/// </div>
#[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<Oklaba> 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<Oklcha> 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<Hsla> for Oklcha {
fn from(value: Hsla) -> Self {
Oklaba::from(value).into()
}
}
impl From<Oklcha> for Hsla {
fn from(value: Oklcha) -> Self {
Oklaba::from(value).into()
}
}
impl From<Hsva> for Oklcha {
fn from(value: Hsva) -> Self {
Oklaba::from(value).into()
}
}
impl From<Oklcha> for Hsva {
fn from(value: Oklcha) -> Self {
Oklaba::from(value).into()
}
}
impl From<Hwba> for Oklcha {
fn from(value: Hwba) -> Self {
Oklaba::from(value).into()
}
}
impl From<Oklcha> for Hwba {
fn from(value: Oklcha) -> Self {
Oklaba::from(value).into()
}
}
impl From<Laba> for Oklcha {
fn from(value: Laba) -> Self {
Oklaba::from(value).into()
}
}
impl From<Oklcha> for Laba {
fn from(value: Oklcha) -> Self {
Oklaba::from(value).into()
}
}
impl From<Lcha> for Oklcha {
fn from(value: Lcha) -> Self {
Oklaba::from(value).into()
}
}
impl From<Oklcha> for Lcha {
fn from(value: Oklcha) -> Self {
Oklaba::from(value).into()
}
}
impl From<LinearRgba> for Oklcha {
fn from(value: LinearRgba) -> Self {
Oklaba::from(value).into()
}
}
impl From<Oklcha> for LinearRgba {
fn from(value: Oklcha) -> Self {
Oklaba::from(value).into()
}
}
impl From<Srgba> for Oklcha {
fn from(value: Srgba) -> Self {
Oklaba::from(value).into()
}
}
impl From<Oklcha> for Srgba {
fn from(value: Oklcha) -> Self {
Oklaba::from(value).into()
}
}
impl From<Xyza> for Oklcha {
fn from(value: Xyza) -> Self {
Oklaba::from(value).into()
}
}
impl From<Oklcha> 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);
}
}

View file

@ -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),
},
];

View file

@ -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<Color> 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<Oklaba> for LegacyColor {
}
}
impl From<LegacyColor> for Oklcha {
fn from(value: LegacyColor) -> Self {
LinearRgba::from(value).into()
}
}
impl From<Oklcha> for LegacyColor {
fn from(value: Oklcha) -> Self {
LinearRgba::from(value).into()
}
}
impl From<LegacyColor> for wgpu::Color {
fn from(color: LegacyColor) -> Self {
if let LegacyColor::RgbaLinear {