st77916 0.1.1

A Rust driver for the ST77916 TFT-LCD display controller
Documentation
//! `embedded-graphics` integration for the ST77916 driver.
//!
//! This module provides [`DrawTarget`] implementations and buffering modes
//! for use with the `embedded-graphics` ecosystem.  It is compiled only when
//! the `embedded-graphics` cargo feature is enabled (on by default).

mod buffering;
mod draw_target;

pub use buffering::{
    framebuffer_size, Buffered, DoubleBuffered, Framebuffer, Unbuffered,
};

use core::marker::PhantomData;

use embedded_graphics_core::pixelcolor::raw::{RawData, RawU16};
use embedded_graphics_core::pixelcolor::{Gray8, GrayColor, Rgb565, Rgb888, RgbColor};
use embedded_graphics_core::prelude::PixelColor;
use embedded_hal::delay::DelayNs;

use crate::{
    ColorMode, ControllerInterface, DriverResult, ResetInterface, St77916, St77916Builder,
};

// ---------------------------------------------------------------------------
// Color encoding trait
// ---------------------------------------------------------------------------

/// Pixel color encoding for the ST77916 framebuffer.
///
/// Implementors define how a color is serialized into the framebuffer's byte
/// layout (big-endian for RGB565/RGB888, single byte for Gray8).
///
/// The trait provides default `fill_row` and `fill_buf` methods that fill
/// memory by repeating `encode()`. Color types can override these for faster
/// fills — for example, [`Rgb565`] detects uniform high/low bytes and uses
/// `buf.fill()` for a single-byte memset when possible.
pub trait St77916Color: PixelColor + Copy {
    const BYTES_PER_PIXEL: usize;

    /// Encode a single pixel into `out[..BYTES_PER_PIXEL]` in big-endian order.
    fn encode(self, out: &mut [u8]);

    /// Fill a row slice with a single repeated color.
    fn fill_row(color: Self, row: &mut [u8]) {
        let bpp = Self::BYTES_PER_PIXEL;
        let mut tmp = [0u8; 4];
        color.encode(&mut tmp[..bpp]);
        for chunk in row.chunks_exact_mut(bpp) {
            chunk.copy_from_slice(&tmp[..bpp]);
        }
    }

    /// Fill an entire buffer with a single color.
    fn fill_buf(color: Self, buf: &mut [u8]) {
        Self::fill_row(color, buf);
    }
}

impl St77916Color for Rgb565 {
    const BYTES_PER_PIXEL: usize = 2;

    #[inline]
    fn encode(self, out: &mut [u8]) {
        let raw = RawU16::from(self).into_inner();
        out[0] = (raw >> 8) as u8;
        out[1] = raw as u8;
    }

    fn fill_buf(color: Self, buf: &mut [u8]) {
        let raw = RawU16::from(color).into_inner();
        let hi = (raw >> 8) as u8;
        let lo = raw as u8;
        if hi == lo {
            buf.fill(hi);
        } else {
            for chunk in buf.chunks_exact_mut(2) {
                chunk[0] = hi;
                chunk[1] = lo;
            }
        }
    }
}

impl St77916Color for Rgb888 {
    const BYTES_PER_PIXEL: usize = 3;

    #[inline]
    fn encode(self, out: &mut [u8]) {
        out[0] = self.r();
        out[1] = self.g();
        out[2] = self.b();
    }

    fn fill_buf(color: Self, buf: &mut [u8]) {
        let r = color.r();
        let g = color.g();
        let b = color.b();
        if r == g && r == b {
            buf.fill(r);
        } else {
            for chunk in buf.chunks_exact_mut(3) {
                chunk[0] = r;
                chunk[1] = g;
                chunk[2] = b;
            }
        }
    }
}

impl St77916Color for Gray8 {
    const BYTES_PER_PIXEL: usize = 1;

    #[inline]
    fn encode(self, out: &mut [u8]) {
        out[0] = self.luma();
    }

    fn fill_buf(color: Self, buf: &mut [u8]) {
        buf.fill(color.luma());
    }
}

// ---------------------------------------------------------------------------
// Builder transition methods
// ---------------------------------------------------------------------------

