use crate::rendering::{display::Display, pixel::Pixel};
use crossterm::queue;
use std::io;
use std::io::Write;
pub trait Renderer {
fn render_pixel(&mut self, x: usize, y: usize, pixel: Pixel, depth: i32);
fn flush(&mut self) -> io::Result<()> { Ok(()) }
fn set_default_bg_color(&mut self, color: [u8; 3]) {
}
}
impl<W: Write> Renderer for DisplayRenderer<W> {
fn render_pixel(&mut self, x: usize, y: usize, pixel: Pixel, depth: i32) {
DisplayRenderer::render_pixel(self, x, y, pixel, depth);
}
fn flush(&mut self) -> io::Result<()> {
DisplayRenderer::flush(self)
}
fn set_default_bg_color(&mut self, color: [u8; 3]) {
DisplayRenderer::set_default_bg_color(self, color);
}
}
pub struct DisplayRenderer<W: Write> {
width: usize,
height: usize,
display: Display<Pixel>,
prev_display: Display<Pixel>,
depth_buffer: Display<i32>,
bg_depth_buffer: Display<i32>,
default_fg_color: [u8; 3],
last_fg_color: [u8; 3],
default_bg_color: [u8; 3],
last_bg_color: [u8; 3],
sink: W,
}
impl<W: Write> DisplayRenderer<W> {
pub fn new_with_sink(width: usize, height: usize, sink: W) -> Self {
let mut prev_display = Display::new(width, height, Pixel::default());
prev_display.fill(Pixel::default().with_color([1, 2, 3]));
Self {
width,
height,
display: Display::new(width, height, Pixel::default()),
prev_display,
depth_buffer: Display::new(width, height, i32::MIN),
bg_depth_buffer: Display::new(width, height, i32::MIN),
sink,
default_fg_color: [255, 255, 255],
last_fg_color: [255, 255, 255],
default_bg_color: [0, 0, 0],
last_bg_color: [0, 0, 0],
}
}
pub fn width(&self) -> usize {
self.width
}
pub fn height(&self) -> usize {
self.height
}
pub fn set_default_fg_color(&mut self, color: [u8; 3]) {
self.default_fg_color = color;
}
pub fn set_default_bg_color(&mut self, color: [u8; 3]) {
self.default_bg_color = color;
}
pub fn resize_discard(&mut self, width: usize, height: usize) {
self.width = width;
self.height = height;
self.display.resize_discard(width, height);
if self.prev_display.height() > height {
self.prev_display.resize_discard(width, height);
self.prev_display
.fill(Pixel::default().with_color([1, 2, 3]));
} else {
self.prev_display.resize_keep(width, height);
}
self.depth_buffer.resize_discard(width, height);
self.bg_depth_buffer.resize_discard(width, height);
}
pub fn resize_keep(&mut self, width: usize, height: usize) {
self.width = width;
self.height = height;
self.display.resize_keep(width, height);
self.prev_display.resize_keep(width, height);
self.depth_buffer.resize_keep(width, height);
self.bg_depth_buffer.resize_keep(width, height);
}
pub fn render_pixel(&mut self, x: usize, y: usize, new_pixel: Pixel, new_depth: i32) {
if x >= self.width || y >= self.height {
return;
}
let old_depth = self.depth_buffer[(x, y)];
if old_depth == i32::MIN {
self.display[(x, y)] = new_pixel;
self.depth_buffer[(x, y)] = new_depth;
if new_pixel.bg_color.is_solid() {
self.bg_depth_buffer[(x, y)] = new_depth;
}
return;
}
let old_pixel = self.display[(x, y)];
let old_bg_depth = self.bg_depth_buffer[(x, y)];
let mut new_bg_color = old_pixel.bg_color;
if old_bg_depth < new_depth && new_pixel.bg_color.is_solid() {
self.bg_depth_buffer[(x, y)] = new_depth;
new_bg_color = new_pixel.bg_color;
}
let (lower_pixel, upper_pixel) = if old_depth < new_depth {
(old_pixel, new_pixel)
} else {
(new_pixel, old_pixel)
};
let mut created_pixel = upper_pixel.put_over(lower_pixel);
created_pixel.bg_color = new_bg_color;
self.display[(x, y)] = created_pixel;
self.depth_buffer[(x, y)] = old_depth.max(new_depth);
}
pub fn reset_screen(&mut self) {
self.display.clear();
self.depth_buffer.clear();
self.bg_depth_buffer.clear();
}
pub fn flush(&mut self) -> io::Result<()> {
queue!(self.sink, crossterm::cursor::MoveTo(0, 0))?;
let render_everything = self.last_bg_color != self.default_bg_color
|| self.last_fg_color != self.default_fg_color;
self.last_fg_color = self.default_fg_color;
self.last_bg_color = self.default_bg_color;
queue!(
self.sink,
crossterm::style::SetColors(crossterm::style::Colors {
foreground: Some(crossterm::style::Color::Rgb {
r: self.default_fg_color[0],
g: self.default_fg_color[1],
b: self.default_fg_color[2],
}),
background: Some(crossterm::style::Color::Rgb {
r: self.default_bg_color[0],
g: self.default_bg_color[1],
b: self.default_bg_color[2],
}),
}),
)?;
let mut curr_pos = (0, 0);
for y in 0..self.height {
for x in 0..self.width {
let pixel = self.display[(x, y)];
if !render_everything {
if pixel == self.prev_display[(x, y)] {
continue;
}
if curr_pos != (x, y) {
queue!(self.sink, crossterm::cursor::MoveTo(x as u16, y as u16))?;
}
}
let mut new_color_change = None;
let mut new_bg_color_change = None;
let new_color = pixel.color.unwrap_or(self.default_fg_color);
if new_color != self.last_fg_color {
new_color_change = Some(crossterm::style::Color::Rgb {
r: new_color[0],
g: new_color[1],
b: new_color[2],
});
self.last_fg_color = new_color;
}
let new_bg_color = pixel.bg_color.unwrap_or(self.default_bg_color);
if new_bg_color != self.last_bg_color {
new_bg_color_change = Some(crossterm::style::Color::Rgb {
r: new_bg_color[0],
g: new_bg_color[1],
b: new_bg_color[2],
});
self.last_bg_color = new_bg_color;
}
queue!(
self.sink,
crossterm::style::SetColors(crossterm::style::Colors {
foreground: new_color_change,
background: new_bg_color_change,
})
)?;
queue!(self.sink, crossterm::style::Print(pixel.c))?;
curr_pos = (x, y);
}
if y < self.height - 1 {
queue!(self.sink, crossterm::cursor::MoveToNextLine(1))?;
curr_pos = (0, y + 1);
}
}
self.sink.flush()?;
std::mem::swap(&mut self.display, &mut self.prev_display);
self.last_fg_color = self.default_fg_color;
self.last_bg_color = self.default_bg_color;
Ok(())
}
}