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 // Use AHash instead of the slower SipHash
type HashMap<K, V> = std::collections::HashMap<K, V, ahash::ABuildHasher>; 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> { fn split_i32(code: i32) -> Vec<u8> {
(0..4).map(|i| ((code >> (8 * i)) & 0xFF) as u8).collect() (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 { fn find_closest(color: Color, max_colors: i16) -> i16 {
match color { match color {
Color::TerminalDefault => -1, Color::TerminalDefault => -1,
@ -92,11 +97,18 @@ fn find_closest(color: Color, max_colors: i16) -> i16 {
Color::Light(BaseColor::White) => 15 % max_colors, Color::Light(BaseColor::White) => 15 % max_colors,
Color::Rgb(r, g, b) if max_colors >= 256 => { Color::Rgb(r, g, b) if max_colors >= 256 => {
// If r = g = b, it may be a grayscale value! // If r = g = b, it may be a grayscale value!
if r == g && g == b && r != 0 && r < 250 { // Grayscale colors have a bit higher resolution than the rest of
// Grayscale // the palette, so if we can use it we should!
// (r = g = b) = 8 + 10 * n
// (r - 8) / 10 = n
// //
// 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; let n = (r - 8) / 10;
i16::from(232 + n) i16::from(232 + n)
} else { } else {
@ -108,6 +120,7 @@ fn find_closest(color: Color, max_colors: i16) -> i16 {
} }
} }
Color::Rgb(r, g, b) => { Color::Rgb(r, g, b) => {
// Have to hack it down to 8 colors.
let r = if r > 127 { 1 } else { 0 }; let r = if r > 127 { 1 } else { 0 };
let g = if g > 127 { 1 } else { 0 }; let g = if g > 127 { 1 } else { 0 };
let b = if b > 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. /// One of the 8 base colors.
/// ///
/// These colors should work on any terminal.
///
/// Note: the actual color used depends on the terminal configuration. /// Note: the actual color used depends on the terminal configuration.
Dark(BaseColor), Dark(BaseColor),
/// Lighter version of a base color. /// 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. /// Note: the actual color used depends on the terminal configuration.
Light(BaseColor), Light(BaseColor),
/// True-color, 24-bit. /// 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), Rgb(u8, u8, u8),
/// Low-resolution /// Low-resolution color.
/// ///
/// Each value should be `<= 5` (you'll get panics otherwise). /// 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), 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. /// Parse a string into a color.
/// ///
/// Examples: /// Examples: