1use crate::core::image::{Rect, RgbImage, Size};
7
8pub fn apply_orientation(image: &mut RgbImage, orientation: u16) {
24 match orientation {
25 1 => {} 2 => flip_horizontal(image),
27 3 => rotate_180(image),
28 4 => flip_vertical(image),
29 5 => {
30 flip_horizontal(image);
32 rotate_90_ccw(image);
33 }
34 6 => rotate_90_cw(image),
35 7 => {
36 flip_horizontal(image);
38 rotate_90_cw(image);
39 }
40 8 => rotate_90_ccw(image),
41 _ => tracing::warn!(
42 "Unknown orientation value: {}, skipping transform",
43 orientation
44 ),
45 }
46}
47
48pub fn flip_horizontal(image: &mut RgbImage) {
50 let w = image.width() as usize;
51 let h = image.height() as usize;
52 for row in 0..h {
53 for col in 0..w / 2 {
54 let a = (row * w + col) * 3;
55 let b = (row * w + (w - 1 - col)) * 3;
56 image.data.swap(a, b);
57 image.data.swap(a + 1, b + 1);
58 image.data.swap(a + 2, b + 2);
59 }
60 }
61}
62
63pub fn flip_vertical(image: &mut RgbImage) {
65 let w = image.width() as usize;
66 let h = image.height() as usize;
67 for row in 0..h / 2 {
68 for col in 0..w {
69 let a = (row * w + col) * 3;
70 let b = ((h - 1 - row) * w + col) * 3;
71 image.data.swap(a, b);
72 image.data.swap(a + 1, b + 1);
73 image.data.swap(a + 2, b + 2);
74 }
75 }
76}
77
78pub fn rotate_180(image: &mut RgbImage) {
80 let n = image.data.len();
81 let mut i = 0;
82 let mut j = n - 3;
83 while i < j {
84 image.data.swap(i, j);
85 image.data.swap(i + 1, j + 1);
86 image.data.swap(i + 2, j + 2);
87 i += 3;
88 j -= 3;
89 }
90}
91
92pub fn rotate_90_cw(image: &mut RgbImage) {
96 let old_w = image.width() as usize;
97 let old_h = image.height() as usize;
98 let new_w = old_h;
99 let new_h = old_w;
100 let mut new_data = vec![0u16; new_w * new_h * 3];
101 for old_row in 0..old_h {
102 for old_col in 0..old_w {
103 let new_row = old_col;
104 let new_col = old_h - 1 - old_row;
105 let src = (old_row * old_w + old_col) * 3;
106 let dst = (new_row * new_w + new_col) * 3;
107 new_data[dst] = image.data[src];
108 new_data[dst + 1] = image.data[src + 1];
109 new_data[dst + 2] = image.data[src + 2];
110 }
111 }
112 image.data = new_data;
113 image.set_size(Size::new(new_w as u32, new_h as u32));
114}
115
116pub fn rotate_90_ccw(image: &mut RgbImage) {
120 let old_w = image.width() as usize;
121 let old_h = image.height() as usize;
122 let new_w = old_h;
123 let new_h = old_w;
124 let mut new_data = vec![0u16; new_w * new_h * 3];
125 for old_row in 0..old_h {
126 for old_col in 0..old_w {
127 let new_row = old_w - 1 - old_col;
128 let new_col = old_row;
129 let src = (old_row * old_w + old_col) * 3;
130 let dst = (new_row * new_w + new_col) * 3;
131 new_data[dst] = image.data[src];
132 new_data[dst + 1] = image.data[src + 1];
133 new_data[dst + 2] = image.data[src + 2];
134 }
135 }
136 image.data = new_data;
137 image.set_size(Size::new(new_w as u32, new_h as u32));
138}
139
140pub fn apply_crop(image: &mut RgbImage, crop: Rect) {
145 let x = crop.origin.x as usize;
146 let y = crop.origin.y as usize;
147 let w = crop.size.width as usize;
148 let h = crop.size.height as usize;
149
150 if x + w <= image.width() as usize && y + h <= image.height() as usize {
151 let img_width = image.width() as usize;
152 let mut new_data = Vec::with_capacity(w * h * 3);
153 for row in 0..h {
154 let src_base = ((y + row) * img_width + x) * 3;
155 new_data.extend_from_slice(&image.data[src_base..src_base + w * 3]);
156 }
157 image.set_size(Size::new(w as u32, h as u32));
158 image.data = new_data;
159 } else {
160 tracing::warn!(
161 "Crop region out of bounds: {:?} vs {}x{}",
162 crop,
163 image.width(),
164 image.height()
165 );
166 }
167}
168
169#[cfg(test)]
170mod tests {
171 use super::*;
172 use crate::core::image::{Point, Size};
173
174 fn make_image(w: u32, h: u32, data: Vec<u16>) -> RgbImage {
175 RgbImage::new(w, h, data)
176 }
177
178 #[test]
179 fn test_flip_horizontal_2x2() {
180 let mut img = make_image(2, 2, vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
182 flip_horizontal(&mut img);
183 assert_eq!(img.data, vec![4, 5, 6, 1, 2, 3, 10, 11, 12, 7, 8, 9]);
184 }
185
186 #[test]
187 fn test_flip_vertical_2x2() {
188 let mut img = make_image(2, 2, vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
189 flip_vertical(&mut img);
190 assert_eq!(img.data, vec![7, 8, 9, 10, 11, 12, 1, 2, 3, 4, 5, 6]);
191 }
192
193 #[test]
194 fn test_rotate_180_2x2() {
195 let mut img = make_image(2, 2, vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
196 rotate_180(&mut img);
197 assert_eq!(img.data, vec![10, 11, 12, 7, 8, 9, 4, 5, 6, 1, 2, 3]);
198 }
199
200 #[test]
201 fn test_rotate_90_cw_2x2() {
202 let mut img = make_image(2, 2, vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
205 rotate_90_cw(&mut img);
206 assert_eq!(img.width(), 2);
207 assert_eq!(img.height(), 2);
208 assert_eq!(img.data, vec![7, 8, 9, 1, 2, 3, 10, 11, 12, 4, 5, 6]);
209 }
210
211 #[test]
212 fn test_rotate_90_ccw_2x2() {
213 let mut img = make_image(2, 2, vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
216 rotate_90_ccw(&mut img);
217 assert_eq!(img.width(), 2);
218 assert_eq!(img.height(), 2);
219 assert_eq!(img.data, vec![4, 5, 6, 10, 11, 12, 1, 2, 3, 7, 8, 9]);
220 }
221
222 #[test]
223 fn test_rotate_90_cw_non_square() {
224 let mut img = make_image(
226 3,
227 2,
228 vec![1, 0, 0, 2, 0, 0, 3, 0, 0, 4, 0, 0, 5, 0, 0, 6, 0, 0],
229 );
230 rotate_90_cw(&mut img);
231 assert_eq!(img.width(), 2);
232 assert_eq!(img.height(), 3);
233 }
234
235 #[test]
236 fn test_apply_orientation_identity() {
237 let mut img = make_image(2, 2, vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
238 let original = img.data.clone();
239 apply_orientation(&mut img, 1);
240 assert_eq!(img.data, original);
241 }
242
243 #[test]
244 fn test_crop_basic() {
245 let mut data = Vec::with_capacity(4 * 4 * 3);
247 for i in 0..16 {
248 data.push(i as u16);
249 data.push(0);
250 data.push(0);
251 }
252 let mut img = make_image(4, 4, data);
253 apply_crop(&mut img, Rect::new(Point::new(1, 1), Size::new(2, 2)));
254 assert_eq!(img.width(), 2);
255 assert_eq!(img.height(), 2);
256 assert_eq!(img.data[0], 5); assert_eq!(img.data[3], 6); }
260
261 #[test]
262 fn test_crop_out_of_bounds() {
263 let mut img = make_image(4, 4, vec![0u16; 4 * 4 * 3]);
264 let original_size = img.size();
265 apply_crop(&mut img, Rect::new(Point::new(3, 3), Size::new(2, 2)));
266 assert_eq!(img.size(), original_size);
268 }
269}