use std::{collections::HashMap, iter::Iterator};
use grid::Grid;
use once_cell::sync::Lazy;
use ratatui::{
style::Color,
widgets::canvas::{Painter, Shape},
};
use crate::preview::terminal::render::{AsciiRender, AsciiRenders, MonoRender, MoonRender, Render};
#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub enum RenderType {
AsciiLevel10,
AsciiLevel70,
Moon,
Mono,
}
type BoxedRender<Pixel> = Box<dyn Render<Pixel = Pixel> + Send + Sync>;
pub static CHAR_RENDERS: Lazy<HashMap<RenderType, BoxedRender<char>>> = Lazy::new(|| {
let mut renders: HashMap<RenderType, BoxedRender<char>> = HashMap::new();
renders.insert(
RenderType::AsciiLevel10,
Box::new(AsciiRender::new(AsciiRenders::Level10)),
);
renders.insert(
RenderType::AsciiLevel70,
Box::new(AsciiRender::new(AsciiRenders::Level70)),
);
renders.insert(RenderType::Moon, Box::new(MoonRender::new()));
renders
});
pub static MONO_RENDER: Lazy<MonoRender> = Lazy::new(MonoRender::default);
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub struct CacheKey {
pub index: usize,
pub rt: RenderType,
pub width: u32,
pub height: u32,
}
pub enum GlyphCache {
Canvas(GlyphCanvasShape),
Paragraph(GlyphParagraph),
}
pub struct GlyphCanvasShape {
h_pad: f64,
v_pad: f64,
canvas_height: f64,
bitmap: Grid<bool>,
}
impl GlyphCanvasShape {
pub fn new(bitmap: Grid<bool>, canvas_width: f64, canvas_height: f64) -> Self {
let h_pad = ((canvas_width - bitmap.cols() as f64) / 2.0).floor();
let v_pad = ((canvas_height - bitmap.rows() as f64) / 2.0).floor();
Self {
h_pad,
v_pad,
canvas_height,
bitmap,
}
}
fn points(&self) -> GlyphCanvasShapePoints<'_> {
GlyphCanvasShapePoints::new(self)
}
}
struct GlyphCanvasShapePoints<'a> {
shape: &'a GlyphCanvasShape,
start: bool,
x: usize,
y: usize,
}
impl<'a> GlyphCanvasShapePoints<'a> {
fn new(shape: &'a GlyphCanvasShape) -> Self {
Self {
shape,
start: false,
x: 0,
y: 0,
}
}
fn next_x_y(&mut self) -> bool {
if self.start {
self.x += 1;
if self.x >= self.shape.bitmap.cols() {
self.y += 1;
self.x = 0;
}
} else {
self.start = true;
}
self.y < self.shape.bitmap.rows()
}
}
impl<'a> Iterator for GlyphCanvasShapePoints<'a> {
type Item = (f64, f64);
fn next(&mut self) -> Option<Self::Item> {
loop {
if !self.next_x_y() {
return None;
}
if self.shape.bitmap[(self.y, self.x)] {
let result = (
self.x as f64 + self.shape.h_pad,
self.shape.canvas_height - self.y as f64 - self.shape.v_pad,
);
return Some(result);
}
}
}
}
impl Shape for GlyphCanvasShape {
fn draw(&self, painter: &mut Painter<'_, '_>) {
for (x, y) in self.points() {
if let Some((x, y)) = painter.get_point(x, y) {
painter.paint(x, y, Color::Reset);
}
}
}
}
pub struct GlyphParagraph {
pub lines: Vec<String>,
}
impl GlyphParagraph {
pub fn new(bitmap: Grid<char>) -> Self {
let lines = bitmap.iter_rows().map(String::from_iter).collect();
Self { lines }
}
}