use std::fmt;
#[derive(Debug, Clone, PartialEq)]
pub struct Color {
pub r: u8,
pub g: u8,
pub b: u8,
pub percentage: f32,
}
impl Color {
#[inline]
pub fn new(r: u8, g: u8, b: u8, percentage: f32) -> Self {
Self { r, g, b, percentage }
}
#[inline]
pub fn to_hex(&self) -> u32 {
((self.r as u32) << 16) | ((self.g as u32) << 8) | (self.b as u32)
}
#[inline]
pub(crate) fn to_f64(&self) -> [f64; 3] {
[self.r as f64, self.g as f64, self.b as f64]
}
#[inline]
pub(crate) fn sq_distance_rgb(a: &[u8; 3], b: &[u8; 3]) -> f64 {
let dr = a[0] as f64 - b[0] as f64;
let dg = a[1] as f64 - b[1] as f64;
let db = a[2] as f64 - b[2] as f64;
dr * dr + dg * dg + db * db
}
}
impl fmt::Display for Color {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"#{:02X}{:02X}{:02X} ({:.1}%)",
self.r,
self.g,
self.b,
self.percentage * 100.0
)
}
}
pub type ColorPalette = Vec<Color>;
pub(crate) fn mean_color(pixels: &[[u8; 3]], percentage: f32) -> Option<Color> {
if pixels.is_empty() {
return None;
}
let n = pixels.len() as f64;
let r = pixels.iter().map(|p| p[0] as f64).sum::<f64>() / n;
let g = pixels.iter().map(|p| p[1] as f64).sum::<f64>() / n;
let b = pixels.iter().map(|p| p[2] as f64).sum::<f64>() / n;
Some(Color::new(r.round() as u8, g.round() as u8, b.round() as u8, percentage))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_to_hex() {
let c = Color::new(0xFF, 0xAB, 0x00, 1.0);
assert_eq!(c.to_hex(), 0xFF_AB_00);
}
#[test]
fn test_display() {
let c = Color::new(255, 0, 128, 0.5);
assert_eq!(format!("{c}"), "#FF0080 (50.0%)");
}
#[test]
fn test_sq_distance() {
let a = [0u8, 0, 0];
let b = [3u8, 4u8, 0];
assert!((Color::sq_distance_rgb(&a, &b) - 25.0).abs() < 1e-10);
}
#[test]
fn test_mean_color_empty() {
assert!(mean_color(&[], 1.0).is_none());
}
#[test]
fn test_mean_color_single() {
let c = mean_color(&[[10, 20, 30]], 1.0).unwrap();
assert_eq!((c.r, c.g, c.b), (10, 20, 30));
}
#[test]
fn test_mean_color_average() {
let pixels = vec![[0u8, 0, 0], [100, 100, 100]];
let c = mean_color(&pixels, 1.0).unwrap();
assert_eq!((c.r, c.g, c.b), (50, 50, 50));
}
}