Improved color management
This commit is contained in:
parent
f113ee79bf
commit
ef1d881c57
114
src/graphics.rs
114
src/graphics.rs
|
@ -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;
|
||||||
|
|
25
src/main.rs
25
src/main.rs
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue