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