fast image operations
add blending overlay at
| -rw-r--r-- | Cargo.toml | 3 | ||||
| -rw-r--r-- | src/affine.rs | 2 | ||||
| -rw-r--r-- | src/lib.rs | 9 | ||||
| -rw-r--r-- | src/overlay.rs | 29 | ||||
| -rw-r--r-- | src/pixels/blending.rs | 15 | ||||
| -rw-r--r-- | src/pixels/mod.rs | 2 | ||||
| -rw-r--r-- | src/pixels/utility.rs | 19 | ||||
| -rw-r--r-- | src/pixels/wam.rs | 28 |
8 files changed, 65 insertions, 42 deletions
@@ -3,7 +3,7 @@ name = "fimg" version = "0.4.44" authors = ["bend-n <[email protected]>"] license = "MIT" -edition = "2021" +edition = "2024" description = "fast image operations" repository = "https://github.com/bend-n/fimg" exclude = ["tdata", "benches/", ".gitignore"] @@ -28,6 +28,7 @@ atools = "0.1.4" qwant = { version = "1.0.0", optional = true } libc = "0.2.154" hinted = { version = "0.0.2", features = ["nightly"] } +lower = "0.2.0" [target.'cfg(windows)'.dependencies] windows = { version = "0.53.0", features = [ diff --git a/src/affine.rs b/src/affine.rs index d18d210..7a971e5 100644 --- a/src/affine.rs +++ b/src/affine.rs @@ -1,5 +1,5 @@ //! Manages the affine image transformations. -use crate::{cloner::ImageCloner, Image}; +use crate::{Image, cloner::ImageCloner}; impl<const CHANNELS: usize> ImageCloner<'_, CHANNELS> { /// Flip an image vertically. @@ -53,11 +53,12 @@ #![feature( type_changing_struct_update, maybe_uninit_write_slice, + custom_inner_attributes, slice_swap_unchecked, generic_const_exprs, + proc_macro_hygiene, iter_array_chunks, core_intrinsics, - slice_as_chunks, rustc_private, portable_simd, array_windows, @@ -116,8 +117,10 @@ mod show; #[cfg(feature = "term")] pub mod term; pub use cloner::ImageCloner; -pub use overlay::{BlendingOverlay, ClonerOverlay, ClonerOverlayAt, Overlay, OverlayAt}; pub use r#dyn::DynImage; +pub use overlay::{ + BlendingOverlay, BlendingOverlayAt, ClonerOverlay, ClonerOverlayAt, Overlay, OverlayAt, +}; trait CopyWithinUnchecked { /// # Safety @@ -621,7 +624,7 @@ impl<T, const CHANNELS: usize> Image<T, CHANNELS> { pub fn cols<U: Copy>( &self, ) -> impl DoubleEndedIterator - + ExactSizeIterator< + + ExactSizeIterator< Item = impl ExactSizeIterator + DoubleEndedIterator<Item = [U; CHANNELS]> + '_, > where diff --git a/src/overlay.rs b/src/overlay.rs index 8733f20..b21985d 100644 --- a/src/overlay.rs +++ b/src/overlay.rs @@ -2,7 +2,7 @@ // TODO Y/YA use crate::{cloner::ImageCloner, uninit}; -use super::{assert_unchecked, Image}; +use super::{Image, assert_unchecked}; use crate::pixels::Blend; use std::{mem::transmute, simd::prelude::*}; @@ -52,6 +52,14 @@ pub trait BlendingOverlay<W> { /// 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 { @@ -136,6 +144,25 @@ where } } +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 ClonerOverlay<4, 4> for ImageCloner<'_, 4> { #[inline] #[must_use = "function does not modify the original image"] diff --git a/src/pixels/blending.rs b/src/pixels/blending.rs index ee22001..20d312e 100644 --- a/src/pixels/blending.rs +++ b/src/pixels/blending.rs @@ -1,7 +1,7 @@ //! module for pixel blending ops -use super::{convert::PFrom, unfloat, Floatify, Unfloatify}; +#![allow(redundant_semicolons)] +use super::{Floatify, Unfloatify, convert::PFrom, unfloat}; use atools::prelude::*; -use umath::FF32; /// Trait for blending pixels together. pub trait Blend<const W: usize> { @@ -10,6 +10,7 @@ pub trait Blend<const W: usize> { } impl Blend<4> for [u8; 4] { + #[lower::apply(algebraic)] fn blend(&mut self, fg: [u8; 4]) { if fg[3] == 0 { return; @@ -26,12 +27,11 @@ impl Blend<4> for [u8; 4] { }; self[..3].copy_from_slice( &fg.trunc() - // SAFETY: no u8 can possibly become INF / NAN .zip(bg.trunc()) - .map(|(f, b)| unsafe { (f * fg[3] + b * bg[3] * (FF32::new(1.0) - fg[3])) / a }) + .map(|(f, b)| (f * fg[3] + b * bg[3] * (1.0 - fg[3])) / a) .unfloat(), ); - self[3] = unfloat(a); + self[3] = unfloat(a) } } @@ -50,6 +50,7 @@ impl Blend<4> for [u8; 3] { } impl Blend<2> for [u8; 2] { + #[lower::apply(algebraic)] fn blend(&mut self, with: [u8; 2]) { let bg = self.float(); let fg = with.float(); @@ -60,10 +61,10 @@ impl Blend<2> for [u8; 2] { } *self = [ // SAFETY: no u8 can do transform bad - (fg[0] * fg[1] + bg[0] * bg[1] * (unsafe { FF32::new(1.0) } - fg[1])) / a, + (fg[0] * fg[1] + bg[0] * bg[1] * (unsafe { (1.0) } - fg[1])) / a, a, ] - .unfloat(); + .unfloat() } } diff --git a/src/pixels/mod.rs b/src/pixels/mod.rs index d757368..ceaa3d4 100644 --- a/src/pixels/mod.rs +++ b/src/pixels/mod.rs @@ -4,6 +4,6 @@ pub mod blending; mod utility; mod wam; pub use blending::Blend; -pub(crate) use utility::{float, unfloat, Floatify, Unfloatify}; +pub(crate) use utility::{Floatify, Unfloatify, float, unfloat}; pub(crate) use wam::Wam; pub mod convert; diff --git a/src/pixels/utility.rs b/src/pixels/utility.rs index e9cca62..78795ac 100644 --- a/src/pixels/utility.rs +++ b/src/pixels/utility.rs @@ -1,4 +1,4 @@ -use umath::FF32; +#[lower::apply(algebraic)] pub trait Unfloatify<const N: usize> { /// computes 255 * n, for all elements fn unfloat(self) -> [u8; N]; @@ -6,12 +6,12 @@ pub trait Unfloatify<const N: usize> { #[inline(always)] /// computes 255 * n -pub fn unfloat(n: FF32) -> u8 { +pub fn unfloat(n: f32) -> u8 { // SAFETY: n is 0..=1 - unsafe { *(FF32::new(255.0) * n) as u8 } + (255.0 * n) as u8 } -impl<const N: usize> Unfloatify<N> for [FF32; N] { +impl<const N: usize> Unfloatify<N> for [f32; N] { fn unfloat(self) -> [u8; N] { self.map(unfloat) } @@ -22,20 +22,21 @@ impl<const N:usize>Unfloatify<N>for[u8; N]{fn unfloat(self)->[u8;N]{self}} pub trait Floatify<const N: usize> { /// computes n / 255, for all elements - fn float(self) -> [FF32; N]; + fn float(self) -> [f32; N]; } /// computes n / 255 -pub fn float(n: u8) -> FF32 { +#[lower::apply(algebraic)] +pub fn float(n: u8) -> f32 { // SAFETY: 0..=255 / 0..=255 mayn't ever be NAN / INF - unsafe { FF32::new(n as f32) / FF32::new(255.0) } + n as f32 / 255.0 } impl<const N: usize> Floatify<N> for [u8; N] { - fn float(self) -> [FF32; N] { + fn float(self) -> [f32; N] { self.map(float) } } #[rustfmt::skip] -impl<const N:usize>Floatify<N>for[FF32;N]{fn float(self)->[FF32;N]{self}} +impl<const N:usize>Floatify<N>for[f32;N]{fn float(self)->[f32;N]{self}} diff --git a/src/pixels/wam.rs b/src/pixels/wam.rs index ccf7d9f..e33d496 100644 --- a/src/pixels/wam.rs +++ b/src/pixels/wam.rs @@ -1,37 +1,27 @@ use super::{float, unfloat}; use atools::prelude::*; -use umath::{generic_float::Constructors, FF32}; #[allow(dead_code)] pub trait Wam { /// this function weighs the sides and combines - /// - /// # Safety - /// - /// pls make l = <code>0..=[f32::MAX]/2</code>, r = <code>0..=[f32::MAX]/2</code> - unsafe fn wam(self, b: Self, l: FF32, r: FF32) -> Self; + fn wam(self, b: Self, l: f32, r: f32) -> Self; } impl<const N: usize> Wam for [u8; N] { - unsafe fn wam(self, b: Self, l: FF32, r: FF32) -> Self { + fn wam(self, b: Self, l: f32, r: f32) -> Self { // SAFETY: read [`weigh`] - self.zip(b).map(|(a, b)| unsafe { weigh(a, b, l, r) }) + self.zip(b).map(|(a, b)| weigh(a, b, l, r)) } } #[inline(always)] -/// # Safety -/// -/// floats must be smart -unsafe fn weigh(a: u8, b: u8, l: FF32, r: FF32) -> u8 { - // SAFETY: float(x) returns 0..=1, 0..=1 * f32::MAX isn't Inf, but if you add 1.0 and then mul by max again, you get inf (big bad, hence unsafe fn) - unsafe { unfloat((float(a) * l + float(b) * r).clamp(FF32::zero(), FF32::one())) } +#[lower::apply(algebraic)] +fn weigh(a: u8, b: u8, l: f32, r: f32) -> u8 { + unfloat((float(a) * l + float(b) * r).clamp(0., 1.)) } #[test] fn weig() { - unsafe { - assert_eq!(weigh(10, 20, FF32::new(0.5), FF32::new(0.5)), 15); - assert_eq!(weigh(10, 20, FF32::new(0.9), FF32::new(0.1)), 11); - assert_eq!(weigh(150, 150, FF32::new(1.8), FF32::new(0.8)), 255); - } + assert_eq!(weigh(10, 20, 0.5, 0.5), 15); + assert_eq!(weigh(10, 20, 0.9, 0.1), 11); + assert_eq!(weigh(150, 150, 1.8, 0.8), 255); } |