fast image operations
uninit flips
| -rw-r--r-- | src/affine.rs | 14 | ||||
| -rw-r--r-- | src/cloner.rs | 7 | ||||
| -rw-r--r-- | src/lib.rs | 30 | ||||
| -rw-r--r-- | src/overlay.rs | 2 | ||||
| -rw-r--r-- | src/span.rs | 8 | ||||
| -rw-r--r-- | src/uninit.rs | 39 |
6 files changed, 61 insertions, 39 deletions
diff --git a/src/affine.rs b/src/affine.rs index b0152a5..9fc7b3c 100644 --- a/src/affine.rs +++ b/src/affine.rs @@ -10,16 +10,17 @@ impl<const CHANNELS: usize> ImageCloner<'_, CHANNELS> { /// ``` #[must_use = "function does not modify the original image"] pub fn flip_v(&self) -> Image<Vec<u8>, CHANNELS> { - let mut out = self.alloc(); + let mut out = self.uninit(); for y in 0..self.height() { for x in 0..self.width() { // 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.set_pixel(x, self.height() - y - 1, p) }; + unsafe { out.write(&p, (x, self.height() - y - 1)) }; } } - out + // SAFETY: init + unsafe { out.assume_init() } } /// Flip an image horizontally @@ -30,16 +31,17 @@ impl<const CHANNELS: usize> ImageCloner<'_, CHANNELS> { /// ``` #[must_use = "function does not modify the original image"] pub fn flip_h(&self) -> Image<Vec<u8>, CHANNELS> { - let mut out = self.alloc(); + let mut out = self.uninit(); for y in 0..self.height() { for x in 0..self.width() { // SAFETY: looping over self, all ok let p = unsafe { self.pixel(x, y) }; // SAFETY: looping over self, all ok - unsafe { out.set_pixel(self.width() - x - 1, y, p) }; + unsafe { out.write(&p, (self.width() - x - 1, y)) }; } } - out + // SAFETY: init + unsafe { out.assume_init() } } } diff --git a/src/cloner.rs b/src/cloner.rs index b38aedd..f110c1c 100644 --- a/src/cloner.rs +++ b/src/cloner.rs @@ -5,7 +5,7 @@ //! # let i = Image::<_, 1>::alloc(5, 5); //! unsafe { i.cloner().rot_270() }; //! ``` -use crate::Image; +use crate::{uninit, Image}; /// A neat way to clone a image. /// @@ -19,6 +19,11 @@ impl<'a, const C: usize> ImageCloner<'a, C> { self.0.to_owned() } + /// create a new uninit image the right size for use + pub(crate) fn uninit(&self) -> uninit::Image<u8, C> { + uninit::Image::new(self.width, self.height) + } + /// Create a [`ImageCloner`] from a <code>[Image]<&\[[u8]\]></code> pub const fn from(i: Image<&'a [u8], C>) -> Self { Self(i) @@ -49,6 +49,7 @@ //! - `default`: \[`save`, `scale`\]. #![feature( maybe_uninit_write_slice, + hint_assert_unchecked, slice_swap_unchecked, generic_const_exprs, slice_as_chunks, @@ -72,7 +73,7 @@ missing_docs )] #![allow(clippy::zero_prefixed_literal, incomplete_features)] -use std::{num::NonZeroU32, ops::Range}; +use std::{hint::assert_unchecked, num::NonZeroU32, ops::Range}; mod affine; #[cfg(feature = "blur")] @@ -122,25 +123,6 @@ impl<T> CopyWithinUnchecked for [T] { } } -/// like assert!(), but causes undefined behaviour at runtime when the condition is not met. -/// -/// # Safety -/// -/// UB if condition is false. -macro_rules! assert_unchecked { - ($cond:expr) => {{ - if !$cond { - #[cfg(debug_assertions)] - let _ = ::core::ptr::NonNull::<()>::dangling().as_ref(); // force unsafe wrapping block - #[cfg(debug_assertions)] - panic!("assertion failed: {} returned false", stringify!($cond)); - #[cfg(not(debug_assertions))] - std::hint::unreachable_unchecked() - } - }}; -} -use assert_unchecked; - trait At { fn at<const C: usize>(self, x: u32, y: u32) -> usize; } @@ -455,9 +437,9 @@ impl<T, const CHANNELS: usize> Image<T, CHANNELS> { T: AsRef<[U]>, { // SAFETY: 0 sized images illegal - unsafe { assert_unchecked!(self.len() > CHANNELS) }; + unsafe { assert_unchecked(self.len() > CHANNELS) }; // SAFETY: no half pixels! - unsafe { assert_unchecked!(self.len() % CHANNELS == 0) }; + unsafe { assert_unchecked(self.len() % CHANNELS == 0) }; self.buffer().as_ref().array_chunks::<CHANNELS>() } @@ -546,9 +528,9 @@ impl<T: AsMut<[u8]> + AsRef<[u8]>, const CHANNELS: usize> Image<T, CHANNELS> { /// Returns a iterator over every pixel, mutably pub fn chunked_mut(&mut self) -> impl Iterator<Item = &mut [u8; CHANNELS]> { // SAFETY: 0 sized images are not allowed - unsafe { assert_unchecked!(self.len() > CHANNELS) }; + unsafe { assert_unchecked(self.len() > CHANNELS) }; // SAFETY: buffer cannot have half pixels - unsafe { assert_unchecked!(self.len() % CHANNELS == 0) }; + unsafe { assert_unchecked(self.len() % CHANNELS == 0) }; self.buffer.as_mut().array_chunks_mut::<CHANNELS>() } diff --git a/src/overlay.rs b/src/overlay.rs index dbb7a0a..eef4272 100644 --- a/src/overlay.rs +++ b/src/overlay.rs @@ -160,7 +160,7 @@ impl<T: AsMut<[u8]> + AsRef<[u8]>, U: AsRef<[u8]>> OverlayAt<Image<U, 4>> for Im #[inline] unsafe fn overlay_at(&mut self, with: &Image<U, 4>, x: u32, y: u32) -> &mut Self { // SAFETY: caller upholds this - unsafe { assert_unchecked!(x + with.width() <= self.width()) }; + unsafe { assert_unchecked(x + with.width() <= self.width()) }; debug_assert!(y + with.height() <= self.height()); for j in 0..with.height() { let i_x = j as usize * with.width() as usize * 4 diff --git a/src/span.rs b/src/span.rs index 7ac76c9..69a01ad 100644 --- a/src/span.rs +++ b/src/span.rs @@ -32,3 +32,11 @@ impl Span for Range<(u32, u32)> { i.at::<C>(sx, sy)..i.at::<C>(ex, ey) } } + +impl Sealed for (u32, u32) {} +impl Span for (u32, u32) { + #[inline(always)] + fn range<const C: usize>(self, i: (u32, u32)) -> Range<usize> { + i.at::<C>(self.0, self.1)..i.at::<C>(self.0, self.1) + C + } +} diff --git a/src/uninit.rs b/src/uninit.rs index 3555beb..c4c341b 100644 --- a/src/uninit.rs +++ b/src/uninit.rs @@ -1,9 +1,14 @@ //! the houser of uninitialized memory. €$@!0В!℡ //! //! contains [`Image`], an uninitialized image. -use std::{mem::MaybeUninit, num::NonZeroU32}; +use std::{ + hint::assert_unchecked, + mem::MaybeUninit, + num::NonZeroU32, + ops::{Index, IndexMut}, +}; -use crate::CopyWithinUnchecked; +use crate::{span::Span, CopyWithinUnchecked}; /// A uninitialized image. Be sure to initialize it! pub struct Image<T: Copy, const CHANNELS: usize> { @@ -13,6 +18,14 @@ pub struct Image<T: Copy, const CHANNELS: usize> { height: NonZeroU32, } +impl<I: Span, T: Copy, const C: usize> Index<I> for Image<T, C> { + type Output = [T]; + + fn index(&self, index: I) -> &Self::Output { + &self.buffer()[index.range::<C>((self.width(), self.height()))] + } +} + impl<T: Copy, const CHANNELS: usize> Image<T, CHANNELS> { /// Create a new uninit image. This is not init. pub fn new(width: NonZeroU32, height: NonZeroU32) -> Self { @@ -27,19 +40,31 @@ impl<T: Copy, const CHANNELS: usize> Image<T, CHANNELS> { /// /// # Safety /// index must be in bounds. - pub unsafe fn write(&mut self, data: &[T], i: impl crate::span::Span) { - let range = i.range::<CHANNELS>((self.width(), self.height())); - // SAFETY: write - let dat = unsafe { self.buf().get_unchecked_mut(range) }; + /// data and indexed range must have same len. + pub unsafe fn write(&mut self, data: &[T], i: impl Span) { + // SAFETY: caller + let dat = unsafe { self.slice(i) }; + // SAFETY: caller + unsafe { assert_unchecked(dat.len() == data.len()) }; MaybeUninit::write_slice(dat, data); } + /// Slice the image. + /// + /// # Safety + /// index must be in bounds. + pub unsafe fn slice(&mut self, i: impl Span) -> &mut [MaybeUninit<T>] { + let range = i.range::<CHANNELS>((self.width(), self.height())); + // SAFETY: assured + unsafe { self.buf().get_unchecked_mut(range) } + } + /// Copy a range to a position. /// /// # Safety /// /// both parts must be in bounds. - pub unsafe fn copy_within(&mut self, i: impl crate::span::Span, to: usize) { + pub unsafe fn copy_within(&mut self, i: impl Span, to: usize) { let range = i.range::<CHANNELS>((self.width(), self.height())); // SAFETY: copy! unsafe { self.buf().copy_within_unchecked(range, to) }; |