1use std::{cell::RefCell, collections::HashMap, path::Path, rc::Rc, str::FromStr};
2
3use error::ImageError;
4use image::{
5 DynamicImage,
6 ImageError::{IoError, Unsupported},
7 RgbImage, RgbaImage,
8};
9
10mod error;
11
12pub fn load<P>(path: P) -> Result<(Vec<Record>, u32, u32), ImageError>
23where
24 P: AsRef<Path>,
25{
26 OcTree::load_with_maxcolor(path, 16)
27}
28
29pub fn load_with_maxcolor<P>(path: P, max_color: u8) -> Result<(Vec<Record>, u32, u32), ImageError>
40where
41 P: AsRef<Path>,
42{
43 OcTree::load_with_maxcolor(path, max_color)
44}
45
46pub fn load_by_image_with_maxcolor(
58 image: &DynamicImage,
59 max_color: u8,
60) -> Result<(Vec<Record>, u32, u32), ImageError> {
61 let options = PaletteOptions {
63 extract_max: max_color,
64 output_max: max_color,
65 merge_delta_e: 0.0,
66 min_ratio: 0.0,
67 };
68 load_by_image_with_options(image, &options)
69}
70
71#[derive(Debug, Clone, Copy)]
78pub struct PaletteOptions {
79 pub extract_max: u8,
80 pub output_max: u8,
81 pub merge_delta_e: f32,
82 pub min_ratio: f32,
83}
84
85impl Default for PaletteOptions {
86 fn default() -> Self {
87 Self {
88 extract_max: 16,
89 output_max: 8,
90 merge_delta_e: 10.0,
91 min_ratio: 0.01,
92 }
93 }
94}
95
96impl PaletteOptions {
97 pub fn new() -> Self {
98 Self::default()
99 }
100 pub fn with_extract_max(mut self, v: u8) -> Self {
101 self.extract_max = v;
102 self
103 }
104 pub fn with_output_max(mut self, v: u8) -> Self {
105 self.output_max = v;
106 self
107 }
108 pub fn with_merge_delta_e(mut self, v: f32) -> Self {
109 self.merge_delta_e = v;
110 self
111 }
112 pub fn with_min_ratio(mut self, v: f32) -> Self {
113 self.min_ratio = v;
114 self
115 }
116}
117
118pub fn load_by_image_with_options(
130 image: &DynamicImage,
131 options: &PaletteOptions,
132) -> Result<(Vec<Record>, u32, u32), ImageError> {
133 let (mut list, width, height) = OcTree::load_by_image(image, options.extract_max as u32);
134
135 #[cfg(feature = "lab")]
136 {
137 if options.merge_delta_e > 0.0 {
138 list = merge_similar(list, options.merge_delta_e);
139 }
140 }
141 #[cfg(not(feature = "lab"))]
142 {
143 if options.merge_delta_e > 0.0 {
144 return Err(ImageError::InvalidParameter);
145 }
146 }
147
148 let total = (width * height) as f32;
149 if options.min_ratio > 0.0 {
150 list.retain(|r| r.count as f32 / total * 100.0 >= options.min_ratio);
151 }
152 list.sort_by(|a, b| b.count.cmp(&a.count));
153 if list.len() > options.output_max as usize {
154 list.truncate(options.output_max as usize);
155 }
156 Ok((list, width, height))
157}
158
159#[cfg(feature = "lab")]
172fn merge_similar(records: Vec<Record>, delta_e: f32) -> Vec<Record> {
173 if delta_e <= 0.0 || records.is_empty() {
174 return records;
175 }
176
177 let mut clusters: Vec<(u64, u64, u64, u32, f64, f64, f64)> = records
180 .into_iter()
181 .map(|rec| {
182 let lab = rec.rgb.to_lab();
183 let count = rec.count as u64;
184 (
185 rec.rgb.r as u64 * count,
186 rec.rgb.g as u64 * count,
187 rec.rgb.b as u64 * count,
188 rec.count,
189 lab.l as f64 * rec.count as f64,
190 lab.a as f64 * rec.count as f64,
191 lab.b as f64 * rec.count as f64,
192 )
193 })
194 .collect();
195
196 loop {
198 let mut best: Option<(usize, usize, f32)> = None;
199 for i in 0..clusters.len() {
200 let ci = &clusters[i];
201 let ci_cnt = ci.3 as f64;
202 let cil = ci.4 / ci_cnt;
203 let cia = ci.5 / ci_cnt;
204 let cib = ci.6 / ci_cnt;
205 for j in (i + 1)..clusters.len() {
206 let cj = &clusters[j];
207 let cj_cnt = cj.3 as f64;
208 let cjl = cj.4 / cj_cnt;
209 let cja = cj.5 / cj_cnt;
210 let cjb = cj.6 / cj_cnt;
211 let dl = (cil - cjl) / 2.0;
212 let da = cia - cja;
213 let db = cib - cjb;
214 let dist = ((dl * dl + da * da + db * db) as f32).sqrt();
215 match best {
216 None => best = Some((i, j, dist)),
217 Some((_, _, d)) if dist < d => best = Some((i, j, dist)),
218 _ => {}
219 }
220 }
221 }
222
223 match best {
224 Some((i, j, dist)) if dist <= delta_e => {
225 let cj = clusters[j].clone();
227 let ci = &mut clusters[i];
228 ci.0 += cj.0;
229 ci.1 += cj.1;
230 ci.2 += cj.2;
231 ci.3 += cj.3;
232 ci.4 += cj.4;
233 ci.5 += cj.5;
234 ci.6 += cj.6;
235 clusters.remove(j);
236 }
237 _ => break,
238 }
239 }
240
241 let mut result: Vec<Record> = clusters
242 .into_iter()
243 .map(|c| {
244 let cnt = c.3 as u64;
245 let r = ((c.0 + cnt / 2) / cnt) as u8;
247 let g = ((c.1 + cnt / 2) / cnt) as u8;
248 let b = ((c.2 + cnt / 2) / cnt) as u8;
249 Record {
250 rgb: RGB { r, g, b },
251 count: c.3,
252 }
253 })
254 .collect();
255 result.sort_by(|a, b| b.count.cmp(&a.count));
256 result
257}
258
259#[derive(Debug)]
260struct OcTree {
261 leaf_num: u32,
262 to_reduce: [Vec<Rc<RefCell<Node>>>; 8],
263 max_color: u32,
264}
265
266impl OcTree {
267 fn load_with_maxcolor<P>(path: P, max_color: u8) -> Result<(Vec<Record>, u32, u32), ImageError>
268 where
269 P: AsRef<Path>,
270 {
271 let image = image::open(path).map_err(|error| match error {
272 Unsupported(error) => ImageError::UnsupportedFile(error),
273 IoError(error) => ImageError::IoError(error),
274 error => ImageError::Unknown(error),
275 })?;
276
277 Ok(Self::load_by_image(&image, max_color.into()))
278 }
279
280 fn load_by_image(image: &DynamicImage, max_color: u32) -> (Vec<Record>, u32, u32) {
281 const ARRAY_REPEAT_VALUE: Vec<Rc<RefCell<Node>>> = Vec::new();
282 let mut tree = OcTree {
283 leaf_num: 0,
284 to_reduce: [ARRAY_REPEAT_VALUE; 8],
285 max_color,
286 };
287
288 let rgb = image.to_rgb8();
289 let image_data = ImageData::from(&rgb);
290
291 let root_share = tree.create_node(0);
292
293 for color in image_data.data {
294 tree.add_color(&root_share, color, 0);
295 while tree.leaf_num > tree.max_color {
296 tree.reduce_tree();
297 }
298 }
299
300 let mut map: HashMap<RGB, u32> = HashMap::new();
301 colors_stats(&root_share, &mut map);
302 let mut list = Vec::new();
303 for (rgb, count) in map {
304 list.push(Record { rgb, count });
305 }
306 list.sort_by(|a, b| b.count.cmp(&a.count));
307 (list, image_data.width, image_data.height)
308 }
309
310 fn create_node(&mut self, level: usize) -> Rc<RefCell<Node>> {
311 let node = Node::new();
312 let node_share: Rc<RefCell<Node>> = Rc::new(RefCell::new(node));
313
314 if level == 7 {
315 let mut node_mut: std::cell::RefMut<Node> = node_share.borrow_mut();
316 node_mut.is_leaf = true;
317 self.leaf_num += 1;
318 } else {
319 let a: Rc<RefCell<Node>> = Rc::clone(&node_share);
320 self.to_reduce[level].push(a);
321 self.to_reduce[level].sort_by_key(|k: &Rc<RefCell<Node>>| k.borrow().pixel_count);
322 }
323
324 node_share
325 }
326
327 fn add_color(&mut self, node_share: &Rc<RefCell<Node>>, rgb: RGB, level: usize) {
328 let mut node: std::cell::RefMut<Node> = node_share.borrow_mut();
329 if node.is_leaf {
330 node.pixel_count += 1;
331 node.r += rgb.r as u32;
332 node.g += rgb.g as u32;
333 node.b += rgb.b as u32;
334 } else {
335 let r = rgb.r >> (7 - level) & 1;
336 let g = rgb.g >> (7 - level) & 1;
337 let b = rgb.b >> (7 - level) & 1;
338
339 let idx = ((r << 2) + (g << 1) + b) as usize;
340
341 if node.children[idx].is_none() {
342 let child_share: Rc<RefCell<Node>> = self.create_node(level + 1);
343 node.children[idx] = Some(child_share);
344 }
345
346 self.add_color(node.children[idx].as_ref().unwrap(), rgb, level + 1);
347 }
348 }
349
350 fn reduce_tree(&mut self) {
351 let mut lv: isize = 6;
353
354 while lv >= 0 && self.to_reduce[lv as usize].len() == 0 {
355 lv -= 1;
356 }
357 if lv < 0 {
358 return;
359 }
360
361 let node_share = self.to_reduce[lv as usize].pop().unwrap();
362 let mut node = node_share.borrow_mut();
363
364 let mut r = 0;
366 let mut g = 0;
367 let mut b = 0;
368 let mut pixel_count = 0;
369
370 for i in 0..8 {
371 if node.children[i].is_none() {
372 continue;
373 }
374 let child_share = node.children[i].as_ref().unwrap();
375 let child = child_share.borrow();
376
377 r += child.r;
378 g += child.g;
379 b += child.b;
380 pixel_count += child.pixel_count;
381 self.leaf_num -= 1;
382 }
383
384 node.is_leaf = true;
385 node.r = r;
386 node.g = g;
387 node.b = b;
388 node.pixel_count = pixel_count;
389
390 self.leaf_num += 1;
391 }
392}
393
394fn colors_stats(node_share: &Rc<RefCell<Node>>, map: &mut HashMap<RGB, u32>) {
395 let node = node_share.borrow_mut();
396 if node.is_leaf {
397 let r = (node.r / node.pixel_count) as u8;
398 let g = (node.g / node.pixel_count) as u8;
399 let b = (node.b / node.pixel_count) as u8;
400 let rgb = RGB::from(&[r, g, b]);
401 if let Some(x) = map.get_mut(&rgb) {
402 *x = *x + node.pixel_count;
403 } else {
404 map.insert(rgb, node.pixel_count);
405 }
406 } else {
407 for i in 0..8 {
408 if node.children[i].is_some() {
409 colors_stats(node.children[i].as_ref().unwrap(), map);
410 }
411 }
412 }
413}
414
415impl From<&RgbImage> for ImageData {
416 fn from(image: &RgbImage) -> Self {
417 let (width, height) = image.dimensions();
418 let size = (width * height) as usize;
419
420 let data = image
421 .pixels()
422 .fold(Vec::with_capacity(size), |mut pixels, pixel| {
423 pixels.push(RGB::from(&[pixel[0], pixel[1], pixel[2]]));
424 pixels
425 });
426
427 Self {
428 data,
429 width,
430 height,
431 }
432 }
433}
434
435impl From<&RgbaImage> for ImageData {
436 fn from(image: &RgbaImage) -> Self {
437 let (width, height) = image.dimensions();
438 let size = (width * height) as usize;
439
440 let data = image.pixels().filter(|pixels| pixels[3] > 0).fold(
441 Vec::with_capacity(size),
442 |mut pixels, pixel| {
443 pixels.push(RGB::from(&[pixel[0], pixel[1], pixel[2]]));
444 pixels
445 },
446 );
447
448 Self {
449 data,
450 width,
451 height,
452 }
453 }
454}
455
456#[derive(Debug, Clone, Eq, Hash, PartialEq)]
457pub struct RGB {
458 pub r: u8,
459 pub g: u8,
460 pub b: u8,
461}
462
463impl RGB {
464 pub fn from(rgb: &[u8; 3]) -> RGB {
465 RGB {
466 r: rgb[0],
467 g: rgb[1],
468 b: rgb[2],
469 }
470 }
471
472 pub fn to_hex(&self) -> String {
473 let r = format!("{:0>2}", format!("{:X}", self.r));
474 let g = format!("{:0>2}", format!("{:X}", self.g));
475 let b = format!("{:0>2}", format!("{:X}", self.b));
476 format!("#{}{}{}", r, g, b)
477 }
478
479 #[cfg(feature = "lab")]
480 pub fn to_lab(&self) -> lab::Lab {
481 lab::Lab::from_rgb(&[self.r, self.g, self.b])
482 }
483}
484
485impl FromStr for RGB {
486 type Err = std::num::ParseIntError;
487
488 fn from_str(hex_code: &str) -> Result<Self, Self::Err> {
489 let r: u8 = u8::from_str_radix(&hex_code[1..3], 16)?;
490 let g: u8 = u8::from_str_radix(&hex_code[3..5], 16)?;
491 let b: u8 = u8::from_str_radix(&hex_code[5..7], 16)?;
492
493 Ok(RGB { r, g, b })
494 }
495}
496
497struct ImageData {
498 data: Vec<RGB>,
499 width: u32,
500 height: u32,
501}
502
503#[derive(Debug)]
504struct Node {
505 is_leaf: bool,
506 r: u32,
507 g: u32,
508 b: u32,
509 pixel_count: u32,
510 children: [Option<Rc<RefCell<Node>>>; 8],
511}
512
513impl Node {
514 fn new() -> Node {
515 const ARRAY_REPEAT_VALUE: Option<Rc<RefCell<Node>>> = None;
516 Node {
517 is_leaf: false,
518 r: 0,
519 g: 0,
520 b: 0,
521 pixel_count: 0,
522 children: [ARRAY_REPEAT_VALUE; 8],
523 }
524 }
525}
526
527#[derive(Debug, Clone)]
528pub struct Record {
529 rgb: RGB,
530 count: u32,
531}
532
533impl Record {
534 pub fn new(rgb: RGB, count: u32) -> Self {
535 Record { rgb, count }
536 }
537 pub fn rgb(&self) -> &RGB {
538 &self.rgb
539 }
540 pub fn count(&self) -> u32 {
541 self.count
542 }
543}