use std::fmt::{Debug, Display, Formatter};
use std::iter::FusedIterator;
use std::marker::PhantomData;
use std::mem::size_of;
use std::ops::{Index, IndexMut, Range};
use super::aligned::AlignedSlice;
use super::math::*;
use super::pixel::*;
#[cfg(feature = "serialize")]
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
pub struct PlaneConfig {
pub stride: usize,
pub alloc_height: usize,
pub width: usize,
pub height: usize,
pub xdec: usize,
pub ydec: usize,
pub xpad: usize,
pub ypad: usize,
pub xorigin: usize,
pub yorigin: usize,
}
impl PlaneConfig {
const STRIDE_ALIGNMENT_LOG2: usize = 6;
#[inline]
pub fn new(
width: usize, height: usize, xdec: usize, ydec: usize, xpad: usize,
ypad: usize, type_size: usize,
) -> Self {
let xorigin =
xpad.align_power_of_two(Self::STRIDE_ALIGNMENT_LOG2 + 1 - type_size);
let yorigin = ypad;
let stride = (xorigin + width + xpad)
.align_power_of_two(Self::STRIDE_ALIGNMENT_LOG2 + 1 - type_size);
let alloc_height = yorigin + height + ypad;
PlaneConfig {
stride,
alloc_height,
width,
height,
xdec,
ydec,
xpad,
ypad,
xorigin,
yorigin,
}
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct PlaneOffset {
pub x: isize,
pub y: isize,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
pub struct PlaneData<T: Pixel> {
data: AlignedSlice<T, { 1 << 6 }>,
}
impl<T: Pixel> std::ops::Deref for PlaneData<T> {
type Target = [T];
fn deref(&self) -> &[T] {
&self.data
}
}
impl<T: Pixel> std::ops::DerefMut for PlaneData<T> {
fn deref_mut(&mut self) -> &mut [T] {
&mut self.data
}
}
impl<T: Pixel> PlaneData<T> {
pub fn new(len: usize) -> Self {
Self { data: AlignedSlice::from_elem(len, T::cast_from(128)) }
}
fn from_slice(data: &[T]) -> Self {
Self { data: AlignedSlice::from_slice(data) }
}
}
#[derive(Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
pub struct Plane<T: Pixel> {
pub data: PlaneData<T>,
pub cfg: PlaneConfig,
}
impl<T: Pixel> Debug for Plane<T>
where
T: Display,
{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "Plane {{ data: [{}, ...], cfg: {:?} }}", self.data[0], self.cfg)
}
}
impl<T: Pixel> Plane<T> {
pub fn new(
width: usize, height: usize, xdec: usize, ydec: usize, xpad: usize,
ypad: usize,
) -> Self {
let cfg =
PlaneConfig::new(width, height, xdec, ydec, xpad, ypad, size_of::<T>());
let data = PlaneData::new(cfg.stride * cfg.alloc_height);
Plane { data, cfg }
}
pub fn from_slice(data: &[T], stride: usize) -> Self {
let len = data.len();
assert!(len % stride == 0);
Self {
data: PlaneData::from_slice(data),
cfg: PlaneConfig {
stride,
alloc_height: len / stride,
width: stride,
height: len / stride,
xdec: 0,
ydec: 0,
xpad: 0,
ypad: 0,
xorigin: 0,
yorigin: 0,
},
}
}
pub fn pad(&mut self, w: usize, h: usize) {
let xorigin = self.cfg.xorigin;
let yorigin = self.cfg.yorigin;
let stride = self.cfg.stride;
let alloc_height = self.cfg.alloc_height;
let width = (w + self.cfg.xdec) >> self.cfg.xdec;
let height = (h + self.cfg.ydec) >> self.cfg.ydec;
if xorigin > 0 {
for y in 0..height {
let base = (yorigin + y) * stride;
let fill_val = self.data[base + xorigin];
self.data[base..base + xorigin].fill(fill_val);
}
}
if xorigin + width < stride {
for y in 0..height {
let base = (yorigin + y) * stride + xorigin + width;
let fill_val = self.data[base - 1];
self.data[base..base + stride - (xorigin + width)].fill(fill_val);
}
}
if yorigin > 0 {
let (top, bottom) = self.data.split_at_mut(yorigin * stride);
let src = &bottom[..stride];
for y in 0..yorigin {
let dst = &mut top[y * stride..(y + 1) * stride];
dst.copy_from_slice(src);
}
}
if yorigin + height < self.cfg.alloc_height {
let (top, bottom) = self.data.split_at_mut((yorigin + height) * stride);
let src = &top[(yorigin + height - 1) * stride..];
for y in 0..alloc_height - (yorigin + height) {
let dst = &mut bottom[y * stride..(y + 1) * stride];
dst.copy_from_slice(src);
}
}
}
pub fn probe_padding(&self, w: usize, h: usize) -> bool {
let PlaneConfig {
xorigin, yorigin, stride, alloc_height, xdec, ydec, ..
} = self.cfg;
let width = (w + xdec) >> xdec;
let height = (h + ydec) >> ydec;
let corner = (yorigin + height - 1) * stride + xorigin + width - 1;
let corner_value = self.data[corner];
self.data[(yorigin + height) * stride - 1] == corner_value
&& self.data[(alloc_height - 1) * stride + xorigin + width - 1]
== corner_value
&& self.data[alloc_height * stride - 1] == corner_value
}
pub fn slice(&self, po: PlaneOffset) -> PlaneSlice<'_, T> {
PlaneSlice { plane: self, x: po.x, y: po.y }
}
pub fn mut_slice(&mut self, po: PlaneOffset) -> PlaneMutSlice<'_, T> {
PlaneMutSlice { plane: self, x: po.x, y: po.y }
}
#[inline]
fn index(&self, x: usize, y: usize) -> usize {
(y + self.cfg.yorigin) * self.cfg.stride + (x + self.cfg.xorigin)
}
#[inline]
pub fn row_range_cropped(&self, x: isize, y: isize) -> Range<usize> {
debug_assert!(self.cfg.yorigin as isize + y >= 0);
debug_assert!(self.cfg.xorigin as isize + x >= 0);
let base_y = (self.cfg.yorigin as isize + y) as usize;
let base_x = (self.cfg.xorigin as isize + x) as usize;
let base = base_y * self.cfg.stride + base_x;
let width = (self.cfg.width as isize - x) as usize;
base..base + width
}
#[inline]
pub fn row_range(&self, x: isize, y: isize) -> Range<usize> {
debug_assert!(self.cfg.yorigin as isize + y >= 0);
debug_assert!(self.cfg.xorigin as isize + x >= 0);
let base_y = (self.cfg.yorigin as isize + y) as usize;
let base_x = (self.cfg.xorigin as isize + x) as usize;
let base = base_y * self.cfg.stride + base_x;
let width = self.cfg.stride - base_x;
base..base + width
}
pub fn p(&self, x: usize, y: usize) -> T {
self.data[self.index(x, y)]
}
pub fn data_origin(&self) -> &[T] {
&self.data[self.index(0, 0)..]
}
pub fn data_origin_mut(&mut self) -> &mut [T] {
let i = self.index(0, 0);
&mut self.data[i..]
}
pub fn copy_from_raw_u8(
&mut self, source: &[u8], source_stride: usize, source_bytewidth: usize,
) {
let stride = self.cfg.stride;
assert!(stride != 0);
assert!(source_stride != 0);
for (self_row, source_row) in self
.data_origin_mut()
.chunks_exact_mut(stride)
.zip(source.chunks_exact(source_stride))
{
match source_bytewidth {
1 => {
for (self_pixel, source_pixel) in
self_row.iter_mut().zip(source_row.iter())
{
*self_pixel = T::cast_from(*source_pixel);
}
}
2 => {
assert!(
size_of::<T>() == 2,
"source bytewidth ({source_bytewidth}) cannot fit in Plane<u8>",
);
debug_assert!(T::type_enum() == PixelType::U16);
let self_row: &mut [u16] = unsafe { std::mem::transmute(self_row) };
let source_row: &[[u8; 2]] = unsafe {
std::slice::from_raw_parts(
source_row.as_ptr().cast(),
source_row.len() / 2,
)
};
for (self_pixel, bytes) in self_row.iter_mut().zip(source_row) {
*self_pixel = u16::from_le_bytes(*bytes);
}
}
_ => {}
}
}
}
pub fn copy_to_raw_u8(
&self, dest: &mut [u8], dest_stride: usize, dest_bytewidth: usize,
) {
let stride = self.cfg.stride;
for (self_row, dest_row) in self
.data_origin()
.chunks_exact(stride)
.zip(dest.chunks_exact_mut(dest_stride))
{
match dest_bytewidth {
1 => {
for (self_pixel, dest_pixel) in
self_row[..self.cfg.width].iter().zip(dest_row.iter_mut())
{
*dest_pixel = u8::cast_from(*self_pixel);
}
}
2 => {
assert!(
size_of::<T>() >= 2,
"dest bytewidth ({dest_bytewidth}) cannot fit in Plane<u8>",
);
let dest_row: &mut [[u8; 2]] = unsafe {
std::slice::from_raw_parts_mut(
dest_row.as_mut_ptr().cast(),
dest_row.len() / 2,
)
};
for (self_pixel, bytes) in
self_row[..self.cfg.width].iter().zip(dest_row)
{
*bytes = u16::cast_from(*self_pixel).to_le_bytes();
}
}
_ => {}
}
}
}
pub fn downsampled(
&self, frame_width: usize, frame_height: usize,
) -> Plane<T> {
let src = self;
let mut new = Plane::new(
src.cfg.width.div_ceil(2),
src.cfg.height.div_ceil(2),
src.cfg.xdec + 1,
src.cfg.ydec + 1,
src.cfg.xpad / 2,
src.cfg.ypad / 2,
);
let width = new.cfg.width;
let height = new.cfg.height;
assert!(width * 2 <= src.cfg.stride - src.cfg.xorigin);
assert!(height * 2 <= src.cfg.alloc_height - src.cfg.yorigin);
let data_origin = src.data_origin();
for (row_idx, dst_row) in new
.mut_slice(PlaneOffset::default())
.rows_iter_mut()
.enumerate()
.take(height)
{
let src_top_row =
&data_origin[(src.cfg.stride * row_idx * 2)..][..(2 * width)];
let src_bottom_row =
&data_origin[(src.cfg.stride * (row_idx * 2 + 1))..][..(2 * width)];
for ((dst, a), b) in dst_row
.iter_mut()
.zip(src_top_row.chunks_exact(2))
.zip(src_bottom_row.chunks_exact(2))
{
let sum = u32::cast_from(a[0])
+ u32::cast_from(a[1])
+ u32::cast_from(b[0])
+ u32::cast_from(b[1]);
let avg = (sum + 2) >> 2;
*dst = T::cast_from(avg);
}
}
new.pad(frame_width, frame_height);
new
}
pub fn downscale<const SCALE: usize>(&self) -> Plane<T> {
let mut new_plane =
Plane::new(self.cfg.width / SCALE, self.cfg.height / SCALE, 0, 0, 0, 0);
self.downscale_in_place::<SCALE>(&mut new_plane);
new_plane
}
pub fn downscale_in_place<const SCALE: usize>(
&self, in_plane: &mut Plane<T>,
) {
let stride = in_plane.cfg.stride;
let width = in_plane.cfg.width;
let height = in_plane.cfg.height;
if stride == 0 || self.cfg.stride == 0 {
panic!("stride cannot be 0");
}
assert!(width * SCALE <= self.cfg.stride - self.cfg.xorigin);
assert!(height * SCALE <= self.cfg.alloc_height - self.cfg.yorigin);
unsafe {
let src = self;
let box_pixels = SCALE * SCALE;
let half_box_pixels = box_pixels as u32 / 2;
let data_origin = src.data_origin();
let plane_data_mut_slice = &mut *in_plane.data;
for row_idx in 0..height {
let dst_row =
plane_data_mut_slice.get_unchecked_mut(row_idx * stride..);
for (col_idx, dst) in
dst_row.get_unchecked_mut(..width).iter_mut().enumerate()
{
macro_rules! generate_inner_loop {
($x:ty) => {
let mut sum = half_box_pixels as $x;
for y in 0..SCALE {
let src_row_idx = row_idx * SCALE + y;
let src_row =
data_origin.get_unchecked((src_row_idx * src.cfg.stride)..);
for x in 0..SCALE {
let src_col_idx = col_idx * SCALE + x;
sum += <$x>::cast_from(*src_row.get_unchecked(src_col_idx));
}
}
let avg = sum as usize / box_pixels;
*dst = T::cast_from(avg);
};
}
if T::type_enum() == PixelType::U8
&& SCALE as u128 * SCALE as u128 * (u8::MAX as u128)
+ half_box_pixels as u128
<= u16::MAX as u128
{
generate_inner_loop!(u16);
} else {
generate_inner_loop!(u32);
}
}
}
}
}
pub fn iter(&self) -> PlaneIter<'_, T> {
PlaneIter::new(self)
}
pub fn rows_iter(&self) -> RowsIter<'_, T> {
RowsIter { plane: self, x: 0, y: 0 }
}
pub fn rows_iter_mut(&mut self) -> RowsIterMut<'_, T> {
RowsIterMut {
plane: self as *mut Plane<T>,
x: 0,
y: 0,
phantom: PhantomData,
}
}
pub fn row(&self, y: isize) -> &[T] {
let range = self.row_range(0, y);
&self.data[range]
}
}
#[derive(Debug)]
pub struct PlaneIter<'a, T: Pixel> {
plane: &'a Plane<T>,
y: usize,
x: usize,
}
impl<'a, T: Pixel> PlaneIter<'a, T> {
pub fn new(plane: &'a Plane<T>) -> Self {
Self { plane, y: 0, x: 0 }
}
fn width(&self) -> usize {
self.plane.cfg.width
}
fn height(&self) -> usize {
self.plane.cfg.height
}
}
impl<T: Pixel> Iterator for PlaneIter<'_, T> {
type Item = T;
fn next(&mut self) -> Option<<Self as Iterator>::Item> {
if self.y == self.height() {
return None;
}
let pixel = self.plane.p(self.x, self.y);
if self.x == self.width() - 1 {
self.x = 0;
self.y += 1;
} else {
self.x += 1;
}
Some(pixel)
}
}
impl<T: Pixel> FusedIterator for PlaneIter<'_, T> {}
#[derive(Clone, Copy, Debug)]
pub struct PlaneSlice<'a, T: Pixel> {
pub plane: &'a Plane<T>,
pub x: isize,
pub y: isize,
}
pub struct RowsIter<'a, T: Pixel> {
plane: &'a Plane<T>,
x: isize,
y: isize,
}
impl<'a, T: Pixel> Iterator for RowsIter<'a, T> {
type Item = &'a [T];
fn next(&mut self) -> Option<Self::Item> {
if self.plane.cfg.height as isize > self.y {
let range = self.plane.row_range_cropped(self.x, self.y);
self.y += 1;
Some(&self.plane.data[range])
} else {
None
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.plane.cfg.height as isize - self.y;
debug_assert!(remaining >= 0);
let remaining = remaining as usize;
(remaining, Some(remaining))
}
}
impl<T: Pixel> ExactSizeIterator for RowsIter<'_, T> {}
impl<T: Pixel> FusedIterator for RowsIter<'_, T> {}
impl<'a, T: Pixel> PlaneSlice<'a, T> {
#[allow(unused)]
pub fn as_ptr(&self) -> *const T {
self[0].as_ptr()
}
pub fn rows_iter(&self) -> RowsIter<'_, T> {
RowsIter { plane: self.plane, x: self.x, y: self.y }
}
pub fn clamp(&self) -> PlaneSlice<'a, T> {
PlaneSlice {
plane: self.plane,
x: self.x.clamp(
-(self.plane.cfg.xorigin as isize),
self.plane.cfg.width as isize,
),
y: self.y.clamp(
-(self.plane.cfg.yorigin as isize),
self.plane.cfg.height as isize,
),
}
}
pub fn subslice(&self, xo: usize, yo: usize) -> PlaneSlice<'a, T> {
PlaneSlice {
plane: self.plane,
x: self.x + xo as isize,
y: self.y + yo as isize,
}
}
pub fn reslice(&self, xo: isize, yo: isize) -> PlaneSlice<'a, T> {
PlaneSlice { plane: self.plane, x: self.x + xo, y: self.y + yo }
}
pub fn go_up(&self, i: usize) -> PlaneSlice<'a, T> {
PlaneSlice { plane: self.plane, x: self.x, y: self.y - i as isize }
}
pub fn go_left(&self, i: usize) -> PlaneSlice<'a, T> {
PlaneSlice { plane: self.plane, x: self.x - i as isize, y: self.y }
}
pub fn p(&self, add_x: usize, add_y: usize) -> T {
let new_y =
(self.y + add_y as isize + self.plane.cfg.yorigin as isize) as usize;
let new_x =
(self.x + add_x as isize + self.plane.cfg.xorigin as isize) as usize;
self.plane.data[new_y * self.plane.cfg.stride + new_x]
}
pub fn accessible(&self, add_x: usize, add_y: usize) -> bool {
let y =
(self.y + add_y as isize + self.plane.cfg.yorigin as isize) as usize;
let x =
(self.x + add_x as isize + self.plane.cfg.xorigin as isize) as usize;
y < self.plane.cfg.alloc_height && x < self.plane.cfg.stride
}
pub fn accessible_neg(&self, sub_x: usize, sub_y: usize) -> bool {
let y = self.y - sub_y as isize + self.plane.cfg.yorigin as isize;
let x = self.x - sub_x as isize + self.plane.cfg.xorigin as isize;
y >= 0 && x >= 0
}
pub fn row_cropped(&self, y: usize) -> &[T] {
let y = (self.y + y as isize + self.plane.cfg.yorigin as isize) as usize;
let x = (self.x + self.plane.cfg.xorigin as isize) as usize;
let start = y * self.plane.cfg.stride + x;
let width = (self.plane.cfg.width as isize - self.x) as usize;
&self.plane.data[start..start + width]
}
pub fn row(&self, y: usize) -> &[T] {
let y = (self.y + y as isize + self.plane.cfg.yorigin as isize) as usize;
let x = (self.x + self.plane.cfg.xorigin as isize) as usize;
let start = y * self.plane.cfg.stride + x;
let width = self.plane.cfg.stride - x;
&self.plane.data[start..start + width]
}
}
impl<T: Pixel> Index<usize> for PlaneSlice<'_, T> {
type Output = [T];
fn index(&self, index: usize) -> &Self::Output {
let range = self.plane.row_range(self.x, self.y + index as isize);
&self.plane.data[range]
}
}
pub struct PlaneMutSlice<'a, T: Pixel> {
pub plane: &'a mut Plane<T>,
pub x: isize,
pub y: isize,
}
pub struct RowsIterMut<'a, T: Pixel> {
plane: *mut Plane<T>,
x: isize,
y: isize,
phantom: PhantomData<&'a mut Plane<T>>,
}
impl<'a, T: Pixel> Iterator for RowsIterMut<'a, T> {
type Item = &'a mut [T];
fn next(&mut self) -> Option<Self::Item> {
let plane = unsafe { &mut *self.plane };
if plane.cfg.height as isize > self.y {
let range = plane.row_range_cropped(self.x, self.y);
self.y += 1;
Some(&mut plane.data[range])
} else {
None
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let plane = unsafe { &mut *self.plane };
let remaining = plane.cfg.height as isize - self.y;
debug_assert!(remaining >= 0);
let remaining = remaining as usize;
(remaining, Some(remaining))
}
}
impl<T: Pixel> ExactSizeIterator for RowsIterMut<'_, T> {}
impl<T: Pixel> FusedIterator for RowsIterMut<'_, T> {}
impl<T: Pixel> PlaneMutSlice<'_, T> {
#[allow(unused)]
pub fn rows_iter(&self) -> RowsIter<'_, T> {
RowsIter { plane: self.plane, x: self.x, y: self.y }
}
pub fn rows_iter_mut(&mut self) -> RowsIterMut<'_, T> {
RowsIterMut {
plane: self.plane as *mut Plane<T>,
x: self.x,
y: self.y,
phantom: PhantomData,
}
}
#[allow(unused)]
pub fn subslice(&mut self, xo: usize, yo: usize) -> PlaneMutSlice<'_, T> {
PlaneMutSlice {
plane: self.plane,
x: self.x + xo as isize,
y: self.y + yo as isize,
}
}
}
impl<T: Pixel> Index<usize> for PlaneMutSlice<'_, T> {
type Output = [T];
fn index(&self, index: usize) -> &Self::Output {
let range = self.plane.row_range(self.x, self.y + index as isize);
&self.plane.data[range]
}
}
impl<T: Pixel> IndexMut<usize> for PlaneMutSlice<'_, T> {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
let range = self.plane.row_range(self.x, self.y + index as isize);
&mut self.plane.data[range]
}
}
#[cfg(test)]
pub mod test {
use super::*;
#[test]
fn plane_data_is_aligned_to_64_bytes() {
for (width, height, pad) in
[(1, 1, 0), (7, 3, 0), (64, 48, 16), (65, 49, 32)]
{
let narrow = Plane::<u8>::new(width, height, 0, 0, pad, pad);
assert_eq!(narrow.data.as_ptr() as usize % 64, 0);
let wide = Plane::<u16>::new(width, height, 0, 0, pad, pad);
assert_eq!(wide.data.as_ptr() as usize % 64, 0);
}
let plane = Plane::from_slice(&[1u8; 64], 8);
assert_eq!(plane.data.as_ptr() as usize % 64, 0);
assert_eq!(plane.clone().data.as_ptr() as usize % 64, 0);
}
#[test]
fn copy_from_raw_u8() {
#[rustfmt::skip]
let mut plane = Plane::from_slice(&[
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 2, 3, 4, 0, 0,
0, 0, 8, 7, 6, 5, 0, 0,
0, 0, 9, 8, 7, 6, 0, 0,
0, 0, 2, 3, 4, 5, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
], 8);
let input = vec![42u8; 64];
plane.copy_from_raw_u8(&input, 8, 1);
println!("{:?}", &plane.data[..10]);
assert_eq!(&input[..64], &plane.data[..64]);
}
#[test]
fn copy_to_raw_u8() {
#[rustfmt::skip]
let plane = Plane::from_slice(&[
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 2, 3, 4, 0, 0,
0, 0, 8, 7, 6, 5, 0, 0,
0, 0, 9, 8, 7, 6, 0, 0,
0, 0, 2, 3, 4, 5, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
], 8);
let mut output = vec![42u8; 64];
plane.copy_to_raw_u8(&mut output, 8, 1);
println!("{:?}", &plane.data[..10]);
assert_eq!(&output[..64], &plane.data[..64]);
}
#[test]
fn test_plane_downsample() {
#[rustfmt::skip]
let plane = Plane::<u8> {
data: PlaneData::from_slice(&[
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 2, 3, 4, 0, 0,
0, 0, 8, 7, 6, 5, 0, 0,
0, 0, 9, 8, 7, 6, 0, 0,
0, 0, 2, 3, 4, 5, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
]),
cfg: PlaneConfig {
stride: 8,
alloc_height: 9,
width: 4,
height: 4,
xdec: 0,
ydec: 0,
xpad: 0,
ypad: 0,
xorigin: 2,
yorigin: 3,
},
};
let downsampled = plane.downsampled(4, 4);
#[rustfmt::skip]
let expected = &[
5, 5,
6, 6,
];
let v: Vec<_> = downsampled.iter().collect();
assert_eq!(&expected[..], &v[..]);
}
#[test]
fn test_plane_downsample_odd() {
#[rustfmt::skip]
let plane = Plane::<u8> {
data: PlaneData::from_slice(&[
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 2, 3, 4, 0, 0,
0, 0, 8, 7, 6, 5, 0, 0,
0, 0, 9, 8, 7, 6, 0, 0,
0, 0, 2, 3, 4, 5, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
]),
cfg: PlaneConfig {
stride: 8,
alloc_height: 9,
width: 3,
height: 3,
xdec: 0,
ydec: 0,
xpad: 0,
ypad: 0,
xorigin: 2,
yorigin: 3,
},
};
let downsampled = plane.downsampled(3, 3);
#[rustfmt::skip]
let expected = &[
5, 5,
6, 6,
];
let v: Vec<_> = downsampled.iter().collect();
assert_eq!(&expected[..], &v[..]);
}
#[test]
fn test_plane_downscale() {
#[rustfmt::skip]
let plane = Plane::<u8> {
data: PlaneData::from_slice(&[
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1, 4, 5, 0, 0,
0, 0, 2, 3, 6, 7, 0, 0,
0, 0, 8, 9, 7, 5, 0, 0,
0, 0, 9, 8, 3, 1, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
]),
cfg: PlaneConfig {
stride: 8,
alloc_height: 9,
width: 4,
height: 4,
xdec: 0,
ydec: 0,
xpad: 0,
ypad: 0,
xorigin: 2,
yorigin: 3,
},
};
let downscaled = plane.downscale::<2>();
#[rustfmt::skip]
let expected = &[
2, 6,
9, 4
];
let v: Vec<_> = downscaled.iter().collect();
assert_eq!(&expected[..], &v[..]);
}
#[test]
fn test_plane_downscale_odd() {
#[rustfmt::skip]
let plane = Plane::<u8> {
data: PlaneData::from_slice(&[
1, 2, 3, 4, 1, 2, 3, 4,
0, 0, 8, 7, 6, 5, 8, 7,
6, 5, 8, 7, 6, 5, 8, 7,
6, 5, 8, 7, 0, 0, 2, 3,
4, 5, 0, 0, 9, 8, 7, 6,
0, 0, 0, 0, 2, 3, 4, 5,
0, 0, 0, 0, 2, 3, 4, 5,
]),
cfg: PlaneConfig {
stride: 8,
alloc_height: 7,
width: 8,
height: 7,
xdec: 0,
ydec: 0,
xpad: 0,
ypad: 0,
xorigin: 0,
yorigin: 0,
},
};
let downscaled = plane.downscale::<3>();
#[rustfmt::skip]
let expected = &[
4, 5,
3, 3
];
let v: Vec<_> = downscaled.iter().collect();
assert_eq!(&expected[..], &v[..]);
}
#[test]
fn test_plane_downscale_odd_2() {
#[rustfmt::skip]
let plane = Plane::<u8> {
data: PlaneData::from_slice(&[
9, 8, 3, 1, 0, 1, 4, 5, 0, 0,
0, 1, 4, 5, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 9, 0,
0, 2, 3, 6, 7, 0, 0, 0, 0, 0,
0, 0, 8, 9, 7, 5, 0, 0, 0, 0,
9, 8, 3, 1, 0, 1, 4, 5, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 2, 3, 6, 7, 0,
0, 0, 0, 0, 0, 0, 8, 9, 7, 5,
0, 0, 0, 0, 9, 8, 3, 1, 0, 0
]),
cfg: PlaneConfig {
stride: 10,
alloc_height: 10,
width: 10,
height: 10,
xdec: 0,
ydec: 0,
xpad: 0,
ypad: 0,
xorigin: 0,
yorigin: 0,
},
};
let downscaled = plane.downscale::<3>();
#[rustfmt::skip]
let expected = &[
3, 1, 2,
4, 4, 1,
0, 0, 4,
];
let v: Vec<_> = downscaled.iter().collect();
assert_eq!(&expected[..], &v[..]);
}
#[test]
fn test_plane_pad() {
#[rustfmt::skip]
let mut plane = Plane::<u8> {
data: PlaneData::from_slice(&[
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 2, 3, 4, 0, 0,
0, 0, 8, 7, 6, 5, 0, 0,
0, 0, 9, 8, 7, 6, 0, 0,
0, 0, 2, 3, 4, 5, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
]),
cfg: PlaneConfig {
stride: 8,
alloc_height: 9,
width: 4,
height: 4,
xdec: 0,
ydec: 0,
xpad: 0,
ypad: 0,
xorigin: 2,
yorigin: 3,
},
};
plane.pad(4, 4);
#[rustfmt::skip]
assert_eq!(&[
1, 1, 1, 2, 3, 4, 4, 4,
1, 1, 1, 2, 3, 4, 4, 4,
1, 1, 1, 2, 3, 4, 4, 4,
1, 1, 1, 2, 3, 4, 4, 4,
8, 8, 8, 7, 6, 5, 5, 5,
9, 9, 9, 8, 7, 6, 6, 6,
2, 2, 2, 3, 4, 5, 5, 5,
2, 2, 2, 3, 4, 5, 5, 5,
2, 2, 2, 3, 4, 5, 5, 5,
], &plane.data[..]);
}
#[test]
fn test_pixel_iterator() {
#[rustfmt::skip]
let plane = Plane::<u8> {
data: PlaneData::from_slice(&[
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 2, 3, 4, 0, 0,
0, 0, 8, 7, 6, 5, 0, 0,
0, 0, 9, 8, 7, 6, 0, 0,
0, 0, 2, 3, 4, 5, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
]),
cfg: PlaneConfig {
stride: 8,
alloc_height: 9,
width: 4,
height: 4,
xdec: 0,
ydec: 0,
xpad: 0,
ypad: 0,
xorigin: 2,
yorigin: 3,
},
};
let pixels: Vec<u8> = plane.iter().collect();
assert_eq!(
&[1, 2, 3, 4, 8, 7, 6, 5, 9, 8, 7, 6, 2, 3, 4, 5,][..],
&pixels[..]
);
}
}