vapours/
generic.rs

1//! Traits that apply to multiple types.
2
3use num_traits::ToPrimitive;
4use vapoursynth4_rs::{
5  ffi::VSColorFamily,
6  frame::{VideoFormat, VideoFrame},
7  node::VideoNode,
8  SampleType, VideoInfo,
9};
10
11use crate::enums::ColorRange;
12
13/// A trait for types that hold a video format.
14pub trait HoldsVideoFormat: Sized {
15  /// Get the video format.
16  #[must_use]
17  fn video_format(&self) -> &VideoFormat;
18
19  /// Get the color family of this clip or format.
20  #[must_use]
21  fn color_family(&self) -> VSColorFamily {
22    self.video_format().color_family
23  }
24
25  /// Get the bit depth of this clip or format.
26  #[must_use]
27  fn depth(&self) -> i32 {
28    self.video_format().bits_per_sample
29  }
30
31  /// Get the sample type of this clip or format.
32  #[must_use]
33  fn sample_type(&self) -> SampleType {
34    self.video_format().sample_type
35  }
36
37  /// Returns the lowest value for the bit depth of this clip or format.
38  #[must_use]
39  fn lowest_value(&self, chroma: Option<bool>, range_in: Option<ColorRange>) -> f32 {
40    let chroma = chroma.unwrap_or(false);
41    let range_in = range_in.unwrap_or(ColorRange::Full);
42
43    if self.sample_type() == SampleType::Float {
44      return if chroma { -0.5 } else { 0.0 };
45    }
46
47    if range_in == ColorRange::Limited {
48      return (16 << (self.depth() - 8))
49        .to_f32()
50        .expect("result should fit in a f32");
51    }
52
53    0.0
54  }
55
56  /// Returns the midpoint value for the bit depth of this clip or format.
57  #[must_use]
58  fn neutral_value(&self) -> f32 {
59    if self.sample_type() == SampleType::Float {
60      return 0.0;
61    }
62
63    (1 << (self.depth() - 1))
64      .to_f32()
65      .expect("result should fit in a f32")
66  }
67
68  /// Returns the peak value for the bit depth of this clip or format.
69  #[must_use]
70  fn peak_value(&self, chroma: Option<bool>, range_in: Option<ColorRange>) -> f32 {
71    let chroma = chroma.unwrap_or(false);
72    let range_in = range_in.unwrap_or(ColorRange::Full);
73
74    if self.sample_type() == SampleType::Float {
75      return if chroma { 0.5 } else { 1.0 };
76    }
77
78    if range_in == ColorRange::Limited {
79      return (if chroma { 240 } else { 235 } << (self.depth() - 8))
80        .to_f32()
81        .expect("result should fit in a f32");
82    }
83
84    ((1 << self.depth()) - 1)
85      .to_f32()
86      .expect("result should fit in a f32")
87  }
88}
89
90impl HoldsVideoFormat for VideoFrame {
91  fn video_format(&self) -> &VideoFormat {
92    self.get_video_format()
93  }
94}
95
96impl HoldsVideoFormat for VideoFormat {
97  fn video_format(&self) -> &VideoFormat {
98    self
99  }
100}
101
102impl HoldsVideoFormat for VideoInfo {
103  fn video_format(&self) -> &VideoFormat {
104    &self.format
105  }
106}
107
108impl HoldsVideoFormat for VideoNode {
109  fn video_format(&self) -> &VideoFormat {
110    self.info().video_format()
111  }
112}