Add no_std support to bevy_color (#16633)

# Objective

- Contributes to #15460

## Solution

- Added the following new features: 
  - `std` (default)
  - `alloc`
  - `encase` (default)
  - `libm`

## Testing

- Added to `compile-check-no-std` CI command

## Notes

- `ColorCurve` requires `alloc` due to how the underlying `EvenCore`
type works.
- `Srgba::to_hex` requires `alloc` to return a `String`.
- This was otherwise a _very_ simple change
This commit is contained in:
Zachary Harrold 2024-12-06 08:21:45 +11:00 committed by GitHub
parent 4be75305f2
commit 73c6479f65
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 48 additions and 15 deletions

View file

@ -17,18 +17,26 @@ bevy_reflect = { path = "../bevy_reflect", version = "0.15.0-dev", features = [
"bevy", "bevy",
], optional = true } ], optional = true }
bytemuck = { version = "1", features = ["derive"] } bytemuck = { version = "1", features = ["derive"] }
serde = { version = "1.0", features = ["derive"], optional = true } serde = { version = "1.0", features = [
"derive",
], default-features = false, optional = true }
derive_more = { version = "1", default-features = false, features = [ derive_more = { version = "1", default-features = false, features = [
"error", "error",
"from", "from",
"display", "display",
] } ] }
wgpu-types = { version = "23", default-features = false, optional = true } wgpu-types = { version = "23", default-features = false, optional = true }
encase = { version = "0.10", default-features = false } encase = { version = "0.10", default-features = false, optional = true }
[features] [features]
default = ["bevy_reflect"] default = ["std", "bevy_reflect", "encase"]
std = ["alloc", "bevy_math/std", "serde?/std"]
alloc = ["bevy_math/alloc", "serde?/alloc"]
serialize = ["serde", "bevy_math/serialize"] serialize = ["serde", "bevy_math/serialize"]
bevy_reflect = ["dep:bevy_reflect", "std"]
wgpu-types = ["dep:wgpu-types", "std"]
encase = ["dep:encase", "std"]
libm = ["bevy_math/libm"]
[lints] [lints]
workspace = true workspace = true

View file

@ -1,11 +1,13 @@
//! Module for calculating distance between two colors in the same color space. //! Module for calculating distance between two colors in the same color space.
use bevy_math::ops;
/// Calculate the distance between this and another color as if they were coordinates /// Calculate the distance between this and another color as if they were coordinates
/// in a Euclidean space. Alpha is not considered in the distance calculation. /// in a Euclidean space. Alpha is not considered in the distance calculation.
pub trait EuclideanDistance: Sized { pub trait EuclideanDistance: Sized {
/// Distance from `self` to `other`. /// Distance from `self` to `other`.
fn distance(&self, other: &Self) -> f32 { fn distance(&self, other: &Self) -> f32 {
self.distance_squared(other).sqrt() ops::sqrt(self.distance_squared(other))
} }
/// Distance squared from `self` to `other`. /// Distance squared from `self` to `other`.

View file

@ -1,4 +1,5 @@
use crate::Mix; use crate::Mix;
use alloc::vec::Vec;
use bevy_math::curve::{ use bevy_math::curve::{
cores::{EvenCore, EvenCoreError}, cores::{EvenCore, EvenCoreError},
Curve, Interval, Curve, Interval,

View file

@ -1,4 +1,4 @@
use bevy_math::{Vec3, Vec4}; use bevy_math::{ops, Vec3, Vec4};
/// Methods for changing the luminance of a color. Note that these methods are not /// Methods for changing the luminance of a color. Note that these methods are not
/// guaranteed to produce consistent results across color spaces, /// guaranteed to produce consistent results across color spaces,
@ -90,7 +90,7 @@ pub trait Hue: Sized {
/// Return a new version of this color with the hue channel rotated by the given degrees. /// Return a new version of this color with the hue channel rotated by the given degrees.
fn rotate_hue(&self, degrees: f32) -> Self { fn rotate_hue(&self, degrees: f32) -> Self {
let rotated_hue = (self.hue() + degrees).rem_euclid(360.); let rotated_hue = ops::rem_euclid(self.hue() + degrees, 360.);
self.with_hue(rotated_hue) self.with_hue(rotated_hue)
} }
} }
@ -131,8 +131,8 @@ pub trait ColorToPacked {
/// takes the shortest path around the color wheel, and that the result is always between /// takes the shortest path around the color wheel, and that the result is always between
/// 0 and 360. /// 0 and 360.
pub(crate) fn lerp_hue(a: f32, b: f32, t: f32) -> f32 { pub(crate) fn lerp_hue(a: f32, b: f32, t: f32) -> f32 {
let diff = (b - a + 180.0).rem_euclid(360.) - 180.; let diff = ops::rem_euclid(b - a + 180.0, 360.) - 180.;
(a + diff * t).rem_euclid(360.0) ops::rem_euclid(a + diff * t, 360.)
} }
#[cfg(test)] #[cfg(test)]

View file

@ -5,7 +5,7 @@
use crate::{ use crate::{
Alpha, ColorToComponents, Gray, Hue, Lcha, LinearRgba, Mix, Srgba, StandardColor, Xyza, Alpha, ColorToComponents, Gray, Hue, Lcha, LinearRgba, Mix, Srgba, StandardColor, Xyza,
}; };
use bevy_math::{Vec3, Vec4}; use bevy_math::{ops, Vec3, Vec4};
#[cfg(feature = "bevy_reflect")] #[cfg(feature = "bevy_reflect")]
use bevy_reflect::prelude::*; use bevy_reflect::prelude::*;
@ -239,7 +239,7 @@ impl From<Hwba> for Srgba {
let v = 1. - blackness; let v = 1. - blackness;
let h = (hue % 360.) / 60.; let h = (hue % 360.) / 60.;
let i = h.floor(); let i = ops::floor(h);
let f = h - i; let f = h - i;
let i = i as u8; let i = i as u8;

View file

@ -4,6 +4,7 @@
html_logo_url = "https://bevyengine.org/assets/icon.png", html_logo_url = "https://bevyengine.org/assets/icon.png",
html_favicon_url = "https://bevyengine.org/assets/icon.png" html_favicon_url = "https://bevyengine.org/assets/icon.png"
)] )]
#![cfg_attr(not(feature = "std"), no_std)]
//! Representations of colors in various color spaces. //! Representations of colors in various color spaces.
//! //!
@ -89,8 +90,12 @@
//! println!("Hsla: {:?}", hsla); //! println!("Hsla: {:?}", hsla);
//! ``` //! ```
#[cfg(feature = "alloc")]
extern crate alloc;
mod color; mod color;
pub mod color_difference; pub mod color_difference;
#[cfg(feature = "alloc")]
mod color_gradient; mod color_gradient;
mod color_ops; mod color_ops;
mod color_range; mod color_range;
@ -121,6 +126,7 @@ pub mod prelude {
} }
pub use color::*; pub use color::*;
#[cfg(feature = "alloc")]
pub use color_gradient::*; pub use color_gradient::*;
pub use color_ops::*; pub use color_ops::*;
pub use color_range::*; pub use color_range::*;