impl<IFACE, RST> St77916Builder<IFACE, RST, ()>
where
    IFACE: ControllerInterface,
    RST: ResetInterface,
{
    /// Opt into an internal framebuffer with `DrawTarget` support.
    ///
    /// ```ignore
    /// .buffered::<Rgb565>(Framebuffer::heap::<FB_SIZE>())
    /// ```
    pub fn buffered<COLOR: St77916Color>(
        self,
        framebuffer: Framebuffer,
    ) -> St77916Builder<IFACE, RST, Buffered<COLOR>> {
        St77916Builder {
            interface: self.interface,
            reset: self.reset,
            config: self.config,
            init_commands: self.init_commands,
            buffer: Buffered {
                data: framebuffer,
                dirty: buffering::DirtyBand::clean(),
                _color: PhantomData,
            },
        }
    }

    /// Opt into double-buffered rendering with `DrawTarget` support.
    ///
    /// ```ignore
    /// .double_buffered::<Rgb565>(fb1, fb2)
    /// ```
    pub fn double_buffered<COLOR: St77916Color>(
        self,
        fb1: Framebuffer,
        fb2: Framebuffer,
    ) -> St77916Builder<IFACE, RST, DoubleBuffered<COLOR>> {
        St77916Builder {
            interface: self.interface,
            reset: self.reset,
            config: self.config,
            init_commands: self.init_commands,
            buffer: DoubleBuffered {
                bufs: [fb1, fb2],
                back_idx: 0,
                _color: PhantomData,
            },
        }
    }

    /// Opt into `DrawTarget` support without a framebuffer.
    ///
    /// ```ignore
    /// .unbuffered::<Rgb565>()
    /// ```
    pub fn unbuffered<COLOR: St77916Color>(
        self,
    ) -> St77916Builder<IFACE, RST, Unbuffered<COLOR>> {
        St77916Builder {
            interface: self.interface,
            reset: self.reset,
            config: self.config,
            init_commands: self.init_commands,
            buffer: Unbuffered {
                _color: PhantomData,
            },
        }
    }
}

// ---------------------------------------------------------------------------
// Build impls for buffered variants
// ---------------------------------------------------------------------------

impl<IFACE, RST, COLOR: St77916Color> St77916Builder<IFACE, RST, Unbuffered<COLOR>>
where
    IFACE: ControllerInterface,
    RST: ResetInterface,
{
    pub fn build<DELAY>(
        self,
        color: ColorMode,
        delay: &mut DELAY,
    ) -> DriverResult<St77916<IFACE, RST, Unbuffered<COLOR>>, IFACE, RST>
    where
        DELAY: DelayNs,
    {
        crate::build_driver(
            self.interface,
            self.reset,
            self.config,
            self.init_commands,
            self.buffer,
            delay,
            color,
        )
    }
}

impl<IFACE, RST, COLOR: St77916Color> St77916Builder<IFACE, RST, Buffered<COLOR>>
where
    IFACE: ControllerInterface,
    RST: ResetInterface,
{
    pub fn build<DELAY>(
        self,
        color: ColorMode,
        delay: &mut DELAY,
    ) -> DriverResult<St77916<IFACE, RST, Buffered<COLOR>>, IFACE, RST>
    where
        DELAY: DelayNs,
    {
        crate::build_driver(
            self.interface,
            self.reset,
            self.config,
            self.init_commands,
            self.buffer,
            delay,
            color,
        )
    }
}

impl<IFACE, RST, COLOR: St77916Color> St77916Builder<IFACE, RST, DoubleBuffered<COLOR>>
where
    IFACE: ControllerInterface,
    RST: ResetInterface,
{
    pub fn build<DELAY>(
        self,
        color: ColorMode,
        delay: &mut DELAY,
    ) -> DriverResult<St77916<IFACE, RST, DoubleBuffered<COLOR>>, IFACE, RST>
    where
        DELAY: DelayNs,
    {
        crate::build_driver(
            self.interface,
            self.reset,
            self.config,
            self.init_commands,
            self.buffer,
            delay,
            color,
        )
    }
}