#![no_std]
#[deny(clippy::all)]
#[macro_use]
extern crate alloc;
use {
alloc::vec::Vec,
core::{
convert::{TryFrom, TryInto},
mem::size_of,
ops::{Index, IndexMut},
ptr,
sync::atomic::{AtomicU64, Ordering::Relaxed},
},
};
pub use rgb::RGB8;
pub use vek::Vec2;
#[derive(Debug, Default)]
pub struct Controller {
vram: Vram,
collection: Vec<Layer>,
}
impl Controller {
pub unsafe fn new(
resolution: Vec2<u32>,
bits_per_pixel: u32,
base_addr_of_vram: usize,
) -> Self {
Self {
vram: Vram::new(resolution, bits_per_pixel, base_addr_of_vram),
collection: Vec::new(),
}
}
pub fn add_layer(&mut self, layer: Layer) -> Id {
let id = layer.id;
let top_left = layer.top_left;
let len = layer.len;
self.collection.push(layer);
self.redraw(top_left, len);
id
}
pub fn edit_layer<T>(&mut self, id: Id, f: T) -> Result<(), Error>
where
T: Fn(&mut Layer),
{
let layer = self.id_to_layer(id)?;
let layer_top_left = layer.top_left;
let layer_len = layer.len;
f(layer);
self.redraw(layer_top_left, layer_len);
Ok(())
}
pub fn set_pixel(
&mut self,
id: Id,
coord: Vec2<u32>,
color: Option<RGB8>,
) -> Result<(), Error> {
let layer = self.id_to_layer(id)?;
let layer_top_left = layer.top_left;
layer[usize::try_from(coord.y).unwrap()][usize::try_from(coord.x).unwrap()] = color;
self.redraw(layer_top_left + coord.as_(), Vec2::one());
Ok(())
}
pub fn slide_layer(&mut self, id: Id, new_top_left: Vec2<i32>) -> Result<(), Error> {
let layer = self.id_to_layer(id)?;
let old_top_left = layer.top_left;
let layer_len = layer.len;
layer.slide(new_top_left);
self.redraw(old_top_left, layer_len);
self.redraw(new_top_left, layer_len);
Ok(())
}
fn redraw(&self, mut vram_top_left: Vec2<i32>, len: Vec2<u32>) {
vram_top_left = Vec2::<i32>::max(
Vec2::min(vram_top_left, self.vram.resolution.as_()),
Vec2::zero(),
);
let vram_bottom_right = vram_top_left + len.as_();
let vram_bottom_right = Vec2::<i32>::max(
Vec2::min(vram_bottom_right, self.vram.resolution.as_()),
Vec2::zero(),
);
for layer in &self.collection {
let layer_bottom_right = layer.top_left + layer.len.as_();
let top_left =
Vec2::<i32>::min(Vec2::max(vram_top_left, layer.top_left), layer_bottom_right);
let bottom_right =
Vec2::<i32>::max(top_left, Vec2::min(vram_bottom_right, layer_bottom_right));
for y in top_left.y..bottom_right.y {
for x in top_left.x..bottom_right.x {
if let Some(rgb) =
layer.buf[(y - layer.top_left.y) as usize][(x - layer.top_left.x) as usize]
{
self.vram.set_color(Vec2::new(x, y).as_(), rgb)
}
}
}
}
}
fn id_to_layer(&mut self, id: Id) -> Result<&mut Layer, Error> {
self.collection
.iter_mut()
.find(|layer| layer.id == id)
.ok_or_else(|| Error::NoSuchLayer(id))
}
}
#[derive(PartialEq, Eq, Hash, Debug, Default)]
pub struct Layer {
buf: Vec<Vec<Option<RGB8>>>,
top_left: Vec2<i32>,
len: Vec2<u32>,
id: Id,
}
impl Layer {
pub fn new(top_left: Vec2<i32>, len: Vec2<u32>) -> Self {
Self {
buf: vec![vec![None; len.x.try_into().unwrap()]; len.y.try_into().unwrap()],
top_left,
len,
id: Id::new(),
}
}
fn slide(&mut self, new_top_left: Vec2<i32>) {
self.top_left = new_top_left;
}
}
impl Index<usize> for Layer {
type Output = [Option<RGB8>];
fn index(&self, index: usize) -> &Self::Output {
&self.buf[index]
}
}
impl IndexMut<usize> for Layer {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
&mut self.buf[index]
}
}
#[derive(Copy, Clone, PartialOrd, PartialEq, Ord, Eq, Hash, Debug, Default)]
pub struct Id(u64);
impl Id {
fn new() -> Self {
static ID: AtomicU64 = AtomicU64::new(0);
Self(ID.fetch_add(1, Relaxed))
}
}
#[derive(Copy, Clone, PartialOrd, PartialEq, Ord, Eq, Hash, Debug)]
pub enum Error {
NoSuchLayer(Id),
}
#[derive(Debug, Default)]
struct Vram {
resolution: Vec2<u32>,
bpp: u32,
base_addr: usize,
}
impl Vram {
fn new(resolution: Vec2<u32>, bpp: u32, base_addr: usize) -> Self {
Self {
resolution,
bpp,
base_addr,
}
}
fn set_color(&self, coord: Vec2<u32>, rgb: RGB8) {
assert_eq!(
Vec2::<u32>::max(Vec2::<u32>::min(coord, self.resolution), Vec2::zero()),
coord
);
let offset_from_base = ((coord.y * self.resolution.x + coord.x) * self.bpp / 8) as isize;
let ptr = (self.base_addr as isize + offset_from_base) as usize;
unsafe {
ptr::write(ptr as _, rgb.b);
ptr::write((ptr + size_of::<u8>()) as _, rgb.g);
ptr::write((ptr + size_of::<u8>() * 2) as _, rgb.r);
}
}
}