Improved color management

This commit is contained in:
marceline-cramer 2023-08-23 12:29:03 -06:00
parent f113ee79bf
commit ef1d881c57
2 changed files with 88 additions and 51 deletions

View File

@ -131,7 +131,7 @@ impl Canvas {
}
}
#[derive(Clone, Hash, PartialEq, Eq)]
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct GlyphInfo {
c: char,
fg: u32,
@ -141,24 +141,22 @@ pub struct GlyphInfo {
pub struct GlyphCache {
renderer: BitmapGlyphRenderer,
colors: Colors,
glyphs: HashMap<GlyphInfo, Box<[u32]>>,
}
impl GlyphCache {
pub fn new(colors: Colors, font_config: &FontConfig) -> Self {
pub fn new(font_config: &FontConfig) -> Self {
Self {
renderer: BitmapGlyphRenderer::new(font_config),
colors,
glyphs: Default::default(),
}
}
pub fn cell_info(&self, cell: &Cell) -> GlyphInfo {
pub fn cell_info(&self, graphics: &Graphics, cell: &Cell) -> GlyphInfo {
GlyphInfo {
c: cell.c,
fg: self.color_to_u32(&cell.fg),
bg: self.color_to_u32(&cell.bg),
fg: graphics.color_to_u32(&cell.fg),
bg: graphics.color_to_u32(&cell.bg),
flags: cell.flags,
}
}
@ -168,21 +166,6 @@ impl GlyphCache {
.entry(glyph.clone())
.or_insert_with(|| self.renderer.draw(glyph).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 {
@ -213,6 +196,8 @@ impl BitmapGlyphRenderer {
}
pub fn draw(&self, glyph: &GlyphInfo) -> Vec<u32> {
// eprintln!("rendering glyph: {:?}", glyph);
let mut buf = Vec::with_capacity(self.cell_width * self.cell_height);
let font = if glyph.flags.contains(CellFlags::BOLD) {
@ -246,6 +231,7 @@ impl BitmapGlyphRenderer {
}
}
} else {
eprintln!("character bitmap miss: {:?}", glyph.c);
buf.resize(buf.capacity(), 0x00ff00ff);
}
@ -275,7 +261,7 @@ impl Graphics {
let bg = ((bg.r as u32) << 16) | ((bg.g as u32) << 8) | (bg.b as u32);
let canvas = Canvas::new(bg, 2000, 2000);
let glyph_cache = GlyphCache::new(colors.clone(), &config.fonts);
let glyph_cache = GlyphCache::new(&config.fonts);
let cell_cache = Default::default();
Self {
@ -295,14 +281,16 @@ impl Graphics {
pub fn load_colors(config: &ColorConfig, colors: &mut Colors) {
use NamedColor::*;
let mut set = |color: NamedColor, value: hex_color::HexColor| {
let rgb = Rgb {
let rgb = |value: hex_color::HexColor| {
Some(Rgb {
r: value.r,
g: value.g,
b: value.b,
};
})
};
colors[color] = Some(rgb);
let mut set = |color: NamedColor, value: hex_color::HexColor| {
colors[color] = rgb(value);
};
set(Background, config.background);
@ -323,19 +311,69 @@ impl Graphics {
set(BrightMagenta, config.bright_magenta);
set(BrightCyan, config.bright_cyan);
set(BrightWhite, config.bright_white);
colors[256] = rgb(config.foreground);
colors[257] = rgb(config.background);
}
pub fn get_color_idx(&self, index: usize) -> Rgb {
if let Some(color) = self.colors[index] {
color
} else if index > 255 {
eprintln!("color miss and out-of-bounds at {index}");
Rgb {
r: 0xff,
g: 0x00,
b: 0xff,
}
} else if let Some(gray) = index.checked_sub(232) {
let value = gray as u8 * 10 + 8;
Rgb {
r: value,
g: value,
b: value,
}
} else if let Some(cube_idx) = index.checked_sub(16) {
let cube_idx = cube_idx as u8;
let r = cube_idx / 36;
let g = (cube_idx / 6) % 6;
let b = cube_idx % 6;
let c = |c| {
if c == 0 {
0
} else {
c * 40 + 55
}
};
Rgb {
r: c(r),
g: c(g),
b: c(b),
}
} else {
eprintln!("color index miss at {index}");
Rgb {
r: 0xff,
g: 0x00,
b: 0xff,
}
}
}
pub fn get_color(&self, color: &Color) -> Rgb {
match color {
Color::Named(name) => self.colors[*name].unwrap(),
Color::Spec(rgb) => *rgb,
Color::Indexed(index) => self.get_color_idx(*index as usize),
}
}
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,
}),
};
let rgb = self.get_color(color);
((rgb.r as u32) << 16) | ((rgb.g as u32) << 8) | (rgb.b as u32)
}
@ -380,7 +418,7 @@ impl Graphics {
continue;
}
let glyph_info = self.glyph_cache.cell_info(&cell);
let glyph_info = self.glyph_cache.cell_info(self, &cell);
if self.cell_cache.get(&term_coords) == Some(&glyph_info) {
continue;

View File

@ -1,6 +1,7 @@
// Copyright (c) 2022 Marceline Cramer
// SPDX-License-Identifier: AGPL-3.0-or-later
use alacritty_terminal::ansi::Color;
use alacritty_terminal::config::PtyConfig;
use alacritty_terminal::event::{Event as TermEvent, EventListener};
use alacritty_terminal::event_loop::{
@ -47,10 +48,11 @@ fn get_login_shell(config: Config) -> String {
config.system.shell
} else {
match std::env::consts::OS {
"linux" | "openbsd" | "netbsd" | "dragonfly" | "solaris" | "macos" =>
std::env::var("SHELL").unwrap_or("/bin/sh".to_string()),
"linux" | "openbsd" | "netbsd" | "dragonfly" | "solaris" | "macos" => {
std::env::var("SHELL").unwrap_or("/bin/sh".to_string())
}
"windows" => r#"C:\Windows\System32\cmd.exe"#.to_string(),
_ => unimplemented!("Unrecognized operating system; cannot get user's shell")
_ => unimplemented!("Unrecognized operating system; cannot get user's shell"),
}
}
}
@ -83,7 +85,9 @@ impl App {
let term_config = alacritty_terminal::config::Config {
pty_config: PtyConfig {
shell: Some(alacritty_terminal::config::Program::Just(get_login_shell(config))),
shell: Some(alacritty_terminal::config::Program::Just(get_login_shell(
config,
))),
working_directory: None,
hold: false,
},
@ -123,22 +127,17 @@ impl App {
while let Ok(event) = self.term_events.try_recv() {
match event {
TermEvent::ColorRequest(index, format) => {
let color = self.graphics.colors[index].unwrap_or(Rgb {
r: 255,
g: 0,
b: 255,
});
self.send_input(&format(color));
let rgb = self.graphics.get_color_idx(index);
self.send_input(&format(rgb));
}
TermEvent::PtyWrite(text) => self.send_input(&text),
TermEvent::Wakeup => self.request_redraw(),
TermEvent::Exit => self.should_quit = true,
_ => {}
event => eprintln!("unhandled event: {:#?}", event),
}
}
}
pub fn request_redraw(&self) {
self.graphics_context.window().request_redraw();
}