mod conversions;
#[cfg(test)]
mod tests;
use std::fmt;
#[derive(Clone, Copy)]
pub struct Srgb(f32, f32, f32);
const GAMMA: f32 = 2.4;
pub fn gamma_expand(x: f32) -> f32 {
match x > 0.04045f32 {
true => f32::powf((x + 0.055f32) / 1.055f32, GAMMA),
false => x / 12.92f32,
}
}
pub fn gamma_compress(x: f32) -> f32 {
match x > 0.0031308f32 {
true => f32::powf(x, 1f32 / GAMMA) * 1.055f32 - 0.055f32,
false => x * 12.92f32,
}
}
impl Srgb {
pub fn new(red: u8, green: u8, blue: u8) -> Self {
let [norm_r, norm_g, norm_b] = [red, green, blue].map(|x| x as f32 / 255f32);
Self(norm_r, norm_g, norm_b)
}
pub fn red(&self) -> f32 {
self.0
}
pub fn green(&self) -> f32 {
self.1
}
pub fn blue(&self) -> f32 {
self.2
}
pub fn red_as_u8(&self) -> u8 {
(self.0 * 255f32).round() as u8
}
pub fn green_as_u8(&self) -> u8 {
(self.1 * 255f32).round() as u8
}
pub fn blue_as_u8(&self) -> u8 {
(self.2 * 255f32).round() as u8
}
pub fn linear_red(&self) -> f32 {
gamma_expand(self.0)
}
pub fn linear_green(&self) -> f32 {
gamma_expand(self.1)
}
pub fn linear_blue(&self) -> f32 {
gamma_expand(self.2)
}
}
impl PartialEq for Srgb {
fn eq(&self, other: &Self) -> bool {
let lhs = [self.red_as_u8(), self.green_as_u8(), self.blue_as_u8()];
let rhs = [other.red_as_u8(), other.green_as_u8(), other.blue_as_u8()];
(0..3).all(|x| lhs[x] == rhs[x])
}
}
impl fmt::Debug for Srgb {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Srgb({}, {}, {})", self.red_as_u8(), self.green_as_u8(), self.blue_as_u8())
}
}