Skip to main content

oxideav_cli_convert/
op.rs

1//! IM-style operations supported by `oxideav convert`.
2//!
3//! Each variant carries exactly the data the CLI needs to hand to
4//! [`crate::plan_to_job`], nothing more.
5
6/// Dither strategy used when `-colors N` forces a paletted output.
7#[derive(Clone, Copy, Debug, PartialEq, Eq)]
8pub enum Dither {
9    None,
10    Bayer,
11    FloydSteinberg,
12}
13
14impl Dither {
15    /// Parse the value after `-dither`. Accepts ImageMagick-ish names
16    /// case-insensitively (`None`, `FloydSteinberg`, `floyd_steinberg`,
17    /// `o8x8` / `ordered` → Bayer).
18    pub fn parse(s: &str) -> Result<Dither, String> {
19        match s.to_ascii_lowercase().as_str() {
20            "none" => Ok(Dither::None),
21            "floyd_steinberg" | "floyd-steinberg" | "floydsteinberg" | "fs" => {
22                Ok(Dither::FloydSteinberg)
23            }
24            "bayer" | "ordered" | "o8x8" => Ok(Dither::Bayer),
25            other => Err(format!(
26                "convert: -dither: unknown strategy '{other}' (expected 'none', 'bayer', or 'floyd_steinberg')"
27            )),
28        }
29    }
30}
31
32/// One convert operation.
33///
34/// Operations apply in source order — same as `imagemagick convert`,
35/// even though we don't yet support IM's stack-reset semantics.
36#[derive(Clone, Debug, PartialEq)]
37pub enum Op {
38    /// `-resize WxH[!]`. `bang = true` means the `!` was present:
39    /// force exact dimensions without preserving aspect ratio.
40    Resize { width: u32, height: u32, bang: bool },
41    /// `-blur RxS`. Sigma defaults to `radius / 2.0` when the `xS`
42    /// portion is omitted (matches IM's convention).
43    Blur { radius: u32, sigma: f32 },
44    /// `-edge R`. Radius ignored by the current Sobel impl but parsed
45    /// for forward-compatibility.
46    Edge { radius: u32 },
47    /// `-colors N` paired with an optional `-dither` preceding it.
48    /// When present, the output is paletted via
49    /// [`oxideav_pixfmt`] before encoding.
50    Colors { count: u32, dither: Dither },
51    /// `-format FMT` — override the container/codec decision
52    /// otherwise derived from the output extension.
53    Format(String),
54    /// `-quality N` — forwarded to the sink codec (e.g. JPEG quality,
55    /// WebP quality). Silently dropped by codecs that don't honour it.
56    Quality(u32),
57    /// `-strip` — request that metadata (EXIF, XMP, ID3, etc.) be
58    /// dropped on write.
59    Strip,
60}
61
62/// The parsed result of one `oxideav convert` invocation.
63#[derive(Clone, Debug)]
64pub struct ConvertPlan {
65    /// Input URI. Currently exactly one — IM's multi-input stack is a
66    /// documented follow-up.
67    pub input: String,
68    /// Chain of operations in source order.
69    pub ops: Vec<Op>,
70    /// Output path.
71    pub output: String,
72}