fast image operations
Diffstat (limited to 'src/overlay.rs')
| -rw-r--r-- | src/overlay.rs | 561 |
1 files changed, 471 insertions, 90 deletions
diff --git a/src/overlay.rs b/src/overlay.rs index 3dbc45e..1823899 100644 --- a/src/overlay.rs +++ b/src/overlay.rs @@ -1,8 +1,10 @@ //! Handles image overlay -use super::{assert_unchecked, really_unsafe_index, Image}; -use std::simd::SimdInt; -use std::simd::SimdPartialOrd; -use std::simd::{simd_swizzle, Simd}; +// TODO Y/YA +use crate::{DynImage, cloner::ImageCloner, uninit}; + +use super::{Image, assert_unchecked}; +use crate::pixels::Blend; +use std::{mem::transmute, simd::prelude::*}; /// Trait for layering a image ontop of another, with a offset to the second image. pub trait OverlayAt<W> { @@ -12,16 +14,69 @@ 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; } + +/// Useful for debugging, sometimes. +#[doc(hidden)] +pub trait OverlayAtClipping<W> { + fn clipping_overlay_at(&mut self, with: &W, x: u32, y: u32) -> &mut Self; +} + +/// Sealant module +mod sealed { + /// Seals the cloner traits + pub trait Sealed {} +} +use sealed::Sealed; +impl<const N: usize> Sealed for ImageCloner<'_, N> {} + +/// [`OverlayAt`] but owned +pub trait ClonerOverlayAt<const W: usize, const C: usize>: Sealed { + /// Overlay with => self at coordinates x, y, without blending, and returning a new image. + /// # Safety + /// + /// UB if x, y is out of bounds + #[must_use = "function does not modify the original image"] + 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> { /// Overlay with => self (does not blend) + /// /// # Safety /// /// UB if a.width != b.width || a.height != b.height unsafe fn overlay(&mut self, with: &W) -> &mut Self; } +/// This blends the images together, like [`imageops::overlay`](https://docs.rs/image/latest/image/imageops/fn.overlay.html). +pub trait BlendingOverlay<W> { + /// Overlay with => self, blending. You probably do not need this, unless your images make much usage of alpha. + /// If you only have 2 alpha states, `0` | `255` (transparent | opaque), please use [`Overlay`], as it is much faster. + /// # Safety + /// + /// UB if a.width != b.width || a.height != b.height + unsafe fn overlay_blended(&mut self, with: &W) -> &mut Self; +} +/// Blending overlay at. +pub trait BlendingOverlayAt<W> { + /// See [BlendingOverlay::overlay_blended]. + /// # Safety + /// + /// UB if x, y is out of bounds + unsafe fn overlay_blended_at(&mut self, with: &W, x: u32, y: u32) -> &mut Self; +} + +/// [`Overlay`] but owned +pub trait ClonerOverlay<const W: usize, const C: usize>: Sealed { + /// 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. /// @@ -30,57 +85,78 @@ pub trait Overlay<W> { /// # Safety /// - UB if rgb.len() % 3 != 0 /// - UB if rgba.len() % 4 != 0 -unsafe fn blit(rgb: &mut [u8], rgba: &[u8]) { - let mut srci = 0; - let mut dsti = 0; - while dsti + 16 <= rgb.len() { - // SAFETY: i think it ok - let old: Simd<u8, 16> = Simd::from_slice(unsafe { rgb.get_unchecked(dsti..dsti + 16) }); - // SAFETY: definetly ok - let new: Simd<u8, 16> = Simd::from_slice(unsafe { rgba.get_unchecked(srci..srci + 16) }); - - let threshold = new.simd_ge(Simd::splat(128)).to_int().cast::<u8>(); +unsafe fn blit(mut rgb: &mut [u8], mut rgba: &[u8]) { + while rgb.len() >= 16 { + let dst = rgb.first_chunk_mut::<16>().unwrap(); + let src = rgba.first_chunk::<16>().unwrap(); + let old = Simd::from_slice(dst); + let new: u8x16 = Simd::from_slice(src); + + let threshold = new.simd_ge(Simd::splat(128)).to_simd().cast::<u8>(); let mut mask = simd_swizzle!( threshold, + // [r, g, b, a (3)] [r, g, b, a(7)] [3, 3, 3, 7, 7, 7, 11, 11, 11, 15, 15, 15, 0, 0, 0, 0] ); mask &= Simd::from_array([ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, ]); - + // [r(0), g, b] <skip a> [r(4), g, b] let new_rgb = simd_swizzle!(new, [0, 1, 2, 4, 5, 6, 8, 9, 10, 12, 13, 14, 0, 0, 0, 0]); let blended = (new_rgb & mask) | (old & !mask); - // SAFETY: 4 * 4 == 16, so in bounds - blended.copy_to_slice(unsafe { rgb.get_unchecked_mut(dsti..dsti + 16) }); - - srci += 16; - dsti += 12; + blended.copy_to_slice(dst); + rgb = &mut rgb[12..]; + rgba = &rgba[16..]; } - - while dsti + 3 <= rgb.len() { - // SAFETY: caller gurantees slice is big enough - if unsafe { *rgba.get_unchecked(srci + 3) } >= 128 { - // SAFETY: slice is big enough! - let src = unsafe { rgba.get_unchecked(srci..=srci + 2) }; - // SAFETY: i hear it bound - let end = unsafe { rgb.get_unchecked_mut(dsti..=dsti + 2) }; - end.copy_from_slice(src); + while rgb.len() >= 3 { + // SAFETY: guaranteed + if unsafe { *rgba.get_unchecked(3) } >= 128 { + rgb[..3].copy_from_slice(&rgba[..3]); } + rgba = &rgba[4..]; + rgb = &mut rgb[3..]; + } +} - srci += 4; - dsti += 3; +#[doc(hidden)] +#[unsafe(no_mangle)] +pub fn copy_rgb_bgr_(i: &[[u8; 3]], o: &mut [[u8; 4]]) { + unsafe { assert_unchecked(i.len() == o.len()) }; + let mut i = i.as_flattened(); + let mut o = o.as_flattened_mut(); + while i.len() >= 32 { + let dst = unsafe { o.first_chunk_mut::<32>().unwrap_unchecked() }; // 8 pixels (32) + let src = u8x32::from_array(*i.first_chunk().unwrap()); // 8 pixels (24) + let src = simd_swizzle!( + src, + // range::<8>().map(_ * 3).map(|x| [x + 2, x + 1, x, 31]).flatten() + [ + 2, 1, 0, 31, 5, 4, 3, 31, 8, 7, 6, 31, 11, 10, 9, 31, 14, 13, 12, 31, 17, 16, 15, + 31, 20, 19, 18, 31, 23, 22, 21, 31, + ], + ); + src.copy_to_slice(dst); + i = &i[24..]; + o = &mut o[32..]; + } + while let Some(&[r, g, b]) = i.first_chunk() { + unsafe { *o.first_chunk_mut::<4>().unwrap_unchecked() = [b, g, r, 0] }; + o = &mut o[4..]; + i = &i[3..]; } } -impl Overlay<Image<&[u8], 4>> for Image<&mut [u8], 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<&[u8], 4>) -> &mut Self { + #[cfg_attr(debug_assertions, track_caller)] + 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, 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); } } @@ -88,11 +164,78 @@ impl Overlay<Image<&[u8], 4>> for Image<&mut [u8], 4> { } } -impl OverlayAt<Image<&[u8], 4>> for Image<&mut [u8], 3> { +impl<const A: usize, const B: usize, T: AsMut<[u8]> + AsRef<[u8]>, U: AsRef<[u8]>> + BlendingOverlay<Image<U, B>> for Image<T, A> +where + [u8; A]: Blend<B>, +{ + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + unsafe fn overlay_blended(&mut self, with: &Image<U, B>) -> &mut Self { + debug_assert!(self.width() == with.width()); + debug_assert!(self.height() == with.height()); + for (other_pixels, own_pixels) in with.chunked().zip(self.chunked_mut()) { + own_pixels.blend(*other_pixels); + } + self + } +} + +impl<const A: usize, const B: usize, T: AsMut<[u8]> + AsRef<[u8]>, U: AsRef<[u8]>> + BlendingOverlayAt<Image<U, B>> for Image<T, A> +where + [u8; A]: Blend<B>, +{ + #[inline] + unsafe fn overlay_blended_at(&mut self, with: &Image<U, B>, x: u32, y: u32) -> &mut Self { + for j in 0..with.height() { + for i in 0..with.width() { + // SAFETY: i, j is in bounds. + let their_px = unsafe { with.pixel(i, j) }; + let our_px = unsafe { self.pixel_mut(i + x, j + y) }; + our_px.blend(*their_px); + } + } + self + } +} + +impl<T: AsMut<[u8]> + AsRef<[u8]>> Image<T, 3> { + #[doc(hidden)] + #[cfg_attr(debug_assertions, track_caller)] + pub unsafe fn blend_alpha_and_color_at( + &mut self, + with: &Image<&[u8], 1>, + color: [u8; 3], + x: u32, + y: u32, + ) { + for j in 0..with.height() { + for i in 0..with.width() { + let &[their_alpha] = unsafe { with.pixel(i, j) }; + let our_pixel = unsafe { self.pixel_mut(i + x, j + y) }; + crate::pixels::blending::blend_alpha_and_color(their_alpha, color, our_pixel); + } + } + } +} + +impl ClonerOverlay<4, 4> for ImageCloner<'_, 4> { #[inline] - unsafe fn overlay_at(&mut self, with: &Image<&[u8], 4>, x: u32, y: u32) -> &mut Self { + 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<T: AsMut<[u8]> + AsRef<[u8]>, U: AsRef<[u8]>> OverlayAt<Image<U, 4>> for Image<T, 3> { + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + unsafe fn overlay_at(&mut self, with: &Image<U, 4>, x: u32, y: u32) -> &mut Self { // 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()); for j in 0..with.height() { let i_x = j as usize * with.width() as usize * 4 @@ -103,9 +246,9 @@ impl OverlayAt<Image<&[u8], 4>> for Image<&mut [u8], 3> { + 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) } } @@ -113,7 +256,190 @@ impl OverlayAt<Image<&[u8], 4>> for Image<&mut [u8], 3> { } } -impl OverlayAt<Image<&[u8], 3>> for Image<&mut [u8], 3> { +impl<U: AsRef<[u8]>> OverlayAt<Image<U, 4>> for uninit::Image<u8, 3> { + #[cfg_attr(debug_assertions, track_caller)] + unsafe fn overlay_at(&mut self, with: &Image<U, 4>, x: u32, y: u32) -> &mut Self { + // SAFETY: caller upholds this + unsafe { assert_unchecked(x + with.width() <= self.width()) }; + debug_assert!(y + with.height() <= self.height()); + for j in 0..with.height() { + let i_x = j as usize * with.width() as usize * 4 + ..(j as usize + 1) * with.width() as usize * 4; + let o_x = ((j as usize + y as usize) * self.width() as usize + x as usize) * 3 + ..((j as usize + y as usize) * self.width() as usize + + x as usize + + with.width() as usize) + * 3; + // SAFETY: index is in bounds + let rgb = unsafe { transmute(self.buf().get_unchecked_mut(o_x)) }; + // SAFETY: bounds are outside index + let rgba = unsafe { with.buffer.as_ref().get_unchecked(i_x) }; + // SAFETY: arguments are 🟢 + unsafe { blit(rgb, rgba) } + } + self + } +} + +impl ClonerOverlayAt<4, 3> for ImageCloner<'_, 3> { + #[inline] + 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<U: AsRef<[u8]>> OverlayAt<Image<U, 3>> for uninit::Image<u8, 3> { + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + unsafe fn overlay_at(&mut self, with: &Image<U, 3>, x: u32, y: u32) -> &mut Self { + for j in 0..(with.width() as usize) { + let i_x = j * (with.width() as usize) * 3..(j + 1) * (with.width() as usize) * 3; + let o_x = ((j + y as usize) * self.width() as usize + x as usize) * 3 + ..((j + y as usize) * self.width() as usize + x as usize + (with.width() as usize)) + * 3; + // <= because ".." range + // debug_assert!(o_x.end <= self.buffer().as_ref().len()); + debug_assert!(i_x.end <= with.buffer().as_ref().len()); + // SAFETY: we are in ⬜! + let b = unsafe { with.buffer.as_ref().get_unchecked(i_x) }; + // SAFETY: should work + unsafe { self.write(b, o_x) }; + } + self + } +} + +impl<T: AsMut<[u8]> + AsRef<[u8]>, U: AsRef<[u8]>> OverlayAt<Image<U, 1>> for Image<T, 3> { + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + /// this impl doesnt make much sense without a color. + unsafe fn overlay_at(&mut self, with: &Image<U, 1>, x: u32, y: u32) -> &mut Self { + for j in 0..with.height() { + for i in 0..with.width() { + // SAFETY: i, j is in bounds. + let &[their_px] = unsafe { with.pixel(i, j) }; + let our_px = unsafe { self.pixel_mut(i + x, j + y) }; + our_px.copy_from_slice(&[their_px; 3]); + } + } + self + } +} + +impl<T: AsMut<[u8]> + AsRef<[u8]>, U: AsRef<[u8]>> OverlayAt<Image<U, 2>> for Image<T, 3> { + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + /// this impl doesnt make much sense without a color. + unsafe fn overlay_at(&mut self, with: &Image<U, 2>, x: u32, y: u32) -> &mut Self { + for j in 0..with.height() { + for i in 0..with.width() { + // SAFETY: i, j is in bounds. + let &[their_px, a] = unsafe { with.pixel(i, j) }; + if a >= 128 { + let our_px = unsafe { self.pixel_mut(i + x, j + y) }; + our_px.copy_from_slice(&[their_px; 3]); + } + } + } + self + } +} + +impl<T: AsMut<[u8]> + AsRef<[u8]>, U: AsRef<[u8]>> OverlayAt<Image<U, 1>> for Image<T, 4> { + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + /// this impl doesnt make much sense without a color. + unsafe fn overlay_at(&mut self, with: &Image<U, 1>, x: u32, y: u32) -> &mut Self { + for j in 0..with.height() { + for i in 0..with.width() { + // SAFETY: i, j is in bounds. + let &[their_px] = unsafe { with.pixel(i, j) }; + let our_px = unsafe { self.pixel_mut(i + x, j + y) }; + our_px.copy_from_slice(&[their_px; 4]); + } + } + self + } +} + +impl<T: AsMut<[u8]> + AsRef<[u8]>, U: AsRef<[u8]>> OverlayAt<Image<U, 2>> for Image<T, 4> { + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + /// this impl doesnt make much sense without a color. + unsafe fn overlay_at(&mut self, with: &Image<U, 2>, x: u32, y: u32) -> &mut Self { + for j in 0..with.height() { + for i in 0..with.width() { + // SAFETY: i, j is in bounds. + let &[their_px, a] = unsafe { with.pixel(i, j) }; + if a >= 128 { + let our_px = unsafe { self.pixel_mut(i + x, j + y) }; + our_px.copy_from_slice(&[their_px; 4]); + } + } + } + self + } +} + +fn coerce<T, U, F: FnMut(T, U)>(f: F) -> F { + f +} +macro_rules! imp { + ($N1:literal => $N2:literal, $conv:expr) => { + impl<T: AsMut<[u8]> + AsRef<[u8]>, U: AsRef<[u8]>> OverlayAtClipping<Image<U, $N1>> + for Image<T, $N2> + { + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + fn clipping_overlay_at(&mut self, with: &Image<U, $N1>, x: u32, y: u32) -> &mut Self { + for j in 0..with.height() { + for i in 0..with.width() { + // SAFETY: i, j is in bounds. + if let Some(their_px) = with.get_pixel(i, j) + && let Some(our_px) = self.get_pixel_mut(i + x, j + y) + { + (coerce::<&mut [u8; $N2], &[u8; $N1], _>($conv))(our_px, their_px); + } + } + } + self + } + } + }; +} +imp!(1 => 1, |a, b| a.copy_from_slice(b)); +imp!(2 => 2, |a, b| a.copy_from_slice(b)); +imp!(3 => 3, |a, b| a.copy_from_slice(b)); +imp!(4 => 4, |a, b| a.copy_from_slice(b)); +imp!(4 => 3, |a, b| if b[3] > 128 { a.copy_from_slice(&b[..3]) }); +imp!(1 => 4, |a, [b]| a.copy_from_slice(&[*b;4])); +imp!(1 => 3, |a, [b]| a.copy_from_slice(&[*b;3])); +imp!(2 => 3, |a, [b, y]| if *y > 128 { a.copy_from_slice(&[*b;3]) }); +imp!(2 => 4, |a, [b, y]| if *y > 128 { a.copy_from_slice(&[*b;3].join(255)); }); +use atools::Join; +imp!(3 => 4, |a, b| a.copy_from_slice(&(*b).join(255))); + +impl<T: AsMut<[u8]> + AsRef<[u8]>, U: AsRef<[u8]>> OverlayAt<Image<U, 3>> for Image<T, 4> { + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + /// its not a optimized impl + unsafe fn overlay_at(&mut self, with: &Image<U, 3>, x: u32, y: u32) -> &mut Self { + for j in 0..with.height() { + for i in 0..with.width() { + // SAFETY: i, j is in bounds. + let their_px = unsafe { with.pixel(i, j) }; + let our_px = unsafe { self.pixel_mut(i + x, j + y) }; + our_px.copy_from_slice(their_px); + } + } + self + } +} + +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. @@ -122,49 +448,60 @@ impl OverlayAt<Image<&[u8], 3>> for Image<&mut [u8], 3> { /// /// UB if x, y is out of bounds #[inline] - unsafe fn overlay_at(&mut self, with: &Image<&[u8], 3>, x: u32, y: u32) -> &mut Self { - /// helper macro for defining rgb=>rgb overlays. allows unrolling - macro_rules! o3x3 { - ($n:expr) => {{ - for j in 0..($n as usize) { - let i_x = j * ($n as usize) * 3..(j + 1) * ($n as usize) * 3; - let o_x = ((j + y as usize) * self.width() as usize + x as usize) * 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()); - // SAFETY: bounds are ✅ - let a = unsafe { self.buffer.get_unchecked_mut(o_x) }; - // SAFETY: we are in ⬜! - let b = unsafe { with.buffer.get_unchecked(i_x) }; - a.copy_from_slice(b); - } - }}; - } - // let it unroll - match with.width() { - 8 => o3x3!(8), - 16 => o3x3!(16), // this branch makes 8x8 0.16 times slower; but 16x16 0.2 times faster. - _ => o3x3!(with.width()), + #[cfg_attr(debug_assertions, track_caller)] + unsafe fn overlay_at(&mut self, with: &Image<U, 3>, x: u32, y: u32) -> &mut Self { + for j in 0..with.height() as usize { + let i_x = // copy entire rows at a time + j * (with.width() as usize) * 3 + ..(j + 1) * (with.width() as usize) * 3; + let o_x = // _ + ((j + y as usize) * self.width() as usize + x as usize) * 3 + ..((j + y as usize) * self.width() as usize + x as usize + with.width() as usize) * 3; + // <= because ".." range + 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.as_mut().get_unchecked_mut(o_x) }; + // SAFETY: we are in ⬜! + let b = unsafe { with.buffer.as_ref().get_unchecked(i_x) }; + a.copy_from_slice(b); } self } } -impl Overlay<Image<&[u8], 4>> for Image<&mut [u8], 3> { +impl ClonerOverlayAt<3, 3> for ImageCloner<'_, 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. + /// + /// # Safety + /// + /// UB if x, y is out of bounds + #[inline] + unsafe fn overlay_at(&self, with: &Image<&[u8], 3>, x: u32, y: u32) -> Image<Vec<u8>, 3> { + let mut out = self.dup(); + // SAFETY: same + unsafe { out.as_mut().overlay_at(with, x, y) }; + out + } +} + +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<&[u8], 4>) -> &mut Self { + #[cfg_attr(debug_assertions, track_caller)] + 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, ) }; @@ -175,33 +512,26 @@ impl Overlay<Image<&[u8], 4>> for Image<&mut [u8], 3> { } } -impl OverlayAt<Image<&[u8], 4>> for Image<&mut [u8], 4> { +impl ClonerOverlay<4, 3> for ImageCloner<'_, 3> { #[inline] - /// Overlay with => self at coordinates x, y, without blending - /// - /// # 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(&mut self, with: &Image<&[u8], 4>, x: u32, y: u32) -> &mut Self { + 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<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() { for i in 0..with.width() { // SAFETY: i, j is in bounds. - let index = unsafe { really_unsafe_index(i, j, with.width()) }; - // SAFETY: using .pixel() results in horrible asm (+5k ns/iter) - let their_px = unsafe { with.buffer.get_unchecked(index * 4..index * 4 + 4) }; - // SAFETY: must be sized right - if unsafe { *their_px.get_unchecked(3) } >= 128 { - // SAFETY: - // they said it cant go over. - // i dont know why, but this has performance importance™ - let x = unsafe { i.unchecked_add(x) }; - // SAFETY: caller gurantees this cannot overflow. - let y = unsafe { j.unchecked_add(y) }; - // SAFETY: compute the offset index. - let index = unsafe { really_unsafe_index(x, y, self.width()) }; + let their_px = unsafe { with.pixel(i, j) }; + if their_px[3] >= 128 { // SAFETY: if everything else goes well, this is fine - let our_px = unsafe { self.buffer.get_unchecked_mut(index * 4..index * 4 + 4) }; + let our_px = unsafe { self.pixel_mut(i + x, j + y) }; our_px.copy_from_slice(their_px); } } @@ -210,3 +540,54 @@ impl OverlayAt<Image<&[u8], 4>> for Image<&mut [u8], 4> { self } } + +impl ClonerOverlayAt<4, 4> for ImageCloner<'_, 4> { + #[inline] + 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 + } +} + +impl<T: AsMut<[u8]> + AsRef<[u8]>, U: AsRef<[u8]>> OverlayAt<DynImage<U>> for Image<T, 3> { + unsafe fn overlay_at(&mut self, with: &DynImage<U>, x: u32, y: u32) -> &mut Self { + crate::r#dyn::e!(with, |with| unsafe { + self.overlay_at(with, x, y); + }); + self + } +} + +impl<T: AsMut<[u8]> + AsRef<[u8]>, U: AsRef<[u8]>> OverlayAt<DynImage<U>> for Image<T, 4> { + unsafe fn overlay_at(&mut self, with: &DynImage<U>, x: u32, y: u32) -> &mut Self { + crate::r#dyn::e!(with, |with| unsafe { + self.overlay_at(with, x, y); + }); + self + } +} +impl<T: AsRef<[u8]>, U: AsRef<[u8]> + AsMut<[u8]>> OverlayAtClipping<DynImage<T>> for Image<U, 3> { + fn clipping_overlay_at(&mut self, with: &DynImage<T>, x: u32, y: u32) -> &mut Self { + crate::r#dyn::e!(with, |with| { self.clipping_overlay_at(with, x, y) }); + self + } +} +impl<T: AsRef<[u8]>, U: AsRef<[u8]> + AsMut<[u8]>> OverlayAtClipping<DynImage<T>> for Image<U, 4> { + fn clipping_overlay_at(&mut self, with: &DynImage<T>, x: u32, y: u32) -> &mut Self { + crate::r#dyn::e!(with, |with| { self.clipping_overlay_at(with, x, y) }); + self + } +} + +impl<U: AsRef<[u8]>> OverlayAt<DynImage<U>> for uninit::Image<u8, 3> { + unsafe fn overlay_at(&mut self, with: &DynImage<U>, x: u32, y: u32) -> &mut Self { + match with { + DynImage::Rgb(with) => unsafe { self.overlay_at(with, x, y) }, + DynImage::Rgba(with) => unsafe { self.overlay_at(with, x, y) }, + _ => unimplemented!(), + }; + self + } +} |