use crate::Size;
use crate::image::{Image, RasterImage, RasterImageMut};
use crate::pixel::ZeroablePixel;
pub fn flip_h_into<I, O>(img: &I, out: &mut O)
where
I: RasterImage,
O: RasterImageMut<Pixel = I::Pixel>,
{
assert_eq!(
img.size(),
out.size(),
"flip_h_into: input size {:?} does not match output size {:?}",
img.size(),
out.size()
);
let w = img.width();
for y in 0..img.height() {
let src = img.row(y);
let dst = out.row_mut(y);
for x in 0..w {
dst[w - 1 - x] = src[x];
}
}
}
#[must_use]
pub fn flip_h<I>(img: &I) -> Image<I::Pixel>
where
I: RasterImage,
I::Pixel: ZeroablePixel,
{
let mut out = Image::<I::Pixel>::zero(img.width(), img.height());
flip_h_into(img, &mut out);
out
}
pub fn flip_v_into<I, O>(img: &I, out: &mut O)
where
I: RasterImage,
O: RasterImageMut<Pixel = I::Pixel>,
{
assert_eq!(
img.size(),
out.size(),
"flip_v_into: input size {:?} does not match output size {:?}",
img.size(),
out.size()
);
let h = img.height();
for y in 0..h {
let src = img.row(y);
let dst = out.row_mut(h - 1 - y);
dst.copy_from_slice(src);
}
}
#[must_use]
pub fn flip_v<I>(img: &I) -> Image<I::Pixel>
where
I: RasterImage,
I::Pixel: ZeroablePixel,
{
let mut out = Image::<I::Pixel>::zero(img.width(), img.height());
flip_v_into(img, &mut out);
out
}
pub fn rotate_90_into<I, O>(img: &I, out: &mut O)
where
I: RasterImage,
O: RasterImageMut<Pixel = I::Pixel>,
{
let expected = Size::new(img.height(), img.width());
assert_eq!(
out.size(),
expected,
"rotate_90_into: expected output size {:?}, got {:?}",
expected,
out.size()
);
let w = img.width();
let h = img.height();
for y in 0..h {
let src = img.row(y);
for (x, &p) in src.iter().enumerate() {
*out.pixel_at_mut(y, w - 1 - x) = p;
}
}
}
#[must_use]
pub fn rotate_90<I>(img: &I) -> Image<I::Pixel>
where
I: RasterImage,
I::Pixel: ZeroablePixel,
{
let mut out = Image::<I::Pixel>::zero(img.height(), img.width());
rotate_90_into(img, &mut out);
out
}
pub fn rotate_180_into<I, O>(img: &I, out: &mut O)
where
I: RasterImage,
O: RasterImageMut<Pixel = I::Pixel>,
{
assert_eq!(
img.size(),
out.size(),
"rotate_180_into: input size {:?} does not match output size {:?}",
img.size(),
out.size()
);
let w = img.width();
let h = img.height();
for y in 0..h {
let src = img.row(y);
let dst = out.row_mut(h - 1 - y);
for x in 0..w {
dst[w - 1 - x] = src[x];
}
}
}
#[must_use]
pub fn rotate_180<I>(img: &I) -> Image<I::Pixel>
where
I: RasterImage,
I::Pixel: ZeroablePixel,
{
let mut out = Image::<I::Pixel>::zero(img.width(), img.height());
rotate_180_into(img, &mut out);
out
}
pub fn rotate_270_into<I, O>(img: &I, out: &mut O)
where
I: RasterImage,
O: RasterImageMut<Pixel = I::Pixel>,
{
let expected = Size::new(img.height(), img.width());
assert_eq!(
out.size(),
expected,
"rotate_270_into: expected output size {:?}, got {:?}",
expected,
out.size()
);
let h = img.height();
for y in 0..h {
let src = img.row(y);
let dst_y = h - 1 - y;
for (x, &p) in src.iter().enumerate() {
*out.pixel_at_mut(dst_y, x) = p;
}
}
}
#[must_use]
pub fn rotate_270<I>(img: &I) -> Image<I::Pixel>
where
I: RasterImage,
I::Pixel: ZeroablePixel,
{
let mut out = Image::<I::Pixel>::zero(img.height(), img.width());
rotate_270_into(img, &mut out);
out
}
pub fn transpose_into<I, O>(img: &I, out: &mut O)
where
I: RasterImage,
O: RasterImageMut<Pixel = I::Pixel>,
{
let expected = Size::new(img.height(), img.width());
assert_eq!(
out.size(),
expected,
"transpose_into: expected output size {:?}, got {:?}",
expected,
out.size()
);
let h = img.height();
for y in 0..h {
let src = img.row(y);
for (x, &p) in src.iter().enumerate() {
*out.pixel_at_mut(y, x) = p;
}
}
}
#[must_use]
pub fn transpose<I>(img: &I) -> Image<I::Pixel>
where
I: RasterImage,
I::Pixel: ZeroablePixel,
{
let mut out = Image::<I::Pixel>::zero(img.height(), img.width());
transpose_into(img, &mut out);
out
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Rectangle;
use crate::image::{Image, ImageArray, ImageView, SubView};
use crate::pixel::{Indexed8, Mono8, Rgb8, Srgb8};
fn mono(v: u8) -> Mono8 {
Mono8::new(v)
}
fn collect<I: ImageView<Pixel = Mono8>>(img: &I) -> Vec<u8> {
let mut out = Vec::with_capacity(img.width() * img.height());
for y in 0..img.height() {
for x in 0..img.width() {
out.push(img.pixel_at(x, y).value());
}
}
out
}
fn from_u8(width: usize, height: usize, data: &[u8]) -> Image<Mono8> {
Image::from_vec(width, height, data.iter().copied().map(mono).collect()).unwrap()
}
#[test]
fn flip_h_2x2() {
let src = from_u8(2, 2, &[1, 2, 3, 4]);
let dst = flip_h(&src);
assert_eq!(collect(&dst), vec![2, 1, 4, 3]);
}
#[test]
fn flip_h_3x2_rectangular() {
let src = from_u8(3, 2, &[1, 2, 3, 4, 5, 6]);
let dst = flip_h(&src);
assert_eq!(collect(&dst), vec![3, 2, 1, 6, 5, 4]);
}
#[test]
fn flip_h_idempotent() {
let src = from_u8(3, 2, &[1, 2, 3, 4, 5, 6]);
let twice = flip_h(&flip_h(&src));
assert_eq!(collect(&twice), collect(&src));
}
#[test]
fn flip_v_2x2() {
let src = from_u8(2, 2, &[1, 2, 3, 4]);
let dst = flip_v(&src);
assert_eq!(collect(&dst), vec![3, 4, 1, 2]);
}
#[test]
fn flip_v_3x2_rectangular() {
let src = from_u8(3, 2, &[1, 2, 3, 4, 5, 6]);
let dst = flip_v(&src);
assert_eq!(collect(&dst), vec![4, 5, 6, 1, 2, 3]);
}
#[test]
fn flip_v_idempotent() {
let src = from_u8(3, 2, &[1, 2, 3, 4, 5, 6]);
let twice = flip_v(&flip_v(&src));
assert_eq!(collect(&twice), collect(&src));
}
#[test]
fn rotate_90_2x3() {
let src = from_u8(2, 3, &[1, 2, 3, 4, 5, 6]);
let dst = rotate_90(&src);
assert_eq!(dst.width(), 3);
assert_eq!(dst.height(), 2);
assert_eq!(collect(&dst), vec![2, 4, 6, 1, 3, 5]);
}
#[test]
fn rotate_90_then_270_is_identity() {
let src = from_u8(3, 2, &[1, 2, 3, 4, 5, 6]);
let round = rotate_270(&rotate_90(&src));
assert_eq!(round.size(), src.size());
assert_eq!(collect(&round), collect(&src));
}
#[test]
fn rotate_90_three_times_equals_rotate_270() {
let src = from_u8(3, 2, &[1, 2, 3, 4, 5, 6]);
let triple = rotate_90(&rotate_90(&rotate_90(&src)));
let direct = rotate_270(&src);
assert_eq!(triple.size(), direct.size());
assert_eq!(collect(&triple), collect(&direct));
}
#[test]
fn rotate_180_3x2_equals_flip_h_compose_flip_v() {
let src = from_u8(3, 2, &[1, 2, 3, 4, 5, 6]);
let by_rotate = rotate_180(&src);
let by_compose = flip_h(&flip_v(&src));
assert_eq!(collect(&by_rotate), collect(&by_compose));
assert_eq!(collect(&by_rotate), vec![6, 5, 4, 3, 2, 1]);
}
#[test]
fn rotate_180_idempotent() {
let src = from_u8(3, 2, &[1, 2, 3, 4, 5, 6]);
let twice = rotate_180(&rotate_180(&src));
assert_eq!(collect(&twice), collect(&src));
}
#[test]
fn rotate_270_2x3() {
let src = from_u8(2, 3, &[1, 2, 3, 4, 5, 6]);
let dst = rotate_270(&src);
assert_eq!(dst.width(), 3);
assert_eq!(dst.height(), 2);
assert_eq!(collect(&dst), vec![5, 3, 1, 6, 4, 2]);
}
#[test]
fn transpose_2x3_matches_flip_v_compose_rotate_90() {
let src = from_u8(2, 3, &[1, 2, 3, 4, 5, 6]);
let by_transpose = transpose(&src);
let by_compose = flip_v(&rotate_90(&src));
assert_eq!(by_transpose.size(), by_compose.size());
assert_eq!(collect(&by_transpose), collect(&by_compose));
assert_eq!(collect(&by_transpose), vec![1, 3, 5, 2, 4, 6]);
}
#[test]
fn transpose_square_is_self_inverse() {
let src = from_u8(3, 3, &[1, 2, 3, 4, 5, 6, 7, 8, 9]);
let twice = transpose(&transpose(&src));
assert_eq!(collect(&twice), collect(&src));
}
#[test]
#[should_panic(expected = "flip_h_into")]
fn flip_h_into_size_mismatch_panics() {
let src = from_u8(2, 2, &[1, 2, 3, 4]);
let mut dst = Image::<Mono8>::zero(3, 2);
flip_h_into(&src, &mut dst);
}
#[test]
#[should_panic(expected = "flip_v_into")]
fn flip_v_into_size_mismatch_panics() {
let src = from_u8(2, 2, &[1, 2, 3, 4]);
let mut dst = Image::<Mono8>::zero(2, 3);
flip_v_into(&src, &mut dst);
}
#[test]
#[should_panic(expected = "rotate_90_into")]
fn rotate_90_into_size_mismatch_panics() {
let src = from_u8(2, 3, &[1, 2, 3, 4, 5, 6]);
let mut dst = Image::<Mono8>::zero(2, 3);
rotate_90_into(&src, &mut dst);
}
#[test]
#[should_panic(expected = "rotate_180_into")]
fn rotate_180_into_size_mismatch_panics() {
let src = from_u8(2, 3, &[1, 2, 3, 4, 5, 6]);
let mut dst = Image::<Mono8>::zero(3, 2);
rotate_180_into(&src, &mut dst);
}
#[test]
#[should_panic(expected = "rotate_270_into")]
fn rotate_270_into_size_mismatch_panics() {
let src = from_u8(2, 3, &[1, 2, 3, 4, 5, 6]);
let mut dst = Image::<Mono8>::zero(2, 3);
rotate_270_into(&src, &mut dst);
}
#[test]
#[should_panic(expected = "transpose_into")]
fn transpose_into_size_mismatch_panics() {
let src = from_u8(2, 3, &[1, 2, 3, 4, 5, 6]);
let mut dst = Image::<Mono8>::zero(2, 3);
transpose_into(&src, &mut dst);
}
#[test]
fn flip_v_subview_input() {
let outer = from_u8(
4,
4,
&[
0, 0, 0, 0, 0, 2, 3, 0, 0, 6, 7, 0, 0, 10, 11, 0, ],
);
let roi = outer.roi(Rectangle::new((1, 1), (2, 3))).unwrap();
let flipped = flip_v(&roi);
assert_eq!(collect(&flipped), vec![10, 11, 6, 7, 2, 3]);
}
#[test]
fn rotate_90_subview_input() {
let outer = from_u8(
4,
4,
&[
0, 0, 0, 0, 0, 2, 3, 0, 0, 6, 7, 0, 0, 10, 11, 0, ],
);
let roi = outer.roi(Rectangle::new((1, 1), (2, 3))).unwrap();
let rotated = rotate_90(&roi);
assert_eq!(rotated.width(), 3);
assert_eq!(rotated.height(), 2);
assert_eq!(collect(&rotated), vec![3, 7, 11, 2, 6, 10]);
}
#[test]
fn rotate_90_imagearray_output() {
let src = from_u8(2, 3, &[1, 2, 3, 4, 5, 6]);
let mut out: ImageArray<Mono8, 3, 2> = ImageArray::new([mono(0); 6]);
rotate_90_into(&src, &mut out);
assert_eq!(out.pixel_at(0, 0), mono(2));
assert_eq!(out.pixel_at(1, 0), mono(4));
assert_eq!(out.pixel_at(2, 0), mono(6));
assert_eq!(out.pixel_at(0, 1), mono(1));
assert_eq!(out.pixel_at(1, 1), mono(3));
assert_eq!(out.pixel_at(2, 1), mono(5));
}
#[test]
fn flip_h_rgb8() {
let a = Rgb8::new(1, 2, 3);
let b = Rgb8::new(4, 5, 6);
let src = Image::from_vec(2, 1, vec![a, b]).unwrap();
let dst = flip_h(&src);
assert_eq!(dst.pixel_at(0, 0), b);
assert_eq!(dst.pixel_at(1, 0), a);
}
#[test]
fn rotate_90_indexed8() {
let src = Image::from_vec(
2,
3,
vec![
Indexed8(1),
Indexed8(2),
Indexed8(3),
Indexed8(4),
Indexed8(5),
Indexed8(6),
],
)
.unwrap();
let dst = rotate_90(&src);
assert_eq!(dst.width(), 3);
assert_eq!(dst.height(), 2);
assert_eq!(dst.pixel_at(0, 0), Indexed8(2));
assert_eq!(dst.pixel_at(1, 0), Indexed8(4));
assert_eq!(dst.pixel_at(2, 0), Indexed8(6));
assert_eq!(dst.pixel_at(0, 1), Indexed8(1));
}
#[test]
fn flip_v_srgb8_gamma_encoded() {
let a = Srgb8::new(10, 20, 30);
let b = Srgb8::new(40, 50, 60);
let src = Image::from_vec(1, 2, vec![a, b]).unwrap();
let dst = flip_v(&src);
assert_eq!(dst.pixel_at(0, 0), b);
assert_eq!(dst.pixel_at(0, 1), a);
}
}