use std::{fmt, ops};
use crate::{about_eq, about_eq_hash, Factor, FactorPercent};
#[repr(C)]
#[derive(Default, Copy, Clone, serde::Serialize, serde::Deserialize)]
pub struct Rgba {
pub red: f32,
pub green: f32,
pub blue: f32,
pub alpha: f32,
}
impl PartialEq for Rgba {
fn eq(&self, other: &Self) -> bool {
about_eq(self.red, other.red, EPSILON)
&& about_eq(self.green, other.green, EPSILON)
&& about_eq(self.blue, other.blue, EPSILON)
&& about_eq(self.alpha, other.alpha, EPSILON)
}
}
impl std::hash::Hash for Rgba {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
about_eq_hash(self.red, EPSILON, state);
about_eq_hash(self.green, EPSILON, state);
about_eq_hash(self.blue, EPSILON, state);
about_eq_hash(self.alpha, EPSILON, state);
}
}
impl Rgba {
pub fn new<C: Into<RgbaComponent>, A: Into<RgbaComponent>>(red: C, green: C, blue: C, alpha: A) -> Rgba {
Rgba {
red: red.into().0,
green: green.into().0,
blue: blue.into().0,
alpha: alpha.into().0,
}
}
pub fn set_red<R: Into<RgbaComponent>>(&mut self, red: R) {
self.red = red.into().0
}
pub fn set_green<G: Into<RgbaComponent>>(&mut self, green: G) {
self.green = green.into().0
}
pub fn set_blue<B: Into<RgbaComponent>>(&mut self, blue: B) {
self.blue = blue.into().0
}
pub fn set_alpha<A: Into<RgbaComponent>>(&mut self, alpha: A) {
self.alpha = alpha.into().0
}
pub fn with_red<R: Into<RgbaComponent>>(mut self, red: R) -> Self {
self.set_red(red);
self
}
pub fn with_green<R: Into<RgbaComponent>>(mut self, green: R) -> Self {
self.set_green(green);
self
}
pub fn with_blue<B: Into<RgbaComponent>>(mut self, blue: B) -> Self {
self.set_blue(blue);
self
}
pub fn with_alpha<A: Into<RgbaComponent>>(mut self, alpha: A) -> Self {
self.set_alpha(alpha);
self
}
pub fn transparent(self) -> Self {
self.with_alpha(0.0)
}
pub fn to_bytes(self) -> [u8; 4] {
[
(self.red * 255.0) as u8,
(self.green * 255.0) as u8,
(self.blue * 255.0) as u8,
(self.alpha * 255.0) as u8,
]
}
}
impl fmt::Debug for Rgba {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if f.alternate() {
f.debug_struct("Rgba")
.field("red", &self.red)
.field("green", &self.green)
.field("blue", &self.blue)
.field("alpha", &self.alpha)
.finish()
} else {
fn i(n: f32) -> u8 {
(clamp_normal(n) * 255.0).round() as u8
}
let a = i(self.alpha);
if a == 255 {
write!(f, "rgb({}, {}, {})", i(self.red), i(self.green), i(self.blue))
} else {
write!(f, "rgba({}, {}, {}, {})", i(self.red), i(self.green), i(self.blue), a)
}
}
}
}
impl fmt::Display for Rgba {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fn i(n: f32) -> u32 {
(clamp_normal(n) * 255.0).round() as u32
}
let mut rgb: u32 = 0;
rgb |= i(self.red) << 16;
rgb |= i(self.green) << 8;
rgb |= i(self.blue);
let a = i(self.alpha);
if a == 255 {
write!(f, "#{rgb:0>6X}")
} else {
let rgba = rgb << 8 | a;
write!(f, "#{rgba:0>8X}")
}
}
}
impl ops::Add<Self> for Rgba {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Rgba {
red: self.red + rhs.red,
green: self.green + rhs.green,
blue: self.blue + rhs.blue,
alpha: self.alpha + rhs.alpha,
}
}
}
impl ops::AddAssign<Self> for Rgba {
fn add_assign(&mut self, rhs: Self) {
*self = *self + rhs;
}
}
impl ops::Sub<Self> for Rgba {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
Rgba {
red: self.red - rhs.red,
green: self.green - rhs.green,
blue: self.blue - rhs.blue,
alpha: self.alpha - rhs.alpha,
}
}
}
impl ops::SubAssign<Self> for Rgba {
fn sub_assign(&mut self, rhs: Self) {
*self = *self - rhs;
}
}
const EPSILON: f32 = 0.00001;
fn clamp_normal(i: f32) -> f32 {
i.clamp(0.0, 1.0)
}
#[derive(Clone, Copy)]
pub struct RgbaComponent(pub f32);
impl From<f32> for RgbaComponent {
fn from(f: f32) -> Self {
RgbaComponent(f)
}
}
impl From<f64> for RgbaComponent {
fn from(f: f64) -> Self {
RgbaComponent(f as f32)
}
}
impl From<u8> for RgbaComponent {
fn from(u: u8) -> Self {
RgbaComponent(f32::from(u) / 255.)
}
}
impl From<FactorPercent> for RgbaComponent {
fn from(p: FactorPercent) -> Self {
RgbaComponent(p.0 / 100.)
}
}
impl From<Factor> for RgbaComponent {
fn from(f: Factor) -> Self {
RgbaComponent(f.0)
}
}