Skip to content

Commit

Permalink
feat: implemented base cli pipeline
Browse files Browse the repository at this point in the history
  • Loading branch information
SalOne22 committed Mar 15, 2024
1 parent ee148f1 commit 18bcbe0
Show file tree
Hide file tree
Showing 17 changed files with 286 additions and 392 deletions.
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ required-features = ["build-binary"]
default = ["resize", "quantization", "mozjpeg", "oxipng", "webp", "avif", "threads"]

# Used for binary
build-binary = ["dep:anyhow", "dep:clap", "dep:indoc", "dep:rayon", "dep:pretty_env_logger", "zune-image/default"]
build-binary = ["dep:anyhow", "dep:clap", "dep:indoc", "dep:rayon", "dep:pretty_env_logger", "dep:zune-imageprocs", "zune-image/default"]

# Enables utilization of threads
threads = ["imagequant?/threads", "mozjpeg?/parallel", "oxipng?/parallel"]
Expand Down Expand Up @@ -65,6 +65,7 @@ clap = { version = "4.5.1", features = ["cargo", "string"], optional = true }
indoc = { version = "2.0.4", optional = true }
pretty_env_logger = { version = "0.5.0", optional = true }
rayon = { version = "1.8.1", optional = true }
zune-imageprocs = { version = "0.4.15", optional = true }

[dev-dependencies]
zune-core = { version = "0.4.12", features = ["std"] }
Expand Down
3 changes: 2 additions & 1 deletion src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ use indoc::indoc;
use self::{codecs::Codecs, preprocessors::Preprocessors, utils::threads};

pub mod codecs;
pub mod pipeline;
pub mod preprocessors;
pub mod utils;

