about summary refs log tree commit diff
diff options
context:
space:
mode:
authorgennyble <gen@nyble.dev>2022-12-10 17:24:16 -0600
committergennyble <gen@nyble.dev>2022-12-10 17:24:16 -0600
commit9ef5ad4644f6a17934f940ce52ff38811daa1631 (patch)
tree38cad0f4132455b8045d5b63692d3b258a1261d2
parent54e14268da4aafc413c092d898272d3967aea328 (diff)
downloadcolorsquash-9ef5ad4644f6a17934f940ce52ff38811daa1631.tar.gz
colorsquash-9ef5ad4644f6a17934f940ce52ff38811daa1631.zip
Libify colorquash
-rw-r--r--Cargo.lock417
-rw-r--r--Cargo.toml16
-rw-r--r--src/lib.rs233
-rw-r--r--src/main.rs175
4 files changed, 256 insertions, 585 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 0c011f8..76ad636 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3,447 +3,60 @@
 version = 3
 
 [[package]]
-name = "adler"
-version = "1.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
-
-[[package]]
-name = "adler32"
-version = "1.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
-
-[[package]]
 name = "ahash"
-version = "0.7.4"
+version = "0.7.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "43bb833f0bf979d8475d38fbf09ed3b8a55e1885fe93ad3f93239fc6a4f17b98"
+checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
 dependencies = [
- "getrandom 0.2.3",
+ "getrandom",
  "once_cell",
  "version_check",
 ]
 
 [[package]]
-name = "autocfg"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
-
-[[package]]
-name = "bitflags"
-version = "1.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
-
-[[package]]
-name = "bytemuck"
-version = "1.7.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "72957246c41db82b8ef88a5486143830adeb8227ef9837740bdec67724cf2c5b"
-
-[[package]]
-name = "byteorder"
-version = "1.4.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
-
-[[package]]
 name = "cfg-if"
 version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
 [[package]]
-name = "color_quant"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
-
-[[package]]
-name = "colors"
+name = "colorsquash"
 version = "0.1.0"
 dependencies = [
  "ahash",
- "image",
- "kmeans_colors",
- "libc",
- "rayon",
-]
-
-[[package]]
-name = "crc32fast"
-version = "1.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a"
-dependencies = [
- "cfg-if",
-]
-
-[[package]]
-name = "crossbeam-channel"
-version = "0.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4"
-dependencies = [
- "cfg-if",
- "crossbeam-utils",
-]
-
-[[package]]
-name = "crossbeam-deque"
-version = "0.8.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e"
-dependencies = [
- "cfg-if",
- "crossbeam-epoch",
- "crossbeam-utils",
-]
-
-[[package]]
-name = "crossbeam-epoch"
-version = "0.9.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd"
-dependencies = [
- "cfg-if",
- "crossbeam-utils",
- "lazy_static",
- "memoffset",
- "scopeguard",
-]
-
-[[package]]
-name = "crossbeam-utils"
-version = "0.8.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db"
-dependencies = [
- "cfg-if",
- "lazy_static",
 ]
 
 [[package]]
-name = "deflate"
-version = "0.8.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174"
-dependencies = [
- "adler32",
- "byteorder",
-]
-
-[[package]]
-name = "either"
-version = "1.6.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
-
-[[package]]
 name = "getrandom"
-version = "0.1.16"
+version = "0.2.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
+checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
 dependencies = [
  "cfg-if",
  "libc",
- "wasi 0.9.0+wasi-snapshot-preview1",
+ "wasi",
 ]
 
 [[package]]
