ndi_sdk_sys/
subsampling.rs

1//! Chroma subsampling
2//!
3//! <https://docs.ndi.video/all/using-ndi/ndi-for-video/digital-video-basics#chroma-subsampling>
4
5use std::fmt::Debug;
6use std::fmt::Display;
7
8/// Describes the chroma subsampling system
9///
10/// The struct fields represent the numbers of the typical `<x_ref>:<x_samples>:<x2_samples>` format
11///
12/// <https://docs.ndi.video/all/using-ndi/ndi-for-video/digital-video-basics#chroma-subsampling>
13#[derive(Clone, Copy, Hash, PartialEq, Eq)]
14pub struct Subsampling {
15    pub x_ref: u8,
16    pub x_samples: u8,
17    pub x2_samples: u8,
18}
19
20impl Display for Subsampling {
21    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22        write!(f, "{}:{}:{}", self.x_ref, self.x_samples, self.x2_samples)
23    }
24}
25
26impl Debug for Subsampling {
27    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28        write!(
29            f,
30            "Subsampling({}:{}:{})",
31            self.x_ref, self.x_samples, self.x2_samples
32        )
33    }
34}
35
36impl Subsampling {
37    pub const fn new(x_ref: u8, x_samples: u8, x2_samples: u8) -> Self {
38        Subsampling {
39            x_ref,
40            x_samples,
41            x2_samples,
42        }
43    }
44
45    /// `4:4:4` (no subsampling)
46    pub const fn none() -> Self {
47        Subsampling {
48            x_ref: 4,
49            x_samples: 4,
50            x2_samples: 4,
51        }
52    }
53
54    pub const fn is_subsampled(&self) -> bool {
55        self.x_ref != self.x_samples || self.x_ref != self.x2_samples
56    }
57
58    /// Checks if the subsampling format is regular, if a format is irregular it just doesn't make any sense
59    pub const fn is_regular(&self) -> bool {
60        if self.x_ref == 0 {
61            return false;
62        }
63
64        if self.x_samples > self.x_ref || self.x2_samples > self.x_samples {
65            return false;
66        }
67
68        self.x_ref.is_multiple_of(self.x_samples) && self.x2_samples.is_multiple_of(self.x_samples)
69    }
70
71    /// Gets the number of x pixels that form a color block
72    ///
73    /// # Panics
74    ///
75    /// Panics if the format is not regular
76    pub fn x_grouping(&self) -> u8 {
77        assert!(
78            self.is_regular(),
79            "Subsampling must be regular to get x grouping"
80        );
81
82        self.x_ref / self.x_samples
83    }
84
85    /// Gets the number of y pixels that form a color block
86    ///
87    /// # Panics
88    ///
89    /// Panics if the format is not regular
90    pub fn y_grouping(&self) -> u8 {
91        assert!(
92            self.is_regular(),
93            "Subsampling must be regular to get y grouping"
94        );
95
96        if self.x2_samples == 0 {
97            2
98        } else if self.x_samples == self.x2_samples {
99            1
100        } else {
101            panic!(
102                "Subsampling is not regular, cannot determine y grouping: {}",
103                self
104            )
105        }
106    }
107}
108
109impl Default for Subsampling {
110    fn default() -> Self {
111        Self {
112            x_ref: 4,
113            x_samples: 4,
114            x2_samples: 4,
115        }
116    }
117}
118
119#[cfg(test)]
120mod tests {
121    use super::Subsampling;
122
123    #[test]
124    fn no_subsampling() {
125        let not_subsampled = Subsampling::new(4, 4, 4);
126        assert!(!not_subsampled.is_subsampled());
127        assert!(not_subsampled.is_regular());
128        assert_eq!(not_subsampled.x_grouping(), 1);
129        assert_eq!(not_subsampled.y_grouping(), 1);
130    }
131
132    #[test]
133    fn subsampling_4_2_2() {
134        let subsampled = Subsampling::new(4, 2, 2);
135        assert!(subsampled.is_subsampled());
136        assert!(subsampled.is_regular());
137        assert_eq!(subsampled.x_grouping(), 2);
138        assert_eq!(subsampled.y_grouping(), 1);
139    }
140
141    #[test]
142    fn subsampling_4_2_0() {
143        let subsampled = Subsampling::new(4, 2, 0);
144        assert!(subsampled.is_subsampled());
145        assert!(subsampled.is_regular());
146        assert_eq!(subsampled.x_grouping(), 2);
147        assert_eq!(subsampled.y_grouping(), 2);
148    }
149
150    #[test]
151    fn subsampling_4_1_1() {
152        let subsampled = Subsampling::new(4, 1, 1);
153        assert!(subsampled.is_subsampled());
154        assert!(subsampled.is_regular());
155        assert_eq!(subsampled.x_grouping(), 4);
156        assert_eq!(subsampled.y_grouping(), 1);
157    }
158
159    #[test]
160    fn subsampling_4_4_0() {
161        let subsampled = Subsampling::new(4, 4, 0);
162        assert!(subsampled.is_subsampled());
163        assert!(subsampled.is_regular());
164        assert_eq!(subsampled.x_grouping(), 1);
165        assert_eq!(subsampled.y_grouping(), 2);
166    }
167
168    #[test]
169    fn irregular_subsampling() {
170        let irregular = Subsampling::new(4, 3, 2);
171        assert!(irregular.is_subsampled());
172        assert!(!irregular.is_regular());
173    }
174
175    #[test]
176    fn zero_reference() {
177        let zero_ref = Subsampling::new(0, 0, 0);
178        assert!(!zero_ref.is_regular());
179    }
180}