use super::buffer::with_capacity;
use super::{Image, Sample};
use crate::{error::Result, transform::Transform};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Rotate90 {
turns: u8,
}
impl Rotate90 {
pub fn new(k: i64) -> Self {
Self {
turns: k.rem_euclid(4) as u8,
}
}
}
impl<T: Sample> Transform<Image<T>> for Rotate90 {
type Output = Image<T>;
fn apply(&self, _index: usize, image: Image<T>) -> Result<Image<T>> {
let (w, h, c) = (image.width(), image.height(), image.channels());
let (out_w, out_h) = match self.turns {
0 => return Ok(image),
2 => (w, h),
_ => (h, w),
};
let src = image.data();
let at = |y: usize, x: usize| -> (usize, usize) {
match self.turns {
1 => (x, w - 1 - y),
2 => (h - 1 - y, w - 1 - x),
_ => (h - 1 - x, y),
}
};
let mut data = with_capacity(src.len())?;
for y in 0..out_h {
for x in 0..out_w {
let (sy, sx) = at(y, x);
let start = (sy * w + sx) * c;
data.extend_from_slice(&src[start..start + c]);
}
}
Image::new(out_w, out_h, c, data)
}
}
#[cfg(test)]
mod tests {
use super::Rotate90;
use crate::image::Image;
use crate::transform::Transform;
#[test]
fn quarter_turns_like_numpy() {
let image = Image::new(3, 2, 1, (0u8..6).collect()).unwrap();
let turn = |k| Rotate90::new(k).apply(0, image.clone()).unwrap();
assert_eq!(turn(1).shape(), [3, 2, 1]);
assert_eq!(turn(1).data(), [2, 5, 1, 4, 0, 3]);
assert_eq!(turn(2).data(), [5, 4, 3, 2, 1, 0]);
assert_eq!(turn(3).data(), [3, 0, 4, 1, 5, 2]);
assert_eq!(turn(-1), turn(3));
assert_eq!(turn(4), image);
}
}