1use channel::Channel;
17use num_traits::Float;
18use rgb::{Rgb, ToRgb};
19use yxy::{Yxy, ToYxy};
20use color_space::{D65, WhitePoint, MatrixColorSpace, Srgb, TransferFunction};
21use num_traits::{zero, NumCast, cast};
22use lab::{Lab, ToLab};
24
25#[derive(Clone, Copy, Debug)]
26pub struct Xyz<T = f32, Wp = D65>
27where T: Channel + Float
28{
29 pub x: T,
30 pub y: T,
31 pub z: T,
32 pub white_point: Wp,
33}
34
35impl<T: Channel + Float, Wp: WhitePoint> Xyz<T,Wp> {
36 pub fn new(x: T, y: T, z: T) -> Xyz<T,Wp> {
37 Xyz{
38 x,
39 y,
40 z,
41 white_point: Wp::default(),
42 }
43 }
44}
45
46pub trait ToXyz {
47 type WhitePoint: WhitePoint;
48 fn to_xyz<T: Channel + Float + std::fmt::Debug>(&self) -> Xyz<T, Self::WhitePoint>;
49}
50
51impl<T: Channel + Float + Clone> ToRgb for Xyz<T, D65> {
52 type Standard = Srgb;
53 fn to_rgb<U: Channel>(&self) -> Rgb<U, Srgb> {
54 let rgb = Srgb::to_rgb_matrix() * self.clone().into();
55 Rgb::new(
56 Srgb::from_linear(rgb[0]).to_channel(),
57 Srgb::from_linear(rgb[1]).to_channel(),
58 Srgb::from_linear(rgb[2]).to_channel(),
59 )
60 }
61}
62
63impl<T: Channel + Float + NumCast, Wp: WhitePoint> ToLab for Xyz<T, Wp> {
64 type WhitePoint = Wp;
65 fn to_lab<U:Channel>(&self) -> Lab<U, Wp> {
66 let mut xyz = [self.x / Wp::xyz().x , self.y / Wp::xyz().y, self.z / Wp::xyz().z];
67 for i in 0..3 {
68 if xyz[i] > cast::<_, T>(216.0).unwrap() / cast(24389.).unwrap() {xyz[i] = xyz[i].cbrt()
70 }else{
71 let k = cast::<_, T>(24389.0).unwrap() / cast(27).unwrap(); xyz[i] = (cast::<_, T>(16.0).unwrap() + k * xyz[i]) / cast(116).unwrap()
73 }
74 }
75
76 return Lab::new(
77 (cast::<_, T>(116.0).unwrap() * xyz[1] - cast(16).unwrap()).to_channel(),
78 (cast::<_, T>(500).unwrap() * (xyz[0] - xyz[1])).to_channel(),
79 (cast::<_, T>(200).unwrap() * (xyz[1] - xyz[2])).to_channel()
80 )
81 }
82}
83
84impl<T: Channel + Float, Wp: WhitePoint> ToYxy for Xyz<T, Wp> {
85 type WhitePoint = Wp;
86 fn to_yxy<U: Channel + Float>(&self) -> Yxy<U, Wp> {
87 let sum = self.x + self.y + self.z;
88 let x;
89 let y;
90 let luma = self.y;
91 if sum < zero() || sum > zero() {
92 x = self.x / sum;
93 y = self.y / sum;
94 }else{
95 x = zero();
96 y = zero();
97 }
98 Yxy{x: x.to_channel(), y: y.to_channel(), luma: luma.to_channel(), white_point: Wp::default()}
99 }
100}