#[cfg(feature = "serialize")]
use serde::{Serialize, Deserialize};
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
use wasm_bindgen::prelude::*;
use num_traits::{AsPrimitive, FromPrimitive, PrimInt, Signed};
use std::fmt;
use std::fmt::{Debug, Display};
use std::mem::size_of;
use std::ops::AddAssign;
pub trait CastFromPrimitive<T>: Copy + 'static {
fn cast_from(v: T) -> Self;
}
macro_rules! impl_cast_from_primitive {
( $T:ty => $U:ty ) => {
impl CastFromPrimitive<$U> for $T {
#[inline(always)]
fn cast_from(v: $U) -> Self { v as Self }
}
};
( $T:ty => { $( $U:ty ),* } ) => {
$( impl_cast_from_primitive!($T => $U); )*
};
}
impl_cast_from_primitive!(u8 => { u32, u64, usize });
impl_cast_from_primitive!(u8 => { i8, i64, isize });
impl_cast_from_primitive!(u16 => { u32, u64, usize });
impl_cast_from_primitive!(u16 => { i8, i64, isize });
impl_cast_from_primitive!(i16 => { u32, u64, usize });
impl_cast_from_primitive!(i16 => { i8, i64, isize });
impl_cast_from_primitive!(i32 => { u32, u64, usize });
impl_cast_from_primitive!(i32 => { i8, i64, isize });
pub trait RegisteredPrimitive:
PrimInt
+ AsPrimitive<u8>
+ AsPrimitive<i16>
+ AsPrimitive<u16>
+ AsPrimitive<i32>
+ AsPrimitive<u32>
+ AsPrimitive<usize>
+ CastFromPrimitive<u8>
+ CastFromPrimitive<i16>
+ CastFromPrimitive<u16>
+ CastFromPrimitive<i32>
+ CastFromPrimitive<u32>
+ CastFromPrimitive<usize>
{
}
impl RegisteredPrimitive for u8 {}
impl RegisteredPrimitive for u16 {}
impl RegisteredPrimitive for i16 {}
impl RegisteredPrimitive for i32 {}
macro_rules! impl_cast_from_pixel_to_primitive {
( $T:ty ) => {
impl<T: RegisteredPrimitive> CastFromPrimitive<T> for $T {
#[inline(always)]
fn cast_from(v: T) -> Self {
v.as_()
}
}
};
}
impl_cast_from_pixel_to_primitive!(u8);
impl_cast_from_pixel_to_primitive!(i16);
impl_cast_from_pixel_to_primitive!(u16);
impl_cast_from_pixel_to_primitive!(i32);
impl_cast_from_pixel_to_primitive!(u32);
#[derive(PartialEq, Eq)]
pub enum PixelType {
U8,
U16,
}
pub trait Pixel:
RegisteredPrimitive + Into<u32> + Into<i32> + Debug + Display + Send + Sync + 'static
{
type Coeff: Coefficient;
fn type_enum() -> PixelType;
#[inline]
#[allow(clippy::wrong_self_convention)]
fn to_asm_stride(in_stride: usize) -> isize {
(in_stride * size_of::<Self>()) as isize
}
}
impl Pixel for u8 {
type Coeff = i16;
#[inline]
fn type_enum() -> PixelType {
PixelType::U8
}
}
impl Pixel for u16 {
type Coeff = i32;
#[inline]
fn type_enum() -> PixelType {
PixelType::U16
}
}
pub trait Coefficient:
RegisteredPrimitive + Into<i32> + AddAssign + Signed + Debug + 'static
{
type Pixel: Pixel;
}
impl Coefficient for i16 {
type Pixel = u8;
}
impl Coefficient for i32 {
type Pixel = u16;
}
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen)]
#[repr(C)]
pub enum ChromaSampling {
#[default]
Cs420,
Cs422,
Cs444,
Cs400,
}
impl FromPrimitive for ChromaSampling {
fn from_i64(n: i64) -> Option<Self> {
use ChromaSampling::*;
match n {
n if n == Cs420 as i64 => Some(Cs420),
n if n == Cs422 as i64 => Some(Cs422),
n if n == Cs444 as i64 => Some(Cs444),
n if n == Cs400 as i64 => Some(Cs400),
_ => None,
}
}
fn from_u64(n: u64) -> Option<Self> {
ChromaSampling::from_i64(n as i64)
}
}
impl fmt::Display for ChromaSampling {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(
f,
"{}",
match self {
ChromaSampling::Cs420 => "4:2:0",
ChromaSampling::Cs422 => "4:2:2",
ChromaSampling::Cs444 => "4:4:4",
ChromaSampling::Cs400 => "Monochrome",
}
)
}
}
impl ChromaSampling {
pub const fn get_decimation(self) -> Option<(usize, usize)> {
use self::ChromaSampling::*;
match self {
Cs420 => Some((1, 1)),
Cs422 => Some((1, 0)),
Cs444 => Some((0, 0)),
Cs400 => None,
}
}
pub const fn get_chroma_dimensions(
self,
luma_width: usize,
luma_height: usize,
) -> (usize, usize) {
if let Some((ss_x, ss_y)) = self.get_decimation() {
((luma_width + ss_x) >> ss_x, (luma_height + ss_y) >> ss_y)
} else {
(0, 0)
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
use wasm_bindgen_test::*;
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
wasm_bindgen_test_configure!(run_in_browser);
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen_test)]
#[test]
fn chroma_sampling_from_int() {
let expected = [
(-1, None),
(0, Some(ChromaSampling::Cs420)),
(1, Some(ChromaSampling::Cs422)),
(2, Some(ChromaSampling::Cs444)),
(3, Some(ChromaSampling::Cs400)),
(4, None),
];
for (int, chroma_sampling) in expected {
let converted = ChromaSampling::from_i32(int);
assert_eq!(chroma_sampling, converted);
let converted_uint = ChromaSampling::from_u32(int as u32);
assert_eq!(chroma_sampling, converted_uint, "FromPrimitive does not return the same result for i32 and u32");
}
}
}