use std::mem::MaybeUninit;
use crate::{Error, Result};
pub trait YUVPixelType:
Sized + Copy + Default + PartialEq + Eq + 'static + private::Sealed
{
}
impl YUVPixelType for u8 {}
impl YUVPixelType for u16 {}
mod private {
pub trait Sealed {}
impl Sealed for u8 {}
impl Sealed for u16 {}
}
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub enum ImageFormat {
YV12,
NV12,
I420,
I422,
I444,
I440,
}
#[inline]
fn check_dim_mult_2(dim: usize, err: Error) -> Result<()> {
if dim % 2 == 0 {
Ok(())
} else {
Err(err)
}
}
impl ImageFormat {
pub(crate) fn from_vpx<T: YUVPixelType>(
value: vpx_sys::vpx_img_fmt,
) -> Option<Self> {
let t = std::any::TypeId::of::<T>();
match value {
vpx_sys::vpx_img_fmt::VPX_IMG_FMT_YV12 => Some(Self::YV12),
vpx_sys::vpx_img_fmt::VPX_IMG_FMT_NV12 => Some(Self::NV12),
vpx_sys::vpx_img_fmt::VPX_IMG_FMT_I420 => {
(t == std::any::TypeId::of::<u8>()).then_some(Self::I420)
}
vpx_sys::vpx_img_fmt::VPX_IMG_FMT_I42016 => {
(t == std::any::TypeId::of::<u16>()).then_some(Self::I420)
}
vpx_sys::vpx_img_fmt::VPX_IMG_FMT_I422 => {
(t == std::any::TypeId::of::<u8>()).then_some(Self::I422)
}
vpx_sys::vpx_img_fmt::VPX_IMG_FMT_I42216 => {
(t == std::any::TypeId::of::<u16>()).then_some(Self::I422)
}
vpx_sys::vpx_img_fmt::VPX_IMG_FMT_I444 => {
(t == std::any::TypeId::of::<u8>()).then_some(Self::I444)
}
vpx_sys::vpx_img_fmt::VPX_IMG_FMT_I44416 => {
(t == std::any::TypeId::of::<u16>()).then_some(Self::I444)
}
vpx_sys::vpx_img_fmt::VPX_IMG_FMT_I440 => {
(t == std::any::TypeId::of::<u8>()).then_some(Self::I440)
}
vpx_sys::vpx_img_fmt::VPX_IMG_FMT_I44016 => {
(t == std::any::TypeId::of::<u16>()).then_some(Self::I440)
}
_ => None,
}
}
pub(crate) fn as_vpx_image<T: YUVPixelType>(&self) -> vpx_sys::vpx_img_fmt {
match self {
Self::YV12 => vpx_sys::vpx_img_fmt::VPX_IMG_FMT_YV12,
Self::NV12 => vpx_sys::vpx_img_fmt::VPX_IMG_FMT_NV12,
Self::I420 => {
if size_of::<T>() == 1 {
vpx_sys::vpx_img_fmt::VPX_IMG_FMT_I420
} else {
vpx_sys::vpx_img_fmt::VPX_IMG_FMT_I42016
}
}
Self::I422 => {
if size_of::<T>() == 1 {
vpx_sys::vpx_img_fmt::VPX_IMG_FMT_I422
} else {
vpx_sys::vpx_img_fmt::VPX_IMG_FMT_I42216
}
}
Self::I444 => {
if size_of::<T>() == 1 {
vpx_sys::vpx_img_fmt::VPX_IMG_FMT_I444
} else {
vpx_sys::vpx_img_fmt::VPX_IMG_FMT_I44416
}
}
Self::I440 => {
if size_of::<T>() == 1 {
vpx_sys::vpx_img_fmt::VPX_IMG_FMT_I440
} else {
vpx_sys::vpx_img_fmt::VPX_IMG_FMT_I44016
}
}
}
}
pub fn buffer_len(&self, width: usize, height: usize) -> Result<usize> {
match self {
Self::YV12 | Self::NV12 | Self::I420 => {
check_dim_mult_2(width, Error::WidthNotMultipleOfTwo)?;
check_dim_mult_2(height, Error::HeightNotMultipleOfTwo)?;
Ok(width * height + 2 * (width / 2) * (height / 2))
}
Self::I422 => {
check_dim_mult_2(width, Error::WidthNotMultipleOfTwo)?;
Ok(width * height + 2 * (width / 2) * height)
}
Self::I440 => {
check_dim_mult_2(height, Error::HeightNotMultipleOfTwo)?;
Ok(width * height + 2 * width * (height / 2))
}
Self::I444 => Ok(3 * width * height),
}
}
fn plane_ranges(&self, width: usize, height: usize) -> Result<PlaneRanges> {
match self {
Self::YV12 => {
check_dim_mult_2(width, Error::WidthNotMultipleOfTwo)?;
check_dim_mult_2(height, Error::HeightNotMultipleOfTwo)?;
let y = 0..(width * height);
let v = y.end..(y.end + (width / 2) * (height / 2));
let u = v.end..(v.end + (width / 2) * (height / 2));
Ok(PlaneRanges { y, u, v })
}
Self::NV12 => {
check_dim_mult_2(width, Error::WidthNotMultipleOfTwo)?;
check_dim_mult_2(height, Error::HeightNotMultipleOfTwo)?;
let y = 0..(width * height);
let u = y.end..(y.end + 2 * (width / 2) * (height / 2));
let v = u.clone();
Ok(PlaneRanges { y, u, v })
}
Self::I420 => {
check_dim_mult_2(width, Error::WidthNotMultipleOfTwo)?;
check_dim_mult_2(height, Error::HeightNotMultipleOfTwo)?;
let y = 0..(width * height);
let u = y.end..(y.end + (width / 2) * (height / 2));
let v = u.end..(u.end + (width / 2) * (height / 2));
Ok(PlaneRanges { y, u, v })
}
Self::I422 => {
check_dim_mult_2(width, Error::WidthNotMultipleOfTwo)?;
let y = 0..(width * height);
let u = y.end..(y.end + (width / 2) * height);
let v = u.end..(u.end + (width / 2) * height);
Ok(PlaneRanges { y, u, v })
}
Self::I440 => {
check_dim_mult_2(width, Error::HeightNotMultipleOfTwo)?;
let y = 0..(width * height);
let u = y.end..(y.end + width * (height / 2));
let v = u.end..(u.end + width * (height / 2));
Ok(PlaneRanges { y, u, v })
}
Self::I444 => {
let y = 0..(width * height);
let u = y.end..(y.end + width * height);
let v = u.end..(u.end + width * height);
Ok(PlaneRanges { y, u, v })
}
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
struct PlaneRanges {
y: std::ops::Range<usize>,
u: std::ops::Range<usize>,
v: std::ops::Range<usize>,
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct YUVImageData<'a, T: YUVPixelType> {
format: ImageFormat,
width: usize,
height: usize,
y: &'a [T],
y_stride: usize,
u: &'a [T],
u_stride: usize,
v: &'a [T],
v_stride: usize,
}
impl<'a, T: YUVPixelType> YUVImageData<'a, T> {
pub(crate) fn from_vpx_image(img: &'a vpx_sys::vpx_image) -> Self {
let format = ImageFormat::from_vpx::<T>(img.fmt)
.expect("Internal image decoding error");
let ranges =
format.plane_ranges(img.w as usize, img.h as usize).unwrap();
let y = img.planes[vpx_sys::VPX_PLANE_Y as usize] as *const T;
let y_stride = img.stride[vpx_sys::VPX_PLANE_Y as usize] as usize;
let y_sz = ranges.y.end - ranges.y.start;
let (u_plane, v_plane) =
if img.fmt == vpx_sys::vpx_img_fmt::VPX_IMG_FMT_YV12 {
(vpx_sys::VPX_PLANE_V as usize, vpx_sys::VPX_PLANE_U as usize)
} else {
(vpx_sys::VPX_PLANE_U as usize, vpx_sys::VPX_PLANE_V as usize)
};
let u = img.planes[u_plane] as *const T;
let u_stride = img.stride[u_plane] as usize;
let u_sz = ranges.u.end - ranges.u.start;
let v = img.planes[v_plane] as *const T;
let v_stride = img.stride[v_plane] as usize;
let v_sz = ranges.v.end - ranges.v.start;
unsafe {
Self {
format,
width: img.d_w as usize,
height: img.d_h as usize,
y: std::slice::from_raw_parts(y, y_sz),
y_stride,
u: std::slice::from_raw_parts(u, u_sz),
u_stride,
v: std::slice::from_raw_parts(v, v_sz),
v_stride,
}
}
}
pub fn from_raw_data(
format: ImageFormat,
width: usize,
height: usize,
data: &'a [T],
) -> Result<Self> {
let buflen_req = format.buffer_len(width, height)?;
if data.len() != buflen_req {
return Err(Error::ImageDataBadLength {
expected: buflen_req,
received: data.len(),
});
}
let ranges = format.plane_ranges(width, height)?;
Ok(Self {
format,
width,
height,
y: &data[ranges.y.clone()],
y_stride: (ranges.y.end - ranges.y.start) / height,
u: &data[ranges.u.clone()],
u_stride: (ranges.u.end - ranges.u.start) / height,
v: &data[ranges.v.clone()],
v_stride: (ranges.v.end - ranges.v.start) / height,
})
}
pub(crate) unsafe fn vpx_img_wrap(&self) -> Result<vpx_sys::vpx_image> {
unsafe {
let mut img = MaybeUninit::zeroed().assume_init();
let result = vpx_sys::vpx_img_wrap(
&mut img,
self.format.as_vpx_image::<T>(),
self.width as u32,
self.height as u32,
align_of::<T>() as u32,
self.y.as_ptr() as *mut u8,
);
assert!(!result.is_null());
Ok(img)
}
}
pub fn format(&self) -> ImageFormat {
self.format
}
pub fn width(&self) -> usize {
self.width
}
pub fn height(&self) -> usize {
self.height
}
pub fn planes(&self) -> YUVImagePlanes<T> {
if self.u.as_ptr() == self.v.as_ptr() {
assert_eq!(self.u.len(), self.v.len());
YUVImagePlanes {
y: self.y,
y_stride: self.y_stride,
uv: UVImagePlanes::Interleaved(UVImagePlanesInterleaved {
uv: self.u,
uv_stride: self.u_stride,
}),
}
} else {
YUVImagePlanes {
y: self.y,
y_stride: self.y_stride,
uv: UVImagePlanes::Separate(UVImagePlanesSeparate {
u: self.u,
u_stride: self.u_stride,
v: self.v,
v_stride: self.v_stride,
}),
}
}
}
pub fn to_owned(&self) -> YUVImageDataOwned<T> {
let buflen = if matches!(self.format, ImageFormat::NV12) {
self.y.len() + self.u.len()
} else {
self.y.len() + self.u.len() + self.v.len()
};
let mut buf = Vec::with_capacity(buflen);
buf.extend(self.y.iter().copied());
let y_plane = 0..self.y.len();
let (u_plane, v_plane) = match self.format {
ImageFormat::YV12 => {
buf.extend(self.v);
buf.extend(self.u);
let v_plane = y_plane.end..(y_plane.end + self.v.len());
let u_plane = v_plane.end..(v_plane.end + self.u.len());
(u_plane, v_plane)
}
ImageFormat::NV12 => {
assert_eq!(self.u.as_ptr(), self.v.as_ptr());
assert_eq!(self.u.len(), self.v.len());
buf.extend(self.u);
let u_plane = y_plane.end..(y_plane.end + self.u.len());
let v_plane = u_plane.clone();
(u_plane, v_plane)
}
_ => {
buf.extend(self.u);
buf.extend(self.v);
let u_plane = y_plane.end..(y_plane.end + self.u.len());
let v_plane = u_plane.end..(u_plane.end + self.v.len());
(u_plane, v_plane)
}
};
YUVImageDataOwned {
format: self.format,
width: self.width,
height: self.height,
buf,
planes: PlaneRanges {
y: y_plane,
u: u_plane,
v: v_plane,
},
y_stride: self.y_stride,
u_stride: self.u_stride,
v_stride: self.v_stride,
}
}
}
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct YUVImageDataOwned<T: YUVPixelType> {
format: ImageFormat,
width: usize,
height: usize,
buf: Vec<T>,
planes: PlaneRanges,
y_stride: usize,
u_stride: usize,
v_stride: usize,
}
impl<T: YUVPixelType> YUVImageDataOwned<T> {
pub fn from_raw_data(
format: ImageFormat,
width: usize,
height: usize,
data: &[T],
) -> Result<Self> {
Self::from_raw_data_vec(format, width, height, data.to_vec())
}
pub fn from_raw_data_vec(
format: ImageFormat,
width: usize,
height: usize,
data: Vec<T>,
) -> Result<Self> {
let buflen_req = format.buffer_len(width, height)?;
if data.len() != buflen_req {
return Err(Error::ImageDataBadLength {
expected: buflen_req,
received: data.len(),
});
}
Ok(Self {
format,
width,
height,
buf: data,
planes: format.plane_ranges(width, height)?,
y_stride: 0,
u_stride: 0,
v_stride: 0,
})
}
pub fn new_empty(
format: ImageFormat,
width: usize,
height: usize,
) -> Result<Self> {
Self::from_raw_data_vec(
format,
width,
height,
vec![T::default(); format.buffer_len(width, height)?],
)
}
pub fn format(&self) -> ImageFormat {
self.format
}
pub fn width(&self) -> usize {
self.width
}
pub fn height(&self) -> usize {
self.height
}
pub fn planes(&self) -> YUVImagePlanes<T> {
if self.planes.u == self.planes.v {
YUVImagePlanes {
y: &self.buf[self.planes.y.clone()],
y_stride: self.y_stride,
uv: UVImagePlanes::Interleaved(UVImagePlanesInterleaved {
uv: &self.buf[self.planes.u.clone()],
uv_stride: 2 * self.u_stride,
}),
}
} else {
YUVImagePlanes {
y: &self.buf[self.planes.y.clone()],
y_stride: self.y_stride,
uv: UVImagePlanes::Separate(UVImagePlanesSeparate {
u: &self.buf[self.planes.u.clone()],
u_stride: self.u_stride,
v: &self.buf[self.planes.v.clone()],
v_stride: self.v_stride,
}),
}
}
}
pub fn planes_mut(&mut self) -> YUVImagePlanesMut<T> {
let (y, uv) = self.buf.split_at_mut(self.planes.y.end);
if self.planes.u == self.planes.v {
YUVImagePlanesMut {
y,
y_stride: self.y_stride,
uv: UVImagePlanesMut::Interleaved(
UVImagePlanesInterleavedMut {
uv,
uv_stride: 2 * self.u_stride,
},
),
}
} else {
let (u, v) = if self.planes.u.start < self.planes.v.start {
uv.split_at_mut(self.planes.u.end - self.planes.u.start)
} else {
let (v, u) =
uv.split_at_mut(self.planes.v.end - self.planes.v.start);
(u, v)
};
YUVImagePlanesMut {
y,
y_stride: self.y_stride,
uv: UVImagePlanesMut::Separate(UVImagePlanesSeparateMut {
u,
u_stride: self.u_stride,
v,
v_stride: self.v_stride,
}),
}
}
}
pub fn as_raw_data(&self) -> &[T] {
&self.buf
}
pub fn as_raw_data_mut(&mut self) -> &mut [T] {
&mut self.buf
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct YUVImagePlanes<'a, T: YUVPixelType> {
pub y: &'a [T],
y_stride: usize,
pub uv: UVImagePlanes<'a, T>,
}
impl<T: YUVPixelType> YUVImagePlanes<'_, T> {
#[inline]
pub fn y_stride(&self) -> usize {
self.y_stride
}
}
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct YUVImagePlanesMut<'a, T: YUVPixelType> {
pub y: &'a mut [T],
y_stride: usize,
pub uv: UVImagePlanesMut<'a, T>,
}
impl<T: YUVPixelType> YUVImagePlanesMut<'_, T> {
#[inline]
pub fn y_stride(&self) -> usize {
self.y_stride
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum UVImagePlanes<'a, T: YUVPixelType> {
Separate(UVImagePlanesSeparate<'a, T>),
Interleaved(UVImagePlanesInterleaved<'a, T>),
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct UVImagePlanesSeparate<'a, T: YUVPixelType> {
pub u: &'a [T],
u_stride: usize,
pub v: &'a [T],
v_stride: usize,
}
impl<T: YUVPixelType> UVImagePlanesSeparate<'_, T> {
#[inline]
pub fn u_stride(&self) -> usize {
self.u_stride
}
#[inline]
pub fn v_stride(&self) -> usize {
self.v_stride
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct UVImagePlanesInterleaved<'a, T: PartialEq + Eq> {
pub uv: &'a [T],
uv_stride: usize,
}
impl<T: YUVPixelType> UVImagePlanesInterleaved<'_, T> {
#[inline]
pub fn uv_stride(&self) -> usize {
self.uv_stride
}
}
#[derive(Debug, PartialEq, Eq, Hash)]
pub enum UVImagePlanesMut<'a, T: YUVPixelType> {
Separate(UVImagePlanesSeparateMut<'a, T>),
Interleaved(UVImagePlanesInterleavedMut<'a, T>),
}
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct UVImagePlanesSeparateMut<'a, T: YUVPixelType> {
pub u: &'a mut [T],
u_stride: usize,
pub v: &'a mut [T],
v_stride: usize,
}
impl<T: YUVPixelType> UVImagePlanesSeparateMut<'_, T> {
#[inline]
pub fn u_stride(&self) -> usize {
self.u_stride
}
#[inline]
pub fn v_stride(&self) -> usize {
self.v_stride
}
}
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct UVImagePlanesInterleavedMut<'a, T: YUVPixelType> {
pub uv: &'a mut [T],
uv_stride: usize,
}
impl<T: YUVPixelType> UVImagePlanesInterleavedMut<'_, T> {
#[inline]
pub fn uv_stride(&self) -> usize {
self.uv_stride
}
}
#[cfg(test)]
mod test {
use super::{ImageFormat, ImageFormat::*, YUVImageData};
use super::{UVImagePlanes, UVImagePlanesMut, YUVImagePlanes};
use crate::image::{YUVImagePlanesMut, YUVPixelType};
use crate::{Result, YUVImageDataOwned};
const WIDTH: usize = 4;
const HEIGHT: usize = 4;
const Y_PIXEL_START: usize = 0;
const U_PIXEL_START: usize = 16;
const V_PIXEL_START: usize = 32;
const DATA_I420: [u8; 24] = [
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 32, 33, 34, 35,
];
const DATA_YV12_REV: [u8; 24] = [
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
32, 33, 34, 35, 16, 17, 18, 19,
];
const DATA_NV12: [u8; 24] = [
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 32, 17, 33, 18, 34, 19, 35,
];
const DATA_I420_16: [u16; 24] = [
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 32, 33, 34, 35,
];
const DATA_I422: [u8; 32] = [
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 32, 33, 34, 35, 36, 37, 38, 39,
];
const DATA_I422_16: [u16; 32] = [
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 32, 33, 34, 35, 36, 37, 38, 39,
];
const DATA_I444: [u8; 48] = [
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
];
const DATA_I444_16: [u16; 48] = [
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
];
#[test]
fn test_image_format_buffer_len() -> Result<()> {
assert_eq!(ImageFormat::YV12.buffer_len(4, 4)?, DATA_I420.len());
assert_eq!(ImageFormat::NV12.buffer_len(4, 4)?, DATA_I420.len());
assert_eq!(I420.buffer_len(4, 4)?, DATA_I420.len());
assert_eq!(I422.buffer_len(4, 4)?, DATA_I422.len());
assert_eq!(I440.buffer_len(4, 4)?, DATA_I422.len());
assert_eq!(I444.buffer_len(4, 4)?, DATA_I444.len());
Ok(())
}
#[test]
fn test_image_format_sizes() -> Result<()> {
assert!(YV12.buffer_len(1, 2).is_err());
assert!(YV12.buffer_len(2, 1).is_err());
assert!(YV12.buffer_len(2, 2).is_ok());
assert!(NV12.buffer_len(1, 2).is_err());
assert!(NV12.buffer_len(2, 1).is_err());
assert!(NV12.buffer_len(2, 2).is_ok());
assert!(I420.buffer_len(1, 2).is_err());
assert!(I420.buffer_len(2, 1).is_err());
assert!(I420.buffer_len(2, 2).is_ok());
assert!(I422.buffer_len(1, 2).is_err());
assert!(I422.buffer_len(2, 1).is_ok());
assert!(I440.buffer_len(1, 2).is_ok());
assert!(I440.buffer_len(2, 1).is_err());
assert!(I444.buffer_len(1, 2).is_ok());
assert!(I444.buffer_len(2, 1).is_ok());
Ok(())
}
#[test]
fn test_imagedata_metainfo() -> Result<()> {
let img = YUVImageData::from_raw_data(YV12, WIDTH, HEIGHT, &DATA_I420)?;
assert_eq!(img.format(), YV12);
assert_eq!(img.width(), WIDTH);
assert_eq!(img.height(), HEIGHT);
let img = YUVImageData::from_raw_data(
ImageFormat::I420,
WIDTH,
HEIGHT,
&DATA_I420_16,
)?;
assert_eq!(img.format(), I420);
assert_eq!(img.width(), WIDTH);
assert_eq!(img.height(), HEIGHT);
Ok(())
}
#[test]
fn test_imagedata_planes() -> Result<()> {
fn test_img<T: YUVPixelType + Into<usize>>(
format: ImageFormat,
width: usize,
height: usize,
separate_planes: bool,
data: &[T],
chroma_sub_fact: usize,
) -> Result<()> {
let img = YUVImageData::from_raw_data(format, width, height, data)?;
test_planes_impl(
width,
height,
&img.planes(),
separate_planes,
chroma_sub_fact,
)
}
fn test_img_owned<T: YUVPixelType + Into<usize>>(
format: ImageFormat,
width: usize,
height: usize,
separate_planes: bool,
data: &[T],
chroma_sub_fact: usize,
) -> Result<()> {
let mut img =
YUVImageDataOwned::from_raw_data(format, width, height, data)?;
test_planes_impl(
width,
height,
&img.planes(),
separate_planes,
chroma_sub_fact,
)?;
test_planes_mut_impl(
width,
height,
&mut img.planes_mut(),
separate_planes,
chroma_sub_fact,
)
}
fn check_plane_pixels<T: YUVPixelType + Into<usize>>(
plane: &[T],
start_off: usize,
) {
plane.iter().enumerate().for_each(|(idx, pixel)| {
assert_eq!(idx + start_off, (*pixel).into());
});
}
fn test_planes_impl<T: YUVPixelType + Into<usize>>(
width: usize,
height: usize,
planes: &YUVImagePlanes<T>,
separate_planes: bool,
chroma_sub_fact: usize,
) -> Result<()> {
assert_eq!(planes.y.len(), width * height);
check_plane_pixels(planes.y, Y_PIXEL_START);
match &planes.uv {
UVImagePlanes::Separate(uv_planes) => {
assert!(separate_planes);
let req_len = width * height / chroma_sub_fact;
assert_eq!(uv_planes.u.len(), req_len);
check_plane_pixels(uv_planes.u, U_PIXEL_START);
assert_eq!(uv_planes.v.len(), req_len);
check_plane_pixels(uv_planes.v, V_PIXEL_START);
}
UVImagePlanes::Interleaved(uv_plane) => {
assert!(!separate_planes);
let req_len = 2 * width * height / chroma_sub_fact;
assert_eq!(uv_plane.uv.len(), req_len);
uv_plane.uv.chunks(2).enumerate().for_each(
|(idx, pixels)| {
let [u_pixel, v_pixel] = *pixels else {
unreachable!();
};
assert_eq!(idx + U_PIXEL_START, u_pixel.into());
assert_eq!(idx + V_PIXEL_START, v_pixel.into());
},
);
}
};
Ok(())
}
fn test_planes_mut_impl<T: YUVPixelType + Into<usize>>(
width: usize,
height: usize,
planes: &mut YUVImagePlanesMut<T>,
separate_planes: bool,
chroma_sub_fact: usize,
) -> Result<()> {
assert_eq!(planes.y.len(), width * height);
check_plane_pixels(planes.y, Y_PIXEL_START);
match &planes.uv {
UVImagePlanesMut::Separate(uv_planes) => {
assert!(separate_planes);
let req_len = width * height / chroma_sub_fact;
assert_eq!(uv_planes.u.len(), req_len);
check_plane_pixels(uv_planes.u, U_PIXEL_START);
assert_eq!(uv_planes.v.len(), req_len);
check_plane_pixels(uv_planes.v, V_PIXEL_START);
}
UVImagePlanesMut::Interleaved(uv_plane) => {
assert!(!separate_planes);
let req_len = 2 * width * height / chroma_sub_fact;
assert_eq!(uv_plane.uv.len(), req_len);
uv_plane.uv.chunks(2).enumerate().for_each(
|(idx, pixels)| {
let [u_pixel, v_pixel] = *pixels else {
unreachable!();
};
assert_eq!(idx + U_PIXEL_START, u_pixel.into());
assert_eq!(idx + V_PIXEL_START, v_pixel.into());
},
);
}
};
Ok(())
}
test_img(YV12, WIDTH, HEIGHT, true, &DATA_YV12_REV, 4)?;
test_img(NV12, WIDTH, HEIGHT, false, &DATA_NV12, 4)?;
test_img(I420, WIDTH, HEIGHT, true, &DATA_I420, 4)?;
test_img(I422, WIDTH, HEIGHT, true, &DATA_I422, 2)?;
test_img(I440, WIDTH, HEIGHT, true, &DATA_I422, 2)?;
test_img(I444, WIDTH, HEIGHT, true, &DATA_I444, 1)?;
test_img(I420, WIDTH, HEIGHT, true, &DATA_I420_16, 4)?;
test_img(I422, WIDTH, HEIGHT, true, &DATA_I422_16, 2)?;
test_img(I440, WIDTH, HEIGHT, true, &DATA_I422_16, 2)?;
test_img(I444, WIDTH, HEIGHT, true, &DATA_I444_16, 1)?;
test_img_owned(YV12, WIDTH, HEIGHT, true, &DATA_YV12_REV, 4)?;
test_img_owned(NV12, WIDTH, HEIGHT, false, &DATA_NV12, 4)?;
test_img_owned(I420, WIDTH, HEIGHT, true, &DATA_I420, 4)?;
test_img_owned(I422, WIDTH, HEIGHT, true, &DATA_I422, 2)?;
test_img_owned(I440, WIDTH, HEIGHT, true, &DATA_I422, 2)?;
test_img_owned(I444, WIDTH, HEIGHT, true, &DATA_I444, 1)?;
test_img_owned(I420, WIDTH, HEIGHT, true, &DATA_I420_16, 4)?;
test_img_owned(I422, WIDTH, HEIGHT, true, &DATA_I422_16, 2)?;
test_img_owned(I440, WIDTH, HEIGHT, true, &DATA_I422_16, 2)?;
test_img_owned(I444, WIDTH, HEIGHT, true, &DATA_I444_16, 1)?;
Ok(())
}
#[test]
fn test_send() {
fn assert_send<T: Send>() {}
assert_send::<YUVImageData<u8>>();
assert_send::<YUVImageData<u16>>();
assert_send::<YUVImageDataOwned<u8>>();
assert_send::<YUVImageDataOwned<u16>>();
}
#[test]
fn test_sync() {
fn assert_sync<T: Sync>() {}
assert_sync::<YUVImageData<u8>>();
assert_sync::<YUVImageData<u16>>();
assert_sync::<YUVImageDataOwned<u8>>();
assert_sync::<YUVImageDataOwned<u16>>();
}
}