Skip to main content

cranpose_render_common/
rgb565.rs

1//! Pixels of 16 bits, 5 of red, 6 of green and 5 of blue, as most small
2//! displays take them, stored in either byte order.
3
4use crate::byte_blend::{blend_byte_pixel, mix_byte};
5
6/// A 16-bit pixel stored with its low byte first.
7#[repr(transparent)]
8#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, bytemuck::Pod, bytemuck::Zeroable)]
9pub struct Rgb565Le(pub u16);
10
11/// A 16-bit pixel stored with its high byte first, as displays on an SPI bus
12/// read it.
13#[repr(transparent)]
14#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, bytemuck::Pod, bytemuck::Zeroable)]
15pub struct Rgb565Be(pub u16);
16
17/// The order in which a display takes the two bytes of a 16-bit pixel.
18#[derive(Clone, Copy, Debug, PartialEq, Eq)]
19pub enum Rgb565Order {
20    /// The low byte first, as a little-endian CPU stores a `u16`.
21    LittleEndian,
22    /// The high byte first, as most displays on an SPI bus read it.
23    BigEndian,
24}
25
26/// How a 16-bit pixel stores its color.
27pub trait Rgb565: Copy {
28    /// The pixel of the red, green and blue of `bytes`, each cut to its high
29    /// bits, as a display keeps a byte color.
30    fn from_rgb(bytes: [u8; 4]) -> Self;
31
32    /// The pixel's red, green and blue as bytes, each widened by repeating
33    /// its high bits, and an opaque alpha.
34    fn to_rgba(self) -> [u8; 4];
35}
36
37fn pack(bytes: [u8; 4]) -> u16 {
38    (u16::from(bytes[0] >> 3) << 11) | (u16::from(bytes[1] >> 2) << 5) | u16::from(bytes[2] >> 3)
39}
40
41fn unpack(value: u16) -> [u8; 4] {
42    let red = (value >> 11) as u8;
43    let green = ((value >> 5) & 0x3F) as u8;
44    let blue = (value & 0x1F) as u8;
45    [
46        (red << 3) | (red >> 2),
47        (green << 2) | (green >> 4),
48        (blue << 3) | (blue >> 2),
49        255,
50    ]
51}
52
53impl Rgb565 for Rgb565Le {
54    fn from_rgb(bytes: [u8; 4]) -> Self {
55        Self(pack(bytes).to_le())
56    }
57
58    fn to_rgba(self) -> [u8; 4] {
59        unpack(u16::from_le(self.0))
60    }
61}
62
63impl Rgb565 for Rgb565Be {
64    fn from_rgb(bytes: [u8; 4]) -> Self {
65        Self(pack(bytes).to_be())
66    }
67
68    fn to_rgba(self) -> [u8; 4] {
69        unpack(u16::from_be(self.0))
70    }
71}
72
73/// A pixel that a solid glyph paints over.
74pub trait GlyphPixel: Copy {
75    /// Puts `src`, straight-alpha bytes, over the pixel, source over.
76    fn blend_bytes(&mut self, src: [u8; 4]);
77}
78
79impl GlyphPixel for [u8; 4] {
80    fn blend_bytes(&mut self, src: [u8; 4]) {
81        blend_byte_pixel(self, src);
82    }
83}
84
85// A 16-bit pixel blends as its bytes would, then keeps their high bits. It
86// has no alpha, so the blend leaves the alpha byte out.
87#[inline]
88fn blend_bytes_565<P: Rgb565>(pixel: &mut P, src: [u8; 4]) {
89    match src[3] {
90        0 => {}
91        255 => *pixel = P::from_rgb(src),
92        alpha => {
93            let under = pixel.to_rgba();
94            let alpha = u32::from(alpha);
95            *pixel = P::from_rgb([
96                mix_byte(src[0], under[0], alpha),
97                mix_byte(src[1], under[1], alpha),
98                mix_byte(src[2], under[2], alpha),
99                255,
100            ]);
101        }
102    }
103}
104
105impl GlyphPixel for Rgb565Le {
106    #[inline]
107    fn blend_bytes(&mut self, src: [u8; 4]) {
108        blend_bytes_565(self, src);
109    }
110}
111
112impl GlyphPixel for Rgb565Be {
113    #[inline]
114    fn blend_bytes(&mut self, src: [u8; 4]) {
115        blend_bytes_565(self, src);
116    }
117}
118
119#[cfg(test)]
120#[path = "tests/rgb565_tests.rs"]
121mod tests;