1use crate::basics::{iround, uceil};
7use crate::math::besj;
8use std::f64::consts::PI;
9
10pub const IMAGE_FILTER_SHIFT: u32 = 14;
15pub const IMAGE_FILTER_SCALE: i32 = 1 << IMAGE_FILTER_SHIFT; pub const IMAGE_FILTER_MASK: i32 = IMAGE_FILTER_SCALE - 1;
17
18pub const IMAGE_SUBPIXEL_SHIFT: u32 = 8;
19pub const IMAGE_SUBPIXEL_SCALE: u32 = 1 << IMAGE_SUBPIXEL_SHIFT; pub const IMAGE_SUBPIXEL_MASK: u32 = IMAGE_SUBPIXEL_SCALE - 1;
21
22pub trait ImageFilterFunction {
31 fn radius(&self) -> f64;
33 fn calc_weight(&self, x: f64) -> f64;
35}
36
37pub struct ImageFilterLut {
48 radius: f64,
49 diameter: u32,
50 start: i32,
51 weight_array: Vec<i16>,
52}
53
54impl ImageFilterLut {
55 pub fn new() -> Self {
57 Self {
58 radius: 0.0,
59 diameter: 0,
60 start: 0,
61 weight_array: Vec::new(),
62 }
63 }
64
65 pub fn new_with_filter<F: ImageFilterFunction>(filter: &F, normalization: bool) -> Self {
67 let mut lut = Self::new();
68 lut.calculate(filter, normalization);
69 lut
70 }
71
72 pub fn radius(&self) -> f64 {
73 self.radius
74 }
75
76 pub fn diameter(&self) -> u32 {
77 self.diameter
78 }
79
80 pub fn start(&self) -> i32 {
81 self.start
82 }
83
84 pub fn weight_array(&self) -> &[i16] {
85 &self.weight_array
86 }
87
88 pub fn calculate<F: ImageFilterFunction>(&mut self, filter: &F, normalization: bool) {
92 let r = filter.radius();
93 self.realloc_lut(r);
94 let pivot = (self.diameter << (IMAGE_SUBPIXEL_SHIFT - 1)) as usize;
95 for i in 0..pivot {
96 let x = i as f64 / IMAGE_SUBPIXEL_SCALE as f64;
97 let y = filter.calc_weight(x);
98 let w = iround(y * IMAGE_FILTER_SCALE as f64) as i16;
99 self.weight_array[pivot + i] = w;
100 self.weight_array[pivot - i] = w;
101 }
102 let end = ((self.diameter as usize) << IMAGE_SUBPIXEL_SHIFT) - 1;
103 self.weight_array[0] = self.weight_array[end];
104 if normalization {
105 self.normalize();
106 }
107 }
108
109 #[allow(clippy::needless_range_loop)]
114 pub fn normalize(&mut self) {
115 let mut flip: i32 = 1;
116 let subpixel_scale = IMAGE_SUBPIXEL_SCALE as usize;
117 let diameter = self.diameter as usize;
118
119 for i in 0..subpixel_scale {
120 loop {
121 let mut sum: i32 = 0;
122 for j in 0..diameter {
123 sum += self.weight_array[j * subpixel_scale + i] as i32;
124 }
125
126 if sum == IMAGE_FILTER_SCALE {
127 break;
128 }
129
130 let k = IMAGE_FILTER_SCALE as f64 / sum as f64;
131 sum = 0;
132 for j in 0..diameter {
133 let idx = j * subpixel_scale + i;
134 let v = iround(self.weight_array[idx] as f64 * k) as i16;
135 self.weight_array[idx] = v;
136 sum += v as i32;
137 }
138
139 sum -= IMAGE_FILTER_SCALE;
140 let inc: i32 = if sum > 0 { -1 } else { 1 };
141
142 let mut j = 0;
143 while j < diameter && sum != 0 {
144 flip ^= 1;
145 let idx = if flip != 0 {
146 diameter / 2 + j / 2
147 } else {
148 diameter / 2 - j / 2
149 };
150 let arr_idx = idx * subpixel_scale + i;
151 let v = self.weight_array[arr_idx] as i32;
152 if v < IMAGE_FILTER_SCALE {
153 self.weight_array[arr_idx] += inc as i16;
154 sum += inc;
155 }
156 j += 1;
157 }
158 }
159 }
160
161 let pivot = diameter << (IMAGE_SUBPIXEL_SHIFT as usize - 1);
162 for i in 0..pivot {
163 self.weight_array[pivot + i] = self.weight_array[pivot - i];
164 }
165 let end = (diameter << IMAGE_SUBPIXEL_SHIFT as usize) - 1;
166 self.weight_array[0] = self.weight_array[end];
167 }
168
169 fn realloc_lut(&mut self, radius: f64) {
170 self.radius = radius;
171 self.diameter = uceil(radius) * 2;
172 self.start = -((self.diameter / 2 - 1) as i32);
173 let size = (self.diameter as usize) << IMAGE_SUBPIXEL_SHIFT;
174 if size > self.weight_array.len() {
175 self.weight_array.resize(size, 0);
176 }
177 }
178}
179
180impl Default for ImageFilterLut {
181 fn default() -> Self {
182 Self::new()
183 }
184}
185
186pub struct ImageFilterBilinear;
192impl ImageFilterFunction for ImageFilterBilinear {
193 fn radius(&self) -> f64 {
194 1.0
195 }
196 fn calc_weight(&self, x: f64) -> f64 {
197 1.0 - x
198 }
199}
200
201pub struct ImageFilterHanning;
203impl ImageFilterFunction for ImageFilterHanning {
204 fn radius(&self) -> f64 {
205 1.0
206 }
207 fn calc_weight(&self, x: f64) -> f64 {
208 0.5 + 0.5 * (PI * x).cos()
209 }
210}
211
212pub struct ImageFilterHamming;
214impl ImageFilterFunction for ImageFilterHamming {
215 fn radius(&self) -> f64 {
216 1.0
217 }
218 fn calc_weight(&self, x: f64) -> f64 {
219 0.54 + 0.46 * (PI * x).cos()
220 }
221}
222
223pub struct ImageFilterHermite;
225impl ImageFilterFunction for ImageFilterHermite {
226 fn radius(&self) -> f64 {
227 1.0
228 }
229 fn calc_weight(&self, x: f64) -> f64 {
230 (2.0 * x - 3.0) * x * x + 1.0
231 }
232}
233
234pub struct ImageFilterQuadric;
236impl ImageFilterFunction for ImageFilterQuadric {
237 fn radius(&self) -> f64 {
238 1.5
239 }
240 fn calc_weight(&self, x: f64) -> f64 {
241 if x < 0.5 {
242 return 0.75 - x * x;
243 }
244 if x < 1.5 {
245 let t = x - 1.5;
246 return 0.5 * t * t;
247 }
248 0.0
249 }
250}
251
252pub struct ImageFilterBicubic;
254impl ImageFilterBicubic {
255 fn pow3(x: f64) -> f64 {
256 if x <= 0.0 {
257 0.0
258 } else {
259 x * x * x
260 }
261 }
262}
263impl ImageFilterFunction for ImageFilterBicubic {
264 fn radius(&self) -> f64 {
265 2.0
266 }
267 fn calc_weight(&self, x: f64) -> f64 {
268 (1.0 / 6.0)
269 * (Self::pow3(x + 2.0) - 4.0 * Self::pow3(x + 1.0) + 6.0 * Self::pow3(x)
270 - 4.0 * Self::pow3(x - 1.0))
271 }
272}
273
274pub struct ImageFilterKaiser {
278 a: f64,
279 i0a: f64,
280 epsilon: f64,
281}
282impl ImageFilterKaiser {
283 pub fn new(b: f64) -> Self {
284 let epsilon = 1e-12;
285 let i0a = 1.0 / Self::bessel_i0(b, epsilon);
286 Self { a: b, i0a, epsilon }
287 }
288
289 fn bessel_i0(x: f64, epsilon: f64) -> f64 {
290 let mut sum = 1.0;
291 let y = x * x / 4.0;
292 let mut t = y;
293 let mut i = 2;
294 while t > epsilon {
295 sum += t;
296 t *= y / (i * i) as f64;
297 i += 1;
298 }
299 sum
300 }
301}
302impl Default for ImageFilterKaiser {
303 fn default() -> Self {
304 Self::new(6.33)
305 }
306}
307impl ImageFilterFunction for ImageFilterKaiser {
308 fn radius(&self) -> f64 {
309 1.0
310 }
311 fn calc_weight(&self, x: f64) -> f64 {
312 Self::bessel_i0(self.a * (1.0 - x * x).sqrt(), self.epsilon) * self.i0a
313 }
314}
315
316pub struct ImageFilterCatrom;
318impl ImageFilterFunction for ImageFilterCatrom {
319 fn radius(&self) -> f64 {
320 2.0
321 }
322 fn calc_weight(&self, x: f64) -> f64 {
323 if x < 1.0 {
324 return 0.5 * (2.0 + x * x * (-5.0 + x * 3.0));
325 }
326 if x < 2.0 {
327 return 0.5 * (4.0 + x * (-8.0 + x * (5.0 - x)));
328 }
329 0.0
330 }
331}
332
333pub struct ImageFilterMitchell {
337 p0: f64,
338 p2: f64,
339 p3: f64,
340 q0: f64,
341 q1: f64,
342 q2: f64,
343 q3: f64,
344}
345impl ImageFilterMitchell {
346 pub fn new(b: f64, c: f64) -> Self {
347 Self {
348 p0: (6.0 - 2.0 * b) / 6.0,
349 p2: (-18.0 + 12.0 * b + 6.0 * c) / 6.0,
350 p3: (12.0 - 9.0 * b - 6.0 * c) / 6.0,
351 q0: (8.0 * b + 24.0 * c) / 6.0,
352 q1: (-12.0 * b - 48.0 * c) / 6.0,
353 q2: (6.0 * b + 30.0 * c) / 6.0,
354 q3: (-b - 6.0 * c) / 6.0,
355 }
356 }
357}
358impl Default for ImageFilterMitchell {
359 fn default() -> Self {
360 Self::new(1.0 / 3.0, 1.0 / 3.0)
361 }
362}
363impl ImageFilterFunction for ImageFilterMitchell {
364 fn radius(&self) -> f64 {
365 2.0
366 }
367 fn calc_weight(&self, x: f64) -> f64 {
368 if x < 1.0 {
369 return self.p0 + x * x * (self.p2 + x * self.p3);
370 }
371 if x < 2.0 {
372 return self.q0 + x * (self.q1 + x * (self.q2 + x * self.q3));
373 }
374 0.0
375 }
376}
377
378pub struct ImageFilterSpline16;
380impl ImageFilterFunction for ImageFilterSpline16 {
381 fn radius(&self) -> f64 {
382 2.0
383 }
384 fn calc_weight(&self, x: f64) -> f64 {
385 if x < 1.0 {
386 return ((x - 9.0 / 5.0) * x - 1.0 / 5.0) * x + 1.0;
387 }
388 ((-1.0 / 3.0 * (x - 1.0) + 4.0 / 5.0) * (x - 1.0) - 7.0 / 15.0) * (x - 1.0)
389 }
390}
391
392pub struct ImageFilterSpline36;
394impl ImageFilterFunction for ImageFilterSpline36 {
395 fn radius(&self) -> f64 {
396 3.0
397 }
398 fn calc_weight(&self, x: f64) -> f64 {
399 if x < 1.0 {
400 return ((13.0 / 11.0 * x - 453.0 / 209.0) * x - 3.0 / 209.0) * x + 1.0;
401 }
402 if x < 2.0 {
403 return ((-6.0 / 11.0 * (x - 1.0) + 270.0 / 209.0) * (x - 1.0) - 156.0 / 209.0)
404 * (x - 1.0);
405 }
406 ((1.0 / 11.0 * (x - 2.0) - 45.0 / 209.0) * (x - 2.0) + 26.0 / 209.0) * (x - 2.0)
407 }
408}
409
410pub struct ImageFilterGaussian;
412impl ImageFilterFunction for ImageFilterGaussian {
413 fn radius(&self) -> f64 {
414 2.0
415 }
416 fn calc_weight(&self, x: f64) -> f64 {
417 (-2.0 * x * x).exp() * (2.0 / PI).sqrt()
418 }
419}
420
421pub struct ImageFilterBessel;
423impl ImageFilterFunction for ImageFilterBessel {
424 fn radius(&self) -> f64 {
425 3.2383
426 }
427 fn calc_weight(&self, x: f64) -> f64 {
428 if x == 0.0 {
429 PI / 4.0
430 } else {
431 besj(PI * x, 1) / (2.0 * x)
432 }
433 }
434}
435
436pub struct ImageFilterSinc {
442 radius: f64,
443}
444impl ImageFilterSinc {
445 pub fn new(r: f64) -> Self {
446 Self {
447 radius: if r < 2.0 { 2.0 } else { r },
448 }
449 }
450}
451impl ImageFilterFunction for ImageFilterSinc {
452 fn radius(&self) -> f64 {
453 self.radius
454 }
455 fn calc_weight(&self, x: f64) -> f64 {
456 if x == 0.0 {
457 return 1.0;
458 }
459 let x = x * PI;
460 x.sin() / x
461 }
462}
463
464pub struct ImageFilterLanczos {
466 radius: f64,
467}
468impl ImageFilterLanczos {
469 pub fn new(r: f64) -> Self {
470 Self {
471 radius: if r < 2.0 { 2.0 } else { r },
472 }
473 }
474}
475impl ImageFilterFunction for ImageFilterLanczos {
476 fn radius(&self) -> f64 {
477 self.radius
478 }
479 fn calc_weight(&self, x: f64) -> f64 {
480 if x == 0.0 {
481 return 1.0;
482 }
483 if x > self.radius {
484 return 0.0;
485 }
486 let x = x * PI;
487 let xr = x / self.radius;
488 (x.sin() / x) * (xr.sin() / xr)
489 }
490}
491
492pub struct ImageFilterBlackman {
494 radius: f64,
495}
496impl ImageFilterBlackman {
497 pub fn new(r: f64) -> Self {
498 Self {
499 radius: if r < 2.0 { 2.0 } else { r },
500 }
501 }
502}
503impl ImageFilterFunction for ImageFilterBlackman {
504 fn radius(&self) -> f64 {
505 self.radius
506 }
507 fn calc_weight(&self, x: f64) -> f64 {
508 if x == 0.0 {
509 return 1.0;
510 }
511 if x > self.radius {
512 return 0.0;
513 }
514 let x = x * PI;
515 let xr = x / self.radius;
516 (x.sin() / x) * (0.42 + 0.5 * xr.cos() + 0.08 * (2.0 * xr).cos())
517 }
518}
519
520pub struct ImageFilterSinc36(ImageFilterSinc);
526impl ImageFilterSinc36 {
527 pub fn new() -> Self {
528 Self(ImageFilterSinc::new(3.0))
529 }
530}
531impl Default for ImageFilterSinc36 {
532 fn default() -> Self {
533 Self::new()
534 }
535}
536impl ImageFilterFunction for ImageFilterSinc36 {
537 fn radius(&self) -> f64 {
538 self.0.radius()
539 }
540 fn calc_weight(&self, x: f64) -> f64 {
541 self.0.calc_weight(x)
542 }
543}
544
545pub struct ImageFilterSinc64(ImageFilterSinc);
547impl ImageFilterSinc64 {
548 pub fn new() -> Self {
549 Self(ImageFilterSinc::new(4.0))
550 }
551}
552impl Default for ImageFilterSinc64 {
553 fn default() -> Self {
554 Self::new()
555 }
556}
557impl ImageFilterFunction for ImageFilterSinc64 {
558 fn radius(&self) -> f64 {
559 self.0.radius()
560 }
561 fn calc_weight(&self, x: f64) -> f64 {
562 self.0.calc_weight(x)
563 }
564}
565
566pub struct ImageFilterSinc100(ImageFilterSinc);
568impl ImageFilterSinc100 {
569 pub fn new() -> Self {
570 Self(ImageFilterSinc::new(5.0))
571 }
572}
573impl Default for ImageFilterSinc100 {
574 fn default() -> Self {
575 Self::new()
576 }
577}
578impl ImageFilterFunction for ImageFilterSinc100 {
579 fn radius(&self) -> f64 {
580 self.0.radius()
581 }
582 fn calc_weight(&self, x: f64) -> f64 {
583 self.0.calc_weight(x)
584 }
585}
586
587pub struct ImageFilterSinc144(ImageFilterSinc);
589impl ImageFilterSinc144 {
590 pub fn new() -> Self {
591 Self(ImageFilterSinc::new(6.0))
592 }
593}
594impl Default for ImageFilterSinc144 {
595 fn default() -> Self {
596 Self::new()
597 }
598}
599impl ImageFilterFunction for ImageFilterSinc144 {
600 fn radius(&self) -> f64 {
601 self.0.radius()
602 }
603 fn calc_weight(&self, x: f64) -> f64 {
604 self.0.calc_weight(x)
605 }
606}
607
608pub struct ImageFilterSinc196(ImageFilterSinc);
610impl ImageFilterSinc196 {
611 pub fn new() -> Self {
612 Self(ImageFilterSinc::new(7.0))
613 }
614}
615impl Default for ImageFilterSinc196 {
616 fn default() -> Self {
617 Self::new()
618 }
619}
620impl ImageFilterFunction for ImageFilterSinc196 {
621 fn radius(&self) -> f64 {
622 self.0.radius()
623 }
624 fn calc_weight(&self, x: f64) -> f64 {
625 self.0.calc_weight(x)
626 }
627}
628
629pub struct ImageFilterSinc256(ImageFilterSinc);
631impl ImageFilterSinc256 {
632 pub fn new() -> Self {
633 Self(ImageFilterSinc::new(8.0))
634 }
635}
636impl Default for ImageFilterSinc256 {
637 fn default() -> Self {
638 Self::new()
639 }
640}
641impl ImageFilterFunction for ImageFilterSinc256 {
642 fn radius(&self) -> f64 {
643 self.0.radius()
644 }
645 fn calc_weight(&self, x: f64) -> f64 {
646 self.0.calc_weight(x)
647 }
648}
649
650pub struct ImageFilterLanczos36(ImageFilterLanczos);
652impl ImageFilterLanczos36 {
653 pub fn new() -> Self {
654 Self(ImageFilterLanczos::new(3.0))
655 }
656}
657impl Default for ImageFilterLanczos36 {
658 fn default() -> Self {
659 Self::new()
660 }
661}
662impl ImageFilterFunction for ImageFilterLanczos36 {
663 fn radius(&self) -> f64 {
664 self.0.radius()
665 }
666 fn calc_weight(&self, x: f64) -> f64 {
667 self.0.calc_weight(x)
668 }
669}
670
671pub struct ImageFilterLanczos64(ImageFilterLanczos);
673impl ImageFilterLanczos64 {
674 pub fn new() -> Self {
675 Self(ImageFilterLanczos::new(4.0))
676 }
677}
678impl Default for ImageFilterLanczos64 {
679 fn default() -> Self {
680 Self::new()
681 }
682}
683impl ImageFilterFunction for ImageFilterLanczos64 {
684 fn radius(&self) -> f64 {
685 self.0.radius()
686 }
687 fn calc_weight(&self, x: f64) -> f64 {
688 self.0.calc_weight(x)
689 }
690}
691
692pub struct ImageFilterLanczos100(ImageFilterLanczos);
694impl ImageFilterLanczos100 {
695 pub fn new() -> Self {
696 Self(ImageFilterLanczos::new(5.0))
697 }
698}
699impl Default for ImageFilterLanczos100 {
700 fn default() -> Self {
701 Self::new()
702 }
703}
704impl ImageFilterFunction for ImageFilterLanczos100 {
705 fn radius(&self) -> f64 {
706 self.0.radius()
707 }
708 fn calc_weight(&self, x: f64) -> f64 {
709 self.0.calc_weight(x)
710 }
711}
712
713pub struct ImageFilterLanczos144(ImageFilterLanczos);
715impl ImageFilterLanczos144 {
716 pub fn new() -> Self {
717 Self(ImageFilterLanczos::new(6.0))
718 }
719}
720impl Default for ImageFilterLanczos144 {
721 fn default() -> Self {
722 Self::new()
723 }
724}
725impl ImageFilterFunction for ImageFilterLanczos144 {
726 fn radius(&self) -> f64 {
727 self.0.radius()
728 }
729 fn calc_weight(&self, x: f64) -> f64 {
730 self.0.calc_weight(x)
731 }
732}
733
734pub struct ImageFilterLanczos196(ImageFilterLanczos);
736impl ImageFilterLanczos196 {
737 pub fn new() -> Self {
738 Self(ImageFilterLanczos::new(7.0))
739 }
740}
741impl Default for ImageFilterLanczos196 {
742 fn default() -> Self {
743 Self::new()
744 }
745}
746impl ImageFilterFunction for ImageFilterLanczos196 {
747 fn radius(&self) -> f64 {
748 self.0.radius()
749 }
750 fn calc_weight(&self, x: f64) -> f64 {
751 self.0.calc_weight(x)
752 }
753}
754
755pub struct ImageFilterLanczos256(ImageFilterLanczos);
757impl ImageFilterLanczos256 {
758 pub fn new() -> Self {
759 Self(ImageFilterLanczos::new(8.0))
760 }
761}
762impl Default for ImageFilterLanczos256 {
763 fn default() -> Self {
764 Self::new()
765 }
766}
767impl ImageFilterFunction for ImageFilterLanczos256 {
768 fn radius(&self) -> f64 {
769 self.0.radius()
770 }
771 fn calc_weight(&self, x: f64) -> f64 {
772 self.0.calc_weight(x)
773 }
774}
775
776pub struct ImageFilterBlackman36(ImageFilterBlackman);
778impl ImageFilterBlackman36 {
779 pub fn new() -> Self {
780 Self(ImageFilterBlackman::new(3.0))
781 }
782}
783impl Default for ImageFilterBlackman36 {
784 fn default() -> Self {
785 Self::new()
786 }
787}
788impl ImageFilterFunction for ImageFilterBlackman36 {
789 fn radius(&self) -> f64 {
790 self.0.radius()
791 }
792 fn calc_weight(&self, x: f64) -> f64 {
793 self.0.calc_weight(x)
794 }
795}
796
797pub struct ImageFilterBlackman64(ImageFilterBlackman);
799impl ImageFilterBlackman64 {
800 pub fn new() -> Self {
801 Self(ImageFilterBlackman::new(4.0))
802 }
803}
804impl Default for ImageFilterBlackman64 {
805 fn default() -> Self {
806 Self::new()
807 }
808}
809impl ImageFilterFunction for ImageFilterBlackman64 {
810 fn radius(&self) -> f64 {
811 self.0.radius()
812 }
813 fn calc_weight(&self, x: f64) -> f64 {
814 self.0.calc_weight(x)
815 }
816}
817
818pub struct ImageFilterBlackman100(ImageFilterBlackman);
820impl ImageFilterBlackman100 {
821 pub fn new() -> Self {
822 Self(ImageFilterBlackman::new(5.0))
823 }
824}
825impl Default for ImageFilterBlackman100 {
826 fn default() -> Self {
827 Self::new()
828 }
829}
830impl ImageFilterFunction for ImageFilterBlackman100 {
831 fn radius(&self) -> f64 {
832 self.0.radius()
833 }
834 fn calc_weight(&self, x: f64) -> f64 {
835 self.0.calc_weight(x)
836 }
837}
838
839pub struct ImageFilterBlackman144(ImageFilterBlackman);
841impl ImageFilterBlackman144 {
842 pub fn new() -> Self {
843 Self(ImageFilterBlackman::new(6.0))
844 }
845}
846impl Default for ImageFilterBlackman144 {
847 fn default() -> Self {
848 Self::new()
849 }
850}
851impl ImageFilterFunction for ImageFilterBlackman144 {
852 fn radius(&self) -> f64 {
853 self.0.radius()
854 }
855 fn calc_weight(&self, x: f64) -> f64 {
856 self.0.calc_weight(x)
857 }
858}
859
860pub struct ImageFilterBlackman196(ImageFilterBlackman);
862impl ImageFilterBlackman196 {
863 pub fn new() -> Self {
864 Self(ImageFilterBlackman::new(7.0))
865 }
866}
867impl Default for ImageFilterBlackman196 {
868 fn default() -> Self {
869 Self::new()
870 }
871}
872impl ImageFilterFunction for ImageFilterBlackman196 {
873 fn radius(&self) -> f64 {
874 self.0.radius()
875 }
876 fn calc_weight(&self, x: f64) -> f64 {
877 self.0.calc_weight(x)
878 }
879}
880
881pub struct ImageFilterBlackman256(ImageFilterBlackman);
883impl ImageFilterBlackman256 {
884 pub fn new() -> Self {
885 Self(ImageFilterBlackman::new(8.0))
886 }
887}
888impl Default for ImageFilterBlackman256 {
889 fn default() -> Self {
890 Self::new()
891 }
892}
893impl ImageFilterFunction for ImageFilterBlackman256 {
894 fn radius(&self) -> f64 {
895 self.0.radius()
896 }
897 fn calc_weight(&self, x: f64) -> f64 {
898 self.0.calc_weight(x)
899 }
900}
901
902#[cfg(test)]
907mod tests {
908 use super::*;
909
910 #[test]
911 fn test_bilinear_radius_and_weight() {
912 let f = ImageFilterBilinear;
913 assert_eq!(f.radius(), 1.0);
914 assert_eq!(f.calc_weight(0.0), 1.0);
915 assert_eq!(f.calc_weight(0.5), 0.5);
916 assert_eq!(f.calc_weight(1.0), 0.0);
917 }
918
919 #[test]
920 fn test_hermite_at_zero() {
921 let f = ImageFilterHermite;
922 assert_eq!(f.calc_weight(0.0), 1.0);
923 }
924
925 #[test]
926 fn test_bicubic_at_zero() {
927 let f = ImageFilterBicubic;
928 let w = f.calc_weight(0.0);
929 let expected = 2.0 / 3.0;
931 assert!(
932 (w - expected).abs() < 1e-10,
933 "bicubic at 0 should be ~{expected}, got {w}"
934 );
935 }
936
937 #[test]
938 fn test_gaussian_at_zero() {
939 let f = ImageFilterGaussian;
940 let expected = (2.0 / PI).sqrt();
941 assert!((f.calc_weight(0.0) - expected).abs() < 1e-10);
942 }
943
944 #[test]
945 fn test_kaiser_at_zero() {
946 let f = ImageFilterKaiser::default();
947 let w = f.calc_weight(0.0);
948 assert!(
949 (w - 1.0).abs() < 1e-10,
950 "kaiser at 0 should be ~1.0, got {w}"
951 );
952 }
953
954 #[test]
955 fn test_mitchell_at_zero() {
956 let f = ImageFilterMitchell::default();
957 let w = f.calc_weight(0.0);
958 let expected = f.p0; assert!((w - expected).abs() < 1e-10);
960 }
961
962 #[test]
963 fn test_sinc_at_zero() {
964 let f = ImageFilterSinc::new(3.0);
965 assert_eq!(f.calc_weight(0.0), 1.0);
966 assert_eq!(f.radius(), 3.0);
967 }
968
969 #[test]
970 fn test_sinc_minimum_radius() {
971 let f = ImageFilterSinc::new(1.0);
972 assert_eq!(f.radius(), 2.0); }
974
975 #[test]
976 fn test_lanczos_at_zero() {
977 let f = ImageFilterLanczos::new(3.0);
978 assert_eq!(f.calc_weight(0.0), 1.0);
979 }
980
981 #[test]
982 fn test_lanczos_beyond_radius() {
983 let f = ImageFilterLanczos::new(3.0);
984 assert_eq!(f.calc_weight(4.0), 0.0);
985 }
986
987 #[test]
988 fn test_blackman_at_zero() {
989 let f = ImageFilterBlackman::new(3.0);
990 assert_eq!(f.calc_weight(0.0), 1.0);
991 }
992
993 #[test]
994 fn test_lut_bilinear() {
995 let lut = ImageFilterLut::new_with_filter(&ImageFilterBilinear, true);
996 assert_eq!(lut.radius(), 1.0);
997 assert_eq!(lut.diameter(), 2);
998 assert_eq!(lut.start(), 0); assert_eq!(lut.weight_array().len(), 512);
1001 }
1002
1003 #[test]
1004 fn test_lut_weight_symmetry() {
1005 let lut = ImageFilterLut::new_with_filter(&ImageFilterBilinear, false);
1006 let weights = lut.weight_array();
1007 let pivot = (lut.diameter() as usize) << (IMAGE_SUBPIXEL_SHIFT as usize - 1);
1008 for i in 1..pivot {
1010 assert_eq!(
1011 weights[pivot + i],
1012 weights[pivot - i],
1013 "asymmetry at offset {i}"
1014 );
1015 }
1016 }
1017
1018 #[test]
1019 fn test_lut_normalization() {
1020 let lut = ImageFilterLut::new_with_filter(&ImageFilterBilinear, true);
1021 let weights = lut.weight_array();
1022 let diameter = lut.diameter() as usize;
1023 let subpixel_scale = IMAGE_SUBPIXEL_SCALE as usize;
1024 for i in 0..subpixel_scale {
1026 let sum: i32 = (0..diameter)
1027 .map(|j| weights[j * subpixel_scale + i] as i32)
1028 .sum();
1029 assert_eq!(sum, IMAGE_FILTER_SCALE, "sum at offset {i} = {sum}");
1030 }
1031 }
1032
1033 #[test]
1034 fn test_lut_bicubic() {
1035 let lut = ImageFilterLut::new_with_filter(&ImageFilterBicubic, true);
1036 assert_eq!(lut.diameter(), 4);
1037 assert_eq!(lut.start(), -1); }
1039
1040 #[test]
1041 fn test_convenience_wrappers() {
1042 assert_eq!(ImageFilterSinc36::new().radius(), 3.0);
1044 assert_eq!(ImageFilterSinc64::new().radius(), 4.0);
1045 assert_eq!(ImageFilterLanczos36::new().radius(), 3.0);
1046 assert_eq!(ImageFilterLanczos256::new().radius(), 8.0);
1047 assert_eq!(ImageFilterBlackman36::new().radius(), 3.0);
1048 assert_eq!(ImageFilterBlackman256::new().radius(), 8.0);
1049 }
1050
1051 #[test]
1052 fn test_bessel_filter() {
1053 let f = ImageFilterBessel;
1054 assert_eq!(f.radius(), 3.2383);
1055 let w0 = f.calc_weight(0.0);
1056 assert!((w0 - PI / 4.0).abs() < 1e-10);
1057 }
1058
1059 #[test]
1060 fn test_quadric_regions() {
1061 let f = ImageFilterQuadric;
1062 assert_eq!(f.calc_weight(0.0), 0.75);
1064 assert!((f.calc_weight(0.5) - 0.5).abs() < 1e-10);
1066 assert_eq!(f.calc_weight(1.5), 0.0);
1068 }
1069
1070 #[test]
1071 fn test_catrom_at_zero() {
1072 let f = ImageFilterCatrom;
1073 assert_eq!(f.calc_weight(0.0), 1.0);
1074 }
1075
1076 #[test]
1077 fn test_hanning_at_zero() {
1078 let f = ImageFilterHanning;
1079 assert_eq!(f.calc_weight(0.0), 1.0);
1080 }
1081
1082 #[test]
1083 fn test_spline16_at_zero() {
1084 let f = ImageFilterSpline16;
1085 assert_eq!(f.calc_weight(0.0), 1.0);
1086 }
1087}