use image::Rgba;
const BLACK: &str = "⬛";
const WHITE: &str = "⬜";
const RED: &str = "🟥";
const ORANGE: &str = "🟧";
const YELLOW: &str = "🟨";
const GREEN: &str = "🟩";
const BLUE: &str = "🟦";
const PURPLE: &str = "🟪";
const BROWN: &str = "🟫";
pub fn color_to_emoji(color: Rgba<u8>) -> &'static str {
let [r, g, b, a] = color.0;
if a < 128 {
return WHITE;
}
let (h, s, l) = rgb_to_hsl(r, g, b);
if l < 0.15 {
return BLACK;
}
if l > 0.85 {
return WHITE;
}
if s < 0.15 {
return if l < 0.5 { BLACK } else { WHITE };
}
if (0.2..0.5).contains(&l) && (15.0..50.0).contains(&h) && s < 0.7 {
return BROWN;
}
hue_to_emoji(h)
}
fn hue_to_emoji(hue: f32) -> &'static str {
let h = hue % 360.0;
if !(15.0..345.0).contains(&h) {
RED
} else if h < 45.0 {
ORANGE
} else if h < 75.0 {
YELLOW
} else if h < 165.0 {
GREEN
} else if h < 255.0 {
BLUE
} else {
PURPLE
}
}
fn rgb_to_hsl(r: u8, g: u8, b: u8) -> (f32, f32, f32) {
let r = r as f32 / 255.0;
let g = g as f32 / 255.0;
let b = b as f32 / 255.0;
let max = r.max(g).max(b);
let min = r.min(g).min(b);
let delta = max - min;
let l = (max + min) / 2.0;
let s = if delta < f32::EPSILON { 0.0 } else { delta / (1.0 - (2.0 * l - 1.0).abs()) };
let h = if delta < f32::EPSILON {
0.0
} else if (max - r).abs() < f32::EPSILON {
60.0 * (((g - b) / delta) % 6.0)
} else if (max - g).abs() < f32::EPSILON {
60.0 * (((b - r) / delta) + 2.0)
} else {
60.0 * (((r - g) / delta) + 4.0)
};
let h = if h < 0.0 { h + 360.0 } else { h };
(h, s, l)
}
pub fn render_emoji_art(image: &image::RgbaImage) -> String {
let mut output = String::new();
for y in 0..image.height() {
for x in 0..image.width() {
let pixel = image.get_pixel(x, y);
output.push_str(color_to_emoji(*pixel));
}
output.push('\n');
}
output
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_transparent() {
assert_eq!(color_to_emoji(Rgba([255, 0, 0, 0])), WHITE);
assert_eq!(color_to_emoji(Rgba([0, 0, 0, 0])), WHITE);
assert_eq!(color_to_emoji(Rgba([128, 128, 128, 64])), WHITE);
assert_eq!(color_to_emoji(Rgba([128, 128, 128, 127])), WHITE);
}
#[test]
fn test_opaque_boundary() {
assert_eq!(color_to_emoji(Rgba([0, 0, 0, 128])), BLACK);
assert_eq!(color_to_emoji(Rgba([255, 0, 0, 128])), RED);
}
#[test]
fn test_black() {
assert_eq!(color_to_emoji(Rgba([0, 0, 0, 255])), BLACK);
assert_eq!(color_to_emoji(Rgba([20, 20, 20, 255])), BLACK);
assert_eq!(color_to_emoji(Rgba([30, 30, 30, 255])), BLACK);
}
#[test]
fn test_white() {
assert_eq!(color_to_emoji(Rgba([255, 255, 255, 255])), WHITE);
assert_eq!(color_to_emoji(Rgba([240, 240, 240, 255])), WHITE);
assert_eq!(color_to_emoji(Rgba([230, 230, 230, 255])), WHITE);
}
#[test]
fn test_red() {
assert_eq!(color_to_emoji(Rgba([255, 0, 0, 255])), RED);
assert_eq!(color_to_emoji(Rgba([200, 50, 50, 255])), RED);
assert_eq!(color_to_emoji(Rgba([255, 50, 50, 255])), RED);
}
#[test]
fn test_orange() {
assert_eq!(color_to_emoji(Rgba([255, 128, 0, 255])), ORANGE);
assert_eq!(color_to_emoji(Rgba([255, 165, 0, 255])), ORANGE);
}
#[test]
fn test_yellow() {
assert_eq!(color_to_emoji(Rgba([255, 255, 0, 255])), YELLOW);
assert_eq!(color_to_emoji(Rgba([255, 230, 0, 255])), YELLOW);
}
#[test]
fn test_green() {
assert_eq!(color_to_emoji(Rgba([0, 255, 0, 255])), GREEN);
assert_eq!(color_to_emoji(Rgba([0, 200, 0, 255])), GREEN);
assert_eq!(color_to_emoji(Rgba([50, 200, 50, 255])), GREEN);
}
#[test]
fn test_blue() {
assert_eq!(color_to_emoji(Rgba([0, 0, 255, 255])), BLUE);
assert_eq!(color_to_emoji(Rgba([0, 128, 255, 255])), BLUE);
assert_eq!(color_to_emoji(Rgba([50, 100, 200, 255])), BLUE);
}
#[test]
fn test_purple() {
assert_eq!(color_to_emoji(Rgba([128, 0, 128, 255])), PURPLE);
assert_eq!(color_to_emoji(Rgba([255, 0, 255, 255])), PURPLE);
assert_eq!(color_to_emoji(Rgba([200, 50, 200, 255])), PURPLE);
}
#[test]
fn test_brown() {
assert_eq!(color_to_emoji(Rgba([139, 90, 43, 255])), BROWN);
assert_eq!(color_to_emoji(Rgba([150, 100, 50, 255])), BROWN);
}
#[test]
fn test_grayscale_mid() {
assert_eq!(color_to_emoji(Rgba([100, 100, 100, 255])), BLACK);
assert_eq!(color_to_emoji(Rgba([160, 160, 160, 255])), WHITE);
}
#[test]
fn test_render_emoji_art() {
let mut image = image::RgbaImage::new(3, 2);
image.put_pixel(0, 0, Rgba([255, 0, 0, 255])); image.put_pixel(1, 0, Rgba([0, 255, 0, 255])); image.put_pixel(2, 0, Rgba([0, 0, 255, 255])); image.put_pixel(0, 1, Rgba([0, 0, 0, 255])); image.put_pixel(1, 1, Rgba([255, 255, 255, 255])); image.put_pixel(2, 1, Rgba([0, 0, 0, 0]));
let output = render_emoji_art(&image);
assert_eq!(output, "🟥🟩🟦\n⬛⬜⬜\n");
}
#[test]
fn test_hsl_conversion() {
let (h, s, l) = rgb_to_hsl(255, 0, 0);
assert!((h - 0.0).abs() < 1.0); assert!((s - 1.0).abs() < 0.01); assert!((l - 0.5).abs() < 0.01);
let (h, s, l) = rgb_to_hsl(0, 255, 0);
assert!((h - 120.0).abs() < 1.0); assert!((s - 1.0).abs() < 0.01);
assert!((l - 0.5).abs() < 0.01);
let (h, s, l) = rgb_to_hsl(0, 0, 255);
assert!((h - 240.0).abs() < 1.0); assert!((s - 1.0).abs() < 0.01);
assert!((l - 0.5).abs() < 0.01);
}
}