1 //! Functions for performing affine transformations.
2 
3 use crate::ImageBuffer;
4 use crate::image::{GenericImage, GenericImageView};
5 use crate::error::{ImageError, ParameterError, ParameterErrorKind};
6 use crate::traits::Pixel;
7 
8 /// Rotate an image 90 degrees clockwise.
rotate90<I: GenericImageView>( image: &I, ) -> ImageBuffer<I::Pixel, Vec<<I::Pixel as Pixel>::Subpixel>> where I::Pixel: 'static,9 pub fn rotate90<I: GenericImageView>(
10     image: &I,
11 ) -> ImageBuffer<I::Pixel, Vec<<I::Pixel as Pixel>::Subpixel>>
12     where I::Pixel: 'static,
13 {
14     let (width, height) = image.dimensions();
15     let mut out = ImageBuffer::new(height, width);
16     let _ = rotate90_in(image, &mut out);
17     out
18 }
19 
20 /// Rotate an image 180 degrees clockwise.
rotate180<I: GenericImageView>( image: &I, ) -> ImageBuffer<I::Pixel, Vec<<I::Pixel as Pixel>::Subpixel>> where I::Pixel: 'static,21 pub fn rotate180<I: GenericImageView>(
22     image: &I,
23 ) -> ImageBuffer<I::Pixel, Vec<<I::Pixel as Pixel>::Subpixel>>
24     where I::Pixel: 'static,
25 {
26     let (width, height) = image.dimensions();
27     let mut out = ImageBuffer::new(width, height);
28     let _ = rotate180_in(image, &mut out);
29     out
30 }
31 
32 /// Rotate an image 270 degrees clockwise.
rotate270<I: GenericImageView>( image: &I, ) -> ImageBuffer<I::Pixel, Vec<<I::Pixel as Pixel>::Subpixel>> where I::Pixel: 'static,33 pub fn rotate270<I: GenericImageView>(
34     image: &I,
35 ) -> ImageBuffer<I::Pixel, Vec<<I::Pixel as Pixel>::Subpixel>>
36     where I::Pixel: 'static,
37 {
38     let (width, height) = image.dimensions();
39     let mut out = ImageBuffer::new(height, width);
40     let _ = rotate270_in(image, &mut out);
41     out
42 }
43 
44 /// Rotate an image 90 degrees clockwise and put the result into the destination [`ImageBuffer`].
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]>45 pub fn rotate90_in<I, Container>(
46     image: &I,
47     destination: &mut ImageBuffer<I::Pixel, Container>
48 ) -> crate::ImageResult<()> where
49     I: GenericImageView,
50     I::Pixel: 'static,
51     Container: std::ops::DerefMut<Target = [<I::Pixel as Pixel>::Subpixel]>
52 {
53     let ((w0, h0), (w1, h1)) = (image.dimensions(), destination.dimensions());
54     if w0 != h1 || h0 != w1 {
55         return Err(ImageError::Parameter(ParameterError::from_kind(
56             ParameterErrorKind::DimensionMismatch,
57         )));
58     }
59 
60     for y in 0..h0 {
61         for x in 0..w0 {
62             let p = image.get_pixel(x, y);
63             destination.put_pixel(h0 - y - 1, x, p);
64         }
65     }
66     Ok(())
67 }
68 
69 /// Rotate an image 180 degrees clockwise and put the result into the destination [`ImageBuffer`].
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]>70 pub fn rotate180_in<I, Container>(
71     image: &I,
72     destination: &mut ImageBuffer<I::Pixel, Container>
73 ) -> crate::ImageResult<()> where
74     I: GenericImageView,
75     I::Pixel: 'static,
76     Container: std::ops::DerefMut<Target = [<I::Pixel as Pixel>::Subpixel]>
77 {
78     let ((w0, h0), (w1, h1)) = (image.dimensions(), destination.dimensions());
79     if w0 != w1 || h0 != h1 {
80         return Err(ImageError::Parameter(ParameterError::from_kind(
81             ParameterErrorKind::DimensionMismatch,
82         )));
83     }
84 
85     for y in 0..h0 {
86         for x in 0..w0 {
87             let p = image.get_pixel(x, y);
88             destination.put_pixel(w0 - x - 1, h0 - y - 1, p);
89         }
90     }
91     Ok(())
92 }
93 
94 /// Rotate an image 270 degrees clockwise and put the result into the destination [`ImageBuffer`].
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]>95 pub fn rotate270_in<I, Container>(
96     image: &I,
97     destination: &mut ImageBuffer<I::Pixel, Container>
98 ) -> crate::ImageResult<()> where
99     I: GenericImageView,
100     I::Pixel: 'static,
101     Container: std::ops::DerefMut<Target = [<I::Pixel as Pixel>::Subpixel]>
102 {
103     let ((w0, h0), (w1, h1)) = (image.dimensions(), destination.dimensions());
104     if w0 != h1 || h0 != w1 {
105         return Err(ImageError::Parameter(ParameterError::from_kind(
106             ParameterErrorKind::DimensionMismatch,
107         )));
108     }
109 
110     for y in 0..h0 {
111         for x in 0..w0 {
112             let p = image.get_pixel(x, y);
113             destination.put_pixel(y, w0 - x - 1, p);
114         }
115     }
116     Ok(())
117 }
118 
119 /// Flip an image horizontally
flip_horizontal<I: GenericImageView>( image: &I, ) -> ImageBuffer<I::Pixel, Vec<<I::Pixel as Pixel>::Subpixel>> where I::Pixel: 'static,120 pub fn flip_horizontal<I: GenericImageView>(
121     image: &I,
122 ) -> ImageBuffer<I::Pixel, Vec<<I::Pixel as Pixel>::Subpixel>>
123     where I::Pixel: 'static,
124 {
125     let (width, height) = image.dimensions();
126     let mut out = ImageBuffer::new(width, height);
127     let _ = flip_horizontal_in(image, &mut out);
128     out
129 }
130 
131 /// Flip an image vertically
flip_vertical<I: GenericImageView>( image: &I, ) -> ImageBuffer<I::Pixel, Vec<<I::Pixel as Pixel>::Subpixel>> where I::Pixel: 'static,132 pub fn flip_vertical<I: GenericImageView>(
133     image: &I,
134 ) -> ImageBuffer<I::Pixel, Vec<<I::Pixel as Pixel>::Subpixel>>
135     where I::Pixel: 'static,
136 {
137     let (width, height) = image.dimensions();
138     let mut out = ImageBuffer::new(width, height);
139     let _ = flip_vertical_in(image, &mut out);
140     out
141 }
142 
143 /// Flip an image horizontally and put the result into the destination [`ImageBuffer`].
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]>144 pub fn flip_horizontal_in<I, Container>(
145     image: &I,
146     destination: &mut ImageBuffer<I::Pixel, Container>
147 ) -> crate::ImageResult<()> where
148     I: GenericImageView,
149     I::Pixel: 'static,
150     Container: std::ops::DerefMut<Target = [<I::Pixel as Pixel>::Subpixel]>
151 {
152     let ((w0, h0), (w1, h1)) = (image.dimensions(), destination.dimensions());
153     if w0 != w1 || h0 != h1 {
154         return Err(ImageError::Parameter(ParameterError::from_kind(
155             ParameterErrorKind::DimensionMismatch,
156         )));
157     }
158 
159     for y in 0..h0 {
160         for x in 0..w0 {
161             let p = image.get_pixel(x, y);
162             destination.put_pixel(w0 - x - 1, y, p);
163         }
164     }
165     Ok(())
166 }
167 
168 /// Flip an image vertically and put the result into the destination [`ImageBuffer`].
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]>169 pub fn flip_vertical_in<I, Container>(
170     image: &I,
171     destination: &mut ImageBuffer<I::Pixel, Container>
172 ) -> crate::ImageResult<()> where
173     I: GenericImageView,
174     I::Pixel: 'static,
175     Container: std::ops::DerefMut<Target = [<I::Pixel as Pixel>::Subpixel]>
176 {
177     let ((w0, h0), (w1, h1)) = (image.dimensions(), destination.dimensions());
178     if w0 != w1 || h0 != h1 {
179         return Err(ImageError::Parameter(ParameterError::from_kind(
180             ParameterErrorKind::DimensionMismatch,
181         )));
182     }
183 
184     for y in 0..h0 {
185         for x in 0..w0 {
186             let p = image.get_pixel(x, y);
187             destination.put_pixel(x, h0 - 1 - y, p);
188         }
189     }
190     Ok(())
191 }
192 
193 /// Rotate an image 180 degrees clockwise in place.
rotate180_in_place<I: GenericImage>(image: &mut I)194 pub fn rotate180_in_place<I: GenericImage>(image: &mut I) {
195     let (width, height) = image.dimensions();
196 
197     for y in 0..height / 2 {
198         for x in 0..width {
199             let p = image.get_pixel(x, y);
200 
201             let x2 = width - x - 1;
202             let y2 = height - y - 1;
203 
204             let p2 = image.get_pixel(x2, y2);
205             image.put_pixel(x, y, p2);
206             image.put_pixel(x2, y2, p);
207         }
208     }
209 
210     if height % 2 != 0 {
211         let middle = height / 2;
212 
213         for x in 0..width / 2 {
214             let p = image.get_pixel(x, middle);
215             let x2 = width - x - 1;
216 
217             let p2 = image.get_pixel(x2, middle);
218             image.put_pixel(x, middle, p2);
219             image.put_pixel(x2, middle, p);
220         }
221     }
222 }
223 
224 /// Flip an image horizontally in place.
flip_horizontal_in_place<I: GenericImage>(image: &mut I)225 pub fn flip_horizontal_in_place<I: GenericImage>(image: &mut I) {
226     let (width, height) = image.dimensions();
227 
228     for y in 0..height {
229         for x in 0..width / 2 {
230             let x2 = width - x - 1;
231             let p2 = image.get_pixel(x2, y);
232             let p = image.get_pixel(x, y);
233             image.put_pixel(x2, y, p);
234             image.put_pixel(x, y, p2);
235         }
236     }
237 }
238 
239 /// Flip an image vertically in place.
flip_vertical_in_place<I: GenericImage>(image: &mut I)240 pub fn flip_vertical_in_place<I: GenericImage>(image: &mut I) {
241     let (width, height) = image.dimensions();
242 
243     for y in 0..height / 2 {
244         for x in 0..width {
245             let y2 = height - y - 1;
246             let p2 = image.get_pixel(x, y2);
247             let p = image.get_pixel(x, y);
248             image.put_pixel(x, y2, p);
249             image.put_pixel(x, y, p2);
250         }
251     }
252 }
253 
254 #[cfg(test)]
255 mod test {
256     use super::{
257         flip_horizontal, flip_horizontal_in_place, flip_vertical, flip_vertical_in_place,
258         rotate180, rotate180_in_place, rotate270, rotate90,
259     };
260     use crate::{GrayImage, ImageBuffer};
261     use crate::image::GenericImage;
262     use crate::traits::Pixel;
263 
264     macro_rules! assert_pixels_eq {
265         ($actual:expr, $expected:expr) => {{
266             let actual_dim = $actual.dimensions();
267             let expected_dim = $expected.dimensions();
268 
269             if actual_dim != expected_dim {
270                 panic!(
271                     "dimensions do not match. \
272                      actual: {:?}, expected: {:?}",
273                     actual_dim, expected_dim
274                 )
275             }
276 
277             let diffs = pixel_diffs($actual, $expected);
278 
279             if !diffs.is_empty() {
280                 let mut err = "pixels do not match. ".to_string();
281 
282                 let diff_messages = diffs
283                     .iter()
284                     .take(5)
285                     .map(|d| format!("\nactual: {:?}, expected {:?} ", d.0, d.1))
286                     .collect::<Vec<_>>()
287                     .join("");
288 
289                 err.push_str(&diff_messages);
290                 panic!(err)
291             }
292         }};
293     }
294 
295     #[test]
test_rotate90()296     fn test_rotate90() {
297         let image: GrayImage =
298             ImageBuffer::from_raw(3, 2, vec![00u8, 01u8, 02u8, 10u8, 11u8, 12u8]).unwrap();
299 
300         let expected: GrayImage =
301             ImageBuffer::from_raw(2, 3, vec![10u8, 00u8, 11u8, 01u8, 12u8, 02u8]).unwrap();
302 
303         assert_pixels_eq!(&rotate90(&image), &expected);
304     }
305 
306     #[test]
test_rotate180()307     fn test_rotate180() {
308         let image: GrayImage =
309             ImageBuffer::from_raw(3, 2, vec![00u8, 01u8, 02u8, 10u8, 11u8, 12u8]).unwrap();
310 
311         let expected: GrayImage =
312             ImageBuffer::from_raw(3, 2, vec![12u8, 11u8, 10u8, 02u8, 01u8, 00u8]).unwrap();
313 
314         assert_pixels_eq!(&rotate180(&image), &expected);
315     }
316 
317     #[test]
test_rotate270()318     fn test_rotate270() {
319         let image: GrayImage =
320             ImageBuffer::from_raw(3, 2, vec![00u8, 01u8, 02u8, 10u8, 11u8, 12u8]).unwrap();
321 
322         let expected: GrayImage =
323             ImageBuffer::from_raw(2, 3, vec![02u8, 12u8, 01u8, 11u8, 00u8, 10u8]).unwrap();
324 
325         assert_pixels_eq!(&rotate270(&image), &expected);
326     }
327 
328     #[test]
test_rotate180_in_place()329     fn test_rotate180_in_place() {
330         let mut image: GrayImage =
331             ImageBuffer::from_raw(3, 2, vec![00u8, 01u8, 02u8, 10u8, 11u8, 12u8]).unwrap();
332 
333         let expected: GrayImage =
334             ImageBuffer::from_raw(3, 2, vec![12u8, 11u8, 10u8, 02u8, 01u8, 00u8]).unwrap();
335 
336         rotate180_in_place(&mut image);
337 
338         assert_pixels_eq!(&image, &expected);
339     }
340 
341     #[test]
test_flip_horizontal()342     fn test_flip_horizontal() {
343         let image: GrayImage =
344             ImageBuffer::from_raw(3, 2, vec![00u8, 01u8, 02u8, 10u8, 11u8, 12u8]).unwrap();
345 
346         let expected: GrayImage =
347             ImageBuffer::from_raw(3, 2, vec![02u8, 01u8, 00u8, 12u8, 11u8, 10u8]).unwrap();
348 
349         assert_pixels_eq!(&flip_horizontal(&image), &expected);
350     }
351 
352     #[test]
test_flip_vertical()353     fn test_flip_vertical() {
354         let image: GrayImage =
355             ImageBuffer::from_raw(3, 2, vec![00u8, 01u8, 02u8, 10u8, 11u8, 12u8]).unwrap();
356 
357         let expected: GrayImage =
358             ImageBuffer::from_raw(3, 2, vec![10u8, 11u8, 12u8, 00u8, 01u8, 02u8]).unwrap();
359 
360         assert_pixels_eq!(&flip_vertical(&image), &expected);
361     }
362 
363     #[test]
test_flip_horizontal_in_place()364     fn test_flip_horizontal_in_place() {
365         let mut image: GrayImage =
366             ImageBuffer::from_raw(3, 2, vec![00u8, 01u8, 02u8, 10u8, 11u8, 12u8]).unwrap();
367 
368         let expected: GrayImage =
369             ImageBuffer::from_raw(3, 2, vec![02u8, 01u8, 00u8, 12u8, 11u8, 10u8]).unwrap();
370 
371         flip_horizontal_in_place(&mut image);
372 
373         assert_pixels_eq!(&image, &expected);
374     }
375 
376     #[test]
test_flip_vertical_in_place()377     fn test_flip_vertical_in_place() {
378         let mut image: GrayImage =
379             ImageBuffer::from_raw(3, 2, vec![00u8, 01u8, 02u8, 10u8, 11u8, 12u8]).unwrap();
380 
381         let expected: GrayImage =
382             ImageBuffer::from_raw(3, 2, vec![10u8, 11u8, 12u8, 00u8, 01u8, 02u8]).unwrap();
383 
384         flip_vertical_in_place(&mut image);
385 
386         assert_pixels_eq!(&image, &expected);
387     }
388 
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,389     fn pixel_diffs<I, J, P>(left: &I, right: &J) -> Vec<((u32, u32, P), (u32, u32, P))>
390     where
391         I: GenericImage<Pixel = P>,
392         J: GenericImage<Pixel = P>,
393         P: Pixel + Eq,
394     {
395         left.pixels()
396             .zip(right.pixels())
397             .filter(|&(p, q)| p != q)
398             .collect::<Vec<_>>()
399     }
400 }
401