-name = "getrandom"
-version = "0.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
-dependencies = [
- "cfg-if",
- "libc",
- "wasi 0.10.2+wasi-snapshot-preview1",
-]
-
-[[package]]
-name = "gif"
-version = "0.11.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a668f699973d0f573d15749b7002a9ac9e1f9c6b220e7b165601334c173d8de"
-dependencies = [
- "color_quant",
- "weezl",
-]
-
-[[package]]
-name = "hermit-abi"
-version = "0.1.19"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "image"
-version = "0.23.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "24ffcb7e7244a9bf19d35bf2883b9c080c4ced3c07a9895572178cdb8f13f6a1"
-dependencies = [
- "bytemuck",
- "byteorder",
- "color_quant",
- "gif",
- "jpeg-decoder",
- "num-iter",
- "num-rational",
- "num-traits",
- "png",
- "scoped_threadpool",
- "tiff",
-]
-
-[[package]]
-name = "jpeg-decoder"
-version = "0.1.22"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "229d53d58899083193af11e15917b5640cd40b29ff475a1fe4ef725deb02d0f2"
-dependencies = [
- "rayon",
-]
-
-[[package]]
-name = "kmeans_colors"
-version = "0.3.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85d1507a3f5aeb9a05fe973e13a41bd46ffbf4f168185eb39f0fcf3ed4cad67e"
-dependencies = [
- "rand",
- "rand_chacha",
-]
-
-[[package]]
-name = "lazy_static"
-version = "1.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
-
-[[package]]
 name = "libc"
-version = "0.2.103"
+version = "0.2.138"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6"
-
-[[package]]
-name = "memoffset"
-version = "0.6.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9"
-dependencies = [
- "autocfg",
-]
-
-[[package]]
-name = "miniz_oxide"
-version = "0.3.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435"
-dependencies = [
- "adler32",
-]
-
-[[package]]
-name = "miniz_oxide"
-version = "0.4.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
-dependencies = [
- "adler",
- "autocfg",
-]
-
-[[package]]
-name = "num-integer"
-version = "0.1.44"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
-dependencies = [
- "autocfg",
- "num-traits",
-]
-
-[[package]]
-name = "num-iter"
-version = "0.1.42"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59"
-dependencies = [
- "autocfg",
- "num-integer",
- "num-traits",
-]
-
-[[package]]
-name = "num-rational"
-version = "0.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07"
-dependencies = [
- "autocfg",
- "num-integer",
- "num-traits",
-]
-
-[[package]]
-name = "num-traits"
-version = "0.2.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
-dependencies = [
- "autocfg",
-]
-
-[[package]]
-name = "num_cpus"
-version = "1.13.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
-dependencies = [
- "hermit-abi",
- "libc",
-]
+checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
 
 [[package]]
 name = "once_cell"
-version = "1.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"
-
-[[package]]
-name = "png"
-version = "0.16.8"
+version = "1.16.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6"
-dependencies = [
- "bitflags",
- "crc32fast",
- "deflate",
- "miniz_oxide 0.3.7",
-]
-
-[[package]]
-name = "ppv-lite86"
-version = "0.2.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
-
-[[package]]
-name = "rand"
-version = "0.7.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
-dependencies = [
- "getrandom 0.1.16",
- "libc",
- "rand_chacha",
- "rand_core",
- "rand_hc",
-]
-
-[[package]]
-name = "rand_chacha"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
-dependencies = [
- "ppv-lite86",
- "rand_core",
-]
-
-[[package]]
-name = "rand_core"
-version = "0.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
-dependencies = [
- "getrandom 0.1.16",
-]
-
-[[package]]
-name = "rand_hc"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
-dependencies = [
- "rand_core",
-]
-
-[[package]]
-name = "rayon"
-version = "1.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90"
-dependencies = [
- "autocfg",
- "crossbeam-deque",
- "either",
- "rayon-core",
-]
-
-[[package]]
-name = "rayon-core"
-version = "1.9.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e"
-dependencies = [
- "crossbeam-channel",
- "crossbeam-deque",
- "crossbeam-utils",
- "lazy_static",
- "num_cpus",
-]
-
-[[package]]
-name = "scoped_threadpool"
-version = "0.1.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8"
-
-[[package]]
-name = "scopeguard"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
-
-[[package]]
-name = "tiff"
-version = "0.6.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a53f4706d65497df0c4349241deddf35f84cee19c87ed86ea8ca590f4464437"
-dependencies = [
- "jpeg-decoder",
- "miniz_oxide 0.4.4",
- "weezl",
-]
+checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
 
 [[package]]
 name = "version_check"
