mirror of
https://github.com/bend-n/fimg.git
synced 2024-12-22 02:28:19 -06:00
add a ImageCloner
This commit is contained in:
parent
1b5b06c36f
commit
829cfa680e
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "fimg"
|
||||
version = "0.4.3"
|
||||
version = "0.4.4"
|
||||
authors = ["bend-n <bend.n@outlook.com>"]
|
||||
license = "MIT"
|
||||
edition = "2021"
|
||||
|
|
144
src/affine.rs
144
src/affine.rs
|
@ -1,19 +1,58 @@
|
|||
//! Manages the affine image transformations.
|
||||
use crate::Image;
|
||||
use crate::{cloner::ImageCloner, Image};
|
||||
|
||||
impl<const CHANNELS: usize> Image<Vec<u8>, CHANNELS> {
|
||||
/// Flip a image horizontally.
|
||||
/// Flip an image horizontally.
|
||||
pub fn flip_h(&mut self) {
|
||||
self.as_mut().flip_h();
|
||||
}
|
||||
/// Flip a image vertically.
|
||||
/// Flip an image vertically.
|
||||
pub fn flip_v(&mut self) {
|
||||
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> {
|
||||
/// Flip a image vertically.
|
||||
/// Flip an image vertically.
|
||||
pub fn flip_v(&mut self) {
|
||||
for y in 0..self.height() / 2 {
|
||||
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) {
|
||||
for y in 0..self.height() {
|
||||
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> {
|
||||
/// Rotate a image 180 degrees clockwise.
|
||||
/// Rotate an image 180 degrees clockwise.
|
||||
pub fn rot_180(&mut self) {
|
||||
self.as_mut().rot_180();
|
||||
}
|
||||
|
||||
/// Rotate a image 90 degrees clockwise.
|
||||
/// Rotate an image 90 degrees clockwise.
|
||||
/// # Safety
|
||||
///
|
||||
/// 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() }
|
||||
}
|
||||
|
||||
/// Rotate a image 270 degrees clockwise, or 90 degrees anti clockwise.
|
||||
/// Rotate an image 270 degrees clockwise, or 90 degrees anti clockwise.
|
||||
/// # Safety
|
||||
///
|
||||
/// UB if the image is not square
|
||||
|
@ -73,42 +112,60 @@ impl<const CHANNELS: usize> Image<Vec<u8>, CHANNELS> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<const CHANNELS: usize> Image<&mut [u8], CHANNELS> {
|
||||
/// Rotate a image 180 degrees clockwise.
|
||||
pub fn rot_180(&mut self) {
|
||||
for y in 0..self.height() / 2 {
|
||||
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 {
|
||||
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);
|
||||
}
|
||||
}
|
||||
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 a image 90 degrees clockwise.
|
||||
/// 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> {
|
||||
/// Rotate an image 180 degrees clockwise.
|
||||
pub fn rot_180(&mut self) {
|
||||
self.flatten_mut().reverse();
|
||||
}
|
||||
|
||||
/// Rotate an image 90 degrees clockwise.
|
||||
/// # Safety
|
||||
///
|
||||
/// UB if the image is not square
|
||||
|
@ -121,7 +178,7 @@ impl<const CHANNELS: usize> Image<&mut [u8], CHANNELS> {
|
|||
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
|
||||
///
|
||||
/// 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>) {
|
||||
debug_assert_eq!(img.width(), img.height());
|
||||
let size = img.width() as usize;
|
||||
// SAFETY: no half pixels
|
||||
let b = unsafe { img.buffer.as_chunks_unchecked_mut::<CHANNELS>() };
|
||||
let b = img.flatten_mut();
|
||||
for i in 0..size {
|
||||
for j in i..size {
|
||||
// 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,
|
||||
stmt_expr_attributes,
|
||||
generic_const_exprs,
|
||||
vec_into_raw_parts,
|
||||
slice_as_chunks,
|
||||
unchecked_math,
|
||||
portable_simd,
|
||||
|
@ -28,9 +29,11 @@ use std::{num::NonZeroU32, slice::SliceIndex};
|
|||
|
||||
mod affine;
|
||||
pub mod builder;
|
||||
pub mod cloner;
|
||||
mod drawing;
|
||||
mod overlay;
|
||||
pub mod scale;
|
||||
use cloner::ImageCloner;
|
||||
pub use overlay::{Overlay, OverlayAt};
|
||||
|
||||
/// 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.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct Image<T, const CHANNELS: usize> {
|
||||
/// column order 2d slice/vec
|
||||
buffer: T,
|
||||
|
@ -91,6 +94,37 @@ pub struct Image<T, const CHANNELS: usize> {
|
|||
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> {
|
||||
#[inline]
|
||||
/// 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> {
|
||||
#[inline]
|
||||
#[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) }
|
||||
}
|
||||
|
||||
/// 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]
|
||||
/// 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
|
||||
unsafe { assert_unchecked!(self.buffer.len() > CHANNELS) };
|
||||
// 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>()
|
||||
}
|
||||
|
||||
/// 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]
|
||||
/// Flatten the chunks of this image into a mutable slice of slices.
|
||||
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> {
|
||||
/// 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
|
||||
pub fn copy(&mut self) -> Image<&mut [u8], CHANNELS> {
|
||||
#[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> {
|
||||
/// Allocates a new image
|
||||
///
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
//! Handles image overlay
|
||||
use crate::cloner::ImageCloner;
|
||||
|
||||
use super::{assert_unchecked, really_unsafe_index, Image};
|
||||
use std::simd::SimdInt;
|
||||
use std::simd::SimdPartialOrd;
|
||||
|
@ -12,6 +14,16 @@ pub trait OverlayAt<W> {
|
|||
/// UB if x, y is out of bounds
|
||||
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.
|
||||
/// Think `magick a b -layers flatten a`
|
||||
pub trait Overlay<W> {
|
||||
|
@ -22,6 +34,15 @@ pub trait Overlay<W> {
|
|||
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]
|
||||
/// 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> {
|
||||
#[inline]
|
||||
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> {
|
||||
/// Overlay a RGB image(with) => self at coordinates x, y.
|
||||
/// 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> {
|
||||
#[inline]
|
||||
/// Overlay with => self at coordinates x, y, without blending
|
||||
|
@ -210,3 +260,19 @@ impl OverlayAt<Image<&[u8], 4>> for Image<&mut [u8], 4> {
|
|||
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