use super::*;
use zenpixels::PixelDescriptor;
fn slice<'a>(data: &'a [u8], w: u32, h: u32, desc: PixelDescriptor) -> PixelSlice<'a> {
PixelSlice::new(data, w, h, w as usize * desc.bytes_per_pixel(), desc).unwrap()
}
const SRC_3X2: [u8; 6] = [0, 1, 2, 3, 4, 5];
fn expected_3x2(o: Orientation) -> (u32, u32, Vec<u8>) {
match o {
Orientation::Identity => (3, 2, vec![0, 1, 2, 3, 4, 5]),
Orientation::FlipH => (3, 2, vec![2, 1, 0, 5, 4, 3]),
Orientation::FlipV => (3, 2, vec![3, 4, 5, 0, 1, 2]),
Orientation::Rotate180 => (3, 2, vec![5, 4, 3, 2, 1, 0]),
Orientation::Transpose => (2, 3, vec![0, 3, 1, 4, 2, 5]),
Orientation::Rotate90 => (2, 3, vec![3, 0, 4, 1, 5, 2]),
Orientation::Rotate270 => (2, 3, vec![2, 5, 1, 4, 0, 3]),
Orientation::Transverse => (2, 3, vec![5, 2, 4, 1, 3, 0]),
_ => unreachable!("non-exhaustive Orientation in test oracle"),
}
}
#[test]
fn all_orientations_match_hand_derived_oracle_gray8() {
let desc = PixelDescriptor::GRAY8;
for &o in &Orientation::ALL {
let out = apply_orientation(slice(&SRC_3X2, 3, 2, desc), o);
let (ew, eh, ebytes) = expected_3x2(o);
assert_eq!((out.width(), out.height()), (ew, eh), "{o:?} dims");
let s = out.as_slice();
for y in 0..eh {
let got = s.row(y);
let exp = &ebytes[y as usize * ew as usize..][..ew as usize];
assert_eq!(got, exp, "{o:?} row {y}");
}
}
}
#[test]
fn all_orientations_match_oracle_rgba8() {
let desc = PixelDescriptor::RGBA8;
let mut src = Vec::new();
for v in 0u8..6 {
src.extend_from_slice(&[v, v + 64, v + 128, 255]);
}
for &o in &Orientation::ALL {
let out = apply_orientation(slice(&src, 3, 2, desc), o);
let (ew, eh, gray) = expected_3x2(o);
assert_eq!((out.width(), out.height()), (ew, eh), "{o:?} dims");
let s = out.as_slice();
for y in 0..eh {
let got = s.row(y);
for x in 0..ew {
let v = gray[(y * ew + x) as usize];
let exp = [v, v + 64, v + 128, 255];
assert_eq!(&got[x as usize * 4..][..4], &exp, "{o:?} px ({x},{y})");
}
}
}
}
fn fill(n: usize) -> Vec<u8> {
let mut v = Vec::with_capacity(n);
let mut s = 0x9e3779b9u32;
for _ in 0..n {
s = s.wrapping_mul(1664525).wrapping_add(1013904223);
v.push((s >> 24) as u8);
}
v
}
#[test]
fn roundtrip_orientation_then_inverse_is_identity() {
for &desc in &[
PixelDescriptor::GRAY8, PixelDescriptor::GRAYA8, PixelDescriptor::RGB8, PixelDescriptor::RGBA8, PixelDescriptor::RGBAF32, ] {
let bpp = desc.bytes_per_pixel();
for &(w, h) in &[(1u32, 1u32), (17, 13), (33, 31), (64, 48)] {
let data = fill(w as usize * h as usize * bpp);
for &o in &Orientation::ALL {
let once = apply_orientation(slice(&data, w, h, desc), o);
let back = apply_orientation(once.as_slice(), o.inverse());
assert_eq!(
(back.width(), back.height()),
(w, h),
"{o:?} {desc:?} {w}x{h}"
);
for y in 0..h {
let exp = &data[y as usize * w as usize * bpp..][..w as usize * bpp];
assert_eq!(
back.as_slice().row(y),
exp,
"{o:?} {desc:?} {w}x{h} row {y}"
);
}
}
}
}
}
#[test]
fn compose_matches_sequential_application() {
let desc = PixelDescriptor::RGBA8;
let (w, h) = (11u32, 7u32);
let data = fill(w as usize * h as usize * 4);
for &a in &Orientation::ALL {
for &b in &Orientation::ALL {
let seq =
apply_orientation(apply_orientation(slice(&data, w, h, desc), a).as_slice(), b);
let fused = apply_orientation(slice(&data, w, h, desc), a.then(b));
assert_eq!(
(seq.width(), seq.height()),
(fused.width(), fused.height()),
"{a:?}.then({b:?}) dims"
);
for y in 0..seq.height() {
assert_eq!(
seq.as_slice().row(y),
fused.as_slice().row(y),
"{a:?}.then({b:?}) row {y}"
);
}
}
}
}
#[test]
fn handles_strided_source() {
for &desc in &[
PixelDescriptor::RGBA8,
PixelDescriptor::RGB8,
PixelDescriptor::GRAY8,
] {
let bpp = desc.bytes_per_pixel();
for &(w, h) in &[(5u32, 4u32), (37, 35)] {
let tight_stride = w as usize * bpp;
let padded_stride = tight_stride + 12;
let tight = fill(tight_stride * h as usize);
let mut padded = vec![0xABu8; padded_stride * h as usize];
for y in 0..h as usize {
padded[y * padded_stride..y * padded_stride + tight_stride]
.copy_from_slice(&tight[y * tight_stride..][..tight_stride]);
}
for &o in &Orientation::ALL {
let tight_slice = PixelSlice::new(&tight, w, h, tight_stride, desc).unwrap();
let padded_slice = PixelSlice::new(&padded, w, h, padded_stride, desc).unwrap();
let a = apply_orientation(tight_slice, o);
let b = apply_orientation(padded_slice, o);
for y in 0..a.height() {
assert_eq!(
a.as_slice().row(y),
b.as_slice().row(y),
"{o:?} {desc:?} {w}x{h} row {y}"
);
}
}
}
}
}
#[test]
fn simd_transpose_matches_scalar_reference_rgba8() {
let desc = PixelDescriptor::RGBA8;
let dims = [
(8u32, 8u32),
(16, 16),
(64, 48),
(17, 13),
(9, 7),
(12, 4),
(4, 12),
(3, 3),
(1, 1),
(5, 5),
];
for &(w, h) in &dims {
let data = fill(w as usize * h as usize * 4);
for &o in &[
Orientation::Transpose,
Orientation::Rotate90,
Orientation::Rotate270,
Orientation::Transverse,
] {
let got = apply_orientation(slice(&data, w, h, desc), o);
let (ow, oh) = o.output_dimensions(w, h);
let mut reference = PixelBuffer::new(ow, oh, desc);
{
let src = slice(&data, w, h, desc);
let mut d = reference.as_slice_mut();
transpose_blocked(&src, &mut d, o, w, h, 4);
}
for y in 0..oh {
assert_eq!(
got.as_slice().row(y),
reference.as_slice().row(y),
"{o:?} {w}x{h} row {y}"
);
}
}
}
}
#[test]
fn tiled_transpose_matches_blocked_reference_across_bpp() {
let descs = [
PixelDescriptor::GRAY8, PixelDescriptor::GRAYA8, PixelDescriptor::RGB8, PixelDescriptor::RGB16, PixelDescriptor::RGBA16, PixelDescriptor::RGBF32, PixelDescriptor::RGBAF32, ];
let dims = [
(8u32, 8u32),
(32, 32),
(64, 48),
(17, 13),
(33, 31),
(40, 33),
(67, 43),
(64, 1),
(1, 64),
(1, 1),
];
for &desc in &descs {
let bpp = desc.bytes_per_pixel();
for &(w, h) in &dims {
let data = fill(w as usize * h as usize * bpp);
for &o in &[
Orientation::Transpose,
Orientation::Rotate90,
Orientation::Rotate270,
Orientation::Transverse,
] {
let got = apply_orientation(slice(&data, w, h, desc), o);
let (ow, oh) = o.output_dimensions(w, h);
let mut reference = PixelBuffer::new(ow, oh, desc);
{
let src = slice(&data, w, h, desc);
let mut d = reference.as_slice_mut();
transpose_blocked(&src, &mut d, o, w, h, bpp);
}
for y in 0..oh {
assert_eq!(
got.as_slice().row(y),
reference.as_slice().row(y),
"{o:?} {desc:?} {w}x{h} row {y}"
);
}
}
}
}
}
#[test]
fn tiled3_fallback_matches_blocked_reference() {
let desc = PixelDescriptor::RGB8;
for &(w, h) in &[(8u32, 8u32), (17, 13), (33, 31), (67, 43), (1, 1), (5, 64)] {
let data = fill(w as usize * h as usize * 3);
for &o in &[
Orientation::Transpose,
Orientation::Rotate90,
Orientation::Rotate270,
Orientation::Transverse,
] {
let flips = inverse_flips(o).unwrap();
let (ow, oh) = o.output_dimensions(w, h);
let mut got = PixelBuffer::new(ow, oh, desc);
let mut want = PixelBuffer::new(ow, oh, desc);
{
let src = slice(&data, w, h, desc);
let mut d = got.as_slice_mut();
transpose_tiled::<3>(&src, &mut d, flips, w, h);
}
{
let src = slice(&data, w, h, desc);
let mut d = want.as_slice_mut();
transpose_blocked(&src, &mut d, o, w, h, 3);
}
for y in 0..oh {
assert_eq!(
got.as_slice().row(y),
want.as_slice().row(y),
"tiled3 {o:?} {w}x{h} row {y}"
);
}
}
}
}
#[cfg(feature = "__bench_orient")]
#[test]
fn staged_matches_production_across_bpp_and_orientations() {
let descs = [
PixelDescriptor::GRAY8,
PixelDescriptor::GRAYA8,
PixelDescriptor::RGB8,
PixelDescriptor::RGBA8,
];
let dims = [
(16u32, 16u32),
(32, 32),
(64, 48),
(17, 13),
(33, 31),
(40, 33),
(67, 43),
(16, 1),
(1, 16),
(1, 1),
(15, 15), ];
for &desc in &descs {
let bpp = desc.bytes_per_pixel();
for &(w, h) in &dims {
let data = fill(w as usize * h as usize * bpp);
for &o in &[
Orientation::Transpose,
Orientation::Rotate90,
Orientation::Rotate270,
Orientation::Transverse,
] {
let want = apply_orientation(slice(&data, w, h, desc), o);
let (ow, oh) = o.output_dimensions(w, h);
let mut got = PixelBuffer::new(ow, oh, desc);
super::__bench_apply_orientation_staged(
slice(&data, w, h, desc),
o,
got.as_slice_mut(),
)
.expect("staged accepts matching dst");
for y in 0..oh {
assert_eq!(
got.as_slice().row(y),
want.as_slice().row(y),
"staged {o:?} {desc:?} {w}x{h} row {y}"
);
}
}
}
}
}
fn naive_oracle(
data: &[u8],
w: u32,
h: u32,
desc: PixelDescriptor,
o: Orientation,
) -> (u32, u32, Vec<u8>) {
let bpp = desc.bytes_per_pixel();
let (ow, oh) = o.output_dimensions(w, h);
let mut out = alloc::vec![0u8; ow as usize * oh as usize * bpp];
for sy in 0..h {
for sx in 0..w {
let (dx, dy) = o.forward_map(sx, sy, w, h);
let s = (sy as usize * w as usize + sx as usize) * bpp;
let d = (dy as usize * ow as usize + dx as usize) * bpp;
out[d..d + bpp].copy_from_slice(&data[s..s + bpp]);
}
}
(ow, oh, out)
}
fn assert_matches_oracle(desc: PixelDescriptor, w: u32, h: u32, o: Orientation) {
let bpp = desc.bytes_per_pixel();
let data = fill(w as usize * h as usize * bpp);
let (ow, oh, want) = naive_oracle(&data, w, h, desc, o);
let got = apply_orientation(slice(&data, w, h, desc), o);
assert_eq!(
(got.width(), got.height()),
(ow, oh),
"dims {o:?} {desc:?} {w}x{h}"
);
let gs = got.as_slice();
for y in 0..oh {
let exp = &want[y as usize * ow as usize * bpp..][..ow as usize * bpp];
assert_eq!(gs.row(y), exp, "{o:?} {desc:?} {w}x{h} row {y}");
}
}
#[test]
fn exhaustive_dense_dims_all_orientations_vs_oracle() {
let descs = [
PixelDescriptor::GRAY8,
PixelDescriptor::GRAYA8,
PixelDescriptor::RGB8,
PixelDescriptor::RGBA8,
PixelDescriptor::RGB16,
PixelDescriptor::RGBA16,
PixelDescriptor::RGBF32,
PixelDescriptor::RGBAF32,
];
for &desc in &descs {
for w in 1u32..=33 {
for h in 1u32..=33 {
for &o in &Orientation::ALL {
assert_matches_oracle(desc, w, h, o);
}
}
}
}
}
#[test]
fn multistripe_boundaries_vs_oracle() {
let descs = [
PixelDescriptor::GRAY8,
PixelDescriptor::GRAYA8,
PixelDescriptor::RGB8,
PixelDescriptor::RGBA8,
PixelDescriptor::RGB16,
PixelDescriptor::RGBA16,
PixelDescriptor::RGBF32,
];
let span = [48u32, 63, 64, 65, 66, 68, 72, 96, 127, 128, 129, 130];
let other = [1u32, 4, 7, 8, 15, 16, 17, 33, 64, 65, 128];
for &desc in &descs {
for &a in &span {
for &b in &other {
for &o in &Orientation::ALL {
assert_matches_oracle(desc, a, b, o);
assert_matches_oracle(desc, b, a, o);
}
}
}
}
}
#[test]
fn exhaustive_scalar_fallbacks_vs_oracle() {
let descs = [
(PixelDescriptor::GRAY8, 1usize),
(PixelDescriptor::GRAYA8, 2),
(PixelDescriptor::RGB8, 3),
(PixelDescriptor::RGBA8, 4),
(PixelDescriptor::RGB16, 6),
(PixelDescriptor::RGBA16, 8),
(PixelDescriptor::RGBF32, 12),
(PixelDescriptor::RGBAF32, 16),
];
let trans = [
Orientation::Transpose,
Orientation::Rotate90,
Orientation::Rotate270,
Orientation::Transverse,
];
for &(desc, bpp) in &descs {
for w in 1u32..=33 {
for h in 1u32..=33 {
let data = fill(w as usize * h as usize * bpp);
for &o in &trans {
let (ow, oh, want) = naive_oracle(&data, w, h, desc, o);
let mut b = PixelBuffer::new(ow, oh, desc);
{
let src = slice(&data, w, h, desc);
transpose_blocked(&src, &mut b.as_slice_mut(), o, w, h, bpp);
}
let flips = inverse_flips(o).unwrap();
let mut t = PixelBuffer::new(ow, oh, desc);
macro_rules! tiled {
($n:literal) => {{
let src = slice(&data, w, h, desc);
transpose_tiled::<$n>(&src, &mut t.as_slice_mut(), flips, w, h);
}};
}
match bpp {
1 => tiled!(1),
2 => tiled!(2),
3 => tiled!(3),
4 => tiled!(4),
6 => tiled!(6),
8 => tiled!(8),
12 => tiled!(12),
16 => tiled!(16),
_ => unreachable!(),
}
for y in 0..oh {
let exp = &want[y as usize * ow as usize * bpp..][..ow as usize * bpp];
assert_eq!(
b.as_slice().row(y),
exp,
"blocked {o:?} {desc:?} {w}x{h} row {y}"
);
assert_eq!(
t.as_slice().row(y),
exp,
"tiled {o:?} {desc:?} {w}x{h} row {y}"
);
}
}
}
}
}
}
#[test]
fn exhaustive_strided_sources_vs_tight() {
let descs = [
PixelDescriptor::GRAY8,
PixelDescriptor::GRAYA8,
PixelDescriptor::RGB8,
PixelDescriptor::RGBA8,
PixelDescriptor::RGB16,
PixelDescriptor::RGBA16,
PixelDescriptor::RGBF32,
PixelDescriptor::RGBAF32,
];
let vals = [1u32, 2, 3, 4, 7, 8, 9, 15, 16, 17, 31, 32, 33, 65, 128];
for &desc in &descs {
let bpp = desc.bytes_per_pixel();
for &w in &vals {
for &h in &vals {
let tight_stride = w as usize * bpp;
let pad = tight_stride + 13 * bpp; let tight = fill(tight_stride * h as usize);
let mut padded = alloc::vec![0xA5u8; pad * h as usize];
for y in 0..h as usize {
padded[y * pad..y * pad + tight_stride]
.copy_from_slice(&tight[y * tight_stride..][..tight_stride]);
}
for &o in &Orientation::ALL {
let ts = PixelSlice::new(&tight, w, h, tight_stride, desc).unwrap();
let ps = PixelSlice::new(&padded, w, h, pad, desc).unwrap();
let a = apply_orientation(ts, o);
let b = apply_orientation(ps, o);
for y in 0..a.height() {
assert_eq!(
a.as_slice().row(y),
b.as_slice().row(y),
"{o:?} {desc:?} {w}x{h} strided row {y}"
);
}
}
}
}
}
}
#[test]
fn pathological_misaligned_sources_vs_oracle() {
let descs = [
PixelDescriptor::GRAY8,
PixelDescriptor::GRAYA8,
PixelDescriptor::RGB8,
PixelDescriptor::RGBA8,
PixelDescriptor::RGB16,
PixelDescriptor::RGBA16,
PixelDescriptor::RGBF32,
PixelDescriptor::RGBAF32,
];
let dims = [
(1u32, 1u32),
(7, 7),
(8, 8),
(16, 16),
(17, 17),
(33, 31),
(31, 33),
(64, 17),
(17, 64),
(65, 9),
];
let pad_px = [1usize, 3]; for &desc in &descs {
let bpp = desc.bytes_per_pixel();
let targets: &[usize] = match desc.min_alignment() {
1 => &[1, 3, 7, 13, 31, 63],
2 => &[2, 6, 14, 30, 62],
4 => &[4, 12, 28, 60],
_ => unreachable!("channel alignment is 1/2/4"),
};
for &(w, h) in &dims {
let tight_stride = w as usize * bpp;
let tight = fill(tight_stride * h as usize);
for &pp in &pad_px {
let stride = (w as usize + pp) * bpp;
let need = stride * h as usize;
for &target in targets {
let mut raw = alloc::vec![0xC3u8; need + 64];
let base = raw.as_ptr() as usize;
let startoff = ((64 - (base % 64)) + target) % 64;
for y in 0..h as usize {
let d = startoff + y * stride;
raw[d..d + tight_stride]
.copy_from_slice(&tight[y * tight_stride..][..tight_stride]);
}
let addr = raw.as_ptr() as usize + startoff;
assert_eq!(addr % 64, target, "misalignment construction");
assert_eq!(
addr % desc.min_alignment(),
0,
"channel alignment must hold (API precondition)"
);
assert_ne!(addr % 16, 0, "must be SIMD-misaligned to be pathological");
for &o in &Orientation::ALL {
let (ow, oh, want) = naive_oracle(&tight, w, h, desc, o);
let view = &raw[startoff..startoff + need];
let s = PixelSlice::new(view, w, h, stride, desc).unwrap();
let got = apply_orientation(s, o);
assert_eq!(
(got.width(), got.height()),
(ow, oh),
"dims target={target} pad={pp} {o:?} {desc:?} {w}x{h}"
);
let gs = got.as_slice();
for y in 0..oh {
let exp = &want[y as usize * ow as usize * bpp..][..ow as usize * bpp];
assert_eq!(
gs.row(y),
exp,
"misaligned target={target} pad={pp} {o:?} {desc:?} {w}x{h} row {y}"
);
}
}
}
}
}
}
}
#[test]
fn into_writes_caller_buffer_and_is_reusable() {
let desc = PixelDescriptor::RGBA8;
let (w, h) = (17u32, 13u32);
let data = fill(w as usize * h as usize * 4);
let (ow, oh) = Orientation::Rotate90.output_dimensions(w, h);
let mut target = PixelBuffer::new(ow, oh, desc);
for &o in &[
Orientation::Rotate90,
Orientation::Rotate270,
Orientation::Transverse,
Orientation::Transpose,
] {
apply_orientation_into(slice(&data, w, h, desc), o, target.as_slice_mut())
.expect("into should accept a correctly-sized buffer");
let want = apply_orientation(slice(&data, w, h, desc), o);
for y in 0..oh {
assert_eq!(
target.as_slice().row(y),
want.as_slice().row(y),
"{o:?} row {y}"
);
}
}
}
#[test]
fn into_rejects_wrong_sized_dst() {
let desc = PixelDescriptor::RGBA8;
let (w, h) = (8u32, 6u32);
let data = fill(w as usize * h as usize * 4);
let mut wrong = PixelBuffer::new(w, h, desc); let result = apply_orientation_into(
slice(&data, w, h, desc),
Orientation::Rotate90,
wrong.as_slice_mut(),
);
assert!(
matches!(result, Err(ConvertError::BufferSize { .. })),
"expected BufferSize, got {result:?}"
);
}
#[test]
fn in_place_matches_out_of_place() {
let descs = [
PixelDescriptor::GRAY8,
PixelDescriptor::GRAYA8,
PixelDescriptor::RGB8,
PixelDescriptor::RGBA8,
PixelDescriptor::RGBAF32,
];
let mut dims: alloc::vec::Vec<(u32, u32)> = alloc::vec::Vec::new();
for w in 1u32..=20 {
for h in 1u32..=20 {
dims.push((w, h));
}
}
for &d in &[
(32u32, 32u32),
(33, 31),
(1, 64),
(64, 1),
(40, 24),
(24, 40),
] {
dims.push(d);
}
for &desc in &descs {
let bpp = desc.bytes_per_pixel();
for &(w, h) in &dims {
let data = fill(w as usize * h as usize * bpp);
for &o in &Orientation::ALL {
let want = apply_orientation(slice(&data, w, h, desc), o);
let mut buf = PixelBuffer::new(w, h, desc);
{
let mut s = buf.as_slice_mut();
for y in 0..h {
s.row_mut(y).copy_from_slice(
&data[y as usize * w as usize * bpp..][..w as usize * bpp],
);
}
}
apply_orientation_in_place(&mut buf, o).expect("in_place should accept bpp ≤ 16");
assert_eq!(
(buf.width(), buf.height()),
(want.width(), want.height()),
"{o:?} {desc:?} {w}x{h} dims"
);
for y in 0..buf.height() {
assert_eq!(
buf.as_slice().row(y),
want.as_slice().row(y),
"{o:?} {desc:?} {w}x{h} row {y}"
);
}
}
}
}
}
#[test]
fn zero_area_inputs_are_handled() {
let desc = PixelDescriptor::RGBA8;
for &(w, h) in &[(0u32, 5u32), (5, 0), (0, 0)] {
for &o in &Orientation::ALL {
let (ow, oh) = o.output_dimensions(w, h);
let src = PixelBuffer::new(w, h, desc);
let out = apply_orientation(src.as_slice(), o);
assert_eq!((out.width(), out.height()), (ow, oh), "{o:?} {w}x{h} dims");
let mut ip = PixelBuffer::new(w, h, desc);
apply_orientation_in_place(&mut ip, o).expect("zero-area in-place ok");
assert_eq!(
(ip.width(), ip.height()),
(ow, oh),
"{o:?} {w}x{h} in-place dims"
);
}
}
}
#[test]
fn in_place_preserves_color_context() {
let desc = PixelDescriptor::RGBA8;
let (w, h) = (6u32, 4u32);
let data = fill(w as usize * h as usize * 4);
let ctx = alloc::sync::Arc::new(zenpixels::ColorContext::from_cicp(zenpixels::Cicp::SRGB));
for &o in &Orientation::ALL {
let mut buf = PixelBuffer::new(w, h, desc).with_color_context(ctx.clone());
{
let mut s = buf.as_slice_mut();
for y in 0..h {
s.row_mut(y)
.copy_from_slice(&data[y as usize * w as usize * 4..][..w as usize * 4]);
}
}
apply_orientation_in_place(&mut buf, o).expect("in-place ok");
assert!(buf.color_context().is_some(), "{o:?} dropped color context");
let want = apply_orientation(slice(&data, w, h, desc), o);
for y in 0..buf.height() {
assert_eq!(
buf.as_slice().row(y),
want.as_slice().row(y),
"{o:?} row {y}"
);
}
}
}
#[test]
fn in_place_padded_stride_matches_out_of_place() {
for &desc in &[
PixelDescriptor::RGBA8,
PixelDescriptor::RGB8,
PixelDescriptor::GRAY8,
] {
let bpp = desc.bytes_per_pixel();
for &(w, h) in &[(5u32, 7u32), (13, 9), (17, 3)] {
let data = fill(w as usize * h as usize * bpp);
for &o in &Orientation::ALL {
let mut buf = PixelBuffer::new_simd_aligned(w, h, desc, 32);
assert!(
buf.as_slice().stride() > w as usize * bpp,
"{desc:?} {w}x{h}: expected a padded stride"
);
{
let mut s = buf.as_slice_mut();
for y in 0..h {
s.row_mut(y).copy_from_slice(
&data[y as usize * w as usize * bpp..][..w as usize * bpp],
);
}
}
let want = apply_orientation(slice(&data, w, h, desc), o);
apply_orientation_in_place(&mut buf, o).expect("padded in-place ok");
assert_eq!(
(buf.width(), buf.height()),
(want.width(), want.height()),
"{o:?} {desc:?} {w}x{h} dims"
);
for y in 0..buf.height() {
assert_eq!(
buf.as_slice().row(y),
want.as_slice().row(y),
"{o:?} {desc:?} {w}x{h} row {y}"
);
}
}
}
}
}
#[test]
fn inverse_flips_some_iff_transposing() {
for &o in &Orientation::ALL {
let expect_some = matches!(
o,
Orientation::Transpose
| Orientation::Rotate90
| Orientation::Rotate270
| Orientation::Transverse
);
assert_eq!(inverse_flips(o).is_some(), expect_some, "{o:?}");
}
}