From e52599e25372827fd3cef1433773c5a1b181fd3e Mon Sep 17 00:00:00 2001 From: gennyble Date: Mon, 9 Oct 2023 01:27:19 -0500 Subject: squash --- .rustfmt.toml | 1 - Cargo.lock | 11 ++++-- README.md | 4 ++- squash/Cargo.toml | 7 +++- squash/src/.rustfmt.toml | 1 + squash/src/main.rs | 88 +++++++++++++++++++++++++++++++++++++++++++++++- src/lib.rs | 3 +- 7 files changed, 108 insertions(+), 7 deletions(-) delete mode 100644 .rustfmt.toml create mode 100644 squash/src/.rustfmt.toml diff --git a/.rustfmt.toml b/.rustfmt.toml deleted file mode 100644 index 218e203..0000000 --- a/.rustfmt.toml +++ /dev/null @@ -1 +0,0 @@ -hard_tabs = true diff --git a/Cargo.lock b/Cargo.lock index f80a769..d04e722 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + [[package]] name = "bitflags" version = "1.3.2" @@ -80,8 +86,7 @@ dependencies = [ [[package]] name = "png" version = "0.17.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd75bf2d8dd3702b9707cdbc56a5b9ef42cec752eb8b3bafc01234558442aa64" +source = "git+https://github.com/image-rs/image-png.git?rev=f10238a1e886b228e7da5301e5c0f5011316f2d6#f10238a1e886b228e7da5301e5c0f5011316f2d6" dependencies = [ "bitflags", "crc32fast", @@ -109,6 +114,8 @@ checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" name = "squash" version = "0.1.0" dependencies = [ + "anyhow", "camino", + "colorsquash", "png", ] diff --git a/README.md b/README.md index 0db61d0..da8832f 100644 --- a/README.md +++ b/README.md @@ -11,4 +11,6 @@ until it selects the necessary amount of colours. [^1] (wikipedia: color quantization)[https://en.wikipedia.org/wiki/Color_quantization] ### squash -A CLI tool to quantize colours. Accepts a path to a PNG; exports an indexed PNG. \ No newline at end of file +A CLI tool to quantize colours :D + +Currently only takes PNG in the RGB colorspace and outputs indexed PNG. \ No newline at end of file diff --git a/squash/Cargo.toml b/squash/Cargo.toml index 18f2be3..16c450a 100644 --- a/squash/Cargo.toml +++ b/squash/Cargo.toml @@ -6,5 +6,10 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +colorsquash = { path = "..", version = "0.1.0" } +anyhow = "1.0.75" camino = "1.1.6" -png = "0.17.10" +# time of writing: +# png has a change to ignore extra iCCP blocks my test image needed. it hasn't +# been released yet, so we're using the git here. the commit we require is e4b4811 +png = { git = "https://github.com/image-rs/image-png.git", rev = "f10238a1e886b228e7da5301e5c0f5011316f2d6" } diff --git a/squash/src/.rustfmt.toml b/squash/src/.rustfmt.toml new file mode 100644 index 0000000..218e203 --- /dev/null +++ b/squash/src/.rustfmt.toml @@ -0,0 +1 @@ +hard_tabs = true diff --git a/squash/src/main.rs b/squash/src/main.rs index f328e4d..b6eb699 100644 --- a/squash/src/main.rs +++ b/squash/src/main.rs @@ -1 +1,87 @@ -fn main() {} +use std::{fs::File, io::BufWriter}; + +use anyhow::bail; +use camino::{Utf8Path, Utf8PathBuf}; +use colorsquash::Squasher; +use png::{ColorType, Decoder, Encoder}; + +fn main() -> Result<(), anyhow::Error> { + // I should use clap or at least getopt, but this is fine. It's 20LOC. + let usage = || -> ! { + println!("usage: squash "); + std::process::exit(0); + }; + let mut argv = std::env::args().skip(1); + + let color_count: u8 = if let Some(Ok(count)) = argv.next().map(|r| r.parse()) { + count + } else { + usage() + }; + + let input_path: Utf8PathBuf = if let Some(path) = argv.next() { + path.into() + } else { + usage(); + }; + + let output_path: Utf8PathBuf = if let Some(path) = argv.next() { + path.into() + } else { + usage(); + }; + + let mut image = get_png(input_path)?; + + let squasher = Squasher::new(color_count, &image.data); + let size = squasher.map_over(&mut image.data); + image.data.resize(size, 0); + + // PNG Output + let file = File::create(output_path)?; + let bufw = BufWriter::new(file); + + let mut enc = Encoder::new(bufw, image.width as u32, image.height as u32); + enc.set_color(ColorType::Indexed); + enc.set_depth(png::BitDepth::Eight); + enc.set_palette(squasher.palette_bytes()); + enc.write_header()?.write_image_data(&image.data)?; + + Ok(()) +} + +fn get_png>(path: P) -> Result { + let decoder = Decoder::new(File::open(path.as_ref())?); + let mut reader = decoder.read_info()?; + + let mut buf = vec![0; reader.output_buffer_size()]; + let info = reader.next_frame(&mut buf)?; + let data = &buf[..info.buffer_size()]; + + println!( + "{}x{} * 3 = {} | out={}, bs={}", + info.width, + info.height, + info.width as usize * info.height as usize * 3, + buf.len(), + info.buffer_size() + ); + + let colors = info.color_type; + match colors { + ColorType::Grayscale | ColorType::GrayscaleAlpha | ColorType::Indexed | ColorType::Rgba => { + bail!("colortype {colors:?} not supported") + } + ColorType::Rgb => Ok(Image { + width: info.width as usize, + height: info.height as usize, + data: data.to_vec(), + }), + } +} + +struct Image { + width: usize, + height: usize, + data: Vec, +} diff --git a/src/lib.rs b/src/lib.rs index a404f38..0757a03 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -174,10 +174,11 @@ impl Squasher { /// # Returns /// The new size of the image pub fn map_over(&self, image: &mut [u8]) -> usize { - for idx in 0..image.len() { + for idx in 0..(image.len() / 3) { let rgb_idx = idx * 3; let color = RGB8::new(image[rgb_idx], image[rgb_idx + 1], image[rgb_idx + 2]); let color_index = self.map[color_index(&color)]; + image[idx] = color_index; } -- cgit 1.4.1-3-g733a5