1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
use crate::prelude::*;
use std::borrow::Cow;
use std::path::{Path, PathBuf};
use structopt::StructOpt;
#[derive(Debug, StructOpt, Default, PartialEq, Clone)]
#[structopt(name = "dither")]
/// Command-line interface & arguments. See [structopt].
pub struct Opt {
/// Provide verbose debug information. Default is false.
#[structopt(short = "v", long = "verbose")]
pub verbose: bool,
/// Input file. Supported file types:
/// `PNG`
/// `JPEG`
/// `GIF`
/// `BMP`
/// `ICO`
/// `TIFF`
#[structopt(name = "input", parse(from_os_str))]
pub input: PathBuf,
/// Color depth. Must be between 1 and 8. See [create_quantize_n_bits_func][crate::create_quantize_n_bits_func] and [create_convert_quantized_to_palette_func][crate::create_convert_quantized_to_palette_func]
#[structopt(long = "depth", default_value = "1")]
pub bit_depth: u8,
/// Output file: will be written to as a .png or .jpg (inferred from file extension). If left empty,
/// a default output path will be created: see [Opt::output_path]
#[structopt(name = "output", parse(from_os_str))]
pub output: Option<PathBuf>,
/// Ditherering algorithm to use. Options are
/// - "floyd" (default)
/// - "atkinson"
/// - "stucki",
/// - "burkes"
/// - "jarvis"
/// - "sierra3"
///
#[structopt(short = "d", long = "dither", default_value = "floyd")]
pub ditherer: Ditherer<'static>,
/// Color mode to use.
/// Options are
/// - bw => grayscale with the specified bit depth. (default)
/// - color => color mode with the specified bit depth.
/// - cga => load the cga palette. equivalent to "cga.plt".
/// - crayon => load the crayon palette. equivalent to "crayon.plt"
/// - $COLOR => single-color mode. options are
/// - $FILENAME" => load palette from file, listed as line-separated RGB values. see "cga.plt" and the readme for more information on palette files.
#[structopt(short = "c", long = "color", default_value = "bw")]
pub color_mode: color::Mode,
}
impl Opt {
/// the [canonicalized][std::fs::canonicalize] input path
pub fn input_path<'a>(&'a self) -> Result<PathBuf> {
match self.input.canonicalize() {
Err(err) => return Err(Error::Input(IOError::new(err, &self.input))),
Ok(abs_path) => Ok(abs_path),
}
}
/// the actual output path. if opts.output exists, this is that, otherwise, this is
/// `"{base}_dithered_{dither}_{color}_{depth}.png"`,
/// where base is the [canonicalized][std::fs::canonicalize] input path, stripped of it's extension.
/// `$dither bunny.png --color=color --dither=atkinson --depth=2` will save to `bunny_atkinson_c_2.png`
///
/// ```
/// # use dither::prelude::*;
/// # use std::path::{PathBuf,Path};
/// let mut opt = Opt::default();
/// opt.bit_depth=1;
/// opt.input = PathBuf::from("bunny.png".to_string());
/// let got_path = opt.output_path().unwrap();
/// assert_eq!("bunny_dithered_floyd_bw_1.png", Path::file_name(got_path.as_ref().as_ref()).unwrap().to_string_lossy());
/// ```
///
pub fn output_path<'a>(&'a self) -> Result<Cow<'a, Path>> {
if let Some(path) = &self.output {
return Ok(Cow::Borrowed(&path));
}
let abs_path = match self.input.canonicalize() {
Err(err) => return Err(Error::Input(IOError::new(err, &self.input))),
Ok(abs_path) => abs_path,
};
let path = format!(
"{base}_dithered_{dither}_{color}_{depth}.png",
base = abs_path.file_stem().unwrap_or_default().to_string_lossy(),
dither = self.ditherer,
color = self.color_mode,
depth = self.bit_depth,
);
Ok(Cow::Owned(PathBuf::from(path)))
}
}