use image::{DynamicImage, GenericImageView, Pixel, Rgb, RgbImage};
const PALETTE: &[Rgb<u8>] = &[
Rgb([0, 0, 0]), // #000000
Rgb([29, 43, 83]), // #1D2B53
Rgb([126, 37, 83]), // #7E2553
Rgb([0, 135, 81]), // #008751
Rgb([171, 82, 54]), // #AB5236
Rgb([95, 87, 79]), // #5F574F
Rgb([194, 195, 199]), // #C2C3C7
Rgb([255, 241, 232]), // #FFF1E8
Rgb([255, 0, 77]), // #FF004D
Rgb([255, 163, 0]), // #FFA300
Rgb([255, 236, 39]), // #FFEC27
Rgb([0, 228, 54]), // #00E436
Rgb([41, 173, 255]), // #29ADFF
Rgb([131, 118, 156]), // #83769C
Rgb([255, 119, 168]), // #FF77A8
Rgb([255, 204, 170]), // #FFCCAA
Rgb([28, 94, 172]), // #1C5EAC
Rgb([0, 165, 161]), // #00A5A1
Rgb([117, 78, 151]), // #754E97
Rgb([18, 83, 89]), // #125359
Rgb([116, 47, 41]), // #742F29
Rgb([73, 45, 56]), // #492D38
Rgb([162, 136, 121]), // #A28879
Rgb([255, 172, 197]), // #FFACC5
Rgb([195, 0, 76]), // #C3004C
Rgb([235, 107, 0]), // #EB6B00
Rgb([144, 236, 66]), // #90EC42
Rgb([0, 178, 81]), // #00B251
Rgb([100, 223, 246]), // #64DFF6
Rgb([189, 154, 223]), // #BD9ADF
Rgb([228, 13, 171]), // #E40DAB
Rgb([255, 133, 109]), // #FF856D
Rgb([32, 32, 32]), // #202020
];
/// Compute the Euclidean distance between two RGB colors
fn color_distance(c1: &Rgb<u8>, c2: &Rgb<u8>) -> u32 {
let r_diff = (c1.0[0] as i32 - c2.0[0] as i32) as f32 * 1.0;
let g_diff = (c1.0[1] as i32 - c2.0[1] as i32) as f32 * 1.0;
let b_diff = (c1.0[2] as i32 - c2.0[2] as i32) as f32 * 1.0;
(r_diff * r_diff + g_diff * g_diff + b_diff * b_diff) as u32
}
/// Find the closest color from the palette
/// Unused but good to have
fn closest_palette_color(color: &Rgb<u8>) -> Rgb<u8> {
*PALETTE
.iter()
.min_by_key(|&palette_color| color_distance(color, &palette_color))
.unwrap()
}
/// Find the closest color from the palette and return its index
fn closest_palette_index(color: &Rgb<u8>) -> u8 {
PALETTE
.iter()
.enumerate()
.min_by_key(|&(_, &palette_color)| color_distance(color, &palette_color))
.map(|(index, _)| index as u8)
.unwrap()
}
/// Process the image and return a Vec<u8> of palette indices
pub fn process_image_to_indices(img: DynamicImage) -> Vec<u8> {
let (width, height) = img.dimensions();
let mut indexed_pixels = Vec::with_capacity((width * height) as usize);
for y in 0..height {
for x in 0..width {
let pixel = img.get_pixel(x, y);
let index = closest_palette_index(&pixel.to_rgb());
indexed_pixels.push(index);
}
}
indexed_pixels
}
/// Process the image by mapping colors to the closest ones in the palette
/// Unused but good to have
pub fn process_image(img: DynamicImage) -> RgbImage {
let (width, height) = img.dimensions();
let mut new_img: image::ImageBuffer<Rgb<u8>, Vec<u8>> = RgbImage::new(width, height);
for y in 0..height {
for x in 0..width {
let pixel = img.get_pixel(x, y);
let new_pixel = closest_palette_color(&pixel.to_rgb());
new_img.put_pixel(x, y, new_pixel);
}
}
new_img
}