use crate::{Ch8, Format};
#[derive(Clone)]
pub struct Palette<F>
where F: Format<Chan = Ch8>
{
table: Vec<F>,
threshold_fn: fn(usize) -> F,
}
impl<F> Palette<F>
where F: Format<Chan = Ch8>
{
pub fn new(capacity: usize) -> Self {
let table = Vec::with_capacity(capacity);
let threshold_fn = |_| F::default();
Palette { table, threshold_fn }
}
pub fn len(&self) -> usize {
self.table.len()
}
pub fn set_threshold_fn(&mut self, threshold_fn: fn(usize) -> F) {
self.threshold_fn = threshold_fn;
}
fn u8_slice(colors: &[F]) -> &[u8] {
unsafe { colors.align_to::<u8>().1 }
}
pub fn as_u8_slice(&self) -> &[u8] {
Self::u8_slice(&self.table)
}
pub fn entry(&self, i: usize) -> Option<F> {
if i < self.table.len() {
Some(self.table[i])
} else {
None
}
}
pub fn set_entry(&mut self, clr: F) -> Option<usize> {
if let Some((i, dif)) = self.best_match(clr) {
if dif.within_threshold((self.threshold_fn)(self.table.len())) {
return Some(i);
}
}
let i = self.table.len();
if i < self.table.capacity() {
self.table.push(clr);
Some(i)
} else {
None
}
}
fn best_match(&self, clr: F) -> Option<(usize, F)> {
let mut best = None;
for (i, c) in self.table.iter().enumerate() {
let dif = clr.difference(*c);
if {
if let Some((_, d)) = best {
dif.within_threshold(d) && dif != d
} else { true }
} {
best = Some((i, dif));
}
}
best
}
pub fn replace_entry(&mut self, i: usize, clr: F) -> Option<F> {
if i < self.table.len() {
let old = self.table[i];
self.table[i] = clr;
Some(old)
} else {
None
}
}
pub fn histogram<T>(&self, ent: &[T]) -> Option<Vec<usize>>
where T: Copy, usize: From<T>
{
let mut hist = vec![0; self.table.len()];
for e in ent {
let i = usize::from(*e);
if i < hist.len() {
hist[i] += 1;
} else {
return None;
}
}
Some(hist)
}
}
#[cfg(test)]
mod test {
use super::super::*;
#[test]
fn fill_16() {
let mut p = Palette::new(16);
assert_eq!(p.entry(4), None);
for i in 0..16 {
let idx = p.set_entry(Rgb8::new(i, i, i)).unwrap();
assert_eq!(i as usize, idx);
}
assert_eq!(p.set_entry(Rgb8::new(255, 255, 255)), None);
for i in 0..16 {
let idx = p.set_entry(Rgb8::new(i, i, i)).unwrap();
assert_eq!(i as usize, idx);
}
assert_eq!(p.entry(5), Some(Rgb8::new(5, 5, 5)));
p.replace_entry(5, Rgb8::new(0x55, 0x55, 0x55));
let v = vec![
0x00,0x00,0x00, 0x01,0x01,0x01, 0x02,0x02,0x02, 0x03,0x03,0x03,
0x04,0x04,0x04, 0x55,0x55,0x55, 0x06,0x06,0x06, 0x07,0x07,0x07,
0x08,0x08,0x08, 0x09,0x09,0x09, 0x0A,0x0A,0x0A, 0x0B,0x0B,0x0B,
0x0C,0x0C,0x0C, 0x0D,0x0D,0x0D, 0x0E,0x0E,0x0E, 0x0F,0x0F,0x0F,
];
assert_eq!(p.as_u8_slice(), &v[..]);
}
#[test]
fn check_hist() {
let mut p = Palette::new(8);
for i in 0..7 {
p.set_entry(Rgb8::new(i, i, i));
}
let v: Vec<u8> = vec![
0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x01,0x02,0x04,0x01,0x02,
0x02,0x04,0x02,0x06,0x04,0x03,0x03,0x03,0x06,0x03,0x01,0x02,
0x01,0x02,0x04,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x01,0x02,
0x00,0x00,0x00,0x02,0x02,0x04,0x01,0x00,0x00,0x00,0x02,0x04,
];
assert_eq!(p.histogram(&v[..]), Some(vec![18, 6, 10, 4, 8, 0, 2]));
}
#[test]
fn matching() {
let mut p = Palette::new(8);
assert_eq!(p.set_entry(Rgb8::new(10, 10, 10)), Some(0));
assert_eq!(p.set_entry(Rgb8::new(20, 20, 20)), Some(1));
assert_eq!(p.set_entry(Rgb8::new(30, 30, 30)), Some(2));
assert_eq!(p.set_entry(Rgb8::new(40, 40, 40)), Some(3));
p.set_threshold_fn(|_| Rgb8::new(4, 5, 6));
assert_eq!(p.set_entry(Rgb8::new(15, 15, 15)), Some(4));
p.set_threshold_fn(|_| Rgb8::new(5, 5, 5));
assert_eq!(p.set_entry(Rgb8::new(35, 35, 35)), Some(2));
}
}