use super::*;
use ::image::{
DynamicImage, GrayAlphaImage, GrayImage, ImageBuffer, Luma, LumaA, Rgb, RgbImage, Rgba,
RgbaImage, metadata::Orientation,
};
fn xy_encoded_rgb8(width: u32, height: u32) -> DynamicImage {
let mut buf = RgbImage::new(width, height);
for y in 0..height {
for x in 0..width {
buf.put_pixel(x, y, Rgb([(x * 10) as u8, (y * 10) as u8, 200]));
}
}
DynamicImage::ImageRgb8(buf)
}
fn xy_encoded_luma8(width: u32, height: u32) -> DynamicImage {
let mut buf = GrayImage::new(width, height);
for y in 0..height {
for x in 0..width {
buf.put_pixel(x, y, Luma([(y * width + x) as u8]));
}
}
DynamicImage::ImageLuma8(buf)
}
fn xy_encoded_luma_alpha8(width: u32, height: u32) -> DynamicImage {
let mut buf = GrayAlphaImage::new(width, height);
for y in 0..height {
for x in 0..width {
let v = (y * width + x) as u8;
buf.put_pixel(x, y, LumaA([v, 255 - v]));
}
}
DynamicImage::ImageLumaA8(buf)
}
fn xy_encoded_rgba8(width: u32, height: u32) -> DynamicImage {
let mut buf = RgbaImage::new(width, height);
for y in 0..height {
for x in 0..width {
buf.put_pixel(
x,
y,
Rgba([(x * 10) as u8, (y * 10) as u8, 200, 255 - (y * 10) as u8]),
);
}
}
DynamicImage::ImageRgba8(buf)
}
#[test]
fn no_transforms_passes_through_unchanged() {
let img = xy_encoded_rgb8(4, 3);
let original_bytes = img.as_rgb8().expect("rgb8 source").as_raw().clone();
let out = apply_orientation_fallible(img, Orientation::NoTransforms).expect("infallible path");
assert_eq!(out.width(), 4);
assert_eq!(out.height(), 3);
assert_eq!(
out.as_rgb8().expect("rgb8 output").as_raw(),
&original_bytes
);
}
#[test]
fn rotate180_in_place_path_matches_reference() {
let img = xy_encoded_rgb8(4, 3);
let reference: ImageBuffer<Rgb<u8>, Vec<u8>> =
::image::imageops::rotate180(img.as_rgb8().expect("rgb8 source"));
let out =
apply_orientation_fallible(img, Orientation::Rotate180).expect("in-place path infallible");
assert_eq!(out.width(), 4, "Rotate180 preserves width");
assert_eq!(out.height(), 3, "Rotate180 preserves height");
assert_eq!(
out.as_rgb8().expect("rgb8 output").as_raw(),
reference.as_raw(),
"Rotate180 pixel bytes must match image::imageops::rotate180"
);
}
#[test]
fn flip_horizontal_in_place_path_matches_reference() {
let img = xy_encoded_rgb8(4, 3);
let reference = ::image::imageops::flip_horizontal(img.as_rgb8().expect("rgb8 source"));
let out =
apply_orientation_fallible(img, Orientation::FlipHorizontal).expect("in-place path infallible");
assert_eq!(
out.as_rgb8().expect("rgb8 output").as_raw(),
reference.as_raw(),
"FlipHorizontal pixel bytes must match image::imageops::flip_horizontal"
);
}
#[test]
fn rotate90_rgb8_manual_matches_reference() {
let img = xy_encoded_rgb8(4, 3);
let reference = ::image::imageops::rotate90(img.as_rgb8().expect("rgb8 source"));
let out = apply_orientation_fallible(img, Orientation::Rotate90)
.expect("manual rotate90 should succeed for a 4x3 image well under the 512 MiB ceiling");
assert_eq!(out.width(), 3, "Rotate90 swaps width <- height");
assert_eq!(out.height(), 4, "Rotate90 swaps height <- width");
assert_eq!(
out.as_rgb8().expect("rgb8 output").as_raw(),
reference.as_raw(),
"manual rotate must yield byte-identical pixels to image::imageops::rotate90"
);
}
#[test]
fn rotate270_rgb8_manual_matches_reference() {
let img = xy_encoded_rgb8(4, 3);
let reference = ::image::imageops::rotate270(img.as_rgb8().expect("rgb8 source"));
let out = apply_orientation_fallible(img, Orientation::Rotate270).expect("rotate270 ok");
assert_eq!(out.width(), 3);
assert_eq!(out.height(), 4);
assert_eq!(
out.as_rgb8().expect("rgb8 output").as_raw(),
reference.as_raw(),
);
}
#[test]
fn rotate90_fliph_rgb8_composite_matches_reference() {
let img = xy_encoded_rgb8(4, 3);
let rotated = ::image::imageops::rotate90(img.as_rgb8().expect("rgb8 source"));
let reference = ::image::imageops::flip_horizontal(&rotated);
let out = apply_orientation_fallible(img, Orientation::Rotate90FlipH)
.expect("Rotate90FlipH composite ok");
assert_eq!(out.width(), reference.width());
assert_eq!(out.height(), reference.height());
assert_eq!(
out.as_rgb8().expect("rgb8 output").as_raw(),
reference.as_raw(),
);
}
#[test]
fn rotate270_fliph_rgb8_composite_matches_reference() {
let img = xy_encoded_rgb8(4, 3);
let rotated = ::image::imageops::rotate270(img.as_rgb8().expect("rgb8 source"));
let reference = ::image::imageops::flip_horizontal(&rotated);
let out = apply_orientation_fallible(img, Orientation::Rotate270FlipH)
.expect("Rotate270FlipH composite ok");
assert_eq!(out.width(), 3);
assert_eq!(out.height(), 4);
assert_eq!(
out.as_rgb8().expect("rgb8 output").as_raw(),
reference.as_raw(),
);
}
#[test]
fn rotate90_rgba8_manual_matches_reference() {
let img = xy_encoded_rgba8(4, 3);
let reference = ::image::imageops::rotate90(img.as_rgba8().expect("rgba8 source"));
let out = apply_orientation_fallible(img, Orientation::Rotate90).expect("rgba8 rotate90 ok");
assert_eq!(out.width(), 3);
assert_eq!(out.height(), 4);
assert_eq!(
out.as_rgba8().expect("rgba8 output").as_raw(),
reference.as_raw(),
"manual Rgba8 rotate90 must preserve all 4 channels byte-identical to image::imageops::rotate90"
);
}
#[test]
fn rotate270_fliph_rgba8_composite_matches_reference() {
let img = xy_encoded_rgba8(4, 3);
let rotated = ::image::imageops::rotate270(img.as_rgba8().expect("rgba8 source"));
let reference = ::image::imageops::flip_horizontal(&rotated);
let out =
apply_orientation_fallible(img, Orientation::Rotate270FlipH).expect("rgba8 Rotate270FlipH ok");
assert_eq!(out.width(), 3);
assert_eq!(out.height(), 4);
assert_eq!(
out.as_rgba8().expect("rgba8 output").as_raw(),
reference.as_raw(),
);
}
#[test]
fn rotate90_luma8_manual_matches_reference() {
let img = xy_encoded_luma8(4, 3);
let reference = ::image::imageops::rotate90(img.as_luma8().expect("luma8 source"));
let out = apply_orientation_fallible(img, Orientation::Rotate90).expect("luma8 rotate90 ok");
assert_eq!(out.width(), 3);
assert_eq!(out.height(), 4);
assert_eq!(
out.as_luma8().expect("luma8 output").as_raw(),
reference.as_raw(),
"manual Luma8 rotate90 must match image::imageops::rotate90 byte-identical"
);
}
#[test]
fn rotate270_luma8_manual_matches_reference() {
let img = xy_encoded_luma8(4, 3);
let reference = ::image::imageops::rotate270(img.as_luma8().expect("luma8 source"));
let out = apply_orientation_fallible(img, Orientation::Rotate270).expect("luma8 rotate270 ok");
assert_eq!(out.width(), 3);
assert_eq!(out.height(), 4);
assert_eq!(
out.as_luma8().expect("luma8 output").as_raw(),
reference.as_raw(),
);
}
#[test]
fn rotate90_luma_alpha8_manual_matches_reference() {
let img = xy_encoded_luma_alpha8(4, 3);
let reference = ::image::imageops::rotate90(img.as_luma_alpha8().expect("luma_alpha8 source"));
let out =
apply_orientation_fallible(img, Orientation::Rotate90).expect("luma_alpha8 rotate90 ok");
assert_eq!(out.width(), 3);
assert_eq!(out.height(), 4);
assert_eq!(
out.as_luma_alpha8().expect("luma_alpha8 output").as_raw(),
reference.as_raw(),
"manual LumaA8 rotate90 must match image::imageops::rotate90 byte-identical"
);
}
fn xy_encoded_rgb16(width: u32, height: u32) -> DynamicImage {
let mut buf: ImageBuffer<Rgb<u16>, Vec<u16>> = ImageBuffer::new(width, height);
for y in 0..height {
for x in 0..width {
buf.put_pixel(
x,
y,
Rgb([
(((x * 10) as u16) + 1) << 8,
(((y * 10) as u16) + 1) << 8,
0xBEEF,
]),
);
}
}
DynamicImage::ImageRgb16(buf)
}
fn xy_encoded_luma16(width: u32, height: u32) -> DynamicImage {
let mut buf: ImageBuffer<Luma<u16>, Vec<u16>> = ImageBuffer::new(width, height);
for y in 0..height {
for x in 0..width {
let idx = (y * width + x) as u16;
buf.put_pixel(x, y, Luma([(idx << 8) | 0x005A]));
}
}
DynamicImage::ImageLuma16(buf)
}
fn xy_encoded_rgb32f(width: u32, height: u32) -> DynamicImage {
let mut buf: ImageBuffer<Rgb<f32>, Vec<f32>> = ImageBuffer::new(width, height);
for y in 0..height {
for x in 0..width {
buf.put_pixel(x, y, Rgb([(x as f32) + 0.25, (y as f32) + 0.5, 0.875]));
}
}
DynamicImage::ImageRgb32F(buf)
}
#[test]
fn rotate90_rgb16_manual_matches_reference() {
let img = xy_encoded_rgb16(4, 3);
let reference = ::image::imageops::rotate90(img.as_rgb16().expect("rgb16 source"));
let out = apply_orientation_fallible(img, Orientation::Rotate90)
.expect("manual Rgb16 rotate90 should succeed under MAX_DECODED_IMAGE_BYTES");
assert_eq!(out.width(), 3, "Rotate90 swaps width <- height");
assert_eq!(out.height(), 4, "Rotate90 swaps height <- width");
assert_eq!(
out.as_rgb16().expect("rgb16 output").as_raw(),
reference.as_raw(),
"manual Rgb16 rotate90 must yield byte-identical u16 subpixels to image::imageops::rotate90"
);
}
#[test]
fn rotate270_luma16_manual_matches_reference() {
let img = xy_encoded_luma16(4, 3);
let reference = ::image::imageops::rotate270(img.as_luma16().expect("luma16 source"));
let out =
apply_orientation_fallible(img, Orientation::Rotate270).expect("manual Luma16 rotate270 ok");
assert_eq!(out.width(), 3);
assert_eq!(out.height(), 4);
assert_eq!(
out.as_luma16().expect("luma16 output").as_raw(),
reference.as_raw(),
"manual Luma16 rotate270 must yield byte-identical u16 subpixels to image::imageops::rotate270"
);
}
#[test]
fn rotate90_rgb32f_manual_matches_reference() {
let img = xy_encoded_rgb32f(4, 3);
let reference = ::image::imageops::rotate90(img.as_rgb32f().expect("rgb32f source"));
let out =
apply_orientation_fallible(img, Orientation::Rotate90).expect("manual Rgb32F rotate90 ok");
assert_eq!(out.width(), 3);
assert_eq!(out.height(), 4);
assert_eq!(
out.as_rgb32f().expect("rgb32f output").as_raw(),
reference.as_raw(),
"manual Rgb32F rotate90 must yield bit-identical f32 subpixels to image::imageops::rotate90 \
(permutation only — no FP arithmetic)"
);
}
#[test]
fn synthetic_16bit_rotate_succeeds_via_orientation_entry_point() {
for rotation in [
Orientation::Rotate90,
Orientation::Rotate270,
Orientation::Rotate90FlipH,
Orientation::Rotate270FlipH,
] {
let img = xy_encoded_rgb16(4, 3);
let out = apply_orientation_fallible(img, rotation)
.expect("16-bit Rgb16 rotate must succeed via the fallible u16 path");
assert_eq!(out.width(), 3, "all four rotate orientations swap w<->h");
assert_eq!(out.height(), 4);
assert!(
matches!(out, DynamicImage::ImageRgb16(_)),
"output must preserve the source DynamicImage variant (Rgb16 in, Rgb16 out)"
);
}
}
#[test]
fn rotate90_accepts_small_image_within_budget() {
let img = xy_encoded_rgb8(1, 1);
let bytes_per_pixel = u64::from(img.color().bytes_per_pixel());
let bytes = u64::from(img.width()) * u64::from(img.height()) * bytes_per_pixel;
assert!(
bytes <= MAX_DECODED_IMAGE_BYTES,
"1x1 Rgb8 = {bytes} bytes must be well under MAX_DECODED_IMAGE_BYTES={MAX_DECODED_IMAGE_BYTES}"
);
let out = apply_orientation_fallible(img, Orientation::Rotate90).expect("1x1 ok");
assert_eq!(out.width(), 1);
assert_eq!(out.height(), 1);
}
fn xy_encoded_luma_alpha16(width: u32, height: u32) -> DynamicImage {
let mut buf: ImageBuffer<LumaA<u16>, Vec<u16>> = ImageBuffer::new(width, height);
for y in 0..height {
for x in 0..width {
let idx = (y * width + x) as u16;
buf.put_pixel(x, y, LumaA([(idx << 8) | 0x0033, 0xFF00 - (idx << 8)]));
}
}
DynamicImage::ImageLumaA16(buf)
}
fn xy_encoded_rgba16(width: u32, height: u32) -> DynamicImage {
let mut buf: ImageBuffer<Rgba<u16>, Vec<u16>> = ImageBuffer::new(width, height);
for y in 0..height {
for x in 0..width {
buf.put_pixel(
x,
y,
Rgba([
(((x * 10) as u16) + 1) << 8,
(((y * 10) as u16) + 1) << 8,
0xBEEF,
0xA55A,
]),
);
}
}
DynamicImage::ImageRgba16(buf)
}
fn xy_encoded_rgba32f(width: u32, height: u32) -> DynamicImage {
let mut buf: ImageBuffer<Rgba<f32>, Vec<f32>> = ImageBuffer::new(width, height);
for y in 0..height {
for x in 0..width {
buf.put_pixel(
x,
y,
Rgba([(x as f32) + 0.25, (y as f32) + 0.5, 0.875, 0.125]),
);
}
}
DynamicImage::ImageRgba32F(buf)
}
#[test]
fn rotate90_luma_alpha16_manual_matches_reference() {
let img = xy_encoded_luma_alpha16(4, 3);
let reference = ::image::imageops::rotate90(img.as_luma_alpha16().expect("luma_alpha16 source"));
let out = apply_orientation_fallible(img, Orientation::Rotate90)
.expect("manual LumaA16 rotate90 should succeed under MAX_DECODED_IMAGE_BYTES");
assert_eq!(out.width(), 3, "Rotate90 swaps width <- height");
assert_eq!(out.height(), 4, "Rotate90 swaps height <- width");
assert_eq!(
out.as_luma_alpha16().expect("luma_alpha16 output").as_raw(),
reference.as_raw(),
"manual LumaA16 rotate90 must yield byte-identical u16 subpixels to image::imageops::rotate90"
);
}
#[test]
fn rotate270_rgba16_manual_matches_reference() {
let img = xy_encoded_rgba16(4, 3);
let reference = ::image::imageops::rotate270(img.as_rgba16().expect("rgba16 source"));
let out =
apply_orientation_fallible(img, Orientation::Rotate270).expect("manual Rgba16 rotate270 ok");
assert_eq!(out.width(), 3);
assert_eq!(out.height(), 4);
assert_eq!(
out.as_rgba16().expect("rgba16 output").as_raw(),
reference.as_raw(),
"manual Rgba16 rotate270 must yield byte-identical u16 subpixels to image::imageops::rotate270"
);
}
#[test]
fn rotate90_fliph_rgba32f_composite_matches_reference() {
let img = xy_encoded_rgba32f(4, 3);
let rotated = ::image::imageops::rotate90(img.as_rgba32f().expect("rgba32f source"));
let reference = ::image::imageops::flip_horizontal(&rotated);
let out = apply_orientation_fallible(img, Orientation::Rotate90FlipH)
.expect("manual Rgba32F Rotate90FlipH ok");
assert_eq!(out.width(), 3);
assert_eq!(out.height(), 4);
assert_eq!(
out.as_rgba32f().expect("rgba32f output").as_raw(),
reference.as_raw(),
"manual Rgba32F Rotate90FlipH must yield bit-identical f32 subpixels to the \
image::imageops rotate90 + flip_horizontal composite (permutation only)"
);
}
#[test]
fn rotate270_rgba8_manual_matches_reference() {
let img = xy_encoded_rgba8(4, 3);
let reference = ::image::imageops::rotate270(img.as_rgba8().expect("rgba8 source"));
let out = apply_orientation_fallible(img, Orientation::Rotate270).expect("rgba8 rotate270 ok");
assert_eq!(out.width(), 3);
assert_eq!(out.height(), 4);
assert_eq!(
out.as_rgba8().expect("rgba8 output").as_raw(),
reference.as_raw(),
"manual Rgba8 rotate270 must match image::imageops::rotate270 byte-identical"
);
}
#[test]
fn rotate90_fliph_rgba8_composite_matches_reference() {
let img = xy_encoded_rgba8(4, 3);
let rotated = ::image::imageops::rotate90(img.as_rgba8().expect("rgba8 source"));
let reference = ::image::imageops::flip_horizontal(&rotated);
let out =
apply_orientation_fallible(img, Orientation::Rotate90FlipH).expect("rgba8 Rotate90FlipH ok");
assert_eq!(out.width(), 3);
assert_eq!(out.height(), 4);
assert_eq!(
out.as_rgba8().expect("rgba8 output").as_raw(),
reference.as_raw(),
"manual Rgba8 Rotate90FlipH must match the image::imageops rotate90 + flip_horizontal composite"
);
}