This commit is contained in:
bendn 2023-11-01 09:09:20 +07:00
parent 24898eb1cd
commit c02376b96b
No known key found for this signature in database
GPG key ID: 0D9D3A2A3B2A93D6
7 changed files with 114 additions and 3 deletions

View file

@ -1,6 +1,6 @@
[package] [package]
name = "fimg" name = "fimg"
version = "0.4.22" version = "0.4.23"
authors = ["bend-n <bend.n@outlook.com>"] authors = ["bend-n <bend.n@outlook.com>"]
license = "MIT" license = "MIT"
edition = "2021" edition = "2021"
@ -17,6 +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 }
[dev-dependencies] [dev-dependencies]
iai = { git = "https://github.com/bend-n/iai.git" } iai = { git = "https://github.com/bend-n/iai.git" }
@ -50,6 +51,7 @@ harness = false
scale = ["fr"] scale = ["fr"]
save = ["png"] save = ["png"]
text = ["fontdue"] text = ["fontdue"]
blur = ["stackblur-iter"]
default = ["save", "scale"] default = ["save", "scale"]
[profile.release] [profile.release]

View file

@ -15,3 +15,4 @@ quick simple image operations
- [x] polygon drawing - [x] polygon drawing
- [x] circle drawing - [x] circle drawing
- [x] text drawing - [x] text drawing
- [x] blur

51
src/blur.rs Normal file
View 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()
}
}

View file

@ -62,3 +62,57 @@ boxconv!(3 => 4);
boxconv!(4 => 1); boxconv!(4 => 1);
boxconv!(4 => 2); boxconv!(4 => 2);
boxconv!(4 => 3); 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) }
}
}

View file

@ -34,6 +34,7 @@
//! Misc image ops: //! Misc image ops:
//! - [`Image::repeated`] //! - [`Image::repeated`]
//! - [`Image::overlay`](Overlay), [`Image::overlay_at`](OverlayAt), [`Image::overlay_blended`](BlendingOverlay) //! - [`Image::overlay`](Overlay), [`Image::overlay_at`](OverlayAt), [`Image::overlay_blended`](BlendingOverlay)
//! - [`Image::blur`]
#![feature( #![feature(
slice_swap_unchecked, slice_swap_unchecked,
generic_const_exprs, generic_const_exprs,
@ -60,6 +61,8 @@
use std::{num::NonZeroU32, slice::SliceIndex}; use std::{num::NonZeroU32, slice::SliceIndex};
mod affine; mod affine;
#[cfg(feature = "blur")]
mod blur;
#[doc(hidden)] #[doc(hidden)]
pub mod builder; pub mod builder;
#[doc(hidden)] #[doc(hidden)]

View file

@ -24,7 +24,7 @@ impl PFrom<2> for Y {
impl PFrom<3> for Y { impl PFrom<3> for Y {
fn pfrom([r, g, b]: RGB) -> Self { 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]
} }
} }

Binary file not shown.