fast image operations
Diffstat (limited to 'src/scale/algorithms.rs')
-rw-r--r--src/scale/algorithms.rs140
1 files changed, 140 insertions, 0 deletions
diff --git a/src/scale/algorithms.rs b/src/scale/algorithms.rs
new file mode 100644
index 0000000..d1dd6cf
--- /dev/null
+++ b/src/scale/algorithms.rs
@@ -0,0 +1,140 @@
+use super::{traits::*, *};
+use std::num::NonZeroU32;
+
+/// [Nearest Neighbor](https://en.wikipedia.org/wiki/Nearest-neighbor_interpolation) image scaling algorithm.
+pub struct Nearest;
+
+impl ScalingAlgorithm for Nearest {
+ /// Can be used on non opaque too! (Nearest is special like that).
+ fn scale_opaque<const N: usize>(
+ i: Image<&[u8], N>,
+ w: NonZeroU32,
+ h: NonZeroU32,
+ ) -> Image<std::boxed::Box<[u8]>, N>
+ where
+ ChannelCount<N>: ToImageView<N>,
+ {
+ let mut dst = fr::Image::new(w, h);
+ // SAFETY: swear, the pixel types are the same
+ unsafe {
+ fr::Resizer::new(fr::ResizeAlg::Nearest)
+ .resize(&ChannelCount::<N>::wrap(i), &mut dst.view_mut())
+ };
+
+ // SAFETY: ctor
+ unsafe { Image::new(dst.width(), dst.height(), dst.into_vec().into()) }
+ }
+
+ #[inline]
+ fn scale_transparent<const N: usize>(
+ i: Image<&mut [u8], N>,
+ w: NonZeroU32,
+ h: NonZeroU32,
+ ) -> Image<std::boxed::Box<[u8]>, N>
+ where
+ ChannelCount<N>: AlphaDiv<N>,
+ {
+ Self::scale_opaque(i.as_ref(), w, h)
+ }
+}
+
+macro_rules! alg {
+ ($for:ident) => {
+ impl ScalingAlgorithm for $for {
+ fn scale_opaque<const N: usize>(
+ i: Image<&[u8], N>,
+ w: NonZeroU32,
+ h: NonZeroU32,
+ ) -> Image<std::boxed::Box<[u8]>, N>
+ where
+ ChannelCount<N>: ToImageView<N>,
+ {
+ let mut dst = fr::Image::new(w, h);
+ // SAFETY: swear, the pixel types are the same
+ unsafe {
+ fr::Resizer::new(fr::ResizeAlg::Convolution(fr::FilterType::$for))
+ .resize(&ChannelCount::<N>::wrap(i), &mut dst.view_mut())
+ };
+
+ // SAFETY: ctor
+ unsafe { Image::new(dst.width(), dst.height(), dst.into_vec().into()) }
+ }
+
+ fn scale_transparent<const N: usize>(
+ i: Image<&mut [u8], N>,
+ w: NonZeroU32,
+ h: NonZeroU32,
+ ) -> Image<std::boxed::Box<[u8]>, N>
+ where
+ ChannelCount<N>: AlphaDiv<N>,
+ {
+ let mut dst = fr::Image::new(w, h);
+ // SAFETY: yes
+ unsafe {
+ fr::Resizer::new(fr::ResizeAlg::Convolution(fr::FilterType::$for))
+ .resize(&ChannelCount::<N>::handle(i).view(), &mut dst.view_mut())
+ }
+
+ // SAFETY: ctor
+ unsafe { Image::new(dst.width(), dst.height(), dst.into_vec().into()) }
+ }
+ }
+ };
+}
+
+/// [Lanczos](https://en.wikipedia.org/wiki/Lanczos_resampling) scaling with a filter size (*a*) of 3.
+pub struct Lanczos3 {}
+alg!(Lanczos3);
+
+/// [Catmull-Rom](https://en.wikipedia.org/wiki/Centripetal_Catmull%E2%80%93Rom_spline) bicubic filtering.
+pub struct CatmullRom {}
+alg!(CatmullRom);
+
+/// Linear interpolation.
+pub struct Bilinear {}
+alg!(Bilinear);
+
+/// The opposite of [`Nearest`].
+pub struct Box {}
+alg!(Box);
+
+/// Hamming filtering has the same performance as a [`Bilinear`] filter, while
+/// providing image (downscaling) quality comparable to bicubic filters like
+/// [`CatmullRom`] or [`Mitchell`]. Creates a sharper image than [`Bilinear`] filtering,
+/// and doesn't have dislocations on local level like [`Box`] suffers from.
+/// Not recommended for upscaling.
+pub struct Hamming {}
+alg!(Hamming);
+
+/// [Mitchell–Netravali](https://en.wikipedia.org/wiki/Mitchell%E2%80%93Netravali_filters) bicubic filtering.
+pub struct Mitchell {}
+alg!(Mitchell);
+
+impl Nearest {
+ /// Resize a image.
+ /// # Safety
+ ///
+ /// `image` must be as big or bigger than `width`, `height.
+ #[must_use = "function does not modify the original image"]
+ #[deprecated = "use Image::scale instead (note that Image::scale does not support any N. if there is a N you would like to see supported, please open a issue)"]
+ pub unsafe fn scale<const N: usize>(
+ image: Image<&[u8], N>,
+ width: u32,
+ height: u32,
+ ) -> Image<Vec<u8>, N> {
+ let x_scale = image.width() as f32 / width as f32;
+ let y_scale = image.height() as f32 / height as f32;
+ let mut out = Image::alloc(width, height);
+ for y in 0..height {
+ for x in 0..width {
+ let x1 = ((x as f32 + 0.5) * x_scale).floor() as u32;
+ let y1 = ((y as f32 + 0.5) * y_scale).floor() as u32;
+ // SAFETY: i asked the caller to make sure its ok
+ let px = unsafe { image.pixel(x1, y1) };
+ // SAFETY: were looping over the width and height of out. its ok.
+ unsafe { out.set_pixel(x, y, px) };
+ }
+ }
+ out
+ }
+}