Skip to main content

bevy_color/
lib.rs

1#![cfg_attr(docsrs, feature(doc_cfg))]
2#![forbid(unsafe_code)]
3#![doc(
4    html_logo_url = "https://bevy.org/assets/icon.png",
5    html_favicon_url = "https://bevy.org/assets/icon.png"
6)]
7#![no_std]
8
9//! Representations of colors in various color spaces.
10//!
11//! This crate provides a number of color representations, including:
12//!
13//! - [`Srgba`] (standard RGBA, with gamma correction)
14//! - [`LinearRgba`] (linear RGBA, without gamma correction)
15//! - [`Hsla`] (hue, saturation, lightness, alpha)
16//! - [`Hsva`] (hue, saturation, value, alpha)
17//! - [`Hwba`] (hue, whiteness, blackness, alpha)
18//! - [`Laba`] (lightness, a-axis, b-axis, alpha)
19//! - [`Lcha`] (lightness, chroma, hue, alpha)
20//! - [`Oklaba`] (lightness, a-axis, b-axis, alpha)
21//! - [`Oklcha`] (lightness, chroma, hue, alpha)
22//! - [`Okhsla`] (hue, saturation, lightness, alpha)
23//! - [`Okhsva`] (hue, saturation, value, alpha)
24//! - [`Okhwba`] (hue, whiteness, blackness, alpha)
25//! - [`Xyza`] (x-axis, y-axis, z-axis, alpha)
26//!
27//! Each of these color spaces is represented as a distinct Rust type.
28//!
29//! # Color Space Usage
30//!
31//! Rendering engines typically use linear RGBA colors, which allow for physically accurate
32//! lighting calculations. However, linear RGBA colors are not perceptually uniform, because
33//! both human eyes and computer monitors have non-linear responses to light. "Standard" RGBA
34//! represents an industry-wide compromise designed to encode colors in a way that looks good to
35//! humans in as few bits as possible, but it is not suitable for lighting calculations.
36//!
37//! Most image file formats and scene graph formats use standard RGBA, because graphic design
38//! tools are intended to be used by humans. However, 3D lighting calculations operate in linear
39//! RGBA, so it is important to convert standard colors to linear before sending them to the GPU.
40//! Most Bevy APIs will handle this conversion automatically, but if you are writing a custom
41//! shader, you will need to do this conversion yourself.
42//!
43//! HSL and LCH are "cylindrical" color spaces, which means they represent colors as a combination
44//! of hue, saturation, and lightness (or chroma). These color spaces are useful for working
45//! with colors in an artistic way - for example, when creating gradients or color palettes.
46//! A gradient in HSL space from red to violet will produce a rainbow. The LCH color space is
47//! more perceptually accurate than HSL, but is less intuitive to work with.
48//!
49//! HSV and HWB are very closely related to HSL in their derivation, having identical definitions for
50//! hue. Where HSL uses saturation and lightness, HSV uses a slightly modified definition of saturation,
51//! and an analog of lightness in the form of value. In contrast, HWB instead uses whiteness and blackness
52//! parameters, which can be used to lighten and darken a particular hue respectively.
53//!
54//! Oklab and Oklch are perceptually uniform color spaces that are designed to be used for tasks such
55//! as image processing. They are not as widely used as the other color spaces, but are useful
56//! for tasks such as color correction and image analysis, where it is important to be able
57//! to do things like change color saturation without causing hue shifts.
58//!
59//! Okhsl, Okhsv and Okhwb are perceptually near-uniform color spaces that have a gamut matching
60//! that of standard RGB, making them convenient spaces for color picking. They can also be useful
61//! for creating perceptually uniform gradients or color palettes, like LCH but more user-friendly.
62//!
63//! XYZ is a foundational space commonly used in the definition of other more modern color
64//! spaces. The space is more formally known as CIE 1931, where the `x` and `z` axes represent
65//! a form of chromaticity, while `y` defines an illuminance level.
66//!
67//! See also the [Wikipedia article on color spaces](https://en.wikipedia.org/wiki/Color_space).
68#![doc = include_str!("../docs/conversion.md")]
69//! <div>
70#![doc = include_str!("../docs/diagrams/model_graph.svg")]
71//! </div>
72//!
73//! # Other Utilities
74//!
75//! The crate also provides a number of color operations, such as blending, color difference,
76//! and color range operations.
77//!
78//! In addition, there is a [`Color`] enum that can represent any of the color
79//! types in this crate. This is useful when you need to store a color in a data structure
80//! that can't be generic over the color type.
81//!
82//! Color types that are either physically or perceptually linear also implement `Add<Self>`, `Sub<Self>`, `Mul<f32>` and `Div<f32>`
83//! allowing you to use them with splines.
84//!
85//! Please note that most often adding or subtracting colors is not what you may want.
86//! Please have a look at other operations like blending, lightening or mixing colors using e.g. [`Mix`] or [`Luminance`] instead.
87//!
88//! # Example
89//!
90//! ```
91//! use bevy_color::{Srgba, Hsla};
92//!
93//! let srgba = Srgba::new(0.5, 0.2, 0.8, 1.0);
94//! let hsla: Hsla = srgba.into();
95//!
96//! println!("Srgba: {:?}", srgba);
97//! println!("Hsla: {:?}", hsla);
98//! ```
99
100#[cfg(feature = "std")]
101extern crate std;
102
103#[cfg(feature = "alloc")]
104extern crate alloc;
105
106mod color;
107pub mod color_difference;
108#[cfg(feature = "alloc")]
109mod color_gradient;
110mod color_ops;
111mod color_range;
112mod hsla;
113mod hsva;
114mod hwba;
115mod interpolate;
116mod laba;
117mod lcha;
118mod linear_rgba;
119mod okcolor_convert;
120mod okhsla;
121mod okhsva;
122mod okhwba;
123mod oklaba;
124mod oklcha;
125pub mod palettes;
126mod primaries;
127mod srgba;
128#[cfg(test)]
129mod test_colors;
130#[cfg(test)]
131mod testing;
132mod xyza;
133
134/// The color prelude.
135///
136/// This includes the most common types in this crate, re-exported for your convenience.
137pub mod prelude {
138    pub use crate::{
139        color::*, color_ops::*, hsla::*, hsva::*, hwba::*, laba::*, lcha::*, linear_rgba::*,
140        okhsla::*, okhsva::*, okhwba::*, oklaba::*, oklcha::*, srgba::*, xyza::*,
141    };
142}
143
144pub use color::*;
145#[cfg(feature = "alloc")]
146pub use color_gradient::*;
147pub use color_ops::*;
148pub use color_range::*;
149pub use hsla::*;
150pub use hsva::*;
151pub use hwba::*;
152pub use laba::*;
153pub use lcha::*;
154pub use linear_rgba::*;
155pub use okhsla::*;
156pub use okhsva::*;
157pub use okhwba::*;
158pub use oklaba::*;
159pub use oklcha::*;
160pub use primaries::*;
161pub use srgba::*;
162pub use xyza::*;
163
164/// Describes the traits that a color should implement for consistency.
165#[expect(
166    clippy::allow_attributes,
167    reason = "If the below attribute on `dead_code` is removed, then rustc complains that `StandardColor` is dead code. However, if we `expect` the `dead_code` lint, then rustc complains of an unfulfilled expectation."
168)]
169#[allow(
170    dead_code,
171    reason = "This is an internal marker trait used to ensure that our color types impl the required traits"
172)]
173pub(crate) trait StandardColor
174where
175    Self: core::fmt::Debug,
176    Self: Clone + Copy,
177    Self: PartialEq,
178    Self: Default,
179    Self: From<Color> + Into<Color>,
180    Self: From<Srgba> + Into<Srgba>,
181    Self: From<LinearRgba> + Into<LinearRgba>,
182    Self: From<Hsla> + Into<Hsla>,
183    Self: From<Hsva> + Into<Hsva>,
184    Self: From<Hwba> + Into<Hwba>,
185    Self: From<Laba> + Into<Laba>,
186    Self: From<Lcha> + Into<Lcha>,
187    Self: From<Oklaba> + Into<Oklaba>,
188    Self: From<Oklcha> + Into<Oklcha>,
189    Self: From<Xyza> + Into<Xyza>,
190    Self: From<Okhsla> + Into<Okhsla>,
191    Self: From<Okhsva> + Into<Okhsva>,
192    Self: From<Okhwba> + Into<Okhwba>,
193    Self: Alpha,
194{
195}
196
197macro_rules! impl_componentwise_vector_space {
198    ($ty: ident, [$($element: ident),+]) => {
199        impl core::ops::Add<Self> for $ty {
200            type Output = Self;
201
202            fn add(self, rhs: Self) -> Self::Output {
203                Self::Output {
204                    $($element: self.$element + rhs.$element,)+
205                }
206            }
207        }
208
209        impl core::ops::AddAssign<Self> for $ty {
210            fn add_assign(&mut self, rhs: Self) {
211                *self = *self + rhs;
212            }
213        }
214
215        impl core::ops::Neg for $ty {
216            type Output = Self;
217
218            fn neg(self) -> Self::Output {
219                Self::Output {
220                    $($element: -self.$element,)+
221                }
222            }
223        }
224
225        impl core::ops::Sub<Self> for $ty {
226            type Output = Self;
227
228            fn sub(self, rhs: Self) -> Self::Output {
229                Self::Output {
230                    $($element: self.$element - rhs.$element,)+
231                }
232            }
233        }
234
235        impl core::ops::SubAssign<Self> for $ty {
236            fn sub_assign(&mut self, rhs: Self) {
237                *self = *self - rhs;
238            }
239        }
240
241        impl core::ops::Mul<f32> for $ty {
242            type Output = Self;
243
244            fn mul(self, rhs: f32) -> Self::Output {
245                Self::Output {
246                    $($element: self.$element * rhs,)+
247                }
248            }
249        }
250
251        impl core::ops::Mul<$ty> for f32 {
252            type Output = $ty;
253
254            fn mul(self, rhs: $ty) -> Self::Output {
255                Self::Output {
256                    $($element: self * rhs.$element,)+
257                }
258            }
259        }
260
261        impl core::ops::MulAssign<f32> for $ty {
262            fn mul_assign(&mut self, rhs: f32) {
263                *self = *self * rhs;
264            }
265        }
266
267        impl core::ops::Div<f32> for $ty {
268            type Output = Self;
269
270            fn div(self, rhs: f32) -> Self::Output {
271                Self::Output {
272                    $($element: self.$element / rhs,)+
273                }
274            }
275        }
276
277        impl core::ops::DivAssign<f32> for $ty {
278            fn div_assign(&mut self, rhs: f32) {
279                *self = *self / rhs;
280            }
281        }
282
283        impl bevy_math::VectorSpace for $ty {
284            type Scalar = f32;
285            const ZERO: Self = Self {
286                $($element: 0.0,)+
287            };
288        }
289
290        impl bevy_math::StableInterpolate for $ty {
291            fn interpolate_stable(&self, other: &Self, t: f32) -> Self {
292                bevy_math::VectorSpace::lerp(*self, *other, t)
293            }
294        }
295    };
296}
297
298pub(crate) use impl_componentwise_vector_space;