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
					
				@ -73,7 +73,12 @@ impl StandardColor for Color {}
 | 
			
		||||
 | 
			
		||||
impl 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()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -115,6 +115,18 @@ pub trait ColorToComponents {
 | 
			
		||||
    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
 | 
			
		||||
/// takes the shortest path around the color wheel, and that the result is always between
 | 
			
		||||
/// 0 and 360.
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
use crate::{
 | 
			
		||||
    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_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.
 | 
			
		||||
    ///
 | 
			
		||||
    /// 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.
 | 
			
		||||
    pub fn as_u32(&self) -> u32 {
 | 
			
		||||
        u32::from_le_bytes([
 | 
			
		||||
            (self.red * 255.0) as u8,
 | 
			
		||||
            (self.green * 255.0) as u8,
 | 
			
		||||
            (self.blue * 255.0) as u8,
 | 
			
		||||
            (self.alpha * 255.0) as u8,
 | 
			
		||||
        ])
 | 
			
		||||
        u32::from_le_bytes(self.to_u8_array())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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")]
 | 
			
		||||
impl From<LinearRgba> for wgpu_types::Color {
 | 
			
		||||
    fn from(color: LinearRgba) -> Self {
 | 
			
		||||
@ -416,6 +423,34 @@ mod tests {
 | 
			
		||||
        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]
 | 
			
		||||
    fn darker_lighter() {
 | 
			
		||||
        // Darker and lighter should be commutative.
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
use crate::color_difference::EuclideanDistance;
 | 
			
		||||
use crate::{
 | 
			
		||||
    impl_componentwise_vector_space, Alpha, ColorToComponents, Gray, LinearRgba, Luminance, Mix,
 | 
			
		||||
    StandardColor, Xyza,
 | 
			
		||||
    impl_componentwise_vector_space, Alpha, ColorToComponents, ColorToPacked, Gray, LinearRgba,
 | 
			
		||||
    Luminance, Mix, StandardColor, Xyza,
 | 
			
		||||
};
 | 
			
		||||
use bevy_math::{Vec3, Vec4};
 | 
			
		||||
use bevy_reflect::prelude::*;
 | 
			
		||||
@ -168,10 +168,7 @@ impl Srgba {
 | 
			
		||||
 | 
			
		||||
    /// Convert this color to CSS-style hexadecimal notation.
 | 
			
		||||
    pub fn to_hex(&self) -> String {
 | 
			
		||||
        let r = (self.red * 255.0).round() as u8;
 | 
			
		||||
        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;
 | 
			
		||||
        let [r, g, b, a] = self.to_u8_array();
 | 
			
		||||
        match a {
 | 
			
		||||
            255 => format!("#{:02X}{:02X}{:02X}", r, g, b),
 | 
			
		||||
            _ => format!("#{:02X}{:02X}{:02X}{:02X}", r, g, b, a),
 | 
			
		||||
@ -189,7 +186,7 @@ impl Srgba {
 | 
			
		||||
    /// See also [`Srgba::new`], [`Srgba::rgba_u8`], [`Srgba::hex`].
 | 
			
		||||
    ///
 | 
			
		||||
    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
 | 
			
		||||
@ -206,12 +203,7 @@ impl Srgba {
 | 
			
		||||
    /// See also [`Srgba::new`], [`Srgba::rgb_u8`], [`Srgba::hex`].
 | 
			
		||||
    ///
 | 
			
		||||
    pub fn rgba_u8(r: u8, g: u8, b: u8, a: u8) -> Self {
 | 
			
		||||
        Self::new(
 | 
			
		||||
            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,
 | 
			
		||||
        )
 | 
			
		||||
        Self::from_u8_array([r, g, b, a])
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// 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 {
 | 
			
		||||
    #[inline]
 | 
			
		||||
    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_math::Vec3;
 | 
			
		||||
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,5 @@
 | 
			
		||||
use bevy_asset::AssetId;
 | 
			
		||||
use bevy_color::ColorToComponents;
 | 
			
		||||
use bevy_core_pipeline::core_3d::CORE_3D_DEPTH_FORMAT;
 | 
			
		||||
use bevy_ecs::entity::EntityHashSet;
 | 
			
		||||
use bevy_ecs::prelude::*;
 | 
			
		||||
 | 
			
		||||
@ -31,7 +31,7 @@
 | 
			
		||||
 | 
			
		||||
use bevy_app::{App, Plugin};
 | 
			
		||||
use bevy_asset::{load_internal_asset, Handle};
 | 
			
		||||
use bevy_color::Color;
 | 
			
		||||
use bevy_color::{Color, ColorToComponents};
 | 
			
		||||
use bevy_core_pipeline::{
 | 
			
		||||
    core_3d::{
 | 
			
		||||
        graph::{Core3d, Node3d},
 | 
			
		||||
@ -617,18 +617,9 @@ pub fn prepare_volumetric_fog_uniforms(
 | 
			
		||||
 | 
			
		||||
    for (entity, volumetric_fog_settings) in view_targets.iter() {
 | 
			
		||||
        let offset = writer.write(&VolumetricFogUniform {
 | 
			
		||||
            fog_color: Vec3::from_slice(
 | 
			
		||||
                &volumetric_fog_settings.fog_color.linear().to_f32_array()[0..3],
 | 
			
		||||
            ),
 | 
			
		||||
            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],
 | 
			
		||||
            ),
 | 
			
		||||
            fog_color: volumetric_fog_settings.fog_color.to_linear().to_vec3(),
 | 
			
		||||
            light_tint: volumetric_fog_settings.light_tint.to_linear().to_vec3(),
 | 
			
		||||
            ambient_color: volumetric_fog_settings.ambient_color.to_linear().to_vec3(),
 | 
			
		||||
            ambient_intensity: volumetric_fog_settings.ambient_intensity,
 | 
			
		||||
            step_count: volumetric_fog_settings.step_count,
 | 
			
		||||
            max_depth: volumetric_fog_settings.max_depth,
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
use crate::{Material2d, Material2dPlugin, MaterialMesh2dBundle};
 | 
			
		||||
use bevy_app::{App, Plugin};
 | 
			
		||||
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_reflect::prelude::*;
 | 
			
		||||
use bevy_render::{
 | 
			
		||||
 | 
			
		||||
@ -5,7 +5,7 @@ use crate::{
 | 
			
		||||
    ComputedTextureSlices, Sprite, WithSprite, SPRITE_SHADER_HANDLE,
 | 
			
		||||
};
 | 
			
		||||
use bevy_asset::{AssetEvent, AssetId, Assets, Handle};
 | 
			
		||||
use bevy_color::LinearRgba;
 | 
			
		||||
use bevy_color::{ColorToComponents, LinearRgba};
 | 
			
		||||
use bevy_core_pipeline::{
 | 
			
		||||
    core_2d::Transparent2d,
 | 
			
		||||
    tonemapping::{
 | 
			
		||||
 | 
			
		||||
@ -2,7 +2,7 @@ mod pipeline;
 | 
			
		||||
mod render_pass;
 | 
			
		||||
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_3d::graph::{Core3d, Node3d};
 | 
			
		||||
use bevy_core_pipeline::{core_2d::Camera2d, core_3d::Camera3d};
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user