diff options
Diffstat (limited to 'vendor/image/src/imageops/affine.rs')
-rw-r--r-- | vendor/image/src/imageops/affine.rs | 410 |
1 files changed, 410 insertions, 0 deletions
diff --git a/vendor/image/src/imageops/affine.rs b/vendor/image/src/imageops/affine.rs new file mode 100644 index 0000000..548381c --- /dev/null +++ b/vendor/image/src/imageops/affine.rs @@ -0,0 +1,410 @@ +//! Functions for performing affine transformations. + +use crate::error::{ImageError, ParameterError, ParameterErrorKind}; +use crate::image::{GenericImage, GenericImageView}; +use crate::traits::Pixel; +use crate::ImageBuffer; + +/// Rotate an image 90 degrees clockwise. +pub fn rotate90<I: GenericImageView>( + image: &I, +) -> ImageBuffer<I::Pixel, Vec<<I::Pixel as Pixel>::Subpixel>> +where + I::Pixel: 'static, +{ + let (width, height) = image.dimensions(); + let mut out = ImageBuffer::new(height, width); + let _ = rotate90_in(image, &mut out); + out +} + +/// Rotate an image 180 degrees clockwise. +pub fn rotate180<I: GenericImageView>( + image: &I, +) -> ImageBuffer<I::Pixel, Vec<<I::Pixel as Pixel>::Subpixel>> +where + I::Pixel: 'static, +{ + let (width, height) = image.dimensions(); + let mut out = ImageBuffer::new(width, height); + let _ = rotate180_in(image, &mut out); + out +} + +/// Rotate an image 270 degrees clockwise. +pub fn rotate270<I: GenericImageView>( + image: &I, +) -> ImageBuffer<I::Pixel, Vec<<I::Pixel as Pixel>::Subpixel>> +where + I::Pixel: 'static, +{ + let (width, height) = image.dimensions(); + let mut out = ImageBuffer::new(height, width); + let _ = rotate270_in(image, &mut out); + out +} + +/// Rotate an image 90 degrees clockwise and put the result into the destination [`ImageBuffer`]. +pub fn rotate90_in<I, Container>( + image: &I, + destination: &mut ImageBuffer<I::Pixel, Container>, +) -> crate::ImageResult<()> +where + I: GenericImageView, + I::Pixel: 'static, + Container: std::ops::DerefMut<Target = [<I::Pixel as Pixel>::Subpixel]>, +{ + let ((w0, h0), (w1, h1)) = (image.dimensions(), destination.dimensions()); + if w0 != h1 || h0 != w1 { + return Err(ImageError::Parameter(ParameterError::from_kind( + ParameterErrorKind::DimensionMismatch, + ))); + } + + for y in 0..h0 { + for x in 0..w0 { + let p = image.get_pixel(x, y); + destination.put_pixel(h0 - y - 1, x, p); + } + } + Ok(()) +} + +/// Rotate an image 180 degrees clockwise and put the result into the destination [`ImageBuffer`]. +pub fn rotate180_in<I, Container>( + image: &I, + destination: &mut ImageBuffer<I::Pixel, Container>, +) -> crate::ImageResult<()> +where + I: GenericImageView, + I::Pixel: 'static, + Container: std::ops::DerefMut<Target = [<I::Pixel as Pixel>::Subpixel]>, +{ + let ((w0, h0), (w1, h1)) = (image.dimensions(), destination.dimensions()); + if w0 != w1 || h0 != h1 { + return Err(ImageError::Parameter(ParameterError::from_kind( + ParameterErrorKind::DimensionMismatch, + ))); + } + + for y in 0..h0 { + for x in 0..w0 { + let p = image.get_pixel(x, y); + destination.put_pixel(w0 - x - 1, h0 - y - 1, p); + } + } + Ok(()) +} + +/// Rotate an image 270 degrees clockwise and put the result into the destination [`ImageBuffer`]. +pub fn rotate270_in<I, Container>( + image: &I, + destination: &mut ImageBuffer<I::Pixel, Container>, +) -> crate::ImageResult<()> +where + I: GenericImageView, + I::Pixel: 'static, + Container: std::ops::DerefMut<Target = [<I::Pixel as Pixel>::Subpixel]>, +{ + let ((w0, h0), (w1, h1)) = (image.dimensions(), destination.dimensions()); + if w0 != h1 || h0 != w1 { + return Err(ImageError::Parameter(ParameterError::from_kind( + ParameterErrorKind::DimensionMismatch, + ))); + } + + for y in 0..h0 { + for x in 0..w0 { + let p = image.get_pixel(x, y); + destination.put_pixel(y, w0 - x - 1, p); + } + } + Ok(()) +} + +/// Flip an image horizontally +pub fn flip_horizontal<I: GenericImageView>( + image: &I, +) -> ImageBuffer<I::Pixel, Vec<<I::Pixel as Pixel>::Subpixel>> +where + I::Pixel: 'static, +{ + let (width, height) = image.dimensions(); + let mut out = ImageBuffer::new(width, height); + let _ = flip_horizontal_in(image, &mut out); + out +} + +/// Flip an image vertically +pub fn flip_vertical<I: GenericImageView>( + image: &I, +) -> ImageBuffer<I::Pixel, Vec<<I::Pixel as Pixel>::Subpixel>> +where + I::Pixel: 'static, +{ + let (width, height) = image.dimensions(); + let mut out = ImageBuffer::new(width, height); + let _ = flip_vertical_in(image, &mut out); + out +} + +/// Flip an image horizontally and put the result into the destination [`ImageBuffer`]. +pub fn flip_horizontal_in<I, Container>( + image: &I, + destination: &mut ImageBuffer<I::Pixel, Container>, +) -> crate::ImageResult<()> +where + I: GenericImageView, + I::Pixel: 'static, + Container: std::ops::DerefMut<Target = [<I::Pixel as Pixel>::Subpixel]>, +{ + let ((w0, h0), (w1, h1)) = (image.dimensions(), destination.dimensions()); + if w0 != w1 || h0 != h1 { + return Err(ImageError::Parameter(ParameterError::from_kind( + ParameterErrorKind::DimensionMismatch, + ))); + } + + for y in 0..h0 { + for x in 0..w0 { + let p = image.get_pixel(x, y); + destination.put_pixel(w0 - x - 1, y, p); + } + } + Ok(()) +} + +/// Flip an image vertically and put the result into the destination [`ImageBuffer`]. +pub fn flip_vertical_in<I, Container>( + image: &I, + destination: &mut ImageBuffer<I::Pixel, Container>, +) -> crate::ImageResult<()> +where + I: GenericImageView, + I::Pixel: 'static, + Container: std::ops::DerefMut<Target = [<I::Pixel as Pixel>::Subpixel]>, +{ + let ((w0, h0), (w1, h1)) = (image.dimensions(), destination.dimensions()); + if w0 != w1 || h0 != h1 { + return Err(ImageError::Parameter(ParameterError::from_kind( + ParameterErrorKind::DimensionMismatch, + ))); + } + + for y in 0..h0 { + for x in 0..w0 { + let p = image.get_pixel(x, y); + destination.put_pixel(x, h0 - 1 - y, p); + } + } + Ok(()) +} + +/// Rotate an image 180 degrees clockwise in place. +pub fn rotate180_in_place<I: GenericImage>(image: &mut I) { + let (width, height) = image.dimensions(); + + for y in 0..height / 2 { + for x in 0..width { + let p = image.get_pixel(x, y); + + let x2 = width - x - 1; + let y2 = height - y - 1; + + let p2 = image.get_pixel(x2, y2); + image.put_pixel(x, y, p2); + image.put_pixel(x2, y2, p); + } + } + + if height % 2 != 0 { + let middle = height / 2; + + for x in 0..width / 2 { + let p = image.get_pixel(x, middle); + let x2 = width - x - 1; + + let p2 = image.get_pixel(x2, middle); + image.put_pixel(x, middle, p2); + image.put_pixel(x2, middle, p); + } + } +} + +/// Flip an image horizontally in place. +pub fn flip_horizontal_in_place<I: GenericImage>(image: &mut I) { + let (width, height) = image.dimensions(); + + for y in 0..height { + for x in 0..width / 2 { + let x2 = width - x - 1; + let p2 = image.get_pixel(x2, y); + let p = image.get_pixel(x, y); + image.put_pixel(x2, y, p); + image.put_pixel(x, y, p2); + } + } +} + +/// Flip an image vertically in place. +pub fn flip_vertical_in_place<I: GenericImage>(image: &mut I) { + let (width, height) = image.dimensions(); + + for y in 0..height / 2 { + for x in 0..width { + let y2 = height - y - 1; + let p2 = image.get_pixel(x, y2); + let p = image.get_pixel(x, y); + image.put_pixel(x, y2, p); + image.put_pixel(x, y, p2); + } + } +} + +#[cfg(test)] +mod test { + use super::{ + flip_horizontal, flip_horizontal_in_place, flip_vertical, flip_vertical_in_place, + rotate180, rotate180_in_place, rotate270, rotate90, + }; + use crate::image::GenericImage; + use crate::traits::Pixel; + use crate::{GrayImage, ImageBuffer}; + + macro_rules! assert_pixels_eq { + ($actual:expr, $expected:expr) => {{ + let actual_dim = $actual.dimensions(); + let expected_dim = $expected.dimensions(); + + if actual_dim != expected_dim { + panic!( + "dimensions do not match. \ + actual: {:?}, expected: {:?}", + actual_dim, expected_dim + ) + } + + let diffs = pixel_diffs($actual, $expected); + + if !diffs.is_empty() { + let mut err = "".to_string(); + + let diff_messages = diffs + .iter() + .take(5) + .map(|d| format!("\nactual: {:?}, expected {:?} ", d.0, d.1)) + .collect::<Vec<_>>() + .join(""); + + err.push_str(&diff_messages); + panic!("pixels do not match. {:?}", err) + } + }}; + } + + #[test] + fn test_rotate90() { + let image: GrayImage = + ImageBuffer::from_raw(3, 2, vec![00u8, 01u8, 02u8, 10u8, 11u8, 12u8]).unwrap(); + + let expected: GrayImage = + ImageBuffer::from_raw(2, 3, vec![10u8, 00u8, 11u8, 01u8, 12u8, 02u8]).unwrap(); + + assert_pixels_eq!(&rotate90(&image), &expected); + } + + #[test] + fn test_rotate180() { + let image: GrayImage = + ImageBuffer::from_raw(3, 2, vec![00u8, 01u8, 02u8, 10u8, 11u8, 12u8]).unwrap(); + + let expected: GrayImage = + ImageBuffer::from_raw(3, 2, vec![12u8, 11u8, 10u8, 02u8, 01u8, 00u8]).unwrap(); + + assert_pixels_eq!(&rotate180(&image), &expected); + } + + #[test] + fn test_rotate270() { + let image: GrayImage = + ImageBuffer::from_raw(3, 2, vec![00u8, 01u8, 02u8, 10u8, 11u8, 12u8]).unwrap(); + + let expected: GrayImage = + ImageBuffer::from_raw(2, 3, vec![02u8, 12u8, 01u8, 11u8, 00u8, 10u8]).unwrap(); + + assert_pixels_eq!(&rotate270(&image), &expected); + } + + #[test] + fn test_rotate180_in_place() { + let mut image: GrayImage = + ImageBuffer::from_raw(3, 2, vec![00u8, 01u8, 02u8, 10u8, 11u8, 12u8]).unwrap(); + + let expected: GrayImage = + ImageBuffer::from_raw(3, 2, vec![12u8, 11u8, 10u8, 02u8, 01u8, 00u8]).unwrap(); + + rotate180_in_place(&mut image); + + assert_pixels_eq!(&image, &expected); + } + + #[test] + fn test_flip_horizontal() { + let image: GrayImage = + ImageBuffer::from_raw(3, 2, vec![00u8, 01u8, 02u8, 10u8, 11u8, 12u8]).unwrap(); + + let expected: GrayImage = + ImageBuffer::from_raw(3, 2, vec![02u8, 01u8, 00u8, 12u8, 11u8, 10u8]).unwrap(); + + assert_pixels_eq!(&flip_horizontal(&image), &expected); + } + + #[test] + fn test_flip_vertical() { + let image: GrayImage = + ImageBuffer::from_raw(3, 2, vec![00u8, 01u8, 02u8, 10u8, 11u8, 12u8]).unwrap(); + + let expected: GrayImage = + ImageBuffer::from_raw(3, 2, vec![10u8, 11u8, 12u8, 00u8, 01u8, 02u8]).unwrap(); + + assert_pixels_eq!(&flip_vertical(&image), &expected); + } + + #[test] + fn test_flip_horizontal_in_place() { + let mut image: GrayImage = + ImageBuffer::from_raw(3, 2, vec![00u8, 01u8, 02u8, 10u8, 11u8, 12u8]).unwrap(); + + let expected: GrayImage = + ImageBuffer::from_raw(3, 2, vec![02u8, 01u8, 00u8, 12u8, 11u8, 10u8]).unwrap(); + + flip_horizontal_in_place(&mut image); + + assert_pixels_eq!(&image, &expected); + } + + #[test] + fn test_flip_vertical_in_place() { + let mut image: GrayImage = + ImageBuffer::from_raw(3, 2, vec![00u8, 01u8, 02u8, 10u8, 11u8, 12u8]).unwrap(); + + let expected: GrayImage = + ImageBuffer::from_raw(3, 2, vec![10u8, 11u8, 12u8, 00u8, 01u8, 02u8]).unwrap(); + + flip_vertical_in_place(&mut image); + + assert_pixels_eq!(&image, &expected); + } + + fn pixel_diffs<I, J, P>(left: &I, right: &J) -> Vec<((u32, u32, P), (u32, u32, P))> + where + I: GenericImage<Pixel = P>, + J: GenericImage<Pixel = P>, + P: Pixel + Eq, + { + left.pixels() + .zip(right.pixels()) + .filter(|&(p, q)| p != q) + .collect::<Vec<_>>() + } +} |