fast image operations
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
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())
                }

                ChannelCount::<N>::unhandle(&mut dst);

                // 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
    }
}