Skip to main content

multi_mono_font/
framebuf.rs

1use core::ops::Sub;
2use embedded_graphics::prelude::*;
3use embedded_graphics::primitives::Rectangle;
4use embedded_graphics::Pixel;
5
6pub trait BulkFlushTarget: DrawTarget {
7    /// Flushes the buffer to the target.
8    fn flush(&mut self, area: &Rectangle, data: &[Self::Color]) -> Result<(), Self::Error>;
9}
10
11pub struct Framebuffer<'a, COL: PixelColor, DRAW: BulkFlushTarget<Color = COL>> {
12    target: &'a mut DRAW,
13    fb: FramebufferInner<'a, COL>,
14}
15
16impl<'a, COL: PixelColor, DRAW: BulkFlushTarget<Color = COL>>
17    Framebuffer<'a, COL, DRAW>
18{
19    /// Creates a new [Framebuffer] instance based on the provided [DrawTarget].
20    pub fn new(target: &'a mut DRAW, buffer: &'a mut [COL]) -> Self {
21        Self {
22            target,
23            fb: FramebufferInner::new(buffer),
24        }
25    }
26}
27
28impl<'a, COL: PixelColor, DRAW: BulkFlushTarget<Color = COL>> Dimensions
29    for Framebuffer<'a, COL, DRAW>
30{
31    fn bounding_box(&self) -> Rectangle {
32        self.target.bounding_box()
33    }
34}
35
36impl<'a, COL: PixelColor, DRAW: BulkFlushTarget<Color = COL>> DrawTarget
37    for Framebuffer<'a, COL, DRAW>
38{
39    type Color = COL;
40    type Error = ();
41
42    fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
43    where
44        I: IntoIterator<Item = Pixel<Self::Color>>,
45    {
46        self.target.draw_iter(pixels).map_err(|_| ())
47    }
48
49    fn fill_contiguous<I>(&mut self, area: &Rectangle, colors: I) -> Result<(), Self::Error>
50    where
51        I: IntoIterator<Item = Self::Color>,
52    {
53        if self.fb.start_drawing(area) {
54            self.fb.fill_contiguous(area, colors)?;
55            self.target
56                .flush(area, self.fb.get_buffer())
57                .map_err(|_| ())?;
58            self.fb.finalize();
59            Ok(())
60        } else {
61            self.target.fill_contiguous(area, colors).map_err(|_| ())
62        }
63    }
64}
65
66struct FramebufferInner<'a, C: PixelColor> {
67    buf: &'a mut [C],
68    size: Size,
69    position: Point,
70    len: usize,
71}
72
73impl<'a, C: PixelColor> FramebufferInner<'a, C> {
74    pub fn new(buf: &'a mut [C]) -> Self {
75        Self {
76            buf,
77            size: Size::zero(),
78            position: Point::zero(),
79            len: 0,
80        }
81    }
82
83    pub fn start_drawing(&mut self, area: &Rectangle) -> bool {
84        if self.len > 0 {
85            return false;
86        }
87
88        let size = area.size;
89        let position = area.top_left;
90        let len = size.width as usize * size.height as usize;
91
92        if len <= self.buf.len() {
93            self.size = size;
94            self.position = position;
95            self.len = len;
96            true
97        } else {
98            false
99        }
100    }
101
102    pub fn get_buffer(&mut self) -> &[C] {
103        &self.buf[0..self.len]
104    }
105
106    pub fn finalize(&mut self) {
107        self.len = 0;
108    }
109}
110
111impl<C: PixelColor> Dimensions for FramebufferInner<'_, C> {
112    fn bounding_box(&self) -> Rectangle {
113        Rectangle::new(self.position, self.size)
114    }
115}
116
117impl<C: PixelColor> DrawTarget for FramebufferInner<'_, C> {
118    type Color = C;
119    type Error = ();
120
121    fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
122    where
123        I: IntoIterator<Item = Pixel<Self::Color>>,
124    {
125        for pixel in pixels {
126            let pt = pixel.0.sub(self.position);
127            let pos = pt.y * self.size.width as i32 + pt.x;
128            if pos < 0 || pos >= self.len as i32 {
129                continue;
130            }
131            self.buf[pos as usize] = pixel.1;
132        }
133
134        Ok(())
135    }
136
137    fn fill_contiguous<I>(&mut self, area: &Rectangle, colors: I) -> Result<(), Self::Error>
138    where
139        I: IntoIterator<Item = Self::Color>,
140    {
141        let drawable_area = area.intersection(&self.bounding_box());
142        if drawable_area.is_zero_sized() {
143            return Ok(());
144        }
145
146        let top_skip = drawable_area.top_left.y - area.top_left.y;
147        let left_skip = drawable_area.top_left.x - area.top_left.x;
148        let right_skip = area.size.width as i32 - (left_skip + drawable_area.size.width as i32);
149        // let bottom_skip = area.size.height as i32 - (top_skip + drawable_area.size.height as i32);
150
151        let mut color_iter = colors.into_iter();
152
153        // skip all un-drawable rows
154        for _ in 0..top_skip {
155            for _ in 0..area.size.width as usize {
156                color_iter.next();
157            }
158        }
159        for y in drawable_area.top_left.y as usize
160            ..drawable_area.top_left.y as usize + drawable_area.size.height as usize
161        {
162            for _ in 0..left_skip {
163                color_iter.next();
164                // skip all left
165            }
166            for x in drawable_area.top_left.x as usize
167                ..drawable_area.top_left.x as usize + drawable_area.size.width as usize
168            {
169                let pos = (y as i32 - self.position.y) as usize * self.size.width as usize
170                    + (x as i32 - self.position.x) as usize;
171                match color_iter.next() {
172                    Some(color) => self.buf[pos] = color,
173                    None => return Ok(()),
174                }
175            }
176            for _ in 0..right_skip {
177                color_iter.next();
178                // skip all right
179            }
180        }
181
182        Ok(())
183    }
184
185    fn fill_solid(&mut self, area: &Rectangle, color: Self::Color) -> Result<(), Self::Error> {
186        // Clamp area to drawable part of the display target
187        let drawable_area = area.intersection(&self.bounding_box());
188
189        // Draw the rectangle
190        for y in drawable_area.top_left.y as usize
191            ..drawable_area.top_left.y as usize + drawable_area.size.height as usize
192        {
193            for x in drawable_area.top_left.x as usize
194                ..drawable_area.top_left.x as usize + drawable_area.size.width as usize
195            {
196                let pos = (y as i32 - self.position.y) as usize * self.size.width as usize
197                    + (x as i32 - self.position.x) as usize;
198                self.buf[pos] = color;
199            }
200        }
201        Ok(())
202    }
203
204    fn clear(&mut self, color: Self::Color) -> Result<(), Self::Error> {
205        self.buf[0..(self.size.width * self.size.height) as usize].fill(color);
206        Ok(())
207    }
208}
209
210impl<C: PixelColor> Drawable for FramebufferInner<'_, C> {
211    type Color = C;
212    type Output = ();
213
214    fn draw<D>(&self, target: &mut D) -> Result<Self::Output, D::Error>
215    where
216        D: DrawTarget<Color = Self::Color>,
217    {
218        target.fill_contiguous(
219            &Rectangle::new(self.position, self.size),
220            self.buf.iter().cloned(),
221        )
222    }
223}