embedded_graphics_framebuf/
backends.rs

1//! Backends for a framebuffer.
2//!
3//! One could use a simple array of [`PixelColor`], or some more elaborate proxy
4//! backends.
5//!
6//! Example:
7//! ```rust
8//! use embedded_graphics::{pixelcolor::Rgb565, prelude::RgbColor};
9//! use embedded_graphics_framebuf::{
10//!     backends::{EndianCorrectedBuffer, EndianCorrection},
11//!     FrameBuf,
12//! };
13//!
14//! // A potential backend
15//! let mut data = [Rgb565::BLACK; 12 * 11];
16//! let mut fbuf = FrameBuf::new(&mut data, 12, 11);
17//!
18//! // Another potential backend that is owned by the framebuffer
19//! let mut fbuf = FrameBuf::new([Rgb565::BLACK; 12 * 11], 12, 11);
20//!
21//! let mut fbuf = FrameBuf::new(
22//!     EndianCorrectedBuffer::new(&mut data, EndianCorrection::ToBigEndian),
23//!     12,
24//!     11,
25//! );
26//! ```
27
28use embedded_graphics::pixelcolor::{raw::RawU16, IntoStorage, PixelColor};
29
30/// This trait marks the requirements for backends for a
31/// [`FrameBuf`](crate::FrameBuf).
32///
33/// In a basic scenario this is just some memory.
34/// But one could implement more elaborate backends which allow manipulation of
35/// the data on the fly.
36pub trait FrameBufferBackend {
37    type Color: PixelColor;
38    /// Sets a pixel to the respective color
39    fn set(&mut self, index: usize, color: Self::Color);
40
41    /// Returns a pixels color
42    fn get(&self, index: usize) -> Self::Color;
43
44    /// Nr of elements in the backend
45    fn nr_elements(&self) -> usize;
46}
47
48impl<'a, C: PixelColor, const N: usize> FrameBufferBackend for &'a mut [C; N] {
49    type Color = C;
50    fn set(&mut self, index: usize, color: C) {
51        self[index] = color
52    }
53
54    fn get(&self, index: usize) -> C {
55        self[index]
56    }
57
58    fn nr_elements(&self) -> usize {
59        self.len()
60    }
61}
62
63impl<C: PixelColor, const N: usize> FrameBufferBackend for [C; N] {
64    type Color = C;
65    fn set(&mut self, index: usize, color: C) {
66        self[index] = color
67    }
68
69    fn get(&self, index: usize) -> C {
70        self[index]
71    }
72
73    fn nr_elements(&self) -> usize {
74        self.len()
75    }
76}
77
78/// Backends implementing this Trait can be used for DMA.
79///
80/// # Safety
81///
82/// The same restrictions as for [`embedded_dma::ReadBuffer`] apply.
83pub unsafe trait DMACapableFrameBufferBackend: FrameBufferBackend {
84    fn data_ptr(&self) -> *const Self::Color;
85}
86
87/// # Safety:
88///
89/// The implementation of the trait for all lifetimes `'a` is safe. However,
90/// this doesn't mean that the use of it is safe for all lifetimes. The
91/// requirements specified in [`embedded_dma::ReadBuffer::read_buffer`] remain.
92unsafe impl<'a, C: PixelColor, const N: usize> DMACapableFrameBufferBackend for &'a mut [C; N] {
93    fn data_ptr(&self) -> *const C {
94        self.as_ptr()
95    }
96}
97
98/// Enum indicating how the bytes should be converted in the host's memory.
99#[derive(PartialEq, Eq)]
100pub enum EndianCorrection {
101    ToLittleEndian,
102    ToBigEndian,
103}
104
105/// A backend for [`FrameBuf`](crate::FrameBuf) which changes the underlying
106/// byte order. This can be useful when using the buffer for DMA with
107/// peripherals that have a different endianness than the host.
108pub struct EndianCorrectedBuffer<'a, C: PixelColor> {
109    data: &'a mut [C],
110    endian: EndianCorrection,
111}
112impl<'a, C: PixelColor> EndianCorrectedBuffer<'a, C> {
113    pub fn new(data: &'a mut [C], endian: EndianCorrection) -> Self {
114        Self { data, endian }
115    }
116}
117impl<'a, C> FrameBufferBackend for EndianCorrectedBuffer<'a, C>
118where
119    // TODO: Make this generic over other
120    // types than u16 with associated
121    // type bounds once they are stable
122    C: IntoStorage<Storage = u16> + PixelColor,
123    RawU16: From<C>,
124    C: core::convert::From<RawU16>,
125{
126    type Color = C;
127    fn set(&mut self, index: usize, color: C) {
128        self.data[index] = match self.endian {
129            EndianCorrection::ToBigEndian => RawU16::new(color.into_storage().to_be()).into(),
130            EndianCorrection::ToLittleEndian => RawU16::new(color.into_storage().to_le()).into(),
131        }
132    }
133
134    fn get(&self, index: usize) -> C {
135        match self.endian {
136            EndianCorrection::ToBigEndian => {
137                C::from(RawU16::new(u16::from_be(self.data[index].into_storage())))
138            }
139            EndianCorrection::ToLittleEndian => {
140                C::from(RawU16::new(u16::from_le(self.data[index].into_storage())))
141            }
142        }
143    }
144
145    fn nr_elements(&self) -> usize {
146        self.data.len()
147    }
148}
149unsafe impl<'a, C> DMACapableFrameBufferBackend for EndianCorrectedBuffer<'a, C>
150where
151    C: IntoStorage<Storage = u16> + PixelColor,
152    RawU16: From<C>,
153    C: core::convert::From<RawU16>,
154{
155    fn data_ptr(&self) -> *const C {
156        self.data.as_ptr()
157    }
158}
159
160#[cfg(test)]
161mod tests {
162    extern crate std;
163
164    use super::*;
165    use crate::FrameBuf;
166    use embedded_graphics::pixelcolor::{raw::RawU16, Rgb565};
167    use embedded_graphics::prelude::{Point, RawData, RgbColor};
168
169    #[test]
170    fn test_no_endian_correction() {
171        let mut data = [Rgb565::BLUE; 2 * 3];
172        let mut fbuf = FrameBuf::new(&mut data, 2, 3);
173        fbuf.set_color_at(Point::new(1, 0), Rgb565::RED);
174        fbuf.set_color_at(Point::new(2, 0), Rgb565::BLUE);
175        // Blue in native endian
176        assert_eq!(RawU16::from(fbuf.data[0]).into_inner(), 0b00000000_00011111);
177        // Red in native endian
178        assert_eq!(RawU16::from(fbuf.data[1]).into_inner(), 0b11111000_00000000);
179        // Blue in native endian
180        assert_eq!(RawU16::from(fbuf.data[2]).into_inner(), 0b00000000_00011111);
181    }
182
183    #[test]
184    fn test_big_endian_correction() {
185        let mut data = [Rgb565::BLUE; 2 * 3];
186        let mut fbuf = FrameBuf::new(
187            EndianCorrectedBuffer::new(&mut data, EndianCorrection::ToBigEndian),
188            2,
189            3,
190        );
191        fbuf.set_color_at(Point::new(1, 0), Rgb565::RED);
192        fbuf.set_color_at(Point::new(2, 0), Rgb565::BLUE);
193
194        // Access functions work as expected
195        assert_eq!(fbuf.get_color_at(Point::new(1, 0)), Rgb565::RED);
196        assert_eq!(fbuf.get_color_at(Point::new(2, 0)), Rgb565::BLUE);
197
198        // Red in big endian
199        assert_eq!(
200            RawU16::from(fbuf.data.data[1]).into_inner(),
201            0b00000000_11111000
202        );
203        // Blue in big endian
204        assert_eq!(
205            RawU16::from(fbuf.data.data[2]).into_inner(),
206            0b00011111_00000000
207        );
208    }
209
210    #[test]
211    fn test_little_endian_correction() {
212        let mut data = [Rgb565::BLUE; 2 * 3];
213        let mut fbuf = FrameBuf::new(
214            EndianCorrectedBuffer::new(&mut data, EndianCorrection::ToLittleEndian),
215            2,
216            3,
217        );
218        fbuf.set_color_at(Point::new(1, 0), Rgb565::RED);
219        fbuf.set_color_at(Point::new(2, 0), Rgb565::BLUE);
220
221        // Access functions work as expected
222        assert_eq!(fbuf.get_color_at(Point::new(1, 0)), Rgb565::RED);
223        assert_eq!(fbuf.get_color_at(Point::new(2, 0)), Rgb565::BLUE);
224
225        // Red in little endian
226        assert_eq!(
227            RawU16::from(fbuf.data.data[1]).into_inner(),
228            0b11111000_00000000
229        );
230        // Blue in little endian
231        assert_eq!(
232            RawU16::from(fbuf.data.data[2]).into_inner(),
233            0b00000000_00011111
234        );
235    }
236}