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
96
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,
    /// the folder to scan for image files

    /// 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)))
    }
}