gc9a01/mode/
graphics.rs

1//! Buffered Graphic Implementation
2
3use display_interface::{DisplayError, WriteOnlyDataCommand};
4
5use crate::{
6    display::{DisplayDefinition, NewZeroed},
7    rotation::DisplayRotation,
8    Gc9a01,
9};
10
11use super::DisplayConfiguration;
12
13use embedded_hal::delay::DelayNs;
14
15/// Buffered Graphic Implementation
16///
17/// This implementation provides a buffer in system memory.
18/// This buffer is drawn to by [`set_pixel`](Gc9a01::set_pixel) commands or
19/// [`embedded-graphics`](https://docs.rs/embedded-graphics) commands.
20/// The display can then be updated using the [`flush`](Gc9a01::flush) method.
21#[derive(Debug, Clone)]
22pub struct BufferedGraphics<D>
23where
24    D: DisplayDefinition,
25{
26    buffer: D::Buffer,
27    min_x: u16,
28    max_x: u16,
29    min_y: u16,
30    max_y: u16,
31}
32
33impl<D> BufferedGraphics<D>
34where
35    D: DisplayDefinition,
36{
37    /// Create a new buffered graphics mode instance.
38    pub(crate) fn new() -> Self {
39        Self {
40            buffer: NewZeroed::new_zeroed(),
41            min_x: u16::MAX,
42            max_x: u16::MIN,
43            min_y: u16::MAX,
44            max_y: u16::MIN,
45        }
46    }
47}
48
49impl<I, D, DELAY> DisplayConfiguration<DELAY> for Gc9a01<I, D, BufferedGraphics<D>>
50where
51    I: WriteOnlyDataCommand,
52    D: DisplayDefinition,
53    DELAY: DelayNs,
54{
55    type Error = DisplayError;
56
57    /// Set display rotation
58    fn set_rotation(&mut self, rot: DisplayRotation) -> Result<(), DisplayError> {
59        self.set_display_rotation(rot)
60    }
61
62    /// Initialise and clear the display in graphics mode.
63    fn init(&mut self, delay: &mut DELAY) -> Result<(), DisplayError> {
64        self.clear();
65        self.init_with_addr_mode(delay)
66    }
67}
68
69impl<I, D> Gc9a01<I, D, BufferedGraphics<D>>
70where
71    I: WriteOnlyDataCommand,
72    D: DisplayDefinition,
73{
74    /// Clear the display buffer
75    /// NOTE: Must use `flush` to apply changes
76    pub fn clear(&mut self) {
77        for b in self.mode.buffer.as_mut() {
78            *b = 0;
79        }
80
81        let (max_x, max_y) = self.dimensions();
82        self.mode.min_x = u16::MIN;
83        self.mode.max_x = max_x;
84        self.mode.min_y = u16::MIN;
85        self.mode.max_y = max_y;
86    }
87
88    pub fn fill(&mut self, color: u16) {
89        for b in self.mode.buffer.as_mut() {
90            *b = color;
91        }
92
93        let (max_x, max_y) = self.dimensions();
94        self.mode.min_x = u16::MIN;
95        self.mode.max_x = max_x;
96        self.mode.min_y = u16::MIN;
97        self.mode.max_y = max_y;
98    }
99
100    /// Write the display buffer
101    ///
102    /// # Errors
103    ///
104    /// This method may return an error if there are communication issues with the display.
105    pub fn flush(&mut self) -> Result<(), DisplayError> {
106        // check if you touch anything
107        if self.mode.max_x < self.mode.min_x || self.mode.max_y < self.mode.min_y {
108            return Ok(());
109        }
110
111        let (bound_width, bound_height) = self.bounds();
112        let (screen_width, screen_height) = self.dimensions();
113
114        // Determine witch bytes need to be sent
115        let disp_min_x = self.mode.min_x;
116        let disp_min_y = self.mode.min_y;
117
118        let (disp_max_x, disp_max_y) = (
119            (self.mode.max_x).min(bound_width),
120            (self.mode.max_y).min(bound_height),
121        );
122
123        // reset idle state
124        self.mode.min_x = u16::MAX;
125        self.mode.max_x = u16::MIN;
126        self.mode.min_y = u16::MAX;
127        self.mode.max_y = u16::MIN;
128
129        let offset_x = match self.display_rotation {
130            DisplayRotation::Rotate0 | DisplayRotation::Rotate270 => D::OFFSET_X,
131            DisplayRotation::Rotate90 | DisplayRotation::Rotate180 => {
132                D::COLS - D::WIDTH - D::OFFSET_X
133            }
134        };
135
136        match self.display_rotation {
137            DisplayRotation::Rotate0 | DisplayRotation::Rotate180 => {
138                self.set_draw_area(
139                    (disp_min_x + offset_x, disp_min_y + D::OFFSET_Y),
140                    (disp_max_x + offset_x, disp_max_y + D::OFFSET_Y),
141                )?;
142
143                Self::flush_buffer_chunks(
144                    &mut self.interface,
145                    self.mode.buffer.as_mut(),
146                    screen_width as usize,
147                    (disp_min_x, disp_min_y),
148                    (disp_max_x, disp_max_y),
149                )
150            }
151            DisplayRotation::Rotate90 | DisplayRotation::Rotate270 => {
152                self.set_draw_area(
153                    (disp_min_y + offset_x, disp_min_x + D::OFFSET_Y),
154                    (disp_max_y + offset_x, disp_max_x + D::OFFSET_Y),
155                )?;
156
157                Self::flush_buffer_chunks(
158                    &mut self.interface,
159                    self.mode.buffer.as_mut(),
160                    screen_height as usize,
161                    (disp_min_y, disp_min_x),
162                    (disp_max_y, disp_max_x),
163                )
164            }
165        }
166    }
167
168    /// Set the pixels
169    ///
170    /// # Errors
171    ///
172    /// This method may return an error if there are communication issues with the display.
173    /// This method may return an error if there are an out of bounds error.
174    pub fn set_pixels<T>(
175        &mut self,
176        start: (u16, u16),
177        end: (u16, u16),
178        colors: T,
179    ) -> Result<(), DisplayError>
180    where
181        T: IntoIterator<Item = u16>,
182    {
183        let x = start.0;
184        let y = start.1;
185        let rotation = self.display_rotation;
186
187        let idx = match rotation {
188            DisplayRotation::Rotate0 | DisplayRotation::Rotate180 => {
189                ((y as usize) * D::WIDTH as usize) + (x as usize)
190            }
191            DisplayRotation::Rotate90 | DisplayRotation::Rotate270 => {
192                ((x as usize) * D::HEIGHT as usize) + (y as usize)
193            }
194        };
195
196        let mut buffer_index = idx;
197        let buffer_len = self.mode.buffer.as_mut().len();
198
199        for color in colors {
200            if buffer_index >= buffer_len {
201                return Err(DisplayError::OutOfBoundsError);
202            }
203
204            // Directly copy the color into the buffer
205            unsafe {
206                *self.mode.buffer.as_mut().get_unchecked_mut(buffer_index) = color;
207            }
208            buffer_index += 1;
209        }
210
211        self.mode.min_x = self.mode.min_x.min(start.0);
212        self.mode.max_x = self.mode.max_x.max(end.0);
213        self.mode.min_y = self.mode.min_y.min(start.1);
214        self.mode.max_y = self.mode.max_y.max(end.1);
215
216        Ok(())
217    }
218
219    /// Set a pixel color. If the X and Y coordinates are out of the bounds
220    /// of the display, this method call is a noop
221    pub fn set_pixel(&mut self, x: u32, y: u32, value: u16) {
222        let rotation = self.display_rotation;
223
224        let idx = match rotation {
225            DisplayRotation::Rotate0 | DisplayRotation::Rotate180 => {
226                ((y as usize) * D::WIDTH as usize) + (x as usize)
227            }
228            DisplayRotation::Rotate90 | DisplayRotation::Rotate270 => {
229                ((x as usize) * D::HEIGHT as usize) + (y as usize)
230            }
231        };
232
233        if let Some(color) = self.mode.buffer.as_mut().get_mut(idx) {
234            self.mode.min_x = self.mode.min_x.min(x as u16);
235            self.mode.max_x = self.mode.max_x.max(x as u16);
236            self.mode.min_y = self.mode.min_y.min(y as u16);
237            self.mode.max_y = self.mode.max_y.max(y as u16);
238
239            *color = value;
240        }
241    }
242}
243
244#[cfg(feature = "graphics")]
245use embedded_graphics_core::{
246    draw_target::DrawTarget,
247    geometry::Size,
248    geometry::{Dimensions, OriginDimensions},
249    pixelcolor::raw::RawU16,
250    pixelcolor::Rgb565,
251    prelude::RawData,
252    Pixel,
253};
254
255#[cfg(feature = "graphics")]
256impl<I, D> OriginDimensions for Gc9a01<I, D, BufferedGraphics<D>>
257where
258    I: WriteOnlyDataCommand,
259    D: DisplayDefinition,
260{
261    fn size(&self) -> Size {
262        let (w, h) = self.dimensions();
263        Size::new(w.into(), h.into())
264    }
265}
266
267#[cfg(feature = "graphics")]
268impl<I, D> DrawTarget for Gc9a01<I, D, BufferedGraphics<D>>
269where
270    I: WriteOnlyDataCommand,
271    D: DisplayDefinition,
272{
273    // TODO: figure out a way to handle all case
274    type Color = Rgb565;
275    type Error = DisplayError;
276
277    fn draw_iter<O>(&mut self, pixels: O) -> Result<(), Self::Error>
278    where
279        O: IntoIterator<Item = Pixel<Self::Color>>,
280    {
281        let bb = self.bounding_box();
282
283        pixels
284            .into_iter()
285            .filter(|&Pixel(pos, _color)| bb.contains(pos))
286            .for_each(|Pixel(pos, color)| {
287                let color: RawU16 = color.into();
288                let color: u16 = color.into_inner();
289                #[allow(clippy::cast_sign_loss)]
290                self.set_pixel(pos.x as u32, pos.y as u32, color);
291            });
292        Ok(())
293    }
294}