1// Copyright (c) 2017-2021, The rav1e contributors. All rights reserved
2//
3// This source code is subject to the terms of the BSD 2 Clause License and
4// the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
5// was not distributed with this source code in the LICENSE file, you can
6// obtain it at www.aomedia.org/license/software. If the Alliance for Open
7// Media Patent License 1.0 was not distributed with this source code in the
8// PATENTS file, you can obtain it at www.aomedia.org/license/patent.
9
10#[cfg(feature = "serialize")]
11use serde::{Serialize, Deserialize};
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
23/// Trait for casting between primitive types.
24pub trait CastFromPrimitive<T>: Copy + 'static {
25 /// Casts the given value into `Self`.
26 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
41// casts to { u8, u16 } are implemented separately using Pixel, so that the
42// compiler understands that CastFromPrimitive<T: Pixel> is always implemented
43impl_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/// Types that can be used as pixel types.
92#[derive(PartialEq, Eq)]
93pub enum PixelType {
94 /// 8 bits per pixel, stored in a `u8`.
95 U8,
96 /// 10 or 12 bits per pixel, stored in a `u16`.
97 U16,
98}
99
100/// A type that can be used as a pixel type.
101pub trait Pixel:
102 RegisteredPrimitive + Into<u32> + Into<i32> + Debug + Display + Send + Sync + 'static
103{
104 type Coeff: Coefficient;
105
106 /// Returns a [`PixelType`] variant corresponding to this type.
107 ///
108 /// [`PixelType`]: enum.PixelType.html
109 fn type_enum() -> PixelType;
110
111 /// Converts stride in pixels to stride in bytes.
112 #[inline]
113 #[allow(clippy::wrong_self_convention)]
114 fn to_asm_stride(in_stride: usize) -> isize {
115 (in_stride * size_of::<Self>()) as isize
116 }
117}
118
119impl Pixel for u8 {
120 type Coeff = i16;
121
122 #[inline]
123 fn type_enum() -> PixelType {
124 PixelType::U8
125 }
126}
127
128impl Pixel for u16 {
129 type Coeff = i32;
130
131 #[inline]
132 fn type_enum() -> PixelType {
133 PixelType::U16
134 }
135}
136
137pub trait Coefficient:
138 RegisteredPrimitive + Into<i32> + AddAssign + Signed + Debug + 'static
139{
140 type Pixel: Pixel;
141}
142
143impl Coefficient for i16 {
144 type Pixel = u8;
145}
146impl Coefficient for i32 {
147 type Pixel = u16;
148}
149
150/// Chroma subsampling format
151#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
152#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
153#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen)]
154#[repr(C)]
155pub enum ChromaSampling {
156 /// Both vertically and horizontally subsampled.
157 #[default]
158 Cs420,
159 /// Horizontally subsampled.
160 Cs422,
161 /// Not subsampled.
162 Cs444,
163 /// Monochrome.
164 Cs400,
165}
166
167impl FromPrimitive for ChromaSampling {
168 fn from_i64(n: i64) -> Option<Self> {
169 use ChromaSampling::*;
170
171 match n {
172 n: i64 if n == Cs420 as i64 => Some(Cs420),
173 n: i64 if n == Cs422 as i64 => Some(Cs422),
174 n: i64 if n == Cs444 as i64 => Some(Cs444),
175 n: i64 if n == Cs400 as i64 => Some(Cs400),
176 _ => None,
177 }
178 }
179
180 fn from_u64(n: u64) -> Option<Self> {
181 ChromaSampling::from_i64(n as i64)
182 }
183}
184
185impl fmt::Display for ChromaSampling {
186 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
187 write!(
188 f,
189 "{}",
190 match self {
191 ChromaSampling::Cs420 => "4:2:0",
192 ChromaSampling::Cs422 => "4:2:2",
193 ChromaSampling::Cs444 => "4:4:4",
194 ChromaSampling::Cs400 => "Monochrome",
195 }
196 )
197 }
198}
199
200impl ChromaSampling {
201 /// Provides the amount to right shift the luma plane dimensions to get the
202 /// chroma plane dimensions.
203 /// Only values 0 or 1 are ever returned.
204 /// The plane dimensions must also be rounded up to accommodate odd luma plane
205 /// sizes.
206 /// Cs400 returns None, as there are no chroma planes.
207 pub const fn get_decimation(self) -> Option<(usize, usize)> {
208 use self::ChromaSampling::*;
209 match self {
210 Cs420 => Some((1, 1)),
211 Cs422 => Some((1, 0)),
212 Cs444 => Some((0, 0)),
213 Cs400 => None,
214 }
215 }
216
217 /// Calculates the size of a chroma plane for this sampling type, given the luma plane dimensions.
218 pub const fn get_chroma_dimensions(
219 self,
220 luma_width: usize,
221 luma_height: usize,
222 ) -> (usize, usize) {
223 if let Some((ss_x, ss_y)) = self.get_decimation() {
224 ((luma_width + ss_x) >> ss_x, (luma_height + ss_y) >> ss_y)
225 } else {
226 (0, 0)
227 }
228 }
229}
230
231#[cfg(test)]
232mod test {
233 use super::*;
234
235 #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
236 use wasm_bindgen_test::*;
237
238 #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
239 wasm_bindgen_test_configure!(run_in_browser);
240
241 #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen_test)]
242 #[test]
243 fn chroma_sampling_from_int() {
244 let expected = [
245 (-1, None),
246 (0, Some(ChromaSampling::Cs420)),
247 (1, Some(ChromaSampling::Cs422)),
248 (2, Some(ChromaSampling::Cs444)),
249 (3, Some(ChromaSampling::Cs400)),
250 (4, None),
251 ];
252
253 for (int, chroma_sampling) in expected {
254 let converted = ChromaSampling::from_i32(int);
255 assert_eq!(chroma_sampling, converted);
256
257 let converted_uint = ChromaSampling::from_u32(int as u32);
258 assert_eq!(chroma_sampling, converted_uint, "FromPrimitive does not return the same result for i32 and u32");
259 }
260 }
261}
262
263