Skip to content

Commit

Permalink
Color maths 4 (#12575)
Browse files Browse the repository at this point in the history
# Objective

- Fixes #12202 

## Solution

- This PR implements componentwise (including alpha) addition,
subtraction and scalar multiplication/division for some color types.
- The mentioned color types are `Laba`, `Oklaba`, `LinearRgba` and
`Xyza` as all of them are either physically or perceptually linear as
mentioned by @alice-i-cecile in the issue.

---

## Changelog

- Scalar mul/div for `LinearRgba` may modify alpha now.

## Migration Guide

- Users of scalar mul/div for `LinearRgba` need to be aware of the
change and maybe use the `.clamp()` methods or manually set the `alpha`
channel.
  • Loading branch information
lynn-lumen authored Mar 19, 2024
1 parent e7a31d0 commit d7372f2
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 50 deletions.
6 changes: 4 additions & 2 deletions crates/bevy_color/src/laba.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
Alpha, ClampColor, Hsla, Hsva, Hwba, LinearRgba, Luminance, Mix, Oklaba, Srgba, StandardColor,
Xyza,
impl_componentwise_point, Alpha, ClampColor, Hsla, Hsva, Hwba, LinearRgba, Luminance, Mix,
Oklaba, Srgba, StandardColor, Xyza,
};
use bevy_reflect::prelude::*;
use serde::{Deserialize, Serialize};
Expand All @@ -25,6 +25,8 @@ pub struct Laba {

impl StandardColor for Laba {}

impl_componentwise_point!(Laba, [lightness, a, b, alpha]);

impl Laba {
/// Construct a new [`Laba`] color from components.
///
Expand Down
64 changes: 64 additions & 0 deletions crates/bevy_color/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@
//! 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.
//!
//! Color types that are either physically or perceptually linear also implement `Add<Self>`, `Sub<Self>`, `Mul<f32>` and `Div<f32>`
//! allowing you to use them with splines.
//!
//! Please note that most often adding or subtracting colors is not what you may want.
//! Please have a look at other operations like blending, lightening or mixing colors using e.g. [`Mix`] or [`Luminance`] instead.
//!
//! # Example
//!
//! ```
Expand Down Expand Up @@ -151,3 +157,61 @@ where
Self: Alpha,
{
}

macro_rules! impl_componentwise_point {
($ty: ident, [$($element: ident),+]) => {
impl std::ops::Add<Self> for $ty {
type Output = Self;

fn add(self, rhs: Self) -> Self::Output {
Self::Output {
$($element: self.$element + rhs.$element,)+
}
}
}

impl std::ops::Sub<Self> for $ty {
type Output = Self;

fn sub(self, rhs: Self) -> Self::Output {
Self::Output {
$($element: self.$element - rhs.$element,)+
}
}
}

impl std::ops::Mul<f32> for $ty {
type Output = Self;

fn mul(self, rhs: f32) -> Self::Output {
Self::Output {
$($element: self.$element * rhs,)+
}
}
}

impl std::ops::Mul<$ty> for f32 {
type Output = $ty;

fn mul(self, rhs: $ty) -> Self::Output {
Self::Output {
$($element: self * rhs.$element,)+
}
}
}

impl std::ops::Div<f32> for $ty {
type Output = Self;

fn div(self, rhs: f32) -> Self::Output {
Self::Output {
$($element: self.$element / rhs,)+
}
}
}

impl bevy_math::cubic_splines::Point for $ty {}
};
}

pub(crate) use impl_componentwise_point;
49 changes: 4 additions & 45 deletions crates/bevy_color/src/linear_rgba.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use std::ops::{Div, Mul};

use crate::{
color_difference::EuclideanDistance, Alpha, ClampColor, Luminance, Mix, StandardColor,
color_difference::EuclideanDistance, impl_componentwise_point, Alpha, ClampColor, Luminance,
Mix, StandardColor,
};
use bevy_math::Vec4;
use bevy_reflect::prelude::*;
Expand Down Expand Up @@ -29,6 +28,8 @@ pub struct LinearRgba {

impl StandardColor for LinearRgba {}

impl_componentwise_point!(LinearRgba, [red, green, blue, alpha]);

impl LinearRgba {
/// A fully black color with full alpha.
pub const BLACK: Self = Self {
Expand Down Expand Up @@ -299,48 +300,6 @@ impl From<LinearRgba> for wgpu::Color {
}
}

/// All color channels are scaled directly,
/// but alpha is unchanged.
///
/// Values are not clamped.
impl Mul<f32> for LinearRgba {
type Output = Self;

fn mul(self, rhs: f32) -> Self {
Self {
red: self.red * rhs,
green: self.green * rhs,
blue: self.blue * rhs,
alpha: self.alpha,
}
}
}

impl Mul<LinearRgba> for f32 {
type Output = LinearRgba;

fn mul(self, rhs: LinearRgba) -> LinearRgba {
rhs * self
}
}

/// All color channels are scaled directly,
/// but alpha is unchanged.
///
/// Values are not clamped.
impl Div<f32> for LinearRgba {
type Output = Self;

fn div(self, rhs: f32) -> Self {
Self {
red: self.red / rhs,
green: self.green / rhs,
blue: self.blue / rhs,
alpha: self.alpha,
}
}
}

// [`LinearRgba`] is intended to be used with shaders
// So it's the only color type that implements [`ShaderType`] to make it easier to use inside shaders
impl encase::ShaderType for LinearRgba {
Expand Down
6 changes: 4 additions & 2 deletions crates/bevy_color/src/oklaba.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
color_difference::EuclideanDistance, Alpha, ClampColor, Hsla, Hsva, Hwba, Lcha, LinearRgba,
Luminance, Mix, Srgba, StandardColor, Xyza,
color_difference::EuclideanDistance, impl_componentwise_point, Alpha, ClampColor, Hsla, Hsva,
Hwba, Lcha, LinearRgba, Luminance, Mix, Srgba, StandardColor, Xyza,
};
use bevy_reflect::prelude::*;
use serde::{Deserialize, Serialize};
Expand All @@ -25,6 +25,8 @@ pub struct Oklaba {

impl StandardColor for Oklaba {}

impl_componentwise_point!(Oklaba, [lightness, a, b, alpha]);

impl Oklaba {
/// Construct a new [`Oklaba`] color from components.
///
Expand Down
6 changes: 5 additions & 1 deletion crates/bevy_color/src/xyza.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use crate::{Alpha, ClampColor, LinearRgba, Luminance, Mix, StandardColor};
use crate::{
impl_componentwise_point, Alpha, ClampColor, LinearRgba, Luminance, Mix, StandardColor,
};
use bevy_reflect::prelude::*;
use serde::{Deserialize, Serialize};

Expand All @@ -22,6 +24,8 @@ pub struct Xyza {

impl StandardColor for Xyza {}

impl_componentwise_point!(Xyza, [x, y, z, alpha]);

impl Xyza {
/// Construct a new [`Xyza`] color from components.
///
Expand Down

0 comments on commit d7372f2

Please sign in to comment.