1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
use crate::color::Color;
use crate::io;
use image::{Rgba, RgbaImage};
use itertools::Itertools;
use std::collections::hash_map::DefaultHasher;
use std::collections::HashMap;
use std::hash::{Hash, Hasher};

pub struct Manager {
    pub image: RgbaImage,
    pub color_file: io::ColorFile,
    pub lookup: HashMap<Rgba<u8>, String>,
}

pub struct ManagerOptions {
    pub image_path: String,
    pub color_file_path: String,
}

impl Manager {
    pub fn new(opts: ManagerOptions) -> Manager {
        let cf = io::read(&opts.color_file_path[..]);
        return Manager {
            image: io::open(&opts.image_path[..]),
            color_file: cf.clone(),
            lookup: cf.create_lookup(),
        };
    }

    pub fn get(&self, x: u32, y: u32) -> PixelData {
        let pixel = self.image.get_pixel(x, y);
        match self.lookup.get(&pixel) {
            Some(name) => {
                return PixelData {
                    x: x,
                    y: y,
                    color: pixel.clone(),
                    color_name: name.clone(),
                }
            }
            _ => {
                return PixelData {
                    x: x,
                    y: y,
                    color: pixel.clone(),
                    color_name: String::from("UNKNOWN"),
                }
            }
        }
    }

    pub fn hash(&self, x: u32, y: u32) -> u64 {
        let mut hasher = DefaultHasher::new();
        self.get(x, y).hash(&mut hasher);
        return hasher.finish();
    }

    pub fn colors_hex(&self) -> Vec<String> {
        self.lookup
            .keys()
            .map(|x| format!("0x{}", x.hex()))
            .collect()
    }

    pub fn colors_rgb(&self) -> Vec<[u8; 3]> {
        self.lookup.keys().map(|x| x.rgb()).collect()
    }

    pub fn color_names(&self) -> Vec<String> {
        self.lookup.values().map(|x| x.clone()).collect()
    }

    pub fn unique_hex_colors(&self) -> Vec<String> {
        self.image
            .pixels()
            .sorted_by(|a, b| a.compare(&b))
            .dedup()
            .map(|x| format!("0x{}", x.hex()))
            .collect()
    }

    pub fn unique_rgb_colors(&self) -> Vec<[u8; 3]> {
        self.image
            .pixels()
            .sorted_by(|a, b| a.compare(&b))
            .dedup()
            .map(|x| x.rgb())
            .collect()
    }

    pub fn show_names(&self) {
        for pixel in self.image.pixels() {
            let c = pixel.paint("██");
            match self.lookup.get(&pixel) {
                Some(name) => print!("{} {} :: ", c, name),
                _ => println!("Color {:?} not found in lookup ...", pixel),
            }
        }
        println!();
    }
}

pub struct PixelData {
    pub x: u32,
    pub y: u32,
    pub color: Rgba<u8>,
    pub color_name: String,
}

impl Hash for PixelData {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.x.hash(state);
        self.y.hash(state);
        self.color.hash(state);
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_manager_get() {
        let opts = ManagerOptions {
            image_path: String::from("src/test-image.png"),
            color_file_path: String::from("src/test-color-file.ron"),
        };
        let manager = Manager::new(opts);
        let pixel_data = manager.get(0, 0);
        assert_eq!(pixel_data.x, 0);
        assert_eq!(pixel_data.y, 0);
        assert_eq!(pixel_data.color.rgba(), [255, 0, 0, 255]);
        assert_eq!(pixel_data.color_name, "red");
    }

    #[test]
    fn test_manager_hash() {
        let opts = ManagerOptions {
            image_path: String::from("src/test-image.png"),
            color_file_path: String::from("src/test-color-file.ron"),
        };
        let manager = Manager::new(opts);
        assert_eq!(manager.hash(0, 0), 14349191598533205778);
    }
}