Skip to main content

embedded_rgba/
rgba.rs

1use embedded_graphics_core::pixelcolor::raw::*;
2use embedded_graphics_core::pixelcolor::*;
3
4/// Simple RGBA color wrapper.
5#[derive(Clone, Copy, Debug, PartialEq, Eq)]
6pub struct Rgba<C: RgbColor>(C, u8);
7
8#[inline(always)]
9fn mul_blend_u8(delta: u32, a: u32) -> u32 {
10    // Exact (delta * a) / 255 using the div255 trick (no slow integer division).
11    // Valid for 0..=65535 inputs; see Hacker's Delight 10-16.
12    let t = delta * a + 128;
13    (t + (t >> 8)) >> 8
14}
15
16impl<C: RgbColor> Rgba<C> {
17    /// Create a new RGBA color.
18    pub const fn new(color: C, alpha: u8) -> Self {
19        Self(color, alpha)
20    }
21
22    /// Get the color component.
23    pub const fn rgb(&self) -> C {
24        self.0
25    }
26
27    pub fn r(&self) -> u8 {
28        self.0.r()
29    }
30
31    pub fn g(&self) -> u8 {
32        self.0.g()
33    }
34
35    pub fn b(&self) -> u8 {
36        self.0.b()
37    }
38
39    /// Get the alpha component (0..=255).
40    pub const fn a(&self) -> u8 {
41        self.1
42    }
43}
44
45impl<C: RgbColor> PixelColor for Rgba<C> {
46    type Raw = C::Raw;
47}
48
49pub trait Blend<T> {
50    fn blend(&self, bg: T) -> T;
51}
52
53impl Blend<Rgb565> for Rgba<Rgb565> {
54    #[inline(always)]
55    fn blend(&self, bg: Rgb565) -> Rgb565 {
56        let a = self.a() as u32;
57        if a == 0 {
58            return bg;
59        }
60        if a == 255 {
61            return self.rgb();
62        }
63
64        let f = self.rgb().into_storage() as u32; // 0RRRRR GGGGGG BBBBB
65        let b = bg.into_storage() as u32;
66
67        let fr = (f >> 11) & 0x1F;
68        let fg = (f >> 5) & 0x3F;
69        let fb = f & 0x1F;
70
71        let br = (b >> 11) & 0x1F;
72        let bgc = (b >> 5) & 0x3F;
73        let bb = b & 0x1F;
74
75        // Blend in native bit depth (5/6/5) using exact div-by-255 trick.
76        let r = (br + mul_blend_u8(fr.wrapping_sub(br), a)) & 0x1F;
77        let g = (bgc + mul_blend_u8(fg.wrapping_sub(bgc), a)) & 0x3F;
78        let bl = (bb + mul_blend_u8(fb.wrapping_sub(bb), a)) & 0x1F;
79
80        let out = ((r << 11) | (g << 5) | bl) as u16;
81        Rgb565::from(RawU16::new(out))
82    }
83}
84
85impl Blend<Rgb888> for Rgba<Rgb888> {
86    #[inline(always)]
87    fn blend(&self, bg: Rgb888) -> Rgb888 {
88        let a = self.a() as u32;
89        if a == 0 {
90            return bg;
91        }
92        if a == 255 {
93            return self.rgb();
94        }
95
96        let fr = self.rgb().r() as u32;
97        let fg = self.rgb().g() as u32;
98        let fb = self.rgb().b() as u32;
99
100        let br = bg.r() as u32;
101        let bgc = bg.g() as u32;
102        let bb = bg.b() as u32;
103
104        let r = (br + mul_blend_u8(fr.wrapping_sub(br), a)) as u8;
105        let g = (bgc + mul_blend_u8(fg.wrapping_sub(bgc), a)) as u8;
106        let b = (bb + mul_blend_u8(fb.wrapping_sub(bb), a)) as u8;
107
108        Rgb888::new(r, g, b)
109    }
110}
111
112impl Blend<Rgb666> for Rgba<Rgb666> {
113    #[inline(always)]
114    fn blend(&self, bg: Rgb666) -> Rgb666 {
115        let a = self.a() as u32;
116        if a == 0 {
117            return bg;
118        }
119        if a == 255 {
120            return self.rgb();
121        }
122
123        let fr = self.rgb().r() as u32; // 0..63
124        let fg = self.rgb().g() as u32; // 0..63
125        let fb = self.rgb().b() as u32; // 0..63
126
127        let br = bg.r() as u32;
128        let bgc = bg.g() as u32;
129        let bb = bg.b() as u32;
130
131        let r = (br + mul_blend_u8(fr.wrapping_sub(br), a)) as u8; // 0..63
132        let g = (bgc + mul_blend_u8(fg.wrapping_sub(bgc), a)) as u8;
133        let b = (bb + mul_blend_u8(fb.wrapping_sub(bb), a)) as u8;
134
135        Rgb666::new(r, g, b)
136    }
137}