Skip to main content

pic_scale_safe/
sampler.rs

1/*
2 * Copyright (c) Radzivon Bartoshyk, 10/2024. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without modification,
5 * are permitted provided that the following conditions are met:
6 *
7 * 1.  Redistributions of source code must retain the above copyright notice, this
8 * list of conditions and the following disclaimer.
9 *
10 * 2.  Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 *
14 * 3.  Neither the name of the copyright holder nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29#![allow(clippy::excessive_precision)]
30
31use crate::math::bartlett::{bartlett, bartlett_hann};
32use crate::math::bc_spline::{
33    b_spline, catmull_rom, hermite_spline, mitchell_netravalli, robidoux, robidoux_sharp,
34};
35use crate::math::bilinear::bilinear;
36use crate::math::blackman::blackman;
37use crate::math::bohman::bohman;
38use crate::math::cubic::{bicubic_spline, cubic_spline};
39use crate::math::gaussian::gaussian;
40use crate::math::hann::{hamming, hann, hanning};
41use crate::math::kaiser::kaiser;
42use crate::math::lagrange::{lagrange2, lagrange3};
43use crate::math::lanczos::{
44    lanczos2, lanczos2_jinc, lanczos3, lanczos3_jinc, lanczos4, lanczos4_jinc, lanczos6,
45    lanczos6_jinc,
46};
47use crate::math::quadric::quadric;
48use crate::math::sinc::sinc;
49use crate::math::sphinx::sphinx;
50use crate::math::spline_n::{spline16, spline36, spline64};
51use crate::math::welch::welch;
52use crate::math::{ConstPI, ConstSqrt2, Jinc};
53use num_traits::{AsPrimitive, Float, Signed};
54use std::ops::{AddAssign, MulAssign, Neg};
55
56#[inline(always)]
57pub(crate) fn box_weight<V: Copy + 'static>(_: V) -> V
58where
59    f32: AsPrimitive<V>,
60{
61    1f32.as_()
62}
63
64#[derive(Debug, Copy, Clone, Default, Ord, PartialOrd, Eq, PartialEq)]
65/// Describes resampling function that will be used
66pub enum ResamplingFunction {
67    Bilinear,
68    Nearest,
69    Cubic,
70    #[default]
71    MitchellNetravalli,
72    CatmullRom,
73    Hermite,
74    BSpline,
75    Hann,
76    Bicubic,
77    Hamming,
78    Hanning,
79    Blackman,
80    Welch,
81    Quadric,
82    Gaussian,
83    Sphinx,
84    Bartlett,
85    Robidoux,
86    RobidouxSharp,
87    Spline16,
88    Spline36,
89    Spline64,
90    Kaiser,
91    BartlettHann,
92    Box,
93    Bohman,
94    Lanczos2,
95    Lanczos3,
96    Lanczos4,
97    Lanczos2Jinc,
98    Lanczos3Jinc,
99    Lanczos4Jinc,
100    Ginseng,
101    HaasnSoft,
102    Lagrange2,
103    Lagrange3,
104    Lanczos6,
105    Lanczos6Jinc,
106    /// This method replicates `INTER_AREA` behaviour from OpenCV
107    Area,
108}
109
110impl From<u32> for ResamplingFunction {
111    fn from(value: u32) -> Self {
112        match value {
113            0 => ResamplingFunction::Bilinear,
114            1 => ResamplingFunction::Nearest,
115            2 => ResamplingFunction::Cubic,
116            3 => ResamplingFunction::MitchellNetravalli,
117            4 => ResamplingFunction::CatmullRom,
118            5 => ResamplingFunction::Hermite,
119            6 => ResamplingFunction::BSpline,
120            7 => ResamplingFunction::Hann,
121            8 => ResamplingFunction::Bicubic,
122            9 => ResamplingFunction::Hamming,
123            10 => ResamplingFunction::Hanning,
124            11 => ResamplingFunction::Blackman,
125            12 => ResamplingFunction::Welch,
126            13 => ResamplingFunction::Quadric,
127            14 => ResamplingFunction::Gaussian,
128            15 => ResamplingFunction::Sphinx,
129            16 => ResamplingFunction::Bartlett,
130            17 => ResamplingFunction::Robidoux,
131            18 => ResamplingFunction::RobidouxSharp,
132            19 => ResamplingFunction::Spline16,
133            20 => ResamplingFunction::Spline36,
134            21 => ResamplingFunction::Spline64,
135            22 => ResamplingFunction::Kaiser,
136            23 => ResamplingFunction::BartlettHann,
137            24 => ResamplingFunction::Box,
138            25 => ResamplingFunction::Bohman,
139            26 => ResamplingFunction::Lanczos2,
140            27 => ResamplingFunction::Lanczos3,
141            28 => ResamplingFunction::Lanczos4,
142            29 => ResamplingFunction::Lanczos2Jinc,
143            30 => ResamplingFunction::Lanczos3Jinc,
144            31 => ResamplingFunction::Lanczos4Jinc,
145            32 => ResamplingFunction::Ginseng,
146            33 => ResamplingFunction::HaasnSoft,
147            34 => ResamplingFunction::Lagrange2,
148            35 => ResamplingFunction::Lagrange3,
149            36 => ResamplingFunction::Lanczos6,
150            37 => ResamplingFunction::Lanczos6Jinc,
151            38 => ResamplingFunction::Area,
152            _ => ResamplingFunction::Bilinear,
153        }
154    }
155}
156
157#[derive(Debug, Copy, Clone)]
158pub(crate) struct ResamplingWindow<T> {
159    pub(crate) window: fn(T) -> T,
160    pub(crate) window_size: f32,
161    pub(crate) blur: f32,
162    pub(crate) taper: f32,
163}
164
165impl<T> ResamplingWindow<T> {
166    fn new(window: fn(T) -> T, window_size: f32, blur: f32, taper: f32) -> ResamplingWindow<T> {
167        ResamplingWindow {
168            window,
169            window_size,
170            blur,
171            taper,
172        }
173    }
174}
175
176#[derive(Debug, Copy, Clone)]
177pub(crate) struct ResamplingFilter<T> {
178    pub kernel: fn(T) -> T,
179    pub window: Option<ResamplingWindow<T>>,
180    pub min_kernel_size: f32,
181    pub is_resizable_kernel: bool,
182    pub is_area_filter: bool,
183}
184
185impl<T> ResamplingFilter<T> {
186    fn new(kernel: fn(T) -> T, min_kernel_size: f32) -> ResamplingFilter<T> {
187        ResamplingFilter {
188            kernel,
189            window: None,
190            min_kernel_size,
191            is_resizable_kernel: true,
192            is_area_filter: false,
193        }
194    }
195
196    fn new_area(kernel: fn(T) -> T, min_kernel_size: f32) -> ResamplingFilter<T> {
197        ResamplingFilter {
198            kernel,
199            window: None,
200            min_kernel_size,
201            is_resizable_kernel: true,
202            is_area_filter: true,
203        }
204    }
205
206    fn new_with_window(
207        kernel: fn(T) -> T,
208        window: ResamplingWindow<T>,
209        min_kernel_size: f32,
210    ) -> ResamplingFilter<T> {
211        ResamplingFilter::<T> {
212            kernel,
213            window: Some(window),
214            min_kernel_size,
215            is_resizable_kernel: true,
216            is_area_filter: false,
217        }
218    }
219
220    fn new_with_fixed_kernel(kernel: fn(T) -> T, min_kernel_size: f32) -> ResamplingFilter<T> {
221        ResamplingFilter::<T> {
222            kernel,
223            window: None,
224            min_kernel_size,
225            is_resizable_kernel: false,
226            is_area_filter: false,
227        }
228    }
229}
230
231impl ResamplingFunction {
232    pub(crate) fn get_resampling_filter<T>(&self) -> ResamplingFilter<T>
233    where
234        T: Copy
235            + Neg
236            + Signed
237            + Float
238            + 'static
239            + ConstPI
240            + MulAssign<T>
241            + AddAssign<T>
242            + AsPrimitive<f64>
243            + AsPrimitive<usize>
244            + Jinc<T>
245            + ConstSqrt2,
246        f32: AsPrimitive<T>,
247        f64: AsPrimitive<T>,
248        usize: AsPrimitive<T>,
249    {
250        match self {
251            ResamplingFunction::Bilinear => ResamplingFilter::new(bilinear, 1f32),
252            ResamplingFunction::Area => ResamplingFilter::new_area(box_weight, 0.5f32),
253            ResamplingFunction::Nearest => {
254                // Just a stab for nearest
255                ResamplingFilter::new(bilinear, 2f32)
256            }
257            ResamplingFunction::Cubic => ResamplingFilter::new(cubic_spline, 2f32),
258            ResamplingFunction::MitchellNetravalli => {
259                ResamplingFilter::new(mitchell_netravalli, 2f32)
260            }
261            ResamplingFunction::Lanczos3 => ResamplingFilter::new(lanczos3, 3f32),
262            ResamplingFunction::CatmullRom => ResamplingFilter::new(catmull_rom, 2f32),
263            ResamplingFunction::Hermite => ResamplingFilter::new(hermite_spline, 2f32),
264            ResamplingFunction::BSpline => ResamplingFilter::new(b_spline, 2f32),
265            ResamplingFunction::Hann => ResamplingFilter::new(hann, 3f32),
266            ResamplingFunction::Bicubic => ResamplingFilter::new(bicubic_spline, 2f32),
267            ResamplingFunction::Lanczos4 => ResamplingFilter::new(lanczos4, 4f32),
268            ResamplingFunction::Lanczos2 => ResamplingFilter::new(lanczos2, 2f32),
269            ResamplingFunction::Hamming => ResamplingFilter::new(hamming, 1f32),
270            ResamplingFunction::Hanning => ResamplingFilter::new(hanning, 2f32),
271            ResamplingFunction::Welch => ResamplingFilter::new(welch, 2f32),
272            ResamplingFunction::Quadric => ResamplingFilter::new(quadric, 2f32),
273            ResamplingFunction::Gaussian => ResamplingFilter::new(gaussian, 2f32),
274            ResamplingFunction::Sphinx => ResamplingFilter::new(sphinx, 2f32),
275            ResamplingFunction::Bartlett => ResamplingFilter::new(bartlett, 2f32),
276            ResamplingFunction::Robidoux => ResamplingFilter::new(robidoux, 2f32),
277            ResamplingFunction::RobidouxSharp => ResamplingFilter::new(robidoux_sharp, 2f32),
278            ResamplingFunction::Spline16 => ResamplingFilter::new_with_fixed_kernel(spline16, 2f32),
279            ResamplingFunction::Spline36 => ResamplingFilter::new_with_fixed_kernel(spline36, 4f32),
280            ResamplingFunction::Spline64 => ResamplingFilter::new_with_fixed_kernel(spline64, 6f32),
281            ResamplingFunction::Kaiser => ResamplingFilter::new(kaiser, 2f32),
282            ResamplingFunction::BartlettHann => ResamplingFilter::new(bartlett_hann, 2f32),
283            ResamplingFunction::Box => ResamplingFilter::new(box_weight, 2f32),
284            ResamplingFunction::Bohman => ResamplingFilter::new(bohman, 2f32),
285            ResamplingFunction::Lanczos2Jinc => ResamplingFilter::new(lanczos2_jinc, 2f32),
286            ResamplingFunction::Lanczos3Jinc => ResamplingFilter::new(lanczos3_jinc, 3f32),
287            ResamplingFunction::Lanczos4Jinc => ResamplingFilter::new(lanczos4_jinc, 4f32),
288            ResamplingFunction::Blackman => ResamplingFilter::new(blackman, 2f32),
289            ResamplingFunction::Ginseng => ResamplingFilter::new_with_window(
290                sinc,
291                ResamplingWindow::new(T::jinc(), 3f32, 1f32, 0f32),
292                3f32,
293            ),
294            ResamplingFunction::HaasnSoft => ResamplingFilter::new_with_window(
295                T::jinc(),
296                ResamplingWindow::new(hanning, 3f32, 1.11f32, 0f32),
297                3f32,
298            ),
299            ResamplingFunction::Lagrange2 => ResamplingFilter::new(lagrange2, 2f32),
300            ResamplingFunction::Lagrange3 => ResamplingFilter::new(lagrange3, 3f32),
301            ResamplingFunction::Lanczos6Jinc => ResamplingFilter::new(lanczos6_jinc, 6f32),
302            ResamplingFunction::Lanczos6 => ResamplingFilter::new(lanczos6, 6f32),
303        }
304    }
305}