View file

@ -2,7 +2,7 @@ use crate::{
color_difference::EuclideanDistance, impl_componentwise_vector_space, Alpha, ColorToComponents, color_difference::EuclideanDistance, impl_componentwise_vector_space, Alpha, ColorToComponents,
ColorToPacked, Gray, Luminance, Mix, StandardColor, ColorToPacked, Gray, Luminance, Mix, StandardColor,
}; };
use bevy_math::{Vec3, Vec4}; use bevy_math::{ops, Vec3, Vec4};
#[cfg(feature = "bevy_reflect")] #[cfg(feature = "bevy_reflect")]
use bevy_reflect::prelude::*; use bevy_reflect::prelude::*;
use bytemuck::{Pod, Zeroable}; use bytemuck::{Pod, Zeroable};
@ -302,11 +302,11 @@ impl ColorToComponents for LinearRgba {
impl ColorToPacked for LinearRgba { impl ColorToPacked for LinearRgba {
fn to_u8_array(self) -> [u8; 4] { fn to_u8_array(self) -> [u8; 4] {
[self.red, self.green, self.blue, self.alpha] [self.red, self.green, self.blue, self.alpha]
.map(|v| (v.clamp(0.0, 1.0) * 255.0).round() as u8) .map(|v| ops::round(v.clamp(0.0, 1.0) * 255.0) as u8)
} }
fn to_u8_array_no_alpha(self) -> [u8; 3] { fn to_u8_array_no_alpha(self) -> [u8; 3] {
[self.red, self.green, self.blue].map(|v| (v.clamp(0.0, 1.0) * 255.0).round() as u8) [self.red, self.green, self.blue].map(|v| ops::round(v.clamp(0.0, 1.0) * 255.0) as u8)
} }
fn from_u8_array(color: [u8; 4]) -> Self { fn from_u8_array(color: [u8; 4]) -> Self {
@ -332,6 +332,7 @@ impl From<LinearRgba> for wgpu_types::Color {
// [`LinearRgba`] is intended to be used with shaders // [`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 // So it's the only color type that implements [`ShaderType`] to make it easier to use inside shaders
#[cfg(feature = "encase")]
impl encase::ShaderType for LinearRgba { impl encase::ShaderType for LinearRgba {
type ExtraMetadata = (); type ExtraMetadata = ();
@ -353,6 +354,7 @@ impl encase::ShaderType for LinearRgba {
const UNIFORM_COMPAT_ASSERT: fn() = || {}; const UNIFORM_COMPAT_ASSERT: fn() = || {};
} }
#[cfg(feature = "encase")]
impl encase::private::WriteInto for LinearRgba { impl encase::private::WriteInto for LinearRgba {
fn write_into<B: encase::private::BufferMut>(&self, writer: &mut encase::private::Writer<B>) { fn write_into<B: encase::private::BufferMut>(&self, writer: &mut encase::private::Writer<B>) {
for el in &[self.red, self.green, self.blue, self.alpha] { for el in &[self.red, self.green, self.blue, self.alpha] {
@ -361,6 +363,7 @@ impl encase::private::WriteInto for LinearRgba {
} }
} }
#[cfg(feature = "encase")]
impl encase::private::ReadFrom for LinearRgba { impl encase::private::ReadFrom for LinearRgba {
fn read_from<B: encase::private::BufferRef>( fn read_from<B: encase::private::BufferRef>(
&mut self, &mut self,
@ -380,6 +383,7 @@ impl encase::private::ReadFrom for LinearRgba {
} }
} }
#[cfg(feature = "encase")]
impl encase::private::CreateFrom for LinearRgba { impl encase::private::CreateFrom for LinearRgba {
fn create_from<B>(reader: &mut encase::private::Reader<B>) -> Self fn create_from<B>(reader: &mut encase::private::Reader<B>) -> Self
where where
@ -400,6 +404,7 @@ impl encase::private::CreateFrom for LinearRgba {
} }
} }
#[cfg(feature = "encase")]
impl encase::ShaderSize for LinearRgba {} impl encase::ShaderSize for LinearRgba {}
#[cfg(test)] #[cfg(test)]

View file

@ -2,6 +2,8 @@ use crate::{
color_difference::EuclideanDistance, impl_componentwise_vector_space, Alpha, ColorToComponents, color_difference::EuclideanDistance, impl_componentwise_vector_space, Alpha, ColorToComponents,
ColorToPacked, Gray, LinearRgba, Luminance, Mix, StandardColor, Xyza, ColorToPacked, Gray, LinearRgba, Luminance, Mix, StandardColor, Xyza,
}; };
#[cfg(feature = "alloc")]
use alloc::{format, string::String};
use bevy_math::{ops, Vec3, Vec4}; use bevy_math::{ops, Vec3, Vec4};
#[cfg(feature = "bevy_reflect")] #[cfg(feature = "bevy_reflect")]
use bevy_reflect::prelude::*; use bevy_reflect::prelude::*;
@ -167,6 +169,7 @@ impl Srgba {
} }
/// Convert this color to CSS-style hexadecimal notation. /// Convert this color to CSS-style hexadecimal notation.
#[cfg(feature = "alloc")]
pub fn to_hex(&self) -> String { pub fn to_hex(&self) -> String {
let [r, g, b, a] = self.to_u8_array(); let [r, g, b, a] = self.to_u8_array();
match a { match a {
@ -366,11 +369,11 @@ impl ColorToComponents for Srgba {
impl ColorToPacked for Srgba { impl ColorToPacked for Srgba {
fn to_u8_array(self) -> [u8; 4] { fn to_u8_array(self) -> [u8; 4] {
[self.red, self.green, self.blue, self.alpha] [self.red, self.green, self.blue, self.alpha]
.map(|v| (v.clamp(0.0, 1.0) * 255.0).round() as u8) .map(|v| ops::round(v.clamp(0.0, 1.0) * 255.0) as u8)
} }
fn to_u8_array_no_alpha(self) -> [u8; 3] { fn to_u8_array_no_alpha(self) -> [u8; 3] {
[self.red, self.green, self.blue].map(|v| (v.clamp(0.0, 1.0) * 255.0).round() as u8) [self.red, self.green, self.blue].map(|v| ops::round(v.clamp(0.0, 1.0) * 255.0) as u8)
} }
fn from_u8_array(color: [u8; 4]) -> Self { fn from_u8_array(color: [u8; 4]) -> Self {

View file

@ -86,6 +86,14 @@ impl Prepare for CompileCheckNoStdCommand {
"Please fix compiler errors in output above for bevy_math no_std compatibility.", "Please fix compiler errors in output above for bevy_math no_std compatibility.",
)); ));
commands.push(PreparedCommand::new::<Self>(
cmd!(
sh,
"cargo check -p bevy_color --no-default-features --features libm --target {target}"
),
"Please fix compiler errors in output above for bevy_color no_std compatibility.",
));
commands commands
} }
} }