-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
281 additions
and
3 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
use cli::cli; | ||
|
||
mod cli; | ||
mod preprocessors; | ||
mod utils; | ||
|
||
fn main() { | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
use clap::{arg, value_parser, ArgGroup, Command}; | ||
use indoc::indoc; | ||
|
||
use resize::ResizeValue; | ||
|
||
use crate::preprocessors::resize::ResizeFilter; | ||
|
||
mod resize; | ||
|
||
pub fn preprocessors(cmd: Command) -> Command { | ||
cmd.group(ArgGroup::new("preprocessors").multiple(true)) | ||
.next_help_heading("Preprocessors") | ||
.arg( | ||
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)), | ||
) | ||
.arg( | ||
arg!(--filter <FILTER> "Filter that used when resizing an image") | ||
.value_parser(value_parser!(ResizeFilter)) | ||
.default_value("lanczos3") | ||
.requires("resize"), | ||
) | ||
.arg( | ||
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)) | ||
.default_missing_value("75") | ||
) | ||
.arg( | ||
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") | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
use anyhow::anyhow; | ||
use clap::{builder::PossibleValue, ValueEnum}; | ||
|
||
#[derive(Debug, Clone, Copy, PartialEq)] | ||
pub enum ResizeFilter { | ||
Box, | ||
Bilinear, | ||
Hamming, | ||
CatmullRom, | ||
Mitchell, | ||
Lanczos3, | ||
} | ||
|
||
impl ValueEnum for ResizeFilter { | ||
fn value_variants<'a>() -> &'a [Self] { | ||
&[ | ||
ResizeFilter::Box, | ||
ResizeFilter::Bilinear, | ||
ResizeFilter::Hamming, | ||
ResizeFilter::CatmullRom, | ||
ResizeFilter::Mitchell, | ||
ResizeFilter::Lanczos3, | ||
] | ||
} | ||
|
||
fn to_possible_value(&self) -> Option<clap::builder::PossibleValue> { | ||
Some(match self { | ||
ResizeFilter::Box => PossibleValue::new("box").help("Each pixel contributes equally to destination. For upscaling, like Nearest."), | ||
ResizeFilter::Bilinear => PossibleValue::new("bilinear").help("Uses linear interpolation among contributing pixels for output"), | ||
ResizeFilter::Hamming => PossibleValue::new("hamming").help("Provides quality akin to bicubic for downscaling, sharper than Bilinear, but not optimal for upscaling"), | ||
ResizeFilter::CatmullRom => PossibleValue::new("catmull-rom").help("Employs cubic interpolation for output pixel calculation"), | ||
ResizeFilter::Mitchell => PossibleValue::new("mitchell").help("Utilizes cubic interpolation for output pixel calculation"), | ||
ResizeFilter::Lanczos3 => PossibleValue::new("lanczos3").help("Applies high-quality Lanczos filter for output pixel calculation"), | ||
}) | ||
} | ||
} | ||
|
||
impl std::fmt::Display for ResizeFilter { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
self.to_possible_value() | ||
.expect("no values are skipped") | ||
.get_name() | ||
.fmt(f) | ||
} | ||
} | ||
|
||
impl std::str::FromStr for ResizeFilter { | ||
type Err = anyhow::Error; | ||
|
||
fn from_str(s: &str) -> Result<Self, Self::Err> { | ||
for variant in Self::value_variants() { | ||
if variant.to_possible_value().unwrap().matches(s, false) { | ||
return Ok(*variant); | ||
} | ||
} | ||
Err(anyhow!("invalid variant: {s}")) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
mod filter; | ||
mod value; | ||
|
||
pub use filter::ResizeFilter; | ||
pub use value::ResizeValue; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
use anyhow::anyhow; | ||
|
||
#[derive(Debug, Clone, PartialEq)] | ||
pub enum ResizeValue { | ||
Multiplier(f32), | ||
Percentage(f32), | ||
Dimensions(Option<u32>, Option<u32>), | ||
} | ||
|
||
impl std::fmt::Display for ResizeValue { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
match self { | ||
ResizeValue::Multiplier(multiplier) => f.write_fmt(format_args!("@{multiplier}")), | ||
ResizeValue::Percentage(percentage) => f.write_fmt(format_args!("{percentage}%")), | ||
ResizeValue::Dimensions(Some(width), Some(height)) => { | ||
f.write_fmt(format_args!("{width}x{height}")) | ||
} | ||
ResizeValue::Dimensions(Some(width), None) => f.write_fmt(format_args!("{width}x_")), | ||
ResizeValue::Dimensions(None, Some(height)) => f.write_fmt(format_args!("_x{height}")), | ||
ResizeValue::Dimensions(None, None) => f.write_fmt(format_args!("base")), | ||
} | ||
} | ||
} | ||
|
||
impl std::str::FromStr for ResizeValue { | ||
type Err = anyhow::Error; | ||
|
||
fn from_str(s: &str) -> Result<Self, Self::Err> { | ||
match s { | ||
s if s.starts_with('@') => Ok(Self::Multiplier(s[1..].parse()?)), | ||
s if s.ends_with('%') => Ok(Self::Percentage(s[..s.len() - 1].parse()?)), | ||
s if s.contains('x') => { | ||
let dimensions: Vec<&str> = s.split('x').collect(); | ||
if dimensions.len() > 2 { | ||
return Err(anyhow!("There is more that 2 dimensions")); | ||
} | ||
|
||
let width = if dimensions[0] == "_" { | ||
None | ||
} else { | ||
Some(dimensions[0].parse::<u32>()?) | ||
}; | ||
|
||
let height = if dimensions[1] == "_" { | ||
None | ||
} else { | ||
Some(dimensions[1].parse::<u32>()?) | ||
}; | ||
|
||
Ok(Self::Dimensions(width, height)) | ||
} | ||
_ => Err(anyhow!("Invalid resize value")), | ||
} | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
|
||
#[test] | ||
fn display() { | ||
assert_eq!(ResizeValue::Multiplier(2.).to_string(), "@2"); | ||
assert_eq!(ResizeValue::Multiplier(1.5).to_string(), "@1.5"); | ||
|
||
assert_eq!(ResizeValue::Percentage(200.).to_string(), "200%"); | ||
assert_eq!(ResizeValue::Percentage(150.).to_string(), "150%"); | ||
|
||
assert_eq!( | ||
ResizeValue::Dimensions(Some(200), Some(200)).to_string(), | ||
"200x200" | ||
); | ||
assert_eq!( | ||
ResizeValue::Dimensions(Some(150), Some(150)).to_string(), | ||
"150x150" | ||
); | ||
|
||
assert_eq!( | ||
ResizeValue::Dimensions(None, Some(200)).to_string(), | ||
"_x200" | ||
); | ||
assert_eq!( | ||
ResizeValue::Dimensions(None, Some(150)).to_string(), | ||
"_x150" | ||
); | ||
|
||
assert_eq!( | ||
ResizeValue::Dimensions(Some(200), None).to_string(), | ||
"200x_" | ||
); | ||
assert_eq!( | ||
ResizeValue::Dimensions(Some(150), None).to_string(), | ||
"150x_" | ||
); | ||
|
||
assert_eq!(ResizeValue::Dimensions(None, None).to_string(), "base"); | ||
} | ||
|
||
#[test] | ||
fn from_str() { | ||
assert_eq!( | ||
"@2".parse::<ResizeValue>().unwrap(), | ||
ResizeValue::Multiplier(2.) | ||
); | ||
|
||
assert_eq!( | ||
"@1.5".parse::<ResizeValue>().unwrap(), | ||
ResizeValue::Multiplier(1.5) | ||
); | ||
|
||
assert_eq!( | ||
"200%".parse::<ResizeValue>().unwrap(), | ||
ResizeValue::Percentage(200.) | ||
); | ||
|
||
assert_eq!( | ||
"150%".parse::<ResizeValue>().unwrap(), | ||
ResizeValue::Percentage(150.) | ||
); | ||
|
||
assert_eq!( | ||
"200x200".parse::<ResizeValue>().unwrap(), | ||
ResizeValue::Dimensions(Some(200), Some(200)) | ||
); | ||
|
||
assert_eq!( | ||
"150x150".parse::<ResizeValue>().unwrap(), | ||
ResizeValue::Dimensions(Some(150), Some(150)) | ||
); | ||
|
||
assert_eq!( | ||
"200x_".parse::<ResizeValue>().unwrap(), | ||
ResizeValue::Dimensions(Some(200), None) | ||
); | ||
|
||
assert_eq!( | ||
"150x_".parse::<ResizeValue>().unwrap(), | ||
ResizeValue::Dimensions(Some(150), None) | ||
); | ||
|
||
assert_eq!( | ||
"_x200".parse::<ResizeValue>().unwrap(), | ||
ResizeValue::Dimensions(None, Some(200)) | ||
); | ||
|
||
assert_eq!( | ||
"_x150".parse::<ResizeValue>().unwrap(), | ||
ResizeValue::Dimensions(None, Some(150)) | ||
); | ||
|
||
assert_eq!( | ||
"_x_".parse::<ResizeValue>().unwrap(), | ||
ResizeValue::Dimensions(None, None) | ||
); | ||
} | ||
} |