use AsRef instead of Deref

This commit is contained in:
bendn 2023-10-04 10:19:52 +07:00
parent 101ca55d29
commit f465e25077
No known key found for this signature in database
GPG key ID: 0D9D3A2A3B2A93D6
12 changed files with 101 additions and 63 deletions

View file

@ -1,6 +1,6 @@
[package]
name = "fimg"
version = "0.4.13"
version = "0.4.14"
authors = ["bend-n <bend.n@outlook.com>"]
license = "MIT"
edition = "2021"

View file

@ -3,12 +3,13 @@ use fimg::*;
macro_rules! bench {
(fn $name: ident() { run $fn: ident() } $($namec:ident)?) => {
fn $name() {
let mut bytes = *include_bytes!("4_128x128.imgbuf");
let mut img: Image<_, 4> =
Image::build(128, 128).buf(include_bytes!("4_128x128.imgbuf").to_vec());
Image::build(128, 128).buf(&mut bytes);
for _ in 0..256 {
#[allow(unused_unsafe)]
unsafe {
img.$fn()
img.as_mut().$fn()
};
}
}

View file

@ -1,7 +1,7 @@
use fimg::*;
fn tri() {
let mut b = [0u8; 1000 * 1000 * 4];
let mut i = Image::<&mut [u8], 4>::build(1000, 1000).buf(&mut b);
i.tri((0., 0.), (1000., 500.), (0., 999.), [255, 255, 255, 255]);
let mut i: Image<_, 4> = fimg::make!(4 channels 1000 x 1000);
i.as_mut()
.tri((0., 0.), (1000., 500.), (0., 999.), [255, 255, 255, 255]);
}
iai::main!(tri);

View file

@ -1,7 +1,7 @@
use fimg::*;
fn overlay_3on3at() {
let mut a: Image<_, 3> = Image::alloc(128, 128);
let mut a = fimg::make!(3 channels 128 x 128);
let b: Image<&[u8], 3> = Image::build(8, 8).buf(include_bytes!("3_8x8.imgbuf"));
for x in 0..16 {
for y in 0..16 {
@ -11,7 +11,7 @@ fn overlay_3on3at() {
}
fn overlay_4on3at() {
let mut a: Image<_, 3> = Image::alloc(128, 128);
let mut a = fimg::make!(3 channels 128 x 128);
let b: Image<&[u8], 4> = Image::build(8, 8).buf(include_bytes!("4_8x8.imgbuf"));
for x in 0..16 {
for y in 0..16 {
@ -21,7 +21,7 @@ fn overlay_4on3at() {
}
fn overlay_4on4at() {
let mut a: Image<_, 4> = Image::alloc(128, 128);
let mut a = fimg::make!(4 channels 128 x 128);
let b: Image<&[u8], 4> = Image::build(8, 8).buf(include_bytes!("4_8x8.imgbuf"));
for x in 0..16 {
for y in 0..16 {

View file

@ -1,6 +1,4 @@
//! Manages the affine image transformations.
use std::ops::DerefMut;
use crate::{cloner::ImageCloner, Image};
impl<const CHANNELS: usize> ImageCloner<'_, CHANNELS> {
@ -45,7 +43,7 @@ impl<const CHANNELS: usize> ImageCloner<'_, CHANNELS> {
}
}
impl<const CHANNELS: usize, T: DerefMut<Target = [u8]>> Image<T, CHANNELS> {
impl<const CHANNELS: usize, T: AsMut<[u8]> + AsRef<[u8]>> Image<T, CHANNELS> {
/// Flip an image vertically.
pub fn flip_v(&mut self) {
for y in 0..self.height() / 2 {
@ -127,7 +125,7 @@ impl<const CHANNELS: usize> ImageCloner<'_, CHANNELS> {
}
}
impl<const CHANNELS: usize, T: DerefMut<Target = [u8]>> Image<T, CHANNELS> {
impl<const CHANNELS: usize, T: AsMut<[u8]> + AsRef<[u8]>> Image<T, CHANNELS> {
/// Rotate an image 180 degrees clockwise.
pub fn rot_180(&mut self) {
self.flatten_mut().reverse();
@ -162,7 +160,7 @@ impl<const CHANNELS: usize, T: DerefMut<Target = [u8]>> Image<T, CHANNELS> {
/// # Safety
///
/// UB if supplied image not square
unsafe fn crev<const CHANNELS: usize, T: DerefMut<Target = [u8]>>(mut img: Image<T, CHANNELS>) {
unsafe fn crev<const CHANNELS: usize, T: AsMut<[u8]> + AsRef<[u8]>>(mut img: Image<T, CHANNELS>) {
debug_assert_eq!(img.width(), img.height());
let size = img.width() as usize;
let b = img.flatten_mut();
@ -202,7 +200,7 @@ unsafe fn transpose_out<const CHANNELS: usize>(
/// # Safety
///
/// UB if supplied image rectangular
unsafe fn transpose<const CHANNELS: usize, T: DerefMut<Target = [u8]>>(
unsafe fn transpose<const CHANNELS: usize, T: AsMut<[u8]> + AsRef<[u8]>>(
img: &mut Image<T, CHANNELS>,
) {
debug_assert_eq!(img.width(), img.height());
@ -220,7 +218,7 @@ unsafe fn transpose<const CHANNELS: usize, T: DerefMut<Target = [u8]>>(
/// # Safety
///
/// UB if image not square
unsafe fn transpose_non_power_of_two<const CHANNELS: usize, T: DerefMut<Target = [u8]>>(
unsafe fn transpose_non_power_of_two<const CHANNELS: usize, T: AsMut<[u8]> + AsRef<[u8]>>(
img: &mut Image<T, CHANNELS>,
) {
debug_assert_eq!(img.width(), img.height());
@ -239,7 +237,7 @@ const TILE: usize = 4;
/// # Safety
///
/// be careful
unsafe fn transpose_tile<const CHANNELS: usize, T: DerefMut<Target = [u8]>>(
unsafe fn transpose_tile<const CHANNELS: usize, T: AsMut<[u8]> + AsRef<[u8]>>(
img: &mut Image<T, CHANNELS>,
row: usize,
col: usize,
@ -275,7 +273,7 @@ unsafe fn transpose_tile<const CHANNELS: usize, T: DerefMut<Target = [u8]>>(
/// # Safety
///
/// be careful
unsafe fn transpose_diag<const CHANNELS: usize, T: DerefMut<Target = [u8]>>(
unsafe fn transpose_diag<const CHANNELS: usize, T: AsMut<[u8]> + AsRef<[u8]>>(
img: &mut Image<T, CHANNELS>,
pos: usize,
size: usize,

View file

@ -1,9 +1,9 @@
//! `Box<cat>`
use std::ops::{DerefMut, Range};
use std::ops::Range;
use crate::Image;
impl<T: DerefMut<Target = [u8]>, const CHANNELS: usize> Image<T, CHANNELS> {
impl<T: AsMut<[u8]> + AsRef<[u8]>, const CHANNELS: usize> Image<T, CHANNELS> {
/// Draw a bordered box
/// ```
/// # use fimg::Image;

View file

@ -1,8 +1,8 @@
//! draw 2d circles
use crate::Image;
use std::ops::DerefMut;
impl<T: DerefMut<Target = [u8]>, const CHANNELS: usize> Image<T, CHANNELS> {
impl<T: AsMut<[u8]> + AsRef<[u8]>, const CHANNELS: usize> Image<T, CHANNELS> {
/// Draws a circle, using the [Bresenham's circle](https://en.wikipedia.org/wiki/Midpoint_circle_algorithm) algorithm.
/// ```
/// # use fimg::Image;

View file

@ -1,7 +1,7 @@
//! adds a `line` function to Image
#![allow(clippy::missing_docs_in_private_items)]
use crate::Image;
use std::{iter::Iterator, ops::DerefMut};
use std::iter::Iterator;
/// taken from [bresenham-rs](https://github.com/mbr/bresenham-rs)
pub struct Bresenham {
@ -127,7 +127,7 @@ impl Iterator for Bresenham {
}
}
impl<T: DerefMut<Target = [u8]>, const CHANNELS: usize> Image<T, CHANNELS> {
impl<T: AsMut<[u8]> + AsRef<[u8]>, const CHANNELS: usize> Image<T, CHANNELS> {
/// Draw a line from point a to point b.
///
/// Points not in bounds will not be included.

View file

@ -2,12 +2,11 @@
use std::{
cmp::{max, min},
f32::consts::TAU,
ops::DerefMut,
};
use crate::Image;
impl<T: DerefMut<Target = [u8]>, const CHANNELS: usize> Image<T, CHANNELS> {
impl<T: AsMut<[u8]> + AsRef<[u8]>, const CHANNELS: usize> Image<T, CHANNELS> {
/// Draws a filled polygon from a slice of points. Please close your poly. (first == last)
///
/// Borrowed from [imageproc](https://docs.rs/imageproc/latest/src/imageproc/drawing/polygon.rs.html#31), modified for less allocations.

View file

@ -1,9 +1,9 @@
//! trongle drawing
use std::ops::DerefMut;
use crate::Image;
impl<T: DerefMut<Target = [u8]>, const CHANNELS: usize> Image<T, CHANNELS> {
impl<T: AsMut<[u8]> + AsRef<[u8]>, const CHANNELS: usize> Image<T, CHANNELS> {
/// Draw a (filled) triangle
/// ```
/// # use fimg::*;

View file

@ -233,7 +233,7 @@ impl<const CHANNELS: usize> Image<&[u8], CHANNELS> {
/// ```
pub const fn make<'a, const WIDTH: u32, const HEIGHT: u32>() -> Image<&'a [u8], CHANNELS>
where
[(); CHANNELS * WIDTH as usize * HEIGHT as usize]: Sized,
[(); CHANNELS * WIDTH as usize * HEIGHT as usize]:,
{
Image {
width: NonZeroU32::new(WIDTH).expect("passed zero width to builder"),
@ -243,7 +243,40 @@ impl<const CHANNELS: usize> Image<&[u8], CHANNELS> {
}
}
impl<T: std::ops::Deref<Target = [u8]>, const CHANNELS: usize> Image<T, CHANNELS> {
#[macro_export]
/// Create a <code>[Image]<[[u8]; N], C></code> with ease. If your looking for a <code>[Image]<&'static [[u8]]></code>, try [`Image::make`].
///
/// ```
/// let mut i = fimg::make!(4 channels 128 x 128);
/// ```
///
/// Implementation note:
/// This is doable with a const generic fn, but it returns a `fimg::Image<[u8; fimg::::{impl#7}::array::{constant#1}], _>` which means you cant actually type it, so its useless.
macro_rules! make {
($channels:literal channels $w:literal x $h: literal) => {
unsafe {
Image::<_, $channels>::new(
match ::core::num::NonZeroU32::new($w) {
Some(n) => n,
None => panic!("width is 0"),
},
match ::core::num::NonZeroU32::new($h) {
::core::option::Option::Some(n) => n,
::core::option::Option::None => panic!("height is 0"),
},
[0_u8; $channels * $w * $h],
)
}
};
}
impl<T: AsRef<[u8]>, const CHANNELS: usize> Image<T, CHANNELS> {
/// The size of the underlying buffer.
#[allow(clippy::len_without_is_empty)]
pub fn len(&self) -> usize {
self.buffer.as_ref().len()
}
/// # Safety
///
/// the output index is not guranteed to be in bounds
@ -271,7 +304,7 @@ impl<T: std::ops::Deref<Target = [u8]>, const CHANNELS: usize> Image<T, CHANNELS
};
// SAFETY: 🧐 is unsound? 😖
let index = unsafe { index.unchecked_mul(CHANNELS) };
debug_assert!(self.buffer.len() > index);
debug_assert!(self.len() > index);
index
}
@ -284,24 +317,24 @@ impl<T: std::ops::Deref<Target = [u8]>, const CHANNELS: usize> Image<T, CHANNELS
/// Reference this image.
pub fn as_ref(&self) -> Image<&[u8], CHANNELS> {
// SAFETY: we got constructed okay, parameters must be valid
unsafe { Image::new(self.width, self.height, &*self.buffer) }
unsafe { Image::new(self.width, self.height, self.buffer.as_ref()) }
}
#[inline]
/// Returns a iterator over every pixel
pub fn chunked(&self) -> impl DoubleEndedIterator<Item = &[u8; CHANNELS]> {
// SAFETY: 0 sized images illegal
unsafe { assert_unchecked!(self.buffer.len() > CHANNELS) };
unsafe { assert_unchecked!(self.len() > CHANNELS) };
// SAFETY: no half pixels!
unsafe { assert_unchecked!(self.buffer.len() % CHANNELS == 0) };
self.buffer.array_chunks::<CHANNELS>()
unsafe { assert_unchecked!(self.len() % CHANNELS == 0) };
self.buffer.as_ref().array_chunks::<CHANNELS>()
}
#[inline]
/// Flatten the chunks of this image into a slice of slices.
pub fn flatten(&self) -> &[[u8; CHANNELS]] {
// SAFETY: buffer cannot have half pixels
unsafe { self.buffer.as_chunks_unchecked::<CHANNELS>() }
unsafe { self.buffer.as_ref().as_chunks_unchecked::<CHANNELS>() }
}
/// Return a pixel at (x, y).
@ -312,13 +345,19 @@ impl<T: std::ops::Deref<Target = [u8]>, const CHANNELS: usize> Image<T, CHANNELS
#[inline]
pub unsafe fn pixel(&self, x: u32, y: u32) -> [u8; CHANNELS] {
// SAFETY: x and y in bounds, slice is okay
let ptr = unsafe { self.buffer.get_unchecked(self.slice(x, y)).as_ptr().cast() };
let ptr = unsafe {
self.buffer
.as_ref()
.get_unchecked(self.slice(x, y))
.as_ptr()
.cast()
};
// SAFETY: slice always returns a length of `CHANNELS`, so we `cast()` it for convenience.
unsafe { *ptr }
}
}
impl<T: std::ops::DerefMut<Target = [u8]>, const CHANNELS: usize> Image<T, CHANNELS> {
impl<T: AsMut<[u8]> + AsRef<[u8]>, const CHANNELS: usize> Image<T, CHANNELS> {
/// Return a mutable reference to a pixel at (x, y).
/// # Safety
///
@ -329,30 +368,30 @@ impl<T: std::ops::DerefMut<Target = [u8]>, const CHANNELS: usize> Image<T, CHANN
// SAFETY: we have been told x, y is in bounds.
let idx = self.slice(x, y);
// SAFETY: slice should always return a valid index
unsafe { self.buffer.get_unchecked_mut(idx) }
unsafe { self.buffer.as_mut().get_unchecked_mut(idx) }
}
#[inline]
/// 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.buffer.len() > CHANNELS) };
unsafe { assert_unchecked!(self.len() > CHANNELS) };
// SAFETY: buffer cannot have half pixels
unsafe { assert_unchecked!(self.buffer.len() % CHANNELS == 0) };
self.buffer.array_chunks_mut::<CHANNELS>()
unsafe { assert_unchecked!(self.len() % CHANNELS == 0) };
self.buffer.as_mut().array_chunks_mut::<CHANNELS>()
}
/// Create a mutref to this image
pub fn as_mut(&mut self) -> Image<&mut [u8], CHANNELS> {
// SAFETY: construction went okay
unsafe { Image::new(self.width, self.height, &mut self.buffer) }
unsafe { Image::new(self.width, self.height, self.buffer.as_mut()) }
}
#[inline]
/// Flatten the chunks of this image into a mutable slice of slices.
pub fn flatten_mut(&mut self) -> &mut [[u8; CHANNELS]] {
// SAFETY: buffer cannot have half pixels
unsafe { self.buffer.as_chunks_unchecked_mut::<CHANNELS>() }
unsafe { self.buffer.as_mut().as_chunks_unchecked_mut::<CHANNELS>() }
}
/// # Safety
@ -363,13 +402,13 @@ impl<T: std::ops::DerefMut<Target = [u8]>, const CHANNELS: usize> Image<T, CHANN
unsafe fn copy_within(&mut self, src: std::ops::Range<usize>, dest: usize) {
let std::ops::Range { start, end } = src;
debug_assert!(
dest <= self.buffer.len() - end - start,
dest <= self.buffer.as_ref().len() - end - start,
"dest is out of bounds"
);
#[allow(clippy::multiple_unsafe_ops_per_block)]
// SAFETY: the caller better be good
unsafe {
let ptr = self.buffer.as_mut_ptr();
let ptr = self.buffer.as_mut().as_mut_ptr();
std::ptr::copy_nonoverlapping(ptr.add(start), ptr.add(dest), end - start)
};
}
@ -399,7 +438,7 @@ impl<const CHANNELS: usize> Image<&mut [u8], CHANNELS> {
}
impl<const CHANNELS: usize> Image<Vec<u8>, CHANNELS> {
/// Allocates a new image
/// Allocates a new image. If `width` and `height` are constant, try using [`make`].
///
/// # Panics
///
@ -417,7 +456,7 @@ impl<const CHANNELS: usize> Image<Vec<u8>, CHANNELS> {
/// helper macro for defining the save() method.
macro_rules! save {
($channels:literal == $clr:ident ($clrhuman:literal)) => {
impl<T: std::ops::Deref<Target = [u8]>> Image<T, $channels> {
impl<T: AsRef<[u8]>> Image<T, $channels> {
#[cfg(feature = "save")]
#[doc = "Save this "]
#[doc = $clrhuman]
@ -436,7 +475,7 @@ macro_rules! save {
(0.15000, 0.06000),
));
let mut writer = enc.write_header().unwrap();
writer.write_image_data(&self.buffer).unwrap();
writer.write_image_data(self.buffer.as_ref()).unwrap();
}
}
};

View file

@ -2,7 +2,6 @@
use crate::cloner::ImageCloner;
use super::{assert_unchecked, Image};
use std::ops::{Deref, DerefMut};
use std::simd::{simd_swizzle, Simd, SimdInt, SimdPartialOrd};
/// Trait for layering a image ontop of another, with a offset to the second image.
@ -100,7 +99,7 @@ unsafe fn blit(rgb: &mut [u8], rgba: &[u8]) {
}
}
impl<T: DerefMut<Target = [u8]>, U: Deref<Target = [u8]>> Overlay<Image<U, 4>> for Image<T, 4> {
impl<T: AsMut<[u8]> + AsRef<[u8]>, U: AsRef<[u8]>> Overlay<Image<U, 4>> for Image<T, 4> {
#[inline]
unsafe fn overlay(&mut self, with: &Image<U, 4>) -> &mut Self {
debug_assert!(self.width() == with.width());
@ -108,7 +107,8 @@ impl<T: DerefMut<Target = [u8]>, U: Deref<Target = [u8]>> Overlay<Image<U, 4>> f
for (i, other_pixels) in with.chunked().enumerate() {
if other_pixels[3] >= 128 {
// SAFETY: outside are bounds of index from slice
let own_pixels = unsafe { self.buffer.get_unchecked_mut(i * 4..i * 4 + 4) };
let own_pixels =
unsafe { self.buffer.as_mut().get_unchecked_mut(i * 4..i * 4 + 4) };
own_pixels.copy_from_slice(other_pixels);
}
}
@ -127,7 +127,7 @@ impl ClonerOverlay<4, 4> for ImageCloner<'_, 4> {
}
}
impl<T: DerefMut<Target = [u8]>, U: Deref<Target = [u8]>> OverlayAt<Image<U, 4>> for Image<T, 3> {
impl<T: AsMut<[u8]> + AsRef<[u8]>, U: AsRef<[u8]>> OverlayAt<Image<U, 4>> for Image<T, 3> {
#[inline]
unsafe fn overlay_at(&mut self, with: &Image<U, 4>, x: u32, y: u32) -> &mut Self {
// SAFETY: caller upholds this
@ -142,9 +142,9 @@ impl<T: DerefMut<Target = [u8]>, U: Deref<Target = [u8]>> OverlayAt<Image<U, 4>>
+ with.width() as usize)
* 3;
// SAFETY: index is in bounds
let rgb = unsafe { self.buffer.get_unchecked_mut(o_x) };
let rgb = unsafe { self.buffer.as_mut().get_unchecked_mut(o_x) };
// SAFETY: bounds are outside index
let rgba = unsafe { with.buffer.get_unchecked(i_x) };
let rgba = unsafe { with.buffer.as_ref().get_unchecked(i_x) };
// SAFETY: arguments are 🟢
unsafe { blit(rgb, rgba) }
}
@ -163,7 +163,7 @@ impl ClonerOverlayAt<4, 3> for ImageCloner<'_, 3> {
}
}
impl<T: DerefMut<Target = [u8]>, U: Deref<Target = [u8]>> OverlayAt<Image<U, 3>> for Image<T, 3> {
impl<T: AsMut<[u8]> + AsRef<[u8]>, U: AsRef<[u8]>> OverlayAt<Image<U, 3>> for Image<T, 3> {
/// Overlay a RGB image(with) => self at coordinates x, y.
/// As this is a `RGBxRGB` operation, blending is unnecessary,
/// and this is simply a copy.
@ -182,12 +182,12 @@ impl<T: DerefMut<Target = [u8]>, U: Deref<Target = [u8]>> OverlayAt<Image<U, 3>>
..((j + y as usize) * self.width() as usize + x as usize + ($n as usize))
* 3;
// <= because ".." range
debug_assert!(o_x.end <= self.buffer().len());
debug_assert!(i_x.end <= with.buffer().len());
debug_assert!(o_x.end <= self.buffer().as_ref().len());
debug_assert!(i_x.end <= with.buffer().as_ref().len());
// SAFETY: bounds are ✅
let a = unsafe { self.buffer.get_unchecked_mut(o_x) };
let a = unsafe { self.buffer.as_mut().get_unchecked_mut(o_x) };
// SAFETY: we are in ⬜!
let b = unsafe { with.buffer.get_unchecked(i_x) };
let b = unsafe { with.buffer.as_ref().get_unchecked(i_x) };
a.copy_from_slice(b);
}
}};
@ -202,19 +202,20 @@ impl<T: DerefMut<Target = [u8]>, U: Deref<Target = [u8]>> OverlayAt<Image<U, 3>>
}
}
impl<T: DerefMut<Target = [u8]>, U: Deref<Target = [u8]>> Overlay<Image<U, 4>> for Image<T, 3> {
impl<T: AsMut<[u8]> + AsRef<[u8]>, U: AsRef<[u8]>> Overlay<Image<U, 4>> for Image<T, 3> {
#[inline]
unsafe fn overlay(&mut self, with: &Image<U, 4>) -> &mut Self {
debug_assert!(self.width() == with.width());
debug_assert!(self.height() == with.height());
for (i, chunk) in with
.buffer
.as_ref()
.chunks_exact(with.width() as usize * 4)
.enumerate()
{
// SAFETY: all the bounds are good
let rgb = unsafe {
self.buffer.get_unchecked_mut(
self.buffer.as_mut().get_unchecked_mut(
i * with.width() as usize * 3..(i + 1) * with.width() as usize * 3,
)
};
@ -236,7 +237,7 @@ impl ClonerOverlay<4, 3> for ImageCloner<'_, 3> {
}
}
impl<T: DerefMut<Target = [u8]>, U: Deref<Target = [u8]>> OverlayAt<Image<U, 4>> for Image<T, 4> {
impl<T: AsMut<[u8]> + AsRef<[u8]>, U: AsRef<[u8]>> OverlayAt<Image<U, 4>> for Image<T, 4> {
#[inline]
unsafe fn overlay_at(&mut self, with: &Image<U, 4>, x: u32, y: u32) -> &mut Self {
for j in 0..with.height() {