#[derive(Debug, thiserror::Error)]
pub enum ColorCreationError {
#[error("parse error: {0}")]
ParseError(String),
}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
pub struct RGBAColor {
pub r: u8,
pub g: u8,
pub b: u8,
pub a: u8,
}
impl RGBAColor {
pub fn new(c: impl Into<RGBAColor>) -> Self {
c.into()
}
pub fn try_new(
c: impl TryInto<RGBAColor, Error = ColorCreationError>,
) -> Result<Self, ColorCreationError> {
c.try_into()
}
pub const fn add(&self, other: &Self) -> Self {
Self {
r: self.r.saturating_add(other.r),
g: self.g.saturating_add(other.g),
b: self.b.saturating_add(other.b),
a: self.a.saturating_add(other.a),
}
}
pub const fn sub(&self, other: &Self) -> Self {
Self {
r: self.r.saturating_sub(other.r),
g: self.g.saturating_sub(other.g),
b: self.b.saturating_sub(other.b),
a: self.a.saturating_sub(other.a),
}
}
pub const fn mul(&self, other: &Self) -> Self {
Self {
r: self.r.saturating_mul(other.r),
g: self.g.saturating_mul(other.g),
b: self.b.saturating_mul(other.b),
a: self.a.saturating_mul(other.a),
}
}
pub const fn colorize(&self, other: &Self) -> Self {
let red = 255u8.wrapping_sub(other.r);
let green = 255u8.wrapping_sub(other.g);
let blue = 255u8.wrapping_sub(other.b);
Self {
r: self.r.saturating_sub(red),
g: self.r.saturating_sub(green),
b: self.r.saturating_sub(blue),
a: self.a,
}
}
}
impl Default for RGBAColor {
fn default() -> Self {
Self {
r: 0,
g: 0,
b: 0,
a: 255,
}
}
}
impl From<u32> for RGBAColor {
fn from(value: u32) -> Self {
if value > 0xFFFFFF {
Self {
r: ((value >> 24) & 0xFF) as u8,
g: ((value >> 16) & 0xFF) as u8,
b: ((value >> 8) & 0xFF) as u8,
a: (value & 0xFF) as u8,
}
} else {
Self {
r: ((value >> 16) & 0xFF) as u8,
g: ((value >> 8) & 0xFF) as u8,
b: (value & 0xFF) as u8,
a: 255,
}
}
}
}
impl TryFrom<&str> for RGBAColor {
type Error = ColorCreationError;
fn try_from(value: &str) -> Result<Self, Self::Error> {
let s = value.strip_prefix('#').unwrap_or(value);
u32::from_str_radix(s, 16)
.map_err(|e| ColorCreationError::ParseError(e.to_string()))
.map(|v| v.into())
}
}
impl TryFrom<String> for RGBAColor {
type Error = ColorCreationError;
fn try_from(value: String) -> Result<Self, Self::Error> {
Self::try_new(&*value)
}
}
impl From<[u8; 3]> for RGBAColor {
fn from(value: [u8; 3]) -> Self {
Self {
r: value[0],
g: value[1],
b: value[2],
a: 255,
}
}
}
impl From<[u8; 4]> for RGBAColor {
fn from(value: [u8; 4]) -> Self {
Self {
r: value[0],
g: value[1],
b: value[2],
a: value[3],
}
}
}
impl From<(u8, u8, u8)> for RGBAColor {
fn from(value: (u8, u8, u8)) -> Self {
Self {
r: value.0,
g: value.1,
b: value.2,
a: 255,
}
}
}
impl From<(u8, u8, u8, u8)> for RGBAColor {
fn from(value: (u8, u8, u8, u8)) -> Self {
Self {
r: value.0,
g: value.1,
b: value.2,
a: value.3,
}
}
}