fast image operations
Diffstat (limited to 'src/overlay.rs')
-rw-r--r--src/overlay.rs579
1 files changed, 500 insertions, 79 deletions
diff --git a/src/overlay.rs b/src/overlay.rs
index d1211a9..1823899 100644
--- a/src/overlay.rs
+++ b/src/overlay.rs
@@ -1,8 +1,12 @@
-use super::{assert_unchecked, really_unsafe_index, Image};
-use std::simd::SimdInt;
-use std::simd::SimdPartialOrd;
-use std::simd::{simd_swizzle, Simd};
+//! Handles image overlay
+// 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> {
/// Overlay with => self at coordinates x, y, without blending
/// # Safety
@@ -11,82 +15,253 @@ pub trait OverlayAt<W> {
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]
-unsafe fn blit(rgb: &mut [u8], rgba: &[u8]) {
- const LAST4: Simd<u8, 16> = Simd::from_array([
- 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0,
- ]);
-
- let mut srci = 0;
- let mut dsti = 0;
- while dsti + 16 <= rgb.len() {
- let old: Simd<u8, 16> = Simd::from_slice(unsafe { rgb.get_unchecked(dsti..dsti + 16) });
- 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>();
+/// SIMD accelerated rgba => rgb overlay.
+///
+/// See [blit](https://en.wikipedia.org/wiki/Bit_blit)
+///
+/// # Safety
+/// - UB if rgb.len() % 3 != 0
+/// - UB if rgba.len() % 4 != 0
+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 &= LAST4;
-
+ 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);
- 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() {
- if unsafe { *rgba.get_unchecked(srci + 3) } >= 128 {
- let src = unsafe { rgba.get_unchecked(srci..srci + 3) };
- let end = unsafe { rgb.get_unchecked_mut(dsti..dsti + 3) };
- unsafe { std::ptr::copy_nonoverlapping(src.as_ptr(), end.as_mut_ptr(), 3) };
+ 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 {
- let idx_begin = unsafe { i.unchecked_mul(4) };
- let idx_end = unsafe { idx_begin.unchecked_add(4) };
- let own_pixels = unsafe { self.buffer.get_unchecked_mut(idx_begin..idx_end) };
- unsafe {
- std::ptr::copy_nonoverlapping(
- other_pixels.as_ptr(),
- own_pixels.as_mut_ptr(),
- 4,
- );
- };
+ // SAFETY: outside are bounds of index from slice
+ let own_pixels =
+ unsafe { self.buffer.as_mut().get_unchecked_mut(i * 4..i * 4 + 4) };
+ own_pixels.copy_from_slice(other_pixels);
}
}
self
}
}
-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]
- unsafe fn overlay_at(&mut self, with: &Image<&[u8], 4>, x: u32, y: u32) -> &mut Self {
- // SAFETY: caller upholds these
- unsafe { assert_unchecked!(x + with.width() <= self.width()) };
- unsafe { assert_unchecked!(y + with.height() <= self.height()) };
+ #[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(&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()) };
+ 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 { self.buffer.as_mut().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<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;
@@ -95,73 +270,268 @@ impl OverlayAt<Image<&[u8], 4>> for Image<&mut [u8], 3> {
+ x as usize
+ with.width() as usize)
* 3;
- let rgb = unsafe { self.buffer.get_unchecked_mut(o_x) };
- let rgba = unsafe { with.buffer.get_unchecked(i_x) };
+ // 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 OverlayAt<Image<&[u8], 3>> for Image<&mut [u8], 3> {
+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]
- unsafe fn overlay_at(&mut self, with: &Image<&[u8], 3>, x: u32, y: u32) -> &mut Self {
- 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;
- let a = unsafe { self.buffer.get_unchecked_mut(o_x) };
- let b = unsafe { with.buffer.get_unchecked(i_x) };
- a.copy_from_slice(b);
+ #[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);
+ }
}
- // 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()),
+ 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.
+ ///
+ /// # Safety
+ ///
+ /// UB if x, y is out of bounds
+ #[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.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,
)
};
+ // SAFETY: we have the rgb and rgba arguments right
unsafe { blit(rgb, chunk) };
}
self
}
}
-impl OverlayAt<Image<&[u8], 4>> for Image<&mut [u8], 4> {
+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<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<&[u8], 4>, x: u32, y: u32) -> &mut Self {
+ 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() {
- let index = unsafe { really_unsafe_index(i, j, with.width()) };
- let their_px = unsafe { with.buffer.get_unchecked(index * 4..index * 4 + 4) };
- if unsafe { *their_px.get_unchecked(3) } >= 128 {
- let x = unsafe { i.unchecked_add(x) };
- let y = unsafe { j.unchecked_add(y) };
- let index = unsafe { really_unsafe_index(x, y, self.width()) };
- let our_px = unsafe { self.buffer.get_unchecked_mut(index * 4..index * 4 + 4) };
+ // SAFETY: i, j is in bounds.
+ 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.pixel_mut(i + x, j + y) };
our_px.copy_from_slice(their_px);
}
}
@@ -170,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
+ }
+}