#![no_std]
#![warn(missing_docs)]
#![warn(clippy::all)]
#![warn(clippy::pedantic)]
#![allow(clippy::cast_possible_truncation)]
#![allow(clippy::cast_sign_loss)]
#[cfg(not(feature = "esp-hal-dma"))]
use embedded_dma::ReadBuffer;
use embedded_graphics::draw_target::DrawTarget;
use embedded_graphics::pixelcolor::Rgb888;
use embedded_graphics::prelude::Point;
#[cfg(feature = "esp-hal-dma")]
use esp_hal::dma::ReadBuffer;
pub mod latched;
pub mod plain;
pub mod tiling;
pub type Color = Rgb888;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WordSize {
Eight,
Sixteen,
}
#[must_use]
pub const fn compute_rows(rows: usize) -> usize {
rows / 2
}
#[must_use]
pub const fn compute_frame_count(bits: u8) -> usize {
(1usize << bits) - 1
}
pub trait FrameBuffer<
const ROWS: usize,
const COLS: usize,
const NROWS: usize,
const BITS: u8,
const FRAME_COUNT: usize,
>: ReadBuffer
{
fn get_word_size(&self) -> WordSize;
}
pub trait MutableFrameBuffer<
const ROWS: usize,
const COLS: usize,
const NROWS: usize,
const BITS: u8,
const FRAME_COUNT: usize,
>:
FrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
+ DrawTarget<Color = Color, Error = core::convert::Infallible>
{
}
pub trait FrameBufferOperations<
const ROWS: usize,
const COLS: usize,
const NROWS: usize,
const BITS: u8,
const FRAME_COUNT: usize,
>: FrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
{
fn erase(&mut self);
fn set_pixel(&mut self, p: Point, color: Color);
}
#[cfg(test)]
mod tests {
extern crate std;
use std::format;
use super::*;
use embedded_graphics::pixelcolor::RgbColor;
#[test]
fn test_compute_rows() {
assert_eq!(compute_rows(32), 16);
assert_eq!(compute_rows(64), 32);
assert_eq!(compute_rows(16), 8);
assert_eq!(compute_rows(128), 64);
assert_eq!(compute_rows(2), 1);
assert_eq!(compute_rows(0), 0);
for rows in [8, 16, 24, 32, 48, 64, 96, 128, 256] {
assert_eq!(compute_rows(rows), rows / 2);
}
}
#[test]
fn test_compute_frame_count() {
assert_eq!(compute_frame_count(1), 1); assert_eq!(compute_frame_count(2), 3); assert_eq!(compute_frame_count(3), 7); assert_eq!(compute_frame_count(4), 15); assert_eq!(compute_frame_count(5), 31); assert_eq!(compute_frame_count(6), 63); assert_eq!(compute_frame_count(7), 127); assert_eq!(compute_frame_count(8), 255);
for bits in 1..=8 {
let expected = (1usize << bits) - 1;
assert_eq!(compute_frame_count(bits), expected);
}
}
#[test]
fn test_compute_frame_count_properties() {
assert!(compute_frame_count(2) > compute_frame_count(1));
assert!(compute_frame_count(3) > compute_frame_count(2));
assert!(compute_frame_count(4) > compute_frame_count(3));
for bits in 1..=7 {
let current_frames = compute_frame_count(bits);
let next_frames = compute_frame_count(bits + 1);
assert_eq!(next_frames, 2 * current_frames + 1);
}
}
#[test]
fn test_word_size_enum() {
let eight = WordSize::Eight;
let sixteen = WordSize::Sixteen;
assert_ne!(eight, sixteen);
assert_eq!(eight, WordSize::Eight);
assert_eq!(sixteen, WordSize::Sixteen);
}
#[test]
fn test_word_size_debug() {
let eight = WordSize::Eight;
let sixteen = WordSize::Sixteen;
let eight_debug = format!("{:?}", eight);
let sixteen_debug = format!("{:?}", sixteen);
assert_eq!(eight_debug, "Eight");
assert_eq!(sixteen_debug, "Sixteen");
}
#[test]
fn test_word_size_clone_copy() {
let original = WordSize::Eight;
let cloned = original.clone();
let copied = original;
assert_eq!(original, cloned);
assert_eq!(original, copied);
assert_eq!(cloned, copied);
}
#[test]
fn test_color_type_alias() {
let red_color: Color = Color::RED;
let red_rgb888: Rgb888 = Rgb888::RED;
assert_eq!(red_color, red_rgb888);
assert_eq!(red_color.r(), 255);
assert_eq!(red_color.g(), 0);
assert_eq!(red_color.b(), 0);
let colors = [
(Color::RED, (255, 0, 0)),
(Color::GREEN, (0, 255, 0)),
(Color::BLUE, (0, 0, 255)),
(Color::WHITE, (255, 255, 255)),
(Color::BLACK, (0, 0, 0)),
(Color::CYAN, (0, 255, 255)),
(Color::MAGENTA, (255, 0, 255)),
(Color::YELLOW, (255, 255, 0)),
];
for (color, (r, g, b)) in colors {
assert_eq!(color.r(), r);
assert_eq!(color.g(), g);
assert_eq!(color.b(), b);
}
}
#[test]
fn test_color_construction() {
let custom_color = Color::new(128, 64, 192);
assert_eq!(custom_color.r(), 128);
assert_eq!(custom_color.g(), 64);
assert_eq!(custom_color.b(), 192);
let rgb888_color = Rgb888::new(128, 64, 192);
assert_eq!(custom_color, rgb888_color);
}
#[test]
fn test_helper_functions_const() {
const ROWS: usize = 32;
const COMPUTED_NROWS: usize = compute_rows(ROWS);
const BITS: u8 = 4;
const COMPUTED_FRAME_COUNT: usize = compute_frame_count(BITS);
assert_eq!(COMPUTED_NROWS, 16);
assert_eq!(COMPUTED_FRAME_COUNT, 15);
}
#[test]
fn test_realistic_panel_configurations() {
struct PanelConfig {
rows: usize,
cols: usize,
bits: u8,
}
let configs = [
PanelConfig {
rows: 32,
cols: 64,
bits: 3,
}, PanelConfig {
rows: 64,
cols: 64,
bits: 4,
}, PanelConfig {
rows: 32,
cols: 32,
bits: 5,
}, PanelConfig {
rows: 16,
cols: 32,
bits: 6,
}, ];
for config in configs {
let nrows = compute_rows(config.rows);
let frame_count = compute_frame_count(config.bits);
assert!(nrows > 0);
assert!(nrows <= config.rows);
assert_eq!(nrows * 2, config.rows);
assert!(config.cols > 0);
assert!(config.cols <= 256);
assert!(frame_count > 0);
assert!(frame_count < 256);
let prev_frame_count = compute_frame_count(config.bits - 1);
assert!(frame_count > prev_frame_count);
}
}
#[test]
fn test_memory_calculations() {
const ROWS: usize = 64;
const COLS: usize = 64;
const BITS: u8 = 4;
const NROWS: usize = compute_rows(ROWS);
const FRAME_COUNT: usize = compute_frame_count(BITS);
assert_eq!(NROWS, 32);
assert_eq!(FRAME_COUNT, 15);
assert_eq!(NROWS * 2, ROWS);
assert_eq!(FRAME_COUNT, (1 << BITS) - 1);
assert!(COLS > 0);
assert!(COLS <= 256); }
#[test]
fn test_edge_cases() {
assert_eq!(compute_rows(2), 1);
assert_eq!(compute_frame_count(1), 1);
assert_eq!(compute_rows(512), 256);
assert_eq!(compute_frame_count(8), 255);
assert_eq!(compute_rows(0), 0);
}
#[test]
fn test_word_size_equality() {
assert_eq!(WordSize::Eight, WordSize::Eight);
assert_eq!(WordSize::Sixteen, WordSize::Sixteen);
assert_ne!(WordSize::Eight, WordSize::Sixteen);
assert_ne!(WordSize::Sixteen, WordSize::Eight);
}
#[test]
fn test_bit_depth_limits() {
for bits in 1..=8 {
let frame_count = compute_frame_count(bits);
assert!(frame_count > 0);
assert!(frame_count < (1 << bits));
assert_eq!(frame_count, (1 << bits) - 1);
}
}
#[test]
fn test_documentation_examples() {
const ROWS: usize = 32;
const COLS: usize = 64;
const NROWS: usize = ROWS / 2;
const BITS: u8 = 8;
const FRAME_COUNT: usize = (1 << BITS) - 1;
assert_eq!(compute_rows(ROWS), NROWS);
assert_eq!(compute_frame_count(BITS), FRAME_COUNT);
assert_eq!(ROWS, 32);
assert_eq!(COLS, 64);
assert_eq!(NROWS, 16);
assert_eq!(FRAME_COUNT, 255);
assert!(COLS > 0);
assert_eq!(NROWS * 2, ROWS);
}
}