From 9cd3d809602ab190e94f19baf707fe90d2ccffc4 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Sun, 19 Aug 2018 00:30:46 +0100 Subject: [PATCH] =?UTF-8?q?Improve=20RGB=E2=86=92256-colour=20palette=20in?= =?UTF-8?q?terpolation=20by=20trying=20cueb=20for=20greys?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 6×6×6 colour cube includes gray colours. Black and white are already used but the cube also includes other greys such as #5f5f5f or #d7d7d7. For those colours it’s better to use colour from the cube rather than from the grayscale ramp. Change rgb2ansi_grey to try approximation using coulours in either section in the pallette and choose the best one. This only affects 20 colours in total. --- src/terminal.rs | 42 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/src/terminal.rs b/src/terminal.rs index d8d6f63a8f..ec15e120fe 100644 --- a/src/terminal.rs +++ b/src/terminal.rs @@ -15,20 +15,38 @@ fn rgb2ansi(r: u8, g: u8, b: u8) -> u8 { /// ANSI palette. #[inline] fn rgb2ansi_grey(y: u8) -> u8 { + // In the 256-colour ANSI palette grey colours are included in the greyscale + // ramp as well as in the colour cube. Because of this we’re trying to + // approximate grey in both and choose whichever gives better result. + const BLACK: u8 = 16; const WHITE: u8 = 231; // The greyscale ramp starts at rgb(8, 8, 8) and steps every rgb(10, 10, 10) - // until rgb(238, 238, 238). We’re adding 6 so that division rounds to - // nearest rather than truncating. Due to asymmetry at the edges those need - // to be handled specially. + // until rgb(238, 238, 238). Due to the asymmetry, edges need to be handled + // separately. if y < 4 { - BLACK + return BLACK; } else if y >= 247 { - WHITE - } else { - 231 + if y >= 234 { 24 } else { ((y + 6) / 10) as u8 } + return WHITE; + } else if y >= 234 { + return 255; + } + + // We’re adding 6 so that division rounds to nearest rather than truncating. + let gi = (y + 6) / 10; + + // There’s only a few values in which using colour cube for grey colours is + // better. + if y >= 92 && y <= 216 { + let grey = (gi * 10 - 2) as i32; + let yi = cube_index(y); + if (cube_value(yi) as i32 - y as i32).abs() < (grey - y as i32).abs() { + return 16 + (36 + 6 + 1) * yi; + } } + + gi + 231 } /// Approximate a 24-bit colour as an index in 6×6×6 colour cube of the @@ -58,7 +76,6 @@ fn cube_index(v: u8) -> u8 { /// Converts index on one dimension of the 6×6×6 ANSI colour cube into value in /// sRGB space. -#[cfg(test)] fn cube_value(i: u8) -> u8 { if i == 0 { 0 @@ -118,6 +135,15 @@ fn test_rgb2ansi_color() { assert_eq!(193, rgb2ansi(0xd7, 0xff, 0xaf)); } +#[test] +fn test_rgb2ansi_grey_using_cube() { + // Even though those are grey colours they have a perfect match in the + // colour cube so use that rather than greyscale ramp. + assert_eq!(59, rgb2ansi(0x5f, 0x5f, 0x5f)); + assert_eq!(102, rgb2ansi(0x87, 0x87, 0x87)); + assert_eq!(145, rgb2ansi(0xaf, 0xaf, 0xaf)); +} + #[test] fn test_rgb2ansi_approx() { assert_eq!(231, rgb2ansi(0xfe, 0xfe, 0xfe));