use alloc::vec;
use alloc::vec::Vec;
pub(crate) fn apply_orientation(
mut rgb: Vec<f32>,
width: usize,
height: usize,
orientation: u16,
) -> (Vec<f32>, usize, usize) {
let w = width;
let h = height;
match orientation {
0 | 1 => (rgb, w, h),
2 => {
flip_horizontal(&mut rgb, w, h);
(rgb, w, h)
}
3 => {
rotate_180(&mut rgb, w, h);
(rgb, w, h)
}
4 => {
flip_vertical(&mut rgb, w, h);
(rgb, w, h)
}
5 => {
let out = remap(&rgb, w, h, w, |dr, dc| (dc, dr));
(out, h, w)
}
6 => {
let out = remap(&rgb, w, h, w, |dr, dc| (h - 1 - dc, dr));
(out, h, w)
}
7 => {
let out = remap(&rgb, w, h, w, |dr, dc| (h - 1 - dc, w - 1 - dr));
(out, h, w)
}
8 => {
let out = remap(&rgb, w, h, w, |dr, dc| (dc, w - 1 - dr));
(out, h, w)
}
_ => (rgb, w, h),
}
}
fn flip_horizontal(rgb: &mut [f32], width: usize, height: usize) {
for r in 0..height {
for c in 0..width / 2 {
let l = (r * width + c) * 3;
let ri = (r * width + (width - 1 - c)) * 3;
for ch in 0..3 {
rgb.swap(l + ch, ri + ch);
}
}
}
}
fn rotate_180(rgb: &mut [f32], width: usize, height: usize) {
let n = width * height;
for i in 0..n / 2 {
let j = n - 1 - i;
let a = i * 3;
let b = j * 3;
for ch in 0..3 {
rgb.swap(a + ch, b + ch);
}
}
}
fn flip_vertical(rgb: &mut [f32], width: usize, height: usize) {
let row_len = width * 3;
for r in 0..height / 2 {
let top = r * row_len;
let bot = (height - 1 - r) * row_len;
for i in 0..row_len {
rgb.swap(top + i, bot + i);
}
}
}
fn remap(
rgb: &[f32],
src_w: usize,
new_w: usize,
new_h: usize,
map: impl Fn(usize, usize) -> (usize, usize),
) -> Vec<f32> {
let mut out = vec![0.0f32; new_w * new_h * 3];
for dr in 0..new_h {
for dc in 0..new_w {
let (sr, sc) = map(dr, dc);
let si = (sr * src_w + sc) * 3;
let di = (dr * new_w + dc) * 3;
out[di] = rgb[si];
out[di + 1] = rgb[si + 1];
out[di + 2] = rgb[si + 2];
}
}
out
}
#[cfg(test)]
mod tests {
extern crate std;
use super::*;
fn make_test_image() -> (Vec<f32>, usize, usize) {
let w = 3;
let h = 2;
let mut rgb = Vec::with_capacity(w * h * 3);
for r in 0..h {
for c in 0..w {
let v = (r * 10 + c) as f32;
rgb.extend_from_slice(&[v, v, v]);
}
}
(rgb, w, h)
}
fn pixel_at(rgb: &[f32], width: usize, row: usize, col: usize) -> f32 {
rgb[(row * width + col) * 3]
}
#[test]
fn orient_1_identity() {
let (rgb, w, h) = make_test_image();
let (out, nw, nh) = apply_orientation(rgb, w, h, 1);
assert_eq!((nw, nh), (3, 2));
assert_eq!(pixel_at(&out, nw, 0, 0), 0.0);
assert_eq!(pixel_at(&out, nw, 0, 2), 2.0);
assert_eq!(pixel_at(&out, nw, 1, 0), 10.0);
}
#[test]
fn orient_2_flip_horizontal() {
let (rgb, w, h) = make_test_image();
let (out, nw, nh) = apply_orientation(rgb, w, h, 2);
assert_eq!((nw, nh), (3, 2));
assert_eq!(pixel_at(&out, nw, 0, 0), 2.0);
assert_eq!(pixel_at(&out, nw, 0, 2), 0.0);
assert_eq!(pixel_at(&out, nw, 1, 0), 12.0);
assert_eq!(pixel_at(&out, nw, 1, 2), 10.0);
}
#[test]
fn orient_3_rotate_180() {
let (rgb, w, h) = make_test_image();
let (out, nw, nh) = apply_orientation(rgb, w, h, 3);
assert_eq!((nw, nh), (3, 2));
assert_eq!(pixel_at(&out, nw, 0, 0), 12.0);
assert_eq!(pixel_at(&out, nw, 1, 2), 0.0);
}
#[test]
fn orient_4_flip_vertical() {
let (rgb, w, h) = make_test_image();
let (out, nw, nh) = apply_orientation(rgb, w, h, 4);
assert_eq!((nw, nh), (3, 2));
assert_eq!(pixel_at(&out, nw, 0, 0), 10.0);
assert_eq!(pixel_at(&out, nw, 1, 0), 0.0);
}
#[test]
fn orient_5_transpose() {
let (rgb, w, h) = make_test_image();
let (out, nw, nh) = apply_orientation(rgb, w, h, 5);
assert_eq!((nw, nh), (2, 3));
assert_eq!(pixel_at(&out, nw, 0, 0), 0.0);
assert_eq!(pixel_at(&out, nw, 0, 1), 10.0);
assert_eq!(pixel_at(&out, nw, 1, 0), 1.0);
assert_eq!(pixel_at(&out, nw, 2, 1), 12.0);
}
#[test]
fn orient_6_rotate_90_cw() {
let (rgb, w, h) = make_test_image();
let (out, nw, nh) = apply_orientation(rgb, w, h, 6);
assert_eq!((nw, nh), (2, 3));
assert_eq!(pixel_at(&out, nw, 0, 0), 10.0);
assert_eq!(pixel_at(&out, nw, 0, 1), 0.0);
assert_eq!(pixel_at(&out, nw, 2, 0), 12.0);
assert_eq!(pixel_at(&out, nw, 2, 1), 2.0);
}
#[test]
fn orient_7_transverse() {
let (rgb, w, h) = make_test_image();
let (out, nw, nh) = apply_orientation(rgb, w, h, 7);
assert_eq!((nw, nh), (2, 3));
assert_eq!(pixel_at(&out, nw, 0, 0), 12.0);
assert_eq!(pixel_at(&out, nw, 2, 1), 0.0);
}
#[test]
fn orient_8_rotate_270_cw() {
let (rgb, w, h) = make_test_image();
let (out, nw, nh) = apply_orientation(rgb, w, h, 8);
assert_eq!((nw, nh), (2, 3));
assert_eq!(pixel_at(&out, nw, 0, 0), 2.0);
assert_eq!(pixel_at(&out, nw, 0, 0), 2.0);
assert_eq!(pixel_at(&out, nw, 0, 1), 12.0);
assert_eq!(pixel_at(&out, nw, 2, 0), 0.0);
assert_eq!(pixel_at(&out, nw, 2, 1), 10.0);
}
#[test]
fn orient_roundtrips() {
let (rgb, w, h) = make_test_image();
let original = rgb.clone();
let (rotated, nw, nh) = apply_orientation(rgb, w, h, 6);
let (back, fw, fh) = apply_orientation(rotated, nw, nh, 8);
assert_eq!((fw, fh), (w, h));
assert_eq!(original, back);
}
}