pub mod algorithms;
pub mod color;
pub mod preprocessors;
pub mod swatch;
use algorithms::kmeans;
use algorithms::median_cut;
use algorithms::octree;
use color::Rgb;
use kmeans::{InitMethod, KmeansColorSpace};
use octree::{OctreeColorSpace, OctreeDepth};
use swatch::Swatch;
pub fn generate_swatches_kmeans(
pixels: &[Rgb],
count: usize,
color_space: KmeansColorSpace,
init: InitMethod,
seed: u64,
) -> Vec<Swatch> {
collect_sorted_swatches(kmeans::extract_colors_kmeans(
pixels,
count,
color_space,
init,
seed,
))
}
pub fn generate_swatches_octree(
pixels: &[Rgb],
count: usize,
color_space: OctreeColorSpace,
max_depth: OctreeDepth,
) -> Vec<Swatch> {
collect_sorted_swatches(octree::extract_colors_octree(
pixels,
count,
color_space,
max_depth,
))
}
pub fn generate_swatches_median_cut(pixels: &[Rgb], count: usize) -> Vec<Swatch> {
collect_sorted_swatches(median_cut::extract_colors_median_cut(pixels, count))
}
fn collect_sorted_swatches(raw: Vec<(Rgb, u32)>) -> Vec<Swatch> {
let mut swatches: Vec<Swatch> = raw.into_iter().map(|(c, p)| Swatch::new(c, p)).collect();
swatches.sort_by_key(|b| std::cmp::Reverse(b.population));
swatches
}
pub fn pixels_from_rgba(data: &[u8]) -> Vec<Rgb> {
data.chunks_exact(4)
.map(|chunk| Rgb::new(chunk[0], chunk[1], chunk[2]))
.collect()
}
const MAX_SAMPLE: usize = 20_000;
fn sample_step(len: usize) -> usize {
if len > MAX_SAMPLE {
len / MAX_SAMPLE
} else {
1
}
}
#[cfg(feature = "wasm")]
use swatch::swatches_to_json;
#[cfg(feature = "wasm")]
use wasm_bindgen::prelude::*;
#[cfg(feature = "wasm")]
#[wasm_bindgen(js_name = generateSwatches)]
pub fn generate_swatches_kmeans_wasm(
rgba_data: &[u8],
count: usize,
color_space: &str,
init_method: &str,
seed: u64,
) -> String {
let pixels = pixels_from_rgba(rgba_data);
let cs = match color_space {
"lab" => KmeansColorSpace::Lab,
"lab-ciede2000" => KmeansColorSpace::LabCIEDE2000,
_ => KmeansColorSpace::Rgb,
};
let init = match init_method {
"random" => InitMethod::Random,
_ => InitMethod::KMeansPlusPlus,
};
let swatches = generate_swatches_kmeans(&pixels, count, cs, init, seed);
swatches_to_json(&swatches)
}
#[cfg(feature = "wasm")]
#[wasm_bindgen(js_name = generateSwatchesOctree)]
pub fn generate_swatches_octree_wasm(
rgba_data: &[u8],
count: usize,
color_space: &str,
max_depth: u32,
) -> String {
let pixels = pixels_from_rgba(rgba_data);
let cs = match color_space {
"lab" => OctreeColorSpace::Lab,
_ => OctreeColorSpace::Rgb,
};
let swatches = generate_swatches_octree(&pixels, count, cs, OctreeDepth::from_u32(max_depth));
swatches_to_json(&swatches)
}
#[cfg(feature = "wasm")]
#[wasm_bindgen(js_name = generateSwatchesMedianCut)]
pub fn generate_swatches_median_cut_wasm(rgba_data: &[u8], count: usize) -> String {
let pixels = pixels_from_rgba(rgba_data);
let swatches = generate_swatches_median_cut(&pixels, count);
swatches_to_json(&swatches)
}
#[cfg(feature = "wasm")]
#[wasm_bindgen(js_name = complementaryColor)]
pub fn complementary_color_wasm(r: u8, g: u8, b: u8) -> Vec<u8> {
let comp = Rgb::new(r, g, b).to_hsl().complement().to_rgb();
vec![comp.r, comp.g, comp.b]
}
#[cfg(feature = "wasm")]
#[wasm_bindgen(js_name = slicPreprocess)]
pub fn slic_preprocess_wasm(
rgba_data: &[u8],
width: usize,
height: usize,
num_superpixels: usize,
compactness: f32,
) -> Vec<u8> {
let pixels = pixels_from_rgba(rgba_data);
let result =
preprocessors::slic::slic_preprocess(&pixels, width, height, num_superpixels, compactness);
preprocessors::rgb_vec_to_rgba(&result)
}
#[cfg(feature = "wasm")]
#[wasm_bindgen(js_name = seedsPreprocess)]
pub fn seeds_preprocess_wasm(
rgba_data: &[u8],
width: usize,
height: usize,
num_superpixels: usize,
num_levels: usize,
histogram_bins: usize,
) -> Vec<u8> {
let pixels = pixels_from_rgba(rgba_data);
let result = preprocessors::seeds::seeds_preprocess(
&pixels,
width,
height,
num_superpixels,
num_levels,
histogram_bins,
);
preprocessors::rgb_vec_to_rgba(&result)
}