use slate_renderer::srgb_u8_to_linear_premul;
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Color(pub [f32; 4]);
impl Color {
pub const TRANSPARENT: Self = Self([0.0, 0.0, 0.0, 0.0]);
pub const WHITE: Self = Self([1.0, 1.0, 1.0, 1.0]);
pub const BLACK: Self = Self([0.0, 0.0, 0.0, 1.0]);
pub const RED: Self = Self([1.0, 0.0, 0.0, 1.0]);
pub const GREEN: Self = Self([0.0, 1.0, 0.0, 1.0]);
pub const BLUE: Self = Self([0.0, 0.0, 1.0, 1.0]);
pub fn from_rgba(r: u8, g: u8, b: u8, a: u8) -> Self {
Self(srgb_u8_to_linear_premul([r, g, b, a]))
}
pub fn from_rgb(r: u8, g: u8, b: u8) -> Self {
Self::from_rgba(r, g, b, 255)
}
pub fn from_hex(hex: &str) -> Option<Self> {
let hex = hex.trim_start_matches('#');
match hex.len() {
3 => {
let r = u8::from_str_radix(&hex[0..1], 16).ok()?;
let g = u8::from_str_radix(&hex[1..2], 16).ok()?;
let b = u8::from_str_radix(&hex[2..3], 16).ok()?;
Some(Self::from_rgb(r * 17, g * 17, b * 17))
}
6 => {
let r = u8::from_str_radix(&hex[0..2], 16).ok()?;
let g = u8::from_str_radix(&hex[2..4], 16).ok()?;
let b = u8::from_str_radix(&hex[4..6], 16).ok()?;
Some(Self::from_rgb(r, g, b))
}
8 => {
let r = u8::from_str_radix(&hex[0..2], 16).ok()?;
let g = u8::from_str_radix(&hex[2..4], 16).ok()?;
let b = u8::from_str_radix(&hex[4..6], 16).ok()?;
let a = u8::from_str_radix(&hex[6..8], 16).ok()?;
Some(Self::from_rgba(r, g, b, a))
}
_ => None,
}
}
pub fn as_array(&self) -> [f32; 4] {
self.0
}
pub fn with_alpha(mut self, alpha: f32) -> Self {
if self.0[3] > 0.0 {
let inv_a = 1.0 / self.0[3];
self.0[0] = self.0[0] * inv_a * alpha;
self.0[1] = self.0[1] * inv_a * alpha;
self.0[2] = self.0[2] * inv_a * alpha;
} else {
self.0[0] *= alpha;
self.0[1] *= alpha;
self.0[2] *= alpha;
}
self.0[3] = alpha;
self
}
}
impl Default for Color {
fn default() -> Self {
Self::TRANSPARENT
}
}
impl From<Color> for [f32; 4] {
fn from(color: Color) -> Self {
color.0
}
}
impl From<[f32; 4]> for Color {
fn from(arr: [f32; 4]) -> Self {
Self(arr)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn color_from_hex_6() {
let red = Color::from_hex("#FF0000").unwrap();
assert_eq!(red.0[3], 1.0); assert!(red.0[0] > 0.9); assert!(red.0[1] < 0.01); assert!(red.0[2] < 0.01); }
#[test]
fn color_from_hex_3() {
let red = Color::from_hex("#F00").unwrap();
assert!(red.0[0] > 0.9);
}
#[test]
fn color_from_hex_8() {
let half_red = Color::from_hex("#FF000080").unwrap();
assert!((half_red.0[3] - 0.5).abs() < 0.1); }
#[test]
fn color_constants() {
assert_eq!(Color::WHITE.0, [1.0, 1.0, 1.0, 1.0]);
assert_eq!(Color::BLACK.0, [0.0, 0.0, 0.0, 1.0]);
assert_eq!(Color::TRANSPARENT.0, [0.0, 0.0, 0.0, 0.0]);
}
#[test]
fn color_invalid_hex() {
assert!(Color::from_hex("invalid").is_none());
assert!(Color::from_hex("#GGG").is_none());
assert!(Color::from_hex("#12345").is_none());
}
#[test]
fn with_alpha_from_zero_preserves_rgb() {
let c = Color([0.5, 0.7, 0.3, 0.0]);
let c2 = c.with_alpha(1.0);
assert!((c2.0[0] - 0.5).abs() < 1e-6);
assert!((c2.0[1] - 0.7).abs() < 1e-6);
assert!((c2.0[2] - 0.3).abs() < 1e-6);
assert!((c2.0[3] - 1.0).abs() < 1e-6);
}
#[test]
fn with_alpha_from_nonzero_unpremultiplies() {
let c = Color([0.5, 0.5, 0.5, 0.5]); let c2 = c.with_alpha(1.0);
assert!((c2.0[0] - 1.0).abs() < 1e-6);
assert!((c2.0[1] - 1.0).abs() < 1e-6);
assert!((c2.0[2] - 1.0).abs() < 1e-6);
assert!((c2.0[3] - 1.0).abs() < 1e-6);
}
}