fast image operations
less safe
bendn 5 weeks ago
parent 1826223 · commit be42cdc
-rw-r--r--src/affine.rs14
-rw-r--r--src/drawing/box.rs10
-rw-r--r--src/drawing/circle.rs4
-rw-r--r--src/drawing/line.rs2
-rw-r--r--src/drawing/poly.rs2
-rw-r--r--src/drawing/tri.rs2
-rw-r--r--src/dyn/mod.rs2
-rw-r--r--src/lib.rs52
-rw-r--r--src/overlay.rs18
-rw-r--r--src/slicing.rs2
-rw-r--r--src/sub.rs2
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.
diff --git a/src/lib.rs b/src/lib.rs
index f507872..75dfa22 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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;
diff --git a/src/sub.rs b/src/sub.rs
index d4b6395..531bfe7 100644
--- a/src/sub.rs
+++ b/src/sub.rs
@@ -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]>,
{