flo_canvas/
color.rs

1use hsluv::*;
2
3///
4/// Possible formats of a colour value
5///
6#[derive(Clone, Copy, PartialEq, Debug, Serialize, Deserialize)]
7pub enum ColorFormat {
8    Rgba,
9    Hsluv
10}
11
12///
13/// Representation of a colour
14///
15#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
16pub enum Color {
17    Rgba(f32, f32, f32, f32),
18    Hsluv(f32, f32, f32, f32)
19}
20
21impl PartialEq for Color {
22    fn eq(&self, col: &Color) -> bool { 
23        use self::Color::*;
24
25        // Colors are equal if they're 'close enough'
26        let distance = match (self, col) {
27            (Rgba(r1, g1, b1, a1), Rgba(r2, g2, b2, a2))    => { (r1-r2)*(r1-r2) + (g1-g2)*(g1-g2) + (b1-b2)*(b1-b2) + (a1-a2)*(a1-a2) }
28            (Hsluv(h1, s1, l1, a1), Hsluv(h2, s2, l2, a2))  => { (h1-h2)*(h1-h2) + (s1-s2)*(s1-s2) + (l1-l2)*(l1-l2) + (a1-a2)*(a1-a2) }
29            _                                               => { return false; }
30        };
31
32        distance < (0.0001 * 0.0001)
33    }
34}
35
36impl Color {
37    ///
38    /// Returns this colour as RGBA components
39    ///
40    pub fn to_rgba_components(&self) -> (f32, f32, f32, f32) {
41        match self {
42            &Color::Rgba(r, g, b, a) => (r, g, b, a),
43
44            &Color::Hsluv(h, s, l, a) => {
45                let (r, g, b) = hsluv_to_rgb((h as f64, s as f64, l as f64));
46                (r as f32, g as f32, b as f32, a)
47            }
48        }
49    }
50
51    ///
52    /// Returns this colour as HSLUV components
53    ///
54    pub fn to_hsluv_components(&self) -> (f32, f32, f32, f32) {
55        match self {
56            &Color::Hsluv(h, s, l, a) => (h, s, l, a),
57
58            &Color::Rgba(r, g, b, a) => {
59                let (h, s, l) = rgb_to_hsluv((r as f64, g as f64, b as f64));
60                let s = if l <= 0.0 { 100.0 } else { s };
61                (h as f32, s as f32, l as f32, a)
62            }
63        }
64    }
65
66    ///
67    /// Converts this colour to another format
68    ///
69    #[inline]
70    pub fn to_format(&self, format: ColorFormat) -> Color {
71        let (r, g, b, a) = self.to_rgba_components();
72
73        match format {
74            ColorFormat::Rgba   => Color::Rgba(r, g, b, a),
75            ColorFormat::Hsluv  => {
76                let (h, s, l) = rgb_to_hsluv((r as f64, g as f64, b as f64));
77                let s = if l <= 0.0 { 100.0 } else { s };
78                Color::Hsluv(h as f32, s as f32, l as f32, a)
79            }
80        }
81    }
82
83    ///
84    /// Returns a new colour that's the same as this one except with a different alpha value
85    ///
86    pub fn with_alpha(&self, new_alpha: f32) -> Color {
87        match self {
88            &Color::Rgba(r, g, b, _)    => Color::Rgba(r, g, b, new_alpha),
89            &Color::Hsluv(h, s, l, _)   => Color::Hsluv(h, s, l, new_alpha)
90        }
91    }
92}
93
94#[cfg(test)]
95mod test {
96    use super::*;
97
98    #[test]
99    fn can_convert_rgba_to_hsluv() {
100        let rgb     = Color::Rgba(0.5, 0.7, 0.2, 0.9);
101        let hsluv   = rgb.to_format(ColorFormat::Hsluv);
102
103        if let Color::Hsluv(h, s, l, a) = hsluv {
104            assert!((h-110.3).abs() < 0.1);
105            assert!((s-89.5).abs() < 0.1);
106            assert!((l-67.1).abs() < 0.1);
107            assert!(a == 0.9);
108        } else {
109            assert!(false)
110        }
111    }
112
113    #[test]
114    fn can_convert_hsluv_to_rgba() {
115        let hsluv   = Color::Hsluv(24.0, 66.0, 60.0, 0.8);
116        let rgb     = hsluv.to_format(ColorFormat::Rgba);
117
118        if let Color::Rgba(r, g, b, a) = rgb {
119            assert!((r-0.89) < 0.1);
120            assert!((g-0.43) < 0.1);
121            assert!((b-0.38) < 0.1);
122            assert!(a == 0.8);
123        } else {
124            assert!(false);
125        }
126    }
127
128    #[test]
129    fn can_get_rgba_components_from_hsluv() {
130        let hsluv           = Color::Hsluv(24.0, 66.0, 60.0, 0.8);
131        let (r, g, b, a)    = hsluv.to_rgba_components();
132
133        assert!((r-0.89) < 0.1);
134        assert!((g-0.43) < 0.1);
135        assert!((b-0.38) < 0.1);
136        assert!(a == 0.8);
137    }
138}