Skip to main content

primitives/foundation/colorspace/
base.rs

1use std::{default, fmt};
2
3use crate::prelude::color;
4
5use super::prelude::*;
6
7use super::*;
8
9/// Basic color representation
10#[derive(Clone, Copy, PartialEq, Debug)] // Eq, Hash 
11#[repr(C)]
12pub struct Color {
13    /// Red component
14    pub red: Float,
15    /// Green component
16    pub green: Float,
17    /// Blue component
18    pub blue: Float,
19    /// Alpha component
20    pub alpha: Float,
21}
22
23impl Color {
24    /// Create color with floating point components
25    pub fn new(red: Float, green: Float, blue: Float, alpha: Float) -> Self {
26        Color {
27            red,
28            green,
29            blue,
30            alpha,
31        }
32    }
33
34    /// Create solid color with byte components
35    pub fn rgb(red: u8, green: u8, blue: u8) -> Self {
36        Self::new(
37            red as Float / 255.,
38            green as Float / 255.,
39            blue as Float / 255.,
40            1.,
41        )
42    }
43
44    /// Create color with byte components
45    pub fn rgba(red: u8, green: u8, blue: u8, alpha: u8) -> Self {
46        Self::new(
47            red as Float / 255.,
48            green as Float / 255.,
49            blue as Float / 255.,
50            alpha as Float / 255.,
51        )
52    }
53
54    /// Create solid color with using hsl color space
55    pub fn hsl(hue: Float, saturation: Float, lightness: Float) -> Self {
56        Self::from_color(HslColor::new(hue, saturation, lightness))
57    }
58
59    /// Create solid color with using hsl color space and alpha component
60    pub fn hsla(hue: Float, saturation: Float, lightness: Float, alpha: Float) -> Self {
61        let mut color = Self::from_color(HslColor::new(hue, saturation, lightness));
62        color.alpha = alpha;
63        color
64    }
65
66    /// Create solid color with using hsv color space
67    pub fn hsv(hue: Float, saturation: Float, value: Float) -> Self {
68        Self::from_color(HslColor::new(hue, saturation, value))
69    }
70
71    /// Create solid color with using hsv color space and alpha component
72    pub fn hsva(hue: Float, saturation: Float, value: Float, alpha: Float) -> Self {
73        let mut color = Self::from_color(HslColor::new(hue, saturation, value));
74        color.alpha = alpha;
75        color
76    }
77
78    /// Create solid color with using cmyk color space
79    pub fn cmyk(cyan: Float, magenta: Float, yellow: Float, key: Float) -> Self {
80        Self::from_color(CmykColor::new(cyan, magenta, yellow, key))
81    }
82    
83    /// Create solid color with using cmyk color space and alpha component
84    pub fn cmyka(cyan: Float, magenta: Float, yellow: Float, key: Float, alpha: Float) -> Self {
85        let mut color = Self::from_color(CmykColor::new(cyan, magenta, yellow, key));
86        color.alpha = alpha;
87        color
88    }
89
90    /// Create solid color with using cmy color space
91    pub fn cmy(cyan: Float, magenta: Float, yellow: Float) -> Self {
92        Self::from_color(CmyColor::new(cyan, magenta, yellow))
93    }
94    
95    /// Create solid color with using cmy color space and alpha component
96    pub fn cmya(cyan: Float, magenta: Float, yellow: Float, alpha: Float) -> Self {
97        let mut color = Self::from_color(CmyColor::new(cyan, magenta, yellow));
98        color.alpha = alpha;
99        color
100    }
101
102    /// Create solid color with using xyz color space
103    #[cfg(feature = "experimental")]
104    pub fn xyz(x: Float, y: Float, z: Float) -> Self {
105        Self::from_color(XyzColor::new(x, y, z))
106    }
107    
108    /// Create solid color with using xyz color space and alpha component
109    #[cfg(feature = "experimental")]
110    pub fn xyza(x: Float, y: Float, z: Float, alpha: Float) -> Self {
111        let mut color = Self::from_color(XyzColor::new(x, y, z));
112        color.alpha = alpha;
113        color
114    }
115
116    /// Create solid color with using lab color space
117    #[cfg(feature = "experimental")]
118    pub fn lab(l: Float, a: Float, b: Float) -> Self {
119        Self::from_color(XyzColor::new(l, a, b))
120    }
121    
122    /// Create solid color with using lab color space and alpha component
123    #[cfg(feature = "experimental")]
124    pub fn laba(l: Float, a: Float, b: Float, alpha: Float) -> Self {
125        let mut color = Self::from_color(XyzColor::new(l, a, b));
126        color.alpha = alpha;
127        color
128    }
129
130    // EMULATE creation of unicolor::Color enum
131
132    /// Create solid color with using rgb color space
133    #[allow(non_snake_case)]
134    #[deprecated]
135    pub fn RGB(red: u8, green: u8, blue: u8) -> Self {
136        Self::rgb(red, green, blue)
137    }
138
139    /// Create solid color with using lab rgb space and alpha component
140    #[allow(non_snake_case)]
141    #[deprecated]
142    pub fn RGBA(red: u8, green: u8, blue: u8, alpha: u8) -> Self {
143        Self::rgba(red, green, blue, alpha)
144    }
145
146    /// Create solid color with using hsl color space
147    #[allow(non_snake_case)]
148    #[deprecated]
149    pub fn HSL(hue: Float, saturation: Float, lightness: Float) -> Self {
150        Self::hsl(hue, saturation, lightness)
151    }
152
153    /// Create solid color with using hsv color space
154    #[allow(non_snake_case)]
155    #[deprecated]
156    pub fn HSV(hue: Float, saturation: Float, value: Float) -> Self {
157        Self::hsv(hue, saturation, value)
158    }
159
160    /// Create solid color with using cmyk color space
161    #[allow(non_snake_case)]
162    #[deprecated]
163    pub fn CMYK(cyan: Float, magenta: Float, yellow: Float, key: Float) -> Self {
164        Self::cmyk(cyan, magenta, yellow, key)
165    }
166
167    /// Create solid color with using cmy color space
168    #[allow(non_snake_case)]
169    #[deprecated]
170    pub fn CMY(cyan: Float, magenta: Float, yellow: Float) -> Self {
171        Self::cmy(cyan, magenta, yellow)
172    }
173
174    /// Create solid color with using xyz color space
175    #[cfg(feature = "experimental")]
176    #[allow(non_snake_case)]
177    #[deprecated]
178    pub fn XYZ(x: Float, y: Float, z: Float) -> Self {
179        Self::xyz(x, y, z)
180    }
181
182    /// Create solid color with using lab color space
183    #[cfg(feature = "experimental")]
184    #[allow(non_snake_case)]
185    #[deprecated]
186    pub fn LAB(l: Float, a: Float, b: Float) -> Self {
187        Self::lab(l, a, b)
188    }
189}
190
191impl fmt::Display for Color {
192    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
193        write!(f, "rgb({}, {}, {})", self.red, self.green, self.blue)
194    }
195}
196
197impl default::Default for Color {
198    fn default() -> Self {
199        color::BLACK
200    }
201}
202
203
204/// Represents color error
205#[derive(Debug)]
206pub enum ColorError {
207    /// Percentage overflow error
208    PercentageOverflow,
209    /// Degree overflow error
210    DegreeOverflow,
211    /// Unimplementer error
212    Unimplemented,
213}
214
215impl fmt::Display for ColorError {
216    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
217        match self {
218            Self::PercentageOverflow => write!(
219                f,
220                "Overflow of Color percentage value (can't be greater than 100%)"
221            ),
222            Self::DegreeOverflow => write!(
223                f,
224                "Overflow of Hue in hsl(v) color space (can't be greater than 360 deg"
225            ),
226            Self::Unimplemented => write!(f, "Unimplemented color conversion"),
227        }
228    }
229}
230
231#[cfg(any(feature = "color_quantization", test))]
232impl Color {
233    pub fn distance(&self, other: Self) -> f64 {
234        let RgbColor {
235            red: s_red,
236            green: s_green,
237            blue: s_blue,
238        } = (*self).into();
239        let RgbColor {
240            red: p_red,
241            green: p_green,
242            blue: p_blue,
243        } = other.into();
244        (((p_red as i32 - s_red as i32).pow(2)
245            + (p_green as i32 - s_green as i32).pow(2)
246            + (p_blue as i32 - s_blue as i32).pow(2)) as f64)
247            .sqrt()
248            .abs()
249    }
250
251    pub fn quantize(&self) -> Self {
252        let mut min_color_distance =
253            ((0xFF_u32.pow(2) + 0xFF_u32.pow(2) + 0xFF_u32.pow(2)) as f64).sqrt();
254        let mut min_distance_color: Option<&Color> = None;
255        for color in color::PALETTE.iter() {
256            let color_distance = self.distance(*color);
257            if color_distance < min_color_distance {
258                min_color_distance = color_distance;
259                min_distance_color = Some(color);
260            }
261        }
262        *min_distance_color.expect("In this palette not found color which distance is smaller than distance from black to white")
263    }
264}
265
266#[cfg(test)]
267pub mod test {
268    use super::super::RgbColor;
269    use super::*;
270    use math::round::stochastic;
271
272    #[test]
273    fn calc_distance() {
274        //println!("distance: YELLOW_0 -> LINE_0 = {}", YELLOW_0.distance(LIME_0));
275        assert_eq!(
276            stochastic(color::YELLOW_0.distance(color::LIME_0), 12),
277            13.928388277184
278        );
279
280        let stochastic_scale = 10;
281        for delta in [2u8, 3u8, 4u8].iter() {
282            let delta = *delta;
283            for src_color in [
284                color::TEAL_1,
285                color::TEAL_2,
286                color::TEAL_3,
287                color::TEAL_4,
288                color::TEAL_5,
289                color::TEAL_6,
290                color::TEAL_7,
291                color::TEAL_8,
292            ]
293            .iter()
294            {
295                let RgbColor {
296                    red: s_red,
297                    green: s_green,
298                    blue: s_blue,
299                } = (*src_color).into();
300                let dst_color = Color::rgb(s_red + delta, s_green + delta, s_blue + delta);
301                assert_eq!(
302                    stochastic(src_color.distance(dst_color), stochastic_scale),
303                    stochastic(
304                        (((delta as i32).pow(2) + (delta as i32).pow(2) + (delta as i32).pow(2))
305                            as f64)
306                            .sqrt(),
307                        stochastic_scale
308                    )
309                )
310            }
311        }
312    }
313
314    #[test]
315    fn quantization() {
316        for delta in [2u8, 3u8, 4u8].iter() {
317            let delta = *delta;
318            for palette_color in [
319                color::CYAN_2,
320                color::CYAN_3,
321                color::CYAN_4,
322                color::CYAN_5,
323                color::CYAN_6,
324                color::CYAN_7,
325            ]
326            .iter()
327            {
328                let RgbColor {
329                    red: p_red,
330                    green: p_green,
331                    blue: p_blue,
332                } = (*palette_color).into();
333                let test_color = Color::rgb(p_red + delta, p_green + delta, p_blue + delta);
334                // println!("test_color = {}", test_color.to_hex_string());
335                let found_color = test_color.quantize();
336                // println!("test_color = {}; found_color = {} == {} ?",
337                //          test_color.to_hex_string(),
338                //          found_color.to_hex_string(),
339                //          palette_color.to_hex_string());
340                let RgbColor {
341                    red: f_red,
342                    green: f_green,
343                    blue: f_blue,
344                } = found_color.into();
345                assert_eq!(f_red, p_red);
346                assert_eq!(f_green, p_green);
347                assert_eq!(f_blue, p_blue);
348            }
349        }
350    }
351}