use alloc::vec;
use alloc::vec::Vec;
const COLOR_CACHE_MULT: u32 = 0x1e35a7bd;
#[derive(Debug, Clone)]
pub struct ColorCache {
colors: Vec<u32>,
bits: u8,
hash_shift: u32,
}
impl ColorCache {
pub fn new(bits: u8) -> Self {
debug_assert!((1..=11).contains(&bits));
let size = 1 << bits;
Self {
colors: vec![0; size],
bits,
hash_shift: 32 - bits as u32,
}
}
#[inline]
pub fn bits(&self) -> u8 {
self.bits
}
#[inline]
pub fn size(&self) -> usize {
self.colors.len()
}
#[inline]
fn hash(&self, argb: u32) -> usize {
(COLOR_CACHE_MULT.wrapping_mul(argb) >> self.hash_shift) as usize
}
#[inline]
pub fn insert(&mut self, argb: u32) {
let idx = self.hash(argb);
self.colors[idx] = argb;
}
#[inline]
pub fn lookup(&self, argb: u32) -> Option<u16> {
let idx = self.hash(argb);
if self.colors[idx] == argb {
Some(idx as u16)
} else {
None
}
}
#[inline]
pub fn get(&self, idx: u16) -> u32 {
self.colors[idx as usize]
}
pub fn clear(&mut self) {
self.colors.fill(0);
}
}
pub fn estimate_optimal_cache_bits(pixels: &[u32], _width: usize, quality: u8) -> u8 {
if quality < 25 || pixels.len() < 100 {
return 0; }
let sample_size = (pixels.len() / 16).clamp(256, 4096);
let step = pixels.len() / sample_size;
let mut best_bits = 0u8;
let mut best_savings = 0i32;
for bits in 1..=10u8 {
let mut cache = ColorCache::new(bits);
let mut hits = 0u32;
let mut total = 0u32;
for i in (0..pixels.len()).step_by(step) {
let argb = pixels[i];
if cache.lookup(argb).is_some() {
hits += 1;
}
cache.insert(argb);
total += 1;
}
let hit_rate = hits as f32 / total.max(1) as f32;
let bits_saved_per_hit = 32 - bits as i32;
let overhead_per_pixel = (bits as i32) / 4; let savings = (hit_rate * bits_saved_per_hit as f32 - overhead_per_pixel as f32) as i32;
if savings > best_savings {
best_savings = savings;
best_bits = bits;
}
}
if best_savings <= 0 {
0 } else {
best_bits
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_color_cache_insert_lookup() {
let mut cache = ColorCache::new(4);
let color = 0xFF112233u32;
assert!(cache.lookup(color).is_none());
cache.insert(color);
let idx = cache.lookup(color);
assert!(idx.is_some());
assert_eq!(cache.get(idx.unwrap()), color);
}
#[test]
fn test_color_cache_collision() {
let mut cache = ColorCache::new(1); let c1 = 0xFF000000u32;
let c2 = 0xFF000001u32;
cache.insert(c1);
cache.insert(c2);
let found1 = cache.lookup(c1).is_some();
let found2 = cache.lookup(c2).is_some();
assert!(found1 || found2);
}
#[test]
fn test_hash_determinism() {
let cache = ColorCache::new(8);
let color = 0xAABBCCDDu32;
let h1 = cache.lookup(color);
let h2 = cache.lookup(color);
assert_eq!(h1, h2);
}
}