Glyph caching

This commit is contained in:
mars 2022-10-05 19:04:45 -06:00
parent 0ba8cb0eb8
commit 90493fe11e
1 changed files with 136 additions and 67 deletions

View File

@ -8,12 +8,14 @@ use alacritty_terminal::event_loop::{
EventLoop as TermEventLoop, Msg as TermMsg, State as TermState,
};
use alacritty_terminal::sync::FairMutex;
use alacritty_terminal::term::cell::{Cell, Flags as CellFlags};
use alacritty_terminal::term::color::{Colors, Rgb};
use alacritty_terminal::tty::Pty;
use alacritty_terminal::Term;
use mio_extras::channel::Sender as MioSender;
use softbuffer::GraphicsContext;
use std::borrow::Cow;
use std::collections::HashMap;
use std::sync::mpsc::{channel, Receiver, Sender};
use std::sync::Arc;
use std::thread::JoinHandle;
@ -40,18 +42,66 @@ impl EventListener for TermListener {
}
}
pub struct Graphics {
normal_font: bdf::Font,
bold_font: bdf::Font,
width: usize,
height: usize,
cell_width: usize,
cell_height: usize,
colors: Colors,
buffer: Vec<u32>,
#[derive(Clone, Hash, PartialEq, Eq)]
pub struct GlyphInfo {
c: char,
fg: u32,
bg: u32,
flags: CellFlags,
}
impl Graphics {
pub struct GlyphCache {
renderer: BitmapGlyphRenderer,
colors: Colors,
glyphs: HashMap<GlyphInfo, Box<[u32]>>,
}
impl GlyphCache {
pub fn new(colors: Colors) -> Self {
Self {
renderer: BitmapGlyphRenderer::new(),
colors,
glyphs: Default::default(),
}
}
pub fn lookup(&mut self, cell: &Cell) -> &[u32] {
let glyph_info = GlyphInfo {
c: cell.c,
fg: self.color_to_u32(&cell.fg),
bg: self.color_to_u32(&cell.bg),
flags: cell.flags,
};
self.glyphs
.entry(glyph_info.clone())
.or_insert_with(|| self.renderer.draw(&glyph_info).into_boxed_slice())
}
// TODO deduplicate
pub fn color_to_u32(&self, color: &Color) -> u32 {
let rgb = match color {
Color::Named(name) => self.colors[*name].unwrap(),
Color::Spec(rgb) => *rgb,
Color::Indexed(index) => self.colors[*index as usize].unwrap_or(Rgb {
r: 255,
g: 0,
b: 255,
}),
};
((rgb.r as u32) << 16) | ((rgb.g as u32) << 8) | (rgb.b as u32)
}
}
pub struct BitmapGlyphRenderer {
normal_font: bdf::Font,
bold_font: bdf::Font,
cell_width: usize,
cell_height: usize,
}
impl BitmapGlyphRenderer {
pub fn new() -> Self {
let normal_font = bdf::read(NORMAL_FONT_DATA).unwrap();
let bold_font = bdf::read(BOLD_FONT_DATA).unwrap();
@ -63,6 +113,67 @@ impl Graphics {
let cell_width = normal_font.bounds().width as usize;
let cell_height = normal_font.bounds().height as usize;
Self {
normal_font,
bold_font,
cell_width,
cell_height,
}
}
pub fn draw(&self, glyph: &GlyphInfo) -> Vec<u32> {
let mut buf = Vec::with_capacity(self.cell_width * self.cell_height);
let font = if glyph.flags.contains(CellFlags::BOLD) {
&self.bold_font
} else {
&self.normal_font
};
if let Some(bitmap) = font.glyphs().get(&glyph.c) {
let (bg, fg) = if glyph.flags.contains(CellFlags::INVERSE) {
(glyph.fg, glyph.bg)
} else {
(glyph.bg, glyph.fg)
};
for y in 0..self.cell_height {
if y == self.cell_height >> 1 && glyph.flags.contains(CellFlags::STRIKEOUT)
|| y == self.cell_height - 1 && glyph.flags.contains(CellFlags::UNDERLINE)
{
buf.resize(buf.len() + self.cell_width, fg);
} else {
for x in 0..self.cell_width {
let color = if bitmap.get(x as u32, y as u32) {
fg
} else {
bg
};
buf.push(color);
}
}
}
} else {
buf.resize(buf.capacity(), 0x00ff00ff);
}
buf
}
}
pub struct Graphics {
glyph_cache: GlyphCache,
width: usize,
height: usize,
cell_width: usize,
cell_height: usize,
colors: Colors,
buffer: Vec<u32>,
}
impl Graphics {
pub fn new() -> Self {
let width = 2000;
let height = 2000;
let buffer = vec![0u32; width * height];
@ -70,13 +181,14 @@ impl Graphics {
let mut colors = Colors::default();
Self::load_colors(&mut colors);
let glyph_cache = GlyphCache::new(colors.clone());
Self {
normal_font,
bold_font,
width,
height,
cell_width,
cell_height,
cell_width: glyph_cache.renderer.cell_width,
cell_height: glyph_cache.renderer.cell_height,
glyph_cache,
colors,
buffer,
}
@ -158,10 +270,6 @@ impl Graphics {
let mut buffer = vec![0u32; (width * height) as usize];
let content = term.renderable_content();
let mut missing_chars = std::collections::HashSet::new();
use alacritty_terminal::term::cell::Flags as CellFlags;
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;
@ -176,62 +284,23 @@ impl Graphics {
continue;
}
let font = if cell.flags.contains(CellFlags::BOLD) {
&self.bold_font
} else {
&self.normal_font
};
let glyph = self.glyph_cache.lookup(&cell);
let px_row = term_row * self.cell_height * width;
let mut px_start = px_row + term_col * self.cell_width;
if let Some(glyph) = font.glyphs().get(&cell.c) {
let (bg, fg) = if cell.flags.contains(CellFlags::INVERSE) {
(&cell.fg, &cell.bg)
} else {
(&cell.bg, &cell.fg)
};
for line in glyph.chunks_exact(self.cell_width) {
let px_end = px_start + self.cell_width;
let bg = self.color_to_u32(&bg);
let fg = self.color_to_u32(&fg);
let px_row = term_row * width * self.cell_height;
let px_start = px_row + term_col * self.cell_width;
for y in 0..self.cell_height {
let px_start = px_start + y * width;
for x in 0..self.cell_width {
let color = if glyph.get(x as u32, y as u32) {
fg
} else {
bg
};
buffer[px_start + x as usize] = color;
}
if px_end >= buffer.len() {
println!("piss");
break;
}
if cell.flags.contains(CellFlags::STRIKEOUT) {
let px_start = px_start + (self.cell_height >> 1) * width;
for x in 0..self.cell_width {
buffer[px_start + x as usize] = fg;
}
}
if cell.flags.contains(CellFlags::UNDERLINE) {
let px_start = px_start + (self.cell_height - 1) * width;
for x in 0..self.cell_width {
buffer[px_start + x as usize] = fg;
}
}
} else {
missing_chars.insert(cell.c);
buffer[px_start..px_end].copy_from_slice(line);
px_start += width;
}
}
if !missing_chars.is_empty() {
log::debug!("Missing characters: {:?}", missing_chars);
}
let term_row = content.cursor.point.line.0 as usize;
let term_col = content.cursor.point.column.0 as usize;