Skip to main content

Add a couple of tests for background picking

ID
76bab5d
date
2024-05-12 12:48:38+00:00
author
Alex Chan <alex@alexwlchan.net>
parent
c523110
message
Add a couple of tests for background picking
changed files
4 files, 81 additions, 31 deletions

Changed files

src/find_dominant_colors.rs (2228) → src/find_dominant_colors.rs (2737)

diff --git a/src/find_dominant_colors.rs b/src/find_dominant_colors.rs
index a437a4c..2e03315 100644
--- a/src/find_dominant_colors.rs
+++ b/src/find_dominant_colors.rs
@@ -1,5 +1,5 @@
 use kmeans_colors::get_kmeans_hamerly;
-use palette::{color_difference::Wcag21RelativeContrast, FromColor, Hsl, Lab, Srgb};
+use palette::{color_difference::Wcag21RelativeContrast, FromColor, Lab, Srgb};
 
 pub fn find_dominant_colors(lab: &Vec<Lab>, max_colors: usize) -> Vec<Lab> {
     // This is based on code from the kmeans-colors binary, but with a bunch of
@@ -40,18 +40,42 @@ pub fn choose_best_color_for_bg(colors: Vec<Lab>, background: &Srgb<u8>) -> Vec<
     // Filter for colors which meet the min contrast ratio
     let allowed_colors: Vec<Srgb<f32>> = extended_colors
         .into_iter()
-        .filter(|c| background.has_min_contrast_text(*c))
+        .filter(|c| background.has_min_contrast_graphics(*c))
         .collect();
 
     // Now pick the color with the highest saturation among the remaining.
     let best_color: Srgb<f32> = allowed_colors
         .into_iter()
         .max_by(|color_a, color_b| {
-            let saturation_a = Hsl::new_srgb(color_a.red, color_a.green, color_a.blue).saturation;
-            let saturation_b = Hsl::new_srgb(color_b.red, color_b.green, color_b.blue).saturation;
-            saturation_a.partial_cmp(&saturation_b).unwrap()
+            saturation(color_a)
+                .partial_cmp(&saturation(color_b))
+                .unwrap()
         })
         .unwrap();
 
     vec![Lab::from_color(best_color)]
 }
+
+// Based on https://filmentor.academy/en/blogs/news/die-wunderbare-welt-der-mathematik-fur-farben
+fn saturation(c: &Srgb<f32>) -> f32 {
+    let min_rgb: f32 = vec![c.red, c.green, c.blue]
+        .into_iter()
+        .min_by(|a, b| a.partial_cmp(b).unwrap())
+        .unwrap();
+    let max_rgb: f32 = vec![c.red, c.green, c.blue]
+        .into_iter()
+        .max_by(|a, b| a.partial_cmp(b).unwrap())
+        .unwrap();
+
+    if min_rgb == max_rgb {
+        return 0.0;
+    }
+
+    let luminosity: f32 = 0.5 * (min_rgb + max_rgb);
+
+    if luminosity == 1.0 {
+        0.0
+    } else {
+        (max_rgb - min_rgb) / (1.0 - (2.0 * luminosity - 1.0).abs())
+    }
+}

src/main.rs (8239) → src/main.rs (8024)

diff --git a/src/main.rs b/src/main.rs
index 3323a51..4d4ecbf 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -8,6 +8,7 @@ use palette::{FromColor, Lab, Srgb};
 mod cli;
 mod find_dominant_colors;
 mod get_image_colors;
+mod printing;
 
 fn main() {
     let matches = cli::app().get_matches();
@@ -37,32 +38,7 @@ fn main() {
         .collect::<Vec<Srgb<u8>>>();
 
     for c in rgb_colors {
-        print_color(c, &background, matches.get_flag("no-palette"));
-    }
-}
-
-// Print the colours to the terminal, using ANSI escape codes to
-// apply formatting if desired.
-//
-// See https://alexwlchan.net/2021/04/coloured-squares/
-// See: https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797?permalink_comment_id=3857871
-fn print_color(c: Srgb<u8>, background: &Option<&Srgb<u8>>, no_palette: bool) {
-    let display_value = format!("#{:02x}{:02x}{:02x}", c.red, c.green, c.blue);
-
-    if no_palette {
-        println!("{}", display_value);
-    } else {
-        // If a background colour is specified, print it behind the
-        // hex strings.
-        match background {
-            Some(bg) => print!("\x1B[48;2;{};{};{}m", bg.red, bg.green, bg.blue),
-            _ => (),
-        };
-
-        println!(
-            "\x1B[38;2;{};{};{}m▇ {}\x1B[0m",
-            c.red, c.green, c.blue, display_value
-        );
+        printing::print_color(c, &background, matches.get_flag("no-palette"));
     }
 }
 
@@ -245,6 +221,30 @@ mod tests {
         );
     }
 
+    #[test]
+    fn it_chooses_the_right_color_for_a_dark_background() {
+        let output = get_success(&[
+            "src/tests/stripes.png",
+            "--max-colours=5",
+            "--best-against-bg=#222",
+            "--no-palette"
+        ]);
+
+        assert_eq!(output.stdout, "#d4fb79\n");
+    }
+
+    #[test]
+    fn it_chooses_the_right_color_for_a_light_background() {
+        let output = get_success(&[
+            "src/tests/stripes.png",
+            "--max-colours=5",
+            "--best-against-bg=#fff",
+            "--no-palette"
+        ]);
+
+        assert_eq!(output.stdout, "#a35c00\n");
+    }
+
     struct DcOutput {
         exit_code: i32,
         stdout: String,

src/printing.rs (0) → src/printing.rs (881)

diff --git a/src/printing.rs b/src/printing.rs
new file mode 100644
index 0000000..8cc5441
--- /dev/null
+++ b/src/printing.rs
@@ -0,0 +1,26 @@
+use palette::Srgb;
+
+// Print the colours to the terminal, using ANSI escape codes to
+// apply formatting if desired.
+//
+// See https://alexwlchan.net/2021/04/coloured-squares/
+// See: https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797?permalink_comment_id=3857871
+pub fn print_color(c: Srgb<u8>, background: &Option<&Srgb<u8>>, no_palette: bool) {
+    let display_value = format!("#{:02x}{:02x}{:02x}", c.red, c.green, c.blue);
+
+    if no_palette {
+        println!("{}", display_value);
+    } else {
+        // If a background colour is specified, print it behind the
+        // hex strings.
+        match background {
+            Some(bg) => print!("\x1B[48;2;{};{};{}m", bg.red, bg.green, bg.blue),
+            _ => (),
+        };
+
+        println!(
+            "\x1B[38;2;{};{};{}m▇ {}\x1B[0m",
+            c.red, c.green, c.blue, display_value
+        );
+    }
+}

src/tests/stripes.png (0) → src/tests/stripes.png (1778)

diff --git a/src/tests/stripes.png b/src/tests/stripes.png
new file mode 100644
index 0000000..ccf56e5
Binary files /dev/null and b/src/tests/stripes.png differ