use crate::{stack_blur, stack_blur_f32, FastBlurChannels, ThreadingPolicy};
use image::{
DynamicImage, GrayAlphaImage, GrayImage, ImageBuffer, Luma, LumaA, Rgb, Rgb32FImage, RgbImage,
Rgba, Rgba32FImage, RgbaImage,
};
#[must_use]
pub fn stack_blur_image(
image: DynamicImage,
radius: u32,
threading_policy: ThreadingPolicy,
) -> Option<DynamicImage> {
match image {
DynamicImage::ImageLuma8(gray) => {
let mut new_image = gray.as_raw().to_vec();
stack_blur(
&mut new_image,
gray.width(),
gray.width(),
gray.height(),
radius,
FastBlurChannels::Plane,
threading_policy,
);
let new_gray_image = GrayImage::from_raw(gray.width(), gray.height(), new_image)?;
Some(DynamicImage::ImageLuma8(new_gray_image))
}
DynamicImage::ImageLumaA8(luma_alpha_image) => {
let mut intensity_plane =
vec![0u8; luma_alpha_image.width() as usize * luma_alpha_image.height() as usize];
let mut alpha_plane =
vec![0u8; luma_alpha_image.width() as usize * luma_alpha_image.height() as usize];
let raw_buffer = luma_alpha_image.as_raw();
for ((intensity, alpha), raw_buffer) in intensity_plane
.iter_mut()
.zip(alpha_plane.iter_mut())
.zip(raw_buffer.chunks_exact(2))
{
*intensity = raw_buffer[0];
*alpha = raw_buffer[1];
}
stack_blur(
&mut intensity_plane,
luma_alpha_image.width(),
luma_alpha_image.width(),
luma_alpha_image.height(),
radius,
FastBlurChannels::Plane,
threading_policy,
);
stack_blur(
&mut alpha_plane,
luma_alpha_image.width(),
luma_alpha_image.width(),
luma_alpha_image.height(),
radius,
FastBlurChannels::Plane,
threading_policy,
);
let mut new_raw_buffer =
vec![
0u8;
luma_alpha_image.width() as usize * luma_alpha_image.height() as usize * 2
];
for ((&intensity, &alpha), raw_buffer) in intensity_plane
.iter()
.zip(alpha_plane.iter())
.zip(new_raw_buffer.chunks_exact_mut(2))
{
raw_buffer[0] = intensity;
raw_buffer[1] = alpha;
}
let new_gray_image = GrayAlphaImage::from_raw(
luma_alpha_image.width(),
luma_alpha_image.height(),
new_raw_buffer,
)?;
Some(DynamicImage::ImageLumaA8(new_gray_image))
}
DynamicImage::ImageRgb8(rgb_image) => {
let mut new_image = rgb_image.as_raw().to_vec();
stack_blur(
&mut new_image,
rgb_image.width() * 3,
rgb_image.width(),
rgb_image.height(),
radius,
FastBlurChannels::Channels3,
threading_policy,
);
let new_rgb_image =
RgbImage::from_raw(rgb_image.width(), rgb_image.height(), new_image)?;
Some(DynamicImage::ImageRgb8(new_rgb_image))
}
DynamicImage::ImageRgba8(rgba_image) => {
let mut new_image = rgba_image.as_raw().to_vec();
stack_blur(
&mut new_image,
rgba_image.width() * 4,
rgba_image.width(),
rgba_image.height(),
radius,
FastBlurChannels::Channels4,
threading_policy,
);
let new_rgba_image =
RgbaImage::from_raw(rgba_image.width(), rgba_image.height(), new_image)?;
Some(DynamicImage::ImageRgba8(new_rgba_image))
}
DynamicImage::ImageLuma16(luma_16) => {
let mut new_image = luma_16
.as_raw()
.iter()
.map(|&x| x as f32 * (1. / (u16::MAX as f32)))
.collect::<Vec<_>>();
stack_blur_f32(
&mut new_image,
luma_16.width(),
luma_16.height(),
radius,
FastBlurChannels::Plane,
threading_policy,
);
let rolled_back_image = new_image
.iter()
.map(|&x| (x * (u16::MAX as f32)).round().min(u16::MAX as f32) as u16)
.collect::<Vec<_>>();
let new_rgb_image = ImageBuffer::<Luma<u16>, Vec<u16>>::from_raw(
luma_16.width(),
luma_16.height(),
rolled_back_image,
)?;
Some(DynamicImage::ImageLuma16(new_rgb_image))
}
DynamicImage::ImageLumaA16(gray_alpha_16) => {
let mut intensity_plane =
vec![0f32; gray_alpha_16.width() as usize * gray_alpha_16.height() as usize];
let mut alpha_plane =
vec![0f32; gray_alpha_16.width() as usize * gray_alpha_16.height() as usize];
let raw_buffer = gray_alpha_16.as_raw();
for ((intensity, alpha), raw_buffer) in intensity_plane
.iter_mut()
.zip(alpha_plane.iter_mut())
.zip(raw_buffer.chunks_exact(2))
{
*intensity = raw_buffer[0] as f32 * (1. / u16::MAX as f32);
*alpha = raw_buffer[1] as f32 * (1. / u16::MAX as f32);
}
stack_blur_f32(
&mut intensity_plane,
gray_alpha_16.width(),
gray_alpha_16.height(),
radius,
FastBlurChannels::Plane,
threading_policy,
);
stack_blur_f32(
&mut alpha_plane,
gray_alpha_16.width(),
gray_alpha_16.height(),
radius,
FastBlurChannels::Plane,
threading_policy,
);
let mut new_raw_buffer =
vec![0u16; gray_alpha_16.width() as usize * gray_alpha_16.height() as usize * 2];
for ((intensity, alpha), raw_buffer) in intensity_plane
.iter()
.zip(alpha_plane.iter())
.zip(new_raw_buffer.chunks_exact_mut(2))
{
raw_buffer[0] = ((*intensity) * (u16::MAX as f32))
.round()
.min(u16::MAX as f32) as u16;
raw_buffer[1] = ((*alpha) * (u16::MAX as f32)).round().min(u16::MAX as f32) as u16;
}
let new_gray_image = ImageBuffer::<LumaA<u16>, Vec<u16>>::from_raw(
gray_alpha_16.width(),
gray_alpha_16.height(),
new_raw_buffer,
)?;
Some(DynamicImage::ImageLumaA16(new_gray_image))
}
DynamicImage::ImageRgb16(rgb_16_image) => {
let mut new_image = rgb_16_image
.as_raw()
.iter()
.map(|&x| x as f32 * (1. / (u16::MAX as f32)))
.collect::<Vec<_>>();
stack_blur_f32(
&mut new_image,
rgb_16_image.width(),
rgb_16_image.height(),
radius,
FastBlurChannels::Channels3,
threading_policy,
);
let rolled_back_image = new_image
.iter()
.map(|&x| (x * (u16::MAX as f32)).round().min(u16::MAX as f32) as u16)
.collect::<Vec<_>>();
let new_rgb_image = ImageBuffer::<Rgb<u16>, Vec<u16>>::from_raw(
rgb_16_image.width(),
rgb_16_image.height(),
rolled_back_image,
)?;
Some(DynamicImage::ImageRgb16(new_rgb_image))
}
DynamicImage::ImageRgba16(rgba_16_image) => {
let mut new_image = rgba_16_image
.as_raw()
.iter()
.map(|&x| x as f32 * (1. / (u16::MAX as f32)))
.collect::<Vec<_>>();
stack_blur_f32(
&mut new_image,
rgba_16_image.width(),
rgba_16_image.height(),
radius,
FastBlurChannels::Channels3,
threading_policy,
);
let rolled_back_image = new_image
.iter()
.map(|&x| (x * (u16::MAX as f32)).round().min(u16::MAX as f32) as u16)
.collect::<Vec<_>>();
let new_rgba_image = ImageBuffer::<Rgba<u16>, Vec<u16>>::from_raw(
rgba_16_image.width(),
rgba_16_image.height(),
rolled_back_image,
)?;
Some(DynamicImage::ImageRgba16(new_rgba_image))
}
DynamicImage::ImageRgb32F(rgb_image_f32) => {
let mut new_image = rgb_image_f32.as_raw().to_vec();
stack_blur_f32(
&mut new_image,
rgb_image_f32.width(),
rgb_image_f32.height(),
radius,
FastBlurChannels::Channels3,
threading_policy,
);
let new_rgb_image =
Rgb32FImage::from_raw(rgb_image_f32.width(), rgb_image_f32.height(), new_image)?;
Some(DynamicImage::ImageRgb32F(new_rgb_image))
}
DynamicImage::ImageRgba32F(rgba_image_f32) => {
let mut new_image = rgba_image_f32.as_raw().to_vec();
stack_blur_f32(
&mut new_image,
rgba_image_f32.width(),
rgba_image_f32.height(),
radius,
FastBlurChannels::Channels4,
threading_policy,
);
let new_rgb_image =
Rgba32FImage::from_raw(rgba_image_f32.width(), rgba_image_f32.height(), new_image)?;
Some(DynamicImage::ImageRgba32F(new_rgb_image))
}
_ => None,
}
}