scaffolding for proper cli commands is in place, still need to write tests
Some checks are pending
build release package / build (push) Waiting to run
Some checks are pending
build release package / build (push) Waiting to run
This commit is contained in:
parent
d19b64e64a
commit
72672b731c
85
Cargo.lock
generated
85
Cargo.lock
generated
@ -2,12 +2,6 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "adler"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "adler2"
|
||||
version = "2.0.0"
|
||||
@ -65,9 +59,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "array-init"
|
||||
version = "2.0.1"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfb6d71005dc22a708c7496eee5c8dc0300ee47355de6256c3b35b12b5fef596"
|
||||
checksum = "3d62b7694a562cdf5a74227903507c56ab2cc8bdd1f781ed5cb4cf9c9f810bfc"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
@ -88,6 +82,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb3b4b9e5a7c7514dfa52869339ee98b3156b0bfb4e8a77c4ff4babb64b1604f"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -102,6 +97,18 @@ dependencies = [
|
||||
"strsim",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.7.3"
|
||||
@ -122,9 +129,9 @@ checksum = "9226dbc05df4fb986f48d730b001532580883c4c06c5d1c213f4b34c1c157178"
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.3.2"
|
||||
version = "1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
|
||||
checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
@ -140,14 +147,20 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.24"
|
||||
version = "1.0.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6"
|
||||
checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"miniz_oxide 0.5.4",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.1"
|
||||
@ -160,15 +173,6 @@ version = "0.4.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34"
|
||||
dependencies = [
|
||||
"adler",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.0"
|
||||
@ -189,7 +193,16 @@ dependencies = [
|
||||
"crc32fast",
|
||||
"fdeflate",
|
||||
"flate2",
|
||||
"miniz_oxide 0.8.0",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -203,6 +216,15 @@ dependencies = [
|
||||
"png",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simd-adler32"
|
||||
version = "0.3.7"
|
||||
@ -215,6 +237,23 @@ version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.87"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.2"
|
||||
|
@ -21,4 +21,4 @@ array-init = "2.0.1"
|
||||
png = "0.17.14"
|
||||
log = "0.4.22"
|
||||
colors-transform = "0.2.11"
|
||||
clap = "4.5.21"
|
||||
clap = { version = "4.5.18", features = ["derive"] }
|
||||
|
11
src/lib.rs
11
src/lib.rs
@ -204,6 +204,16 @@ pub mod qoi_lib {
|
||||
}
|
||||
|
||||
}
|
||||
pub fn pixels_to_bytes(&self) -> Vec<u8> {
|
||||
let mut buf: Vec<u8> = Vec::with_capacity(self.height as usize * self.width as usize * 4 as usize);
|
||||
for pixel in &self.pixels {
|
||||
buf.push(pixel.r);
|
||||
buf.push(pixel.g);
|
||||
buf.push(pixel.b);
|
||||
buf.push(pixel.a);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
@ -402,7 +412,6 @@ pub mod qoi_lib {
|
||||
(store % 64) as u8
|
||||
}
|
||||
|
||||
//Definitely broken in some way
|
||||
pub fn encode_from_image(img: Image) -> Vec<u8> {
|
||||
let mut prev_pixel: Pixel = Pixel {
|
||||
r: 0u8,
|
||||
|
182
src/main.rs
182
src/main.rs
@ -1,6 +1,6 @@
|
||||
use std::env;
|
||||
|
||||
use clap::{Parser, Subcommand};
|
||||
use std::fs::File;
|
||||
use std::io;
|
||||
use std::io::{BufReader, Read};
|
||||
use std::time::SystemTime;
|
||||
|
||||
@ -96,12 +96,10 @@ fn demo() {
|
||||
}
|
||||
|
||||
//Attempts to encode given png image as second argument into qoi
|
||||
fn encode(args: &Vec<String>) {
|
||||
//Path is fetched from arguments
|
||||
let path = &args[2];
|
||||
fn encode(in_path: &str, out_path: &str) {
|
||||
|
||||
//Init png decoder, attempt to decode png into bitmap, throw error if unsuccessful
|
||||
let decoder = png::Decoder::new(File::open(path).unwrap());
|
||||
let decoder = png::Decoder::new(File::open(in_path).unwrap());
|
||||
let mut reader = match decoder.read_info() {
|
||||
Ok(reader) => reader,
|
||||
Err(e) => panic!("ERROR: couldn't read file: {e:}"),
|
||||
@ -130,30 +128,13 @@ fn encode(args: &Vec<String>) {
|
||||
Err(err) => panic!("Problem generating image: {:?}", err),
|
||||
};
|
||||
|
||||
//encode generated bitmap
|
||||
if args.len() >= 4 {
|
||||
let filename: &String = &args[3];
|
||||
write_to_file(encode_from_image(img), filename).expect("ERROR: Can't write file.");
|
||||
} else {
|
||||
let mut filename = path.clone();
|
||||
for _i in 0..4 {
|
||||
filename.pop();
|
||||
}
|
||||
write_to_file(encode_from_image(img), filename.as_str()).expect("ERROR: Can't write file.");
|
||||
}
|
||||
write_to_file(encode_from_image(img), out_path).expect("ERROR: Can't write file.");
|
||||
println!("Encoding successful!");
|
||||
}
|
||||
|
||||
fn decode(args: &Vec<String>) -> io::Result<()> {
|
||||
let mut path: String = String::new();
|
||||
if args.len() > 2 {
|
||||
path.push_str(args[2].as_str());
|
||||
} else {
|
||||
println!("ERROR: incorrect number of arguments! (specify file to decode!).");
|
||||
()
|
||||
}
|
||||
|
||||
let f: File = match File::open(path.as_str()) {
|
||||
fn decode(path: &str) -> Result<Image, std::io::Error> {
|
||||
let f: File = match File::open(path) {
|
||||
Ok(f) => f,
|
||||
Err(e) => panic!("ERROR: {e:?}"),
|
||||
};
|
||||
@ -163,68 +144,125 @@ fn decode(args: &Vec<String>) -> io::Result<()> {
|
||||
reader.read_to_end(&mut bytes)?;
|
||||
|
||||
match qoi::qoi_lib::decode(bytes) {
|
||||
Ok(_img) => println!("Decoding successful!"),
|
||||
Ok(img) => {
|
||||
println!("Decoding successful!");
|
||||
return Ok(img);
|
||||
},
|
||||
Err(err) => panic!("ERROR: {err:?}"),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn bench(args: &Vec<String>) {
|
||||
if args.len() < 4 {
|
||||
panic!("ERROR: invalid number of arguments!");
|
||||
}
|
||||
fn bench(input: &str, output: Option<String>) {
|
||||
|
||||
let start = SystemTime::now();
|
||||
encode(args);
|
||||
let out_path = match output {
|
||||
Some(s) => s,
|
||||
None => input.strip_suffix(".png").unwrap_or(input).to_owned()
|
||||
};
|
||||
|
||||
encode(input, &out_path);
|
||||
|
||||
match start.elapsed() {
|
||||
Ok(elapsed) => println!("Encode took {} μs", elapsed.as_micros()),
|
||||
Ok(elapsed) => {
|
||||
if elapsed.as_millis() == 0 {
|
||||
println!("Encode took {:?} μs to complete", elapsed.as_micros());
|
||||
} else if elapsed.as_millis() > 999 {
|
||||
println!("Encode took {:.3} s to complete", elapsed.as_secs_f32());
|
||||
} else {
|
||||
println!("Encode took {:?} ms to complete", elapsed.as_millis());
|
||||
}
|
||||
},
|
||||
|
||||
Err(e) => panic!("ERROR: {e:?}"),
|
||||
}
|
||||
let mut new_arg: Vec<String> = Vec::new();
|
||||
new_arg.push(String::from(""));
|
||||
new_arg.push(String::from(""));
|
||||
let mut to_push: String = args[3].clone();
|
||||
to_push.push_str(".qoi");
|
||||
new_arg.push(to_push);
|
||||
let start = SystemTime::now();
|
||||
decode(&new_arg).expect(
|
||||
"ERROR: Unspecified error during io-pipeline. Ensure file path is valid and can be read.",
|
||||
);
|
||||
let mut out_path: String = out_path.to_owned();
|
||||
if !(out_path.contains(".qoi")) {
|
||||
out_path.push_str(".qoi");
|
||||
}
|
||||
match decode(&out_path) {
|
||||
Ok(img) => {
|
||||
|
||||
let out_buf = img.pixels_to_bytes();
|
||||
let _ = write_to_file(out_buf, out_path.strip_suffix(".qoi").unwrap()).expect("whoops!");
|
||||
},
|
||||
Err(e) => panic!("Error: {e:?}")
|
||||
}
|
||||
match start.elapsed() {
|
||||
Ok(elapsed) => println!("Decode took {} μs", elapsed.as_micros()),
|
||||
Ok(elapsed) => {
|
||||
if elapsed.as_millis() == 0 {
|
||||
println!("Encode took {:?} μs to complete", elapsed.as_micros());
|
||||
} else if elapsed.as_millis() > 999 {
|
||||
println!("Encode took {:.3} s to complete", elapsed.as_secs_f32());
|
||||
} else {
|
||||
println!("Encode took {:?} ms to complete", elapsed.as_millis());
|
||||
}
|
||||
},
|
||||
|
||||
Err(e) => panic!("ERROR: {e:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(name = "QOI Image Transcoder")]
|
||||
#[command(version, about, long_about = None)]
|
||||
#[command(next_line_help = true)]
|
||||
struct Cli {
|
||||
#[arg(short,long, action = clap::ArgAction::Count)]
|
||||
verbose: Option<u8>,
|
||||
|
||||
#[command(subcommand)]
|
||||
command: Commands
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
enum Commands {
|
||||
Encode {
|
||||
input: String,
|
||||
output: Option<String>
|
||||
},
|
||||
Decode {
|
||||
input: String,
|
||||
out_fmt: String,
|
||||
output: Option<String>
|
||||
},
|
||||
Bench {
|
||||
input: String,
|
||||
output: Option<String>
|
||||
},
|
||||
Demo {
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
//Initialize logger
|
||||
init().expect("Failed to initialize logger.");
|
||||
let cli: Cli = Cli::parse();
|
||||
|
||||
let args: Vec<String> = env::args().collect();
|
||||
|
||||
if args.len() == 1 {
|
||||
panic!("ERROR: no arguments send!");
|
||||
}
|
||||
|
||||
match args[1].as_str() {
|
||||
"demo" => {
|
||||
demo();
|
||||
}
|
||||
//can only handle pngs for now
|
||||
"encode" => {
|
||||
encode(&args);
|
||||
}
|
||||
"decode" => {
|
||||
decode(&args).expect("ERROR: Unspecified error during io-pipeline. Ensure file path is valid and can be read.");
|
||||
}
|
||||
"bench" => {
|
||||
bench(&args);
|
||||
}
|
||||
"help" => {
|
||||
println!("qoi supports the following commands: \n encode [IMAGE] (encodes given png-encoded into .qoi) \n decode [IMAGE] decodes given .qoi to .png \n bench [INPUT] [OUTPUT] encodes input .png into .qoi with encoding speed measured in microseconds.")
|
||||
}
|
||||
_ => {
|
||||
panic!("Invalid arguments!")
|
||||
}
|
||||
match &cli.command {
|
||||
Commands::Bench { input, output } => {
|
||||
bench(&input, output.clone());
|
||||
},
|
||||
Commands::Decode { input, out_fmt, output } => {
|
||||
if out_fmt != "png" {
|
||||
panic!("Unsupported output format!")
|
||||
} else {
|
||||
let img = match decode(&input) {
|
||||
Ok(i) => i,
|
||||
Err(e) => panic!("Error: {e:?}")
|
||||
};
|
||||
let out_path = match output {
|
||||
Some(s) => s,
|
||||
None => input
|
||||
};
|
||||
let _ = write_to_file(img.pixels_to_bytes(), &out_path).expect("Error writing file!");
|
||||
}
|
||||
},
|
||||
Commands::Encode { input, output } => {
|
||||
let out_path = match output {
|
||||
Some(s) => s,
|
||||
None => input
|
||||
};
|
||||
encode(&input, &out_path);
|
||||
},
|
||||
Commands::Demo { } => demo()
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user