mirror of
https://github.com/bend-n/fimg.git
synced 2024-12-21 18:28:19 -06:00
blur
This commit is contained in:
parent
24898eb1cd
commit
c02376b96b
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "fimg"
|
||||
version = "0.4.22"
|
||||
version = "0.4.23"
|
||||
authors = ["bend-n <bend.n@outlook.com>"]
|
||||
license = "MIT"
|
||||
edition = "2021"
|
||||
|
@ -17,6 +17,7 @@ fontdue = { version = "0.7.3", optional = true }
|
|||
vecto = "0.1.0"
|
||||
umath = "0.0.7"
|
||||
fr = { version = "0.1.1", package = "fer", optional = true }
|
||||
stackblur-iter = { version = "0.2.0", features = ["simd"], optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
iai = { git = "https://github.com/bend-n/iai.git" }
|
||||
|
@ -50,6 +51,7 @@ harness = false
|
|||
scale = ["fr"]
|
||||
save = ["png"]
|
||||
text = ["fontdue"]
|
||||
blur = ["stackblur-iter"]
|
||||
default = ["save", "scale"]
|
||||
|
||||
[profile.release]
|
||||
|
|
|
@ -14,4 +14,5 @@ quick simple image operations
|
|||
- [x] box drawing
|
||||
- [x] polygon drawing
|
||||
- [x] circle drawing
|
||||
- [x] text drawing
|
||||
- [x] text drawing
|
||||
- [x] blur
|
51
src/blur.rs
Normal file
51
src/blur.rs
Normal file
|
@ -0,0 +1,51 @@
|
|||
use stackblur_iter::imgref::ImgRefMut;
|
||||
|
||||
use crate::{pixels::convert::PFrom, Image};
|
||||
|
||||
impl<T: AsMut<[u32]> + AsRef<[u32]>> Image<T, 1> {
|
||||
/// Blur a image of packed 32 bit integers, `[0xAARRGGBB]`.
|
||||
pub fn blur_argb(&mut self, radius: usize) {
|
||||
let w = self.width() as usize;
|
||||
let h = self.height() as usize;
|
||||
stackblur_iter::simd_blur_argb::<4>(&mut ImgRefMut::new(self.buffer.as_mut(), w, h), radius)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Image<Box<[u8]>, N>
|
||||
where
|
||||
[u8; 4]: PFrom<N>,
|
||||
[u8; N]: PFrom<4>,
|
||||
{
|
||||
/// Blur a image.
|
||||
/// ```
|
||||
/// # use fimg::Image;
|
||||
/// let mut i = Image::alloc(300, 300).boxed();
|
||||
/// // draw a lil pentagon
|
||||
/// i.poly((150., 150.), 5, 100.0, 0.0, [255]);
|
||||
/// // give it some blur
|
||||
/// i.blur(25);
|
||||
/// assert_eq!(include_bytes!("../tdata/blurred_pentagon.imgbuf"), i.bytes())
|
||||
/// ```
|
||||
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 mut argb = Image::<Box<[u32]>, 1>::from(self.as_ref());
|
||||
argb.blur_argb(radius);
|
||||
for (i, n) in crate::convert::unpack_all(&argb.buffer).enumerate() {
|
||||
*unsafe { self.buffer.get_unchecked_mut(i) } = n;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Image<&[u8], N>
|
||||
where
|
||||
[u8; 4]: PFrom<N>,
|
||||
[u8; N]: PFrom<4>,
|
||||
{
|
||||
/// Blur a image.
|
||||
pub fn blur(self, radius: usize) -> Image<Box<[u8]>, N> {
|
||||
let mut argb = Image::<Box<[u32]>, 1>::from(self);
|
||||
argb.blur_argb(radius);
|
||||
// SAFETY: ctor
|
||||
unsafe { Image::new(argb.width, argb.height, &**argb.buffer()) }.into()
|
||||
}
|
||||
}
|
|
@ -62,3 +62,57 @@ boxconv!(3 => 4);
|
|||
boxconv!(4 => 1);
|
||||
boxconv!(4 => 2);
|
||||
boxconv!(4 => 3);
|
||||
|
||||
#[inline]
|
||||
fn pack([r, g, b, a]: [u8; 4]) -> u32 {
|
||||
((a as u32) << 24) | ((r as u32) << 16) | ((g as u32) << 8) | (b as u32)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn unpack(n: u32) -> [u8; 4] {
|
||||
[
|
||||
((n >> 16) & 0xFF) as u8,
|
||||
((n >> 8) & 0xFF) as u8,
|
||||
(n & 0xFF) as u8,
|
||||
((n >> 24) & 0xFF) as u8,
|
||||
]
|
||||
}
|
||||
|
||||
impl<const N: usize> From<Image<&[u8], N>> for Image<Box<[u32]>, 1>
|
||||
where
|
||||
[u8; 4]: PFrom<N>,
|
||||
{
|
||||
/// Pack into ARGB.
|
||||
fn from(value: Image<&[u8], N>) -> Self {
|
||||
let buf = value
|
||||
.chunked()
|
||||
.copied()
|
||||
.map(PFrom::pfrom)
|
||||
.map(pack)
|
||||
.collect();
|
||||
// SAFETY: ctor
|
||||
unsafe { Self::new(value.width, value.height, buf) }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unpack_all<const N: usize>(buffer: &[u32]) -> impl Iterator<Item = u8> + '_
|
||||
where
|
||||
[u8; N]: PFrom<4>,
|
||||
{
|
||||
buffer
|
||||
.iter()
|
||||
.copied()
|
||||
.map(unpack)
|
||||
.flat_map(<[u8; N] as PFrom<4>>::pfrom)
|
||||
}
|
||||
|
||||
impl<const N: usize> From<Image<&[u32], 1>> for Image<Box<[u8]>, N>
|
||||
where
|
||||
[u8; N]: PFrom<4>,
|
||||
{
|
||||
fn from(value: Image<&[u32], 1>) -> Self {
|
||||
let buf = unpack_all(value.buffer).collect();
|
||||
// SAFETY: ctor
|
||||
unsafe { Self::new(value.width, value.height, buf) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
//! Misc image ops:
|
||||
//! - [`Image::repeated`]
|
||||
//! - [`Image::overlay`](Overlay), [`Image::overlay_at`](OverlayAt), [`Image::overlay_blended`](BlendingOverlay)
|
||||
//! - [`Image::blur`]
|
||||
#![feature(
|
||||
slice_swap_unchecked,
|
||||
generic_const_exprs,
|
||||
|
@ -60,6 +61,8 @@
|
|||
use std::{num::NonZeroU32, slice::SliceIndex};
|
||||
|
||||
mod affine;
|
||||
#[cfg(feature = "blur")]
|
||||
mod blur;
|
||||
#[doc(hidden)]
|
||||
pub mod builder;
|
||||
#[doc(hidden)]
|
||||
|
|
|
@ -24,7 +24,7 @@ impl PFrom<2> for Y {
|
|||
|
||||
impl PFrom<3> for Y {
|
||||
fn pfrom([r, g, b]: RGB) -> Self {
|
||||
[((2126 * r as u16 + 7152 * g as u16 + 722 * b as u16) / 10000) as u8]
|
||||
[((2126 * r as u32 + 7152 * g as u32 + 722 * b as u32) / 10000) as u8]
|
||||
}
|
||||
}
|
||||
|
||||
|
|
BIN
tdata/blurred_pentagon.imgbuf
Normal file
BIN
tdata/blurred_pentagon.imgbuf
Normal file
Binary file not shown.
Loading…
Reference in a new issue