1#[cfg(feature = "serialize")]
11use serde::{Deserialize, Serialize};
12
13#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
14use wasm_bindgen::prelude::*;
15
16use num_traits::{AsPrimitive, FromPrimitive, PrimInt, Signed};
17
18use std::fmt;
19use std::fmt::{Debug, Display};
20use std::mem::size_of;
21use std::ops::AddAssign;
22
23pub trait CastFromPrimitive<T>: Copy + 'static {
25 fn cast_from(v: T) -> Self;
27}
28
29macro_rules! impl_cast_from_primitive {
30 ( $T:ty => $U:ty ) => {
31 impl CastFromPrimitive<$U> for $T {
32 #[inline(always)]
33 fn cast_from(v: $U) -> Self { v as Self }
34 }
35 };
36 ( $T:ty => { $( $U:ty ),* } ) => {
37 $( impl_cast_from_primitive!($T => $U); )*
38 };
39}
40
41impl_cast_from_primitive!(u8 => { u32, u64, usize });
44impl_cast_from_primitive!(u8 => { i8, i64, isize });
45impl_cast_from_primitive!(u16 => { u32, u64, usize });
46impl_cast_from_primitive!(u16 => { i8, i64, isize });
47impl_cast_from_primitive!(i16 => { u32, u64, usize });
48impl_cast_from_primitive!(i16 => { i8, i64, isize });
49impl_cast_from_primitive!(i32 => { u32, u64, usize });
50impl_cast_from_primitive!(i32 => { i8, i64, isize });
51
52pub trait RegisteredPrimitive:
53 PrimInt
54 + AsPrimitive<u8>
55 + AsPrimitive<i16>
56 + AsPrimitive<u16>
57 + AsPrimitive<i32>
58 + AsPrimitive<u32>
59 + AsPrimitive<usize>
60 + CastFromPrimitive<u8>
61 + CastFromPrimitive<i16>
62 + CastFromPrimitive<u16>
63 + CastFromPrimitive<i32>
64 + CastFromPrimitive<u32>
65 + CastFromPrimitive<usize>
66{
67}
68
69impl RegisteredPrimitive for u8 {}
70impl RegisteredPrimitive for u16 {}
71impl RegisteredPrimitive for i16 {}
72impl RegisteredPrimitive for i32 {}
73
74macro_rules! impl_cast_from_pixel_to_primitive {
75 ( $T:ty ) => {
76 impl<T: RegisteredPrimitive> CastFromPrimitive<T> for $T {
77 #[inline(always)]
78 fn cast_from(v: T) -> Self {
79 v.as_()
80 }
81 }
82 };
83}
84
85impl_cast_from_pixel_to_primitive!(u8);
86impl_cast_from_pixel_to_primitive!(i16);
87impl_cast_from_pixel_to_primitive!(u16);
88impl_cast_from_pixel_to_primitive!(i32);
89impl_cast_from_pixel_to_primitive!(u32);
90
91#[derive(PartialEq, Eq)]
93pub enum PixelType {
94 U8,
96 U16,
98}
99
100pub trait Pixel:
102 RegisteredPrimitive + Into<u32> + Into<i32> + Debug + Display + Send + Sync + 'static
103{
104 type Coeff: Coefficient;
105
106 fn type_enum() -> PixelType;
110
111 #[inline]
113 fn to_asm_stride(in_stride: usize) -> isize {
114 (in_stride * size_of::<Self>()) as isize
115 }
116}
117
118impl Pixel for u8 {
119 type Coeff = i16;
120
121 #[inline]
122 fn type_enum() -> PixelType {
123 PixelType::U8
124 }
125}
126
127impl Pixel for u16 {
128 type Coeff = i32;
129
130 #[inline]
131 fn type_enum() -> PixelType {
132 PixelType::U16
133 }
134}
135
136pub trait Coefficient:
137 RegisteredPrimitive + Into<i32> + AddAssign + Signed + Debug + 'static
138{
139 type Pixel: Pixel;
140}
141
142impl Coefficient for i16 {
143 type Pixel = u8;
144}
145impl Coefficient for i32 {
146 type Pixel = u16;
147}
148
149#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
151#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
152#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen)]
153#[repr(C)]
154pub enum ChromaSampling {
155 #[default]
157 Cs420,
158 Cs422,
160 Cs444,
162 Cs400,
164}
165
166impl FromPrimitive for ChromaSampling {
167 fn from_i64(n: i64) -> Option<Self> {
168 use ChromaSampling::*;
169
170 match n {
171 n if n == Cs420 as i64 => Some(Cs420),
172 n if n == Cs422 as i64 => Some(Cs422),
173 n if n == Cs444 as i64 => Some(Cs444),
174 n if n == Cs400 as i64 => Some(Cs400),
175 _ => None,
176 }
177 }
178
179 fn from_u64(n: u64) -> Option<Self> {
180 ChromaSampling::from_i64(n as i64)
181 }
182}
183
184impl fmt::Display for ChromaSampling {
185 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
186 write!(
187 f,
188 "{}",
189 match self {
190 ChromaSampling::Cs420 => "4:2:0",
191 ChromaSampling::Cs422 => "4:2:2",
192 ChromaSampling::Cs444 => "4:4:4",
193 ChromaSampling::Cs400 => "Monochrome",
194 }
195 )
196 }
197}
198
199impl ChromaSampling {
200 pub const fn get_decimation(self) -> Option<(usize, usize)> {
207 use self::ChromaSampling::*;
208 match self {
209 Cs420 => Some((1, 1)),
210 Cs422 => Some((1, 0)),
211 Cs444 => Some((0, 0)),
212 Cs400 => None,
213 }
214 }
215
216 pub const fn get_chroma_dimensions(
218 self,
219 luma_width: usize,
220 luma_height: usize,
221 ) -> (usize, usize) {
222 if let Some((ss_x, ss_y)) = self.get_decimation() {
223 ((luma_width + ss_x) >> ss_x, (luma_height + ss_y) >> ss_y)
224 } else {
225 (0, 0)
226 }
227 }
228}
229
230#[cfg(test)]
231mod test {
232 use super::*;
233
234 #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
235 use wasm_bindgen_test::*;
236
237 #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
238 wasm_bindgen_test_configure!(run_in_browser);
239
240 #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen_test)]
241 #[test]
242 fn asm_stride() {
243 let tests = [(7, 7, 14), (12, 12, 24), (1234, 1234, 2468)];
244
245 for (in_stride, u8_bytes, u16_bytes) in tests {
246 assert_eq!(u8::to_asm_stride(in_stride), u8_bytes);
247 assert_eq!(u16::to_asm_stride(in_stride), u16_bytes);
248 }
249 }
250
251 #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen_test)]
252 #[test]
253 fn type_enum() {
254 assert!(u8::type_enum() == PixelType::U8);
255 assert!(u16::type_enum() == PixelType::U16);
256 }
257
258 #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen_test)]
259 #[test]
260 fn chroma_sampling_from_int() {
261 let expected = [
262 (-1, None),
263 (0, Some(ChromaSampling::Cs420)),
264 (1, Some(ChromaSampling::Cs422)),
265 (2, Some(ChromaSampling::Cs444)),
266 (3, Some(ChromaSampling::Cs400)),
267 (4, None),
268 ];
269
270 for (int, chroma_sampling) in expected {
271 let converted = ChromaSampling::from_i32(int);
272 assert_eq!(chroma_sampling, converted);
273
274 let converted_uint = ChromaSampling::from_u32(int as u32);
275 assert_eq!(chroma_sampling, converted_uint);
276 }
277 }
278
279 #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen_test)]
280 #[test]
281 fn display_chroma_sampling() {
282 use std::fmt::Write;
283
284 let all_cs = [
285 (ChromaSampling::Cs420, "4:2:0"),
286 (ChromaSampling::Cs422, "4:2:2"),
287 (ChromaSampling::Cs444, "4:4:4"),
288 (ChromaSampling::Cs400, "Monochrome"),
289 ];
290
291 for (cs, expected) in all_cs {
292 let mut s = String::new();
293 write!(&mut s, "{cs}").expect("can display");
294 assert_eq!(&s, expected);
295 }
296 }
297
298 #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen_test)]
299 #[test]
300 fn chroma_sampling_dimensions() {
301 let tests = [
302 ((1024, 768), ChromaSampling::Cs444, (1024, 768)),
303 ((1024, 768), ChromaSampling::Cs422, (512, 768)),
304 ((1024, 768), ChromaSampling::Cs420, (512, 384)),
305 ((1024, 768), ChromaSampling::Cs400, (0, 0)),
306 ((1023, 768), ChromaSampling::Cs422, (512, 768)),
308 ((1023, 767), ChromaSampling::Cs420, (512, 384)),
309 ];
310
311 for (luma, cs, expected_chroma) in tests {
312 let chroma = cs.get_chroma_dimensions(luma.0, luma.1);
313 assert_eq!(chroma, expected_chroma);
314 }
315 }
316}