mirror of
https://github.com/waveplate/img2irc.git
synced 2024-12-22 13:46:37 +00:00
add new CLI options for crop, rotate, flip, filter, scale, and aspect ratio
This commit is contained in:
parent
492079af6c
commit
703000e68a
273
src/args.rs
273
src/args.rs
@ -1,173 +1,300 @@
|
|||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(clap::ValueEnum, Clone, Debug)]
|
||||||
|
pub enum SamplingFilter {
|
||||||
|
Nearest,
|
||||||
|
Triangle,
|
||||||
|
CatmullRom,
|
||||||
|
Gaussian,
|
||||||
|
Lanczos3,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(clap::ValueEnum, Clone, Debug)]
|
||||||
|
pub enum ColourSpace {
|
||||||
|
HSL,
|
||||||
|
HSV,
|
||||||
|
HSLUV,
|
||||||
|
LCH,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Parser, Clone, Debug)]
|
||||||
#[command(author, version, about, long_about = None)]
|
#[command(author, version, about, long_about = None)]
|
||||||
pub struct Args {
|
pub struct Args {
|
||||||
/// image url or file path
|
/// image url or file path
|
||||||
#[arg(index = 1)]
|
#[arg(index = 1)]
|
||||||
pub image: String,
|
pub image: String,
|
||||||
|
|
||||||
/// irc
|
/// output image width in columns
|
||||||
|
#[arg(short, long)]
|
||||||
|
pub width: Option<u32>,
|
||||||
|
|
||||||
|
/// output image height in rows
|
||||||
|
#[arg(short, long)]
|
||||||
|
pub height: Option<u32>,
|
||||||
|
|
||||||
|
/// scaling factors (x:y, e.g., "2:2")
|
||||||
|
#[arg(long, value_parser = parse_xy_pair)]
|
||||||
|
pub scale: Option<(f32, f32)>,
|
||||||
|
|
||||||
|
/// final aspect ratio (x:y, e.g., "2:1")
|
||||||
|
#[arg(long, value_parser = parse_xy_pair)]
|
||||||
|
pub aspect: Option<(f32, f32)>,
|
||||||
|
|
||||||
|
/// crop image (x1,y1,x2,y2)
|
||||||
|
#[arg(long, value_parser = parse_crop_coordinates)]
|
||||||
|
pub crop: Option<(u32, u32, u32, u32)>,
|
||||||
|
|
||||||
|
/// sampling filter
|
||||||
|
#[arg(long, value_enum, default_value_t = SamplingFilter::Nearest)]
|
||||||
|
pub filter: SamplingFilter,
|
||||||
|
|
||||||
|
/// rotate degrees
|
||||||
|
#[arg(long, default_value_t = 0)]
|
||||||
|
pub rotate: i32,
|
||||||
|
|
||||||
|
/// flip horizontal
|
||||||
#[arg(long, default_value_t = false)]
|
#[arg(long, default_value_t = false)]
|
||||||
|
pub fliph: bool,
|
||||||
|
|
||||||
|
/// flip vertical
|
||||||
|
#[arg(long, default_value_t = false)]
|
||||||
|
pub flipv: bool,
|
||||||
|
|
||||||
|
/// use IRC99 colours
|
||||||
|
#[arg(long, default_value_t = false, group = "colour", required_unless_present_any = ["ansi", "ansi24"])]
|
||||||
pub irc: bool,
|
pub irc: bool,
|
||||||
|
|
||||||
/// 8-bit ansi
|
/// use 8-bit ANSI colours
|
||||||
#[arg(long, default_value_t = false)]
|
#[arg(long, default_value_t = false, group = "colour", required_unless_present_any = ["irc", "ansi24"])]
|
||||||
pub ansi: bool,
|
pub ansi: bool,
|
||||||
|
|
||||||
/// 24-bit ansi
|
/// use 24-bit ANSI colours
|
||||||
#[arg(long, default_value_t = false)]
|
#[arg(long, default_value_t = false, group = "colour", required_unless_present_any = ["irc", "ansi"])]
|
||||||
pub ansi24: bool,
|
pub ansi24: bool,
|
||||||
|
|
||||||
/// quarterblock
|
/// use braille pixels
|
||||||
#[arg(long, default_value_t = false)]
|
#[arg(long, default_value_t = false, group = "pixel")]
|
||||||
|
pub braille: bool,
|
||||||
|
|
||||||
|
/// use halfblock pixels
|
||||||
|
#[arg(long, alias = "halfblock", default_value_t = true, group = "pixel")]
|
||||||
|
pub hb: bool,
|
||||||
|
|
||||||
|
/// use quarterblocks pixels
|
||||||
|
#[arg(long, alias = "quarterblock", default_value_t = false, group = "pixel")]
|
||||||
pub qb: bool,
|
pub qb: bool,
|
||||||
|
|
||||||
/// image width to resize to
|
/// adjust brightness (0 = no change)
|
||||||
#[arg(short, long, default_value_t = 50)]
|
#[arg(short = 'b', long, default_value_t = 0.0, allow_hyphen_values = true)]
|
||||||
pub width: u32,
|
|
||||||
|
|
||||||
/// brightness (-255 to 255)
|
|
||||||
#[arg(short, long, require_equals = true, default_value_t = 0.0)]
|
|
||||||
pub brightness: f32,
|
pub brightness: f32,
|
||||||
|
|
||||||
/// contrast (-255 to 255)
|
/// adjust contrast (0 = no change)
|
||||||
#[arg(short, long, require_equals = true, default_value_t = 0.0)]
|
#[arg(short = 'c', long, default_value_t = 0.0, allow_hyphen_values = true)]
|
||||||
pub contrast: f32,
|
pub contrast: f32,
|
||||||
|
|
||||||
/// saturation (-255 to 255)
|
/// adjust gamma (0 to 255)
|
||||||
#[arg(short, long, require_equals = true, default_value_t = 0.0)]
|
#[arg(short = 'g', long, default_value_t = 0.0, allow_hyphen_values = true)]
|
||||||
pub saturation: f32,
|
|
||||||
|
|
||||||
/// hue (0 to 360)
|
|
||||||
#[arg(short = 'H', long, default_value_t = 0.0)]
|
|
||||||
pub hue: f32,
|
|
||||||
|
|
||||||
/// gamma (0 to 255)
|
|
||||||
#[arg(short, long, default_value_t = 0.0)]
|
|
||||||
pub gamma: f32,
|
pub gamma: f32,
|
||||||
|
|
||||||
/// dither (1 to 8)
|
/// adjust saturation (0 = no change)
|
||||||
#[arg(long, default_value_t = 0)]
|
#[arg(short = 's', long, default_value_t = 0.0, allow_hyphen_values = true)]
|
||||||
|
pub saturation: f32,
|
||||||
|
|
||||||
|
/// rotate hue (0 to 360)
|
||||||
|
#[arg(short = 'u', long, default_value_t = 0.0)]
|
||||||
|
pub hue: f32,
|
||||||
|
|
||||||
|
/// colors are inverted, opposite on the color wheel
|
||||||
|
#[arg(short = 'i', long, default_value_t = false)]
|
||||||
|
pub invert: bool,
|
||||||
|
|
||||||
|
/// dithering (1 to 8)
|
||||||
|
#[arg(long, short = 'd', long, default_value_t = 0)]
|
||||||
pub dither: u32,
|
pub dither: u32,
|
||||||
|
|
||||||
/// pixelize size
|
/// adjust luma brightness (braille only)
|
||||||
|
#[arg(short = 'B', long, default_value_t = 0.0, allow_hyphen_values = true, requires = "braille")]
|
||||||
|
pub luma_brightness: f32,
|
||||||
|
|
||||||
|
/// adjust luma contrast (braille only)
|
||||||
|
#[arg(short = 'C', long, default_value_t = 0.0, allow_hyphen_values = true, requires = "braille")]
|
||||||
|
pub luma_contrast: f32,
|
||||||
|
|
||||||
|
/// adjust luma gamma (braille only)
|
||||||
|
#[arg(short = 'G', long, default_value_t = 0.0, allow_hyphen_values = true, requires = "braille")]
|
||||||
|
pub luma_gamma: f32,
|
||||||
|
|
||||||
|
/// adjust luma saturation (braille only)
|
||||||
|
#[arg(short = 'S', long, default_value_t = 0.0, allow_hyphen_values = true, requires = "braille")]
|
||||||
|
pub luma_saturation: f32,
|
||||||
|
|
||||||
|
/// luminance is inverted
|
||||||
|
#[arg(short = 'I', long, default_value_t = false, requires = "braille")]
|
||||||
|
pub luma_invert: bool,
|
||||||
|
|
||||||
|
/// colour space
|
||||||
|
#[arg(long, value_enum, default_value_t = ColourSpace::HSV)]
|
||||||
|
pub colorspace: ColourSpace,
|
||||||
|
|
||||||
|
/// converts image to black and white
|
||||||
|
#[arg(long, default_value_t = false, group = "grayscale_opts")]
|
||||||
|
pub grayscale: bool,
|
||||||
|
|
||||||
|
/// exclude grayscale colours from the palette
|
||||||
|
#[arg(long, default_value_t = false, group = "grayscale_opts")]
|
||||||
|
pub nograyscale: bool,
|
||||||
|
|
||||||
|
/// pixelize pixel size
|
||||||
#[arg(long, default_value_t = 0)]
|
#[arg(long, default_value_t = 0)]
|
||||||
pub pixelize: i32,
|
pub pixelize: i32,
|
||||||
|
|
||||||
|
/// simple average of all the neighboring pixels surrounding a given pixel
|
||||||
|
#[arg(long="boxblur", default_value_t = false)]
|
||||||
|
pub box_blur: bool,
|
||||||
|
|
||||||
/// gaussian blur radius
|
/// gaussian blur radius
|
||||||
#[arg(long, default_value_t = 0)]
|
#[arg(long="gaussianblur", default_value_t = 0)]
|
||||||
pub gaussian_blur: i32,
|
pub gaussian_blur: i32,
|
||||||
|
|
||||||
/// oil ("<radius>,<intensity>")
|
/// oil filter ("[radius],[intensity]")
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
pub oil: Option<String>,
|
pub oil: Option<String>,
|
||||||
|
|
||||||
/// grayscale
|
/// made up of small dots creating a continuous-tone illusion
|
||||||
#[arg(long, default_value_t = false)]
|
|
||||||
pub grayscale: bool,
|
|
||||||
|
|
||||||
/// no grayscale
|
|
||||||
#[arg(long, default_value_t = false)]
|
|
||||||
pub nograyscale: bool,
|
|
||||||
|
|
||||||
/// halftone
|
|
||||||
#[arg(long, default_value_t = false)]
|
#[arg(long, default_value_t = false)]
|
||||||
pub halftone: bool,
|
pub halftone: bool,
|
||||||
|
|
||||||
/// sepia
|
/// brownish, aged appearance like old photographs
|
||||||
#[arg(long, default_value_t = false)]
|
#[arg(long, default_value_t = false)]
|
||||||
pub sepia: bool,
|
pub sepia: bool,
|
||||||
|
|
||||||
/// normalize
|
/// adjusts brightness and contrast for better image quality
|
||||||
#[arg(long, default_value_t = false)]
|
#[arg(long, default_value_t = false)]
|
||||||
pub normalize: bool,
|
pub normalize: bool,
|
||||||
|
|
||||||
/// noise
|
/// random variations in brightness and color like film grain
|
||||||
#[arg(long, default_value_t = false)]
|
#[arg(long, default_value_t = false)]
|
||||||
pub noise: bool,
|
pub noise: bool,
|
||||||
|
|
||||||
/// emboss
|
/// gives a raised, 3d appearance
|
||||||
#[arg(long, default_value_t = false)]
|
#[arg(long, default_value_t = false)]
|
||||||
pub emboss: bool,
|
pub emboss: bool,
|
||||||
|
|
||||||
/// box_blur
|
/// no modifications, unchanged image
|
||||||
#[arg(long, default_value_t = false)]
|
|
||||||
pub box_blur: bool,
|
|
||||||
|
|
||||||
/// identity
|
|
||||||
#[arg(long, default_value_t = false)]
|
#[arg(long, default_value_t = false)]
|
||||||
pub identity: bool,
|
pub identity: bool,
|
||||||
|
|
||||||
/// laplace
|
/// enhances edges and boundaries in an image
|
||||||
#[arg(long, default_value_t = false)]
|
#[arg(long, default_value_t = false)]
|
||||||
pub laplace: bool,
|
pub laplace: bool,
|
||||||
|
|
||||||
/// noise reduction
|
/// reduces noise for a cleaner, clearer image
|
||||||
#[arg(long, default_value_t = false)]
|
#[arg(long="denoise", default_value_t = false)]
|
||||||
pub noise_reduction: bool,
|
pub noise_reduction: bool,
|
||||||
|
|
||||||
/// sharpen
|
/// increases clarity and definition, making edges and details more distinct
|
||||||
#[arg(long, default_value_t = false)]
|
#[arg(long, default_value_t = false)]
|
||||||
pub sharpen: bool,
|
pub sharpen: bool,
|
||||||
|
|
||||||
/// cali
|
/// cool blue tone with increased contrast
|
||||||
#[arg(long, default_value_t = false)]
|
#[arg(long, default_value_t = false)]
|
||||||
pub cali: bool,
|
pub cali: bool,
|
||||||
|
|
||||||
/// dramatic
|
/// high contrast and vivid colors for a dramatic effect
|
||||||
#[arg(long, default_value_t = false)]
|
#[arg(long, default_value_t = false)]
|
||||||
pub dramatic: bool,
|
pub dramatic: bool,
|
||||||
|
|
||||||
/// firenze
|
/// warm, earthy tones reminiscent of tuscan landscapes
|
||||||
#[arg(long, default_value_t = false)]
|
#[arg(long, default_value_t = false)]
|
||||||
pub firenze: bool,
|
pub firenze: bool,
|
||||||
|
|
||||||
/// golden
|
/// warm, golden glow like sunset light
|
||||||
#[arg(long, default_value_t = false)]
|
#[arg(long, default_value_t = false)]
|
||||||
pub golden: bool,
|
pub golden: bool,
|
||||||
|
|
||||||
/// lix
|
/// high-contrast black and white appearance with increased sharpness
|
||||||
#[arg(long, default_value_t = false)]
|
#[arg(long, default_value_t = false)]
|
||||||
pub lix: bool,
|
pub lix: bool,
|
||||||
|
|
||||||
/// lofi
|
/// low-fidelity, retro appearance like old photographs or film
|
||||||
#[arg(long, default_value_t = false)]
|
#[arg(long, default_value_t = false)]
|
||||||
pub lofi: bool,
|
pub lofi: bool,
|
||||||
|
|
||||||
/// neue
|
/// clean, modern appearance with neutral colors and simple design
|
||||||
#[arg(long, default_value_t = false)]
|
#[arg(long, default_value_t = false)]
|
||||||
pub neue: bool,
|
pub neue: bool,
|
||||||
|
|
||||||
/// obsidian
|
/// dark, monochromatic appearance with black and gray shades
|
||||||
#[arg(long, default_value_t = false)]
|
#[arg(long, default_value_t = false)]
|
||||||
pub obsidian: bool,
|
pub obsidian: bool,
|
||||||
|
|
||||||
/// pastel_pink
|
/// soft, delicate pink tint like pastel colors
|
||||||
#[arg(long, default_value_t = false)]
|
#[arg(long="pastelpink", default_value_t = false)]
|
||||||
pub pastel_pink: bool,
|
pub pastel_pink: bool,
|
||||||
|
|
||||||
/// ryo
|
/// bright, high-contrast appearance with vivid colors and sharp details
|
||||||
#[arg(long, default_value_t = false)]
|
#[arg(long, default_value_t = false)]
|
||||||
pub ryo: bool,
|
pub ryo: bool,
|
||||||
|
|
||||||
/// invert
|
/// blurred, frosted appearance as if viewed through semi-transparent surface
|
||||||
#[arg(long, default_value_t = false)]
|
#[arg(long="frostedglass", default_value_t = false)]
|
||||||
pub invert: bool,
|
|
||||||
|
|
||||||
/// frosted glass
|
|
||||||
#[arg(long, default_value_t = false)]
|
|
||||||
pub frosted_glass: bool,
|
pub frosted_glass: bool,
|
||||||
|
|
||||||
/// solarize
|
/// strange, otherworldly appearance with inverted colors and surreal atmosphere
|
||||||
#[arg(long, default_value_t = false)]
|
#[arg(long, default_value_t = false)]
|
||||||
pub solarize: bool,
|
pub solarize: bool,
|
||||||
|
|
||||||
/// edge detection
|
/// highlights edges and boundaries in an image
|
||||||
#[arg(long, default_value_t = false)]
|
#[arg(long="edgedetection", default_value_t = false)]
|
||||||
pub edge_detection: bool,
|
pub edge_detection: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_args() -> Args {
|
pub fn parse_args() -> Args {
|
||||||
Args::parse()
|
Args::parse()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_xy_pair(s: &str) -> Result<(f32, f32), String> {
|
||||||
|
let parts: Vec<&str> = s.split(':').collect();
|
||||||
|
if parts.len() != 2 {
|
||||||
|
return Err(format!(
|
||||||
|
"Invalid format. Expected 'value1:value2', got '{}'",
|
||||||
|
s
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let first = parts[0].parse::<f32>().map_err(|e| {
|
||||||
|
format!(
|
||||||
|
"Failed to parse the first value ('{}') as f32: {}",
|
||||||
|
parts[0], e
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
let second = parts[1].parse::<f32>().map_err(|e| {
|
||||||
|
format!(
|
||||||
|
"Failed to parse the second value ('{}') as f32: {}",
|
||||||
|
parts[1], e
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
if first <= 0.0 || second <= 0.0 {
|
||||||
|
return Err("Both values must be positive numbers.".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((first, second))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_crop_coordinates(s: &str) -> Result<(u32, u32, u32, u32), String> {
|
||||||
|
let coords: Vec<&str> = s.split(',').collect();
|
||||||
|
if coords.len() != 4 {
|
||||||
|
return Err(format!(
|
||||||
|
"Invalid crop format '{}'. Expected 'x1,y1,x2,y2' (e.g., '50,50,200,200').",
|
||||||
|
s
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let x1 = coords[0].parse::<u32>().map_err(|_| format!("Invalid x1 value '{}'.", coords[0]))?;
|
||||||
|
let y1 = coords[1].parse::<u32>().map_err(|_| format!("Invalid y1 value '{}'.", coords[1]))?;
|
||||||
|
let x2 = coords[2].parse::<u32>().map_err(|_| format!("Invalid x2 value '{}'.", coords[2]))?;
|
||||||
|
let y2 = coords[3].parse::<u32>().map_err(|_| format!("Invalid y2 value '{}'.", coords[3]))?;
|
||||||
|
|
||||||
|
Ok((x1, y1, x2, y2))
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user