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 { pub struct GlyphInfo {
c: char, c: char,
fg: u32, fg: u32,
@ -141,24 +141,22 @@ pub struct GlyphInfo {
pub struct GlyphCache { pub struct GlyphCache {
renderer: BitmapGlyphRenderer, renderer: BitmapGlyphRenderer,
colors: Colors,
glyphs: HashMap<GlyphInfo, Box<[u32]>>, glyphs: HashMap<GlyphInfo, Box<[u32]>>,
} }
impl GlyphCache { impl GlyphCache {
pub fn new(colors: Colors, font_config: &FontConfig) -> Self { pub fn new(font_config: &FontConfig) -> Self {
Self { Self {
renderer: BitmapGlyphRenderer::new(font_config), renderer: BitmapGlyphRenderer::new(font_config),
colors,
glyphs: Default::default(), glyphs: Default::default(),
} }
} }
pub fn cell_info(&self, cell: &Cell) -> GlyphInfo { pub fn cell_info(&self, graphics: &Graphics, cell: &Cell) -> GlyphInfo {
GlyphInfo { GlyphInfo {
c: cell.c, c: cell.c,
fg: self.color_to_u32(&cell.fg), fg: graphics.color_to_u32(&cell.fg),
bg: self.color_to_u32(&cell.bg), bg: graphics.color_to_u32(&cell.bg),
flags: cell.flags, flags: cell.flags,
} }
} }
@ -168,21 +166,6 @@ impl GlyphCache {
.entry(glyph.clone()) .entry(glyph.clone())
.or_insert_with(|| self.renderer.draw(glyph).into_boxed_slice()) .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 { pub struct BitmapGlyphRenderer {
@ -213,6 +196,8 @@ impl BitmapGlyphRenderer {
} }
pub fn draw(&self, glyph: &GlyphInfo) -> Vec<u32> { 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 mut buf = Vec::with_capacity(self.cell_width * self.cell_height);
let font = if glyph.flags.contains(CellFlags::BOLD) { let font = if glyph.flags.contains(CellFlags::BOLD) {
@ -246,6 +231,7 @@ impl BitmapGlyphRenderer {
} }
} }
} else { } else {
eprintln!("character bitmap miss: {:?}", glyph.c);
buf.resize(buf.capacity(), 0x00ff00ff); 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 bg = ((bg.r as u32) << 16) | ((bg.g as u32) << 8) | (bg.b as u32);
let canvas = Canvas::new(bg, 2000, 2000); 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(); let cell_cache = Default::default();
Self { Self {
@ -295,14 +281,16 @@ impl Graphics {
pub fn load_colors(config: &ColorConfig, colors: &mut Colors) { pub fn load_colors(config: &ColorConfig, colors: &mut Colors) {
use NamedColor::*; use NamedColor::*;
let mut set = |color: NamedColor, value: hex_color::HexColor| { let rgb = |value: hex_color::HexColor| {
let rgb = Rgb { Some(Rgb {
r: value.r, r: value.r,
g: value.g, g: value.g,
b: value.b, b: value.b,
}; })
};
colors[color] = Some(rgb); let mut set = |color: NamedColor, value: hex_color::HexColor| {
colors[color] = rgb(value);
}; };
set(Background, config.background); set(Background, config.background);
@ -323,19 +311,69 @@ impl Graphics {
set(BrightMagenta, config.bright_magenta); set(BrightMagenta, config.bright_magenta);
set(BrightCyan, config.bright_cyan); set(BrightCyan, config.bright_cyan);
set(BrightWhite, config.bright_white); 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 { pub fn color_to_u32(&self, color: &Color) -> u32 {
let rgb = match color { let rgb = self.get_color(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) ((rgb.r as u32) << 16) | ((rgb.g as u32) << 8) | (rgb.b as u32)
} }
@ -380,7 +418,7 @@ impl Graphics {
continue; 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) { if self.cell_cache.get(&term_coords) == Some(&glyph_info) {
continue; continue;

View File

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