mirror of
https://github.com/bend-n/fimg.git
synced 2024-12-22 18:38:21 -06:00
add a ImageCloner
This commit is contained in:
parent
1b5b06c36f
commit
829cfa680e
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "fimg"
|
name = "fimg"
|
||||||
version = "0.4.3"
|
version = "0.4.4"
|
||||||
authors = ["bend-n <bend.n@outlook.com>"]
|
authors = ["bend-n <bend.n@outlook.com>"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
142
src/affine.rs
142
src/affine.rs
|
@ -1,19 +1,58 @@
|
||||||
//! Manages the affine image transformations.
|
//! Manages the affine image transformations.
|
||||||
use crate::Image;
|
use crate::{cloner::ImageCloner, Image};
|
||||||
|
|
||||||
impl<const CHANNELS: usize> Image<Vec<u8>, CHANNELS> {
|
impl<const CHANNELS: usize> Image<Vec<u8>, CHANNELS> {
|
||||||
/// Flip a image horizontally.
|
/// Flip an image horizontally.
|
||||||
pub fn flip_h(&mut self) {
|
pub fn flip_h(&mut self) {
|
||||||
self.as_mut().flip_h();
|
self.as_mut().flip_h();
|
||||||
}
|
}
|
||||||
/// Flip a image vertically.
|
/// Flip an image vertically.
|
||||||
pub fn flip_v(&mut self) {
|
pub fn flip_v(&mut self) {
|
||||||
self.as_mut().flip_v();
|
self.as_mut().flip_v();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl<const CHANNELS: usize> ImageCloner<'_, CHANNELS> {
|
||||||
|
/// Flip an image vertically.
|
||||||
|
/// ```
|
||||||
|
/// # use fimg::Image;
|
||||||
|
/// let a = Image::<_, 1>::build(2,2).buf(vec![21,42,90,01]);
|
||||||
|
/// assert_eq!(a.cloner().flip_v().take_buffer(), [90,01,21,42]);
|
||||||
|
/// ```
|
||||||
|
pub fn flip_v(&self) -> Image<Vec<u8>, CHANNELS> {
|
||||||
|
let mut out = self.alloc();
|
||||||
|
for y in 0..self.height() {
|
||||||
|
for x in 0..self.width() {
|
||||||
|
// 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.set_pixel(x, self.height() - y - 1, p) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Flip an image horizontally
|
||||||
|
/// ```
|
||||||
|
/// # use fimg::Image;
|
||||||
|
/// let a = Image::<_,1>::build(2,2).buf(vec![90,01,21,42]);
|
||||||
|
/// assert_eq!(a.cloner().flip_h().take_buffer(), [01,90,42,21]);
|
||||||
|
/// ```
|
||||||
|
pub fn flip_h(&self) -> Image<Vec<u8>, CHANNELS> {
|
||||||
|
let mut out = self.alloc();
|
||||||
|
for y in 0..self.height() {
|
||||||
|
for x in 0..self.width() {
|
||||||
|
// SAFETY: looping over self, all ok
|
||||||
|
let p = unsafe { self.pixel(x, y) };
|
||||||
|
// SAFETY: looping over self, all ok
|
||||||
|
unsafe { out.set_pixel(self.width() - x - 1, y, p) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<const CHANNELS: usize> Image<&mut [u8], CHANNELS> {
|
impl<const CHANNELS: usize> Image<&mut [u8], CHANNELS> {
|
||||||
/// Flip a image vertically.
|
/// Flip an image vertically.
|
||||||
pub fn flip_v(&mut self) {
|
pub fn flip_v(&mut self) {
|
||||||
for y in 0..self.height() / 2 {
|
for y in 0..self.height() / 2 {
|
||||||
for x in 0..self.width() {
|
for x in 0..self.width() {
|
||||||
|
@ -30,7 +69,7 @@ impl<const CHANNELS: usize> Image<&mut [u8], CHANNELS> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Flip a image horizontally.
|
/// Flip an image horizontally.
|
||||||
pub fn flip_h(&mut self) {
|
pub fn flip_h(&mut self) {
|
||||||
for y in 0..self.height() {
|
for y in 0..self.height() {
|
||||||
for x in 0..self.width() / 2 {
|
for x in 0..self.width() / 2 {
|
||||||
|
@ -49,12 +88,12 @@ impl<const CHANNELS: usize> Image<&mut [u8], CHANNELS> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const CHANNELS: usize> Image<Vec<u8>, CHANNELS> {
|
impl<const CHANNELS: usize> Image<Vec<u8>, CHANNELS> {
|
||||||
/// Rotate a image 180 degrees clockwise.
|
/// Rotate an image 180 degrees clockwise.
|
||||||
pub fn rot_180(&mut self) {
|
pub fn rot_180(&mut self) {
|
||||||
self.as_mut().rot_180();
|
self.as_mut().rot_180();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Rotate a image 90 degrees clockwise.
|
/// Rotate an image 90 degrees clockwise.
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// UB if the image is not square
|
/// UB if the image is not square
|
||||||
|
@ -63,7 +102,7 @@ impl<const CHANNELS: usize> Image<Vec<u8>, CHANNELS> {
|
||||||
unsafe { self.as_mut().rot_90() }
|
unsafe { self.as_mut().rot_90() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Rotate a image 270 degrees clockwise, or 90 degrees anti clockwise.
|
/// Rotate an image 270 degrees clockwise, or 90 degrees anti clockwise.
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// UB if the image is not square
|
/// UB if the image is not square
|
||||||
|
@ -73,42 +112,60 @@ impl<const CHANNELS: usize> Image<Vec<u8>, CHANNELS> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<const CHANNELS: usize> ImageCloner<'_, CHANNELS> {
|
||||||
|
/// Rotate an image 180 degrees clockwise.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use fimg::Image;
|
||||||
|
/// let a = Image::<_,1>::build(2,2).buf(vec![00,01,02,10]);
|
||||||
|
/// assert_eq!(a.cloner().rot_180().take_buffer(), vec![10,02,01,00]);
|
||||||
|
/// ```
|
||||||
|
pub fn rot_180(&self) -> Image<Vec<u8>, CHANNELS> {
|
||||||
|
let s = (self.width() * self.height()) as usize;
|
||||||
|
let mut v: Vec<[u8; CHANNELS]> = Vec::with_capacity(s);
|
||||||
|
for (x, y) in self.chunked().rev().zip(&mut v.spare_capacity_mut()[..]) {
|
||||||
|
y.write(*x);
|
||||||
|
}
|
||||||
|
// SAFETY: we just wrote the right amount
|
||||||
|
unsafe { v.set_len(s) };
|
||||||
|
let (v, _, c) = v.into_raw_parts();
|
||||||
|
let s = s * CHANNELS;
|
||||||
|
// SAFETY: init with with_cap, set len to s, s is init amount, chunked returns nm, capacity handled, flatten vec
|
||||||
|
let v = unsafe { Vec::from_raw_parts(v.cast::<u8>(), s, c * CHANNELS) };
|
||||||
|
// SAFETY: s is w * h.
|
||||||
|
unsafe { Image::new(self.width, self.height, v) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Rotate an image 90 degrees clockwise.
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// UB if the image is not square
|
||||||
|
pub unsafe fn rot_90(&self) -> Image<Vec<u8>, CHANNELS> {
|
||||||
|
let mut out = self.flip_v();
|
||||||
|
// SAFETY: sqar
|
||||||
|
unsafe { transpose(&mut out.as_mut()) };
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Rotate an image 270 degrees clockwise, or 90 degrees anti clockwise.
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// UB if the image is not square
|
||||||
|
pub unsafe fn rot_270(&self) -> Image<Vec<u8>, CHANNELS> {
|
||||||
|
let mut out = self.flip_h();
|
||||||
|
// SAFETY: sqar
|
||||||
|
unsafe { transpose(&mut out.as_mut()) };
|
||||||
|
out
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<const CHANNELS: usize> Image<&mut [u8], CHANNELS> {
|
impl<const CHANNELS: usize> Image<&mut [u8], CHANNELS> {
|
||||||
/// Rotate a image 180 degrees clockwise.
|
/// Rotate an image 180 degrees clockwise.
|
||||||
pub fn rot_180(&mut self) {
|
pub fn rot_180(&mut self) {
|
||||||
for y in 0..self.height() / 2 {
|
self.flatten_mut().reverse();
|
||||||
for x in 0..self.width() {
|
|
||||||
// SAFETY: x, y come from the loop, must be ok
|
|
||||||
let p = unsafe { self.pixel(x, y) };
|
|
||||||
let x2 = self.width() - x - 1;
|
|
||||||
let y2 = self.height() - y - 1;
|
|
||||||
// SAFETY: values are good
|
|
||||||
let p2 = unsafe { self.pixel(x2, y2) };
|
|
||||||
// SAFETY: swapping would be cool, alas.
|
|
||||||
unsafe { self.set_pixel(x, y, p2) };
|
|
||||||
// SAFETY: although maybe i can cast it to a `[[u8; CHANNELS]]` and swap that 🤔
|
|
||||||
unsafe { self.set_pixel(x2, y2, p) };
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.height() % 2 != 0 {
|
/// Rotate an image 90 degrees clockwise.
|
||||||
let middle = self.height() / 2;
|
|
||||||
|
|
||||||
for x in 0..self.width() / 2 {
|
|
||||||
let x2 = self.width() - x - 1;
|
|
||||||
#[allow(clippy::multiple_unsafe_ops_per_block)]
|
|
||||||
// SAFETY: its just doing the swappy
|
|
||||||
unsafe {
|
|
||||||
let p = self.pixel(x, middle);
|
|
||||||
let p2 = self.pixel(x2, middle);
|
|
||||||
self.set_pixel(x, middle, p2);
|
|
||||||
self.set_pixel(x2, middle, p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Rotate a image 90 degrees clockwise.
|
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// UB if the image is not square
|
/// UB if the image is not square
|
||||||
|
@ -121,7 +178,7 @@ impl<const CHANNELS: usize> Image<&mut [u8], CHANNELS> {
|
||||||
unsafe { transpose(self) };
|
unsafe { transpose(self) };
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Rotate a image 270 degrees clockwise, or 90 degrees anti clockwise.
|
/// Rotate an image 270 degrees clockwise, or 90 degrees anti clockwise.
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// UB if the image is not square
|
/// UB if the image is not square
|
||||||
|
@ -156,8 +213,7 @@ unsafe fn transpose<const CHANNELS: usize>(img: &mut Image<&mut [u8], CHANNELS>)
|
||||||
unsafe fn transpose_non_power_of_two<const CHANNELS: usize>(img: &mut Image<&mut [u8], CHANNELS>) {
|
unsafe fn transpose_non_power_of_two<const CHANNELS: usize>(img: &mut Image<&mut [u8], CHANNELS>) {
|
||||||
debug_assert_eq!(img.width(), img.height());
|
debug_assert_eq!(img.width(), img.height());
|
||||||
let size = img.width() as usize;
|
let size = img.width() as usize;
|
||||||
// SAFETY: no half pixels
|
let b = img.flatten_mut();
|
||||||
let b = unsafe { img.buffer.as_chunks_unchecked_mut::<CHANNELS>() };
|
|
||||||
for i in 0..size {
|
for i in 0..size {
|
||||||
for j in i..size {
|
for j in i..size {
|
||||||
// SAFETY: caller ensures squarity
|
// SAFETY: caller ensures squarity
|
||||||
|
|
39
src/cloner.rs
Normal file
39
src/cloner.rs
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
//! provides a [`ImageCloner`]
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! # use fimg::Image;
|
||||||
|
//! # let i = Image::<_, 1>::alloc(5, 5);
|
||||||
|
//! unsafe { i.cloner().rot_270() };
|
||||||
|
//! ```
|
||||||
|
use crate::Image;
|
||||||
|
|
||||||
|
/// A neat way to clone a image.
|
||||||
|
///
|
||||||
|
/// Consider it a way to clone->apply a image operation, but better.
|
||||||
|
/// Please note that some methods may(although none at current) have different safety invariants from their in place counterparts.
|
||||||
|
pub struct ImageCloner<'a, const C: usize>(Image<&'a [u8], C>);
|
||||||
|
|
||||||
|
impl<'a, const C: usize> ImageCloner<'a, C> {
|
||||||
|
/// duplicate the inner image.
|
||||||
|
pub(crate) fn dup(&self) -> Image<Vec<u8>, C> {
|
||||||
|
self.0.to_owned()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a [`ImageCloner`] from a <code>[Image]<&\[[u8]\]></code>
|
||||||
|
pub const fn from(i: Image<&'a [u8], C>) -> Self {
|
||||||
|
Self(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Alloc a buffer the right size for use
|
||||||
|
pub(crate) fn alloc(&self) -> Image<Vec<u8>, C> {
|
||||||
|
Image::alloc(self.width(), self.height())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, const C: usize> std::ops::Deref for ImageCloner<'a, C> {
|
||||||
|
type Target = Image<&'a [u8], C>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
83
src/lib.rs
83
src/lib.rs
|
@ -5,6 +5,7 @@
|
||||||
slice_swap_unchecked,
|
slice_swap_unchecked,
|
||||||
stmt_expr_attributes,
|
stmt_expr_attributes,
|
||||||
generic_const_exprs,
|
generic_const_exprs,
|
||||||
|
vec_into_raw_parts,
|
||||||
slice_as_chunks,
|
slice_as_chunks,
|
||||||
unchecked_math,
|
unchecked_math,
|
||||||
portable_simd,
|
portable_simd,
|
||||||
|
@ -28,9 +29,11 @@ use std::{num::NonZeroU32, slice::SliceIndex};
|
||||||
|
|
||||||
mod affine;
|
mod affine;
|
||||||
pub mod builder;
|
pub mod builder;
|
||||||
|
pub mod cloner;
|
||||||
mod drawing;
|
mod drawing;
|
||||||
mod overlay;
|
mod overlay;
|
||||||
pub mod scale;
|
pub mod scale;
|
||||||
|
use cloner::ImageCloner;
|
||||||
pub use overlay::{Overlay, OverlayAt};
|
pub use overlay::{Overlay, OverlayAt};
|
||||||
|
|
||||||
/// like assert!(), but causes undefined behaviour at runtime when the condition is not met.
|
/// like assert!(), but causes undefined behaviour at runtime when the condition is not met.
|
||||||
|
@ -81,7 +84,7 @@ unsafe fn really_unsafe_index(x: u32, y: u32, w: u32) -> usize {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A image with a variable number of channels, and a nonzero size.
|
/// A image with a variable number of channels, and a nonzero size.
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub struct Image<T, const CHANNELS: usize> {
|
pub struct Image<T, const CHANNELS: usize> {
|
||||||
/// column order 2d slice/vec
|
/// column order 2d slice/vec
|
||||||
buffer: T,
|
buffer: T,
|
||||||
|
@ -91,6 +94,37 @@ pub struct Image<T, const CHANNELS: usize> {
|
||||||
height: NonZeroU32,
|
height: NonZeroU32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: Clone, const CHANNELS: usize> Clone for Image<T, CHANNELS> {
|
||||||
|
/// Returns a duplicate of this image.
|
||||||
|
/// ```
|
||||||
|
/// # use fimg::Image;
|
||||||
|
/// # let i = Image::<Vec<_>, 1>::alloc(5,5);
|
||||||
|
/// let new_i = i.clone();
|
||||||
|
/// ```
|
||||||
|
/// If you find yourself in the pattern of
|
||||||
|
/// ```
|
||||||
|
/// # use fimg::Image;
|
||||||
|
/// # let i = Image::<Vec<_>, 1>::alloc(5,5);
|
||||||
|
/// let mut i = i.clone();
|
||||||
|
/// unsafe { i.rot_90() };
|
||||||
|
/// ```
|
||||||
|
/// STOP!
|
||||||
|
///
|
||||||
|
/// Instead use
|
||||||
|
/// ```
|
||||||
|
/// # use fimg::Image;
|
||||||
|
/// # let i = Image::<Vec<_>, 1>::alloc(5,5);
|
||||||
|
/// let i = unsafe { i.cloner().rot_90() };
|
||||||
|
/// ```
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
buffer: self.buffer.clone(),
|
||||||
|
width: self.width,
|
||||||
|
height: self.height,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T, const CHANNELS: usize> Image<T, CHANNELS> {
|
impl<T, const CHANNELS: usize> Image<T, CHANNELS> {
|
||||||
#[inline]
|
#[inline]
|
||||||
/// get the height as a [`u32`]
|
/// get the height as a [`u32`]
|
||||||
|
@ -156,6 +190,8 @@ impl<const CHANNELS: usize, T: Clone> Image<&mut [T], CHANNELS> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<const CHANNELS: usize> Copy for Image<&[u8], CHANNELS> {}
|
||||||
|
|
||||||
impl<const CHANNELS: usize> Image<&[u8], CHANNELS> {
|
impl<const CHANNELS: usize> Image<&[u8], CHANNELS> {
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
@ -209,9 +245,20 @@ impl<T: std::ops::Deref<Target = [u8]>, const CHANNELS: usize> Image<T, CHANNELS
|
||||||
index..unsafe { index.unchecked_add(CHANNELS) }
|
index..unsafe { index.unchecked_add(CHANNELS) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Procure a [`ImageCloner`].
|
||||||
|
pub fn cloner(&self) -> ImageCloner<'_, CHANNELS> {
|
||||||
|
ImageCloner::from(self.as_ref())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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) }
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
/// Returns a iterator over every pixel
|
/// Returns a iterator over every pixel
|
||||||
pub fn chunked(&self) -> impl Iterator<Item = &[u8; CHANNELS]> {
|
pub fn chunked(&self) -> impl DoubleEndedIterator<Item = &[u8; CHANNELS]> {
|
||||||
// SAFETY: 0 sized images illegal
|
// SAFETY: 0 sized images illegal
|
||||||
unsafe { assert_unchecked!(self.buffer.len() > CHANNELS) };
|
unsafe { assert_unchecked!(self.buffer.len() > CHANNELS) };
|
||||||
// SAFETY: no half pixels!
|
// SAFETY: no half pixels!
|
||||||
|
@ -266,6 +313,12 @@ impl<T: std::ops::DerefMut<Target = [u8]>, const CHANNELS: usize> Image<T, CHANN
|
||||||
self.buffer.array_chunks_mut::<CHANNELS>()
|
self.buffer.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) }
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
/// Flatten the chunks of this image into a mutable slice of slices.
|
/// Flatten the chunks of this image into a mutable slice of slices.
|
||||||
pub fn flatten_mut(&mut self) -> &mut [[u8; CHANNELS]] {
|
pub fn flatten_mut(&mut self) -> &mut [[u8; CHANNELS]] {
|
||||||
|
@ -288,12 +341,6 @@ impl<T: std::ops::DerefMut<Target = [u8]>, const CHANNELS: usize> Image<T, CHANN
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const CHANNELS: usize> Image<&mut [u8], CHANNELS> {
|
impl<const CHANNELS: usize> Image<&mut [u8], CHANNELS> {
|
||||||
/// Downcast the mutable reference
|
|
||||||
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) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Copy this ref image
|
/// Copy this ref image
|
||||||
pub fn copy(&mut self) -> Image<&mut [u8], CHANNELS> {
|
pub fn copy(&mut self) -> Image<&mut [u8], CHANNELS> {
|
||||||
#[allow(clippy::undocumented_unsafe_blocks)]
|
#[allow(clippy::undocumented_unsafe_blocks)]
|
||||||
|
@ -303,26 +350,6 @@ impl<const CHANNELS: usize> Image<&mut [u8], CHANNELS> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const CHANNELS: usize> Image<Vec<u8>, CHANNELS> {
|
|
||||||
/// Create a reference to this owned image
|
|
||||||
pub fn as_ref(&self) -> Image<&[u8], CHANNELS> {
|
|
||||||
#[allow(clippy::undocumented_unsafe_blocks)]
|
|
||||||
unsafe {
|
|
||||||
Image::new(self.width, self.height, &self.buffer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<const CHANNELS: usize> Image<Vec<u8>, CHANNELS> {
|
|
||||||
/// Create a mutable reference to this owned image
|
|
||||||
pub fn as_mut(&mut self) -> Image<&mut [u8], CHANNELS> {
|
|
||||||
#[allow(clippy::undocumented_unsafe_blocks)]
|
|
||||||
unsafe {
|
|
||||||
Image::new(self.width, self.height, &mut self.buffer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<const CHANNELS: usize> Image<Vec<u8>, CHANNELS> {
|
impl<const CHANNELS: usize> Image<Vec<u8>, CHANNELS> {
|
||||||
/// Allocates a new image
|
/// Allocates a new image
|
||||||
///
|
///
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
//! Handles image overlay
|
//! Handles image overlay
|
||||||
|
use crate::cloner::ImageCloner;
|
||||||
|
|
||||||
use super::{assert_unchecked, really_unsafe_index, Image};
|
use super::{assert_unchecked, really_unsafe_index, Image};
|
||||||
use std::simd::SimdInt;
|
use std::simd::SimdInt;
|
||||||
use std::simd::SimdPartialOrd;
|
use std::simd::SimdPartialOrd;
|
||||||
|
@ -12,6 +14,16 @@ pub trait OverlayAt<W> {
|
||||||
/// UB if x, y is out of bounds
|
/// UB if x, y is out of bounds
|
||||||
unsafe fn overlay_at(&mut self, with: &W, x: u32, y: u32) -> &mut Self;
|
unsafe fn overlay_at(&mut self, with: &W, x: u32, y: u32) -> &mut Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// [`OverlayAt`] but owned
|
||||||
|
pub trait ClonerOverlayAt<const W: usize, const C: usize> {
|
||||||
|
/// Overlay with => self at coordinates x, y, without blending, and returning a new image.
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// UB if x, y is out of bounds
|
||||||
|
unsafe fn overlay_at(&self, with: &Image<&[u8], W>, x: u32, y: u32) -> Image<Vec<u8>, C>;
|
||||||
|
}
|
||||||
|
|
||||||
/// Trait for layering images ontop of each other.
|
/// Trait for layering images ontop of each other.
|
||||||
/// Think `magick a b -layers flatten a`
|
/// Think `magick a b -layers flatten a`
|
||||||
pub trait Overlay<W> {
|
pub trait Overlay<W> {
|
||||||
|
@ -22,6 +34,15 @@ pub trait Overlay<W> {
|
||||||
unsafe fn overlay(&mut self, with: &W) -> &mut Self;
|
unsafe fn overlay(&mut self, with: &W) -> &mut Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// [`Overlay`] but owned
|
||||||
|
pub trait ClonerOverlay<const W: usize, const C: usize> {
|
||||||
|
/// Overlay with => self (does not blend)
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// UB if a.width != b.width || a.height != b.height
|
||||||
|
unsafe fn overlay(&self, with: &Image<&[u8], W>) -> Image<Vec<u8>, C>;
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
/// SIMD accelerated rgba => rgb overlay.
|
/// SIMD accelerated rgba => rgb overlay.
|
||||||
///
|
///
|
||||||
|
@ -88,6 +109,16 @@ impl Overlay<Image<&[u8], 4>> for Image<&mut [u8], 4> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ClonerOverlay<4, 4> for ImageCloner<'_, 4> {
|
||||||
|
#[inline]
|
||||||
|
unsafe fn overlay(&self, with: &Image<&[u8], 4>) -> Image<Vec<u8>, 4> {
|
||||||
|
let mut out = self.dup();
|
||||||
|
// SAFETY: same
|
||||||
|
unsafe { out.as_mut().overlay(with) };
|
||||||
|
out
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl OverlayAt<Image<&[u8], 4>> for Image<&mut [u8], 3> {
|
impl OverlayAt<Image<&[u8], 4>> for Image<&mut [u8], 3> {
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn overlay_at(&mut self, with: &Image<&[u8], 4>, x: u32, y: u32) -> &mut Self {
|
unsafe fn overlay_at(&mut self, with: &Image<&[u8], 4>, x: u32, y: u32) -> &mut Self {
|
||||||
|
@ -113,6 +144,15 @@ impl OverlayAt<Image<&[u8], 4>> for Image<&mut [u8], 3> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ClonerOverlayAt<4, 3> for ImageCloner<'_, 3> {
|
||||||
|
unsafe fn overlay_at(&self, with: &Image<&[u8], 4>, x: u32, y: u32) -> Image<Vec<u8>, 3> {
|
||||||
|
let mut new = self.dup();
|
||||||
|
// SAFETY: same
|
||||||
|
unsafe { new.as_mut().overlay_at(with, x, y) };
|
||||||
|
new
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl OverlayAt<Image<&[u8], 3>> for Image<&mut [u8], 3> {
|
impl OverlayAt<Image<&[u8], 3>> for Image<&mut [u8], 3> {
|
||||||
/// Overlay a RGB image(with) => self at coordinates x, y.
|
/// Overlay a RGB image(with) => self at coordinates x, y.
|
||||||
/// As this is a `RGBxRGB` operation, blending is unnecessary,
|
/// As this is a `RGBxRGB` operation, blending is unnecessary,
|
||||||
|
@ -175,6 +215,16 @@ impl Overlay<Image<&[u8], 4>> for Image<&mut [u8], 3> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ClonerOverlay<4, 3> for ImageCloner<'_, 3> {
|
||||||
|
#[inline]
|
||||||
|
unsafe fn overlay(&self, with: &Image<&[u8], 4>) -> Image<Vec<u8>, 3> {
|
||||||
|
let mut out = self.dup();
|
||||||
|
// SAFETY: same
|
||||||
|
unsafe { out.as_mut().overlay(with) };
|
||||||
|
out
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl OverlayAt<Image<&[u8], 4>> for Image<&mut [u8], 4> {
|
impl OverlayAt<Image<&[u8], 4>> for Image<&mut [u8], 4> {
|
||||||
#[inline]
|
#[inline]
|
||||||
/// Overlay with => self at coordinates x, y, without blending
|
/// Overlay with => self at coordinates x, y, without blending
|
||||||
|
@ -210,3 +260,19 @@ impl OverlayAt<Image<&[u8], 4>> for Image<&mut [u8], 4> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ClonerOverlayAt<4, 4> for ImageCloner<'_, 4> {
|
||||||
|
#[inline]
|
||||||
|
/// Overlay with => self at coordinates x, y, without blending, returning a new Image
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// - UB if x, y is out of bounds
|
||||||
|
/// - UB if x + with.width() > [`u32::MAX`]
|
||||||
|
/// - UB if y + with.height() > [`u32::MAX`]
|
||||||
|
unsafe fn overlay_at(&self, with: &Image<&[u8], 4>, x: u32, y: u32) -> Image<Vec<u8>, 4> {
|
||||||
|
let mut out = self.dup();
|
||||||
|
// SAFETY: same
|
||||||
|
unsafe { out.as_mut().overlay_at(with, x, y) };
|
||||||
|
out
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue