Add Canvas abstractions

This commit is contained in:
mars 2022-10-06 15:45:53 -06:00
parent 3fa469537f
commit acefda1d28
2 changed files with 136 additions and 54 deletions

View File

@ -11,6 +11,113 @@ static BOLD_FONT_DATA: &[u8] = include_bytes!("ter-u16b.bdf");
use super::TermListener;
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
pub struct Point(pub usize, pub usize);
impl Point {
pub const ZERO: Self = Self(0, 0);
pub const ONE: Self = Self(1, 1);
}
impl std::ops::Add for Point {
type Output = Self;
fn add(self, rhs: Self) -> Self {
Self(self.0 + rhs.0, self.1 + rhs.1)
}
}
impl std::ops::Mul for Point {
type Output = Self;
fn mul(self, rhs: Self) -> Self {
Self(self.0 * rhs.0, self.1 * rhs.1)
}
}
#[derive(Clone, Debug)]
pub struct Canvas {
pub width: usize,
pub height: usize,
pub buffer: Box<[u32]>,
}
impl Canvas {
#[inline]
pub fn new(width: usize, height: usize) -> Self {
Self {
width,
height,
buffer: vec![0; width * height].into_boxed_slice(),
}
}
#[inline]
pub fn clip(&self, xy: Point) -> Point {
Point(xy.0.min(self.width - 1), xy.1.min(self.height - 1))
}
#[inline]
pub fn draw_rect(&mut self, color: u32, tl: Point, br: Point) {
if tl.0 >= self.width || tl.1 >= self.height {
return;
}
let br = self.clip(br);
unsafe {
self.draw_rect_unchecked(color, tl, br);
}
}
#[inline]
pub unsafe fn draw_rect_unchecked(&mut self, color: u32, tl: Point, br: Point) {
let row = tl.1 * self.width;
let mut dst_start = row + tl.0;
let mut dst_end = row + br.0;
for _ in tl.1..br.1 {
let dst = dst_start..dst_end;
self.buffer.get_unchecked_mut(dst).fill(color);
dst_start += self.width;
dst_end += self.width;
}
}
#[inline]
pub fn blit(&mut self, xy: Point, src: &Canvas) {
let size = Point(src.width, src.height);
self.blit_from_slice(xy, &src.buffer, size);
}
#[inline]
pub fn blit_from_slice(&mut self, xy: Point, src_buffer: &[u32], src_size: Point) {
if xy.0 < self.width && xy.1 < self.height {
let cw = (self.width - xy.0 - 1).min(src_size.0);
let ch = (self.height - xy.1 - 1).min(src_size.1);
let dst_row = xy.1 * self.width;
let mut dst_start = dst_row + xy.0;
let mut dst_end = dst_start + cw;
let mut src_start = 0;
let mut src_end = cw;
for _ in 0..ch {
unsafe {
let dst_buf = self.buffer.get_unchecked_mut(dst_start..dst_end);
let src_buf = src_buffer.get_unchecked(src_start..src_end);
dst_buf.copy_from_slice(src_buf);
dst_start += self.width;
dst_end += self.width;
src_start += src_size.0;
src_end += src_size.0;
}
}
}
}
}
#[derive(Clone, Hash, PartialEq, Eq)]
pub struct GlyphInfo {
c: char,
@ -134,21 +241,17 @@ impl BitmapGlyphRenderer {
}
pub struct Graphics {
pub width: usize,
pub height: usize,
pub canvas: Canvas,
pub cell_width: usize,
pub cell_height: usize,
pub colors: Colors,
glyph_cache: GlyphCache,
cell_cache: HashMap<(usize, usize), GlyphInfo>,
buffer: Vec<u32>,
cell_cache: HashMap<Point, GlyphInfo>,
}
impl Graphics {
pub fn new() -> Self {
let width = 2000;
let height = 2000;
let buffer = vec![0u32; width * height];
let canvas = Canvas::new(2000, 2000);
let mut colors = Colors::default();
Self::load_colors(&mut colors);
@ -157,14 +260,12 @@ impl Graphics {
let cell_cache = Default::default();
Self {
width,
height,
canvas,
cell_width: glyph_cache.renderer.cell_width,
cell_height: glyph_cache.renderer.cell_height,
glyph_cache,
cell_cache,
colors,
buffer,
}
}
@ -228,12 +329,7 @@ impl Graphics {
}
pub fn resize(&mut self, width: usize, height: usize) {
self.width = width;
self.height = height;
self.buffer.clear();
self.buffer.resize(width * height, 0);
self.canvas = Canvas::new(width, height);
self.cell_cache.clear();
}
@ -245,19 +341,19 @@ impl Graphics {
(size.width as usize, size.height as usize)
};
if width != self.width || height != self.height {
if width != self.canvas.width || height != self.canvas.height {
self.resize(width, height);
}
let cursor_color = Color::Named(NamedColor::Foreground);
let cursor_color = self.color_to_u32(&cursor_color);
let buffer = self.buffer.as_mut_slice();
let cell_size = Point(self.cell_width, self.cell_height);
let content = term.renderable_content();
for cell in content.display_iter.into_iter() {
let term_row = cell.point.line.0 as usize;
let term_col = cell.point.column.0 as usize;
let term_coords = (term_row, term_col);
let term_row = cell.point.line.0 as usize;
let term_coords = Point(term_col, term_row);
if (term_row + 1) * self.cell_height >= height
|| (term_col + 1) * self.cell_width >= width
@ -277,26 +373,14 @@ impl Graphics {
self.cell_cache.insert(term_coords, glyph_info.clone());
}
let xy = term_coords * cell_size;
let glyph = self.glyph_cache.lookup(&glyph_info);
let px_row = term_row * self.cell_height * width;
let mut px_start = px_row + term_col * self.cell_width;
for line in glyph.chunks_exact(self.cell_width) {
let px_end = px_start + self.cell_width;
if px_end >= buffer.len() {
println!("piss");
break;
}
buffer[px_start..px_end].copy_from_slice(line);
px_start += width;
}
self.canvas.blit_from_slice(xy, glyph, cell_size);
}
let term_row = content.cursor.point.line.0 as usize;
let term_col = content.cursor.point.column.0 as usize;
let term_coords = (term_row, term_col);
let term_row = content.cursor.point.line.0 as usize;
let term_coords = Point(term_col, term_row);
if (term_row + 1) * self.cell_height < height && (term_col + 1) * self.cell_width < width {
let mut px_row = term_row * width * self.cell_height + term_col * self.cell_width;
@ -304,27 +388,25 @@ impl Graphics {
use alacritty_terminal::ansi::CursorShape;
match content.cursor.shape {
CursorShape::Block => {
for _y in 0..self.cell_height {
for x in 0..self.cell_width {
buffer[px_row + x as usize] = cursor_color;
}
px_row += width;
}
let tl = term_coords * cell_size;
let br = tl + cell_size;
self.canvas.draw_rect(cursor_color, tl, br);
}
CursorShape::Beam => {
for _y in 0..self.cell_height {
buffer[px_row] = cursor_color;
px_row += width;
}
let tl = term_coords * cell_size;
let br = tl + Point(cell_size.0, 2);
self.canvas.draw_rect(cursor_color, tl, br);
}
CursorShape::Underline => {
px_row += (self.cell_height - 1) * width;
for x in 0..self.cell_width {
buffer[px_row + x as usize] = cursor_color;
}
let mut tl = term_coords * cell_size;
let br = tl + cell_size;
tl.0 += cell_size.0 - 2;
self.canvas.draw_rect(cursor_color, tl, br);
}
CursorShape::HollowBlock => {
// TODO Canvas::draw_hollow_rect()
let buffer = &mut self.canvas.buffer;
for x in 0..self.cell_width {
buffer[px_row + x as usize] = cursor_color;
}
@ -348,7 +430,7 @@ impl Graphics {
}
let copy_start = std::time::Instant::now();
context.set_buffer(&buffer, width as u16, height as u16);
context.set_buffer(&self.canvas.buffer, width as u16, height as u16);
log::debug!("buffer copy time: {:?}", copy_start.elapsed());
log::debug!("redraw() time: {:?}", start.elapsed());

View File

@ -55,8 +55,8 @@ impl App {
let graphics = Graphics::new();
let term_size = alacritty_terminal::term::SizeInfo::new(
graphics.width as f32,
graphics.height as f32,
graphics.canvas.width as f32,
graphics.canvas.height as f32,
graphics.cell_width as f32,
graphics.cell_height as f32,
0.0,