Skip to content

Commit

Permalink
bevy_color: Add Oklch Color Space (#12168)
Browse files Browse the repository at this point in the history
# 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).
  • Loading branch information
bushrat011899 authored Feb 28, 2024
1 parent 6f849d3 commit 6774e04
Show file tree
Hide file tree
Showing 6 changed files with 425 additions and 7 deletions.
10 changes: 8 additions & 2 deletions crates/bevy_color/crates/gen_tests/src/main.rs
Original file line number Diff line number Diff line change
@@ -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"),
Expand All @@ -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 {{
Expand All @@ -38,6 +38,7 @@ pub struct TestColor {{
pub lab: Laba,
pub lch: Lcha,
pub oklab: Oklaba,
pub oklch: Oklcha,
pub xyz: Xyza,
}}
"
Expand All @@ -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!(
Expand All @@ -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),
Expand All @@ -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),
Expand Down
60 changes: 59 additions & 1 deletion crates/bevy_color/src/color.rs
Original file line number Diff line number Diff line change
@@ -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};

Expand Down Expand Up @@ -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),
}
Expand Down Expand Up @@ -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 })
Expand Down Expand Up @@ -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),
}

Expand All @@ -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(),
}
}
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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(),
}
}
Expand All @@ -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(),
}
}
Expand All @@ -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(),
}
}
Expand All @@ -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(),
}
}
Expand All @@ -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(),
}
}
Expand All @@ -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(),
}
}
Expand All @@ -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(),
}
}
Expand All @@ -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(),
}
}
Expand All @@ -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,
}
}
Expand Down
8 changes: 6 additions & 2 deletions crates/bevy_color/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
//!
Expand Down Expand Up @@ -87,6 +87,7 @@ mod laba;
mod lcha;
mod linear_rgba;
mod oklaba;
mod oklcha;
pub mod palettes;
mod srgba;
#[cfg(test)]
Expand All @@ -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::*;
}
Expand All @@ -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::*;

Expand All @@ -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,
{
Expand Down
Loading

0 comments on commit 6774e04

Please sign in to comment.