fast image operations
| -rw-r--r-- | src/affine.rs | 14 | ||||
| -rw-r--r-- | src/drawing/box.rs | 10 | ||||
| -rw-r--r-- | src/drawing/circle.rs | 4 | ||||
| -rw-r--r-- | src/drawing/line.rs | 2 | ||||
| -rw-r--r-- | src/drawing/poly.rs | 2 | ||||
| -rw-r--r-- | src/drawing/tri.rs | 2 | ||||
| -rw-r--r-- | src/dyn/mod.rs | 2 | ||||
| -rw-r--r-- | src/lib.rs | 52 | ||||
| -rw-r--r-- | src/overlay.rs | 18 | ||||
| -rw-r--r-- | src/slicing.rs | 2 | ||||
| -rw-r--r-- | src/sub.rs | 2 |
11 files changed, 57 insertions, 53 deletions
diff --git a/src/affine.rs b/src/affine.rs index 7a971e5..fe3656d 100644 --- a/src/affine.rs +++ b/src/affine.rs @@ -16,7 +16,7 @@ impl<const CHANNELS: usize> ImageCloner<'_, CHANNELS> { // SAFETY: looping over self, all ok (could be safe versions, bounds would be elided) let p = unsafe { self.pixel(x, y) }; // SAFETY: looping over self. - unsafe { out.write(&p, (x, self.height() - y - 1)) }; + unsafe { out.write(p, (x, self.height() - y - 1)) }; } } // SAFETY: init @@ -37,7 +37,7 @@ impl<const CHANNELS: usize> ImageCloner<'_, CHANNELS> { // SAFETY: looping over self, all ok let p = unsafe { self.pixel(x, y) }; // SAFETY: looping over self, all ok - unsafe { out.write(&p, (self.width() - x - 1, y)) }; + unsafe { out.write(p, (self.width() - x - 1, y)) }; } } // SAFETY: init @@ -54,10 +54,7 @@ impl<const CHANNELS: usize, T: AsMut<[u8]> + AsRef<[u8]>> Image<T, CHANNELS> { #[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); + self.swap_pixel((x, y2), (x, y)) } } } @@ -71,10 +68,7 @@ impl<const CHANNELS: usize, T: AsMut<[u8]> + AsRef<[u8]>> Image<T, CHANNELS> { #[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); + self.swap_pixel((x2, y), (x, y)) } } } diff --git a/src/drawing/box.rs b/src/drawing/box.rs index b4b1d3e..a2c90bd 100644 --- a/src/drawing/box.rs +++ b/src/drawing/box.rs @@ -15,19 +15,19 @@ impl<T: AsMut<[u8]> + AsRef<[u8]>, const CHANNELS: usize> Image<T, CHANNELS> { // skip sides, leave that to second loop for x in clamp(x1 + 1..width + x1, 0..self.width()) { // SAFETY: clamped to bounds - unsafe { self.set_pixel(x, y1, c) }; + unsafe { self.set_pixel(x, y1, &c) }; } for x in clamp(x1 + 1..width + x1, 0..self.width()) { // SAFETY: clamped to bounds - unsafe { self.set_pixel(x, (y1 + height).min(self.height() - 1), c) }; + unsafe { self.set_pixel(x, (y1 + height).min(self.height() - 1), &c) }; } for y in clamp(y1..height + y1 + 1, 0..self.height()) { // SAFETY: clamped to bounds - unsafe { self.set_pixel(x1, y, c) }; + unsafe { self.set_pixel(x1, y, &c) }; } for y in clamp(y1..height + y1 + 1, 0..self.height()) { // SAFETY: clamped to bounds - unsafe { self.set_pixel((x1 + width).min(self.width() - 1), y, c) }; + unsafe { self.set_pixel((x1 + width).min(self.width() - 1), y, &c) }; } } @@ -42,7 +42,7 @@ impl<T: AsMut<[u8]> + AsRef<[u8]>, const CHANNELS: usize> Image<T, CHANNELS> { for x in clamp(x1..1 + width + x1, 0..self.width()) { for y in clamp(y1..1 + height + y1, 0..self.height()) { // SAFETY: clamped to bounds - unsafe { self.set_pixel(x, y, c) }; + unsafe { self.set_pixel(x, y, &c) }; } } } diff --git a/src/drawing/circle.rs b/src/drawing/circle.rs index 1adb7e5..421cb6a 100644 --- a/src/drawing/circle.rs +++ b/src/drawing/circle.rs @@ -18,7 +18,7 @@ impl<T: AsMut<[u8]> + AsRef<[u8]>, const CHANNELS: usize> Image<T, CHANNELS> { ($($x:expr,$y:expr);+;) => { $(if $x >= 0 && $x < self.width() as i32 && $y >= 0 && $y < self.height() as i32 { // SAFETY: ^ - unsafe { self.set_pixel($x as u32, $y as u32, c) }; + unsafe { self.set_pixel($x as u32, $y as u32, &c) }; })+ }; } @@ -58,7 +58,7 @@ impl<T: AsMut<[u8]> + AsRef<[u8]>, const CHANNELS: usize> Image<T, CHANNELS> { let y = y + yc; if x >= 0 && x < self.width() as i32 && y >= 0 && y < self.height() as i32 { // SAFETY: ^ - unsafe { self.set_pixel(x as u32, y as u32, c) }; + unsafe { self.set_pixel(x as u32, y as u32, &c) }; } } } diff --git a/src/drawing/line.rs b/src/drawing/line.rs index f180680..24c0df4 100644 --- a/src/drawing/line.rs +++ b/src/drawing/line.rs @@ -14,7 +14,7 @@ impl<T: AsMut<[u8]> + AsRef<[u8]>, const CHANNELS: usize> Image<T, CHANNELS> { .flatten() .for_each(|(x, y)| { // SAFETY: x, y are clipped to self. - unsafe { self.set_pixel(x, y, color) } + unsafe { self.set_pixel(x, y, &color) } }); } diff --git a/src/drawing/poly.rs b/src/drawing/poly.rs index 2ef27ed..5a67a07 100644 --- a/src/drawing/poly.rs +++ b/src/drawing/poly.rs @@ -59,7 +59,7 @@ impl<T: AsMut<[u8]> + AsRef<[u8]>, const CHANNELS: usize> Image<T, CHANNELS> { for x in from..=to { // SAFETY: bounds are checked - unsafe { self.set_pixel(x as u32, y as u32, c) }; + unsafe { self.set_pixel(x as u32, y as u32, &c) }; } } } diff --git a/src/drawing/tri.rs b/src/drawing/tri.rs index ab89acd..7ca13b6 100644 --- a/src/drawing/tri.rs +++ b/src/drawing/tri.rs @@ -59,7 +59,7 @@ impl<T: AsMut<[u8]> + AsRef<[u8]>, const CHANNELS: usize> Image<T, CHANNELS> { > 0. } { // SAFETY: x, y are bounded - unsafe { self.set_pixel(x, y, col) }; + unsafe { self.set_pixel(x, y, &col) }; } } } diff --git a/src/dyn/mod.rs b/src/dyn/mod.rs index f3817d3..a760813 100644 --- a/src/dyn/mod.rs +++ b/src/dyn/mod.rs @@ -142,7 +142,7 @@ impl<T: AsRef<[u8]>> DynImage<T> { [u8; P]: PFrom<3>, [u8; P]: PFrom<4>, { - e!(self, |i| PFrom::pfrom(unsafe { i.pixel(x, y) })) + e!(self, |i| PFrom::pfrom(unsafe { *i.pixel(x, y) })) } /// Bytes of this image. @@ -86,12 +86,7 @@ )] use array_chunks::*; use hinted::HintExt; -use std::{ - hint::assert_unchecked, - intrinsics::transmute_unchecked, - num::NonZeroU32, - ops::{Range, RangeBounds, RangeInclusive}, -}; +use std::{hint::assert_unchecked, intrinsics::transmute_unchecked, num::NonZeroU32, ops::Range}; mod affine; #[cfg(feature = "blur")] @@ -539,13 +534,15 @@ impl<T, const CHANNELS: usize> Image<T, CHANNELS> { } /// Get a pixel. Optionally. Yeah! - pub fn get_pixel<U: Copy>(&self, x: u32, y: u32) -> Option<[U; CHANNELS]> + pub fn get_pixel<U>(&self, x: u32, y: u32) -> Option<&[U; CHANNELS]> where T: AsRef<[U]>, { ((x < self.width()) & (y < self.height())).then(|| unsafe { - self.buffer().as_ref()[self.slice(x, y)] - .try_into() + self.buffer() + .as_ref() + .get_unchecked(self.slice(x, y)) + .as_array() .unwrap_unchecked() }) } @@ -556,7 +553,7 @@ impl<T, const CHANNELS: usize> Image<T, CHANNELS> { /// - UB if x, y is out of bounds /// - UB if buffer is too small #[inline] - pub unsafe fn pixel<U: Copy>(&self, x: u32, y: u32) -> [U; CHANNELS] + pub unsafe fn pixel<U>(&self, x: u32, y: u32) -> &[U; CHANNELS] where T: AsRef<[U]>, { @@ -630,11 +627,14 @@ impl<T, const CHANNELS: usize> Image<T, CHANNELS> { where T: AsMut<[U]> + AsRef<[U]>, { - let idx = self.slice(x, y); - self.buffer - .as_mut() - .get_mut(idx) - .map(|x| x.try_into().unwrap()) + let sl = self.slice(x, y); + ((x < self.width()) & (y < self.height())).then(|| unsafe { + self.buffer_mut() + .as_mut() + .get_unchecked_mut(sl) + .as_mut_array() + .unwrap_unchecked() + }) } /// iterator over columns @@ -661,11 +661,11 @@ impl<T, const CHANNELS: usize> Image<T, CHANNELS> { /// ); /// ``` #[must_use = "iterators are lazy and do nothing unless consumed"] - pub fn cols<U: Copy>( - &self, + pub fn cols<'a, U: Copy + 'a>( + &'a self, ) -> impl DoubleEndedIterator + ExactSizeIterator< - Item = impl ExactSizeIterator + DoubleEndedIterator<Item = [U; CHANNELS]> + '_, + Item = impl ExactSizeIterator + DoubleEndedIterator<Item = &'a [U; CHANNELS]>, > where T: AsRef<[U]>, @@ -741,10 +741,10 @@ impl<T, const CHANNELS: usize> Image<T, CHANNELS> { /// Get the pixels from an iterator. /// # Safety /// the points must be on the image. - pub unsafe fn pixels_of<'l, U: Copy>( + pub unsafe fn pixels_of<'l, U: Copy + 'l>( &'l self, iterator: impl ExactSizeIterator<Item = (u32, u32)> + 'l, - ) -> impl ExactSizeIterator<Item = [U; CHANNELS]> + 'l + ) -> impl ExactSizeIterator<Item = &'l [U; CHANNELS]> where T: AsRef<[U]>, { @@ -789,12 +789,22 @@ impl<T: AsMut<[u8]> + AsRef<[u8]>, const CHANNELS: usize> Image<T, CHANNELS> { /// /// UB if x, y is out of bounds. #[inline] - pub unsafe fn set_pixel(&mut self, x: u32, y: u32, px: [u8; CHANNELS]) { + pub unsafe fn set_pixel(&mut self, x: u32, y: u32, px: &[u8; CHANNELS]) { // SAFETY: Caller says that x, y is in bounds let out = unsafe { self.pixel_mut(x, y) }; // SAFETY: px must be CHANNELS long unsafe { std::ptr::copy_nonoverlapping(px.as_ptr(), out.as_mut_ptr(), CHANNELS) }; } + + /// Such swap. not actually implemented properly. + pub unsafe fn swap_pixel(&mut self, (x1, y1): (u32, u32), (x2, y2): (u32, u32)) { + unsafe { + let &p2 = self.pixel(x2, y2); + let &p1 = self.pixel(x1, y1); + self.set_pixel(x2, y2, &p1); + self.set_pixel(x1, y1, &p2); + } + } } impl<const CHANNELS: usize> Image<&mut [u8], CHANNELS> { diff --git a/src/overlay.rs b/src/overlay.rs index da70713..79fc4b7 100644 --- a/src/overlay.rs +++ b/src/overlay.rs @@ -191,7 +191,7 @@ where for j in 0..with.height() { for i in 0..with.width() { // SAFETY: i, j is in bounds. - let their_px = unsafe { &with.pixel(i, j) }; + let their_px = unsafe { with.pixel(i, j) }; let our_px = unsafe { self.pixel_mut(i + x, j + y) }; our_px.blend(*their_px); } @@ -212,7 +212,7 @@ impl<T: AsMut<[u8]> + AsRef<[u8]>> Image<T, 3> { ) { for j in 0..with.height() { for i in 0..with.width() { - let &[their_alpha] = unsafe { &with.pixel(i, j) }; + let &[their_alpha] = unsafe { with.pixel(i, j) }; let our_pixel = unsafe { self.pixel_mut(i + x, j + y) }; crate::pixels::blending::blend_alpha_and_color(their_alpha, color, our_pixel); } @@ -320,7 +320,7 @@ impl<T: AsMut<[u8]> + AsRef<[u8]>, U: AsRef<[u8]>> OverlayAt<Image<U, 1>> for Im for j in 0..with.height() { for i in 0..with.width() { // SAFETY: i, j is in bounds. - let &[their_px] = unsafe { &with.pixel(i, j) }; + let &[their_px] = unsafe { with.pixel(i, j) }; let our_px = unsafe { self.pixel_mut(i + x, j + y) }; our_px.copy_from_slice(&[their_px; 3]); } @@ -337,7 +337,7 @@ impl<T: AsMut<[u8]> + AsRef<[u8]>, U: AsRef<[u8]>> OverlayAt<Image<U, 2>> for Im for j in 0..with.height() { for i in 0..with.width() { // SAFETY: i, j is in bounds. - let &[their_px, a] = unsafe { &with.pixel(i, j) }; + let &[their_px, a] = unsafe { with.pixel(i, j) }; if a >= 128 { let our_px = unsafe { self.pixel_mut(i + x, j + y) }; our_px.copy_from_slice(&[their_px; 3]); @@ -356,7 +356,7 @@ impl<T: AsMut<[u8]> + AsRef<[u8]>, U: AsRef<[u8]>> OverlayAt<Image<U, 1>> for Im for j in 0..with.height() { for i in 0..with.width() { // SAFETY: i, j is in bounds. - let &[their_px] = unsafe { &with.pixel(i, j) }; + let &[their_px] = unsafe { with.pixel(i, j) }; let our_px = unsafe { self.pixel_mut(i + x, j + y) }; our_px.copy_from_slice(&[their_px; 4]); } @@ -373,7 +373,7 @@ impl<T: AsMut<[u8]> + AsRef<[u8]>, U: AsRef<[u8]>> OverlayAt<Image<U, 2>> for Im for j in 0..with.height() { for i in 0..with.width() { // SAFETY: i, j is in bounds. - let &[their_px, a] = unsafe { &with.pixel(i, j) }; + let &[their_px, a] = unsafe { with.pixel(i, j) }; if a >= 128 { let our_px = unsafe { self.pixel_mut(i + x, j + y) }; our_px.copy_from_slice(&[their_px; 4]); @@ -394,7 +394,7 @@ impl<T: AsMut<[u8]> + AsRef<[u8]>, U: AsRef<[u8]>> OverlayAtClipping<Image<U, 3> if let Some(their_px) = with.get_pixel(i, j) && let Some(our_px) = self.get_pixel_mut(i + x, j + y) { - our_px.copy_from_slice(&their_px); + our_px.copy_from_slice(their_px); } } } @@ -409,7 +409,7 @@ impl<T: AsMut<[u8]> + AsRef<[u8]>, U: AsRef<[u8]>> OverlayAt<Image<U, 3>> for Im for j in 0..with.height() { for i in 0..with.width() { // SAFETY: i, j is in bounds. - let their_px = unsafe { &with.pixel(i, j) }; + let their_px = unsafe { with.pixel(i, j) }; let our_px = unsafe { self.pixel_mut(i + x, j + y) }; our_px.copy_from_slice(their_px); } @@ -507,7 +507,7 @@ impl<T: AsMut<[u8]> + AsRef<[u8]>, U: AsRef<[u8]>> OverlayAt<Image<U, 4>> for Im for j in 0..with.height() { for i in 0..with.width() { // SAFETY: i, j is in bounds. - let their_px = unsafe { &with.pixel(i, j) }; + let their_px = unsafe { with.pixel(i, j) }; if their_px[3] >= 128 { // SAFETY: if everything else goes well, this is fine let our_px = unsafe { self.pixel_mut(i + x, j + y) }; diff --git a/src/slicing.rs b/src/slicing.rs index 947c4ae..86924d3 100644 --- a/src/slicing.rs +++ b/src/slicing.rs @@ -1,4 +1,4 @@ -use std::ops::{Range, RangeBounds, RangeFull, RangeInclusive}; +use std::ops::{Range, RangeBounds, RangeInclusive}; use crate::Image; @@ -153,7 +153,7 @@ impl<W, const C: usize> SubImage<W, C> { /// # Safety /// /// this pixel must be in bounds. - pub unsafe fn pixel<U: Copy>(&self, x: u32, y: u32) -> [U; C] + pub unsafe fn pixel<U: Copy>(&self, x: u32, y: u32) -> &[U; C] where W: AsRef<[U]>, { |