1use crate::color::Color;
2use crate::io;
3use image::{Rgba, RgbaImage};
4use itertools::Itertools;
5use std::collections::hash_map::DefaultHasher;
6use std::collections::HashMap;
7use std::hash::{Hash, Hasher};
8
9pub struct Manager {
10 pub image: RgbaImage,
11 pub color_file: io::ColorFile,
12 pub lookup: HashMap<Rgba<u8>, String>,
13}
14
15pub struct ManagerOptions {
16 pub image_path: String,
17 pub color_file_path: String,
18}
19
20impl Manager {
21 pub fn new(opts: ManagerOptions) -> Manager {
22 let cf = io::read(&opts.color_file_path[..]);
23 return Manager {
24 image: io::open(&opts.image_path[..]),
25 color_file: cf.clone(),
26 lookup: cf.create_lookup(),
27 };
28 }
29
30 pub fn get(&self, x: u32, y: u32) -> PixelData {
31 let pixel = self.image.get_pixel(x, y);
32 match self.lookup.get(&pixel) {
33 Some(name) => {
34 return PixelData {
35 x: x,
36 y: y,
37 color: pixel.clone(),
38 color_name: name.clone(),
39 }
40 }
41 _ => {
42 return PixelData {
43 x: x,
44 y: y,
45 color: pixel.clone(),
46 color_name: String::from("UNKNOWN"),
47 }
48 }
49 }
50 }
51
52 pub fn hash(&self, x: u32, y: u32) -> u64 {
53 let mut hasher = DefaultHasher::new();
54 self.get(x, y).hash(&mut hasher);
55 return hasher.finish();
56 }
57
58 pub fn colors_hex(&self) -> Vec<String> {
59 self.lookup
60 .keys()
61 .map(|x| format!("0x{}", x.hex()))
62 .collect()
63 }
64
65 pub fn colors_rgb(&self) -> Vec<[u8; 3]> {
66 self.lookup.keys().map(|x| x.rgb()).collect()
67 }
68
69 pub fn color_names(&self) -> Vec<String> {
70 self.lookup.values().map(|x| x.clone()).collect()
71 }
72
73 pub fn unique_hex_colors(&self) -> Vec<String> {
74 self.image
75 .pixels()
76 .sorted_by(|a, b| a.compare(&b))
77 .dedup()
78 .map(|x| format!("0x{}", x.hex()))
79 .collect()
80 }
81
82 pub fn unique_rgb_colors(&self) -> Vec<[u8; 3]> {
83 self.image
84 .pixels()
85 .sorted_by(|a, b| a.compare(&b))
86 .dedup()
87 .map(|x| x.rgb())
88 .collect()
89 }
90
91 pub fn show_names(&self) {
92 for pixel in self.image.pixels() {
93 let c = pixel.paint("██");
94 match self.lookup.get(&pixel) {
95 Some(name) => print!("{} {} :: ", c, name),
96 _ => println!("Color {:?} not found in lookup ...", pixel),
97 }
98 }
99 println!();
100 }
101}
102
103pub struct PixelData {
104 pub x: u32,
105 pub y: u32,
106 pub color: Rgba<u8>,
107 pub color_name: String,
108}
109
110impl Hash for PixelData {
111 fn hash<H: Hasher>(&self, state: &mut H) {
112 self.x.hash(state);
113 self.y.hash(state);
114 self.color.hash(state);
115 }
116}
117
118#[cfg(test)]
119mod tests {
120 use super::*;
121
122 #[test]
123 fn test_manager_get() {
124 let opts = ManagerOptions {
125 image_path: String::from("src/test-image.png"),
126 color_file_path: String::from("src/test-color-file.ron"),
127 };
128 let manager = Manager::new(opts);
129 let pixel_data = manager.get(0, 0);
130 assert_eq!(pixel_data.x, 0);
131 assert_eq!(pixel_data.y, 0);
132 assert_eq!(pixel_data.color.rgba(), [255, 0, 0, 255]);
133 assert_eq!(pixel_data.color_name, "red");
134 }
135
136 #[test]
137 fn test_manager_hash() {
138 let opts = ManagerOptions {
139 image_path: String::from("src/test-image.png"),
140 color_file_path: String::from("src/test-color-file.ron"),
141 };
142 let manager = Manager::new(opts);
143 assert_eq!(manager.hash(0, 0), 14349191598533205778);
144 }
145}