use image::{load_from_memory_with_format, RgbaImage}; use std::sync::OnceLock; use wasm_bindgen::prelude::*; use include_dir::{include_dir, Dir, DirEntry}; static mut SAMPLES: OnceLock> = OnceLock::new(); static SAMPLES_DIR: Dir = include_dir!("src/samples"); pub fn init() { unsafe { SAMPLES.get_or_init(|| { log("reading image samples"); read_color_samples().unwrap() }); } } pub fn get_closest_color<'a>(r: u8, g: u8, b: u8) -> &'static ColorSample { unsafe { let color_samples = SAMPLES.get_mut().unwrap(); let mut closest = None; let mut min_diff: f32 = 4294967295.0; //0xFFFFFFFF for sample in color_samples { let diff = get_distance(sample.r, sample.g, sample.b, r, g, b); if diff < min_diff { closest = Some(sample); min_diff = diff; } } let closest = closest.unwrap(); if closest.image.is_none() { let sample_image = load_from_memory_with_format(closest.raw_bytes, image::ImageFormat::Jpeg) .unwrap() .to_rgba8(); closest.image = Some(sample_image); } closest } } /// returns squared euclidian color distance /// as if colors were points in 3d space fn get_distance(r1: u8, g1: u8, b1: u8, r2: u8, g2: u8, b2: u8) -> f32 { let red_dif = r1 as f32 - r2 as f32; let green_dif = g1 as f32 - g2 as f32; let blue_dif = b1 as f32 - b2 as f32; return red_dif * red_dif + green_dif * green_dif + blue_dif * blue_dif; } /// read all sample jpegs into memory pub fn read_color_samples() -> anyhow::Result> { let mut color_samples: Vec = Vec::new(); for entry in SAMPLES_DIR.entries() { if let DirEntry::File(f) = entry { let filename = entry.path().file_name().unwrap().to_str().unwrap(); let hex_r = &filename[0..2]; let hex_g = &filename[2..4]; let hex_b = &filename[4..6]; color_samples.push(ColorSample { filename: filename.into(), r: u8::from_str_radix(&hex_r, 16)?, g: u8::from_str_radix(&hex_g, 16)?, b: u8::from_str_radix(&hex_b, 16)?, raw_bytes: f.contents(), image: None, }); } } log("Done reading image samples"); Ok(color_samples) } #[derive(Debug)] pub struct ColorSample { pub filename: String, pub r: u8, pub g: u8, pub b: u8, pub raw_bytes: &'static [u8], pub image: Option, } #[wasm_bindgen] extern "C" { #[wasm_bindgen(js_namespace = console)] pub fn log(s: &str); #[wasm_bindgen(js_namespace = console, js_name = log)] fn log_u32(a: u32); #[wasm_bindgen(js_namespace = console, js_name = log)] fn log_many(a: &str, b: &str); }