fimg/
overlay.rs

1//! Handles image overlay
2// TODO Y/YA
3use crate::{cloner::ImageCloner, uninit};
4
5use super::{Image, assert_unchecked};
6use crate::pixels::Blend;
7use std::{mem::transmute, simd::prelude::*};
8
9/// Trait for layering a image ontop of another, with a offset to the second image.
10pub trait OverlayAt<W> {
11    /// Overlay with => self at coordinates x, y, without blending
12    /// # Safety
13    ///
14    /// UB if x, y is out of bounds
15    unsafe fn overlay_at(&mut self, with: &W, x: u32, y: u32) -> &mut Self;
16}
17
18/// Sealant module
19mod sealed {
20    /// Seals the cloner traits
21    pub trait Sealed {}
22}
23use sealed::Sealed;
24impl<const N: usize> Sealed for ImageCloner<'_, N> {}
25
26/// [`OverlayAt`] but owned
27pub trait ClonerOverlayAt<const W: usize, const C: usize>: Sealed {
28    /// Overlay with => self at coordinates x, y, without blending, and returning a new image.
29    /// # Safety
30    ///
31    /// UB if x, y is out of bounds
32    #[must_use = "function does not modify the original image"]
33    unsafe fn overlay_at(&self, with: &Image<&[u8], W>, x: u32, y: u32) -> Image<Vec<u8>, C>;
34}
35
36/// Trait for layering images ontop of each other.
37/// Think `magick a b -layers flatten a`
38pub trait Overlay<W> {
39    /// Overlay with => self (does not blend)
40    ///
41    /// # Safety
42    ///
43    /// UB if a.width != b.width || a.height != b.height
44    unsafe fn overlay(&mut self, with: &W) -> &mut Self;
45}
46
47/// This blends the images together, like [`imageops::overlay`](https://docs.rs/image/latest/image/imageops/fn.overlay.html).
48pub trait BlendingOverlay<W> {
49    /// Overlay with => self, blending. You probably do not need this, unless your images make much usage of alpha.
50    /// If you only have 2 alpha states, `0` | `255` (transparent | opaque), please use [`Overlay`], as it is much faster.
51    /// # Safety
52    ///
53    /// UB if a.width != b.width || a.height != b.height
54    unsafe fn overlay_blended(&mut self, with: &W) -> &mut Self;
55}
56/// Blending overlay at.
57pub trait BlendingOverlayAt<W> {
58    /// See [BlendingOverlay::overlay_blended].
59    /// # Safety
60    ///
61    /// UB if x, y is out of bounds
62    unsafe fn overlay_blended_at(&mut self, with: &W, x: u32, y: u32) -> &mut Self;
63}
64
65/// [`Overlay`] but owned
66pub trait ClonerOverlay<const W: usize, const C: usize>: Sealed {
67    /// Overlay with => self (does not blend)
68    /// # Safety
69    ///
70    /// UB if a.width != b.width || a.height != b.height
71    unsafe fn overlay(&self, with: &Image<&[u8], W>) -> Image<Vec<u8>, C>;
72}
73
74#[inline]
75/// SIMD accelerated rgba => rgb overlay.
76///
77/// See [blit](https://en.wikipedia.org/wiki/Bit_blit)
78///
79/// # Safety
80/// - UB if rgb.len() % 3 != 0
81/// - UB if rgba.len() % 4 != 0
82unsafe fn blit(mut rgb: &mut [u8], mut rgba: &[u8]) {
83    while rgb.len() >= 16 {
84        let dst = rgb.first_chunk_mut::<16>().unwrap();
85        let src = rgba.first_chunk::<16>().unwrap();
86        let old = Simd::from_slice(dst);
87        let new: u8x16 = Simd::from_slice(src);
88
89        let threshold = new.simd_ge(Simd::splat(128)).to_int().cast::<u8>();
90        let mut mask = simd_swizzle!(
91            threshold,
92            // [r, g, b, a (3)] [r, g, b, a(7)]
93            [3, 3, 3, 7, 7, 7, 11, 11, 11, 15, 15, 15, 0, 0, 0, 0]
94        );
95        mask &= Simd::from_array([
96            255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0,
97        ]);
98        // [r(0), g, b] <skip a> [r(4), g, b]
99        let new_rgb = simd_swizzle!(new, [0, 1, 2, 4, 5, 6, 8, 9, 10, 12, 13, 14, 0, 0, 0, 0]);
100        let blended = (new_rgb & mask) | (old & !mask);
101        blended.copy_to_slice(dst);
102        rgb = &mut rgb[12..];
103        rgba = &rgba[16..];
104    }
105    while rgb.len() >= 3 {
106        // SAFETY: guaranteed
107        if unsafe { *rgba.get_unchecked(3) } >= 128 {
108            rgb[..3].copy_from_slice(&rgba[..3]);
109        }
110        rgba = &rgba[4..];
111        rgb = &mut rgb[3..];
112    }
113}
114
115impl<T: AsMut<[u8]> + AsRef<[u8]>, U: AsRef<[u8]>> Overlay<Image<U, 4>> for Image<T, 4> {
116    #[inline]
117    #[cfg_attr(debug_assertions, track_caller)]
118    unsafe fn overlay(&mut self, with: &Image<U, 4>) -> &mut Self {
119        debug_assert!(self.width() == with.width());
120        debug_assert!(self.height() == with.height());
121        for (i, other_pixels) in with.chunked().enumerate() {
122            if other_pixels[3] >= 128 {
123                // SAFETY: outside are bounds of index from slice
124                let own_pixels =
125                    unsafe { self.buffer.as_mut().get_unchecked_mut(i * 4..i * 4 + 4) };
126                own_pixels.copy_from_slice(other_pixels);
127            }
128        }
129        self
130    }
131}
132
133impl<const A: usize, const B: usize, T: AsMut<[u8]> + AsRef<[u8]>, U: AsRef<[u8]>>
134    BlendingOverlay<Image<U, B>> for Image<T, A>
135where
136    [u8; A]: Blend<B>,
137{
138    #[inline]
139    #[cfg_attr(debug_assertions, track_caller)]
140    unsafe fn overlay_blended(&mut self, with: &Image<U, B>) -> &mut Self {
141        debug_assert!(self.width() == with.width());
142        debug_assert!(self.height() == with.height());
143        for (other_pixels, own_pixels) in with.chunked().zip(self.chunked_mut()) {
144            own_pixels.blend(*other_pixels);
145        }
146        self
147    }
148}
149
150impl<const A: usize, const B: usize, T: AsMut<[u8]> + AsRef<[u8]>, U: AsRef<[u8]>>
151    BlendingOverlayAt<Image<U, B>> for Image<T, A>
152where
153    [u8; A]: Blend<B>,
154{
155    #[inline]
156    unsafe fn overlay_blended_at(&mut self, with: &Image<U, B>, x: u32, y: u32) -> &mut Self {
157        for j in 0..with.height() {
158            for i in 0..with.width() {
159                // SAFETY: i, j is in bounds.
160                let their_px = unsafe { &with.pixel(i, j) };
161                let our_px = unsafe { self.pixel_mut(i + x, j + y) };
162                our_px.blend(*their_px);
163            }
164        }
165        self
166    }
167}
168
169impl<T: AsMut<[u8]> + AsRef<[u8]>> Image<T, 3> {
170    #[doc(hidden)]
171    #[cfg_attr(debug_assertions, track_caller)]
172    pub unsafe fn blend_alpha_and_color_at(
173        &mut self,
174        with: &Image<&[u8], 1>,
175        color: [u8; 3],
176        x: u32,
177        y: u32,
178    ) {
179        for j in 0..with.height() {
180            for i in 0..with.width() {
181                let &[their_alpha] = unsafe { &with.pixel(i, j) };
182                let our_pixel = unsafe { self.pixel_mut(i + x, j + y) };
183                crate::pixels::blending::blend_alpha_and_color(their_alpha, color, our_pixel);
184            }
185        }
186    }
187}
188
189impl ClonerOverlay<4, 4> for ImageCloner<'_, 4> {
190    #[inline]
191    unsafe fn overlay(&self, with: &Image<&[u8], 4>) -> Image<Vec<u8>, 4> {
192        let mut out = self.dup();
193        // SAFETY: same
194        unsafe { out.as_mut().overlay(with) };
195        out
196    }
197}
198
199impl<T: AsMut<[u8]> + AsRef<[u8]>, U: AsRef<[u8]>> OverlayAt<Image<U, 4>> for Image<T, 3> {
200    #[inline]
201    #[cfg_attr(debug_assertions, track_caller)]
202    unsafe fn overlay_at(&mut self, with: &Image<U, 4>, x: u32, y: u32) -> &mut Self {
203        // SAFETY: caller upholds this
204        unsafe { assert_unchecked(x + with.width() <= self.width()) };
205        debug_assert!(y + with.height() <= self.height());
206        for j in 0..with.height() {
207            let i_x = j as usize * with.width() as usize * 4
208                ..(j as usize + 1) * with.width() as usize * 4;
209            let o_x = ((j as usize + y as usize) * self.width() as usize + x as usize) * 3
210                ..((j as usize + y as usize) * self.width() as usize
211                    + x as usize
212                    + with.width() as usize)
213                    * 3;
214            // SAFETY: index is in bounds
215            let rgb = unsafe { self.buffer.as_mut().get_unchecked_mut(o_x) };
216            // SAFETY: bounds are outside index
217            let rgba = unsafe { with.buffer.as_ref().get_unchecked(i_x) };
218            // SAFETY: arguments are 🟢
219            unsafe { blit(rgb, rgba) }
220        }
221        self
222    }
223}
224
225impl<U: AsRef<[u8]>> OverlayAt<Image<U, 4>> for uninit::Image<u8, 3> {
226    #[cfg_attr(debug_assertions, track_caller)]
227    unsafe fn overlay_at(&mut self, with: &Image<U, 4>, x: u32, y: u32) -> &mut Self {
228        // SAFETY: caller upholds this
229        unsafe { assert_unchecked(x + with.width() <= self.width()) };
230        debug_assert!(y + with.height() <= self.height());
231        for j in 0..with.height() {
232            let i_x = j as usize * with.width() as usize * 4
233                ..(j as usize + 1) * with.width() as usize * 4;
234            let o_x = ((j as usize + y as usize) * self.width() as usize + x as usize) * 3
235                ..((j as usize + y as usize) * self.width() as usize
236                    + x as usize
237                    + with.width() as usize)
238                    * 3;
239            // SAFETY: index is in bounds
240            let rgb = unsafe { transmute(self.buf().get_unchecked_mut(o_x)) };
241            // SAFETY: bounds are outside index
242            let rgba = unsafe { with.buffer.as_ref().get_unchecked(i_x) };
243            // SAFETY: arguments are 🟢
244            unsafe { blit(rgb, rgba) }
245        }
246        self
247    }
248}
249
250impl ClonerOverlayAt<4, 3> for ImageCloner<'_, 3> {
251    #[inline]
252    unsafe fn overlay_at(&self, with: &Image<&[u8], 4>, x: u32, y: u32) -> Image<Vec<u8>, 3> {
253        let mut new = self.dup();
254        // SAFETY: same
255        unsafe { new.as_mut().overlay_at(with, x, y) };
256        new
257    }
258}
259
260impl<U: AsRef<[u8]>> OverlayAt<Image<U, 3>> for uninit::Image<u8, 3> {
261    #[inline]
262    #[cfg_attr(debug_assertions, track_caller)]
263    unsafe fn overlay_at(&mut self, with: &Image<U, 3>, x: u32, y: u32) -> &mut Self {
264        for j in 0..(with.width() as usize) {
265            let i_x = j * (with.width() as usize) * 3..(j + 1) * (with.width() as usize) * 3;
266            let o_x = ((j + y as usize) * self.width() as usize + x as usize) * 3
267                ..((j + y as usize) * self.width() as usize + x as usize + (with.width() as usize))
268                    * 3;
269            // <= because ".." range
270            // debug_assert!(o_x.end <= self.buffer().as_ref().len());
271            debug_assert!(i_x.end <= with.buffer().as_ref().len());
272            // SAFETY: we are in ⬜!
273            let b = unsafe { with.buffer.as_ref().get_unchecked(i_x) };
274            // SAFETY: should work
275            unsafe { self.write(b, o_x) };
276        }
277        self
278    }
279}
280
281impl<T: AsMut<[u8]> + AsRef<[u8]>, U: AsRef<[u8]>> OverlayAt<Image<U, 3>> for Image<T, 3> {
282    /// Overlay a RGB image(with) => self at coordinates x, y.
283    /// As this is a `RGBxRGB` operation, blending is unnecessary,
284    /// and this is simply a copy.
285    ///
286    /// # Safety
287    ///
288    /// UB if x, y is out of bounds
289    #[inline]
290    #[cfg_attr(debug_assertions, track_caller)]
291    unsafe fn overlay_at(&mut self, with: &Image<U, 3>, x: u32, y: u32) -> &mut Self {
292        /// helper macro for defining rgb=>rgb overlays. allows unrolling
293        macro_rules! o3x3 {
294            ($n:expr) => {{
295                for j in 0..($n as usize) {
296                    let i_x = j * ($n as usize) * 3..(j + 1) * ($n as usize) * 3;
297                    let o_x = ((j + y as usize) * self.width() as usize + x as usize) * 3
298                        ..((j + y as usize) * self.width() as usize + x as usize + ($n as usize))
299                            * 3;
300                    // <= because ".." range
301                    debug_assert!(o_x.end <= self.buffer().as_ref().len());
302                    debug_assert!(i_x.end <= with.buffer().as_ref().len());
303                    // SAFETY: bounds are ✅
304                    let a = unsafe { self.buffer.as_mut().get_unchecked_mut(o_x) };
305                    // SAFETY: we are in ⬜!
306                    let b = unsafe { with.buffer.as_ref().get_unchecked(i_x) };
307                    a.copy_from_slice(b);
308                }
309            }};
310        }
311        // let it unroll
312        match with.width() {
313            8 => o3x3!(8),
314            16 => o3x3!(16), // this branch makes 8x8 0.16 times slower; but 16x16 0.2 times faster.
315            _ => o3x3!(with.width()),
316        }
317        self
318    }
319}
320
321impl ClonerOverlayAt<3, 3> for ImageCloner<'_, 3> {
322    /// Overlay a RGB image(with) => self at coordinates x, y.
323    /// As this is a `RGBxRGB` operation, blending is unnecessary,
324    /// and this is simply a copy.
325    ///
326    /// # Safety
327    ///
328    /// UB if x, y is out of bounds
329    #[inline]
330    unsafe fn overlay_at(&self, with: &Image<&[u8], 3>, x: u32, y: u32) -> Image<Vec<u8>, 3> {
331        let mut out = self.dup();
332        // SAFETY: same
333        unsafe { out.as_mut().overlay_at(with, x, y) };
334        out
335    }
336}
337
338impl<T: AsMut<[u8]> + AsRef<[u8]>, U: AsRef<[u8]>> Overlay<Image<U, 4>> for Image<T, 3> {
339    #[inline]
340    #[cfg_attr(debug_assertions, track_caller)]
341    unsafe fn overlay(&mut self, with: &Image<U, 4>) -> &mut Self {
342        debug_assert!(self.width() == with.width());
343        debug_assert!(self.height() == with.height());
344        for (i, chunk) in with
345            .buffer
346            .as_ref()
347            .chunks_exact(with.width() as usize * 4)
348            .enumerate()
349        {
350            // SAFETY: all the bounds are good
351            let rgb = unsafe {
352                self.buffer.as_mut().get_unchecked_mut(
353                    i * with.width() as usize * 3..(i + 1) * with.width() as usize * 3,
354                )
355            };
356            // SAFETY: we have the rgb and rgba arguments right
357            unsafe { blit(rgb, chunk) };
358        }
359        self
360    }
361}
362
363impl ClonerOverlay<4, 3> for ImageCloner<'_, 3> {
364    #[inline]
365    unsafe fn overlay(&self, with: &Image<&[u8], 4>) -> Image<Vec<u8>, 3> {
366        let mut out = self.dup();
367        // SAFETY: same
368        unsafe { out.as_mut().overlay(with) };
369        out
370    }
371}
372
373impl<T: AsMut<[u8]> + AsRef<[u8]>, U: AsRef<[u8]>> OverlayAt<Image<U, 4>> for Image<T, 4> {
374    #[inline]
375    unsafe fn overlay_at(&mut self, with: &Image<U, 4>, x: u32, y: u32) -> &mut Self {
376        for j in 0..with.height() {
377            for i in 0..with.width() {
378                // SAFETY: i, j is in bounds.
379                let their_px = unsafe { &with.pixel(i, j) };
380                if their_px[3] >= 128 {
381                    // SAFETY: if everything else goes well, this is fine
382                    let our_px = unsafe { self.pixel_mut(i + x, j + y) };
383                    our_px.copy_from_slice(their_px);
384                }
385            }
386        }
387
388        self
389    }
390}
391
392impl ClonerOverlayAt<4, 4> for ImageCloner<'_, 4> {
393    #[inline]
394    unsafe fn overlay_at(&self, with: &Image<&[u8], 4>, x: u32, y: u32) -> Image<Vec<u8>, 4> {
395        let mut out = self.dup();
396        // SAFETY: same
397        unsafe { out.as_mut().overlay_at(with, x, y) };
398        out
399    }
400}