rust_color/
colors.rs

1use std::fmt::{Display, Error};
2
3pub trait Color: Copy {
4    type Type;
5    fn complement(&self) -> Self::Type;
6
7    fn get_rgba(&self) -> RgbaColorType;
8    fn get_hsla(&self) -> HslaColorType;
9    fn set_opacity(&mut self, a: f64) -> Self::Type;
10}
11
12#[derive(Debug, Clone, Copy, PartialEq)]
13pub struct RgbaColorType {
14    pub r: f64,
15    pub g: f64,
16    pub b: f64,
17    pub a: f64,
18}
19
20#[derive(Debug, Clone, Copy, PartialEq)]
21pub struct HslaColorType {
22    pub h: f64,
23    pub s: f64,
24    pub l: f64,
25    pub a: f64,
26}
27
28impl RgbaColorType {
29    pub fn new(r: f64, g: f64, b: f64) -> RgbaColorType {
30        RgbaColorType {
31            r: r,
32            g: g,
33            b: b,
34            a: 1.0,
35        }
36    }
37}
38
39impl HslaColorType {
40    pub fn new(h: f64, s: f64, l: f64) -> HslaColorType {
41        let mut a = HslaColorType {
42            h: h,
43            s: s,
44            l: l,
45            a: 1.0,
46        };
47        a.validate();
48        a
49    }
50
51    fn validate(&mut self) {
52        self.h = self.h % 360.0;
53        if self.h < 0.0 {
54            self.h += 360.0
55        };
56
57        if self.s < 0.0 {
58            self.s = 0.0
59        };
60        if self.s > 100.0 {
61            self.s = 100.0
62        };
63        if self.l < 0.0 {
64            self.l = 0.0
65        };
66        if self.l > 100.0 {
67            self.l = 100.0
68        };
69    }
70}
71fn hue2rgb(p: f64, q: f64, t: f64) -> f64 {
72    let mut t = t;
73    if t < 0.0 {
74        t += 1.0;
75    }
76    if t > 1.0 {
77        t -= 1.0;
78    }
79    if t < 1.0 / 6.0 {
80        p + (q - p) * 6.0 * t
81    } else if t < 1.0 / 2.0 {
82        q
83    } else if t < 2.0 / 3.0 {
84        p + (q - p) * (2.0 / 3.0 - t) * 6.0
85    } else {
86        p
87    }
88}
89
90impl From<[f64; 3]> for RgbaColorType {
91    fn from(c: [f64; 3]) -> RgbaColorType {
92        RgbaColorType::new(c[0], c[1], c[2])
93    }
94}
95impl From<[u8; 3]> for RgbaColorType {
96    fn from(c: [u8; 3]) -> RgbaColorType {
97        RgbaColorType::new(
98            c[0] as f64 / 255.0,
99            c[1] as f64 / 255.0,
100            c[2] as f64 / 255.0,
101        )
102    }
103}
104
105impl Into<[u8; 3]> for RgbaColorType {
106    fn into(self) -> [u8; 3] {
107        [
108            (self.r * 255.0).round() as u8,
109            (self.g * 255.0).round() as u8,
110            (self.b * 255.0).round() as u8,
111        ]
112    }
113}
114impl Into<[u8; 4]> for RgbaColorType {
115    fn into(self) -> [u8; 4] {
116        [
117            (self.r * 255.0).round() as u8,
118            (self.g * 255.0).round() as u8,
119            (self.b * 255.0).round() as u8,
120            (self.a * 255.0).round() as u8,
121        ]
122    }
123}
124impl Into<[f64; 4]> for RgbaColorType {
125    fn into(self) -> [f64; 4] {
126        [self.r, self.g, self.b, self.a]
127    }
128}
129
130impl Into<[f32; 4]> for RgbaColorType {
131    fn into(self) -> [f32; 4] {
132        [self.r as f32, self.g as f32, self.b as f32, self.a as f32]
133    }
134}
135impl From<HslaColorType> for RgbaColorType {
136    fn from(c: HslaColorType) -> RgbaColorType {
137        let h: f64 = c.h / 360.0;
138        let s: f64 = c.s / 100.0;
139        let l: f64 = c.l / 100.0;
140
141        let mut r: f64 = l;
142        let mut g: f64 = l;
143        let mut b: f64 = l;
144
145        if s != 0.0 {
146            let q = if l < 0.5 {
147                l * (1.0 + s)
148            } else {
149                l + s - l * s
150            };
151            let p = 2.0 * l - q;
152            r = hue2rgb(p, q, h + 1.0 / 3.0);
153            g = hue2rgb(p, q, h);
154            b = hue2rgb(p, q, h - 1.0 / 3.0);
155        }
156        RgbaColorType::new(r, g, b)
157    }
158}
159impl From<RgbaColorType> for HslaColorType {
160    fn from(c: RgbaColorType) -> HslaColorType {
161        let mut h: f64;
162        let mut s: f64;
163        let mut l: f64;
164        let r: f64 = c.r;
165        let g: f64 = c.g;
166        let b: f64 = c.b;
167
168        let max: f64 = c.r.max(c.g).max(c.b);
169        let min: f64 = c.r.min(c.g).min(c.b);
170        h = (max + min) / 2.0;
171        s = h;
172        l = h;
173
174        let error = 0.00001;
175        if (max - min).abs() < error {
176            // achromatic
177
178            h = 0.0;
179            s = 0.0;
180        } else {
181            let d = max - min;
182            s = if l > 0.5 {
183                d / (2.0 - max - min)
184            } else {
185                d / (max + min)
186            };
187            if (max - r).abs() < error {
188                h = (g - b) / d + (if g < b { 6.0 } else { 0.0 })
189            } else if (max - g).abs() < error {
190                h = (b - r) / d + 2.0
191            } else if (max - b).abs() < error {
192                h = (r - g) / d + 4.0;
193            }
194
195            h /= 6.0;
196        }
197        h *= 360.0;
198        s *= 100.0;
199        l *= 100.0;
200
201        HslaColorType::new(h, s, l)
202    }
203}
204impl Display for RgbaColorType {
205    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::result::Result<(), std::fmt::Error> {
206        fmt.write_str(format!("RGB({:?}, {:?}, {:?})", self.r, self.g, self.b).as_str())
207    }
208}
209impl Color for RgbaColorType {
210    type Type = RgbaColorType;
211
212    fn set_opacity(&mut self, a:  f64) -> Self::Type {
213        self.a = a;
214        *self
215    }
216
217    fn complement(&self) -> Self::Type {
218        RgbaColorType{r:1.0 - self.r, g: 1.0 - self.g, b: 1.0 - self.b, a: self.a}
219    }
220    fn get_hsla(&self) -> HslaColorType {
221        (*self).into()
222    }
223    fn get_rgba(&self) -> RgbaColorType {
224        *self
225    }
226}
227
228mod tests {
229    use super::*;
230    #[test]
231    fn test_new() {
232        let c = RgbaColorType::new(0.1, 0.2, 0.3);
233        assert_eq!(
234            c,
235            RgbaColorType {
236                r: 0.1,
237                g: 0.2,
238                b: 0.3,
239                a: 1.0
240            }
241        );
242    }
243
244    #[test]
245    fn test_complement() {
246        let c = RgbaColorType::new(0.1, 0.2, 0.3);
247        assert_eq!(
248            c.complement(),
249            RgbaColorType {
250                r: 0.9,
251                g: 0.8,
252                b: 0.7,
253                a: 1.0
254            }
255        );
256    }
257
258    #[test]
259    fn test_from_array() {
260        let a: RgbaColorType = RgbaColorType::from([1.0, 0.5, 0.0]);
261        assert_eq!(
262            a,
263            RgbaColorType {
264                r: 1.0,
265                g: 0.5,
266                b: 0.0,
267                a: 1.0
268            }
269        );
270    }
271
272    #[test]
273    fn test_from_bytes() {
274        let a = RgbaColorType::from([255, 255, 255]);
275        assert_eq!(
276            a,
277            RgbaColorType {
278                r: 1.0,
279                g: 1.0,
280                b: 1.0,
281                a: 1.0
282            }
283        );
284        let b = RgbaColorType::from([128, 128, 128]);
285        assert!(
286            error(
287                b,
288                RgbaColorType {
289                    r: 0.5,
290                    g: 0.5,
291                    b: 0.5,
292                    a: 1.0
293                }
294            ) < 0.005
295        );
296    }
297    #[test]
298    fn test_into() {
299        let a = RgbaColorType::new(1.0, 0.5, 0.25);
300        let arr: [u8; 3] = a.into();
301        assert_eq!(arr, [255, 128, 64]);
302    }
303    #[test]
304    fn test_conv() {
305        let a = RgbaColorType::new(1.0, 0.5, 0.25);
306        let b: HslaColorType = a.into();
307        assert_eq!(a, b.into());
308    }
309    fn error(c: RgbaColorType, d: RgbaColorType) -> f64 {
310        let e = c.r - d.r + c.g - d.g + c.b - d.b + c.a - d.a;
311        e / 4.0
312    }
313}