Skip to main content

multi_mono_font/
framebuf.rs

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