use super::{deg, percent, Angle, Color, Ratio, HSL, HSLA};
use std::fmt;
pub fn rgb(r: u8, g: u8, b: u8) -> RGB {
RGB {
r: Ratio::from_u8(r),
g: Ratio::from_u8(g),
b: Ratio::from_u8(b),
}
}
pub fn rgba(r: u8, g: u8, b: u8, a: f32) -> RGBA {
RGBA {
r: Ratio::from_u8(r),
g: Ratio::from_u8(g),
b: Ratio::from_u8(b),
a: Ratio::from_f32(a),
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct RGB {
pub r: Ratio,
pub g: Ratio,
pub b: Ratio,
}
impl fmt::Display for RGB {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"rgb({}, {}, {})",
self.r.as_u8(),
self.g.as_u8(),
self.b.as_u8()
)
}
}
impl Color for RGB {
type Alpha = RGBA;
fn to_css(self) -> String {
self.to_string()
}
fn to_rgb(self) -> RGB {
self
}
fn to_rgba(self) -> RGBA {
let RGB { r, g, b } = self;
RGBA {
r,
g,
b,
a: percent(100),
}
}
fn to_hsl(self) -> HSL {
self.to_rgba().to_hsl()
}
fn to_hsla(self) -> HSLA {
self.to_rgba().to_hsla()
}
fn saturate(self, amount: Ratio) -> Self {
self.to_rgba().saturate(amount).to_rgb()
}
fn desaturate(self, amount: Ratio) -> Self {
self.to_rgba().desaturate(amount).to_rgb()
}
fn lighten(self, amount: Ratio) -> Self {
self.to_rgba().lighten(amount).to_rgb()
}
fn darken(self, amount: Ratio) -> Self {
self.to_rgba().darken(amount).to_rgb()
}
fn fadein(self, amount: Ratio) -> RGBA {
self.to_rgba().fadein(amount)
}
fn fadeout(self, amount: Ratio) -> RGBA {
self.to_rgba().fadeout(amount)
}
fn fade(self, amount: Ratio) -> RGBA {
self.to_rgba().fade(amount)
}
fn spin(self, amount: Angle) -> Self {
self.to_rgba().spin(amount).to_rgb()
}
fn mix<T: Color>(self, other: T, weight: Ratio) -> RGBA {
self.to_rgba().mix(other, weight)
}
fn tint(self, weight: Ratio) -> Self {
self.to_rgba().tint(weight).to_rgb()
}
fn shade(self, weight: Ratio) -> Self {
self.to_rgba().shade(weight).to_rgb()
}
fn greyscale(self) -> Self {
self.to_rgba().greyscale().to_rgb()
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct RGBA {
pub r: Ratio,
pub g: Ratio,
pub b: Ratio,
pub a: Ratio,
}
impl fmt::Display for RGBA {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"rgba({}, {}, {}, {:.02})",
self.r.as_u8(),
self.g.as_u8(),
self.b.as_u8(),
self.a.as_f32()
)
}
}
impl Color for RGBA {
type Alpha = Self;
fn to_css(self) -> String {
self.to_string()
}
fn to_rgb(self) -> RGB {
let RGBA { r, g, b, .. } = self;
RGB { r, g, b }
}
fn to_rgba(self) -> RGBA {
self
}
fn to_hsl(self) -> HSL {
self.to_hsla().to_hsl()
}
fn to_hsla(self) -> HSLA {
let RGBA { r, g, b, a } = self;
if r == g && g == b {
return HSLA {
h: deg(0),
s: percent(0),
l: r,
a,
};
}
let r = self.r.as_f32();
let g = self.g.as_f32();
let b = self.b.as_f32();
let max = if r > g && r > b {
r
} else if g > b {
g
} else {
b
};
let min = if r < g && r < b {
r
} else if g < b {
g
} else {
b
};
let luminosity = (max + min) / 2.0;
let saturation = if max == min {
0.0
} else if luminosity < 0.5 {
(max - min) / (max + min)
} else {
(max - min) / (2.0 - (max + min))
};
let hue = if max == r {
60.0 * (g - b) / (max - min)
} else if max == g {
120.0 + 60.0 * (b - r) / (max - min)
} else {
240.0 + 60.0 * (r - g) / (max - min)
};
HSLA {
h: deg(hue.round() as i32),
s: Ratio::from_f32(saturation),
l: Ratio::from_f32(luminosity),
a,
}
}
fn saturate(self, amount: Ratio) -> Self {
self.to_hsla().saturate(amount).to_rgba()
}
fn desaturate(self, amount: Ratio) -> Self {
self.to_hsla().desaturate(amount).to_rgba()
}
fn lighten(self, amount: Ratio) -> Self {
self.to_hsla().lighten(amount).to_rgba()
}
fn darken(self, amount: Ratio) -> Self {
self.to_hsla().darken(amount).to_rgba()
}
fn fadein(self, amount: Ratio) -> Self {
self.fade(self.a + amount)
}
fn fadeout(self, amount: Ratio) -> Self {
self.fade(self.a - amount)
}
fn fade(self, amount: Ratio) -> Self {
let RGBA { r, g, b, .. } = self;
RGBA { r, g, b, a: amount }
}
fn spin(self, amount: Angle) -> Self {
self.to_hsla().spin(amount).to_rgba()
}
fn mix<T: Color>(self, other: T, weight: Ratio) -> Self {
let RGBA {
r: r_lhs,
g: g_lhs,
b: b_lhs,
a: a_lhs,
} = self;
let RGBA {
r: r_rhs,
g: g_rhs,
b: b_rhs,
a: a_rhs,
} = other.to_rgba();
let w = (weight.as_f32() * 2.0) - 1.0;
let a = a_lhs.as_f32() - a_rhs.as_f32();
let rgb_weight = if w * a == -1.0 {
w
} else {
(w + a) / (1.0 + w * a)
};
let rgb_weight = (rgb_weight + 1.0) / 2.0;
let rgb_weight_lhs = Ratio::from_f32(rgb_weight);
let rgb_weight_rhs = Ratio::from_f32(1.0) - rgb_weight_lhs;
let alpha_weight_lhs = weight;
let alpha_weight_rhs = Ratio::from_f32(1.0) - alpha_weight_lhs;
RGBA {
r: (r_lhs * rgb_weight_lhs) + (r_rhs * rgb_weight_rhs),
g: (g_lhs * rgb_weight_lhs) + (g_rhs * rgb_weight_rhs),
b: (b_lhs * rgb_weight_lhs) + (b_rhs * rgb_weight_rhs),
a: (a_lhs * alpha_weight_lhs) + (a_rhs * alpha_weight_rhs),
}
}
fn tint(self, weight: Ratio) -> Self {
self.mix(rgb(255, 255, 255), weight)
}
fn shade(self, weight: Ratio) -> Self {
self.mix(rgb(0, 0, 0), weight)
}
fn greyscale(self) -> Self {
self.to_hsla().greyscale().to_rgba()
}
}