video_resize/resize/
algorithms.rs

1use std::f64::consts::PI;
2
3use crate::ResizeAlgorithm;
4
5/// Very fast, simple point resizing.
6/// Likely to produce heavy aliasing--i.e. a pixelated effect.
7/// Not generally useful.
8pub struct Point;
9
10impl ResizeAlgorithm for Point {
11    #[inline(always)]
12    fn support() -> u32 {
13        0
14    }
15
16    #[inline(always)]
17    fn new() -> Self {
18        Self
19    }
20
21    #[inline(always)]
22    fn process(&self, _x: f64) -> f64 {
23        1.0_f64
24    }
25}
26
27/// A bilinear resizer.
28/// Tends to produce heavy blurring, not generally useful.
29pub struct Bilinear;
30
31impl ResizeAlgorithm for Bilinear {
32    #[inline(always)]
33    fn support() -> u32 {
34        1
35    }
36
37    #[inline(always)]
38    fn new() -> Self {
39        Self
40    }
41
42    #[inline(always)]
43    fn process(&self, x: f64) -> f64 {
44        (1.0 - x.abs()).max(0.0)
45    }
46}
47
48/// A bicubic resizer with params (b=1.0, c=0.0).
49/// Tends to produce heavy blurring, not generally useful.
50pub struct BicubicBSpline {
51    polys: BicubicPolys,
52}
53
54impl ResizeAlgorithm for BicubicBSpline {
55    #[inline(always)]
56    fn support() -> u32 {
57        2
58    }
59
60    #[inline(always)]
61    fn new() -> Self {
62        Self {
63            polys: BicubicPolys::new(1.0, 0.0),
64        }
65    }
66
67    #[inline(always)]
68    fn process(&self, x: f64) -> f64 {
69        self.polys.process(x)
70    }
71}
72
73/// A bicubic resizer with params (b=0.0, c=0.0).
74/// Useful for downscaling.
75pub struct BicubicHermite {
76    polys: BicubicPolys,
77}
78
79impl ResizeAlgorithm for BicubicHermite {
80    #[inline(always)]
81    fn support() -> u32 {
82        2
83    }
84
85    #[inline(always)]
86    fn new() -> Self {
87        Self {
88            polys: BicubicPolys::new(0.0, 0.0),
89        }
90    }
91
92    #[inline(always)]
93    fn process(&self, x: f64) -> f64 {
94        self.polys.process(x)
95    }
96}
97
98/// A bicubic resizer with params (b=1/3, c=1/3).
99/// Useful for downscaling.
100pub struct BicubicMitchell {
101    polys: BicubicPolys,
102}
103
104impl ResizeAlgorithm for BicubicMitchell {
105    #[inline(always)]
106    fn support() -> u32 {
107        2
108    }
109
110    #[inline(always)]
111    fn new() -> Self {
112        Self {
113            polys: BicubicPolys::new(1.0 / 3.0, 1.0 / 3.0),
114        }
115    }
116
117    #[inline(always)]
118    fn process(&self, x: f64) -> f64 {
119        self.polys.process(x)
120    }
121}
122
123/// A bicubic resizer with params (b=0.0, c=0.5).
124/// Fast, general purpose.
125pub struct BicubicCatmullRom {
126    polys: BicubicPolys,
127}
128
129impl ResizeAlgorithm for BicubicCatmullRom {
130    #[inline(always)]
131    fn support() -> u32 {
132        2
133    }
134
135    #[inline(always)]
136    fn new() -> Self {
137        Self {
138            polys: BicubicPolys::new(0.0, 0.5),
139        }
140    }
141
142    #[inline(always)]
143    fn process(&self, x: f64) -> f64 {
144        self.polys.process(x)
145    }
146}
147
148/// 3-tap Lanczos resizer.
149/// Sharp, good for upscaling.
150pub struct Lanczos3;
151
152impl ResizeAlgorithm for Lanczos3 {
153    #[inline(always)]
154    fn support() -> u32 {
155        3
156    }
157
158    #[inline(always)]
159    fn new() -> Self {
160        Self
161    }
162
163    #[inline(always)]
164    fn process(&self, x: f64) -> f64 {
165        lanczos_filter(x, 3)
166    }
167}
168
169/// 4-tap Lanczos resizer.
170/// Sharp, good for upscaling.
171pub struct Lanczos4;
172
173impl ResizeAlgorithm for Lanczos4 {
174    #[inline(always)]
175    fn support() -> u32 {
176        4
177    }
178
179    #[inline(always)]
180    fn new() -> Self {
181        Self
182    }
183
184    #[inline(always)]
185    fn process(&self, x: f64) -> f64 {
186        lanczos_filter(x, 4)
187    }
188}
189
190/// 16-tap spline resizer.
191/// Good balance between speed, sharpness, and ringing.
192pub struct Spline16;
193
194impl ResizeAlgorithm for Spline16 {
195    #[inline(always)]
196    fn support() -> u32 {
197        2
198    }
199
200    #[inline(always)]
201    fn new() -> Self {
202        Self
203    }
204
205    #[inline(always)]
206    fn process(&self, x: f64) -> f64 {
207        let x = x.abs();
208
209        if x < 1.0_f64 {
210            return poly3(x, 1.0, -1.0 / 5.0, -9.0 / 5.0, 1.0);
211        }
212        if x < 2.0_f64 {
213            let x = x - 1.0_f64;
214            return poly3(x, 0.0, -7.0 / 15.0, 4.0 / 5.0, -1.0 / 3.0);
215        }
216        0.0_f64
217    }
218}
219
220/// 36-tap spline resizer.
221/// High quality, good balance between sharpness and ringing.
222pub struct Spline36;
223
224impl ResizeAlgorithm for Spline36 {
225    #[inline(always)]
226    fn support() -> u32 {
227        3
228    }
229
230    #[inline(always)]
231    fn new() -> Self {
232        Self
233    }
234
235    #[inline(always)]
236    fn process(&self, x: f64) -> f64 {
237        let x = x.abs();
238
239        if x < 1.0_f64 {
240            return poly3(x, 1.0, -3.0 / 209.0, -453.0 / 209.0, 13.0 / 11.0);
241        }
242        if x < 2.0_f64 {
243            let x = x - 1.0_f64;
244            return poly3(x, 0.0, -156.0 / 209.0, 270.0 / 209.0, -6.0 / 11.0);
245        }
246        if x < 3.0_f64 {
247            let x = x - 2.0_f64;
248            return poly3(x, 0.0, 26.0 / 209.0, -45.0 / 209.0, 1.0 / 11.0);
249        }
250        0.0_f64
251    }
252}
253
254/// 64-tap spline resizer.
255/// High quality, good balance between sharpness and ringing.
256pub struct Spline64;
257
258impl ResizeAlgorithm for Spline64 {
259    #[inline(always)]
260    fn support() -> u32 {
261        4
262    }
263
264    #[inline(always)]
265    fn new() -> Self {
266        Self
267    }
268
269    #[inline(always)]
270    fn process(&self, x: f64) -> f64 {
271        let x = x.abs();
272
273        if x < 1.0_f64 {
274            return poly3(x, 1.0, -3.0 / 2911.0, -6387.0 / 2911.0, 49.0 / 41.0);
275        }
276        if x < 2.0_f64 {
277            let x = x - 1.0_f64;
278            return poly3(x, 0.0, -2328.0 / 2911.0, 4032.0 / 2911.0, -24.0 / 41.0);
279        }
280        if x < 3.0_f64 {
281            let x = x - 2.0_f64;
282            return poly3(x, 0.0, 582.0 / 2911.0, -1008.0 / 2911.0, 6.0 / 41.0);
283        }
284        if x < 4.0_f64 {
285            let x = x - 3.0_f64;
286            return poly3(x, 0.0, -97.0 / 2911.0, 168.0 / 2911.0, -1.0 / 41.0);
287        }
288        0.0_f64
289    }
290}
291
292#[derive(Clone, Copy)]
293struct BicubicPolys {
294    p0: f64,
295    p2: f64,
296    p3: f64,
297    q0: f64,
298    q1: f64,
299    q2: f64,
300    q3: f64,
301}
302
303impl BicubicPolys {
304    pub fn new(b: f64, c: f64) -> Self {
305        // p0: (  6.0 -  2.0 * b           ) / 6.0
306        // p2: (-18.0 + 12.0 * b +  6.0 * c) / 6.0
307        // p3: ( 12.0 -  9.0 * b -  6.0 * c) / 6.0
308        // q0: (         8.0 * b + 24.0 * c) / 6.0
309        // q1: (       -12.0 * b - 48.0 * c) / 6.0
310        // q2: (         6.0 * b + 30.0 * c) / 6.0
311        // q3: (              -b -  6.0 * c) / 6.0
312        Self {
313            p0: b.mul_add(-2.0, 6.0) / 6.0,
314            p2: c.mul_add(6.0, b.mul_add(12.0, -18.0)) / 6.0,
315            p3: c.mul_add(-6.0, b.mul_add(-9.0, 12.0)) / 6.0,
316            q0: c.mul_add(24.0, b * 8.0) / 6.0,
317            q1: c.mul_add(-48.0, b * -12.0) / 6.0,
318            q2: c.mul_add(30.0, b * 6.0) / 6.0,
319            q3: c.mul_add(-6.0, -b) / 6.0,
320        }
321    }
322
323    #[inline(always)]
324    pub fn process(&self, x: f64) -> f64 {
325        let x = x.abs();
326
327        if x < 1.0_f64 {
328            return poly3(x, self.p0, 0.0, self.p2, self.p3);
329        }
330        if x < 2.0_f64 {
331            return poly3(x, self.q0, self.q1, self.q2, self.q3);
332        }
333        0.0_f64
334    }
335}
336
337#[inline(always)]
338fn poly3(x: f64, c0: f64, c1: f64, c2: f64, c3: f64) -> f64 {
339    // c0 + x * (c1 + x * (c2 + x * c3))
340    x.mul_add(x.mul_add(x.mul_add(c3, c2), c1), c0)
341}
342
343#[inline(always)]
344fn lanczos_filter(x: f64, taps: usize) -> f64 {
345    let x = x.abs();
346    if x < taps as f64 {
347        sinc(x) * sinc(x / taps as f64)
348    } else {
349        0.0_f64
350    }
351}
352
353#[inline(always)]
354fn sinc(x: f64) -> f64 {
355    // Guaranteed to not yield division by zero on IEEE machine with accurate sin(x).
356    if x == 0.0_f64 {
357        1.0_f64
358    } else {
359        (x * PI).sin() / (x * PI)
360    }
361}