use bound::Bound;
use color::{Color, XYZColor};
use consts::ADOBE_RGB_TRANSFORM as ADOBE_RGB;
use consts::ADOBE_RGB_TRANSFORM_LU as ADOBE_RGB_LU;
use coord::Coord;
use illuminants::Illuminant;
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
pub struct AdobeRGBColor {
pub r: f64,
pub g: f64,
pub b: f64,
}
impl Color for AdobeRGBColor {
fn from_xyz(xyz: XYZColor) -> AdobeRGBColor {
let xyz_c = xyz.color_adapt(Illuminant::D65);
let rgb = *ADOBE_RGB * vector![xyz_c.x, xyz_c.y, xyz_c.z];
let clamp = |x: f64| {
if x > 1.0 {
1.0
} else if x < 0.0 {
0.0
} else {
x
}
};
let gamma = |x: f64| x.powf(256.0 / 563.0);
AdobeRGBColor {
r: gamma(clamp(rgb[0])),
g: gamma(clamp(rgb[1])),
b: gamma(clamp(rgb[2])),
}
}
fn to_xyz(&self, illuminant: Illuminant) -> XYZColor {
let ungamma = |x: f64| x.powf(563.0 / 256.0);
let xyz_vec = ADOBE_RGB_LU
.solve(&vector![ungamma(self.r), ungamma(self.g), ungamma(self.b)])
.expect("Matrix is invertible.");
XYZColor {
x: xyz_vec[0],
y: xyz_vec[1],
z: xyz_vec[2],
illuminant: Illuminant::D65,
}
.color_adapt(illuminant)
}
}
impl From<Coord> for AdobeRGBColor {
fn from(c: Coord) -> AdobeRGBColor {
AdobeRGBColor {
r: c.x,
g: c.y,
b: c.z,
}
}
}
impl From<AdobeRGBColor> for Coord {
fn from(val: AdobeRGBColor) -> Self {
Coord {
x: val.r,
y: val.g,
z: val.b,
}
}
}
impl Bound for AdobeRGBColor {
fn bounds() -> [(f64, f64); 3] {
[(0., 1.), (0., 1.), (0., 1.)]
}
}
#[cfg(test)]
mod tests {
#[allow(unused_imports)]
use super::*;
use consts::TEST_PRECISION;
#[test]
fn test_adobe_rgb_xyz_conversion() {
let xyz1 = XYZColor {
x: 0.4,
y: 0.2,
z: 0.5,
illuminant: Illuminant::D75,
};
let xyz2 = AdobeRGBColor::from_xyz(xyz1).to_xyz(Illuminant::D75);
assert!(xyz1.approx_equal(&xyz2));
assert!(xyz1.distance(&xyz2) <= TEST_PRECISION);
}
#[test]
fn test_adobe_rgb_clamping() {
let argb = AdobeRGBColor {
r: 1.1,
g: 0.6,
b: 0.8,
};
let argb2 = AdobeRGBColor {
r: 1.0,
g: 0.6,
b: 0.8,
};
let argbprime = argb.convert::<XYZColor>().convert::<AdobeRGBColor>();
let argb2prime = argb2.convert::<XYZColor>().convert::<AdobeRGBColor>();
let xyz1 = argbprime.to_xyz(Illuminant::D50);
let xyz2 = argb2prime.to_xyz(Illuminant::D50);
assert!(xyz1.approx_equal(&xyz2));
assert!(xyz1.distance(&xyz2) <= TEST_PRECISION);
}
}