mirror of
https://github.com/bend-n/fimg.git
synced 2024-12-22 10:28:21 -06:00
optimize blur and provide in place options
This commit is contained in:
parent
669a541bb7
commit
df2c7d2436
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "fimg"
|
name = "fimg"
|
||||||
version = "0.4.26"
|
version = "0.4.28"
|
||||||
authors = ["bend-n <bend.n@outlook.com>"]
|
authors = ["bend-n <bend.n@outlook.com>"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
@ -17,7 +17,7 @@ fontdue = { version = "0.7.3", optional = true }
|
||||||
vecto = "0.1.0"
|
vecto = "0.1.0"
|
||||||
umath = "0.0.7"
|
umath = "0.0.7"
|
||||||
fr = { version = "0.1.1", package = "fer", optional = true }
|
fr = { version = "0.1.1", package = "fer", optional = true }
|
||||||
stackblur-iter = { version = "0.2.0", features = ["simd"], optional = true }
|
slur = { version = "0.1.0", optional = true }
|
||||||
clipline = "0.1.1"
|
clipline = "0.1.1"
|
||||||
minifb = { version = "0.25.0", default-features = false, features = [
|
minifb = { version = "0.25.0", default-features = false, features = [
|
||||||
"x11",
|
"x11",
|
||||||
|
@ -56,7 +56,7 @@ harness = false
|
||||||
scale = ["fr"]
|
scale = ["fr"]
|
||||||
save = ["png"]
|
save = ["png"]
|
||||||
text = ["fontdue"]
|
text = ["fontdue"]
|
||||||
blur = ["stackblur-iter"]
|
blur = ["slur"]
|
||||||
real-show = ["minifb", "text"]
|
real-show = ["minifb", "text"]
|
||||||
default = ["save", "scale"]
|
default = ["save", "scale"]
|
||||||
|
|
||||||
|
|
166
src/blur.rs
166
src/blur.rs
|
@ -1,4 +1,11 @@
|
||||||
use stackblur_iter::imgref::ImgRefMut;
|
use slur::{
|
||||||
|
color::{u32xN, BlurU32},
|
||||||
|
imgref::ImgRefMut,
|
||||||
|
};
|
||||||
|
use std::{
|
||||||
|
ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign},
|
||||||
|
simd::Simd,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::Image;
|
use crate::Image;
|
||||||
|
|
||||||
|
@ -7,43 +14,156 @@ impl<T: AsMut<[u32]> + AsRef<[u32]>> Image<T, 1> {
|
||||||
pub fn blur_argb(&mut self, radius: usize) {
|
pub fn blur_argb(&mut self, radius: usize) {
|
||||||
let w = self.width() as usize;
|
let w = self.width() as usize;
|
||||||
let h = self.height() as usize;
|
let h = self.height() as usize;
|
||||||
stackblur_iter::simd_blur_argb::<4>(&mut ImgRefMut::new(self.buffer.as_mut(), w, h), radius)
|
slur::simd_blur_argb::<4>(&mut ImgRefMut::new(self.buffer.as_mut(), w, h), radius)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const N: usize> Image<Box<[u8]>, N>
|
macro_rules! simd {
|
||||||
where
|
($n:literal) => {
|
||||||
[u8; N]: crate::Pack,
|
impl<T: AsMut<[u8]> + AsRef<[u8]>> Image<T, $n> {
|
||||||
{
|
/// Blur a image.
|
||||||
|
pub fn blur_in(&mut self, radius: usize) {
|
||||||
|
let (w, h) = (self.width() as usize, self.height() as usize);
|
||||||
|
let px = self.flatten_mut();
|
||||||
|
slur::blur(
|
||||||
|
&mut ImgRefMut::new(px, w, h),
|
||||||
|
radius,
|
||||||
|
|x| slur::color::u32xN(std::simd::Simd::from_array(x.map(|x| x as u32))),
|
||||||
|
|x| x.0.to_array().map(|x| x as u8),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
simd!(4);
|
||||||
|
simd!(2);
|
||||||
|
|
||||||
|
impl<T: AsMut<[u8]> + AsRef<[u8]>> Image<T, 3> {
|
||||||
/// Blur a image.
|
/// Blur a image.
|
||||||
/// ```
|
/// ```
|
||||||
/// # use fimg::Image;
|
/// # use fimg::Image;
|
||||||
/// let mut i = Image::alloc(300, 300).boxed();
|
/// let mut i = Image::alloc(300, 300).boxed();
|
||||||
|
/// // draw a trongle
|
||||||
|
/// i.poly((150., 150.), 3, 100.0, 0.0, [255, 255, 255]);
|
||||||
|
/// // give it some blur
|
||||||
|
/// i.blur_in(25);
|
||||||
|
/// ```
|
||||||
|
pub fn blur_in(&mut self, radius: usize) {
|
||||||
|
let (w, h) = (self.width() as usize, self.height() as usize);
|
||||||
|
let px = self.flatten_mut();
|
||||||
|
slur::blur(
|
||||||
|
&mut ImgRefMut::new(px, w, h),
|
||||||
|
radius,
|
||||||
|
|x| Px::from(*x),
|
||||||
|
|x| x.into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: AsMut<[u8]> + AsRef<[u8]>> Image<T, 1> {
|
||||||
|
/// Blur a image. No copy.
|
||||||
|
/// ```
|
||||||
|
/// # use fimg::Image;
|
||||||
|
/// let mut i = Image::alloc(300, 300);
|
||||||
/// // draw a lil pentagon
|
/// // draw a lil pentagon
|
||||||
/// i.poly((150., 150.), 5, 100.0, 0.0, [255]);
|
/// i.poly((150., 150.), 5, 100.0, 0.0, [255]);
|
||||||
/// // give it some blur
|
/// // give it some blur
|
||||||
/// i.blur(25);
|
/// i.blur(25);
|
||||||
/// assert_eq!(include_bytes!("../tdata/blurred_pentagon.imgbuf"), i.bytes())
|
/// # assert_eq!(include_bytes!("../tdata/blurred_pentagon.imgbuf"), i.bytes())
|
||||||
/// ```
|
/// ```
|
||||||
pub fn blur(&mut self, radius: usize) {
|
pub fn blur(&mut self, radius: usize) {
|
||||||
// you know, i optimized blurslice a fair bit, and yet, despite all the extra bit twiddling stackblur-iter is faster.
|
let (w, h) = (self.width() as usize, self.height() as usize);
|
||||||
let mut argb = Image::<Box<[u32]>, 1>::from(self.as_ref());
|
slur::simd_blur::<_, _, _, 8>(
|
||||||
argb.blur_argb(radius);
|
&mut ImgRefMut::new(self.buffer.as_mut(), w, h),
|
||||||
for (i, n) in crate::convert::unpack_all(&argb.buffer).enumerate() {
|
radius,
|
||||||
*unsafe { self.buffer.get_unchecked_mut(i) } = n;
|
|x| u32xN(Simd::from_array(x.map(|&x| x as u32))),
|
||||||
}
|
|x| x.0.as_array().map(|x| x as u8),
|
||||||
|
|&x| BlurU32(x as u32),
|
||||||
|
|x| x.0 as u8,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const N: usize> Image<&[u8], N>
|
macro_rules! blur_packing {
|
||||||
where
|
($n:literal) => {
|
||||||
[u8; N]: crate::Pack,
|
impl<T: AsRef<[u8]> + AsMut<[u8]>> Image<T, $n> {
|
||||||
{
|
/// Blur a image. This will allocate a <code>[Image]<[Box]<[[u32]]>, 1></code>.
|
||||||
/// Blur a image.
|
/// If you want no copy, but slower if you dont have a simd-able cpu, check out [`Image::blur_in`].
|
||||||
pub fn blur(self, radius: usize) -> Image<Box<[u8]>, N> {
|
/// ```
|
||||||
let mut argb = Image::<Box<[u32]>, 1>::from(self);
|
/// # use fimg::Image;
|
||||||
argb.blur_argb(radius);
|
/// let mut i = Image::alloc(300, 300);
|
||||||
// SAFETY: ctor
|
/// // draw a sqar
|
||||||
unsafe { Image::new(argb.width, argb.height, &**argb.buffer()) }.into()
|
/// i.poly((150., 150.), 4, 100.0, 0.0, [255]);
|
||||||
|
/// // give it some blur
|
||||||
|
/// i.blur(25);
|
||||||
|
/// ```
|
||||||
|
pub fn blur(&mut self, radius: usize) {
|
||||||
|
// the bit twiddling lets it simd better
|
||||||
|
let mut argb = Image::<Box<[u32]>, 1>::from(self.as_ref());
|
||||||
|
argb.blur_argb(radius);
|
||||||
|
for (i, n) in crate::convert::unpack_all::<$n>(&argb.buffer).enumerate() {
|
||||||
|
*unsafe { self.buffer.as_mut().get_unchecked_mut(i) } = n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
blur_packing!(2);
|
||||||
|
blur_packing!(3);
|
||||||
|
blur_packing!(4);
|
||||||
|
|
||||||
|
#[repr(transparent)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
struct Px<const N: usize>([u32; N]);
|
||||||
|
|
||||||
|
impl<const N: usize> Default for Px<N> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self([0; N])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<const N: usize> From<[u8; N]> for Px<N> {
|
||||||
|
fn from(x: [u8; N]) -> Self {
|
||||||
|
Self(x.map(|x| x as u32))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const N: usize> From<Px<N>> for [u8; N] {
|
||||||
|
fn from(v: Px<N>) -> Self {
|
||||||
|
v.0.map(|x| x as u8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! op {
|
||||||
|
($name:ident, $as:ident, $fn:ident, $ass_fn:ident, $meth:ident) => {
|
||||||
|
impl<const N: usize> $name<usize> for Px<N> {
|
||||||
|
type Output = Px<N>;
|
||||||
|
|
||||||
|
fn $fn(self, rhs: usize) -> Self::Output {
|
||||||
|
Self(self.0.map(|x| x.$meth(rhs as u32)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const N: usize> $name for Px<N> {
|
||||||
|
type Output = Px<N>;
|
||||||
|
fn $fn(self, rhs: Px<N>) -> Self::Output {
|
||||||
|
let mut out = [0; N];
|
||||||
|
for ((a, b), x) in self.0.iter().zip(rhs.0.iter()).zip(out.iter_mut()) {
|
||||||
|
*x = a.$meth(*b);
|
||||||
|
}
|
||||||
|
Self(out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const N: usize> $as for Px<N> {
|
||||||
|
fn $ass_fn(&mut self, rhs: Self) {
|
||||||
|
*self = self.$fn(rhs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
op!(Mul, MulAssign, mul, mul_assign, wrapping_mul);
|
||||||
|
op!(Sub, SubAssign, sub, sub_assign, wrapping_sub);
|
||||||
|
op!(Add, AddAssign, add, add_assign, wrapping_add);
|
||||||
|
op!(Div, DivAssign, div, div_assign, wrapping_div);
|
||||||
|
|
Loading…
Reference in a new issue