Refactor color list management

There's now a ColorList type that provides strongly typed indexing for
not only usize but NamedColor as well. Previously, the `NamedColor` was
casted to a `usize` when indexing the colors. Additionally, only one
copy of the ColorList needs to exist;it's borrowed from the `Config`
when rendering, and the renderer doesn't need a copy.
This commit is contained in:
Joe Wilm 2016-12-04 13:13:44 -08:00
parent 23e36f1925
commit ce32eee099
4 changed files with 196 additions and 89 deletions

View File

@ -349,6 +349,22 @@ pub enum NamedColor {
Background,
}
impl NamedColor {
pub fn to_bright(&self) -> Self {
match *self {
NamedColor::Black => NamedColor::BrightBlack,
NamedColor::Red => NamedColor::BrightRed,
NamedColor::Green => NamedColor::BrightGreen,
NamedColor::Yellow => NamedColor::BrightYellow,
NamedColor::Blue => NamedColor::BrightBlue,
NamedColor::Magenta => NamedColor::BrightMagenta,
NamedColor::Cyan => NamedColor::BrightCyan,
NamedColor::White => NamedColor::BrightWhite,
val => val
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum Color {
Named(NamedColor),

View File

@ -4,11 +4,13 @@
//! parameters including font family and style, font size, etc. In the future,
//! the config file will also hold user and platform specific keybindings.
use std::env;
use std::fmt;
use std::fs;
use std::io::{self, Read};
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::sync::mpsc;
use std::ops::{Index, IndexMut};
use ::Rgb;
use font::Size;
@ -19,11 +21,150 @@ use notify::{Watcher as WatcherApi, RecommendedWatcher as FileWatcher, op};
use input::{Action, Binding, MouseBinding, KeyBinding};
use ansi;
/// Function that returns true for serde default
fn true_bool() -> bool {
true
}
/// List of indexed colors
///
/// The first 16 entries are the standard ansi named colors. Items 16..232 are
/// the color cube. Items 233..256 are the grayscale ramp. Finally, item 256 is
/// the configured foreground color, and item 257 is the configured background
/// color.
pub struct ColorList([Rgb; 258]);
impl fmt::Debug for ColorList {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("ColorList[..]")
}
}
impl Default for ColorList {
fn default() -> ColorList {
ColorList::from(Colors::default())
}
}
impl From<Colors> for ColorList {
fn from(colors: Colors) -> ColorList {
// Type inference fails without this annotation
let mut list: ColorList = unsafe { ::std::mem::uninitialized() };
list.fill_named(&colors);
list.fill_cube();
list.fill_gray_ramp();
list
}
}
impl ColorList {
fn fill_named(&mut self, colors: &Colors) {
// Normals
self[ansi::NamedColor::Black] = colors.normal.black;
self[ansi::NamedColor::Red] = colors.normal.red;
self[ansi::NamedColor::Green] = colors.normal.green;
self[ansi::NamedColor::Yellow] = colors.normal.yellow;
self[ansi::NamedColor::Blue] = colors.normal.blue;
self[ansi::NamedColor::Magenta] = colors.normal.magenta;
self[ansi::NamedColor::Cyan] = colors.normal.cyan;
self[ansi::NamedColor::White] = colors.normal.white;
// Brights
self[ansi::NamedColor::BrightBlack] = colors.bright.black;
self[ansi::NamedColor::BrightRed] = colors.bright.red;
self[ansi::NamedColor::BrightGreen] = colors.bright.green;
self[ansi::NamedColor::BrightYellow] = colors.bright.yellow;
self[ansi::NamedColor::BrightBlue] = colors.bright.blue;
self[ansi::NamedColor::BrightMagenta] = colors.bright.magenta;
self[ansi::NamedColor::BrightCyan] = colors.bright.cyan;
self[ansi::NamedColor::BrightWhite] = colors.bright.white;
// Foreground and background
self[ansi::NamedColor::Foreground] = colors.primary.foreground;
self[ansi::NamedColor::Background] = colors.primary.background;
}
fn fill_cube(&mut self) {
let mut index = 16;
// Build colors
for r in 0..6 {
for g in 0..6 {
for b in 0..6 {
self[index] = Rgb {
r: if r == 0 { 0 } else { r * 40 + 55 },
b: if b == 0 { 0 } else { b * 40 + 55 },
g: if g == 0 { 0 } else { g * 40 + 55 },
};
index += 1;
}
}
}
debug_assert!(index == 232);
}
fn fill_gray_ramp(&mut self) {
let mut index = 232;
for i in 0..24 {
let value = i * 10 + 8;
self[index] = Rgb {
r: value,
g: value,
b: value
};
index += 1;
}
debug_assert!(index == 256);
}
}
impl Index<ansi::NamedColor> for ColorList {
type Output = Rgb;
#[inline]
fn index(&self, idx: ansi::NamedColor) -> &Self::Output {
&self.0[idx as usize]
}
}
impl IndexMut<ansi::NamedColor> for ColorList {
#[inline]
fn index_mut(&mut self, idx: ansi::NamedColor) -> &mut Self::Output {
&mut self.0[idx as usize]
}
}
impl Index<usize> for ColorList {
type Output = Rgb;
#[inline]
fn index(&self, idx: usize) -> &Self::Output {
&self.0[idx]
}
}
impl Index<u8> for ColorList {
type Output = Rgb;
#[inline]
fn index(&self, idx: u8) -> &Self::Output {
&self.0[idx as usize]
}
}
impl IndexMut<usize> for ColorList {
#[inline]
fn index_mut(&mut self, idx: usize) -> &mut Self::Output {
&mut self.0[idx]
}
}
/// Top-level config type
#[derive(Debug, Deserialize)]
pub struct Config {
@ -43,9 +184,8 @@ pub struct Config {
#[serde(default="true_bool")]
draw_bold_text_with_bright_colors: bool,
/// The standard ANSI colors to use
#[serde(default)]
colors: Colors,
colors: ColorList,
/// Keybindings
#[serde(default)]
@ -415,6 +555,15 @@ impl de::Deserialize for RawBinding {
}
}
impl de::Deserialize for ColorList {
fn deserialize<D>(deserializer: &mut D) -> ::std::result::Result<Self, D::Error>
where D: de::Deserializer
{
let named_colors = Colors::deserialize(deserializer)?;
Ok(ColorList::from(named_colors))
}
}
impl de::Deserialize for MouseBinding {
fn deserialize<D>(deserializer: &mut D) -> ::std::result::Result<Self, D::Error>
where D: de::Deserializer
@ -665,59 +814,8 @@ impl Config {
///
/// The ordering returned here is expected by the terminal. Colors are simply indexed in this
/// array for performance.
pub fn color_list(&self) -> Vec<Rgb> {
let mut colors = Vec::with_capacity(258);
// Normals
colors.push(self.colors.normal.black);
colors.push(self.colors.normal.red);
colors.push(self.colors.normal.green);
colors.push(self.colors.normal.yellow);
colors.push(self.colors.normal.blue);
colors.push(self.colors.normal.magenta);
colors.push(self.colors.normal.cyan);
colors.push(self.colors.normal.white);
// Brights
colors.push(self.colors.bright.black);
colors.push(self.colors.bright.red);
colors.push(self.colors.bright.green);
colors.push(self.colors.bright.yellow);
colors.push(self.colors.bright.blue);
colors.push(self.colors.bright.magenta);
colors.push(self.colors.bright.cyan);
colors.push(self.colors.bright.white);
// Build colors
for r in 0..6 {
for g in 0..6 {
for b in 0..6 {
colors.push(Rgb {
r: if r == 0 { 0 } else { r * 40 + 55 },
b: if b == 0 { 0 } else { b * 40 + 55 },
g: if g == 0 { 0 } else { g * 40 + 55 },
});
}
}
}
// Build grays
for i in 0..24 {
let value = i * 10 + 8;
colors.push(Rgb {
r: value,
g: value,
b: value
});
}
debug_assert!(colors.len() == 256);
// Foreground and background
colors.push(self.colors.primary.foreground);
colors.push(self.colors.primary.background);
colors
pub fn color_list(&self) -> &ColorList {
&self.colors
}
pub fn key_bindings(&self) -> &[KeyBinding] {
@ -728,14 +826,6 @@ impl Config {
&self.mouse_bindings[..]
}
pub fn fg_color(&self) -> Rgb {
self.colors.primary.foreground
}
pub fn bg_color(&self) -> Rgb {
self.colors.primary.background
}
#[inline]
pub fn draw_bold_text_with_bright_colors(&self) -> bool {
self.draw_bold_text_with_bright_colors

View File

@ -256,7 +256,7 @@ fn main() {
let terminal = terminal.lock();
signal_flag.set(false);
if terminal.dirty {
display.draw(terminal);
display.draw(terminal, &config);
}
if process_should_exit() {
@ -330,7 +330,7 @@ impl Display {
/// A reference to Term whose state is being drawn must be provided.
///
/// This call may block if vsync is enabled
pub fn draw(&mut self, mut terminal: MutexGuard<Term>) {
pub fn draw(&mut self, mut terminal: MutexGuard<Term>, config: &Config) {
terminal.dirty = false;
// Resize events new_size and are handled outside the poll_events
@ -360,7 +360,7 @@ impl Display {
let _sampler = self.meter.sampler();
let size_info = terminal.size_info().clone();
self.renderer.with_api(&size_info, |mut api| {
self.renderer.with_api(config, &size_info, |mut api| {
api.clear();
// Draw the grid
@ -372,7 +372,7 @@ impl Display {
if self.render_timer {
let timing = format!("{:.3} usec", self.meter.average());
let color = alacritty::ansi::Color::Spec(Rgb { r: 0xd5, g: 0x4e, b: 0x53 });
self.renderer.with_api(terminal.size_info(), |mut api| {
self.renderer.with_api(config, terminal.size_info(), |mut api| {
api.render_string(&timing[..], glyph_cache, &color);
});
}

View File

@ -28,7 +28,7 @@ use index::{Line, Column};
use ansi::{Color, NamedColor};
use config::Config;
use config::{Config, ColorList};
use term::{self, cell, IndexedCell, Cell};
use super::Rgb;
@ -243,7 +243,6 @@ pub struct QuadRenderer {
atlas: Vec<Atlas>,
active_tex: GLuint,
batch: Batch,
colors: Vec<Rgb>,
draw_bold_text_with_bright_colors: bool,
rx: mpsc::Receiver<Msg>,
}
@ -254,7 +253,7 @@ pub struct RenderApi<'a> {
batch: &'a mut Batch,
atlas: &'a mut Vec<Atlas>,
program: &'a mut ShaderProgram,
colors: &'a [Rgb],
colors: &'a ColorList,
}
#[derive(Debug)]
@ -273,7 +272,6 @@ pub struct PackedVertex {
pub struct Batch {
tex: GLuint,
instances: Vec<InstanceData>,
colors: Vec<Rgb>,
draw_bold_text_with_bright_colors: bool,
}
@ -283,12 +281,16 @@ impl Batch {
Batch {
tex: 0,
instances: Vec::with_capacity(BATCH_MAX),
colors: config.color_list(),
draw_bold_text_with_bright_colors: config.draw_bold_text_with_bright_colors(),
}
}
pub fn add_item(&mut self, cell: &IndexedCell, glyph: &Glyph) {
pub fn add_item(
&mut self,
cell: &IndexedCell,
glyph: &Glyph,
colors: &ColorList
) {
if self.is_empty() {
self.tex = glyph.tex_id;
}
@ -296,13 +298,10 @@ impl Batch {
let fg = match cell.fg {
Color::Spec(rgb) => rgb,
Color::Named(ansi) => {
if self.draw_bold_text_with_bright_colors
&& cell.bold()
&& ansi < NamedColor::BrightBlack
{
self.colors[ansi as usize + 8]
if self.draw_bold_text_with_bright_colors && cell.bold() {
colors[ansi.to_bright()]
} else {
self.colors[ansi as usize]
colors[ansi]
}
},
Color::Indexed(idx) => {
@ -315,14 +314,14 @@ impl Batch {
idx
};
self.colors[idx as usize]
colors[idx]
}
};
let bg = match cell.bg {
Color::Spec(rgb) => rgb,
Color::Named(ansi) => self.colors[ansi as usize],
Color::Indexed(idx) => self.colors[idx as usize],
Color::Named(ansi) => colors[ansi],
Color::Indexed(idx) => colors[idx],
};
self.instances.push(InstanceData {
@ -528,7 +527,6 @@ impl QuadRenderer {
atlas: Vec::new(),
active_tex: 0,
batch: Batch::new(config),
colors: config.color_list(),
rx: msg_rx,
draw_bold_text_with_bright_colors: config.draw_bold_text_with_bright_colors(),
};
@ -540,12 +538,15 @@ impl QuadRenderer {
}
pub fn update_config(&mut self, config: &Config) {
self.colors = config.color_list();
self.batch.colors = config.color_list();
self.batch.draw_bold_text_with_bright_colors = config.draw_bold_text_with_bright_colors();
}
pub fn with_api<F, T>(&mut self, props: &term::SizeInfo, func: F) -> T
pub fn with_api<F, T>(
&mut self,
config: &Config,
props: &term::SizeInfo,
func: F
) -> T
where F: FnOnce(RenderApi) -> T
{
while let Ok(msg) = self.rx.try_recv() {
@ -571,7 +572,7 @@ impl QuadRenderer {
batch: &mut self.batch,
atlas: &mut self.atlas,
program: &mut self.program,
colors: &self.colors,
colors: &config.color_list(),
});
unsafe {
@ -635,7 +636,7 @@ impl QuadRenderer {
impl<'a> RenderApi<'a> {
pub fn clear(&self) {
let color = self.colors[NamedColor::Background as usize];
let color = self.colors[NamedColor::Background];
unsafe {
gl::ClearColor(
color.r as f32 / 255.0,
@ -712,7 +713,7 @@ impl<'a> RenderApi<'a> {
}
}
self.batch.add_item(cell, glyph);
self.batch.add_item(cell, glyph, self.colors);
// Render batch and clear if it's full
if self.batch.full() {