use crossterm::{cursor, queue, style};
use std::io::{Stdout, Write};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Cell {
pub character: char,
pub foreground: crossterm::style::Color,
pub background: crossterm::style::Color,
}
impl Default for Cell {
fn default() -> Self {
Self {
character: ' ',
foreground: crossterm::style::Color::White,
background: crossterm::style::Color::Black,
}
}
}
pub struct Renderer {
pub width: u16,
pub height: u16,
front_buffer: Vec<Cell>,
back_buffer: Vec<Cell>,
first_frame: bool,
}
impl Renderer {
pub fn new(width: u16, height: u16) -> Self {
let size = (width as usize) * (height as usize);
Self {
width,
height,
front_buffer: vec![Cell::default(); size],
back_buffer: vec![Cell::default(); size],
first_frame: true,
}
}
pub fn resize(&mut self, width: u16, height: u16) {
self.width = width;
self.height = height;
let size = (width as usize) * (height as usize);
self.front_buffer = vec![Cell::default(); size];
self.back_buffer = vec![Cell::default(); size];
self.first_frame = true;
}
pub fn clear(&mut self) {
for cell in &mut self.back_buffer {
*cell = Cell::default();
}
}
pub fn set_cell(&mut self, column: u16, row: u16, cell: Cell) {
if column < self.width && row < self.height {
let index = (row as usize) * (self.width as usize) + (column as usize);
self.back_buffer[index] = cell;
}
}
pub fn present(&mut self, stdout: &mut Stdout) -> std::io::Result<()> {
let mut last_foreground: Option<crossterm::style::Color> = None;
let mut last_background: Option<crossterm::style::Color> = None;
for row in 0..self.height {
for column in 0..self.width {
let index = (row as usize) * (self.width as usize) + (column as usize);
let back = self.back_buffer[index];
if self.first_frame || self.front_buffer[index] != back {
queue!(stdout, cursor::MoveTo(column, row))?;
if last_foreground != Some(back.foreground) {
queue!(stdout, style::SetForegroundColor(back.foreground))?;
last_foreground = Some(back.foreground);
}
if last_background != Some(back.background) {
queue!(stdout, style::SetBackgroundColor(back.background))?;
last_background = Some(back.background);
}
queue!(stdout, style::Print(back.character))?;
}
}
}
stdout.flush()?;
std::mem::swap(&mut self.front_buffer, &mut self.back_buffer);
self.first_frame = false;
Ok(())
}
}