pub fn cli() -> Command {
command!()
.arg_required_else_help(true)
.arg(
arg!([FILES] ... "Input file(s) to process")
arg!([files] ... "Input file(s) to process")
.long_help(indoc! {"Input file(s) to process
If the file path contains spaces, enclose the path with double quotation marks on both sides."})
Expand Down
5 changes: 5 additions & 0 deletions src/cli/codecs/farbfeld.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
use clap::Command;

pub fn farbfeld() -> Command {
Command::new("farbfeld").about("Encode images into Farbfeld format")
}
7 changes: 7 additions & 0 deletions src/cli/codecs/jpeg_xl.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
use clap::Command;

pub fn jpeg_xl() -> Command {
Command::new("jpeg_xl")
.about("Encode images into jpeg xl format")
.alias("jxl")
}
9 changes: 7 additions & 2 deletions src/cli/codecs/mod.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
use clap::Command;

use self::jpeg::jpeg;
use self::{farbfeld::farbfeld, jpeg::jpeg, jpeg_xl::jpeg_xl, png::png, ppm::ppm, qoi::qoi};

mod farbfeld;
mod jpeg;
mod jpeg_xl;
mod png;
mod ppm;
mod qoi;

impl Codecs for Command {
fn codecs(self) -> Self {
self.subcommands([jpeg()])
self.subcommands([farbfeld(), jpeg(), jpeg_xl(), png(), ppm(), qoi()])
}
}

Expand Down
5 changes: 5 additions & 0 deletions src/cli/codecs/png.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
use clap::Command;

pub fn png() -> Command {
Command::new("png").about("Encode images into PNG format")
}
5 changes: 5 additions & 0 deletions src/cli/codecs/ppm.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
use clap::Command;

pub fn ppm() -> Command {
Command::new("ppm").about("Encode images into PPM format")
}
5 changes: 5 additions & 0 deletions src/cli/codecs/qoi.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
use clap::Command;

pub fn qoi() -> Command {
Command::new("qoi").about("Encode images into QOI format")
}
134 changes: 134 additions & 0 deletions src/cli/pipeline.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
use std::{collections::BTreeMap, fs::read, path::Path};

use clap::ArgMatches;
use zune_core::bytestream::ZByteReader;
use zune_image::{
codecs::{
farbfeld::FarbFeldEncoder, jpeg::JpegEncoder, jpeg_xl::JxlEncoder, png::PngEncoder,
ppm::PPMEncoder, qoi::QoiEncoder, ImageFormat,
},
errors::ImageErrors,
image::Image,
traits::{DecoderTrait, EncoderTrait, OperationsTrait},
};

pub fn decode<P: AsRef<Path>>(f: P) -> Result<Image, ImageErrors> {
Image::open(f.as_ref()).or_else(|e| {
if matches!(e, ImageErrors::ImageDecoderNotIncluded(_)) {
let file_content = read("tests/files/avif/f1t.avif")?;

#[cfg(feature = "avif")]
if libavif::is_avif(&file_content) {
use rimage::codecs::avif::AvifDecoder;

let reader = ZByteReader::new(file_content);

let mut decoder = AvifDecoder::try_new(reader)?;

return <AvifDecoder<ZByteReader<Vec<u8>>> as DecoderTrait<Vec<u8>>>::decode(
&mut decoder,
);
};

#[cfg(feature = "webp")]
if f.as_ref()
.extension()
.is_some_and(|f| f.eq_ignore_ascii_case("webp"))
{
use rimage::codecs::webp::WebPDecoder;

let reader = ZByteReader::new(file_content);

let mut decoder = WebPDecoder::try_new(reader)?;

return <WebPDecoder<ZByteReader<Vec<u8>>> as DecoderTrait<Vec<u8>>>::decode(
&mut decoder,
);
};

Err(ImageErrors::ImageDecoderNotImplemented(
ImageFormat::Unknown,
))
} else {
Err(e)
}
})
}

pub fn operations(matches: &ArgMatches, img: &Image) -> BTreeMap<usize, Box<dyn OperationsTrait>> {
let mut map: BTreeMap<usize, Box<dyn OperationsTrait>> = BTreeMap::new();

#[cfg(feature = "resize")]
{
use crate::cli::preprocessors::{ResizeFilter, ResizeValue};
use fast_image_resize::ResizeAlg;
use rimage::operations::resize::Resize;

if let Some(values) = matches.get_many::<ResizeValue>("resize") {
let filter = matches.get_one::<ResizeFilter>("filter");

let (w, h) = img.dimensions();

values
.into_iter()
.zip(matches.indices_of("resize").unwrap())
.for_each(|(value, idx)| {
let (w, h) = value.map_dimensions(w, h);
log::trace!("setup resize {value} on index {idx}");

map.insert(
idx,
Box::new(Resize::new(
w,
h,
filter
.copied()
.map(Into::<ResizeAlg>::into)
.unwrap_or_default(),
)),
);
})
}
}

#[cfg(feature = "quantization")]
{
use rimage::operations::quantize::Quantize;

if let Some(values) = matches.get_many::<u8>("quantization") {
let dithering = matches.get_one::<u8>("dithering");

values
.into_iter()
.zip(matches.indices_of("quantization").unwrap())
.for_each(|(value, idx)| {
log::trace!("setup quantization {value} on index {idx}");

map.insert(
idx,
Box::new(Quantize::new(*value, dithering.map(|q| *q as f32 / 100.))),
);
})
}
}

map
}

pub fn encoder(matches: &ArgMatches) -> Result<(Box<dyn EncoderTrait>, &'static str), ImageErrors> {
match matches.subcommand() {
Some((name, matches)) => match name {
"farbfeld" => Ok((Box::new(FarbFeldEncoder::new()), "ff")),
"jpeg" => Ok((Box::new(JpegEncoder::new()), "jpg")),
"jpeg_xl" => Ok((Box::new(JxlEncoder::new()), "jxl")),
"png" => Ok((Box::new(PngEncoder::new()), "png")),
"ppm" => Ok((Box::new(PPMEncoder::new()), "ppm")),
"qoi" => Ok((Box::new(QoiEncoder::new()), "qoi")),

name => Err(ImageErrors::GenericString(format!(
"Encoder \"{name}\" not found",
))),
},
None => Err(ImageErrors::GenericStr("No encoder used")),
}
}
39 changes: 16 additions & 23 deletions src/cli/preprocessors/mod.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
use clap::{arg, value_parser, ArgAction, ArgGroup, Command};
use indoc::indoc;

use resize::ResizeValue;
#[cfg(feature = "resize")]
pub use resize::{ResizeFilter, ResizeValue};

use crate::cli::preprocessors::resize::{ResizeFilter, ResizeFit};

pub mod pipeline;
mod quantization;
#[cfg(feature = "resize")]
mod resize;

impl Preprocessors for Command {
Expand All @@ -17,49 +15,44 @@ impl Preprocessors for Command {
.multiple(true)
)
.next_help_heading("Preprocessors")
.arg(
.args([
#[cfg(feature = "resize")]
arg!(--resize <RESIZE> "Resize the image(s) according to the specified criteria")
.long_help(indoc! {"Resize the image(s) according to the specified criteria
Possible values:
- @1.5: Enlarge image size by this multiplier
- 150%: Adjust image size by this percentage
- 100x100: Resize image to these dimensions
- 200x_: Adjust image dimensions while maintaining the aspect ratio based on the specified dimension"})
.value_parser(value_parser!(ResizeValue))
.action(ArgAction::Append),
)
.arg(

#[cfg(feature = "resize")]
arg!(--filter <FILTER> "Filter that used when resizing an image")
.value_parser(value_parser!(ResizeFilter))
.default_value("lanczos3")
.requires("resize"),
)
.arg(
arg!(--fit <FIT> "Specifies how to fit image")
.value_parser(value_parser!(ResizeFit))
.default_value("stretch")
.requires("resize"),
)
.arg(

#[cfg(feature = "quantization")]
arg!(--quantization [QUALITY] "Enables quantization with optional quality")
.long_help(indoc! {"Enables quantization with optional quality
If quality is not provided default 75% quality is used"})
.value_parser(value_parser!(u8).range(1..=100))
.action(ArgAction::Append)
.default_missing_value("75")
)
.arg(
.default_missing_value("75"),

#[cfg(feature = "quantization")]
arg!(--dithering [QUALITY] "Enables dithering with optional quality")
.long_help(indoc! {"Enables dithering with optional quality
Used with --quantization flag.
If quality is not provided default 75% quality is used"})
.value_parser(value_parser!(u8).range(1..=100))
.default_missing_value("75")
.requires("quantization")
)
])
}
}

Expand Down
28 changes: 0 additions & 28 deletions src/cli/preprocessors/pipeline.rs

This file was deleted.

Loading

0 comments on commit 18bcbe0

Please sign in to comment.