2023-09-05 20:31:53 -05:00
|
|
|
//! Manages the affine image transformations.
|
2023-09-04 21:59:33 -05:00
|
|
|
use crate::Image;
|
2023-09-04 18:45:23 -05:00
|
|
|
|
2023-09-04 21:59:33 -05:00
|
|
|
impl<const CHANNELS: usize> Image<Vec<u8>, CHANNELS> {
|
2023-09-04 18:45:23 -05:00
|
|
|
/// Flip a image horizontally.
|
2023-09-04 21:59:33 -05:00
|
|
|
pub fn flip_h(&mut self) {
|
2023-09-04 18:45:23 -05:00
|
|
|
self.as_mut().flip_h();
|
|
|
|
}
|
2023-09-04 21:59:33 -05:00
|
|
|
/// Flip a image vertically.
|
|
|
|
pub fn flip_v(&mut self) {
|
2023-09-04 18:45:23 -05:00
|
|
|
self.as_mut().flip_v();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-04 21:59:33 -05:00
|
|
|
impl<const CHANNELS: usize> Image<&mut [u8], CHANNELS> {
|
|
|
|
/// Flip a image vertically.
|
|
|
|
pub fn flip_v(&mut self) {
|
2023-09-04 18:45:23 -05:00
|
|
|
for y in 0..self.height() / 2 {
|
|
|
|
for x in 0..self.width() {
|
|
|
|
let y2 = self.height() - y - 1;
|
|
|
|
// SAFETY: within bounds
|
|
|
|
let p2 = unsafe { self.pixel(x, y2) };
|
|
|
|
let p = unsafe { self.pixel(x, y) };
|
|
|
|
unsafe { self.set_pixel(x, y2, p) };
|
|
|
|
unsafe { self.set_pixel(x, y, p2) };
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-04 21:59:33 -05:00
|
|
|
/// Flip a image horizontally.
|
2023-09-05 05:56:30 -05:00
|
|
|
pub fn flip_h(&mut self) {
|
2023-09-04 18:45:23 -05:00
|
|
|
for y in 0..self.height() {
|
|
|
|
for x in 0..self.width() / 2 {
|
|
|
|
let x2 = self.width() - x - 1;
|
|
|
|
let p2 = unsafe { self.pixel(x2, y) };
|
|
|
|
let p = unsafe { self.pixel(x, y) };
|
|
|
|
unsafe { self.set_pixel(x2, y, p) };
|
|
|
|
unsafe { self.set_pixel(x, y, p2) };
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-04 21:59:33 -05:00
|
|
|
impl<const CHANNELS: usize> Image<Vec<u8>, CHANNELS> {
|
|
|
|
/// Rotate a image 180 degrees clockwise.
|
|
|
|
pub fn rot_180(&mut self) {
|
2023-09-04 18:45:23 -05:00
|
|
|
self.as_mut().rot_180();
|
|
|
|
}
|
|
|
|
|
2023-09-04 21:59:33 -05:00
|
|
|
/// Rotate a image 90 degrees clockwise.
|
|
|
|
/// # Safety
|
|
|
|
///
|
|
|
|
/// UB if the image is not square
|
|
|
|
pub unsafe fn rot_90(&mut self) {
|
2023-09-04 18:45:23 -05:00
|
|
|
unsafe { self.as_mut().rot_90() }
|
|
|
|
}
|
|
|
|
|
2023-09-04 21:59:33 -05:00
|
|
|
/// Rotate a image 270 degrees clockwise, or 90 degrees anti clockwise.
|
|
|
|
/// # Safety
|
|
|
|
///
|
|
|
|
/// UB if the image is not square
|
|
|
|
pub unsafe fn rot_270(&mut self) {
|
2023-09-04 18:45:23 -05:00
|
|
|
unsafe { self.as_mut().rot_270() }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-04 21:59:33 -05:00
|
|
|
impl<const CHANNELS: usize> Image<&mut [u8], CHANNELS> {
|
|
|
|
/// Rotate a image 180 degrees clockwise.
|
|
|
|
pub fn rot_180(&mut self) {
|
2023-09-04 18:45:23 -05:00
|
|
|
for y in 0..self.height() / 2 {
|
|
|
|
for x in 0..self.width() {
|
|
|
|
let p = unsafe { self.pixel(x, y) };
|
|
|
|
let x2 = self.width() - x - 1;
|
|
|
|
let y2 = self.height() - y - 1;
|
|
|
|
let p2 = unsafe { self.pixel(x2, y2) };
|
|
|
|
unsafe { self.set_pixel(x, y, p2) };
|
|
|
|
unsafe { self.set_pixel(x2, y2, p) };
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if self.height() % 2 != 0 {
|
|
|
|
let middle = self.height() / 2;
|
|
|
|
|
|
|
|
for x in 0..self.width() / 2 {
|
|
|
|
let p = unsafe { self.pixel(x, middle) };
|
|
|
|
let x2 = self.width() - x - 1;
|
|
|
|
let p2 = unsafe { self.pixel(x2, middle) };
|
|
|
|
unsafe { self.set_pixel(x, middle, p2) };
|
|
|
|
unsafe { self.set_pixel(x2, middle, p) };
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-04 21:59:33 -05:00
|
|
|
/// Rotate a image 90 degrees clockwise.
|
|
|
|
/// # Safety
|
|
|
|
///
|
|
|
|
/// UB if the image is not square
|
2023-09-04 18:45:23 -05:00
|
|
|
#[inline]
|
2023-09-04 21:59:33 -05:00
|
|
|
pub unsafe fn rot_90(&mut self) {
|
2023-09-04 18:45:23 -05:00
|
|
|
// This is done by first flipping
|
|
|
|
self.flip_v();
|
2023-09-04 21:59:33 -05:00
|
|
|
// Then transposing the image, as to not allocate.
|
2023-09-04 19:27:10 -05:00
|
|
|
// SAFETY: caller ensures square
|
2023-09-04 18:45:23 -05:00
|
|
|
unsafe { transpose(self) };
|
|
|
|
}
|
|
|
|
|
2023-09-04 21:59:33 -05:00
|
|
|
/// Rotate a image 270 degrees clockwise, or 90 degrees anti clockwise.
|
|
|
|
/// # Safety
|
|
|
|
///
|
|
|
|
/// UB if the image is not square
|
2023-09-04 18:45:23 -05:00
|
|
|
#[inline]
|
2023-09-04 21:59:33 -05:00
|
|
|
pub unsafe fn rot_270(&mut self) {
|
2023-09-04 18:45:23 -05:00
|
|
|
self.flip_h();
|
2023-09-04 19:27:10 -05:00
|
|
|
// SAFETY: caller ensures squareness
|
2023-09-04 18:45:23 -05:00
|
|
|
unsafe { transpose(self) };
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-04 19:27:10 -05:00
|
|
|
/// Transpose a square image
|
2023-09-04 18:45:23 -05:00
|
|
|
/// # Safety
|
|
|
|
///
|
|
|
|
/// UB if supplied image rectangular
|
|
|
|
unsafe fn transpose<const CHANNELS: usize>(img: &mut Image<&mut [u8], CHANNELS>) {
|
|
|
|
debug_assert_eq!(img.width(), img.height());
|
2023-09-04 19:27:10 -05:00
|
|
|
let size = img.width() as usize;
|
|
|
|
// SAFETY: no half pixels
|
|
|
|
let b = unsafe { img.buffer.as_chunks_unchecked_mut::<CHANNELS>() };
|
2023-09-04 18:45:23 -05:00
|
|
|
for i in 0..size {
|
|
|
|
for j in i..size {
|
2023-09-04 19:27:10 -05:00
|
|
|
// SAFETY: caller ensures squarity
|
|
|
|
unsafe {
|
|
|
|
b.swap_unchecked(i * size + j, j * size + i);
|
|
|
|
};
|
2023-09-04 18:45:23 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
use crate::img;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn rotate_90() {
|
|
|
|
let mut from = img![
|
|
|
|
[00, 01]
|
|
|
|
[02, 10]
|
|
|
|
];
|
|
|
|
unsafe { from.rot_90() };
|
|
|
|
assert_eq!(
|
|
|
|
from,
|
|
|
|
img![
|
|
|
|
[02, 00]
|
|
|
|
[10, 01]
|
|
|
|
]
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn rotate_180() {
|
|
|
|
let mut from = img![
|
|
|
|
[00, 01]
|
|
|
|
[02, 10]
|
|
|
|
];
|
|
|
|
from.rot_180();
|
|
|
|
assert_eq!(
|
|
|
|
from,
|
|
|
|
img![
|
|
|
|
[10, 02]
|
|
|
|
[01, 00]
|
|
|
|
]
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn rotate_270() {
|
|
|
|
let mut from = img![
|
|
|
|
[00, 01]
|
|
|
|
[20, 10]
|
|
|
|
];
|
|
|
|
unsafe { from.rot_270() };
|
|
|
|
assert_eq!(
|
|
|
|
from,
|
|
|
|
img![
|
|
|
|
[01, 10]
|
|
|
|
[00, 20]
|
|
|
|
]
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn flip_vertical() {
|
|
|
|
let mut from = img![
|
|
|
|
[90, 01]
|
|
|
|
[21, 42]
|
|
|
|
];
|
|
|
|
from.flip_v();
|
|
|
|
assert_eq!(
|
|
|
|
from,
|
|
|
|
img![
|
|
|
|
[21, 42]
|
|
|
|
[90, 01]
|
|
|
|
]
|
|
|
|
);
|
|
|
|
}
|
|
|
|
#[test]
|
|
|
|
fn flip_horizontal() {
|
|
|
|
let mut from = img![
|
|
|
|
[90, 01]
|
|
|
|
[21, 42]
|
|
|
|
];
|
|
|
|
from.flip_h();
|
|
|
|
assert_eq!(
|
|
|
|
from,
|
|
|
|
img![
|
|
|
|
[01, 90]
|
|
|
|
[42, 21]
|
|
|
|
]
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|