use siege_math::{Vec2, Vec3, Mat3};
use cie1931::{Cie1931, D65};
use super::Chromaticity;
pub const SRGB_CHROMATICITIES: Chromaticity = Chromaticity {
red: Vec2 { x: 0.64000, y: 0.33000 },
green: Vec2 { x: 0.30000, y: 0.60000 },
blue: Vec2 { x: 0.15000, y: 0.06000 },
white: Vec2 { x: 0.31270, y: 0.32900 }
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Srgb24(pub u8, pub u8, pub u8);
impl Srgb24 {
pub fn new(r: u8, g: u8, b: u8) -> Srgb24 {
Srgb24(r,g,b)
}
}
impl From<Srgb> for Srgb24 {
fn from(srgb: Srgb) -> Srgb24 {
Srgb24(
(srgb.v.x * 255.0).round() as u8,
(srgb.v.y * 255.0).round() as u8,
(srgb.v.z * 255.0).round() as u8
)
}
}
impl From<Srgb24> for Srgb {
fn from(srgb24: Srgb24) -> Srgb {
Srgb::new(
srgb24.0 as f32 / 255.0,
srgb24.1 as f32 / 255.0,
srgb24.2 as f32 / 255.0
)
}
}
#[derive(Debug, Clone)]
pub struct Srgb {
pub v: Vec3<f32>
}
impl Srgb {
pub fn new(r: f32, g: f32, b: f32) -> Srgb {
Srgb {
v: Vec3::new(r, g, b)
}
}
#[inline]
pub fn r(&self) -> f32 {
self.v.x
}
#[inline]
pub fn g(&self) -> f32 {
self.v.y
}
#[inline]
pub fn b(&self) -> f32 {
self.v.z
}
}
#[derive(Debug, Clone)]
pub struct LinearSrgb {
pub v: Vec3<f32>
}
impl LinearSrgb {
pub fn new(r: f32, g: f32, b: f32) -> LinearSrgb {
LinearSrgb {
v: Vec3::new(r, g, b)
}
}
#[inline]
pub fn r(&self) -> f32 {
self.v.x
}
#[inline]
pub fn g(&self) -> f32 {
self.v.y
}
#[inline]
pub fn b(&self) -> f32 {
self.v.z
}
pub fn get_luminance(&self) -> f32
{
Vec3::<f32>::new(0.2126729,
0.7151522,
0.0721750).dot(self.v)
}
pub fn set_luminance(&mut self, luminance: f32) {
let newlum = if luminance < 0.0 { 0.0 } else { luminance };
let oldlum: f32 = self.get_luminance();
if oldlum==0.0 { return; } let scale: f32 = newlum/oldlum;
self.v.x = (self.v.x * scale).min(1.0);
self.v.y = (self.v.y * scale).min(1.0);
self.v.z = (self.v.z * scale).min(1.0);
}
pub fn get_brightness(&self) -> f32
{
0.299 * self.r() + 0.587 * self.g() + 0.114 * self.b()
}
pub fn set_brightness(&mut self, brightness: f32) {
let original = self.get_brightness();
let scale = brightness / original;
self.v *= scale;
}
pub fn set_max_brightness(&mut self) {
let scale = self.r().max(self.g()).max(self.b());
self.v /= scale;
}
}
impl From<Cie1931<D65>> for LinearSrgb {
fn from(input: Cie1931<D65>) -> LinearSrgb {
let m: Mat3<f32> = Mat3::new(
3.2406255, -1.5372080, -0.49862860,
-0.96893071, 1.8757561, 0.041517524,
0.055710120, -0.20402105, 1.0569959
);
LinearSrgb {
v: &m * &input.v
}
}
}
impl From<LinearSrgb> for Cie1931<D65> {
fn from(input: LinearSrgb) -> Cie1931<D65> {
let m: Mat3<f32> = Mat3::new(
0.4124, 0.3576, 0.1805,
0.2126, 0.7152, 0.0722,
0.0193, 0.1192, 0.9505
);
let cv = &m * &input.v;
Cie1931::new(cv.x, cv.y, cv.z)
}
}
impl From<Srgb> for LinearSrgb {
fn from(srgb: Srgb) -> LinearSrgb {
let f = |x: f32| -> f32 {
if x <= 0.04045 { x / 12.92 }
else { ((x + 0.055)/1.055).powf(2.4) }
};
let mut red = f(srgb.r());
if red>1.0 { red = 1.0 };
let mut green = f(srgb.g());
if green>1.0 { green = 1.0 };
let mut blue = f(srgb.b());
if blue>1.0 { blue = 1.0 };
LinearSrgb::new(red, green, blue)
}
}
impl From<LinearSrgb> for Srgb {
fn from(s: LinearSrgb) -> Srgb {
let a = 0.055;
let f = |x: f32| -> f32 {
if x <= 0.0031308 { 12.92 * x }
else { (1.0 + a) * x.powf(1.0/2.4) - a }
};
let mut red = f(s.r());
if red>1.0 { red = 1.0 };
let mut green = f(s.g());
if green>1.0 { green = 1.0 };
let mut blue = f(s.b());
if blue>1.0 { blue = 1.0 };
Srgb::new(red, green, blue)
}
}
#[cfg(test)]
mod tests {
use super::*;
use cie1931::{Cie1931, D65};
use float_cmp::ApproxEq;
#[test]
fn test_scale_brightness() {
let mut x = LinearSrgb {
v: Vec3::new( 0.5, 0.1, 0.4 )
};
x.set_brightness(0.8);
assert!(x.get_brightness() > 0.799999);
assert!(x.get_brightness() < 0.800001);
x.set_brightness(0.2);
assert!(x.get_brightness() > 0.199999);
assert!(x.get_brightness() < 0.200001);
}
#[test]
fn test_to_and_from_24() {
let srgb24 = Srgb24(1,64,255);
let srgb: Srgb = From::from(srgb24.clone());
let srgb24_2: Srgb24 = From::from(srgb);
assert_eq!(srgb24, srgb24_2);
}
#[test]
fn test_to_and_from_cie1931() {
let lsrgb = LinearSrgb::new(0.5, 0.2, 0.7);
let xyz: Cie1931<D65> = From::from(lsrgb.clone());
let lsrgb2: LinearSrgb = From::from(xyz);
assert!(lsrgb.v.approx_eq(&lsrgb2.v, 10, 10.0 * ::std::f32::EPSILON));
}
#[test]
fn test_xyz_to_srgb() {
let xyz = Cie1931::<D65>::new(0.25, 0.40, 0.10);
let lsrgb: LinearSrgb = From::from(xyz);
let srgb: Srgb = From::from(lsrgb);
let srgbu: Srgb24 = From::from(srgb);
assert_eq!(srgbu.0, 106);
assert_eq!(srgbu.1, 190);
assert_eq!(srgbu.2, 55);
}
#[test]
fn test_to_and_from_linear() {
let srgb = Srgb::new(0.1245, 0.0924, 0.9812);
let l: LinearSrgb = From::from(srgb.clone());
let srgb2: Srgb = From::from(l);
assert!(srgb.v.approx_eq(&srgb2.v, 10, 10.0 * ::std::f32::EPSILON));
}
#[test]
fn test_set_max_brightness() {
let mut lsrgb = LinearSrgb::new(0.1, 0.25, 0.5);
lsrgb.set_max_brightness();
assert!(lsrgb.v.approx_eq(
&Vec3::<f32>::new(0.2, 0.5, 1.0),
10, 10.0 * ::std::f32::EPSILON));
let mut lsrgb = LinearSrgb::new(1.5, 0.8234, 0.24);
lsrgb.set_max_brightness();
assert!(lsrgb.r().approx_eq(&1.0,
10,
10.0 * ::std::f32::EPSILON));
}
}