Skip to main content

minifb_ui/color/
mod.rs

1/// RGBA color struct
2#[derive(Clone, Copy)]
3pub struct Color {
4    pub r: u8,
5    pub g: u8,
6    pub b: u8,
7    pub a: u8,
8}
9
10impl Default for Color {
11    fn default() -> Self {
12        Self { r: 0, g: 0, b: 0, a: 255 }
13    }
14}
15
16impl Color {
17    /// Returns a u32 representing its color as RGB (ignores alpha)
18    pub fn as_u32(&self) -> u32 {
19        (self.r as u32) << 16 | (self.g as u32) << 8 | (self.b as u32)
20    }
21
22    /// Creates a new opaque color from RGB values
23    pub fn new(r: u8, g: u8, b: u8) -> Self {
24        Self { r, g, b, a: 255 }
25    }
26
27    /// Creates a new color with alpha
28    pub fn rgba(r: u8, g: u8, b: u8, a: u8) -> Self {
29        Self { r, g, b, a }
30    }
31
32    /// Linearly interpolates between two colors by t (0.0 = self, 1.0 = other)
33    pub fn lerp(&self, other: &Color, t: f32) -> Color {
34        let t = t.clamp(0.0, 1.0);
35        let inv = 1.0 - t;
36        Color {
37            r: (self.r as f32 * inv + other.r as f32 * t) as u8,
38            g: (self.g as f32 * inv + other.g as f32 * t) as u8,
39            b: (self.b as f32 * inv + other.b as f32 * t) as u8,
40            a: (self.a as f32 * inv + other.a as f32 * t) as u8,
41        }
42    }
43
44    /// Returns the complementary (inverse) color
45    pub fn complement(&self) -> Color {
46        Color { r: 255 - self.r, g: 255 - self.g, b: 255 - self.b, a: self.a }
47    }
48
49    /// Parses a hex color string like "#RRGGBB" or "RRGGBB"
50    pub fn from_hex(s: &str) -> Option<Color> {
51        let s = s.trim_start_matches('#');
52        if s.len() != 6 { return None; }
53        let r = u8::from_str_radix(&s[0..2], 16).ok()?;
54        let g = u8::from_str_radix(&s[2..4], 16).ok()?;
55        let b = u8::from_str_radix(&s[4..6], 16).ok()?;
56        Some(Color::new(r, g, b))
57    }
58
59    /// Creates a color from HSV values (h: 0.0–360.0, s: 0.0–1.0, v: 0.0–1.0)
60    pub fn from_hsv(h: f32, s: f32, v: f32) -> Color {
61        let s = s.clamp(0.0, 1.0);
62        let v = v.clamp(0.0, 1.0);
63        let h = ((h % 360.0) + 360.0) % 360.0;
64        let c = v * s;
65        let x = c * (1.0 - ((h / 60.0) % 2.0 - 1.0).abs());
66        let m = v - c;
67        let (r, g, b) = match h as u32 {
68            0..60 => (c, x, 0.0),
69            60..120 => (x, c, 0.0),
70            120..180 => (0.0, c, x),
71            180..240 => (0.0, x, c),
72            240..300 => (x, 0.0, c),
73            _ => (c, 0.0, x),
74        };
75        Color::new(
76            ((r + m) * 255.0) as u8,
77            ((g + m) * 255.0) as u8,
78            ((b + m) * 255.0) as u8,
79        )
80    }
81}
82
83impl From<u32> for Color {
84    fn from(color: u32) -> Self {
85        Self {
86            r: ((color >> 16) & 0xFF) as u8,
87            g: ((color >> 8)  & 0xFF) as u8,
88            b: (color & 0xFF) as u8,
89            a: 255,
90        }
91    }
92}