fast image operations
good function
bendn 5 weeks ago
parent e72d81b · commit 1826223
-rw-r--r--benches/scaling.rs4
-rw-r--r--src/lib.rs38
-rw-r--r--src/slicing.rs51
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]),+ $(,)?) => {
diff --git a/src/lib.rs b/src/lib.rs
index 3784f64..f507872 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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)
+ }
+}