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}