fast image operations
Diffstat (limited to 'src/affine.rs')
| -rw-r--r-- | src/affine.rs | 149 |
1 files changed, 67 insertions, 82 deletions
diff --git a/src/affine.rs b/src/affine.rs index 393b571..362a746 100644 --- a/src/affine.rs +++ b/src/affine.rs @@ -1,87 +1,92 @@ -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); +//! Manages the affine image transformations. +use crate::Image; +impl<const CHANNELS: usize> Image<Vec<u8>, CHANNELS> { /// Flip a image horizontally. - fn flip_h(&mut self); -} - -impl<const CHANNELS: usize> Flips for Image<Vec<u8>, CHANNELS> { - fn flip_h(&mut self) { + pub fn flip_h(&mut self) { self.as_mut().flip_h(); } - fn flip_v(&mut self) { + /// Flip a image vertically. + pub fn flip_v(&mut self) { self.as_mut().flip_v(); } } -impl<const CHANNELS: usize> Flips for Image<&mut [u8], CHANNELS> { - fn flip_v(&mut self) { +impl<const CHANNELS: usize> Image<&mut [u8], CHANNELS> { + /// Flip a image vertically. + pub 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 - 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) }; + 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); + } } } } - fn flip_h(&mut self) { + /// Flip a image horizontally. + pub fn flip_h(&mut self) { 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) }; + #[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); + } } } } } -impl<const CHANNELS: usize> Rotations for Image<Vec<u8>, CHANNELS> { - fn rot_180(&mut self) { +impl<const CHANNELS: usize> Image<Vec<u8>, CHANNELS> { + /// Rotate a image 180 degrees clockwise. + pub fn rot_180(&mut self) { self.as_mut().rot_180(); } - unsafe fn rot_90(&mut self) { + /// 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 { self.as_mut().rot_90() } } - unsafe fn rot_270(&mut self) { + /// 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 { self.as_mut().rot_270() } } } -impl<const CHANNELS: usize> Rotations for Image<&mut [u8], CHANNELS> { - fn rot_180(&mut self) { +impl<const CHANNELS: usize> Image<&mut [u8], CHANNELS> { + /// Rotate a image 180 degrees clockwise. + pub 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) }; } } @@ -90,26 +95,38 @@ impl<const CHANNELS: usize> Rotations for 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; - let p2 = unsafe { self.pixel(x2, middle) }; - unsafe { self.set_pixel(x, middle, p2) }; - unsafe { self.set_pixel(x2, middle, p) }; + #[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); + } } } } + /// Rotate a image 90 degrees clockwise. + /// # Safety + /// + /// UB if the image is not square #[inline] - unsafe fn rot_90(&mut self) { + pub unsafe fn rot_90(&mut self) { // This is done by first flipping self.flip_v(); - // Then transposing the image, to save allocations. + // Then transposing the image, as to not allocate. // 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] - unsafe fn rot_270(&mut self) { + pub unsafe fn rot_270(&mut self) { self.flip_h(); // SAFETY: caller ensures squareness unsafe { transpose(self) }; @@ -219,35 +236,3 @@ 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() }); -} |