st7305-lcd 0.2.0

Platform-independent no_std driver for the Sitronix ST7305 reflective LCD controller (embedded-hal 1.0, blocking and async)
Documentation
use embedded_graphics_core::{
    geometry::{Point, Size},
    primitives::Rectangle,
};

use super::PixelMap;
use crate::cmd::base::{ColumnAddressSet, RowAddressSet};

/// Pixels per `CASET` column (a band, counted from the top of the panel).
const BAND: u32 = 12;
/// Pixels per `RASET` row (a pair of pixel columns).
const PAIR: u32 = 2;
/// Bytes of one band in one `RASET` row.
const BAND_BYTES: usize = 3;

/// A window of the GRAM that covers a pixel rectangle grown to the panel grid: 2 pixels
/// horizontally and 12 pixels vertically.
///
/// Coordinates are native (not rotated). The window gives the arguments of
/// [`set_address_window`](crate::blocking::St7305::set_address_window) and, with
/// [`FrameBuffer::copy_window`](super::FrameBuffer::copy_window), the bytes for the following
/// `RAMWR`. The grid and the byte order were verified on the Waveshare 4.2" panel
/// ([`presets::waveshare_rlcd_42`](crate::presets::waveshare_rlcd_42)); for other panels this
/// is an assumption.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Window {
    /// Aligned to the grid and inside the panel.
    area: Rectangle,
    caset_first: u8,
    raset_first: u8,
}

impl Window {
    /// The smallest window of the panel `M` that contains `area` clipped to the panel, `None`
    /// if nothing of `area` is on the panel.
    ///
    /// The panel width must be even and its height a multiple of 12, otherwise the last band
    /// is cut.
    pub fn covering<M: PixelMap>(area: Rectangle) -> Option<Self> {
        let panel = Rectangle::new(Point::zero(), Size::new(M::WIDTH, M::HEIGHT));
        let bottom_right = area.intersection(&panel).bottom_right()?; // inclusive
        let top_left = area.intersection(&panel).top_left;

        // After the clipping everything is non-negative.
        let x0 = top_left.x as u32 / PAIR * PAIR;
        let y0 = top_left.y as u32 / BAND * BAND;
        let x1 = (bottom_right.x as u32 / PAIR + 1) * PAIR; // exclusive
        let y1 = (bottom_right.y as u32 / BAND + 1) * BAND;

        Some(Self {
            area: Rectangle::new(
                Point::new(x0 as i32, y0 as i32),
                Size::new(x1 - x0, y1 - y0),
            ),
            caset_first: M::CASET_FIRST,
            raset_first: M::RASET_FIRST,
        })
    }

    /// The window of the whole panel `M`: the GRAM window of a full frame.
    pub fn full<M: PixelMap>() -> Self {
        let panel = Rectangle::new(Point::zero(), Size::new(M::WIDTH, M::HEIGHT));
        Self::covering::<M>(panel).expect("a panel has pixels")
    }

    /// The pixels the window really covers (native coordinates).
    pub const fn bounds(&self) -> Rectangle {
        self.area
    }

    /// Number of bytes that `RAMWR` expects for this window.
    pub const fn byte_len(&self) -> usize {
        (self.pairs() * self.bands()) as usize * BAND_BYTES
    }

    /// Argument of `set_address_window`.
    pub fn column_address_set(&self) -> ColumnAddressSet {
        let xs = u32::from(self.caset_first) + self.band0();
        ColumnAddressSet::new(xs as u8, (xs + self.bands() - 1) as u8)
    }

    /// Argument of `set_address_window`.
    pub fn row_address_set(&self) -> RowAddressSet {
        let ys = u32::from(self.raset_first) + self.pair0();
        RowAddressSet::new(ys as u8, (ys + self.pairs() - 1) as u8)
    }

    const fn pair0(&self) -> u32 {
        self.area.top_left.x as u32 / PAIR
    }

    const fn pairs(&self) -> u32 {
        self.area.size.width / PAIR
    }

    const fn band0(&self) -> u32 {
        self.area.top_left.y as u32 / BAND
    }

    const fn bands(&self) -> u32 {
        self.area.size.height / BAND
    }

    /// Copies the bytes of the window from a full native frame `buf` of a panel `height`
    /// pixels high; returns `&out[..self.byte_len()]`.
    ///
    /// # Panics
    ///
    /// If `out` is shorter than [`Self::byte_len`] or `buf` is not a full frame.
    pub(super) fn copy_from<'a>(&self, buf: &[u8], height: u32, out: &'a mut [u8]) -> &'a [u8] {
        let out = &mut out[..self.byte_len()];
        let column = (height / 4) as usize; // bytes per pair of pixel columns
        let width = self.bands() as usize * BAND_BYTES;
        // Bands are counted from the top, bytes in a column from the bottom.
        let offset = column - (self.band0() + self.bands()) as usize * BAND_BYTES;
        let pair0 = self.pair0() as usize;

        for (i, chunk) in out.chunks_exact_mut(width).enumerate() {
            let start = (pair0 + i) * column + offset;
            chunk.copy_from_slice(&buf[start..start + width]);
        }
        out
    }
}