-version = "0.9.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
-
-[[package]]
-name = "wasi"
-version = "0.9.0+wasi-snapshot-preview1"
+version = "0.9.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
 
 [[package]]
 name = "wasi"
-version = "0.10.2+wasi-snapshot-preview1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
-
-[[package]]
-name = "weezl"
-version = "0.1.5"
+version = "0.11.0+wasi-snapshot-preview1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d8b77fdfd5a253be4ab714e4ffa3c49caf146b4de743e97510c0656cf90f1e8e"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
diff --git a/Cargo.toml b/Cargo.toml
index 9a2c9ad..e1a36ba 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,20 +1,20 @@
 [package]
-name = "colors"
+name = "colorsquash"
 version = "0.1.0"
-authors = ["Brad Alfirevic <brad@genbyte.dev>"]
+authors = ["Genny Z <gen@nyble.dev>"]
 edition = "2021"
 
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [dependencies]
-image = "0.23.14"
+#image = "0.23.14"
 ahash = "0.7.4"
-libc = "0.2.103"
-rayon = "*"
+#libc = "0.2.103"
+#rayon = "*"
 
-[dependencies.kmeans_colors]
-version = "0.3"
-default-features = false
+#[dependencies.kmeans_colors]
+#version = "0.3"
+#default-features = false
 
 [profile.release]
 debug = true
