From d1a55c98f44dee8f742e4b9a8fa392cd6b65ac51 Mon Sep 17 00:00:00 2001 From: Nihil Carcosa Date: Fri, 15 Nov 2024 22:59:48 +0100 Subject: [PATCH] Fixed input commands, png writing fixed. --- Cargo.lock | 2 +- Cargo.toml | 4 +-- src/lib.rs | 64 +++++++++++++++++++++++++++++----- src/main.rs | 98 ++++++++++++++++++++++++++++++++++------------------- 4 files changed, 122 insertions(+), 46 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2b297ab..3054d4c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -207,7 +207,7 @@ dependencies = [ [[package]] name = "qoi" -version = "0.9.0" +version = "0.9.9" dependencies = [ "array-init", "clap", diff --git a/Cargo.toml b/Cargo.toml index 4d44780..cac4ca5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "qoi" -version = "0.9.0" +version = "0.9.9" edition = "2021" authors = ["nihil carcosa "] description = "CLI tool and rust library for the de- and encoding of images to the QOI format." @@ -10,7 +10,7 @@ repository = "https://git.valhrafnaz.gay/valhrafnaz/qoi-img" license-file = "LICENSE" keywords = ["image_compression", "encoding", "qoi", "cli", "library"] categories = ["command-line-utilities", "encoding", "graphics", "compression"] -exlude = ["/qoi_test_images"] +exclude = ["/qoi_test_images"] publish = ["gitea"] diff --git a/src/lib.rs b/src/lib.rs index 47af193..fbfc186 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,9 @@ pub mod qoi_lib { use std::fmt; use std::fs::*; use std::io::prelude::*; + use std::io::BufWriter; + use std::path::Path; + use png; use array_init; @@ -40,7 +43,7 @@ pub mod qoi_lib { } //boilerplate implementation of the log crate - struct SimpleLogger; + pub struct SimpleLogger; impl log::Log for SimpleLogger { fn enabled(&self, metadata: &log::Metadata) -> bool { @@ -84,8 +87,8 @@ pub mod qoi_lib { /// # /// # } /// ``` - pub fn init() -> Result<(), SetLoggerError> { - log::set_logger(&LOGGER).map(|()| log::set_max_level(LevelFilter::Debug)) + pub fn init(level: LevelFilter) -> Result<(), SetLoggerError> { + log::set_logger(&LOGGER).map(|()| log::set_max_level(level)) } /// Custom image struct, which is used to store decoded data. Used by [encode_from_image] to encode the necessary data in bytes. Requires a Vector over [Pixel] values, `Vec`, @@ -204,7 +207,7 @@ pub mod qoi_lib { } } - pub fn pixels_to_bytes(&self) -> Vec { + pub fn to_bytes(&self) -> Vec { let mut buf: Vec = Vec::with_capacity(self.height as usize * self.width as usize * 4 as usize); for pixel in &self.pixels { buf.push(pixel.r); @@ -214,6 +217,39 @@ pub mod qoi_lib { } return buf; } + pub fn write_png(&self, path: &str) { + let mut file_path: String = String::new(); + file_path.push_str(path); + if !path.contains(".png") { + file_path.push_str(".png"); + } + let path = Path::new(&file_path); + let file = match File::create(path) { + Ok(f) => f, + Err(e) => panic!("ERROR during writing output file: {e:?}") + }; + let buf: Vec = self.to_bytes(); + let ref mut w = BufWriter::new(file); + let mut encoder = png::Encoder::new(w, self.width, self.height); + + encoder.set_color(png::ColorType::Rgba); + encoder.set_depth(png::BitDepth::Eight); + + encoder.set_source_gamma(png::ScaledFloat::new(1.0 / 2.2)); // 1.0 / 2.2, unscaled, but rounded + let source_chromaticities = png::SourceChromaticities::new( // Using unscaled instantiation here + (0.31270, 0.32900), + (0.64000, 0.33000), + (0.30000, 0.60000), + (0.15000, 0.06000) + ); + encoder.set_source_chromaticities(source_chromaticities); + let mut writer = encoder.write_header().unwrap(); + match writer.write_image_data(&buf) { + Ok(_a) => (), + Err(e) => panic!("Cannot write output file! {e:?}") + } + writer.finish().unwrap(); + } } #[derive(Clone, Copy, Debug, PartialEq)] @@ -577,10 +613,20 @@ pub mod qoi_lib { encoded_bytes } - + /// Writes Image as byte vector to file with name given as string slice. + /// ```rust + /// use qoi::qoi_lib::* + /// + /// let img = Image::new(); + /// let bytes: Vec = img.to_bytes(); + /// let name = "qoi-image"; + /// write_to_file(bytes, name); + /// ``` pub fn write_to_file(bytes: Vec, filename: &str) -> std::io::Result<()> { let mut file_path: String = String::from(filename); - file_path.push_str(".qoi"); + if !filename.contains(".qoi") { + file_path.push_str(".qoi"); + } let mut buffer = File::create(file_path)?; let mut pos = 0; @@ -806,11 +852,11 @@ pub mod qoi_lib { use super::*; use std::io; use std::io::{BufReader, Read}; - use std::path::*; #[test] fn diff_test() { - init().expect("Logger initialisation failed!"); + let level: LevelFilter = LevelFilter::Debug; + init(level).expect("Logger initialisation failed!"); let pix1: Pixel = Pixel::new(0, 0, 0, 255); let pix2: Pixel = Pixel::new(255, 255, 255, 255); @@ -899,7 +945,7 @@ pub mod qoi_lib { if !(file_path_str.contains(".png")) { continue; } - println!("{:}",file_path_str); + debug!("{:}",file_path_str); let file = match File::open(&file_path) { Ok(f) => f, Err(e) => panic!("Cannot read file! \n {e:?}") diff --git a/src/main.rs b/src/main.rs index 44ad324..ff627ea 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,5 @@ -use clap::{Parser, Subcommand}; +use clap::{Args,Parser, Subcommand}; use std::fs::File; use std::io::{BufReader, Read}; use std::time::SystemTime; @@ -7,6 +7,7 @@ use std::time::SystemTime; use colors_transform::{Color, Hsl, Rgb}; use png; use qoi::qoi_lib::*; +use log::{error,info}; fn encode_checkerboard() { let mut pixels: Vec = Vec::with_capacity(64 * 64); @@ -59,7 +60,7 @@ fn encode_debug() { 1 => img_data.push(rgb.get_green() as u8), 2 => img_data.push(rgb.get_blue() as u8), 3 => img_data.push(alpha as u8), - _ => panic!("unrecoverable for-loop failure"), + _ => error!("unrecoverable for-loop failure"), } } } @@ -73,11 +74,11 @@ fn encode_debug() { let stop = match start.elapsed() { Ok(elapsed) => elapsed.as_millis(), Err(e) => { - println!("Error: {e:?}"); + error!("Error: {e:?}"); return (); } }; - println!("Encode took: {} ms.", stop); + info!("Encode took: {} ms.", stop); write_to_file(img_bytes, "test").expect("Error writing file!"); } @@ -87,7 +88,7 @@ fn demo() { let stop = match start.elapsed() { Ok(elapsed) => elapsed.as_millis(), Err(e) => { - println!("Error: {e:?}"); + error!("Error: {e:?}"); return (); } }; @@ -129,7 +130,7 @@ fn encode(in_path: &str, out_path: &str) { }; write_to_file(encode_from_image(img), out_path).expect("ERROR: Can't write file."); - println!("Encoding successful!"); + info!("Encoding successful!"); } @@ -145,7 +146,7 @@ fn decode(path: &str) -> Result { match qoi::qoi_lib::decode(bytes) { Ok(img) => { - println!("Decoding successful!"); + info!("Decoding successful!"); return Ok(img); }, Err(err) => panic!("ERROR: {err:?}"), @@ -177,13 +178,13 @@ fn bench(input: &str, output: Option) { } let start = SystemTime::now(); let mut out_path: String = out_path.to_owned(); - if !(out_path.contains(".qoi")) { + if !out_path.contains(".qoi") { out_path.push_str(".qoi"); } match decode(&out_path) { Ok(img) => { - let out_buf = img.pixels_to_bytes(); + let out_buf = img.to_bytes(); let _ = write_to_file(out_buf, out_path.strip_suffix(".qoi").unwrap()).expect("whoops!"); }, Err(e) => panic!("Error: {e:?}") @@ -208,6 +209,7 @@ fn bench(input: &str, output: Option) { #[command(version, about, long_about = None)] #[command(next_line_help = true)] struct Cli { + /// Specify log verbosity, respects multiple applications. #[arg(short,long, action = clap::ArgAction::Count)] verbose: Option, @@ -217,51 +219,79 @@ struct Cli { #[derive(Subcommand)] enum Commands { - Encode { - input: String, - output: Option - }, - Decode { - input: String, - out_fmt: String, - output: Option - }, - Bench { - input: String, - output: Option - }, + /// Encode given [IMAGE] from { png } to qoi. + Encode(EncodeArgs), + /// Decode given qoi to specified [FORMAT]. + Decode(DecodeArgs), + /// Benchmark en- and decoder by passing in [IMAGE] and optionally specifying [OUTPUT] file. + Bench(BenchArgs), + /// Demo the application. Demo { } } +#[derive(Args)] +struct BenchArgs { + /// File to be encoded. + #[arg(short,long)] + input: String, + /// Optional output path. + #[arg(short,long)] + output: Option +} + +#[derive(Args)] +struct DecodeArgs { + /// Qoi file to be decoded + #[arg(short,long)] + input: String, + /// Format to transcode into + #[arg(short,long)] + format: String, + /// Optional file path + #[arg(short,long)] + output: Option +} + +#[derive(Args)] +struct EncodeArgs { + // File to be encoded + #[arg(short,long)] + input: String, + // Optional output path + #[arg(short,long)] + output: Option +} + fn main() { let cli: Cli = Cli::parse(); + match &cli.command { - Commands::Bench { input, output } => { - bench(&input, output.clone()); + Commands::Bench(args) => { + bench(&args.input, args.output.clone()); }, - Commands::Decode { input, out_fmt, output } => { - if out_fmt != "png" { + Commands::Decode(args)=> { + if args.format != "png" { panic!("Unsupported output format!") } else { - let img = match decode(&input) { + let img = match decode(&args.input) { Ok(i) => i, Err(e) => panic!("Error: {e:?}") }; - let out_path = match output { + let out_path = match &args.output { Some(s) => s, - None => input + None => &args.input }; - let _ = write_to_file(img.pixels_to_bytes(), &out_path).expect("Error writing file!"); + img.write_png(&out_path); } }, - Commands::Encode { input, output } => { - let out_path = match output { + Commands::Encode(args) => { + let out_path = match &args.output { Some(s) => s, - None => input + None => &args.input }; - encode(&input, &out_path); + encode(&args.input, &out_path); }, Commands::Demo { } => demo() }