color/
lab.rs

1use channel::Channel;
2use color_space::{WhitePoint};
3use num_traits::{Float, NumCast, cast, zero};
4use xyz::{Xyz, ToXyz};
5use std::ops::{Add, Mul};
6
7#[derive(Clone, Copy, Debug)]
8pub struct Lab<T, Wp>{
9    pub l: T,
10    pub a: T,
11    pub b: T,
12    pub white_point: Wp,
13}
14
15impl<T, Wp: WhitePoint> Lab<T, Wp>{
16    pub fn new(l: T, a: T, b: T) -> Lab<T, Wp>{
17        Lab { l, a, b, white_point: Wp::default() }
18    }
19}
20
21
22impl<T: Copy, Wp: WhitePoint> Lab<T, Wp>{
23    pub fn brightness(&self) -> T {
24        self.l
25    }
26}
27
28impl<T: Float, Wp: WhitePoint> Lab<T, Wp>{
29    pub fn chromacity(&self) -> T {
30        (self.a.powi(2) + self.b.powi(2)).sqrt()
31    }
32
33    pub fn hue(&self) -> T {
34        let h = self.b.atan2(self.a);
35        if h < zero() {
36            h + cast(std::f64::consts::TAU).unwrap()
37        }else{
38            h
39        }
40    }
41
42    pub fn offset_chromacity(&self, chroma_offset: T) -> Lab<T, Wp>{
43        let current_croma = self.chromacity();
44        let offset_a = self.a / current_croma * chroma_offset;
45        let offset_b = self.b / current_croma * chroma_offset;
46        Lab::new(
47            self.l,
48            self.a + offset_a,
49            self.b + offset_b,
50        )
51    }
52}
53
54pub trait ToLab {
55    type WhitePoint: WhitePoint;
56    fn to_lab<T: Channel>(&self) -> Lab<T, Self::WhitePoint>;
57}
58
59impl<T: Channel + Float + NumCast, Wp: WhitePoint> ToXyz for Lab<T, Wp> {
60    type WhitePoint = Wp;
61    fn to_xyz<U: Channel + Float>(&self) -> Xyz<U, Wp> {
62        let fy = (self.l + cast(16).unwrap()) / cast(116).unwrap();
63        let fx = self.a / cast(500).unwrap() + fy;
64        let fz = fy - self.b / cast(200).unwrap();
65        let fxcb=fx*fx*fx;
66        let fzcb=fz*fz*fz;
67        let mut xyz = [fxcb, cast(0.).unwrap(), fzcb];
68        let eps= cast(216.0 / 24389.).unwrap(); // See BruceLindbloom.com
69        if fxcb <= eps {
70            xyz[0] = (cast::<f64,T>(108.0).unwrap() * fx / cast(841).unwrap()) - cast::<f64,T>(432.0).unwrap() / cast(24389.).unwrap()
71        };
72        if fzcb <= eps{
73             xyz[2] = (cast::<f64,T>(108.0).unwrap() * fz / cast(841).unwrap()) - cast::<f64,T>(432.0).unwrap() / cast(24389.).unwrap()
74        }
75        if self.l > cast(8.).unwrap() { // See BruceLindbloom.com
76            xyz[1]=fy.powi(3)
77        }else{
78            xyz[1]=self.l * cast(27.0).unwrap() / cast(24389).unwrap(); // See BruceLindbloom.com
79        }
80        xyz[0] = xyz[0] * Wp::xyz().x;
81        xyz[1] = xyz[1] * Wp::xyz().y;
82        xyz[2] = xyz[2] * Wp::xyz().z;
83
84        Xyz::new(xyz[0].to_channel(), xyz[1].to_channel(), xyz[2].to_channel())
85    }
86}
87
88impl<T: Channel + Float + NumCast, Wp: WhitePoint> Add for Lab<T,Wp>{
89    type Output = Lab<T, Wp>;
90    fn add(self, other: Lab<T, Wp>) -> Lab<T, Wp> {
91        Lab::new(self.l + other.l, self.a + other.a, self.b + other.b)
92    }
93}
94
95impl<T: Channel + Float + NumCast, Wp: WhitePoint> Mul<T> for Lab<T,Wp>{
96    type Output = Lab<T, Wp>;
97    fn mul(self, other: T) -> Lab<T, Wp> {
98        Lab::new(self.l * other, self.a * other, self.b * other)
99    }
100}