typistapp/
color.rs

1/// A utility struct for color-related operations.
2pub struct Color {}
3
4impl Color {
5    /// Calculates the luminance of an RGBA color.
6    ///
7    /// # Arguments
8    ///
9    /// * `rgba` - A reference to a 4-element array representing a color in RGBA format (0–255 range).
10    ///
11    /// # Returns
12    ///
13    /// * A `f64` value representing the luminance (brightness) of the color, normalized to the 0.0–1.0 range.
14    ///
15    /// # Example
16    ///
17    /// ```no_run
18    /// use typistapp::color::Color;
19    ///
20    /// let luminance = Color::luminance_from_rgba(&[255, 255, 255, 255]);
21    /// assert!(luminance > 0.9);
22    /// ```
23    pub fn luminance_from_rgba(rgba: &[u8; 4]) -> f64 {
24        let r = rgba[0] as f64 / 255.0;
25        let g = rgba[1] as f64 / 255.0;
26        let b = rgba[2] as f64 / 255.0;
27
28        let yuv = Self::convert_rgb_to_yuv(r, g, b);
29        Self::luminance_from_yuv(&yuv)
30    }
31
32    /// Converts an RGB color to YUV color space.
33    ///
34    /// # Arguments
35    ///
36    /// * `r` - Red component (0.0–1.0).
37    /// * `g` - Green component (0.0–1.0).
38    /// * `b` - Blue component (0.0–1.0).
39    ///
40    /// # Returns
41    ///
42    /// * A `[f64; 3]` array where the elements represent the Y (luminance), U, and V components respectively.
43    pub fn convert_rgb_to_yuv(r: f64, g: f64, b: f64) -> [f64; 3] {
44        let y = 0.299 * r + 0.587 * g + 0.114 * b;
45        let u = -0.169 * r - 0.331 * g + 0.500 * b;
46        let v = 0.500 * r - 0.419 * g - 0.081 * b;
47        [y, u, v]
48    }
49
50    /// Extracts the luminance component (Y) from a YUV color.
51    ///
52    /// # Arguments
53    ///
54    /// * `yuv` - A reference to a 3-element array representing a YUV color.
55    ///
56    /// # Returns
57    ///
58    /// * A `f64` value representing the luminance.
59    pub fn luminance_from_yuv(yuv: &[f64; 3]) -> f64 {
60        yuv[0] // Y component represents luminance
61    }
62}
63
64#[cfg(test)]
65mod tests {
66    use super::Color;
67
68    #[test]
69    fn luminance_black() {
70        let rgba = [0, 0, 0, 255]; // black
71        let lum = Color::luminance_from_rgba(&rgba);
72        assert!((lum - 0.0).abs() < 1e-6);
73    }
74
75    #[test]
76    fn luminance_white() {
77        let rgba = [255, 255, 255, 255]; // white
78        let lum = Color::luminance_from_rgba(&rgba);
79        assert!((lum - 1.0).abs() < 1e-6);
80    }
81
82    #[test]
83    fn luminance_gray() {
84        let rgba = [128, 128, 128, 255];
85        let lum = Color::luminance_from_rgba(&rgba);
86        // Expected ~0.502, allow small margin
87        assert!((lum - 0.502).abs() < 0.01);
88    }
89
90    #[test]
91    fn luminance_red() {
92        let rgba = [255, 0, 0, 255];
93        let lum = Color::luminance_from_rgba(&rgba);
94        // Expected ~0.299
95        assert!((lum - 0.299).abs() < 0.01);
96    }
97
98    #[test]
99    fn luminance_green() {
100        let rgba = [0, 255, 0, 255];
101        let lum = Color::luminance_from_rgba(&rgba);
102        // Expected ~0.587
103        assert!((lum - 0.587).abs() < 0.01);
104    }
105
106    #[test]
107    fn luminance_blue() {
108        let rgba = [0, 0, 255, 255];
109        let lum = Color::luminance_from_rgba(&rgba);
110        // Expected ~0.114
111        assert!((lum - 0.114).abs() < 0.01);
112    }
113
114    #[test]
115    fn convert_rgb_to_yuv() {
116        let yuv = Color::convert_rgb_to_yuv(1.0, 0.0, 0.0); // pure red
117        // Y ≈ 0.299
118        assert!((yuv[0] - 0.299).abs() < 0.01);
119        // U and V can vary; check rough expected signs
120        assert!(yuv[1] < 0.0);
121        assert!(yuv[2] > 0.0);
122    }
123
124    #[test]
125    fn luminance_from_yuv_direct() {
126        let yuv = [0.42, 0.1, -0.1];
127        let lum = Color::luminance_from_yuv(&yuv);
128        assert!((lum - 0.42).abs() < 1e-6);
129    }
130}