fast image operations
Diffstat (limited to 'src/affine.rs')
-rw-r--r--src/affine.rs149
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() });
+}