\ No newline at end of file
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..8efd6ad
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,233 @@
+use std::collections::HashMap;
+use std::ops::Deref;
+
+use ahash::RandomState;
+
+pub struct Squasher<T> {
+    palette: Vec<(Rgb, usize)>,
+    larget_count: usize,
+    map: Vec<T>,
+}
+
+impl<T: Count> Squasher<T> {
+    /// Creates a new squasher and allocates a new color map. A color map
+    /// contains every 24-bit color and ends up with an amount of memory
+    /// equal to `16MB * std::mem::size_of(T)`
+    pub fn new(max_colors: T, buffer: &[u8]) -> Self {
+        let sorted = Self::unique_and_sort(buffer);
+        let selected = Self::select_colors(&sorted, max_colors);
+
+        let mut this = Self {
+            palette: selected,
+            larget_count: sorted.first().unwrap().1,
+            map: vec![T::zero(); 256 * 256 * 256],
+        };
+
+        this.map_selected(&sorted);
+
+        this
+    }
+
+    /// Take an RGB image buffer and an output buffer. The function will fill
+    /// the output buffer with indexes into the Palette.
+    pub fn map_image(&mut self, image: &[u8], buffer: &mut [T]) {
+        let sorted = Self::unique_and_sort(image);
+        self.map_selected(&sorted);
+
+        for (idx, color) in image.chunks(3).enumerate() {
+            let index = self.map[color_index(&Rgb([color[0], color[1], color[2]]))];
+
+            buffer[idx] = index;
+        }
+    }
+
+    /// Retrieve the palette this squasher is working from
+    pub fn palette(&self) -> Vec<Rgb> {
+        self.palette.iter().map(|ahh| ahh.0).collect()
+    }
+
+    /// Retrieve the palette as bytes
+    pub fn palette_bytes(&self) -> Vec<u8> {
+        self.palette
+            .clone()
+            .into_iter()
+            .map(|rgb| rgb.0.into_iter())
+            .flatten()
+            .collect()
+    }
+
+    /// Takes an image buffer of RGB data and fill the color map
+    fn unique_and_sort(buffer: &[u8]) -> Vec<(Rgb, usize)> {
+        let mut colors: HashMap<Rgb, usize, RandomState> = HashMap::default();
+
+        //count pixels
+        for pixel in buffer.chunks(3) {
+            let rgb = Rgb([pixel[0], pixel[1], pixel[2]]);
+
+            match colors.get_mut(&rgb) {
+                None => {
+                    colors.insert(rgb, 1);
+                }
+                Some(n) => *n += 1,
+            }
+        }
+
+        let mut sorted: Vec<(Rgb, usize)> = colors.into_iter().collect();
+        sorted.sort_by(|(colour1, freq1), (colour2, freq2)| {
+            freq2
+                .cmp(freq1)
+                .then(colour2[0].cmp(&colour1[0]))
+                .then(colour2[1].cmp(&colour1[1]))
+                .then(colour2[2].cmp(&colour1[2]))
+        });
+
+        sorted
+    }
+
+    fn select_colors(sorted: &[(Rgb, usize)], max_colors: T) -> Vec<(Rgb, usize)> {
+        #[allow(non_snake_case)]
+        let RGB_TOLERANCE: f32 = 0.04 * 256.0;
+        let mut selected_colors: Vec<(Rgb, usize)> = Vec::with_capacity(max_colors.as_usize());
+
+        for (key, count) in sorted.iter() {
+            if max_colors.le(&selected_colors.len()) {
+                break;
+            } else if selected_colors
+                .iter()
+                .all(|color| rgb_difference(key, &color.0) > RGB_TOLERANCE)
+            {
+                selected_colors.push((*key, *count));
+            }
+        }
+
+        selected_colors
+    }
+
+    fn map_selected(&mut self, sorted: &[(Rgb, usize)]) {
+        for (sorted, _) in sorted {
+            let mut min_diff = f32::MAX;
+            let mut min_index = usize::MAX;
+
+            for (index, (selected, count)) in self.palette.iter().enumerate() {
+                //let count_weight = *count as f32 / self.larget_count as f32;
+                let diff = rgb_difference(sorted, selected); // - count_weight * 64.0;
+
+                // This is kind of racist genny
+                /*if selected[0] + selected[1] + selected[2] < 72 {
+                    continue;
+                }*/
+
+                //println!("{diff} - {selected:?}");
+
+                if diff.max(0.0) < min_diff {
+                    min_diff = diff;
+                    min_index = index;
+                }
+            }
+
+            self.map[color_index(sorted)] = T::from_usize(min_index);
+        }
+    }
+}
+
+pub trait Count: Copy + Clone {
+    fn zero() -> Self;
+    fn as_usize(&self) -> usize;
+    fn from_usize(from: usize) -> Self;
+    fn le(&self, rhs: &usize) -> bool;
+}
+
+macro_rules! count_impl {
+    ($kind:ty) => {
+        impl Count for $kind {
+            fn zero() -> Self {
+                0
+            }
+
+            fn as_usize(&self) -> usize {
+                *self as usize
+            }
+
+            #[inline(always)]
+            fn from_usize(from: usize) -> Self {
+                from as Self
+            }
+
+            #[inline(always)]
+            fn le(&self, rhs: &usize) -> bool {
+                *self as usize <= *rhs
+            }
+        }
+    };
+}
+
+count_impl!(u8);
+count_impl!(u16);
+count_impl!(u32);
+count_impl!(u64);
+count_impl!(usize);
+
+#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
+pub struct Rgb([u8; 3]);
+
+impl Deref for Rgb {
+    type Target = [u8; 3];
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+#[inline(always)]
+fn color_index(c: &Rgb) -> usize {
+    c.0[0] as usize * (256 * 256) + c.0[1] as usize * 256 + c.0[2] as usize
+}
+
+#[allow(clippy::many_single_char_names)]
+#[inline(always)]
+fn rgb_difference(a: &Rgb, b: &Rgb) -> f32 {
+    let absdiff = |a: u8, b: u8| (a as f32 - b as f32).abs();
+
+    /*let hsv1 = pixel_rgb_to_hsv(a);
+    let hsv2 = pixel_rgb_to_hsv(b);*/
+
+    //let diff_max = 3.0;
+
+    absdiff(a.0[0], b.0[0]) + absdiff(a.0[1], b.0[1]) + absdiff(a.0[2], b.0[2])
+    /*(((hsv1.0 / 90.0) - (hsv2.0 / 90.0)).abs()
+    + (hsv1.1 - hsv2.1).abs()
+    + ((hsv1.2 - hsv1.2).abs()))
+    / diff_max*/
+}
+
+fn pixel_rgb_to_hsv(a: &Rgb) -> (f32, f32, f32) {
+    let (r, g, b) = (
+        a.0[0] as f32 / 256.0,
+        a.0[1] as f32 / 256.0,
+        a.0[2] as f32 / 256.0,
+    );
+
+    let value = r.max(g.max(b));
+    let x_min = r.min(g.min(b));
+    let chroma = value - x_min;
+
+    let hue = if chroma == 0.0 {
+        0.0
+    } else if value == r {
+        60.0 * ((g - b) / chroma)
+    } else if value == g {
+        60.0 * (2.0 + (b - r) / chroma)
+    } else if value == b {
+        60.0 * (4.0 + (r - g) / chroma)
+    } else {
+        unreachable!()
+    };
+
+    let value_saturation = if value == 0.0 { 0.0 } else { chroma / value };
+
+    /* Rotate the color wheel counter clockwise to the negative location
+          |       Keep the wheel in place and remove any full rotations
+     _____V____ _____V____
+    |          |          |*/
+    ((hue + 360.0) % 360.0, value_saturation * 2.0, value * 2.0)
+}
diff --git a/src/main.rs b/src/main.rs
deleted file mode 100644
index f1915f8..0000000
--- a/src/main.rs
+++ /dev/null
@@ -1,175 +0,0 @@
-use std::time::Instant;
-use std::{collections::HashMap, env::args};
-
-use image::io::Reader as ImageReader;
-use image::Rgb;
-
-use ahash::RandomState;
-
-use rayon::prelude::*;
-
-const MAX_COLORS: usize = 16;
-
-const RGB_TOLERANCE: f32 = 0.25 + (1.0 - (MAX_COLORS as f32 / 256.0));
-
-fn main() {
-    let filename = args().nth(1).unwrap();
-    let outname = args().nth(2).unwrap();
-    // The percent of RGB value difference a color has to surpass to be considered unique
-
-    let imageread = ImageReader::open(&filename).expect("Failed to open image!");
-    let mut image = imageread
-        .decode()
-        .expect("Failed to decode image!")
-        .into_rgb8();
-
-    //let mem_before_sort = mallinfo().hblkhd as usize;
-    let start_sort = Instant::now();
-    let sorted_colors = unique_and_sort(image.pixels());
-    println!("Sort took {}s", start_sort.elapsed().as_secs_f32());
-
-    //let mem_before_selection = mallinfo().hblkhd as usize;
-    let start_selection = Instant::now();
-    let selected_colors = select_colors(&sorted_colors);
-    println!(
-        "Color Selection took {}s. Count {}",
-        start_selection.elapsed().as_secs_f32(),
-        selected_colors.len()
-    );
-
-    let start_array = Instant::now();
-    let mut array = vec![0usize; 256 * 256 * 256];
-    println!(
-        "Array creation took {}s",
-        start_array.elapsed().as_secs_f32()
-    );
-
-    let start_map = Instant::now();
-
-    for (sorted, _) in &sorted_colors {
-        let mut min_diff = f32::MAX;
-        let mut min_index = usize::MAX;
-
-        for (index, selected) in selected_colors.iter().enumerate() {
-            let diff = rgb_difference(sorted, selected);
-            if diff < min_diff {
-                min_diff = diff;
-                min_index = index;
-            }
-        }
-
-        array[color_index(sorted)] = min_index;
-    }
-
-    println!(
-        "Creating color map {:.2}s",
-        start_map.elapsed().as_secs_f32()
-    );
-
-    let start_fill = Instant::now();
-    // Max complexity is O(n * max_colors)
-    for color in image.pixels_mut() {
-        let index = array[color_index(color)];
-
-        *color = selected_colors[index];
-    }
-    println!(
-        "Took {:.2}s to fill in the image.\nTotal time from sort {:.2}s",
-        start_fill.elapsed().as_secs_f32(),
-        start_sort.elapsed().as_secs_f32()
-    );
-
-    image.save(outname).expect("Failed to write out");
-}
-
-fn unique_and_sort<'a, T>(pixels: T) -> Vec<(Rgb<u8>, usize)>
-where
-    T: Iterator<Item = &'a Rgb<u8>>,
-{
-    let mut colors: HashMap<Rgb<u8>, usize, RandomState> = HashMap::default();
-
-    //count pixels
-    for pixel in pixels {
-        match colors.get_mut(pixel) {
-            None => {
-                colors.insert(*pixel, 1);
-            }
-            Some(n) => *n += 1,
-        }
-    }
-
-    let mut sorted: Vec<(Rgb<u8>, usize)> = colors.into_par_iter().collect();
-    sorted.sort_by(|(colour1, freq1), (colour2, freq2)| {
-        freq2
-            .cmp(freq1)
-            .then(colour2[0].cmp(&colour1[0]))
-            .then(colour2[1].cmp(&colour1[1]))
-            .then(colour2[2].cmp(&colour1[2]))
-    });
-
-    sorted
-}
-
-fn select_colors(sorted: &[(Rgb<u8>, usize)]) -> Vec<Rgb<u8>> {
-    let mut selected_colors: Vec<Rgb<u8>> = Vec::with_capacity(MAX_COLORS);
-
-    for (key, _value) in sorted.iter() {
-        if selected_colors.len() >= MAX_COLORS {
-            break;
-        } else if selected_colors
-            .iter()
-            .all(|color| rgb_difference(key, color) > RGB_TOLERANCE)
-        {
-            selected_colors.push(*key);
-        }
-    }
-
-    selected_colors
-}
-
-#[inline(always)]
-fn color_index(c: &Rgb<u8>) -> usize {
-    c.0[0] as usize * (256 * 256) + c.0[1] as usize * 256 + c.0[2] as usize
-}
-
-#[allow(clippy::many_single_char_names)]
-#[inline(always)]
-fn rgb_difference(a: &Rgb<u8>, z: &Rgb<u8>) -> f32 {
-    let (a, b, c) = pixel_rgb_to_hsv(a);
-    let (d, e, f) = pixel_rgb_to_hsv(z);
-
-    (((c - f) * (c - f)) + ((a - d).abs() / 90.0) + (b - e).abs()) as f32
-}
-
-#[allow(clippy::float_cmp)]
-fn pixel_rgb_to_hsv(a: &Rgb<u8>) -> (f32, f32, f32) {
-    let (r, g, b) = (
-        a.0[0] as f32 / 256.0,
-        a.0[1] as f32 / 256.0,
-        a.0[2] as f32 / 256.0,
-    );
-
-    let value = r.max(g.max(b));
-    let x_min = r.min(g.min(b));
-    let chroma = value - x_min;
-
-    let hue = if chroma == 0.0 {
-        0.0
-    } else if value == r {
-        60.0 * ((g - b) / chroma)
-    } else if value == g {
-        60.0 * (2.0 + (b - r) / chroma)
-    } else if value == b {
-        60.0 * (4.0 + (r - g) / chroma)
-    } else {
-        unreachable!()
-    };
-
-    let value_saturation = if value == 0.0 { 0.0 } else { chroma / value };
-
-    /* Rotate the color wheel counter clockwise to the negative location
-          |       Keep the wheel in place and remove any full rotations
-     _____V____ _____V____
-    |          |          |*/
-    ((hue + 360.0) % 360.0, value_saturation * 2.0, value * 2.0)
-}