mirror of
https://github.com/bend-n/fimg.git
synced 2024-12-22 10:28:21 -06:00
add uninit image type
This commit is contained in:
parent
1f71a8ab8b
commit
d56ce34d6e
78
src/lib.rs
78
src/lib.rs
|
@ -72,7 +72,7 @@
|
||||||
missing_docs
|
missing_docs
|
||||||
)]
|
)]
|
||||||
#![allow(clippy::zero_prefixed_literal, incomplete_features)]
|
#![allow(clippy::zero_prefixed_literal, incomplete_features)]
|
||||||
use std::{mem::MaybeUninit, num::NonZeroU32, ops::Range};
|
use std::{num::NonZeroU32, ops::Range};
|
||||||
|
|
||||||
mod affine;
|
mod affine;
|
||||||
#[cfg(feature = "blur")]
|
#[cfg(feature = "blur")]
|
||||||
|
@ -87,7 +87,9 @@ mod r#dyn;
|
||||||
pub(crate) mod math;
|
pub(crate) mod math;
|
||||||
mod overlay;
|
mod overlay;
|
||||||
mod pack;
|
mod pack;
|
||||||
|
mod span;
|
||||||
mod sub;
|
mod sub;
|
||||||
|
pub mod uninit;
|
||||||
pub use pack::Pack;
|
pub use pack::Pack;
|
||||||
pub mod pixels;
|
pub mod pixels;
|
||||||
#[cfg(feature = "scale")]
|
#[cfg(feature = "scale")]
|
||||||
|
@ -139,6 +141,26 @@ macro_rules! assert_unchecked {
|
||||||
}
|
}
|
||||||
use assert_unchecked;
|
use assert_unchecked;
|
||||||
|
|
||||||
|
trait At {
|
||||||
|
fn at<const C: usize>(self, x: u32, y: u32) -> usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl At for (u32, u32) {
|
||||||
|
fn at<const C: usize>(self, x: u32, y: u32) -> usize {
|
||||||
|
debug_assert!(x < self.0, "x out of bounds");
|
||||||
|
debug_assert!(y < self.1, "y out of bounds");
|
||||||
|
#[allow(clippy::multiple_unsafe_ops_per_block)]
|
||||||
|
// SAFETY: me when uncheck math: 😧 (FIXME)
|
||||||
|
let index = unsafe {
|
||||||
|
// y * w + x
|
||||||
|
let tmp = (y as usize).unchecked_mul(self.0 as usize);
|
||||||
|
tmp.unchecked_add(x as usize)
|
||||||
|
};
|
||||||
|
// SAFETY: 🧐 is unsound? 😖
|
||||||
|
unsafe { index.unchecked_mul(C) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Image<&[u8], 3> {
|
impl Image<&[u8], 3> {
|
||||||
/// Tile self till it fills a new image of size x, y
|
/// Tile self till it fills a new image of size x, y
|
||||||
/// # Safety
|
/// # Safety
|
||||||
|
@ -152,7 +174,10 @@ impl Image<&[u8], 3> {
|
||||||
/// ```
|
/// ```
|
||||||
#[must_use = "function does not modify the original image"]
|
#[must_use = "function does not modify the original image"]
|
||||||
pub unsafe fn repeated(&self, out_width: u32, out_height: u32) -> Image<Vec<u8>, 3> {
|
pub unsafe fn repeated(&self, out_width: u32, out_height: u32) -> Image<Vec<u8>, 3> {
|
||||||
let mut img = Vec::with_capacity(3 * out_width as usize * out_height as usize);
|
let mut img = uninit::Image::new(
|
||||||
|
out_width.try_into().unwrap(),
|
||||||
|
out_height.try_into().unwrap(),
|
||||||
|
);
|
||||||
debug_assert!(out_width % self.width() == 0);
|
debug_assert!(out_width % self.width() == 0);
|
||||||
debug_assert!(out_height % self.height() == 0);
|
debug_assert!(out_height % self.height() == 0);
|
||||||
for y in 0..self.height() {
|
for y in 0..self.height() {
|
||||||
|
@ -162,43 +187,25 @@ impl Image<&[u8], 3> {
|
||||||
.get_unchecked(self.at(0, y)..self.at(0, y) + (self.width() as usize * 3))
|
.get_unchecked(self.at(0, y)..self.at(0, y) + (self.width() as usize * 3))
|
||||||
};
|
};
|
||||||
debug_assert_eq!(from.len(), self.width() as usize * 3);
|
debug_assert_eq!(from.len(), self.width() as usize * 3);
|
||||||
let first =
|
let first = (0, y)..(self.width(), y);
|
||||||
((y * out_width) as usize * 3)..((y * out_width + self.width()) as usize * 3);
|
// SAFETY: copy it in
|
||||||
// SAFETY: put it in the output
|
unsafe { img.write(from, first.clone()) };
|
||||||
let to = unsafe { img.spare_capacity_mut().get_unchecked_mut(first.clone()) };
|
|
||||||
// copy it in
|
|
||||||
unsafe { assert_unchecked!(to.len() == from.len()) };
|
|
||||||
MaybeUninit::write_slice(to, from);
|
|
||||||
|
|
||||||
for x in 1..(out_width / self.width()) {
|
for x in 1..(out_width / self.width()) {
|
||||||
let section = (y * out_width + x * self.width()) as usize * 3;
|
let section = img.at(x * self.width(), y);
|
||||||
// SAFETY: copy each row of the image one by one
|
// SAFETY: copy each row of the image one by one
|
||||||
unsafe {
|
unsafe { img.copy_within(first.clone(), section) };
|
||||||
img.spare_capacity_mut()
|
|
||||||
.copy_within_unchecked(first.clone(), section)
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let first_row = 0..(self.height() * out_width) as usize * 3;
|
let first_row = 0..img.at(0, self.height());
|
||||||
for y in 1..(out_height / self.height()) {
|
for y in 1..(out_height / self.height()) {
|
||||||
let this_row = (y * self.height() * out_width) as usize * 3;
|
let this_row = img.at(0, y * self.height());
|
||||||
// SAFETY: copy entire blocks of image at a time
|
// SAFETY: copy entire blocks of image at a time
|
||||||
unsafe {
|
unsafe { img.copy_within(first_row.clone(), this_row) };
|
||||||
img.spare_capacity_mut()
|
|
||||||
.copy_within_unchecked(first_row.clone(), this_row)
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
// SAFETY: we init
|
// SAFETY: we init
|
||||||
unsafe { img.set_len(3 * out_width as usize * out_height as usize) };
|
unsafe { img.assume_init() }
|
||||||
// SAFETY: ok
|
|
||||||
unsafe {
|
|
||||||
Image::new(
|
|
||||||
out_width.try_into().unwrap(),
|
|
||||||
out_height.try_into().unwrap(),
|
|
||||||
img,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -297,18 +304,7 @@ impl<T, const CHANNELS: usize> Image<T, CHANNELS> {
|
||||||
/// the output index is not guranteed to be in bounds
|
/// the output index is not guranteed to be in bounds
|
||||||
#[inline]
|
#[inline]
|
||||||
fn at(&self, x: u32, y: u32) -> usize {
|
fn at(&self, x: u32, y: u32) -> usize {
|
||||||
debug_assert!(x < self.width(), "x out of bounds");
|
(self.width(), self.height()).at::<CHANNELS>(x, y)
|
||||||
debug_assert!(y < self.height(), "y out of bounds");
|
|
||||||
#[allow(clippy::multiple_unsafe_ops_per_block)]
|
|
||||||
// SAFETY: me when uncheck math: 😧 (FIXME)
|
|
||||||
let index = unsafe {
|
|
||||||
let w = self.width();
|
|
||||||
// y * w + x
|
|
||||||
let tmp = (y as usize).unchecked_mul(w as usize);
|
|
||||||
tmp.unchecked_add(x as usize)
|
|
||||||
};
|
|
||||||
// SAFETY: 🧐 is unsound? 😖
|
|
||||||
unsafe { index.unchecked_mul(CHANNELS) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # Safety
|
/// # Safety
|
||||||
|
|
34
src/span.rs
Normal file
34
src/span.rs
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
use crate::At;
|
||||||
|
use std::ops::Range;
|
||||||
|
|
||||||
|
mod sealer {
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub trait Sealed {}
|
||||||
|
}
|
||||||
|
use sealer::Sealed;
|
||||||
|
|
||||||
|
/// Trait for that which can be used to index a image.
|
||||||
|
pub trait Span: Sealed {
|
||||||
|
#[doc(hidden)]
|
||||||
|
fn range<const C: usize>(self, i: (u32, u32)) -> Range<usize>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sealed for Range<usize> {}
|
||||||
|
impl Span for Range<usize> {
|
||||||
|
#[inline(always)]
|
||||||
|
fn range<const C: usize>(self, _: (u32, u32)) -> Range<usize> {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sealed for Range<(u32, u32)> {}
|
||||||
|
impl Span for Range<(u32, u32)> {
|
||||||
|
#[inline(always)]
|
||||||
|
fn range<const C: usize>(self, i: (u32, u32)) -> Range<usize> {
|
||||||
|
let Self {
|
||||||
|
start: (sx, sy),
|
||||||
|
end: (ex, ey),
|
||||||
|
} = self;
|
||||||
|
i.at::<C>(sx, sy)..i.at::<C>(ex, ey)
|
||||||
|
}
|
||||||
|
}
|
120
src/uninit.rs
Normal file
120
src/uninit.rs
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
//! the houser of uninitialized memory. €$@!0В!℡
|
||||||
|
//!
|
||||||
|
//! contains [`Image`], an uninitialized image.
|
||||||
|
use std::{mem::MaybeUninit, num::NonZeroU32};
|
||||||
|
|
||||||
|
use crate::CopyWithinUnchecked;
|
||||||
|
|
||||||
|
/// A uninitialized image. Be sure to initialize it!
|
||||||
|
pub struct Image<T: Copy, const CHANNELS: usize> {
|
||||||
|
/// Has capacity w * h * c
|
||||||
|
buffer: Vec<T>,
|
||||||
|
width: NonZeroU32,
|
||||||
|
height: NonZeroU32,
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
Self {
|
||||||
|
buffer: Vec::with_capacity(width.get() as usize * height.get() as usize * CHANNELS),
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write to the image.
|
||||||
|
///
|
||||||
|
/// # 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) };
|
||||||
|
MaybeUninit::write_slice(dat, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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) {
|
||||||
|
let range = i.range::<CHANNELS>((self.width(), self.height()));
|
||||||
|
// SAFETY: copy!
|
||||||
|
unsafe { self.buf().copy_within_unchecked(range, to) };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// the output index is not guranteed to be in bounds
|
||||||
|
#[inline]
|
||||||
|
pub fn at(&self, x: u32, y: u32) -> usize {
|
||||||
|
crate::At::at::<CHANNELS>((self.width(), self.height()), x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
/// get the height as a [`u32`]
|
||||||
|
pub const fn height(&self) -> u32 {
|
||||||
|
self.height.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
/// get the width as a [`u32`]
|
||||||
|
pub const fn width(&self) -> u32 {
|
||||||
|
self.width.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
/// create a new image
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// does not check that buffer.capacity() == w * h * C
|
||||||
|
///
|
||||||
|
/// using this with invalid values may result in future UB
|
||||||
|
pub const unsafe fn with_buf(buffer: Vec<T>, width: NonZeroU32, height: NonZeroU32) -> Self {
|
||||||
|
Self {
|
||||||
|
buffer,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// consumes the image, returning the image buffer
|
||||||
|
pub fn take_buffer(self) -> Vec<T> {
|
||||||
|
self.buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
/// returns a immutable reference to the backing buffer
|
||||||
|
pub fn buffer(&self) -> &[T] {
|
||||||
|
&self.buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
/// returns a mutable reference to the backing buffer
|
||||||
|
pub fn buf(&mut self) -> &mut [MaybeUninit<T>] {
|
||||||
|
self.buffer.spare_capacity_mut()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// initializes this image, assuming you have done your job
|
||||||
|
/// # Safety
|
||||||
|
/// requires initialization
|
||||||
|
pub unsafe fn init(&mut self) {
|
||||||
|
// SAFETY: we have trust for our callers.
|
||||||
|
unsafe {
|
||||||
|
self.buffer
|
||||||
|
.set_len(self.width() as usize * self.height() as usize * CHANNELS)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// initializes this image, mapping to a normal [`crate::Image`] type.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// UB if you have not init the image
|
||||||
|
pub unsafe fn assume_init(mut self) -> crate::Image<Vec<T>, CHANNELS> {
|
||||||
|
// SAFETY: its apparently init
|
||||||
|
unsafe { self.init() };
|
||||||
|
// SAFETY: image all init, good to go
|
||||||
|
unsafe { crate::Image::new(self.width, self.height, self.buffer) }
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue