Ncurses: fix bad grayscale projection for very dark or very bright colors

This commit is contained in:
Alexandre Bury 2020-01-08 16:49:42 -08:00
parent 8ed3085772
commit 45a9d54801
2 changed files with 38 additions and 5 deletions

View File

@ -16,6 +16,7 @@ pub mod pan;
// Use AHash instead of the slower SipHash
type HashMap<K, V> = std::collections::HashMap<K, V, ahash::ABuildHasher>;
/// Split a i32 into individual bytes, little endian (least significant byte first).
fn split_i32(code: i32) -> Vec<u8> {
(0..4).map(|i| ((code >> (8 * i)) & 0xFF) as u8).collect()
}
@ -71,6 +72,10 @@ fn find_closest_pair(pair: ColorPair, max_colors: i16) -> (i16, i16) {
)
}
/// Finds the closest index in the 256-color palette.
///
/// If `max_colors` is less than 256 (like 8 or 16), the color will be
/// downgraded to the closest one available.
fn find_closest(color: Color, max_colors: i16) -> i16 {
match color {
Color::TerminalDefault => -1,
@ -92,11 +97,18 @@ fn find_closest(color: Color, max_colors: i16) -> i16 {
Color::Light(BaseColor::White) => 15 % max_colors,
Color::Rgb(r, g, b) if max_colors >= 256 => {
// If r = g = b, it may be a grayscale value!
if r == g && g == b && r != 0 && r < 250 {
// Grayscale
// (r = g = b) = 8 + 10 * n
// (r - 8) / 10 = n
// Grayscale colors have a bit higher resolution than the rest of
// the palette, so if we can use it we should!
//
// r=g=b < 8 should go to pure black instead.
// r=g=b >= 247 should go to pure white.
// TODO: project almost-gray colors as well?
if r == g && g == b && r >= 8 && r < 247 {
// The grayscale palette says the colors 232+n are:
// (r = g = b) = 8 + 10 * n
// With 0 <= n <= 23. This gives:
// (r - 8) / 10 = n
let n = (r - 8) / 10;
i16::from(232 + n)
} else {
@ -108,6 +120,7 @@ fn find_closest(color: Color, max_colors: i16) -> i16 {
}
}
Color::Rgb(r, g, b) => {
// Have to hack it down to 8 colors.
let r = if r > 127 { 1 } else { 0 };
let g = if g > 127 { 1 } else { 0 };
let b = if b > 127 { 1 } else { 0 };

View File

@ -71,22 +71,30 @@ pub enum Color {
/// One of the 8 base colors.
///
/// These colors should work on any terminal.
///
/// Note: the actual color used depends on the terminal configuration.
Dark(BaseColor),
/// Lighter version of a base color.
///
/// The native linux TTY usually doesn't support these colors, but almost
/// all terminal emulators should.
///
/// Note: the actual color used depends on the terminal configuration.
Light(BaseColor),
/// True-color, 24-bit.
///
/// On terminals that don't support this, the color will be "downgraded"
/// to the closest one available.
Rgb(u8, u8, u8),
/// Low-resolution
/// Low-resolution color.
///
/// Each value should be `<= 5` (you'll get panics otherwise).
///
/// These 216 possible colors are part of the default color palette.
/// These 216 possible colors are part of the default color palette (256 colors).
RgbLowRes(u8, u8, u8),
}
@ -126,6 +134,18 @@ impl Color {
}
}
/// Creates a `Color::RgbLowRes` from the given values for red, green and
/// blue.
///
/// Returns `None` if any of the values exceeds 5.
pub fn low_res(r: u8, g: u8, b: u8) -> Option<Self> {
if r <= 5 && g <= 5 && b <= 5 {
Some(Color::RgbLowRes(r, g, b))
} else {
None
}
}
/// Parse a string into a color.
///
/// Examples: