mirror of
https://github.com/bevyengine/bevy
synced 2024-11-25 14:10:19 +00:00
Adds back in way to convert color to u8 array, implemented for the two RGB color types, also renames Color::linear to Color::to_linear. (#13759)
# Objective One thing missing from the new Color implementation in 0.14 is the ability to easily convert to a u8 representation of the rgb color. (note this is a redo of PR https://github.com/bevyengine/bevy/pull/13739 as I needed to move the source branch ## Solution I have added to_u8_array and to_u8_array_no_alpha to a new trait called ColorToPacked to mirror the f32 conversions in ColorToComponents and implemented the new trait for Srgba and LinearRgba. To go with those I also added matching from_u8... functions and converted a couple of cases that used ad-hoc implementations of that conversion to use these. After discussion on Discord of the experience of using the API I renamed Color::linear to Color::to_linear, as without that it looks like a constructor (like Color::rgb). I also added to_srgba which is the other commonly converted to type of color (for UI and 2D) to match to_linear. Removed a redundant extra implementation of to_f32_array for LinearColor as it is also supplied in ColorToComponents (I'm surprised that's allowed?) ## Testing Ran all tests and manually tested. Added to_and_from_u8 to linear_rgba::tests ## Changelog visible change is Color::linear becomes Color::to_linear. --------- Co-authored-by: John Payne <20407779+johngpayne@users.noreply.github.com>
This commit is contained in:
parent
c50a4d8821
commit
298b01f10d
10 changed files with 100 additions and 45 deletions
|
@ -73,7 +73,12 @@ impl StandardColor for Color {}
|
||||||
|
|
||||||
impl Color {
|
impl Color {
|
||||||
/// Return the color as a linear RGBA color.
|
/// Return the color as a linear RGBA color.
|
||||||
pub fn linear(&self) -> LinearRgba {
|
pub fn to_linear(&self) -> LinearRgba {
|
||||||
|
(*self).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the color as an SRGBA color.
|
||||||
|
pub fn to_srgba(&self) -> Srgba {
|
||||||
(*self).into()
|
(*self).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -115,6 +115,18 @@ pub trait ColorToComponents {
|
||||||
fn from_vec3(color: Vec3) -> Self;
|
fn from_vec3(color: Vec3) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Trait with methods for converting colors to packed non-color types
|
||||||
|
pub trait ColorToPacked {
|
||||||
|
/// Convert to [u8; 4] where that makes sense (Srgba is most relevant)
|
||||||
|
fn to_u8_array(self) -> [u8; 4];
|
||||||
|
/// Convert to [u8; 3] where that makes sense (Srgba is most relevant)
|
||||||
|
fn to_u8_array_no_alpha(self) -> [u8; 3];
|
||||||
|
/// Convert from [u8; 4] where that makes sense (Srgba is most relevant)
|
||||||
|
fn from_u8_array(color: [u8; 4]) -> Self;
|
||||||
|
/// Convert to [u8; 3] where that makes sense (Srgba is most relevant)
|
||||||
|
fn from_u8_array_no_alpha(color: [u8; 3]) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
/// Utility function for interpolating hue values. This ensures that the interpolation
|
/// Utility function for interpolating hue values. This ensures that the interpolation
|
||||||
/// 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.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
color_difference::EuclideanDistance, impl_componentwise_vector_space, Alpha, ColorToComponents,
|
color_difference::EuclideanDistance, impl_componentwise_vector_space, Alpha, ColorToComponents,
|
||||||
Gray, Luminance, Mix, StandardColor,
|
ColorToPacked, Gray, Luminance, Mix, StandardColor,
|
||||||
};
|
};
|
||||||
use bevy_math::{Vec3, Vec4};
|
use bevy_math::{Vec3, Vec4};
|
||||||
use bevy_reflect::prelude::*;
|
use bevy_reflect::prelude::*;
|
||||||
|
@ -149,24 +149,12 @@ impl LinearRgba {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts the color into a [f32; 4] array in RGBA order.
|
|
||||||
///
|
|
||||||
/// This is useful for passing the color to a shader.
|
|
||||||
pub fn to_f32_array(&self) -> [f32; 4] {
|
|
||||||
[self.red, self.green, self.blue, self.alpha]
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Converts this color to a u32.
|
/// Converts this color to a u32.
|
||||||
///
|
///
|
||||||
/// Maps the RGBA channels in RGBA order to a little-endian byte array (GPUs are little-endian).
|
/// Maps the RGBA channels in RGBA order to a little-endian byte array (GPUs are little-endian).
|
||||||
/// `A` will be the most significant byte and `R` the least significant.
|
/// `A` will be the most significant byte and `R` the least significant.
|
||||||
pub fn as_u32(&self) -> u32 {
|
pub fn as_u32(&self) -> u32 {
|
||||||
u32::from_le_bytes([
|
u32::from_le_bytes(self.to_u8_array())
|
||||||
(self.red * 255.0) as u8,
|
|
||||||
(self.green * 255.0) as u8,
|
|
||||||
(self.blue * 255.0) as u8,
|
|
||||||
(self.alpha * 255.0) as u8,
|
|
||||||
])
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -310,6 +298,25 @@ impl ColorToComponents for LinearRgba {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ColorToPacked for LinearRgba {
|
||||||
|
fn to_u8_array(self) -> [u8; 4] {
|
||||||
|
[self.red, self.green, self.blue, self.alpha]
|
||||||
|
.map(|v| (v.clamp(0.0, 1.0) * 255.0).round() as u8)
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_u8_array(color: [u8; 4]) -> Self {
|
||||||
|
Self::from_f32_array(color.map(|u| u as f32 / 255.0))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_u8_array_no_alpha(color: [u8; 3]) -> Self {
|
||||||
|
Self::from_f32_array_no_alpha(color.map(|u| u as f32 / 255.0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "wgpu-types")]
|
#[cfg(feature = "wgpu-types")]
|
||||||
impl From<LinearRgba> for wgpu_types::Color {
|
impl From<LinearRgba> for wgpu_types::Color {
|
||||||
fn from(color: LinearRgba) -> Self {
|
fn from(color: LinearRgba) -> Self {
|
||||||
|
@ -416,6 +423,34 @@ mod tests {
|
||||||
assert_eq!(a.distance_squared(&b), 1.0);
|
assert_eq!(a.distance_squared(&b), 1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn to_and_from_u8() {
|
||||||
|
// from_u8_array
|
||||||
|
let a = LinearRgba::from_u8_array([255, 0, 0, 255]);
|
||||||
|
let b = LinearRgba::new(1.0, 0.0, 0.0, 1.0);
|
||||||
|
assert_eq!(a, b);
|
||||||
|
|
||||||
|
// from_u8_array_no_alpha
|
||||||
|
let a = LinearRgba::from_u8_array_no_alpha([255, 255, 0]);
|
||||||
|
let b = LinearRgba::rgb(1.0, 1.0, 0.0);
|
||||||
|
assert_eq!(a, b);
|
||||||
|
|
||||||
|
// to_u8_array
|
||||||
|
let a = LinearRgba::new(0.0, 0.0, 1.0, 1.0).to_u8_array();
|
||||||
|
let b = [0, 0, 255, 255];
|
||||||
|
assert_eq!(a, b);
|
||||||
|
|
||||||
|
// to_u8_array_no_alpha
|
||||||
|
let a = LinearRgba::rgb(0.0, 1.0, 1.0).to_u8_array_no_alpha();
|
||||||
|
let b = [0, 255, 255];
|
||||||
|
assert_eq!(a, b);
|
||||||
|
|
||||||
|
// clamping
|
||||||
|
let a = LinearRgba::rgb(0.0, 100.0, -100.0).to_u8_array_no_alpha();
|
||||||
|
let b = [0, 255, 0];
|
||||||
|
assert_eq!(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn darker_lighter() {
|
fn darker_lighter() {
|
||||||
// Darker and lighter should be commutative.
|
// Darker and lighter should be commutative.
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::color_difference::EuclideanDistance;
|
use crate::color_difference::EuclideanDistance;
|
||||||
use crate::{
|
use crate::{
|
||||||
impl_componentwise_vector_space, Alpha, ColorToComponents, Gray, LinearRgba, Luminance, Mix,
|
impl_componentwise_vector_space, Alpha, ColorToComponents, ColorToPacked, Gray, LinearRgba,
|
||||||
StandardColor, Xyza,
|
Luminance, Mix, StandardColor, Xyza,
|
||||||
};
|
};
|
||||||
use bevy_math::{Vec3, Vec4};
|
use bevy_math::{Vec3, Vec4};
|
||||||
use bevy_reflect::prelude::*;
|
use bevy_reflect::prelude::*;
|
||||||
|
@ -168,10 +168,7 @@ impl Srgba {
|
||||||
|
|
||||||
/// Convert this color to CSS-style hexadecimal notation.
|
/// Convert this color to CSS-style hexadecimal notation.
|
||||||
pub fn to_hex(&self) -> String {
|
pub fn to_hex(&self) -> String {
|
||||||
let r = (self.red * 255.0).round() as u8;
|
let [r, g, b, a] = self.to_u8_array();
|
||||||
let g = (self.green * 255.0).round() as u8;
|
|
||||||
let b = (self.blue * 255.0).round() as u8;
|
|
||||||
let a = (self.alpha * 255.0).round() as u8;
|
|
||||||
match a {
|
match a {
|
||||||
255 => format!("#{:02X}{:02X}{:02X}", r, g, b),
|
255 => format!("#{:02X}{:02X}{:02X}", r, g, b),
|
||||||
_ => format!("#{:02X}{:02X}{:02X}{:02X}", r, g, b, a),
|
_ => format!("#{:02X}{:02X}{:02X}{:02X}", r, g, b, a),
|
||||||
|
@ -189,7 +186,7 @@ impl Srgba {
|
||||||
/// See also [`Srgba::new`], [`Srgba::rgba_u8`], [`Srgba::hex`].
|
/// See also [`Srgba::new`], [`Srgba::rgba_u8`], [`Srgba::hex`].
|
||||||
///
|
///
|
||||||
pub fn rgb_u8(r: u8, g: u8, b: u8) -> Self {
|
pub fn rgb_u8(r: u8, g: u8, b: u8) -> Self {
|
||||||
Self::rgba_u8(r, g, b, u8::MAX)
|
Self::from_u8_array_no_alpha([r, g, b])
|
||||||
}
|
}
|
||||||
|
|
||||||
// Float operations in const fn are not stable yet
|
// Float operations in const fn are not stable yet
|
||||||
|
@ -206,12 +203,7 @@ impl Srgba {
|
||||||
/// See also [`Srgba::new`], [`Srgba::rgb_u8`], [`Srgba::hex`].
|
/// See also [`Srgba::new`], [`Srgba::rgb_u8`], [`Srgba::hex`].
|
||||||
///
|
///
|
||||||
pub fn rgba_u8(r: u8, g: u8, b: u8, a: u8) -> Self {
|
pub fn rgba_u8(r: u8, g: u8, b: u8, a: u8) -> Self {
|
||||||
Self::new(
|
Self::from_u8_array([r, g, b, a])
|
||||||
r as f32 / u8::MAX as f32,
|
|
||||||
g as f32 / u8::MAX as f32,
|
|
||||||
b as f32 / u8::MAX as f32,
|
|
||||||
a as f32 / u8::MAX as f32,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts a non-linear sRGB value to a linear one via [gamma correction](https://en.wikipedia.org/wiki/Gamma_correction).
|
/// Converts a non-linear sRGB value to a linear one via [gamma correction](https://en.wikipedia.org/wiki/Gamma_correction).
|
||||||
|
@ -373,6 +365,25 @@ impl ColorToComponents for Srgba {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ColorToPacked for Srgba {
|
||||||
|
fn to_u8_array(self) -> [u8; 4] {
|
||||||
|
[self.red, self.green, self.blue, self.alpha]
|
||||||
|
.map(|v| (v.clamp(0.0, 1.0) * 255.0).round() as u8)
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_u8_array(color: [u8; 4]) -> Self {
|
||||||
|
Self::from_f32_array(color.map(|u| u as f32 / 255.0))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_u8_array_no_alpha(color: [u8; 3]) -> Self {
|
||||||
|
Self::from_f32_array_no_alpha(color.map(|u| u as f32 / 255.0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<LinearRgba> for Srgba {
|
impl From<LinearRgba> for Srgba {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(value: LinearRgba) -> Self {
|
fn from(value: LinearRgba) -> Self {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use bevy_color::{Color, LinearRgba};
|
use bevy_color::{Color, ColorToComponents, LinearRgba};
|
||||||
use bevy_ecs::prelude::*;
|
use bevy_ecs::prelude::*;
|
||||||
use bevy_math::Vec3;
|
use bevy_math::Vec3;
|
||||||
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
|
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use bevy_asset::AssetId;
|
use bevy_asset::AssetId;
|
||||||
|
use bevy_color::ColorToComponents;
|
||||||
use bevy_core_pipeline::core_3d::CORE_3D_DEPTH_FORMAT;
|
use bevy_core_pipeline::core_3d::CORE_3D_DEPTH_FORMAT;
|
||||||
use bevy_ecs::entity::EntityHashSet;
|
use bevy_ecs::entity::EntityHashSet;
|
||||||
use bevy_ecs::prelude::*;
|
use bevy_ecs::prelude::*;
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
|
|
||||||
use bevy_app::{App, Plugin};
|
use bevy_app::{App, Plugin};
|
||||||
use bevy_asset::{load_internal_asset, Handle};
|
use bevy_asset::{load_internal_asset, Handle};
|
||||||
use bevy_color::Color;
|
use bevy_color::{Color, ColorToComponents};
|
||||||
use bevy_core_pipeline::{
|
use bevy_core_pipeline::{
|
||||||
core_3d::{
|
core_3d::{
|
||||||
graph::{Core3d, Node3d},
|
graph::{Core3d, Node3d},
|
||||||
|
@ -617,18 +617,9 @@ pub fn prepare_volumetric_fog_uniforms(
|
||||||
|
|
||||||
for (entity, volumetric_fog_settings) in view_targets.iter() {
|
for (entity, volumetric_fog_settings) in view_targets.iter() {
|
||||||
let offset = writer.write(&VolumetricFogUniform {
|
let offset = writer.write(&VolumetricFogUniform {
|
||||||
fog_color: Vec3::from_slice(
|
fog_color: volumetric_fog_settings.fog_color.to_linear().to_vec3(),
|
||||||
&volumetric_fog_settings.fog_color.linear().to_f32_array()[0..3],
|
light_tint: volumetric_fog_settings.light_tint.to_linear().to_vec3(),
|
||||||
),
|
ambient_color: volumetric_fog_settings.ambient_color.to_linear().to_vec3(),
|
||||||
light_tint: Vec3::from_slice(
|
|
||||||
&volumetric_fog_settings.light_tint.linear().to_f32_array()[0..3],
|
|
||||||
),
|
|
||||||
ambient_color: Vec3::from_slice(
|
|
||||||
&volumetric_fog_settings
|
|
||||||
.ambient_color
|
|
||||||
.linear()
|
|
||||||
.to_f32_array()[0..3],
|
|
||||||
),
|
|
||||||
ambient_intensity: volumetric_fog_settings.ambient_intensity,
|
ambient_intensity: volumetric_fog_settings.ambient_intensity,
|
||||||
step_count: volumetric_fog_settings.step_count,
|
step_count: volumetric_fog_settings.step_count,
|
||||||
max_depth: volumetric_fog_settings.max_depth,
|
max_depth: volumetric_fog_settings.max_depth,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::{Material2d, Material2dPlugin, MaterialMesh2dBundle};
|
use crate::{Material2d, Material2dPlugin, MaterialMesh2dBundle};
|
||||||
use bevy_app::{App, Plugin};
|
use bevy_app::{App, Plugin};
|
||||||
use bevy_asset::{load_internal_asset, Asset, AssetApp, Assets, Handle};
|
use bevy_asset::{load_internal_asset, Asset, AssetApp, Assets, Handle};
|
||||||
use bevy_color::{Color, LinearRgba};
|
use bevy_color::{Color, ColorToComponents, LinearRgba};
|
||||||
use bevy_math::Vec4;
|
use bevy_math::Vec4;
|
||||||
use bevy_reflect::prelude::*;
|
use bevy_reflect::prelude::*;
|
||||||
use bevy_render::{
|
use bevy_render::{
|
||||||
|
|
|
@ -5,7 +5,7 @@ use crate::{
|
||||||
ComputedTextureSlices, Sprite, WithSprite, SPRITE_SHADER_HANDLE,
|
ComputedTextureSlices, Sprite, WithSprite, SPRITE_SHADER_HANDLE,
|
||||||
};
|
};
|
||||||
use bevy_asset::{AssetEvent, AssetId, Assets, Handle};
|
use bevy_asset::{AssetEvent, AssetId, Assets, Handle};
|
||||||
use bevy_color::LinearRgba;
|
use bevy_color::{ColorToComponents, LinearRgba};
|
||||||
use bevy_core_pipeline::{
|
use bevy_core_pipeline::{
|
||||||
core_2d::Transparent2d,
|
core_2d::Transparent2d,
|
||||||
tonemapping::{
|
tonemapping::{
|
||||||
|
|
|
@ -2,7 +2,7 @@ mod pipeline;
|
||||||
mod render_pass;
|
mod render_pass;
|
||||||
mod ui_material_pipeline;
|
mod ui_material_pipeline;
|
||||||
|
|
||||||
use bevy_color::{Alpha, LinearRgba};
|
use bevy_color::{Alpha, ColorToComponents, LinearRgba};
|
||||||
use bevy_core_pipeline::core_2d::graph::{Core2d, Node2d};
|
use bevy_core_pipeline::core_2d::graph::{Core2d, Node2d};
|
||||||
use bevy_core_pipeline::core_3d::graph::{Core3d, Node3d};
|
use bevy_core_pipeline::core_3d::graph::{Core3d, Node3d};
|
||||||
use bevy_core_pipeline::{core_2d::Camera2d, core_3d::Camera3d};
|
use bevy_core_pipeline::{core_2d::Camera2d, core_3d::Camera3d};
|
||||||
|
|
Loading…
Reference in a new issue