Fixed input commands, png writing fixed.
Some checks failed
build release package / build (push) Has been cancelled
Some checks failed
build release package / build (push) Has been cancelled
This commit is contained in:
parent
72672b731c
commit
d1a55c98f4
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -207,7 +207,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "qoi"
|
name = "qoi"
|
||||||
version = "0.9.0"
|
version = "0.9.9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"array-init",
|
"array-init",
|
||||||
"clap",
|
"clap",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "qoi"
|
name = "qoi"
|
||||||
version = "0.9.0"
|
version = "0.9.9"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = ["nihil carcosa <nihil@valhrafnaz.gay>"]
|
authors = ["nihil carcosa <nihil@valhrafnaz.gay>"]
|
||||||
description = "CLI tool and rust library for the de- and encoding of images to the QOI format."
|
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"
|
license-file = "LICENSE"
|
||||||
keywords = ["image_compression", "encoding", "qoi", "cli", "library"]
|
keywords = ["image_compression", "encoding", "qoi", "cli", "library"]
|
||||||
categories = ["command-line-utilities", "encoding", "graphics", "compression"]
|
categories = ["command-line-utilities", "encoding", "graphics", "compression"]
|
||||||
exlude = ["/qoi_test_images"]
|
exclude = ["/qoi_test_images"]
|
||||||
publish = ["gitea"]
|
publish = ["gitea"]
|
||||||
|
|
||||||
|
|
||||||
|
64
src/lib.rs
64
src/lib.rs
@ -8,6 +8,9 @@ pub mod qoi_lib {
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::fs::*;
|
use std::fs::*;
|
||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
|
use std::io::BufWriter;
|
||||||
|
use std::path::Path;
|
||||||
|
use png;
|
||||||
|
|
||||||
|
|
||||||
use array_init;
|
use array_init;
|
||||||
@ -40,7 +43,7 @@ pub mod qoi_lib {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//boilerplate implementation of the log crate
|
//boilerplate implementation of the log crate
|
||||||
struct SimpleLogger;
|
pub struct SimpleLogger;
|
||||||
|
|
||||||
impl log::Log for SimpleLogger {
|
impl log::Log for SimpleLogger {
|
||||||
fn enabled(&self, metadata: &log::Metadata) -> bool {
|
fn enabled(&self, metadata: &log::Metadata) -> bool {
|
||||||
@ -84,8 +87,8 @@ pub mod qoi_lib {
|
|||||||
/// #
|
/// #
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn init() -> Result<(), SetLoggerError> {
|
pub fn init(level: LevelFilter) -> Result<(), SetLoggerError> {
|
||||||
log::set_logger(&LOGGER).map(|()| log::set_max_level(LevelFilter::Debug))
|
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<Pixel>`,
|
/// 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<Pixel>`,
|
||||||
@ -204,7 +207,7 @@ pub mod qoi_lib {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
pub fn pixels_to_bytes(&self) -> Vec<u8> {
|
pub fn to_bytes(&self) -> Vec<u8> {
|
||||||
let mut buf: Vec<u8> = Vec::with_capacity(self.height as usize * self.width as usize * 4 as usize);
|
let mut buf: Vec<u8> = Vec::with_capacity(self.height as usize * self.width as usize * 4 as usize);
|
||||||
for pixel in &self.pixels {
|
for pixel in &self.pixels {
|
||||||
buf.push(pixel.r);
|
buf.push(pixel.r);
|
||||||
@ -214,6 +217,39 @@ pub mod qoi_lib {
|
|||||||
}
|
}
|
||||||
return buf;
|
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<u8> = 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)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
@ -577,10 +613,20 @@ pub mod qoi_lib {
|
|||||||
|
|
||||||
encoded_bytes
|
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<u8> = img.to_bytes();
|
||||||
|
/// let name = "qoi-image";
|
||||||
|
/// write_to_file(bytes, name);
|
||||||
|
/// ```
|
||||||
pub fn write_to_file(bytes: Vec<u8>, filename: &str) -> std::io::Result<()> {
|
pub fn write_to_file(bytes: Vec<u8>, filename: &str) -> std::io::Result<()> {
|
||||||
let mut file_path: String = String::from(filename);
|
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 buffer = File::create(file_path)?;
|
||||||
let mut pos = 0;
|
let mut pos = 0;
|
||||||
@ -806,11 +852,11 @@ pub mod qoi_lib {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::io::{BufReader, Read};
|
use std::io::{BufReader, Read};
|
||||||
use std::path::*;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn diff_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 pix1: Pixel = Pixel::new(0, 0, 0, 255);
|
||||||
let pix2: Pixel = Pixel::new(255, 255, 255, 255);
|
let pix2: Pixel = Pixel::new(255, 255, 255, 255);
|
||||||
|
|
||||||
@ -899,7 +945,7 @@ pub mod qoi_lib {
|
|||||||
if !(file_path_str.contains(".png")) {
|
if !(file_path_str.contains(".png")) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
println!("{:}",file_path_str);
|
debug!("{:}",file_path_str);
|
||||||
let file = match File::open(&file_path) {
|
let file = match File::open(&file_path) {
|
||||||
Ok(f) => f,
|
Ok(f) => f,
|
||||||
Err(e) => panic!("Cannot read file! \n {e:?}")
|
Err(e) => panic!("Cannot read file! \n {e:?}")
|
||||||
|
98
src/main.rs
98
src/main.rs
@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Args,Parser, Subcommand};
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{BufReader, Read};
|
use std::io::{BufReader, Read};
|
||||||
use std::time::SystemTime;
|
use std::time::SystemTime;
|
||||||
@ -7,6 +7,7 @@ use std::time::SystemTime;
|
|||||||
use colors_transform::{Color, Hsl, Rgb};
|
use colors_transform::{Color, Hsl, Rgb};
|
||||||
use png;
|
use png;
|
||||||
use qoi::qoi_lib::*;
|
use qoi::qoi_lib::*;
|
||||||
|
use log::{error,info};
|
||||||
|
|
||||||
fn encode_checkerboard() {
|
fn encode_checkerboard() {
|
||||||
let mut pixels: Vec<Pixel> = Vec::with_capacity(64 * 64);
|
let mut pixels: Vec<Pixel> = Vec::with_capacity(64 * 64);
|
||||||
@ -59,7 +60,7 @@ fn encode_debug() {
|
|||||||
1 => img_data.push(rgb.get_green() as u8),
|
1 => img_data.push(rgb.get_green() as u8),
|
||||||
2 => img_data.push(rgb.get_blue() as u8),
|
2 => img_data.push(rgb.get_blue() as u8),
|
||||||
3 => img_data.push(alpha 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() {
|
let stop = match start.elapsed() {
|
||||||
Ok(elapsed) => elapsed.as_millis(),
|
Ok(elapsed) => elapsed.as_millis(),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("Error: {e:?}");
|
error!("Error: {e:?}");
|
||||||
return ();
|
return ();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
println!("Encode took: {} ms.", stop);
|
info!("Encode took: {} ms.", stop);
|
||||||
write_to_file(img_bytes, "test").expect("Error writing file!");
|
write_to_file(img_bytes, "test").expect("Error writing file!");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,7 +88,7 @@ fn demo() {
|
|||||||
let stop = match start.elapsed() {
|
let stop = match start.elapsed() {
|
||||||
Ok(elapsed) => elapsed.as_millis(),
|
Ok(elapsed) => elapsed.as_millis(),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("Error: {e:?}");
|
error!("Error: {e:?}");
|
||||||
return ();
|
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.");
|
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<Image, std::io::Error> {
|
|||||||
|
|
||||||
match qoi::qoi_lib::decode(bytes) {
|
match qoi::qoi_lib::decode(bytes) {
|
||||||
Ok(img) => {
|
Ok(img) => {
|
||||||
println!("Decoding successful!");
|
info!("Decoding successful!");
|
||||||
return Ok(img);
|
return Ok(img);
|
||||||
},
|
},
|
||||||
Err(err) => panic!("ERROR: {err:?}"),
|
Err(err) => panic!("ERROR: {err:?}"),
|
||||||
@ -177,13 +178,13 @@ fn bench(input: &str, output: Option<String>) {
|
|||||||
}
|
}
|
||||||
let start = SystemTime::now();
|
let start = SystemTime::now();
|
||||||
let mut out_path: String = out_path.to_owned();
|
let mut out_path: String = out_path.to_owned();
|
||||||
if !(out_path.contains(".qoi")) {
|
if !out_path.contains(".qoi") {
|
||||||
out_path.push_str(".qoi");
|
out_path.push_str(".qoi");
|
||||||
}
|
}
|
||||||
match decode(&out_path) {
|
match decode(&out_path) {
|
||||||
Ok(img) => {
|
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!");
|
let _ = write_to_file(out_buf, out_path.strip_suffix(".qoi").unwrap()).expect("whoops!");
|
||||||
},
|
},
|
||||||
Err(e) => panic!("Error: {e:?}")
|
Err(e) => panic!("Error: {e:?}")
|
||||||
@ -208,6 +209,7 @@ fn bench(input: &str, output: Option<String>) {
|
|||||||
#[command(version, about, long_about = None)]
|
#[command(version, about, long_about = None)]
|
||||||
#[command(next_line_help = true)]
|
#[command(next_line_help = true)]
|
||||||
struct Cli {
|
struct Cli {
|
||||||
|
/// Specify log verbosity, respects multiple applications.
|
||||||
#[arg(short,long, action = clap::ArgAction::Count)]
|
#[arg(short,long, action = clap::ArgAction::Count)]
|
||||||
verbose: Option<u8>,
|
verbose: Option<u8>,
|
||||||
|
|
||||||
@ -217,51 +219,79 @@ struct Cli {
|
|||||||
|
|
||||||
#[derive(Subcommand)]
|
#[derive(Subcommand)]
|
||||||
enum Commands {
|
enum Commands {
|
||||||
Encode {
|
/// Encode given [IMAGE] from { png } to qoi.
|
||||||
input: String,
|
Encode(EncodeArgs),
|
||||||
output: Option<String>
|
/// Decode given qoi to specified [FORMAT].
|
||||||
},
|
Decode(DecodeArgs),
|
||||||
Decode {
|
/// Benchmark en- and decoder by passing in [IMAGE] and optionally specifying [OUTPUT] file.
|
||||||
input: String,
|
Bench(BenchArgs),
|
||||||
out_fmt: String,
|
/// Demo the application.
|
||||||
output: Option<String>
|
|
||||||
},
|
|
||||||
Bench {
|
|
||||||
input: String,
|
|
||||||
output: Option<String>
|
|
||||||
},
|
|
||||||
Demo {
|
Demo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Args)]
|
||||||
|
struct BenchArgs {
|
||||||
|
/// File to be encoded.
|
||||||
|
#[arg(short,long)]
|
||||||
|
input: String,
|
||||||
|
/// Optional output path.
|
||||||
|
#[arg(short,long)]
|
||||||
|
output: Option<String>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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<String>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Args)]
|
||||||
|
struct EncodeArgs {
|
||||||
|
// File to be encoded
|
||||||
|
#[arg(short,long)]
|
||||||
|
input: String,
|
||||||
|
// Optional output path
|
||||||
|
#[arg(short,long)]
|
||||||
|
output: Option<String>
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let cli: Cli = Cli::parse();
|
let cli: Cli = Cli::parse();
|
||||||
|
|
||||||
|
|
||||||
match &cli.command {
|
match &cli.command {
|
||||||
Commands::Bench { input, output } => {
|
Commands::Bench(args) => {
|
||||||
bench(&input, output.clone());
|
bench(&args.input, args.output.clone());
|
||||||
},
|
},
|
||||||
Commands::Decode { input, out_fmt, output } => {
|
Commands::Decode(args)=> {
|
||||||
if out_fmt != "png" {
|
if args.format != "png" {
|
||||||
panic!("Unsupported output format!")
|
panic!("Unsupported output format!")
|
||||||
} else {
|
} else {
|
||||||
let img = match decode(&input) {
|
let img = match decode(&args.input) {
|
||||||
Ok(i) => i,
|
Ok(i) => i,
|
||||||
Err(e) => panic!("Error: {e:?}")
|
Err(e) => panic!("Error: {e:?}")
|
||||||
};
|
};
|
||||||
let out_path = match output {
|
let out_path = match &args.output {
|
||||||
Some(s) => s,
|
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 } => {
|
Commands::Encode(args) => {
|
||||||
let out_path = match output {
|
let out_path = match &args.output {
|
||||||
Some(s) => s,
|
Some(s) => s,
|
||||||
None => input
|
None => &args.input
|
||||||
};
|
};
|
||||||
encode(&input, &out_path);
|
encode(&args.input, &out_path);
|
||||||
},
|
},
|
||||||
Commands::Demo { } => demo()
|
Commands::Demo { } => demo()
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user