fast image operations
Diffstat (limited to 'src/affine.rs')
| -rw-r--r-- | src/affine.rs | 149 |
1 files changed, 82 insertions, 67 deletions
diff --git a/src/affine.rs b/src/affine.rs index 362a746..393b571 100644 --- a/src/affine.rs +++ b/src/affine.rs @@ -1,92 +1,87 @@ -//! Manages the affine image transformations. -use crate::Image; +use crate::{FromRefMut, Image}; + +pub trait Rotations { + /// Rotate a image 180 degrees clockwise. + fn rot_180(&mut self); + /// Rotate a image 90 degrees clockwise. + /// # Safety + /// + /// UB if the image is not square + unsafe fn rot_90(&mut self); + /// Rotate a image 270 degrees clockwise, or 90 degrees anti clockwise. + /// # Safety + /// + /// UB if the image is not square + unsafe fn rot_270(&mut self); +} + +pub trait Flips { + /// Flip a image vertically. + fn flip_v(&mut self); -impl<const CHANNELS: usize> Image<Vec<u8>, CHANNELS> { /// Flip a image horizontally. - pub fn flip_h(&mut self) { + fn flip_h(&mut self); +} + +impl<const CHANNELS: usize> Flips for Image<Vec<u8>, CHANNELS> { + fn flip_h(&mut self) { self.as_mut().flip_h(); } - /// Flip a image vertically. - pub fn flip_v(&mut self) { + fn flip_v(&mut self) { self.as_mut().flip_v(); } } -impl<const CHANNELS: usize> Image<&mut [u8], CHANNELS> { - /// Flip a image vertically. - pub fn flip_v(&mut self) { +impl<const CHANNELS: usize> Flips for Image<&mut [u8], CHANNELS> { + fn flip_v(&mut self) { for y in 0..self.height() / 2 { for x in 0..self.width() { let y2 = self.height() - y - 1; - #[allow(clippy::multiple_unsafe_ops_per_block)] // SAFETY: within bounds - unsafe { - let p2 = self.pixel(x, y2); - let p = self.pixel(x, y); - self.set_pixel(x, y2, p); - self.set_pixel(x, y, p2); - } + 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) }; } } } - /// Flip a image horizontally. - pub fn flip_h(&mut self) { + fn flip_h(&mut self) { for y in 0..self.height() { for x in 0..self.width() / 2 { let x2 = self.width() - x - 1; - #[allow(clippy::multiple_unsafe_ops_per_block)] - // SAFETY: bounded - unsafe { - let p2 = self.pixel(x2, y); - let p = self.pixel(x, y); - self.set_pixel(x2, y, p); - self.set_pixel(x, y, p2); - } + 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) }; } } } } -impl<const CHANNELS: usize> Image<Vec<u8>, CHANNELS> { - /// Rotate a image 180 degrees clockwise. - pub fn rot_180(&mut self) { +impl<const CHANNELS: usize> Rotations for Image<Vec<u8>, CHANNELS> { + fn rot_180(&mut self) { self.as_mut().rot_180(); } - /// Rotate a image 90 degrees clockwise. - /// # Safety - /// - /// UB if the image is not square - pub unsafe fn rot_90(&mut self) { - // SAFETY: make sure to keep the safety docs linked + unsafe fn rot_90(&mut self) { unsafe { self.as_mut().rot_90() } } - /// 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) { - // SAFETY: idk this is just a convenience impl + unsafe fn rot_270(&mut self) { unsafe { self.as_mut().rot_270() } } } -impl<const CHANNELS: usize> Image<&mut [u8], CHANNELS> { - /// Rotate a image 180 degrees clockwise. - pub fn rot_180(&mut self) { +impl<const CHANNELS: usize> Rotations for Image<&mut [u8], CHANNELS> { + fn rot_180(&mut self) { for y in 0..self.height() / 2 { for x in 0..self.width() { - // SAFETY: x, y come from the loop, must be ok let p = unsafe { self.pixel(x, y) }; let x2 = self.width() - x - 1; let y2 = self.height() - y - 1; - // SAFETY: values are good let p2 = unsafe { self.pixel(x2, y2) }; - // SAFETY: swapping would be cool, alas. unsafe { self.set_pixel(x, y, p2) }; - // SAFETY: although maybe i can cast it to a `[[u8; CHANNELS]]` and swap that 🤔 unsafe { self.set_pixel(x2, y2, p) }; } } @@ -95,38 +90,26 @@ impl<const CHANNELS: usize> Image<&mut [u8], CHANNELS> { 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; - #[allow(clippy::multiple_unsafe_ops_per_block)] - // SAFETY: its just doing the swappy - unsafe { - let p = self.pixel(x, middle); - let p2 = self.pixel(x2, middle); - self.set_pixel(x, middle, p2); - self.set_pixel(x2, middle, p); - } + let p2 = unsafe { self.pixel(x2, middle) }; + unsafe { self.set_pixel(x, middle, p2) }; + unsafe { self.set_pixel(x2, middle, p) }; } } } - /// Rotate a image 90 degrees clockwise. - /// # Safety - /// - /// UB if the image is not square #[inline] - pub unsafe fn rot_90(&mut self) { + unsafe fn rot_90(&mut self) { // This is done by first flipping self.flip_v(); - // Then transposing the image, as to not allocate. + // Then transposing the image, to save allocations. // SAFETY: caller ensures square unsafe { transpose(self) }; } - /// Rotate a image 270 degrees clockwise, or 90 degrees anti clockwise. - /// # Safety - /// - /// UB if the image is not square #[inline] - pub unsafe fn rot_270(&mut self) { + unsafe fn rot_270(&mut self) { self.flip_h(); // SAFETY: caller ensures squareness unsafe { transpose(self) }; @@ -236,3 +219,35 @@ mod tests { ); } } + +#[cfg(test)] +mod bench { + use super::*; + extern crate test; + use crate::Image; + use test::Bencher; + + macro_rules! bench { + (fn $name: ident() { run $fn: ident() }) => { + #[bench] + fn $name(b: &mut Bencher) { + let mut img: Image<_, 4> = Image::new( + 64.try_into().unwrap(), + 64.try_into().unwrap(), + include_bytes!("../test_data/4_180x180.imgbuf").to_vec(), + ); + b.iter(|| { + for _ in 0..256 { + img.flip_h(); + } + }); + } + }; + } + + bench!(fn flip_h() { run flip_h() }); + bench!(fn flip_v() { run flip_v() }); + bench!(fn rotate_90() { run rot_90() }); + bench!(fn rotate_180() { run rot_180() }); + bench!(fn rotate_270() { run rot_270() }); +} |