fast image operations
good function
| -rw-r--r-- | benches/scaling.rs | 4 | ||||
| -rw-r--r-- | src/lib.rs | 38 | ||||
| -rw-r--r-- | src/slicing.rs | 51 |
3 files changed, 90 insertions, 3 deletions
diff --git a/benches/scaling.rs b/benches/scaling.rs index 389998f..3a55a6a 100644 --- a/benches/scaling.rs +++ b/benches/scaling.rs @@ -1,4 +1,6 @@ -use fimg::{scale::*, Image}; +#![feature(generic_const_exprs)] +#![allow(incomplete_features)] +use fimg::{Image, scale::*}; macro_rules! bench { ($([$a: ident, $alg:ident]),+ $(,)?) => { @@ -59,6 +59,7 @@ iter_array_chunks, const_trait_impl, core_intrinsics, + new_range_api, rustc_private, portable_simd, const_convert, @@ -85,11 +86,17 @@ )] use array_chunks::*; use hinted::HintExt; -use std::{hint::assert_unchecked, intrinsics::transmute_unchecked, num::NonZeroU32, ops::Range}; +use std::{ + hint::assert_unchecked, + intrinsics::transmute_unchecked, + num::NonZeroU32, + ops::{Range, RangeBounds, RangeInclusive}, +}; mod affine; #[cfg(feature = "blur")] mod blur; +mod slicing; pub use sub::{Cropper, SubImage}; pub mod builder; #[doc(hidden)] @@ -312,7 +319,7 @@ impl<T, const CHANNELS: usize> Image<T, CHANNELS> { /// the output index is not guaranteed to be in bounds #[inline] #[cfg_attr(debug_assertions, track_caller)] - fn at(&self, x: u32, y: u32) -> usize { + pub fn at(&self, x: u32, y: u32) -> usize { (self.width(), self.height()).at::<CHANNELS>(x, y) } @@ -557,6 +564,31 @@ impl<T, const CHANNELS: usize> Image<T, CHANNELS> { unsafe { self.get_pixel(x, y).unwrap_unchecked() } } + /// pixels contiguously from start to end + /// they gotta be in bounds + /// + /// i think this is unsound because you can make asref do whatever the fucking fuck you fucking want but thats fucking on you + pub unsafe fn pixels<U: Copy>(&self, r: impl PBounds) -> &[[U; CHANNELS]] + where + T: AsRef<[U]>, + { + let b = self.bounds(r); + unsafe { self.buffer.as_ref().get_unchecked(b).as_chunks_unchecked() } + } + /// pixels contiguously from start to end + pub unsafe fn pixels_mut<U: Copy>(&mut self, r: impl PBounds) -> &mut [[U; CHANNELS]] + where + T: AsRef<[U]> + AsMut<[U]>, + { + let b = self.bounds(r); + unsafe { + self.buffer + .as_mut() + .get_unchecked_mut(b) + .as_chunks_unchecked_mut() + } + } + /// Returns a [`PixelEntry`] pub fn replace<U: Copy>( &mut self, @@ -938,3 +970,5 @@ macro_rules! img { } #[cfg(test)] use img; + +use crate::slicing::PBounds; diff --git a/src/slicing.rs b/src/slicing.rs new file mode 100644 index 0000000..947c4ae --- /dev/null +++ b/src/slicing.rs @@ -0,0 +1,51 @@ +use std::ops::{Range, RangeBounds, RangeFull, RangeInclusive}; + +use crate::Image; + +impl<const CHANNELS: usize, T> Image<T, CHANNELS> { + /// ``` + /// let i = fimg::Image::<_, 1>::alloc(5, 5); + /// dbg!(i.bounds((0, 0)..(6, 0))); + /// panic!(); + /// ``` + pub fn bounds<U>(&self, r: impl PBounds) -> std::ops::Range<usize> + where + T: AsRef<[U]>, + { + let r = r.bound(); + let start = match r.start_bound() { + std::ops::Bound::Included(&(x, y)) => self.at(x, y), + std::ops::Bound::Excluded(&(x, y)) => self.at(x, y) + CHANNELS, + std::ops::Bound::Unbounded => 0, + }; + let end = match r.end_bound() { + std::ops::Bound::Included(&(x, y)) => self.at(x, y) + CHANNELS, + std::ops::Bound::Excluded(&(x, y)) => self.at(x, y), + std::ops::Bound::Unbounded => self.buffer.as_ref().len(), + }; + start..end + } +} +pub trait PBounds { + fn bound(self) -> impl RangeBounds<(u32, u32)>; +} +impl PBounds for Range<(u32, u32)> { + fn bound(self) -> impl RangeBounds<(u32, u32)> { + self + } +} +impl PBounds for RangeInclusive<(u32, u32)> { + fn bound(self) -> impl RangeBounds<(u32, u32)> { + self + } +} +impl PBounds for (Range<u32>, u32) { + fn bound(self) -> impl RangeBounds<(u32, u32)> { + (self.0.start, self.1)..(self.0.end, self.1) + } +} +impl PBounds for (u32, Range<u32>) { + fn bound(self) -> impl RangeBounds<(u32, u32)> { + (self.0, self.1.start)..(self.0, self.1.end) + } +} |