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