summaryrefslogtreecommitdiffstats
path: root/alacritty_terminal
diff options
context:
space:
mode:
authorChristian Duerr <contact@christianduerr.com>2020-07-26 01:04:39 +0000
committerGitHub <noreply@github.com>2020-07-26 01:04:39 +0000
commitbedf5f3004e8f33011925ca471be02ead96f4581 (patch)
tree0512bbd51514e5bca8be3b1f11cadffaa2eae113 /alacritty_terminal
parent9a4d847d897016ac3f1662fec45d372b879072cd (diff)
Invert fixed color cursor if it's close to cell bg
This should reduce the number of times people with fixed cursor colors run into troubles when existing text is already colored. Using just the background color as a metric instead of both background and foreground color should ensure that the cursor still has a clear shape, since just changing the foreground color for a cursor might be difficult to see. Always inverting the entire cursor instead of keeping the fixed foreground color is important to make sure the contrast isn't messed up. Fixes #4016.
Diffstat (limited to 'alacritty_terminal')
-rw-r--r--alacritty_terminal/src/term/color.rs61
-rw-r--r--alacritty_terminal/src/term/mod.rs21
2 files changed, 79 insertions, 3 deletions
diff --git a/alacritty_terminal/src/term/color.rs b/alacritty_terminal/src/term/color.rs
index 9aeb7061..fc35952b 100644
--- a/alacritty_terminal/src/term/color.rs
+++ b/alacritty_terminal/src/term/color.rs
@@ -25,6 +25,42 @@ pub struct Rgb {
pub b: u8,
}
+impl Rgb {
+ /// Implementation of W3C's luminance algorithm:
+ /// https://www.w3.org/TR/WCAG20/#relativeluminancedef
+ fn luminance(self) -> f64 {
+ let channel_luminance = |channel| {
+ let channel = channel as f64 / 255.;
+ if channel <= 0.03928 {
+ channel / 12.92
+ } else {
+ f64::powf((channel + 0.055) / 1.055, 2.4)
+ }
+ };
+
+ let r_luminance = channel_luminance(self.r);
+ let g_luminance = channel_luminance(self.g);
+ let b_luminance = channel_luminance(self.b);
+
+ 0.2126 * r_luminance + 0.7152 * g_luminance + 0.0722 * b_luminance
+ }
+
+ /// Implementation of W3C's contrast algorithm:
+ /// https://www.w3.org/TR/WCAG20/#contrast-ratiodef
+ pub fn contrast(self, other: Rgb) -> f64 {
+ let self_luminance = self.luminance();
+ let other_luminance = other.luminance();
+
+ let (darker, lighter) = if self_luminance > other_luminance {
+ (other_luminance, self_luminance)
+ } else {
+ (self_luminance, other_luminance)
+ };
+
+ (lighter + 0.05) / (darker + 0.05)
+ }
+}
+
// A multiply function for Rgb, as the default dim is just *2/3.
impl Mul<f32> for Rgb {
type Output = Rgb;
@@ -370,3 +406,28 @@ impl IndexMut<u8> for List {
&mut self.0[idx as usize]
}
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ use std::f64::EPSILON;
+
+ #[test]
+ fn contrast() {
+ let rgb1 = Rgb { r: 0xff, g: 0xff, b: 0xff };
+ let rgb2 = Rgb { r: 0x00, g: 0x00, b: 0x00 };
+ assert!((rgb1.contrast(rgb2) - 21.).abs() < EPSILON);
+
+ let rgb1 = Rgb { r: 0xff, g: 0xff, b: 0xff };
+ assert!((rgb1.contrast(rgb1) - 1.).abs() < EPSILON);
+
+ let rgb1 = Rgb { r: 0xff, g: 0x00, b: 0xff };
+ let rgb2 = Rgb { r: 0x00, g: 0xff, b: 0x00 };
+ assert!((rgb1.contrast(rgb2) - 2.285_543_608_124_253_3).abs() < EPSILON);
+
+ let rgb1 = Rgb { r: 0x12, g: 0x34, b: 0x56 };
+ let rgb2 = Rgb { r: 0xfe, g: 0xdc, b: 0xba };
+ assert!((rgb1.contrast(rgb2) - 9.786_558_997_257_74).abs() < EPSILON);
+ }
+}
diff --git a/alacritty_terminal/src/term/mod.rs b/alacritty_terminal/src/term/mod.rs
index f27852de..7d00961e 100644
--- a/alacritty_terminal/src/term/mod.rs
+++ b/alacritty_terminal/src/term/mod.rs
@@ -31,6 +31,9 @@ mod search;
/// Max size of the window title stack.
const TITLE_STACK_MAX_DEPTH: usize = 4096;
+/// Minimum contrast between a fixed cursor color and the cell's background.
+const MIN_CURSOR_CONTRAST: f64 = 1.5;
+
/// Maximum number of linewraps followed outside of the viewport during search highlighting.
const MAX_SEARCH_LINES: usize = 100;
@@ -389,13 +392,19 @@ impl<'a, C> Iterator for RenderableCellsIter<'a, C> {
if self.cursor.point.line == self.inner.line()
&& self.cursor.point.col == self.inner.column()
{
- // Handle cell below cursor.
if self.cursor.rendered {
+ // Handle cell below cursor.
let cell = self.inner.next()?;
let mut cell = RenderableCell::new(self, cell);
if self.cursor.key.style == CursorStyle::Block {
- cell.fg = self.cursor.text_color.color(cell.fg, cell.bg);
+ // Invert cursor if static background is close to the cell's background.
+ match self.cursor.cursor_color {
+ CellRgb::Rgb(col) if col.contrast(cell.bg) >= MIN_CURSOR_CONTRAST => {
+ cell.fg = self.cursor.text_color.color(cell.fg, cell.bg);
+ },
+ _ => cell.fg = cell.bg,
+ }
}
return Some(cell);
@@ -412,7 +421,13 @@ impl<'a, C> Iterator for RenderableCellsIter<'a, C> {
let mut cell = RenderableCell::new(self, cell);
cell.inner = RenderableCellContent::Cursor(self.cursor.key);
- cell.fg = self.cursor.cursor_color.color(cell.fg, cell.bg);
+
+ // Only apply static color if it isn't close to the cell's current background.
+ if let CellRgb::Rgb(color) = self.cursor.cursor_color {
+ if color.contrast(cell.bg) >= MIN_CURSOR_CONTRAST {
+ cell.fg = self.cursor.cursor_color.color(cell.fg, cell.bg);
+ }
+ }
return Some(cell);
}