Skip to main content

firefly_runtime/
color.rs

1use embedded_graphics::pixelcolor::raw::RawU16;
2use embedded_graphics::pixelcolor::*;
3
4/// Color optimized for fast rendering on the device.
5#[derive(Eq, PartialEq, Clone, Copy)]
6pub struct Rgb16(pub u8, pub u8);
7
8impl Rgb16 {
9    pub const fn from_rgb(r: u16, g: u16, b: u16) -> Self {
10        let r = r >> 3;
11        let g = g >> 2;
12        let b = b >> 3;
13        let raw = (b << 11) | ((g & 0b_0011_1111) << 5) | (r & 0b_0001_1111);
14        let raw = raw.to_le_bytes();
15        Self(!raw[0], !raw[1])
16    }
17
18    pub const fn into_rgb(self) -> (u8, u8, u8) {
19        let raw = u16::from_le_bytes([!self.0, !self.1]);
20        let r = (raw << 3) as u8;
21        let g = ((raw >> 5) << 2) as u8;
22        let b = ((raw >> 11) << 3) as u8;
23        (r, g, b)
24    }
25}
26
27impl PixelColor for Rgb16 {
28    type Raw = RawU16;
29}
30
31impl RgbColor for Rgb16 {
32    const MAX_R: u8 = 32;
33    const MAX_G: u8 = 64;
34    const MAX_B: u8 = 32;
35    const BLACK: Self = Self::from_rgb(0, 0, 0);
36    const RED: Self = Self::from_rgb(255, 0, 0);
37    const GREEN: Self = Self::from_rgb(0, 255, 0);
38    const BLUE: Self = Self::from_rgb(0, 0, 255);
39    const YELLOW: Self = Self::from_rgb(255, 255, 0);
40    const MAGENTA: Self = Self::from_rgb(255, 0, 255);
41    const CYAN: Self = Self::from_rgb(0, 255, 255);
42    const WHITE: Self = Self::from_rgb(255, 255, 255);
43
44    fn r(&self) -> u8 {
45        let (r, _, _) = self.into_rgb();
46        r
47    }
48
49    fn g(&self) -> u8 {
50        let (_, g, _) = self.into_rgb();
51        g
52    }
53
54    fn b(&self) -> u8 {
55        let (_, _, b) = self.into_rgb();
56        b
57    }
58}
59
60impl From<Rgb888> for Rgb16 {
61    fn from(c: Rgb888) -> Self {
62        let r = u16::from(c.r());
63        let g = u16::from(c.g());
64        let b = u16::from(c.b());
65        Self::from_rgb(r, g, b)
66    }
67}
68
69impl From<Rgb16> for Rgb888 {
70    fn from(c: Rgb16) -> Self {
71        let (mut r, mut g, mut b) = c.into_rgb();
72        if r == 0b1111_1000 {
73            r = 0xff;
74        }
75        if g == 0b1111_1100 {
76            g = 0xff;
77        }
78        if b == 0b1111_1000 {
79            b = 0xff;
80        }
81        Self::new(r, g, b)
82    }
83}
84
85/// Create RGB (or BGR) color from R, G, and B components in 0-255 range.
86pub trait FromRGB {
87    /// The white background color.
88    const BG: Self;
89    /// The primary black text color.
90    const PRIMARY: Self;
91    /// The dark green accent color.
92    const ACCENT: Self;
93    /// The dark red danger color.
94    const DANGER: Self;
95    /// The gray muted text color.
96    const MUTED: Self;
97
98    fn from_rgb(rgb: Rgb16) -> Self;
99}
100
101impl FromRGB for Rgb16 {
102    const BG: Self = Self::from_rgb(0xf4, 0xf4, 0xf4);
103    const PRIMARY: Self = Self::from_rgb(0x1a, 0x1c, 0x2c);
104    const ACCENT: Self = Self::from_rgb(0x38, 0xb7, 0x64);
105    const DANGER: Self = Self::from_rgb(0xb1, 0x3e, 0x53);
106    const MUTED: Self = Self::from_rgb(0x94, 0xb0, 0xc2);
107
108    fn from_rgb(rgb: Self) -> Self {
109        rgb
110    }
111}
112
113impl FromRGB for Rgb565 {
114    const BG: Self = new_rgb565(0xf4, 0xf4, 0xf4);
115    const PRIMARY: Self = new_rgb565(0x1a, 0x1c, 0x2c);
116    const ACCENT: Self = new_rgb565(0x38, 0xb7, 0x64);
117    const DANGER: Self = new_rgb565(0xb1, 0x3e, 0x53);
118    const MUTED: Self = new_rgb565(0x94, 0xb0, 0xc2);
119
120    fn from_rgb(rgb: Rgb16) -> Self {
121        let (r, g, b) = rgb.into_rgb();
122        Self::new(r, g, b)
123    }
124}
125
126impl FromRGB for Rgb888 {
127    const BG: Self = Self::new(0xf4, 0xf4, 0xf4);
128    const PRIMARY: Self = Self::new(0x1a, 0x1c, 0x2c);
129    const ACCENT: Self = Self::new(0x38, 0xb7, 0x64);
130    const DANGER: Self = Self::new(0xb1, 0x3e, 0x53);
131    const MUTED: Self = Self::new(0x94, 0xb0, 0xc2);
132
133    fn from_rgb(rgb: Rgb16) -> Self {
134        rgb.into()
135    }
136}
137
138const fn new_rgb565(r: u8, g: u8, b: u8) -> Rgb565 {
139    let r = r as u32 * Rgb565::MAX_R as u32 / Rgb888::MAX_R as u32;
140    let g = g as u32 * Rgb565::MAX_G as u32 / Rgb888::MAX_G as u32;
141    let b = b as u32 * Rgb565::MAX_B as u32 / Rgb888::MAX_B as u32;
142    debug_assert!(r < 256);
143    debug_assert!(g < 256);
144    debug_assert!(b < 256);
145    Rgb565::new(r as u8, g as u8, b as u8)
146}