//! 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 { /// Overlay with => self at coordinates x, y, without blending /// # Safety /// /// 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 { 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 Sealed for ImageCloner<'_, N> {} /// [`OverlayAt`] but owned pub trait ClonerOverlayAt: 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, C>; } /// Trait for layering images ontop of each other. /// Think `magick a b -layers flatten a` pub trait Overlay { /// 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 { /// 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 { /// 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: 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, C>; } #[inline] /// 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::(); 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] [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(dst); rgb = &mut rgb[12..]; rgba = &rgba[16..]; } 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..]; } } #[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 + AsRef<[u8]>, U: AsRef<[u8]>> Overlay> for Image { #[inline] #[cfg_attr(debug_assertions, track_caller)] unsafe fn overlay(&mut self, with: &Image) -> &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.as_mut().get_unchecked_mut(i * 4..i * 4 + 4) }; own_pixels.copy_from_slice(other_pixels); } } self } } impl + AsRef<[u8]>, U: AsRef<[u8]>> BlendingOverlay> for Image where [u8; A]: Blend, { #[inline] #[cfg_attr(debug_assertions, track_caller)] unsafe fn overlay_blended(&mut self, with: &Image) -> &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 + AsRef<[u8]>, U: AsRef<[u8]>> BlendingOverlayAt> for Image where [u8; A]: Blend, { #[inline] unsafe fn overlay_blended_at(&mut self, with: &Image, 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 + AsRef<[u8]>> Image { #[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, 4> { let mut out = self.dup(); // SAFETY: same unsafe { out.as_mut().overlay(with) }; out } } impl + AsRef<[u8]>, U: AsRef<[u8]>> OverlayAt> for Image { #[inline] #[cfg_attr(debug_assertions, track_caller)] unsafe fn overlay_at(&mut self, with: &Image, 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> OverlayAt> for uninit::Image { #[cfg_attr(debug_assertions, track_caller)] unsafe fn overlay_at(&mut self, with: &Image, 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, 3> { let mut new = self.dup(); // SAFETY: same unsafe { new.as_mut().overlay_at(with, x, y) }; new } } impl> OverlayAt> for uninit::Image { #[inline] #[cfg_attr(debug_assertions, track_caller)] unsafe fn overlay_at(&mut self, with: &Image, 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 + AsRef<[u8]>, U: AsRef<[u8]>> OverlayAt> for Image { #[inline] #[cfg_attr(debug_assertions, track_caller)] /// this impl doesnt make much sense without a color. unsafe fn overlay_at(&mut self, with: &Image, 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 + AsRef<[u8]>, U: AsRef<[u8]>> OverlayAt> for Image { #[inline] #[cfg_attr(debug_assertions, track_caller)] /// this impl doesnt make much sense without a color. unsafe fn overlay_at(&mut self, with: &Image, 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 + AsRef<[u8]>, U: AsRef<[u8]>> OverlayAt> for Image { #[inline] #[cfg_attr(debug_assertions, track_caller)] /// this impl doesnt make much sense without a color. unsafe fn overlay_at(&mut self, with: &Image, 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 + AsRef<[u8]>, U: AsRef<[u8]>> OverlayAt> for Image { #[inline] #[cfg_attr(debug_assertions, track_caller)] /// this impl doesnt make much sense without a color. unsafe fn overlay_at(&mut self, with: &Image, 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 } } impl + AsRef<[u8]>, U: AsRef<[u8]>> OverlayAtClipping> for Image { #[inline] #[cfg_attr(debug_assertions, track_caller)] fn clipping_overlay_at(&mut self, with: &Image, 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) { our_px.copy_from_slice(their_px); } } } self } } impl + AsRef<[u8]>, U: AsRef<[u8]>> OverlayAt> for Image { #[inline] #[cfg_attr(debug_assertions, track_caller)] /// its not a optimized impl unsafe fn overlay_at(&mut self, with: &Image, 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 + AsRef<[u8]>, U: AsRef<[u8]>> OverlayAt> for Image { /// 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, 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 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, 3> { let mut out = self.dup(); // SAFETY: same unsafe { out.as_mut().overlay_at(with, x, y) }; out } } impl + AsRef<[u8]>, U: AsRef<[u8]>> Overlay> for Image { #[inline] #[cfg_attr(debug_assertions, track_caller)] unsafe fn overlay(&mut self, with: &Image) -> &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.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 ClonerOverlay<4, 3> for ImageCloner<'_, 3> { #[inline] unsafe fn overlay(&self, with: &Image<&[u8], 4>) -> Image, 3> { let mut out = self.dup(); // SAFETY: same unsafe { out.as_mut().overlay(with) }; out } } impl + AsRef<[u8]>, U: AsRef<[u8]>> OverlayAt> for Image { #[inline] unsafe fn overlay_at(&mut self, with: &Image, 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) }; 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); } } } self } } impl ClonerOverlayAt<4, 4> for ImageCloner<'_, 4> { #[inline] unsafe fn overlay_at(&self, with: &Image<&[u8], 4>, x: u32, y: u32) -> Image, 4> { let mut out = self.dup(); // SAFETY: same unsafe { out.as_mut().overlay_at(with, x, y) }; out } } impl + AsRef<[u8]>, U: AsRef<[u8]>> OverlayAt> for Image { unsafe fn overlay_at(&mut self, with: &DynImage, x: u32, y: u32) -> &mut Self { crate::r#dyn::e!(with, |with| unsafe { self.overlay_at(with, x, y); }); self } } impl + AsRef<[u8]>, U: AsRef<[u8]>> OverlayAt> for Image { unsafe fn overlay_at(&mut self, with: &DynImage, x: u32, y: u32) -> &mut Self { crate::r#dyn::e!(with, |with| unsafe { self.overlay_at(with, x, y); }); self } } impl> OverlayAt> for uninit::Image { unsafe fn overlay_at(&mut self, with: &DynImage, 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 } }