Skip to main content

proka_kernel/graphics/
color.rs

1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2pub struct Color {
3    pub r: u8,
4    pub g: u8,
5    pub b: u8,
6    pub a: u8,
7}
8
9impl Color {
10    /// Creates a new Color with the specified RGB values and default alpha (255)
11    pub const fn new(r: u8, g: u8, b: u8) -> Self {
12        Self { r, g, b, a: 255 }
13    }
14
15    /// Creates a new Color with the specified RGBA values
16    pub const fn with_alpha(r: u8, g: u8, b: u8, a: u8) -> Self {
17        Self { r, g, b, a }
18    }
19
20    pub fn from_hex(hex: &str) -> Self {
21        let hex = hex.trim_start_matches('#');
22        let r = u8::from_str_radix(&hex[0..2], 16).unwrap_or(255);
23        let g = u8::from_str_radix(&hex[2..4], 16).unwrap_or(255);
24        let b = u8::from_str_radix(&hex[4..6], 16).unwrap_or(255);
25
26        if hex.len() == 8 {
27            let a = u8::from_str_radix(&hex[6..8], 16).unwrap_or(255);
28            Self::with_alpha(r, g, b, a)
29        } else {
30            Self::new(r, g, b)
31        }
32    }
33
34    pub fn to_u32(&self, include_alpha: bool) -> u32 {
35        let r = self.r as u32;
36        let g = self.g as u32;
37        let b = self.b as u32;
38        if include_alpha {
39            let a = self.a as u32;
40            return (a << 24) | (r << 16) | (g << 8) | b;
41        }
42        (r << 24) | (g << 16) | (b << 8)
43    }
44
45    pub fn from_u32(color: u32) -> Self {
46        let r = (color >> 24) & 0xFF;
47        let g = (color >> 16) & 0xFF;
48        let b = (color >> 8) & 0xFF;
49        Self::new(r as u8, g as u8, b as u8)
50    }
51
52    pub fn mix_alpha(&self, alpha: u8) -> Self {
53        Self::with_alpha(self.r, self.g, self.b, alpha)
54    }
55
56    pub fn mix(&self, other: &Color, alpha: u8) -> Self {
57        let r =
58            ((self.r as u16 * alpha as u16 + other.r as u16 * (255 - alpha) as u16) / 255) as u8;
59        let g =
60            ((self.g as u16 * alpha as u16 + other.g as u16 * (255 - alpha) as u16) / 255) as u8;
61        let b =
62            ((self.b as u16 * alpha as u16 + other.b as u16 * (255 - alpha) as u16) / 255) as u8;
63        let a = alpha;
64        Self { r, g, b, a }
65    }
66    pub fn invert(&self) -> Color {
67        Color::new(255 - self.r, 255 - self.g, 255 - self.b)
68    }
69}
70
71#[macro_export]
72macro_rules! color {
73    // RGB 格式 (自动填充 alpha=255)
74    ($r:expr, $g:expr, $b:expr) => {
75        Color::new($r as u8, $g as u8, $b as u8)
76    };
77
78    // RGBA 格式
79    ($r:expr, $g:expr, $b:expr, $a:expr) => {
80        Color::with_alpha($r as u8, $g as u8, $b as u8, $a as u8)
81    };
82
83    // 十六进制颜色码 (支持 #RRGGBB 和 #RRGGBBAA)
84    (#$hex:expr) => {
85        Color::from_hex($hex)
86    };
87}
88
89// 基础颜色
90pub const BLACK: Color = color!(0, 0, 0);
91pub const WHITE: Color = color!(255, 255, 255);
92pub const RED: Color = color!(255, 0, 0);
93pub const GREEN: Color = color!(0, 255, 0);
94pub const BLUE: Color = color!(0, 0, 255);
95// 实用混合色
96pub const YELLOW: Color = color!(255, 255, 0); // 红+绿
97pub const CYAN: Color = color!(0, 255, 255); // 绿+蓝
98pub const MAGENTA: Color = color!(255, 0, 255); // 红+蓝
99                                                // 中性色
100pub const GRAY: Color = color!(128, 128, 128);
101
102#[cfg(test)]
103mod tests {
104    use super::*;
105
106    #[test_case]
107    fn test_color_from_hex_rgb() {
108        let c = Color::from_hex("#ff8040");
109        assert_eq!(c.r, 255);
110        assert_eq!(c.g, 128);
111        assert_eq!(c.b, 64);
112        assert_eq!(c.a, 255);
113    }
114
115    #[test_case]
116    fn test_color_from_hex_rgba() {
117        let c = Color::from_hex("#ff804080");
118        assert_eq!(c.r, 255);
119        assert_eq!(c.g, 128);
120        assert_eq!(c.b, 64);
121        assert_eq!(c.a, 128);
122    }
123
124    #[test_case]
125    fn test_color_from_hex_without_hash() {
126        let c = Color::from_hex("ff8040");
127        assert_eq!(c.r, 255);
128        assert_eq!(c.g, 128);
129        assert_eq!(c.b, 64);
130    }
131
132    #[test_case]
133    fn test_color_from_hex_invalid() {
134        let c = Color::from_hex("invalid");
135        assert_eq!(c.r, 255);
136        assert_eq!(c.g, 255);
137        assert_eq!(c.b, 255);
138    }
139
140    #[test_case]
141    fn test_color_to_u32_with_alpha() {
142        let c = Color::with_alpha(0x12, 0x34, 0x56, 0x78);
143        let val = c.to_u32(true);
144        assert_eq!(val, 0x78123456);
145    }
146
147    #[test_case]
148    fn test_color_to_u32_without_alpha() {
149        let c = Color::new(0x12, 0x34, 0x56);
150        let val = c.to_u32(false);
151        assert_eq!(val, 0x12345600);
152    }
153
154    #[test_case]
155    fn test_color_from_u32() {
156        let val = 0x12345600;
157        let c = Color::from_u32(val);
158        assert_eq!(c.r, 0x12);
159        assert_eq!(c.g, 0x34);
160        assert_eq!(c.b, 0x56);
161    }
162
163    #[test_case]
164    fn test_color_mix_alpha() {
165        let c = Color::new(255, 128, 64);
166        let c = c.mix_alpha(128);
167        assert_eq!(c.r, 255);
168        assert_eq!(c.g, 128);
169        assert_eq!(c.b, 64);
170        assert_eq!(c.a, 128);
171    }
172
173    #[test_case]
174    fn test_color_mix_half() {
175        let c1 = Color::new(255, 0, 0);
176        let c2 = Color::new(0, 255, 0);
177        let result = c1.mix(&c2, 128);
178        assert_eq!(result.r, 128);
179        assert_eq!(result.g, 127);
180        assert_eq!(result.b, 0);
181    }
182
183    #[test_case]
184    fn test_color_mix_full_first() {
185        let c1 = Color::new(255, 0, 0);
186        let c2 = Color::new(0, 255, 0);
187        let result = c1.mix(&c2, 255);
188        assert_eq!(result.r, 255);
189        assert_eq!(result.g, 0);
190        assert_eq!(result.b, 0);
191    }
192
193    #[test_case]
194    fn test_color_mix_full_second() {
195        let c1 = Color::new(255, 0, 0);
196        let c2 = Color::new(0, 255, 0);
197        let result = c1.mix(&c2, 0);
198        assert_eq!(result.r, 0);
199        assert_eq!(result.g, 255);
200        assert_eq!(result.b, 0);
201    }
202
203    #[test_case]
204    fn test_color_invert() {
205        let c = Color::new(0, 128, 255);
206        let inverted = c.invert();
207        assert_eq!(inverted.r, 255);
208        assert_eq!(inverted.g, 127);
209        assert_eq!(inverted.b, 0);
210    }
211
212    #[test_case]
213    fn test_color_invert_white() {
214        let c = Color::new(255, 255, 255);
215        let inverted = c.invert();
216        assert_eq!(inverted, BLACK);
217    }
218
219    #[test_case]
220    fn test_color_invert_black() {
221        let c = Color::new(0, 0, 0);
222        let inverted = c.invert();
223        assert_eq!(inverted, WHITE);
224    }
225}