zune_imageprocs/
mirror.rs

1/*
2 * Copyright (c) 2023.
3 *
4 * This software is free software;
5 *
6 * You can redistribute it or modify it under terms of the MIT, Apache License or Zlib license
7 */
8//! Rearrange the pixels along a certain axis.
9
10use zune_core::bit_depth::BitType;
11use zune_image::errors::ImageErrors;
12use zune_image::image::Image;
13use zune_image::traits::OperationsTrait;
14
15/// Supported mirror modes
16#[derive(Copy, Clone, Eq, PartialEq)]
17pub enum MirrorMode {
18    ///
19    /// ```text           
20    ///  old image     new image
21    ///  ┌─────────┐   ┌──────────┐
22    ///  │a b c d e│   │a b c d e │
23    ///  │f g h i j│   │a b d d e │
24    ///  └─────────┘   └──────────┘
25    /// ```
26    North,
27    ///
28    /// ```text           
29    ///  old image     new image
30    ///  ┌─────────┐   ┌──────────┐
31    ///  │a b c d e│   │f g h i j │
32    ///  │f g h i j│   │f g h i j │
33    ///  └─────────┘   └──────────┘
34    /// ```
35    South,
36    ///
37    /// ```text           
38    ///  old image     new image
39    ///  ┌─────────┐   ┌──────────┐
40    ///  │a b c d e│   │a b c b a │
41    ///  │f g h i j│   │f g h g f │
42    ///  └─────────┘   └──────────┘
43    /// ```
44    East,
45    ///
46    /// ```text           
47    ///  old image     new image
48    ///  ┌─────────┐   ┌──────────┐
49    ///  │a b c d e│   │e d c d e │
50    ///  │f g h i j│   │j i h i j │
51    ///  └─────────┘   └──────────┘
52    /// ```
53    West
54}
55
56/// Rearrange the pixels along a certain axis.
57///
58/// To see the effect of this
59/// see the image [mirror-modes](crate::mirror::MirrorMode) documentation
60/// for each used mode
61pub struct Mirror {
62    mode: MirrorMode
63}
64
65impl Mirror {
66    /// Create a new mirror filter
67    #[must_use]
68    pub fn new(mode: MirrorMode) -> Mirror {
69        Self { mode }
70    }
71}
72
73impl OperationsTrait for Mirror {
74    fn name(&self) -> &'static str {
75        "Mirror"
76    }
77
78    fn execute_impl(&self, image: &mut Image) -> Result<(), ImageErrors> {
79        let (width, height) = image.dimensions();
80        let depth = image.depth();
81
82        for channel in image.channels_mut(false) {
83            match depth.bit_type() {
84                BitType::U8 => {
85                    mirror(
86                        channel.reinterpret_as_mut::<u8>()?,
87                        width,
88                        height,
89                        self.mode
90                    );
91                }
92
93                BitType::U16 => {
94                    mirror(
95                        channel.reinterpret_as_mut::<u16>()?,
96                        width,
97                        height,
98                        self.mode
99                    );
100                }
101                BitType::F32 => {
102                    mirror(
103                        channel.reinterpret_as_mut::<f32>()?,
104                        width,
105                        height,
106                        self.mode
107                    );
108                }
109                d => return Err(ImageErrors::ImageOperationNotImplemented(self.name(), d))
110            }
111        }
112
113        Ok(())
114    }
115    fn supported_types(&self) -> &'static [BitType] {
116        &[BitType::U8, BitType::U16, BitType::F32]
117    }
118}
119
120/// Mirror an image by duplicating pixels from one edge to the other half
121///
122/// E.g a mirror along the east direction looks like
123///
124/// ```text           
125///  old image     new image
126///  ┌─────────┐   ┌──────────┐
127///  │a b c d e│   │a b c b a │
128///  │f g h i j│   │f g h g f │
129///  └─────────┘   └──────────┘
130/// ```
131pub fn mirror<T: Copy>(in_pixels: &mut [T], width: usize, height: usize, mode: MirrorMode) {
132    if mode == MirrorMode::East || mode == MirrorMode::West {
133        for width_stride in in_pixels.chunks_exact_mut(width) {
134            // split into 2
135            let (left, right) = width_stride.split_at_mut(width / 2);
136
137            if mode == MirrorMode::West {
138                // write
139                left.iter().zip(right.iter_mut().rev()).for_each(|(l, r)| {
140                    *r = *l;
141                });
142            }
143            if mode == MirrorMode::East {
144                // write
145                left.iter_mut().zip(right.iter().rev()).for_each(|(l, r)| {
146                    *l = *r;
147                });
148            }
149        }
150    } else if mode == MirrorMode::North || mode == MirrorMode::South {
151        // split the image along the halfway axis
152        let halfway = width * (height / 2);
153
154        let (top, bottom) = in_pixels.split_at_mut(halfway);
155
156        for (top_width_stride, bottom_width_stride) in top
157            .chunks_exact_mut(width)
158            .zip(bottom.rchunks_exact_mut(width))
159        {
160            if mode == MirrorMode::North {
161                bottom_width_stride.copy_from_slice(top_width_stride);
162            } else if mode == MirrorMode::South {
163                top_width_stride.copy_from_slice(bottom_width_stride);
164            }
165        }
166    }
167}