mirror of
https://github.com/bend-n/fimg.git
synced 2024-12-22 02:28:19 -06:00
uninit flips
This commit is contained in:
parent
d56ce34d6e
commit
698f8d468a
|
@ -10,16 +10,17 @@ impl<const CHANNELS: usize> ImageCloner<'_, CHANNELS> {
|
||||||
/// ```
|
/// ```
|
||||||
#[must_use = "function does not modify the original image"]
|
#[must_use = "function does not modify the original image"]
|
||||||
pub fn flip_v(&self) -> Image<Vec<u8>, CHANNELS> {
|
pub fn flip_v(&self) -> Image<Vec<u8>, CHANNELS> {
|
||||||
let mut out = self.alloc();
|
let mut out = self.uninit();
|
||||||
for y in 0..self.height() {
|
for y in 0..self.height() {
|
||||||
for x in 0..self.width() {
|
for x in 0..self.width() {
|
||||||
// SAFETY: looping over self, all ok (could be safe versions, bounds would be elided)
|
// SAFETY: looping over self, all ok (could be safe versions, bounds would be elided)
|
||||||
let p = unsafe { self.pixel(x, y) };
|
let p = unsafe { self.pixel(x, y) };
|
||||||
// SAFETY: looping over self.
|
// SAFETY: looping over self.
|
||||||
unsafe { out.set_pixel(x, self.height() - y - 1, p) };
|
unsafe { out.write(&p, (x, self.height() - y - 1)) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
out
|
// SAFETY: init
|
||||||
|
unsafe { out.assume_init() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Flip an image horizontally
|
/// Flip an image horizontally
|
||||||
|
@ -30,16 +31,17 @@ impl<const CHANNELS: usize> ImageCloner<'_, CHANNELS> {
|
||||||
/// ```
|
/// ```
|
||||||
#[must_use = "function does not modify the original image"]
|
#[must_use = "function does not modify the original image"]
|
||||||
pub fn flip_h(&self) -> Image<Vec<u8>, CHANNELS> {
|
pub fn flip_h(&self) -> Image<Vec<u8>, CHANNELS> {
|
||||||
let mut out = self.alloc();
|
let mut out = self.uninit();
|
||||||
for y in 0..self.height() {
|
for y in 0..self.height() {
|
||||||
for x in 0..self.width() {
|
for x in 0..self.width() {
|
||||||
// SAFETY: looping over self, all ok
|
// SAFETY: looping over self, all ok
|
||||||
let p = unsafe { self.pixel(x, y) };
|
let p = unsafe { self.pixel(x, y) };
|
||||||
// SAFETY: looping over self, all ok
|
// SAFETY: looping over self, all ok
|
||||||
unsafe { out.set_pixel(self.width() - x - 1, y, p) };
|
unsafe { out.write(&p, (self.width() - x - 1, y)) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
out
|
// SAFETY: init
|
||||||
|
unsafe { out.assume_init() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
//! # let i = Image::<_, 1>::alloc(5, 5);
|
//! # let i = Image::<_, 1>::alloc(5, 5);
|
||||||
//! unsafe { i.cloner().rot_270() };
|
//! unsafe { i.cloner().rot_270() };
|
||||||
//! ```
|
//! ```
|
||||||
use crate::Image;
|
use crate::{uninit, Image};
|
||||||
|
|
||||||
/// A neat way to clone a image.
|
/// A neat way to clone a image.
|
||||||
///
|
///
|
||||||
|
@ -19,6 +19,11 @@ impl<'a, const C: usize> ImageCloner<'a, C> {
|
||||||
self.0.to_owned()
|
self.0.to_owned()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// create a new uninit image the right size for use
|
||||||
|
pub(crate) fn uninit(&self) -> uninit::Image<u8, C> {
|
||||||
|
uninit::Image::new(self.width, self.height)
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a [`ImageCloner`] from a <code>[Image]<&\[[u8]\]></code>
|
/// Create a [`ImageCloner`] from a <code>[Image]<&\[[u8]\]></code>
|
||||||
pub const fn from(i: Image<&'a [u8], C>) -> Self {
|
pub const fn from(i: Image<&'a [u8], C>) -> Self {
|
||||||
Self(i)
|
Self(i)
|
||||||
|
|
30
src/lib.rs
30
src/lib.rs
|
@ -49,6 +49,7 @@
|
||||||
//! - `default`: \[`save`, `scale`\].
|
//! - `default`: \[`save`, `scale`\].
|
||||||
#![feature(
|
#![feature(
|
||||||
maybe_uninit_write_slice,
|
maybe_uninit_write_slice,
|
||||||
|
hint_assert_unchecked,
|
||||||
slice_swap_unchecked,
|
slice_swap_unchecked,
|
||||||
generic_const_exprs,
|
generic_const_exprs,
|
||||||
slice_as_chunks,
|
slice_as_chunks,
|
||||||
|
@ -72,7 +73,7 @@
|
||||||
missing_docs
|
missing_docs
|
||||||
)]
|
)]
|
||||||
#![allow(clippy::zero_prefixed_literal, incomplete_features)]
|
#![allow(clippy::zero_prefixed_literal, incomplete_features)]
|
||||||
use std::{num::NonZeroU32, ops::Range};
|
use std::{hint::assert_unchecked, num::NonZeroU32, ops::Range};
|
||||||
|
|
||||||
mod affine;
|
mod affine;
|
||||||
#[cfg(feature = "blur")]
|
#[cfg(feature = "blur")]
|
||||||
|
@ -122,25 +123,6 @@ impl<T> CopyWithinUnchecked for [T] {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// like assert!(), but causes undefined behaviour at runtime when the condition is not met.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// UB if condition is false.
|
|
||||||
macro_rules! assert_unchecked {
|
|
||||||
($cond:expr) => {{
|
|
||||||
if !$cond {
|
|
||||||
#[cfg(debug_assertions)]
|
|
||||||
let _ = ::core::ptr::NonNull::<()>::dangling().as_ref(); // force unsafe wrapping block
|
|
||||||
#[cfg(debug_assertions)]
|
|
||||||
panic!("assertion failed: {} returned false", stringify!($cond));
|
|
||||||
#[cfg(not(debug_assertions))]
|
|
||||||
std::hint::unreachable_unchecked()
|
|
||||||
}
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
use assert_unchecked;
|
|
||||||
|
|
||||||
trait At {
|
trait At {
|
||||||
fn at<const C: usize>(self, x: u32, y: u32) -> usize;
|
fn at<const C: usize>(self, x: u32, y: u32) -> usize;
|
||||||
}
|
}
|
||||||
|
@ -455,9 +437,9 @@ impl<T, const CHANNELS: usize> Image<T, CHANNELS> {
|
||||||
T: AsRef<[U]>,
|
T: AsRef<[U]>,
|
||||||
{
|
{
|
||||||
// SAFETY: 0 sized images illegal
|
// SAFETY: 0 sized images illegal
|
||||||
unsafe { assert_unchecked!(self.len() > CHANNELS) };
|
unsafe { assert_unchecked(self.len() > CHANNELS) };
|
||||||
// SAFETY: no half pixels!
|
// SAFETY: no half pixels!
|
||||||
unsafe { assert_unchecked!(self.len() % CHANNELS == 0) };
|
unsafe { assert_unchecked(self.len() % CHANNELS == 0) };
|
||||||
self.buffer().as_ref().array_chunks::<CHANNELS>()
|
self.buffer().as_ref().array_chunks::<CHANNELS>()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -546,9 +528,9 @@ impl<T: AsMut<[u8]> + AsRef<[u8]>, const CHANNELS: usize> Image<T, CHANNELS> {
|
||||||
/// Returns a iterator over every pixel, mutably
|
/// Returns a iterator over every pixel, mutably
|
||||||
pub fn chunked_mut(&mut self) -> impl Iterator<Item = &mut [u8; CHANNELS]> {
|
pub fn chunked_mut(&mut self) -> impl Iterator<Item = &mut [u8; CHANNELS]> {
|
||||||
// SAFETY: 0 sized images are not allowed
|
// SAFETY: 0 sized images are not allowed
|
||||||
unsafe { assert_unchecked!(self.len() > CHANNELS) };
|
unsafe { assert_unchecked(self.len() > CHANNELS) };
|
||||||
// SAFETY: buffer cannot have half pixels
|
// SAFETY: buffer cannot have half pixels
|
||||||
unsafe { assert_unchecked!(self.len() % CHANNELS == 0) };
|
unsafe { assert_unchecked(self.len() % CHANNELS == 0) };
|
||||||
self.buffer.as_mut().array_chunks_mut::<CHANNELS>()
|
self.buffer.as_mut().array_chunks_mut::<CHANNELS>()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -160,7 +160,7 @@ impl<T: AsMut<[u8]> + AsRef<[u8]>, U: AsRef<[u8]>> OverlayAt<Image<U, 4>> for Im
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn overlay_at(&mut self, with: &Image<U, 4>, x: u32, y: u32) -> &mut Self {
|
unsafe fn overlay_at(&mut self, with: &Image<U, 4>, x: u32, y: u32) -> &mut Self {
|
||||||
// SAFETY: caller upholds this
|
// SAFETY: caller upholds this
|
||||||
unsafe { assert_unchecked!(x + with.width() <= self.width()) };
|
unsafe { assert_unchecked(x + with.width() <= self.width()) };
|
||||||
debug_assert!(y + with.height() <= self.height());
|
debug_assert!(y + with.height() <= self.height());
|
||||||
for j in 0..with.height() {
|
for j in 0..with.height() {
|
||||||
let i_x = j as usize * with.width() as usize * 4
|
let i_x = j as usize * with.width() as usize * 4
|
||||||
|
|
|
@ -32,3 +32,11 @@ impl Span for Range<(u32, u32)> {
|
||||||
i.at::<C>(sx, sy)..i.at::<C>(ex, ey)
|
i.at::<C>(sx, sy)..i.at::<C>(ex, ey)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Sealed for (u32, u32) {}
|
||||||
|
impl Span for (u32, u32) {
|
||||||
|
#[inline(always)]
|
||||||
|
fn range<const C: usize>(self, i: (u32, u32)) -> Range<usize> {
|
||||||
|
i.at::<C>(self.0, self.1)..i.at::<C>(self.0, self.1) + C
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,9 +1,14 @@
|
||||||
//! the houser of uninitialized memory. €$@!0В!℡
|
//! the houser of uninitialized memory. €$@!0В!℡
|
||||||
//!
|
//!
|
||||||
//! contains [`Image`], an uninitialized image.
|
//! contains [`Image`], an uninitialized image.
|
||||||
use std::{mem::MaybeUninit, num::NonZeroU32};
|
use std::{
|
||||||
|
hint::assert_unchecked,
|
||||||
|
mem::MaybeUninit,
|
||||||
|
num::NonZeroU32,
|
||||||
|
ops::{Index, IndexMut},
|
||||||
|
};
|
||||||
|
|
||||||
use crate::CopyWithinUnchecked;
|
use crate::{span::Span, CopyWithinUnchecked};
|
||||||
|
|
||||||
/// A uninitialized image. Be sure to initialize it!
|
/// A uninitialized image. Be sure to initialize it!
|
||||||
pub struct Image<T: Copy, const CHANNELS: usize> {
|
pub struct Image<T: Copy, const CHANNELS: usize> {
|
||||||
|
@ -13,6 +18,14 @@ pub struct Image<T: Copy, const CHANNELS: usize> {
|
||||||
height: NonZeroU32,
|
height: NonZeroU32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<I: Span, T: Copy, const C: usize> Index<I> for Image<T, C> {
|
||||||
|
type Output = [T];
|
||||||
|
|
||||||
|
fn index(&self, index: I) -> &Self::Output {
|
||||||
|
&self.buffer()[index.range::<C>((self.width(), self.height()))]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T: Copy, const CHANNELS: usize> Image<T, CHANNELS> {
|
impl<T: Copy, const CHANNELS: usize> Image<T, CHANNELS> {
|
||||||
/// Create a new uninit image. This is not init.
|
/// Create a new uninit image. This is not init.
|
||||||
pub fn new(width: NonZeroU32, height: NonZeroU32) -> Self {
|
pub fn new(width: NonZeroU32, height: NonZeroU32) -> Self {
|
||||||
|
@ -27,19 +40,31 @@ impl<T: Copy, const CHANNELS: usize> Image<T, CHANNELS> {
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// index must be in bounds.
|
/// index must be in bounds.
|
||||||
pub unsafe fn write(&mut self, data: &[T], i: impl crate::span::Span) {
|
/// data and indexed range must have same len.
|
||||||
let range = i.range::<CHANNELS>((self.width(), self.height()));
|
pub unsafe fn write(&mut self, data: &[T], i: impl Span) {
|
||||||
// SAFETY: write
|
// SAFETY: caller
|
||||||
let dat = unsafe { self.buf().get_unchecked_mut(range) };
|
let dat = unsafe { self.slice(i) };
|
||||||
|
// SAFETY: caller
|
||||||
|
unsafe { assert_unchecked(dat.len() == data.len()) };
|
||||||
MaybeUninit::write_slice(dat, data);
|
MaybeUninit::write_slice(dat, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Slice the image.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// index must be in bounds.
|
||||||
|
pub unsafe fn slice(&mut self, i: impl Span) -> &mut [MaybeUninit<T>] {
|
||||||
|
let range = i.range::<CHANNELS>((self.width(), self.height()));
|
||||||
|
// SAFETY: assured
|
||||||
|
unsafe { self.buf().get_unchecked_mut(range) }
|
||||||
|
}
|
||||||
|
|
||||||
/// Copy a range to a position.
|
/// Copy a range to a position.
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// both parts must be in bounds.
|
/// both parts must be in bounds.
|
||||||
pub unsafe fn copy_within(&mut self, i: impl crate::span::Span, to: usize) {
|
pub unsafe fn copy_within(&mut self, i: impl Span, to: usize) {
|
||||||
let range = i.range::<CHANNELS>((self.width(), self.height()));
|
let range = i.range::<CHANNELS>((self.width(), self.height()));
|
||||||
// SAFETY: copy!
|
// SAFETY: copy!
|
||||||
unsafe { self.buf().copy_within_unchecked(range, to) };
|
unsafe { self.buf().copy_within_unchecked(range, to) };
|
||||||
|
|
Loading…
Reference in a new issue