type Rgb = (u8, u8, u8);
pub static ACI_COLORS: [Rgb; 256] = [
(0, 0, 0),
(255, 0, 0), (255, 255, 0), (0, 255, 0), (0, 255, 255), (0, 0, 255), (255, 0, 255), (255, 255, 255), (128, 128, 128), (192, 192, 192), (255, 0, 0), (255, 127, 127), (165, 0, 0), (165, 82, 82), (127, 0, 0), (127, 63, 63), (76, 0, 0), (76, 38, 38), (38, 0, 0), (38, 19, 19), (255, 63, 0), (255, 159, 127), (165, 41, 0), (165, 103, 82), (127, 31, 0), (127, 79, 63), (76, 19, 0), (76, 47, 38), (38, 9, 0), (38, 23, 19), (255, 127, 0), (255, 191, 127), (165, 82, 0), (165, 124, 82), (127, 63, 0), (127, 95, 63), (76, 38, 0), (76, 57, 38), (38, 19, 0), (38, 28, 19), (255, 191, 0), (255, 223, 127), (165, 124, 0), (165, 145, 82), (127, 95, 0), (127, 111, 63), (76, 57, 0), (76, 66, 38), (38, 28, 0), (38, 33, 19), (255, 255, 0), (255, 255, 127), (165, 165, 0), (165, 165, 82), (127, 127, 0), (127, 127, 63), (76, 76, 0), (76, 76, 38), (38, 38, 0), (38, 38, 19), (191, 255, 0), (223, 255, 127), (124, 165, 0), (145, 165, 82), (95, 127, 0), (111, 127, 63), (57, 76, 0), (66, 76, 38), (28, 38, 0), (33, 38, 19), (127, 255, 0), (191, 255, 127), (82, 165, 0), (124, 165, 82), (63, 127, 0), (95, 127, 63), (38, 76, 0), (57, 76, 38), (19, 38, 0), (28, 38, 19), (63, 255, 0), (159, 255, 127), (41, 165, 0), (103, 165, 82), (31, 127, 0), (79, 127, 63), (19, 76, 0), (47, 76, 38), (9, 38, 0), (23, 38, 19), (0, 255, 0), (127, 255, 127), (0, 165, 0), (82, 165, 82), (0, 127, 0), (63, 127, 63), (0, 76, 0), (38, 76, 38), (0, 38, 0), (19, 38, 19), (0, 255, 63), (127, 255, 159), (0, 165, 41), (82, 165, 103), (0, 127, 31), (63, 127, 79), (0, 76, 19), (38, 76, 47), (0, 38, 9), (19, 38, 23), (0, 255, 127), (127, 255, 191), (0, 165, 82), (82, 165, 124), (0, 127, 63), (63, 127, 95), (0, 76, 38), (38, 76, 57), (0, 38, 19), (19, 38, 28), (0, 255, 191), (127, 255, 223), (0, 165, 124), (82, 165, 145), (0, 127, 95), (63, 127, 111), (0, 76, 57), (38, 76, 66), (0, 38, 28), (19, 38, 33), (0, 255, 255), (127, 255, 255), (0, 165, 165), (82, 165, 165), (0, 127, 127), (63, 127, 127), (0, 76, 76), (38, 76, 76), (0, 38, 38), (19, 38, 38), (0, 191, 255), (127, 223, 255), (0, 124, 165), (82, 145, 165), (0, 95, 127), (63, 111, 127), (0, 57, 76), (38, 66, 76), (0, 28, 38), (19, 33, 38), (0, 127, 255), (127, 191, 255), (0, 82, 165), (82, 124, 165), (0, 63, 127), (63, 95, 127), (0, 38, 76), (38, 57, 76), (0, 19, 38), (19, 28, 38), (0, 63, 255), (127, 159, 255), (0, 41, 165), (82, 103, 165), (0, 31, 127), (63, 79, 127), (0, 19, 76), (38, 47, 76), (0, 9, 38), (19, 23, 38), (0, 0, 255), (127, 127, 255), (0, 0, 165), (82, 82, 165), (0, 0, 127), (63, 63, 127), (0, 0, 76), (38, 38, 76), (0, 0, 38), (19, 19, 38), (63, 0, 255), (159, 127, 255), (41, 0, 165), (103, 82, 165), (31, 0, 127), (79, 63, 127), (19, 0, 76), (47, 38, 76), (9, 0, 38), (23, 19, 38), (127, 0, 255), (191, 127, 255), (82, 0, 165), (124, 82, 165), (63, 0, 127), (95, 63, 127), (38, 0, 76), (57, 38, 76), (19, 0, 38), (28, 19, 38), (191, 0, 255), (223, 127, 255), (124, 0, 165), (145, 82, 165), (95, 0, 127), (111, 63, 127), (57, 0, 76), (66, 38, 76), (28, 0, 38), (33, 19, 38), (255, 0, 255), (255, 127, 255), (165, 0, 165), (165, 82, 165), (127, 0, 127), (127, 63, 127), (76, 0, 76), (76, 38, 76), (38, 0, 38), (38, 19, 38), (255, 0, 191), (255, 127, 223), (165, 0, 124), (165, 82, 145), (127, 0, 95), (127, 63, 111), (76, 0, 57), (76, 38, 66), (38, 0, 28), (38, 19, 33), (255, 0, 127), (255, 127, 191), (165, 0, 82), (165, 82, 124), (127, 0, 63), (127, 63, 95), (76, 0, 38), (76, 38, 57), (38, 0, 19), (38, 19, 28), (255, 0, 63), (255, 127, 159), (165, 0, 41), (165, 82, 103), (127, 0, 31), (127, 63, 79), (76, 0, 19), (76, 38, 47), (38, 0, 9), (38, 19, 23), (51, 51, 51), (91, 91, 91), (132, 132, 132), (173, 173, 173), (214, 214, 214), (255, 255, 255), ];
#[inline]
pub fn aci_to_rgb(index: u8) -> Option<(u8, u8, u8)> {
if index == 0 {
None
} else {
Some(ACI_COLORS[index as usize])
}
}
pub fn nearest_aci(r: u8, g: u8, b: u8) -> u8 {
let mut best_idx: u8 = 7;
let mut best_dist = u32::MAX;
for i in 1u16..=255 {
let (cr, cg, cb) = ACI_COLORS[i as usize];
let dr = (r as i32) - (cr as i32);
let dg = (g as i32) - (cg as i32);
let db = (b as i32) - (cb as i32);
let dist = (dr * dr + dg * dg + db * db) as u32;
if dist < best_dist {
best_dist = dist;
best_idx = i as u8;
if dist == 0 {
break;
}
}
}
best_idx
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn aci_standard_colors() {
assert_eq!(aci_to_rgb(1), Some((255, 0, 0))); assert_eq!(aci_to_rgb(2), Some((255, 255, 0))); assert_eq!(aci_to_rgb(3), Some((0, 255, 0))); assert_eq!(aci_to_rgb(4), Some((0, 255, 255))); assert_eq!(aci_to_rgb(5), Some((0, 0, 255))); assert_eq!(aci_to_rgb(6), Some((255, 0, 255))); assert_eq!(aci_to_rgb(7), Some((255, 255, 255))); }
#[test]
fn aci_byblock_returns_none() {
assert_eq!(aci_to_rgb(0), None);
}
#[test]
fn nearest_aci_exact_match() {
assert_eq!(nearest_aci(255, 0, 0), 1); assert_eq!(nearest_aci(0, 255, 0), 3); assert_eq!(nearest_aci(0, 0, 255), 5); assert_eq!(nearest_aci(255, 255, 0), 2); }
#[test]
fn nearest_aci_approximate() {
let idx = nearest_aci(250, 5, 5);
assert_eq!(idx, 1);
}
#[test]
fn table_length() {
assert_eq!(ACI_COLORS.len(), 256);
}
#[test]
fn grays_are_correct() {
assert_eq!(aci_to_rgb(8), Some((128, 128, 128))); assert_eq!(aci_to_rgb(9), Some((192, 192, 192))); assert_eq!(aci_to_rgb(250), Some((51, 51, 51)));
assert_eq!(aci_to_rgb(255), Some((255, 255, 255)));
}
}