use super::texture::Texture;
use crate::{
colorspace::*,
impl_render_resource_bytes,
renderer::{RenderResource, RenderResourceType},
};
use bevy_asset::Handle;
use bevy_core::{Byteable, Bytes};
use bevy_math::{Vec3, Vec4};
use bevy_property::Property;
use serde::{Deserialize, Serialize};
use std::ops::{Add, AddAssign, Mul, MulAssign};
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Property)]
pub struct Color {
red: f32,
green: f32,
blue: f32,
alpha: f32,
}
unsafe impl Byteable for Color {}
impl Color {
pub const BLACK: Color = Color::rgb_linear(0.0, 0.0, 0.0);
pub const BLUE: Color = Color::rgb_linear(0.0, 0.0, 1.0);
pub const GREEN: Color = Color::rgb_linear(0.0, 1.0, 0.0);
pub const NONE: Color = Color::rgba_linear(0.0, 0.0, 0.0, 0.0);
pub const PINK: Color = Color::rgb_linear(1.0, 0.08, 0.58);
pub const RED: Color = Color::rgb_linear(1.0, 0.0, 0.0);
pub const WHITE: Color = Color::rgb_linear(1.0, 1.0, 1.0);
pub fn rgb(r: f32, g: f32, b: f32) -> Color {
Color {
red: r,
green: g,
blue: b,
alpha: 1.0,
}
.as_nonlinear_srgb_to_linear_srgb()
}
pub fn rgba(r: f32, g: f32, b: f32, a: f32) -> Color {
Color {
red: r,
green: g,
blue: b,
alpha: a,
}
.as_nonlinear_srgb_to_linear_srgb()
}
pub const fn rgb_linear(r: f32, g: f32, b: f32) -> Color {
Color {
red: r,
green: g,
blue: b,
alpha: 1.0,
}
}
pub const fn rgba_linear(r: f32, g: f32, b: f32, a: f32) -> Color {
Color {
red: r,
green: g,
blue: b,
alpha: a,
}
}
pub fn hex<T: AsRef<str>>(hex: T) -> Result<Color, HexColorError> {
let hex = hex.as_ref();
if hex.len() == 3 {
let mut data = [0; 6];
for (i, ch) in hex.chars().enumerate() {
data[i * 2] = ch as u8;
data[i * 2 + 1] = ch as u8;
}
return decode_rgb(&data);
}
if hex.len() == 4 {
let mut data = [0; 8];
for (i, ch) in hex.chars().enumerate() {
data[i * 2] = ch as u8;
data[i * 2 + 1] = ch as u8;
}
return decode_rgba(&data);
}
if hex.len() == 6 {
return decode_rgb(hex.as_bytes());
}
if hex.len() == 8 {
return decode_rgba(hex.as_bytes());
}
Err(HexColorError::Length)
}
pub fn rgb_u8(r: u8, g: u8, b: u8) -> Color {
Color::rgba_u8(r, g, b, u8::MAX)
}
pub fn rgba_u8(r: u8, g: u8, b: u8, a: u8) -> Color {
Color::rgba(
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,
)
}
fn as_nonlinear_srgb_to_linear_srgb(self) -> Color {
Color {
red: self.red.nonlinear_to_linear_srgb(),
green: self.green.nonlinear_to_linear_srgb(),
blue: self.blue.nonlinear_to_linear_srgb(),
alpha: self.alpha,
}
}
pub fn r(&self) -> f32 {
self.red.linear_to_nonlinear_srgb()
}
pub fn g(&self) -> f32 {
self.green.linear_to_nonlinear_srgb()
}
pub fn b(&self) -> f32 {
self.blue.linear_to_nonlinear_srgb()
}
pub fn r_linear(&self) -> f32 {
self.red
}
pub fn g_linear(&self) -> f32 {
self.green
}
pub fn b_linear(&self) -> f32 {
self.blue
}
pub fn a(&self) -> f32 {
self.alpha
}
pub fn set_r(&mut self, r: f32) -> &mut Self {
self.red = r.nonlinear_to_linear_srgb();
self
}
pub fn set_g(&mut self, g: f32) -> &mut Self {
self.green = g.nonlinear_to_linear_srgb();
self
}
pub fn set_b(&mut self, b: f32) -> &mut Self {
self.blue = b.nonlinear_to_linear_srgb();
self
}
pub fn set_r_linear(&mut self, r: f32) -> &mut Self {
self.red = r;
self
}
pub fn set_g_linear(&mut self, g: f32) -> &mut Self {
self.green = g;
self
}
pub fn set_b_linear(&mut self, b: f32) -> &mut Self {
self.blue = b;
self
}
pub fn set_a(&mut self, a: f32) -> &mut Self {
self.alpha = a;
self
}
}
impl Default for Color {
fn default() -> Self {
Color::WHITE
}
}
impl AddAssign<Color> for Color {
fn add_assign(&mut self, rhs: Color) {
*self = Color {
red: self.red + rhs.red,
green: self.green + rhs.green,
blue: self.blue + rhs.blue,
alpha: self.alpha + rhs.alpha,
}
}
}
impl Add<Color> for Color {
type Output = Color;
fn add(self, rhs: Color) -> Self::Output {
Color {
red: self.red + rhs.red,
green: self.green + rhs.green,
blue: self.blue + rhs.blue,
alpha: self.alpha + rhs.alpha,
}
}
}
impl Add<Vec4> for Color {
type Output = Color;
fn add(self, rhs: Vec4) -> Self::Output {
Color {
red: self.red + rhs.x(),
green: self.green + rhs.y(),
blue: self.blue + rhs.z(),
alpha: self.alpha + rhs.w(),
}
}
}
impl From<Color> for [f32; 4] {
fn from(color: Color) -> Self {
[color.r(), color.g(), color.b(), color.a()]
}
}
impl From<[f32; 4]> for Color {
fn from([r, g, b, a]: [f32; 4]) -> Self {
Color::rgba(r, g, b, a)
}
}
impl From<Color> for Vec4 {
fn from(color: Color) -> Self {
Vec4::new(color.r(), color.g(), color.b(), color.a())
}
}
impl From<Vec4> for Color {
fn from(vec4: Vec4) -> Self {
Color::rgba(vec4.x(), vec4.y(), vec4.z(), vec4.w())
}
}
impl Mul<f32> for Color {
type Output = Color;
fn mul(self, rhs: f32) -> Self::Output {
Color::rgba(self.r() * rhs, self.g() * rhs, self.b() * rhs, self.a())
}
}
impl MulAssign<f32> for Color {
fn mul_assign(&mut self, rhs: f32) {
self.set_r(self.r() * rhs);
self.set_g(self.g() * rhs);
self.set_b(self.b() * rhs);
}
}
impl Mul<Vec4> for Color {
type Output = Color;
fn mul(self, rhs: Vec4) -> Self::Output {
Color::rgba(
self.r() * rhs.x(),
self.g() * rhs.y(),
self.b() * rhs.z(),
self.a() * rhs.w(),
)
}
}
impl MulAssign<Vec4> for Color {
fn mul_assign(&mut self, rhs: Vec4) {
self.set_r(self.r() * rhs.x());
self.set_g(self.g() * rhs.y());
self.set_b(self.b() * rhs.z());
self.set_a(self.a() * rhs.w());
}
}
impl Mul<Vec3> for Color {
type Output = Color;
fn mul(self, rhs: Vec3) -> Self::Output {
Color::rgba(
self.r() * rhs.x(),
self.g() * rhs.y(),
self.b() * rhs.z(),
self.a(),
)
}
}
impl MulAssign<Vec3> for Color {
fn mul_assign(&mut self, rhs: Vec3) {
self.set_r(self.r() * rhs.x());
self.set_g(self.g() * rhs.y());
self.set_b(self.b() * rhs.z());
}
}
impl Mul<[f32; 4]> for Color {
type Output = Color;
fn mul(self, [r, g, b, a]: [f32; 4]) -> Self::Output {
Color::rgba(self.r() * r, self.g() * g, self.b() * b, self.a() * a)
}
}
impl MulAssign<[f32; 4]> for Color {
fn mul_assign(&mut self, [r, g, b, a]: [f32; 4]) {
self.set_r(self.r() * r);
self.set_g(self.g() * g);
self.set_b(self.b() * b);
self.set_a(self.a() * a);
}
}
impl Mul<[f32; 3]> for Color {
type Output = Color;
fn mul(self, [r, g, b]: [f32; 3]) -> Self::Output {
Color::rgba(self.r() * r, self.g() * g, self.b() * b, self.a())
}
}
impl MulAssign<[f32; 3]> for Color {
fn mul_assign(&mut self, [r, g, b]: [f32; 3]) {
self.set_r(self.r() * r);
self.set_g(self.g() * g);
self.set_b(self.b() * b);
}
}
impl Bytes for ColorSource {
fn write_bytes(&self, buffer: &mut [u8]) {
match *self {
ColorSource::Color(ref color) => color.write_bytes(buffer),
ColorSource::Texture(_) => {}
}
}
fn byte_len(&self) -> usize {
match *self {
ColorSource::Color(ref color) => color.byte_len(),
ColorSource::Texture(_) => 0,
}
}
}
pub enum ColorSource {
Color(Color),
Texture(Handle<Texture>),
}
impl From<[f32; 4]> for ColorSource {
fn from(f32s: [f32; 4]) -> Self {
ColorSource::Color(f32s.into())
}
}
impl From<Vec4> for ColorSource {
fn from(vec4: Vec4) -> Self {
ColorSource::Color(vec4.into())
}
}
impl From<Color> for ColorSource {
fn from(color: Color) -> Self {
ColorSource::Color(color)
}
}
impl From<Handle<Texture>> for ColorSource {
fn from(texture: Handle<Texture>) -> Self {
ColorSource::Texture(texture)
}
}
impl_render_resource_bytes!(Color);
#[derive(Debug)]
pub enum HexColorError {
Length,
Hex(hex::FromHexError),
}
fn decode_rgb(data: &[u8]) -> Result<Color, HexColorError> {
let mut buf = [0; 3];
match hex::decode_to_slice(data, &mut buf) {
Ok(_) => {
let r = buf[0] as f32 / 255.0;
let g = buf[1] as f32 / 255.0;
let b = buf[2] as f32 / 255.0;
Ok(Color::rgb(r, g, b))
}
Err(err) => Err(HexColorError::Hex(err)),
}
}
fn decode_rgba(data: &[u8]) -> Result<Color, HexColorError> {
let mut buf = [0; 4];
match hex::decode_to_slice(data, &mut buf) {
Ok(_) => {
let r = buf[0] as f32 / 255.0;
let g = buf[1] as f32 / 255.0;
let b = buf[2] as f32 / 255.0;
let a = buf[3] as f32 / 255.0;
Ok(Color::rgba(r, g, b, a))
}
Err(err) => Err(HexColorError::Hex(err)),
}
}
#[test]
fn test_color_components_roundtrip() {
let mut color = Color::NONE;
color.set_r(0.5).set_g(0.5).set_b(0.5).set_a(0.5);
const EPS: f32 = 0.001;
assert!((color.r() - 0.5).abs() < EPS);
assert!((color.g() - 0.5).abs() < EPS);
assert!((color.b() - 0.5).abs() < EPS);
assert!((color.a() - 0.5).abs() < EPS);
}
#[test]
fn test_hex_color() {
assert_eq!(Color::hex("FFF").unwrap(), Color::rgb(1.0, 1.0, 1.0));
assert_eq!(Color::hex("000").unwrap(), Color::rgb(0.0, 0.0, 0.0));
assert!(Color::hex("---").is_err());
assert_eq!(Color::hex("FFFF").unwrap(), Color::rgba(1.0, 1.0, 1.0, 1.0));
assert_eq!(Color::hex("0000").unwrap(), Color::rgba(0.0, 0.0, 0.0, 0.0));
assert!(Color::hex("----").is_err());
assert_eq!(Color::hex("FFFFFF").unwrap(), Color::rgb(1.0, 1.0, 1.0));
assert_eq!(Color::hex("000000").unwrap(), Color::rgb(0.0, 0.0, 0.0));
assert!(Color::hex("------").is_err());
assert_eq!(
Color::hex("FFFFFFFF").unwrap(),
Color::rgba(1.0, 1.0, 1.0, 1.0)
);
assert_eq!(
Color::hex("00000000").unwrap(),
Color::rgba(0.0, 0.0, 0.0, 0.0)
);
assert!(Color::hex("--------").is_err());
assert!(Color::hex("1234567890").is_err());
}
#[test]
fn test_conversions_vec4() {
let starting_vec4 = Vec4::new(0.4, 0.5, 0.6, 1.0);
let starting_color = Color::from(starting_vec4);
assert_eq!(starting_vec4, Vec4::from(starting_color),);
let transformation = Vec4::new(0.5, 0.5, 0.5, 1.0);
assert_eq!(
starting_color * transformation,
Color::from(starting_vec4 * transformation),
);
}
#[test]
fn test_mul_and_mulassign_f32() {
let transformation = 0.5;
let starting_color = Color::rgba(0.4, 0.5, 0.6, 1.0);
assert_eq!(
starting_color * transformation,
Color::rgba(0.4 * 0.5, 0.5 * 0.5, 0.6 * 0.5, 1.0),
);
let mut mutated_color = starting_color;
mutated_color *= transformation;
assert_eq!(starting_color * transformation, mutated_color,);
}
#[test]
fn test_mul_and_mulassign_f32by3() {
let transformation = [0.4, 0.5, 0.6];
let starting_color = Color::rgba(0.4, 0.5, 0.6, 1.0);
assert_eq!(
starting_color * transformation,
Color::rgba(0.4 * 0.4, 0.5 * 0.5, 0.6 * 0.6, 1.0),
);
let mut mutated_color = starting_color;
mutated_color *= transformation;
assert_eq!(starting_color * transformation, mutated_color,);
}
#[test]
fn test_mul_and_mulassign_f32by4() {
let transformation = [0.4, 0.5, 0.6, 0.9];
let starting_color = Color::rgba(0.4, 0.5, 0.6, 1.0);
assert_eq!(
starting_color * transformation,
Color::rgba(0.4 * 0.4, 0.5 * 0.5, 0.6 * 0.6, 1.0 * 0.9),
);
let mut mutated_color = starting_color;
mutated_color *= transformation;
assert_eq!(starting_color * transformation, mutated_color,);
}
#[test]
fn test_mul_and_mulassign_vec3() {
let transformation = Vec3::new(0.2, 0.3, 0.4);
let starting_color = Color::rgba(0.4, 0.5, 0.6, 1.0);
assert_eq!(
starting_color * transformation,
Color::rgba(0.4 * 0.2, 0.5 * 0.3, 0.6 * 0.4, 1.0),
);
let mut mutated_color = starting_color;
mutated_color *= transformation;
assert_eq!(starting_color * transformation, mutated_color,);
}
#[test]
fn test_mul_and_mulassign_vec4() {
let transformation = Vec4::new(0.2, 0.3, 0.4, 0.5);
let starting_color = Color::rgba(0.4, 0.5, 0.6, 1.0);
assert_eq!(
starting_color * transformation,
Color::rgba(0.4 * 0.2, 0.5 * 0.3, 0.6 * 0.4, 1.0 * 0.5),
);
let mut mutated_color = starting_color;
mutated_color *= transformation;
assert_eq!(starting_color * transformation, mutated_color,);
}