1pub mod algorithms;
28pub mod color;
29pub mod preprocessors;
30pub mod swatch;
31
32use algorithms::kmeans;
33use algorithms::median_cut;
34use algorithms::octree;
35use color::Rgb;
36use kmeans::{InitMethod, KmeansColorSpace};
37use octree::{OctreeColorSpace, OctreeDepth};
38use swatch::Swatch;
39
40pub fn generate_swatches_kmeans(
59 pixels: &[Rgb],
60 count: usize,
61 color_space: KmeansColorSpace,
62 init: InitMethod,
63 seed: u64,
64) -> Vec<Swatch> {
65 collect_sorted_swatches(kmeans::extract_colors_kmeans(
66 pixels,
67 count,
68 color_space,
69 init,
70 seed,
71 ))
72}
73
74pub fn generate_swatches_octree(
75 pixels: &[Rgb],
76 count: usize,
77 color_space: OctreeColorSpace,
78 max_depth: OctreeDepth,
79) -> Vec<Swatch> {
80 collect_sorted_swatches(octree::extract_colors_octree(
81 pixels,
82 count,
83 color_space,
84 max_depth,
85 ))
86}
87
88pub fn generate_swatches_median_cut(pixels: &[Rgb], count: usize) -> Vec<Swatch> {
89 collect_sorted_swatches(median_cut::extract_colors_median_cut(pixels, count))
90}
91
92fn collect_sorted_swatches(raw: Vec<(Rgb, u32)>) -> Vec<Swatch> {
93 let mut swatches: Vec<Swatch> = raw.into_iter().map(|(c, p)| Swatch::new(c, p)).collect();
94 swatches.sort_by_key(|b| std::cmp::Reverse(b.population));
95 swatches
96}
97
98pub fn pixels_from_rgba(data: &[u8]) -> Vec<Rgb> {
115 data.chunks_exact(4)
116 .map(|chunk| Rgb::new(chunk[0], chunk[1], chunk[2]))
117 .collect()
118}
119
120const MAX_SAMPLE: usize = 20_000;
121
122fn sample_step(len: usize) -> usize {
123 if len > MAX_SAMPLE {
124 len / MAX_SAMPLE
125 } else {
126 1
127 }
128}
129
130#[cfg(feature = "wasm")]
131use swatch::swatches_to_json;
132#[cfg(feature = "wasm")]
133use wasm_bindgen::prelude::*;
134
135#[cfg(feature = "wasm")]
136#[wasm_bindgen(js_name = generateSwatches)]
137pub fn generate_swatches_kmeans_wasm(
138 rgba_data: &[u8],
139 count: usize,
140 color_space: &str,
141 init_method: &str,
142 seed: u64,
143) -> String {
144 let pixels = pixels_from_rgba(rgba_data);
145
146 let cs = match color_space {
147 "lab" => KmeansColorSpace::Lab,
148 "lab-ciede2000" => KmeansColorSpace::LabCIEDE2000,
149 _ => KmeansColorSpace::Rgb,
150 };
151 let init = match init_method {
152 "random" => InitMethod::Random,
153 _ => InitMethod::KMeansPlusPlus,
154 };
155
156 let swatches = generate_swatches_kmeans(&pixels, count, cs, init, seed);
157 swatches_to_json(&swatches)
158}
159
160#[cfg(feature = "wasm")]
161#[wasm_bindgen(js_name = generateSwatchesOctree)]
162pub fn generate_swatches_octree_wasm(
163 rgba_data: &[u8],
164 count: usize,
165 color_space: &str,
166 max_depth: u32,
167) -> String {
168 let pixels = pixels_from_rgba(rgba_data);
169
170 let cs = match color_space {
171 "lab" => OctreeColorSpace::Lab,
172 _ => OctreeColorSpace::Rgb,
173 };
174
175 let swatches = generate_swatches_octree(&pixels, count, cs, OctreeDepth::from_u32(max_depth));
176 swatches_to_json(&swatches)
177}
178
179#[cfg(feature = "wasm")]
180#[wasm_bindgen(js_name = generateSwatchesMedianCut)]
181pub fn generate_swatches_median_cut_wasm(rgba_data: &[u8], count: usize) -> String {
182 let pixels = pixels_from_rgba(rgba_data);
183 let swatches = generate_swatches_median_cut(&pixels, count);
184 swatches_to_json(&swatches)
185}
186
187#[cfg(feature = "wasm")]
188#[wasm_bindgen(js_name = complementaryColor)]
189pub fn complementary_color_wasm(r: u8, g: u8, b: u8) -> Vec<u8> {
190 let comp = Rgb::new(r, g, b).to_hsl().complement().to_rgb();
191 vec![comp.r, comp.g, comp.b]
192}
193
194#[cfg(feature = "wasm")]
195#[wasm_bindgen(js_name = slicPreprocess)]
196pub fn slic_preprocess_wasm(
197 rgba_data: &[u8],
198 width: usize,
199 height: usize,
200 num_superpixels: usize,
201 compactness: f32,
202) -> Vec<u8> {
203 let pixels = pixels_from_rgba(rgba_data);
204 let result =
205 preprocessors::slic::slic_preprocess(&pixels, width, height, num_superpixels, compactness);
206 preprocessors::rgb_vec_to_rgba(&result)
207}
208
209#[cfg(feature = "wasm")]
210#[wasm_bindgen(js_name = seedsPreprocess)]
211pub fn seeds_preprocess_wasm(
212 rgba_data: &[u8],
213 width: usize,
214 height: usize,
215 num_superpixels: usize,
216 num_levels: usize,
217 histogram_bins: usize,
218) -> Vec<u8> {
219 let pixels = pixels_from_rgba(rgba_data);
220 let result = preprocessors::seeds::seeds_preprocess(
221 &pixels,
222 width,
223 height,
224 num_superpixels,
225 num_levels,
226 histogram_bins,
227 );
228 preprocessors::rgb_vec_to_rgba(&result)
229}