diff options
author | gennyble <gen@nyble.dev> | 2024-01-12 12:08:19 -0600 |
---|---|---|
committer | gennyble <gen@nyble.dev> | 2024-01-12 12:08:19 -0600 |
commit | 0ec3c8d1c51ea2286e7762f3a3d1cbebd7700f50 (patch) | |
tree | b51a97b76457299c0647232893a99991a9d17a6d | |
parent | 093bb9d6889f6e026f14edc04e2f79bdea3c0b56 (diff) | |
download | colorsquash-0ec3c8d1c51ea2286e7762f3a3d1cbebd7700f50.tar.gz colorsquash-0ec3c8d1c51ea2286e7762f3a3d1cbebd7700f50.zip |
squash: fix tolerance and add algorithm selection
-rw-r--r-- | Cargo.lock | 45 | ||||
-rw-r--r-- | Cargo.toml | 1 | ||||
-rw-r--r-- | squash/Cargo.toml | 4 | ||||
-rw-r--r-- | squash/README.md | 3 | ||||
-rw-r--r-- | squash/src/cli.rs | 37 | ||||
-rw-r--r-- | squash/src/image.rs | 11 | ||||
-rw-r--r-- | squash/src/main.rs | 15 | ||||
-rw-r--r-- | src/lib.rs | 7 |
8 files changed, 109 insertions, 14 deletions
diff --git a/Cargo.lock b/Cargo.lock index fc918c1..4e9b559 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -27,6 +27,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" [[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] name = "bytemuck" version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -48,6 +60,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" name = "colorsquash" version = "0.1.0" dependencies = [ + "gifed", "rgb", ] @@ -80,9 +93,16 @@ dependencies = [ ] [[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] name = "gifed" -version = "0.2.0" +version = "0.3.0" dependencies = [ + "bitvec", "weezl", ] @@ -115,6 +135,12 @@ dependencies = [ ] [[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] name = "rgb" version = "0.8.36" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -131,7 +157,7 @@ checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" [[package]] name = "squash" -version = "0.1.0" +version = "0.2.0" dependencies = [ "anyhow", "camino", @@ -142,12 +168,27 @@ dependencies = [ ] [[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] name = "weezl" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" [[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] name = "zune-core" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/Cargo.toml b/Cargo.toml index a4f7347..c5bb6a7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ repository = "https://github.com/gennyble/colorsquash" [dependencies] rgb = "0.8.36" +gifed = { path = "../gifed/gifed", optional = true } [workspace] members = ["squash"] diff --git a/squash/Cargo.toml b/squash/Cargo.toml index 68fe712..760dcaf 100644 --- a/squash/Cargo.toml +++ b/squash/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "squash" -version = "0.1.0" +version = "0.2.0" authors = ["gennyble <gen@nyble.dev>"] edition = "2021" license = "ISC" @@ -11,7 +11,7 @@ repository = "https://github.com/gennyble/colorsquash/tree/main/squash" [dependencies] # the meat 'o the thing! the meaning behind it all -colorsquash = { path = "..", version = "0.1.0" } +colorsquash = { path = "..", version = "0.1.0", features = ["gifed"] } # just useful tools for writing binaries anyhow = "1.0.75" diff --git a/squash/README.md b/squash/README.md new file mode 100644 index 0000000..6dd691c --- /dev/null +++ b/squash/README.md @@ -0,0 +1,3 @@ +# squash +A command line color quantization program. Accepts most JPEG/PNG as input +and outputs indexed PNG/GIF \ No newline at end of file diff --git a/squash/src/cli.rs b/squash/src/cli.rs index 2e9251f..dec36fa 100644 --- a/squash/src/cli.rs +++ b/squash/src/cli.rs @@ -9,6 +9,7 @@ const AUTHORS: &str = env!("CARGO_PKG_AUTHORS"); pub struct Cli { pub color_count: u8, pub tolerance: Option<f32>, + pub difference: DifferenceFn, pub input: Utf8PathBuf, pub in_type: InType, pub output: Utf8PathBuf, @@ -21,6 +22,7 @@ pub struct Cli { struct BuildingCli { pub color_count: Option<u8>, pub tolerance: Option<f32>, + pub difference: DifferenceFn, } impl BuildingCli { @@ -59,6 +61,7 @@ impl BuildingCli { Cli { color_count: self.color_count.unwrap_or(Self::DEFAULT_COLORS), tolerance: self.tolerance, + difference: self.difference, input, in_type, output, @@ -77,6 +80,13 @@ pub enum OutType { Gif, } +#[derive(Debug, Default)] +pub enum DifferenceFn { + #[default] + Rgb, + Redmean, +} + pub fn build() -> Cli { let mut free = vec![]; let mut building = BuildingCli::default(); @@ -124,6 +134,15 @@ pub fn build() -> Cli { building.tolerance = Some(tol); } }, + Some(("difference", algo)) | Some(("dif", algo)) => match algo { + "rgb" => building.difference = DifferenceFn::Rgb, + "redmean" => building.difference = DifferenceFn::Redmean, + _ => { + eprintln!("'{algo}' is not recognized as an algorithm. See help=algorithms"); + std::process::exit(1); + } + }, + Some(("help", "algorithms")) => print_help_algorithms(), Some(("help", _)) => print_help(), Some(("version", _)) => print_version(), Some((key, _)) => { @@ -153,7 +172,11 @@ fn print_help() -> ! { println!("ARGUMENTS:"); println!(" colors=<int> | clrs=<int>"); println!(" the number of colours the final image should contain"); - println!(" a whole number more than 0 and less than, or equal, 256 [Default 256]\n"); + println!(" a whole number more than 0 and less than, or equal, 256"); + println!(" [Default 256]\n"); + println!(" difference=<algorithm> | did=<algorithm>"); + println!(" the color comparison function to use. one of: rgb, redmean"); + println!(" for more details use help=algorithms. [Default rgb]"); println!(" tolerance=<float> | tol=<float>"); println!(" how different colours should be to be added to the palette"); println!(" a number > 0 and <= 100\n"); @@ -164,6 +187,18 @@ fn print_help() -> ! { std::process::exit(0) } +fn print_help_algorithms() -> ! { + println!("ALGORITHMS"); + println!("rgb:"); + println!(" a straight, rather naïve, RGB comparison. It sums the channel"); + println!(" differences. This is it, really:"); + println!(" |a.red - b.red| + |a.green - b.green| + |a.blue - b.blue|\n"); + println!("redmean:"); + println!(" a slightly more intelligent algorithm that weighs the channels"); + println!(" in an attempt to more better align with human color perception."); + std::process::exit(0) +} + fn print_version() -> ! { println!("squash version {VERSION}"); println!("written by {AUTHORS}"); diff --git a/squash/src/image.rs b/squash/src/image.rs index 098e8a0..5c7b45a 100644 --- a/squash/src/image.rs +++ b/squash/src/image.rs @@ -3,7 +3,7 @@ use std::{fs::File, io::BufWriter}; use anyhow::{anyhow, bail}; use camino::{Utf8Path, Utf8PathBuf}; use colorsquash::Squasher; -use gifed::writer::{GifBuilder, ImageBuilder}; +use gifed::{writer::ImageBuilder, Gif}; use png::{ColorType, Decoder, Encoder}; use zune_jpeg::{zune_core::colorspace::ColorSpace, JpegDecoder}; @@ -94,11 +94,10 @@ pub fn save_gif( squasher: Squasher<u8>, path: Utf8PathBuf, ) -> Result<(), anyhow::Error> { - GifBuilder::new(image.width as u16, image.height as u16) - .palette(squasher.palette_bytes().as_slice().try_into().unwrap()) - .image(ImageBuilder::new(image.width as u16, image.height as u16).build(image.data)?) - .build()? - .save(path)?; + let mut gif = Gif::new(image.width as u16, image.height as u16); + gif.set_palette(Some(squasher.palette_gifed())); + gif.push(ImageBuilder::new(image.width as u16, image.height as u16).build(image.data)?); + gif.save(path)?; Ok(()) } diff --git a/squash/src/main.rs b/squash/src/main.rs index 8b77b47..5437ea1 100644 --- a/squash/src/main.rs +++ b/squash/src/main.rs @@ -1,4 +1,5 @@ -use colorsquash::Squasher; +use cli::DifferenceFn; +use colorsquash::{Squasher, SquasherBuilder}; use crate::cli::{InType, OutType}; @@ -7,6 +8,7 @@ mod image; fn main() -> Result<(), anyhow::Error> { //gen: I should use clap or at least getopt, but this is fine. + //gen: I like experimenting with the cli :) let cli = cli::build(); let mut image = match cli.in_type { @@ -14,12 +16,19 @@ fn main() -> Result<(), anyhow::Error> { InType::Jpeg => image::get_jpg(cli.input)?, }; - let mut squasher = Squasher::new(cli.color_count, &image.data); + let mut builder = SquasherBuilder::default().max_colors(cli.color_count); if let Some(tol) = cli.tolerance { - squasher.set_tolerance(tol); + builder = builder.tolerance(tol); } + builder = match cli.difference { + DifferenceFn::Rgb => builder.difference(&colorsquash::difference::rgb_difference), + DifferenceFn::Redmean => builder.difference(&colorsquash::difference::redmean_difference), + }; + + let mut squasher = builder.build(&image.data); + let size = squasher.map_over(&mut image.data); image.data.resize(size, 0); diff --git a/src/lib.rs b/src/lib.rs index 8d94c8e..213adfc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -144,6 +144,13 @@ impl<T: Count> Squasher<T> { } } + #[cfg(feature = "gifed")] + pub fn palette_gifed(&self) -> gifed::block::Palette { + use rgb::ComponentBytes; + + self.palette.as_slice().as_bytes().try_into().unwrap() + } + /// Retrieve the palette this squasher is working from pub fn palette(&self) -> &[RGB8] { &self.palette |