Skip to main content

st7305_lcd/framebuffer/
window.rs

1use embedded_graphics_core::{
2    geometry::{Point, Size},
3    primitives::Rectangle,
4};
5
6use super::PixelMap;
7use crate::cmd::base::{ColumnAddressSet, RowAddressSet};
8
9/// Pixels per `CASET` column (a band, counted from the top of the panel).
10const BAND: u32 = 12;
11/// Pixels per `RASET` row (a pair of pixel columns).
12const PAIR: u32 = 2;
13/// Bytes of one band in one `RASET` row.
14const BAND_BYTES: usize = 3;
15
16/// A window of the GRAM that covers a pixel rectangle grown to the panel grid: 2 pixels
17/// horizontally and 12 pixels vertically.
18///
19/// Coordinates are native (not rotated). The window gives the arguments of
20/// [`set_address_window`](crate::blocking::St7305::set_address_window) and, with
21/// [`FrameBuffer::copy_window`](super::FrameBuffer::copy_window), the bytes for the following
22/// `RAMWR`. The grid and the byte order were verified on the Waveshare 4.2" panel
23/// ([`presets::waveshare_rlcd_42`](crate::presets::waveshare_rlcd_42)); for other panels this
24/// is an assumption.
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26pub struct Window {
27    /// Aligned to the grid and inside the panel.
28    area: Rectangle,
29    caset_first: u8,
30    raset_first: u8,
31}
32
33impl Window {
34    /// The smallest window of the panel `M` that contains `area` clipped to the panel, `None`
35    /// if nothing of `area` is on the panel.
36    ///
37    /// The panel width must be even and its height a multiple of 12, otherwise the last band
38    /// is cut.
39    pub fn covering<M: PixelMap>(area: Rectangle) -> Option<Self> {
40        let panel = Rectangle::new(Point::zero(), Size::new(M::WIDTH, M::HEIGHT));
41        let bottom_right = area.intersection(&panel).bottom_right()?; // inclusive
42        let top_left = area.intersection(&panel).top_left;
43
44        // After the clipping everything is non-negative.
45        let x0 = top_left.x as u32 / PAIR * PAIR;
46        let y0 = top_left.y as u32 / BAND * BAND;
47        let x1 = (bottom_right.x as u32 / PAIR + 1) * PAIR; // exclusive
48        let y1 = (bottom_right.y as u32 / BAND + 1) * BAND;
49
50        Some(Self {
51            area: Rectangle::new(
52                Point::new(x0 as i32, y0 as i32),
53                Size::new(x1 - x0, y1 - y0),
54            ),
55            caset_first: M::CASET_FIRST,
56            raset_first: M::RASET_FIRST,
57        })
58    }
59
60    /// The window of the whole panel `M`: the GRAM window of a full frame.
61    pub fn full<M: PixelMap>() -> Self {
62        let panel = Rectangle::new(Point::zero(), Size::new(M::WIDTH, M::HEIGHT));
63        Self::covering::<M>(panel).expect("a panel has pixels")
64    }
65
66    /// The pixels the window really covers (native coordinates).
67    pub const fn bounds(&self) -> Rectangle {
68        self.area
69    }
70
71    /// Number of bytes that `RAMWR` expects for this window.
72    pub const fn byte_len(&self) -> usize {
73        (self.pairs() * self.bands()) as usize * BAND_BYTES
74    }
75
76    /// Argument of `set_address_window`.
77    pub fn column_address_set(&self) -> ColumnAddressSet {
78        let xs = u32::from(self.caset_first) + self.band0();
79        ColumnAddressSet::new(xs as u8, (xs + self.bands() - 1) as u8)
80    }
81
82    /// Argument of `set_address_window`.
83    pub fn row_address_set(&self) -> RowAddressSet {
84        let ys = u32::from(self.raset_first) + self.pair0();
85        RowAddressSet::new(ys as u8, (ys + self.pairs() - 1) as u8)
86    }
87
88    const fn pair0(&self) -> u32 {
89        self.area.top_left.x as u32 / PAIR
90    }
91
92    const fn pairs(&self) -> u32 {
93        self.area.size.width / PAIR
94    }
95
96    const fn band0(&self) -> u32 {
97        self.area.top_left.y as u32 / BAND
98    }
99
100    const fn bands(&self) -> u32 {
101        self.area.size.height / BAND
102    }
103
104    /// Copies the bytes of the window from a full native frame `buf` of a panel `height`
105    /// pixels high; returns `&out[..self.byte_len()]`.
106    ///
107    /// # Panics
108    ///
109    /// If `out` is shorter than [`Self::byte_len`] or `buf` is not a full frame.
110    pub(super) fn copy_from<'a>(&self, buf: &[u8], height: u32, out: &'a mut [u8]) -> &'a [u8] {
111        let out = &mut out[..self.byte_len()];
112        let column = (height / 4) as usize; // bytes per pair of pixel columns
113        let width = self.bands() as usize * BAND_BYTES;
114        // Bands are counted from the top, bytes in a column from the bottom.
115        let offset = column - (self.band0() + self.bands()) as usize * BAND_BYTES;
116        let pair0 = self.pair0() as usize;
117
118        for (i, chunk) in out.chunks_exact_mut(width).enumerate() {
119            let start = (pair0 + i) * column + offset;
120            chunk.copy_from_slice(&buf[start..start + width]);
121        }
122        out
123    }
124}