bevy_color: Created a private trait StandardColor (#12072)

# Objective

- Assist Bevy contributors in the creation of `bevy_color` spaces by
ensuring consistent API implementation.

## Solution

Created a `pub(crate)` trait `StandardColor` which has all the
requirements for a typical color space (e.g, `Srgba`, `Color`, etc.).

---

## Changelog

- Implemented traits missing from certain color spaces.

## Migration Guide

_No migration required_
This commit is contained in:
Zachary Harrold 2024-02-24 14:04:03 +11:00 committed by GitHub
parent d31de3f139
commit a52b2518fc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 152 additions and 12 deletions

View file

@ -1,10 +1,14 @@
use crate::{Hsla, Lcha, LinearRgba, Oklaba, Srgba};
use crate::{Alpha, Hsla, Lcha, LinearRgba, Oklaba, Srgba, StandardColor};
use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize};
use bevy_render::color::Color as LegacyColor;
use serde::{Deserialize, Serialize};
/// An enumerated type that can represent any of the color types in this crate.
///
/// This is useful when you need to store a color in a data structure that can't be generic over
/// the color type.
#[derive(Debug, Clone, Copy, PartialEq)]
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Reflect)]
#[reflect(PartialEq, Serialize, Deserialize)]
pub enum Color {
/// A color in the sRGB color space with alpha.
Srgba(Srgba),
@ -18,6 +22,8 @@ pub enum Color {
Oklaba(Oklaba),
}
impl StandardColor for Color {}
impl Color {
/// Return the color as a linear RGBA color.
pub fn linear(&self) -> LinearRgba {
@ -37,6 +43,32 @@ impl Default for Color {
}
}
impl Alpha for Color {
fn with_alpha(&self, alpha: f32) -> Self {
let mut new = *self;
match &mut new {
Color::Srgba(x) => *x = x.with_alpha(alpha),
Color::LinearRgba(x) => *x = x.with_alpha(alpha),
Color::Hsla(x) => *x = x.with_alpha(alpha),
Color::Lcha(x) => *x = x.with_alpha(alpha),
Color::Oklaba(x) => *x = x.with_alpha(alpha),
}
new
}
fn alpha(&self) -> f32 {
match self {
Color::Srgba(x) => x.alpha(),
Color::LinearRgba(x) => x.alpha(),
Color::Hsla(x) => x.alpha(),
Color::Lcha(x) => x.alpha(),
Color::Oklaba(x) => x.alpha(),
}
}
}
impl From<Srgba> for Color {
fn from(value: Srgba) -> Self {
Self::Srgba(value)
@ -126,3 +158,26 @@ impl From<Color> for Oklaba {
}
}
}
impl From<LegacyColor> for Color {
fn from(value: LegacyColor) -> Self {
match value {
LegacyColor::Rgba { .. } => Srgba::from(value).into(),
LegacyColor::RgbaLinear { .. } => LinearRgba::from(value).into(),
LegacyColor::Hsla { .. } => Hsla::from(value).into(),
LegacyColor::Lcha { .. } => Lcha::from(value).into(),
}
}
}
impl From<Color> for LegacyColor {
fn from(value: Color) -> Self {
match value {
Color::Srgba(x) => x.into(),
Color::LinearRgba(x) => x.into(),
Color::Hsla(x) => x.into(),
Color::Lcha(x) => x.into(),
Color::Oklaba(x) => x.into(),
}
}
}

View file

@ -1,4 +1,4 @@
use crate::{Alpha, LinearRgba, Luminance, Mix, Srgba};
use crate::{Alpha, Lcha, LinearRgba, Luminance, Mix, Oklaba, Srgba, StandardColor};
use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize};
use bevy_render::color::HslRepresentation;
use serde::{Deserialize, Serialize};
@ -17,6 +17,8 @@ pub struct Hsla {
pub alpha: f32,
}
impl StandardColor for Hsla {}
impl Hsla {
/// Construct a new [`Hsla`] color from components.
///
@ -119,12 +121,6 @@ impl From<Srgba> for Hsla {
}
}
impl From<LinearRgba> for Hsla {
fn from(value: LinearRgba) -> Self {
Hsla::from(Srgba::from(value))
}
}
impl From<Hsla> for bevy_render::color::Color {
fn from(value: Hsla) -> Self {
bevy_render::color::Color::Hsla {
@ -150,6 +146,24 @@ impl From<bevy_render::color::Color> for Hsla {
}
}
impl From<LinearRgba> for Hsla {
fn from(value: LinearRgba) -> Self {
Srgba::from(value).into()
}
}
impl From<Oklaba> for Hsla {
fn from(value: Oklaba) -> Self {
Srgba::from(value).into()
}
}
impl From<Lcha> for Hsla {
fn from(value: Lcha) -> Self {
Srgba::from(value).into()
}
}
#[cfg(test)]
mod tests {
use super::*;

View file

@ -1,4 +1,4 @@
use crate::{Alpha, LinearRgba, Luminance, Mix, Srgba};
use crate::{Alpha, Hsla, LinearRgba, Luminance, Mix, Oklaba, Srgba, StandardColor};
use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize};
use bevy_render::color::LchRepresentation;
use serde::{Deserialize, Serialize};
@ -17,6 +17,8 @@ pub struct Lcha {
pub alpha: f32,
}
impl StandardColor for Lcha {}
impl Lcha {
/// Construct a new [`Lcha`] color from components.
///
@ -165,6 +167,18 @@ impl From<bevy_render::color::Color> for Lcha {
}
}
impl From<Oklaba> for Lcha {
fn from(value: Oklaba) -> Self {
Srgba::from(value).into()
}
}
impl From<Hsla> for Lcha {
fn from(value: Hsla) -> Self {
Srgba::from(value).into()
}
}
#[cfg(test)]
mod tests {
use super::*;

View file

@ -87,3 +87,25 @@ pub use lcha::*;
pub use linear_rgba::*;
pub use oklaba::*;
pub use srgba::*;
use bevy_render::color::Color as LegacyColor;
/// Describes the traits that a color should implement for consistency.
pub(crate) trait StandardColor
where
Self: core::fmt::Debug,
Self: Clone + Copy,
Self: PartialEq,
Self: serde::Serialize + for<'a> serde::Deserialize<'a>,
Self: bevy_reflect::Reflect,
Self: Default,
Self: From<Color> + Into<Color>,
Self: From<LegacyColor> + Into<LegacyColor>,
Self: From<Srgba> + Into<Srgba>,
Self: From<LinearRgba> + Into<LinearRgba>,
Self: From<Hsla> + Into<Hsla>,
Self: From<Lcha> + Into<Lcha>,
Self: From<Oklaba> + Into<Oklaba>,
Self: Alpha,
{
}

View file

@ -1,5 +1,6 @@
use crate::{
color_difference::EuclideanDistance, oklaba::Oklaba, Alpha, Hsla, Luminance, Mix, Srgba,
StandardColor,
};
use bevy_math::Vec4;
use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize};
@ -20,6 +21,8 @@ pub struct LinearRgba {
pub alpha: f32,
}
impl StandardColor for LinearRgba {}
impl LinearRgba {
/// Construct a new [`LinearRgba`] color from components.
pub const fn new(red: f32, green: f32, blue: f32, alpha: f32) -> Self {

View file

@ -1,5 +1,9 @@
use crate::{color_difference::EuclideanDistance, Alpha, LinearRgba, Luminance, Mix, Srgba};
use crate::{
color_difference::EuclideanDistance, Alpha, Hsla, Lcha, LinearRgba, Luminance, Mix, Srgba,
StandardColor,
};
use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize};
use bevy_render::color::Color as LegacyColor;
use serde::{Deserialize, Serialize};
/// Color in Oklaba color space, with alpha
@ -16,6 +20,8 @@ pub struct Oklaba {
pub alpha: f32,
}
impl StandardColor for Oklaba {}
impl Oklaba {
/// Construct a new [`Oklaba`] color from components.
///
@ -132,6 +138,30 @@ impl From<Srgba> for Oklaba {
}
}
impl From<Lcha> for Oklaba {
fn from(value: Lcha) -> Self {
LinearRgba::from(value).into()
}
}
impl From<Hsla> for Oklaba {
fn from(value: Hsla) -> Self {
LinearRgba::from(value).into()
}
}
impl From<LegacyColor> for Oklaba {
fn from(value: LegacyColor) -> Self {
LinearRgba::from(value).into()
}
}
impl From<Oklaba> for LegacyColor {
fn from(value: Oklaba) -> Self {
LinearRgba::from(value).into()
}
}
#[cfg(test)]
mod tests {
use super::*;

View file

@ -1,6 +1,6 @@
use crate::color_difference::EuclideanDistance;
use crate::oklaba::Oklaba;
use crate::{Alpha, Hsla, LinearRgba, Luminance, Mix};
use crate::{Alpha, Hsla, LinearRgba, Luminance, Mix, StandardColor};
use bevy_math::Vec4;
use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize};
use bevy_render::color::{HexColorError, HslRepresentation, SrgbColorSpace};
@ -20,6 +20,8 @@ pub struct Srgba {
pub alpha: f32,
}
impl StandardColor for Srgba {}
impl Srgba {
// The standard VGA colors, with alpha set to 1.0.
// https://en.wikipedia.org/wiki/Web_colors#Basic_colors