vapours/
utils.rs

1use const_str::cstr;
2use strum_macros::EnumString;
3use vapoursynth4_rs::{
4  core::Core,
5  key,
6  map::{AppendMode, Value},
7  node::VideoNode,
8};
9
10use crate::errors::VapoursError;
11
12const FMTCONV_NAMESPACE: &str = "fmtc";
13
14/// Enum for `zimg_dither_type_e` and fmtconv `dmode`.
15#[derive(Clone, Copy, Debug, EnumString, Eq, PartialEq)]
16#[strum(ascii_case_insensitive, serialize_all = "snake_case")]
17pub enum DitherType {
18  /// Choose automatically.
19  Auto,
20
21  /// Round to nearest.
22  None,
23
24  /// Bayer patterned dither.
25  Ordered,
26
27  /// Pseudo-random noise of magnitude 0.5.
28  Random,
29
30  /// Floyd-Steinberg error diffusion.
31  ErrorDiffusion,
32
33  /// Floyd-Steinberg error diffusion. Modified for serpentine scan (avoids worm
34  /// artifacts).
35  ErrorDiffusionFmtc,
36
37  /// Another type of error diffusion. Quick and excellent quality, similar to
38  /// Floyd-Steinberg.
39  #[strum(serialize = "sierra_2_4a")]
40  Sierra24a,
41
42  /// Another error diffusion kernel. Preserves delicate edges better but
43  /// distorts gradients.
44  Stucki,
45
46  /// Another error diffusion kernel. Generates distinct patterns but keeps
47  /// clean the flat areas (noise modulation).
48  Atkinson,
49
50  /// Another error diffusion kernel. Slow, available only for integer input at
51  /// the moment. Avoids usual F-S artifacts.
52  Ostromoukhov,
53
54  /// A way to generate blue-noise dither and has a much better visual aspect
55  /// than ordered dithering.
56  Void,
57
58  /// Dither using quasirandom sequences. Good intermediary between void,
59  /// cluster, and error diffusion algorithms.
60  Quasirandom,
61}
62
63/// [`Core`] extensions.
64pub trait VapoursCore {
65  /// Bit depth conversion.
66  ///
67  /// # Errors
68  ///
69  /// Returns an error if the fmtconv plugin is not found or on any error
70  /// accessing frame properties.
71  fn depth(&self, clip: VideoNode, bit_depth: u32) -> Result<VideoNode, VapoursError>;
72}
73
74impl VapoursCore for Core {
75  fn depth(&self, clip: VideoNode, bit_depth: u32) -> Result<VideoNode, VapoursError> {
76    todo!("Needs configurable dither type, non-fmtc dithering, and probably more.");
77
78    let Some(fmtc_plugin) = self.get_plugin_by_id(cstr!(FMTCONV_NAMESPACE)) else {
79      return Err(VapoursError::DependencyNotFoundError(
80        FMTCONV_NAMESPACE.to_string(),
81      ));
82    };
83
84    let mut args = self.create_map();
85    args
86      .set(key!(c"clip"), Value::VideoNode(clip), AppendMode::Replace)
87      .map_err(|_| VapoursError::FramePropertyError("clip".to_string()))?;
88    args
89      .set(
90        key!(c"bitdepth"),
91        Value::Int(i64::from(bit_depth)),
92        AppendMode::Replace,
93      )
94      .map_err(|_| VapoursError::FramePropertyError("clip".to_string()))?;
95    let ret = fmtc_plugin.invoke(cstr!("bitdepth"), &args);
96    ret
97      .get_video_node(key!(c"clip"), 0)
98      .map_err(|_| VapoursError::FramePropertyError("clip".to_string()))
99  }
100}