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}