NumeRe v1.1.4
NumeRe: Framework für Numerische Rechnungen
resampler.cpp
Go to the documentation of this file.
1// resampler.cpp, Separable filtering image rescaler v2.21, Rich Geldreich - richgel99@gmail.com
2// See unlicense at the bottom of resampler.h, or at http://unlicense.org/
3//
4// Feb. 1996: Creation, losely based on a heavily bugfixed version of Schumacher's resampler in Graphics Gems 3.
5// Oct. 2000: Ported to C++, tweaks.
6// May 2001: Continous to discrete mapping, box filter tweaks.
7// March 9, 2002: Kaiser filter grabbed from Jonathan Blow's GD magazine mipmap sample code.
8// Sept. 8, 2002: Comments cleaned up a bit.
9// Dec. 31, 2008: v2.2: Bit more cleanup, released as public domain.
10// June 4, 2012: v2.21: Switched to unlicense.org, integrated GCC fixes supplied by Peter Nagy <petern@crytek.com>, Anteru at anteru.net, and clay@coge.net,
11// added Codeblocks project (for testing with MinGW and GCC), VS2008 static code analysis pass.
12#include <stdlib.h>
13#include <math.h>
14#include <float.h>
15#include <assert.h>
16#include <string.h>
17#include <iostream>
18#include "resampler.h"
19
20#define resampler_assert assert
21
22static inline int resampler_range_check(int v, int h)
23{
24 (void)h;
25 resampler_assert((v >= 0) && (v < h));
26 return v;
27}
28
29#ifndef max
30#define max(a,b) (((a) > (b)) ? (a) : (b))
31#endif
32
33#ifndef min
34#define min(a,b) (((a) < (b)) ? (a) : (b))
35#endif
36
37#ifndef TRUE
38#define TRUE (1)
39#endif
40
41#ifndef FALSE
42#define FALSE (0)
43#endif
44
45#define RESAMPLER_DEBUG 0
46
47#define M_PI 3.14159265358979323846
48
49// Float to int cast with truncation.
50static inline int cast_to_int(Resample_Real i)
51{
52 return (int)i;
53}
54
55// (x mod y) with special handling for negative x values.
56static inline int posmod(int x, int y)
57{
58 if (x >= 0)
59 return (x % y);
60 else
61 {
62 int m = (-x) % y;
63
64 if (m != 0)
65 m = y - m;
66
67 return (m);
68 }
69}
70
71// To add your own filter, insert the new function below and update the filter table.
72// There is no need to make the filter function particularly fast, because it's
73// only called during initializing to create the X and Y axis contributor tables.
74
75#define BOX_FILTER_SUPPORT (0.5f)
76static Resample_Real box_filter(Resample_Real t) /* pulse/Fourier window */
77{
78 // make_clist() calls the filter function with t inverted (pos = left, neg = right)
79 if ((t >= -0.5f) && (t < 0.5f))
80 return 1.0f;
81 else
82 return 0.0f;
83}
84
85#define TENT_FILTER_SUPPORT (1.0f)
86static Resample_Real tent_filter(Resample_Real t) /* box (*) box, bilinear/triangle */
87{
88 if (t < 0.0f)
89 t = -t;
90
91 if (t < 1.0f)
92 return 1.0f - t;
93 else
94 return 0.0f;
95}
96
97#define BELL_SUPPORT (1.5f)
98static Resample_Real bell_filter(Resample_Real t) /* box (*) box (*) box */
99{
100 if (t < 0.0f)
101 t = -t;
102
103 if (t < .5f)
104 return (.75f - (t * t));
105
106 if (t < 1.5f)
107 {
108 t = (t - 1.5f);
109 return (.5f * (t * t));
110 }
111
112 return (0.0f);
113}
114
115#define B_SPLINE_SUPPORT (2.0f)
116static Resample_Real B_spline_filter(Resample_Real t) /* box (*) box (*) box (*) box */
117{
118 Resample_Real tt;
119
120 if (t < 0.0f)
121 t = -t;
122
123 if (t < 1.0f)
124 {
125 tt = t * t;
126 return ((.5f * tt * t) - tt + (2.0f / 3.0f));
127 }
128 else if (t < 2.0f)
129 {
130 t = 2.0f - t;
131 return ((1.0f / 6.0f) * (t * t * t));
132 }
133
134 return (0.0f);
135}
136
137// Dodgson, N., "Quadratic Interpolation for Image Resampling"
138#define QUADRATIC_SUPPORT 1.5f
140{
141 if (t < 0.0f)
142 t = -t;
143 if (t < QUADRATIC_SUPPORT)
144 {
145 Resample_Real tt = t * t;
146 if (t <= .5f)
147 return (-2.0f * R) * tt + .5f * (R + 1.0f);
148 else
149 return (R * tt) + (-2.0f * R - .5f) * t + (3.0f / 4.0f) * (R + 1.0f);
150 }
151 else
152 return 0.0f;
153}
154
156{
157 return quadratic(t, 1.0f);
158}
159
161{
162 return quadratic(t, .5f);
163}
164
166{
167 return quadratic(t, .8f);
168}
169
170// Mitchell, D. and A. Netravali, "Reconstruction Filters in Computer Graphics."
171// Computer Graphics, Vol. 22, No. 4, pp. 221-228.
172// (B, C)
173// (1/3, 1/3) - Defaults recommended by Mitchell and Netravali
174// (1, 0) - Equivalent to the Cubic B-Spline
175// (0, 0.5) - Equivalent to the Catmull-Rom Spline
176// (0, C) - The family of Cardinal Cubic Splines
177// (B, 0) - Duff's tensioned B-Splines.
179{
180 Resample_Real tt;
181
182 tt = t * t;
183
184 if (t < 0.0f)
185 t = -t;
186
187 if (t < 1.0f)
188 {
189 t = (((12.0f - 9.0f * B - 6.0f * C) * (t * tt))
190 + ((-18.0f + 12.0f * B + 6.0f * C) * tt)
191 + (6.0f - 2.0f * B));
192
193 return (t / 6.0f);
194 }
195 else if (t < 2.0f)
196 {
197 t = (((-1.0f * B - 6.0f * C) * (t * tt))
198 + ((6.0f * B + 30.0f * C) * tt)
199 + ((-12.0f * B - 48.0f * C) * t)
200 + (8.0f * B + 24.0f * C));
201
202 return (t / 6.0f);
203 }
204
205 return (0.0f);
206}
207
208#define MITCHELL_SUPPORT (2.0f)
210{
211 return mitchell(t, 1.0f / 3.0f, 1.0f / 3.0f);
212}
213
214#define CATMULL_ROM_SUPPORT (2.0f)
216{
217 return mitchell(t, 0.0f, .5f);
218}
219
220static double sinc(double x)
221{
222 x = (x * M_PI);
223
224 if ((x < 0.01f) && (x > -0.01f))
225 return 1.0f + x * x * (-1.0f / 6.0f + x * x * 1.0f / 120.0f);
226
227 return sin(x) / x;
228}
229
230static Resample_Real clean(double t)
231{
232 const Resample_Real EPSILON = .0000125f;
233 if (fabs(t) < EPSILON)
234 return 0.0f;
235 return (Resample_Real)t;
236}
237
238//static double blackman_window(double x)
239//{
240// return .42f + .50f * cos(M_PI*x) + .08f * cos(2.0f*M_PI*x);
241//}
242
243static double blackman_exact_window(double x)
244{
245 return 0.42659071f + 0.49656062f * cos(M_PI * x) + 0.07684867f * cos(2.0f * M_PI * x);
246}
247
248#define BLACKMAN_SUPPORT (3.0f)
250{
251 if (t < 0.0f)
252 t = -t;
253
254 if (t < 3.0f)
255 //return clean(sinc(t) * blackman_window(t / 3.0f));
256 return clean(sinc(t) * blackman_exact_window(t / 3.0f));
257 else
258 return (0.0f);
259}
260
261#define GAUSSIAN_SUPPORT (1.25f)
262static Resample_Real gaussian_filter(Resample_Real t) // with blackman window
263{
264 if (t < 0)
265 t = -t;
266 if (t < GAUSSIAN_SUPPORT)
267 return clean(exp(-2.0f * t * t) * sqrt(2.0f / M_PI) * blackman_exact_window(t / GAUSSIAN_SUPPORT));
268 else
269 return 0.0f;
270}
271
272// Windowed sinc -- see "Jimm Blinn's Corner: Dirty Pixels" pg. 26.
273#define LANCZOS3_SUPPORT (3.0f)
275{
276 if (t < 0.0f)
277 t = -t;
278
279 if (t < 3.0f)
280 return clean(sinc(t) * sinc(t / 3.0f));
281 else
282 return (0.0f);
283}
284
285#define LANCZOS4_SUPPORT (4.0f)
287{
288 if (t < 0.0f)
289 t = -t;
290
291 if (t < 4.0f)
292 return clean(sinc(t) * sinc(t / 4.0f));
293 else
294 return (0.0f);
295}
296
297#define LANCZOS6_SUPPORT (6.0f)
299{
300 if (t < 0.0f)
301 t = -t;
302
303 if (t < 6.0f)
304 return clean(sinc(t) * sinc(t / 6.0f));
305 else
306 return (0.0f);
307}
308
309#define LANCZOS12_SUPPORT (12.0f)
311{
312 if (t < 0.0f)
313 t = -t;
314
315 if (t < 12.0f)
316 return clean(sinc(t) * sinc(t / 12.0f));
317 else
318 return (0.0f);
319}
320
321static double bessel0(double x)
322{
323 const double EPSILON_RATIO = 1E-16;
324 double xh, sum, pow, ds;
325 int k;
326
327 xh = 0.5 * x;
328 sum = 1.0;
329 pow = 1.0;
330 k = 0;
331 ds = 1.0;
332 while (ds > sum * EPSILON_RATIO)
333 {
334 ++k;
335 pow = pow * (xh / k);
336 ds = pow * pow;
337 sum = sum + ds;
338 }
339
340 return sum;
341}
342
343static const Resample_Real KAISER_ALPHA = 4.0;
344static double kaiser(double alpha, double half_width, double x)
345{
346 const double ratio = (x / half_width);
347 return bessel0(alpha * sqrt(1 - ratio * ratio)) / bessel0(alpha);
348}
349
350#define KAISER_SUPPORT 3
352{
353 if (t < 0.0f)
354 t = -t;
355
356 if (t < KAISER_SUPPORT)
357 {
358 // db atten
359 const Resample_Real att = 40.0f;
360 const Resample_Real alpha = (Resample_Real)(exp(log((double)0.58417 * (att - 20.96)) * 0.4) + 0.07886 * (att - 20.96));
361 //const Resample_Real alpha = KAISER_ALPHA;
362 return (Resample_Real)clean(sinc(t) * kaiser(alpha, KAISER_SUPPORT, t));
363 }
364
365 return 0.0f;
366}
367
368// filters[] is a list of all the available filter functions.
369static struct
370{
371 char name[32];
374} g_filters[] =
375{
378 {"bell", bell_filter, BELL_SUPPORT },
379 {"bspline", B_spline_filter, B_SPLINE_SUPPORT },
380 {"mitchell", mitchell_filter, MITCHELL_SUPPORT },
381 {"lanczos3", lanczos3_filter, LANCZOS3_SUPPORT },
382 {"blackman", blackman_filter, BLACKMAN_SUPPORT },
383 {"lanczos4", lanczos4_filter, LANCZOS4_SUPPORT },
384 {"lanczos6", lanczos6_filter, LANCZOS6_SUPPORT },
385 {"lanczos12", lanczos12_filter, LANCZOS12_SUPPORT },
386 {"kaiser", kaiser_filter, KAISER_SUPPORT },
387 {"gaussian", gaussian_filter, GAUSSIAN_SUPPORT },
388 {"catmullrom", catmull_rom_filter, CATMULL_ROM_SUPPORT },
389 {"quadratic_interp", quadratic_interp_filter, QUADRATIC_SUPPORT },
390 {"quadratic_approx", quadratic_approx_filter, QUADRATIC_SUPPORT },
391 {"quadratic_mix", quadratic_mix_filter, QUADRATIC_SUPPORT },
393
394static const int NUM_FILTERS = sizeof(g_filters) / sizeof(g_filters[0]);
395
396/* Ensure that the contributing source sample is
397* within bounds. If not, reflect, clamp, or wrap.
398*/
399int Resampler::reflect(const int j, const int src_x, const Boundary_Op boundary_op)
400{
401 int n;
402
403 if (j < 0)
404 {
405 if (boundary_op == BOUNDARY_REFLECT)
406 {
407 n = -j;
408
409 if (n >= src_x)
410 n = src_x - 1;
411 }
412 else if (boundary_op == BOUNDARY_WRAP)
413 n = posmod(j, src_x);
414 else
415 n = 0;
416 }
417 else if (j >= src_x)
418 {
419 if (boundary_op == BOUNDARY_REFLECT)
420 {
421 n = (src_x - j) + (src_x - 1);
422
423 if (n < 0)
424 n = 0;
425 }
426 else if (boundary_op == BOUNDARY_WRAP)
427 n = posmod(j, src_x);
428 else
429 n = src_x - 1;
430 }
431 else
432 n = j;
433
434 return n;
435}
436
437// The make_clist() method generates, for all destination samples,
438// the list of all source samples with non-zero weighted contributions.
440 int src_x, int dst_x, Boundary_Op boundary_op,
441 Resample_Real (*Pfilter)(Resample_Real),
442 Resample_Real filter_support,
443 Resample_Real filter_scale,
444 Resample_Real src_ofs)
445{
446 typedef struct
447 {
448 // The center of the range in DISCRETE coordinates (pixel center = 0.0f).
449 Resample_Real center;
450 int left, right;
451 } Contrib_Bounds;
452
453 int i, j, k, n, left, right;
454 Resample_Real total_weight;
455 Resample_Real xscale, center, half_width, weight;
456 Contrib_List* Pcontrib;
457 Contrib* Pcpool;
458 Contrib* Pcpool_next;
459 Contrib_Bounds* Pcontrib_bounds;
460
461 if ((Pcontrib = (Contrib_List*)calloc(dst_x, sizeof(Contrib_List))) == NULL)
462 return NULL;
463
464 Pcontrib_bounds = (Contrib_Bounds*)calloc(dst_x, sizeof(Contrib_Bounds));
465 if (!Pcontrib_bounds)
466 {
467 free(Pcontrib);
468 return (NULL);
469 }
470
471 const Resample_Real oo_filter_scale = 1.0f / filter_scale;
472
473 const Resample_Real NUDGE = 0.5f;
474 xscale = dst_x / (Resample_Real)src_x;
475
476 if (xscale < 1.0f)
477 {
478 int total;
479 (void)total;
480
481 /* Handle case when there are fewer destination
482 * samples than source samples (downsampling/minification).
483 */
484
485 // stretched half width of filter
486 half_width = (filter_support / xscale) * filter_scale;
487
488 // Find the range of source sample(s) that will contribute to each destination sample.
489
490 for (i = 0, n = 0; i < dst_x; i++)
491 {
492 // Convert from discrete to continuous coordinates, scale, then convert back to discrete.
493 center = ((Resample_Real)i + NUDGE) / xscale;
494 center -= NUDGE;
495 center += src_ofs;
496
497 left = cast_to_int((Resample_Real)floor(center - half_width));
498 right = cast_to_int((Resample_Real)ceil(center + half_width));
499
500 Pcontrib_bounds[i].center = center;
501 Pcontrib_bounds[i].left = left;
502 Pcontrib_bounds[i].right = right;
503
504 n += (right - left + 1);
505 }
506
507 /* Allocate memory for contributors. */
508
509 if ((n == 0) || ((Pcpool = (Contrib*)calloc(n, sizeof(Contrib))) == NULL))
510 {
511 free(Pcontrib);
512 free(Pcontrib_bounds);
513 return NULL;
514 }
515 total = n;
516
517 Pcpool_next = Pcpool;
518
519 /* Create the list of source samples which
520 * contribute to each destination sample.
521 */
522
523 for (i = 0; i < dst_x; i++)
524 {
525 int max_k = -1;
526 Resample_Real max_w = -1e+20f;
527
528 center = Pcontrib_bounds[i].center;
529 left = Pcontrib_bounds[i].left;
530 right = Pcontrib_bounds[i].right;
531
532 Pcontrib[i].n = 0;
533 Pcontrib[i].p = Pcpool_next;
534 Pcpool_next += (right - left + 1);
535 resampler_assert ((Pcpool_next - Pcpool) <= total);
536
537 total_weight = 0;
538
539 for (j = left; j <= right; j++)
540 total_weight += (*Pfilter)((center - (Resample_Real)j) * xscale * oo_filter_scale);
541 const Resample_Real norm = static_cast<Resample_Real>(1.0f / total_weight);
542
543 total_weight = 0;
544
545#if RESAMPLER_DEBUG
546 printf("%i: ", i);
547#endif
548
549 for (j = left; j <= right; j++)
550 {
551 weight = (*Pfilter)((center - (Resample_Real)j) * xscale * oo_filter_scale) * norm;
552 if (weight == 0.0f)
553 continue;
554
555 n = reflect(j, src_x, boundary_op);
556
557#if RESAMPLER_DEBUG
558 printf("%i(%f), ", n, weight);
559#endif
560
561 /* Increment the number of source
562 * samples which contribute to the
563 * current destination sample.
564 */
565
566 k = Pcontrib[i].n++;
567
568 Pcontrib[i].p[k].pixel = (unsigned short)(n); /* store src sample number */
569 Pcontrib[i].p[k].weight = weight; /* store src sample weight */
570
571 total_weight += weight; /* total weight of all contributors */
572
573 if (weight > max_w)
574 {
575 max_w = weight;
576 max_k = k;
577 }
578 }
579
580#if RESAMPLER_DEBUG
581 printf("\n\n");
582#endif
583
584 //resampler_assert(Pcontrib[i].n);
585 //resampler_assert(max_k != -1);
586 if ((max_k == -1) || (Pcontrib[i].n == 0))
587 {
588 free(Pcpool);
589 free(Pcontrib);
590 free(Pcontrib_bounds);
591 return NULL;
592 }
593
594 if (total_weight != 1.0f)
595 Pcontrib[i].p[max_k].weight += 1.0f - total_weight;
596 }
597 }
598 else
599 {
600 /* Handle case when there are more
601 * destination samples than source
602 * samples (upsampling).
603 */
604
605 half_width = filter_support * filter_scale;
606
607 // Find the source sample(s) that contribute to each destination sample.
608
609 for (i = 0, n = 0; i < dst_x; i++)
610 {
611 // Convert from discrete to continuous coordinates, scale, then convert back to discrete.
612 center = ((Resample_Real)i + NUDGE) / xscale;
613 center -= NUDGE;
614 center += src_ofs;
615
616 left = cast_to_int((Resample_Real)floor(center - half_width));
617 right = cast_to_int((Resample_Real)ceil(center + half_width));
618
619 Pcontrib_bounds[i].center = center;
620 Pcontrib_bounds[i].left = left;
621 Pcontrib_bounds[i].right = right;
622
623 n += (right - left + 1);
624 }
625
626 /* Allocate memory for contributors. */
627
628 int total = n;
629 if ((total == 0) || ((Pcpool = (Contrib*)calloc(total, sizeof(Contrib))) == NULL))
630 {
631 free(Pcontrib);
632 free(Pcontrib_bounds);
633 return NULL;
634 }
635
636 Pcpool_next = Pcpool;
637
638 /* Create the list of source samples which
639 * contribute to each destination sample.
640 */
641
642 for (i = 0; i < dst_x; i++)
643 {
644 int max_k = -1;
645 Resample_Real max_w = -1e+20f;
646
647 center = Pcontrib_bounds[i].center;
648 left = Pcontrib_bounds[i].left;
649 right = Pcontrib_bounds[i].right;
650
651 Pcontrib[i].n = 0;
652 Pcontrib[i].p = Pcpool_next;
653 Pcpool_next += (right - left + 1);
654 resampler_assert((Pcpool_next - Pcpool) <= total);
655
656 total_weight = 0;
657 for (j = left; j <= right; j++)
658 total_weight += (*Pfilter)((center - (Resample_Real)j) * oo_filter_scale);
659
660 const Resample_Real norm = static_cast<Resample_Real>(1.0f / total_weight);
661
662 total_weight = 0;
663
664#if RESAMPLER_DEBUG
665 printf("%i: ", i);
666#endif
667
668 for (j = left; j <= right; j++)
669 {
670 weight = (*Pfilter)((center - (Resample_Real)j) * oo_filter_scale) * norm;
671 if (weight == 0.0f)
672 continue;
673
674 n = reflect(j, src_x, boundary_op);
675
676#if RESAMPLER_DEBUG
677 printf("%i(%f), ", n, weight);
678#endif
679
680 /* Increment the number of source
681 * samples which contribute to the
682 * current destination sample.
683 */
684
685 k = Pcontrib[i].n++;
686
687 Pcontrib[i].p[k].pixel = (unsigned short)(n); /* store src sample number */
688 Pcontrib[i].p[k].weight = weight; /* store src sample weight */
689
690 total_weight += weight; /* total weight of all contributors */
691
692 if (weight > max_w)
693 {
694 max_w = weight;
695 max_k = k;
696 }
697 }
698
699#if RESAMPLER_DEBUG
700 printf("\n\n");
701#endif
702
703 //resampler_assert(Pcontrib[i].n);
704 //resampler_assert(max_k != -1);
705
706 if ((max_k == -1) || (Pcontrib[i].n == 0))
707 {
708 free(Pcpool);
709 free(Pcontrib);
710 free(Pcontrib_bounds);
711 return NULL;
712 }
713
714 if (total_weight != 1.0f)
715 Pcontrib[i].p[max_k].weight += 1.0f - total_weight;
716 }
717 }
718
719#if RESAMPLER_DEBUG
720 printf("*******\n");
721#endif
722
723 free(Pcontrib_bounds);
724
725 return Pcontrib;
726}
727
728void Resampler::resample_x(Sample* Pdst, const Sample* Psrc)
729{
730 resampler_assert(Pdst);
731 resampler_assert(Psrc);
732
733 int i, j;
734 Sample total;
735 Contrib_List* Pclist = m_Pclist_x;
736 Contrib* p;
737
738 for (i = m_resample_dst_x; i > 0; i--, Pclist++)
739 {
740#if RESAMPLER_DEBUG_OPS
741 total_ops += Pclist->n;
742#endif
743
744 for (j = Pclist->n, p = Pclist->p, total = 0; j > 0; j--, p++)
745 {
746 if (!isnan(Psrc[p->pixel]))
747 total += Psrc[p->pixel] * p->weight;
748 //std::cout << Psrc[p->pixel] << " " << p->pixel << " " << p->weight << " ";
749 }
750 //std::cout << std::endl;
751 *Pdst++ = total;
752 }
753}
754
755void Resampler::scale_y_mov(Sample* Ptmp, const Sample* Psrc, Resample_Real weight, int dst_x)
756{
757 int i;
758
759#if RESAMPLER_DEBUG_OPS
760 total_ops += dst_x;
761#endif
762
763 // Not += because temp buf wasn't cleared.
764 for (i = dst_x; i > 0; i--)
765 *Ptmp++ = *Psrc++ * weight;
766}
767
768void Resampler::scale_y_add(Sample* Ptmp, const Sample* Psrc, Resample_Real weight, int dst_x)
769{
770#if RESAMPLER_DEBUG_OPS
771 total_ops += dst_x;
772#endif
773
774 for (int i = dst_x; i > 0; i--)
775 (*Ptmp++) += *Psrc++ * weight;
776}
777
778void Resampler::clamp(Sample* Pdst, int n)
779{
780 while (n > 0)
781 {
782 *Pdst = clamp_sample(*Pdst);
783 ++Pdst;
784 n--;
785 }
786}
787
789{
790 int i, j;
791 Sample* Psrc;
793
794 Sample* Ptmp = m_delay_x_resample ? m_Ptmp_buf : Pdst;
795 resampler_assert(Ptmp);
796
797 /* Process each contributor. */
798
799 for (i = 0; i < Pclist->n; i++)
800 {
801 /* locate the contributor's location in the scan
802 * buffer -- the contributor must always be found!
803 */
804
805 for (j = 0; j < MAX_SCAN_BUF_SIZE; j++)
806 if (m_Pscan_buf->scan_buf_y[j] == Pclist->p[i].pixel)
807 break;
808
810
811 Psrc = m_Pscan_buf->scan_buf_l[j];
812
813 if (!i)
814 scale_y_mov(Ptmp, Psrc, Pclist->p[i].weight, m_intermediate_x);
815 else
816 scale_y_add(Ptmp, Psrc, Pclist->p[i].weight, m_intermediate_x);
817
818 /* If this source line doesn't contribute to any
819 * more destination lines then mark the scanline buffer slot
820 * which holds this source line as free.
821 * (The max. number of slots used depends on the Y
822 * axis sampling factor and the scaled filter width.)
823 */
824
826 {
828 m_Pscan_buf->scan_buf_y[j] = -1;
829 }
830 }
831
832 /* Now generate the destination line */
833
834 if (m_delay_x_resample) // Was X resampling delayed until after Y resampling?
835 {
836 resampler_assert(Pdst != Ptmp);
837 resample_x(Pdst, Ptmp);
838 }
839 else
840 {
841 resampler_assert(Pdst == Ptmp);
842 }
843
844 if (m_lo < m_hi)
845 clamp(Pdst, m_resample_dst_x);
846}
847
849{
850 int i;
851
853 return false;
854
855 /* Does this source line contribute
856 * to any destination line? if not,
857 * exit now.
858 */
859
861 {
862 m_cur_src_y++;
863 return true;
864 }
865
866 /* Find an empty slot in the scanline buffer. (FIXME: Perf. is terrible here with extreme scaling ratios.) */
867
868 for (i = 0; i < MAX_SCAN_BUF_SIZE; i++)
869 if (m_Pscan_buf->scan_buf_y[i] == -1)
870 break;
871
872 /* If the buffer is full, exit with an error. */
873
874 if (i == MAX_SCAN_BUF_SIZE)
875 {
877 return false;
878 }
879
882
883 /* Does this slot have any memory allocated to it? */
884
885 if (!m_Pscan_buf->scan_buf_l[i])
886 {
887 if ((m_Pscan_buf->scan_buf_l[i] = (Sample*)malloc(m_intermediate_x * sizeof(Sample))) == NULL)
888 {
890 return false;
891 }
892 }
893 /*for (int j = 0; j < m_intermediate_x; j++)
894 std::cout << Psrc[j] << " ";
895 std::cout << std::endl;*/
896 // Resampling on the X axis first?
898 {
900
901 // Y-X resampling order
902 memcpy(m_Pscan_buf->scan_buf_l[i], Psrc, m_intermediate_x * sizeof(Sample));
903 /*for (int j = 0; j < m_intermediate_x; j++)
904 std::cout << m_Pscan_buf->scan_buf_l[i][j] << " ";
905 std::cout << std::endl;*/
906 }
907 else
908 {
910
911 // X-Y resampling order
913 /*for (int j = 0; j < m_intermediate_x; j++)
914 std::cout << m_Pscan_buf->scan_buf_l[i][j] << " ";
915 std::cout << std::endl;*/
916
917 }
918
919 m_cur_src_y++;
920
921 return true;
922}
923
925{
926 int i;
927
928 /* If all the destination lines have been
929 * generated, then always return NULL.
930 */
931
933 return NULL;
934
935 /* Check to see if all the required
936 * contributors are present, if not,
937 * return NULL.
938 */
939
940 for (i = 0; i < m_Pclist_y[m_cur_dst_y].n; i++)
942 return NULL;
943
945
946 m_cur_dst_y++;
947
948 return m_Pdst_buf;
949}
950
952{
953 int i;
954
955#if RESAMPLER_DEBUG_OPS
956 printf("actual ops: %i\n", total_ops);
957#endif
958
959 free(m_Pdst_buf);
960 m_Pdst_buf = NULL;
961
962 if (m_Ptmp_buf)
963 {
964 free(m_Ptmp_buf);
965 m_Ptmp_buf = NULL;
966 }
967
968 /* Don't deallocate a contibutor list
969 * if the user passed us one of their own.
970 */
971
972 if ((m_Pclist_x) && (!m_clist_x_forced))
973 {
974 free(m_Pclist_x->p);
975 free(m_Pclist_x);
976 m_Pclist_x = NULL;
977 }
978
979 if ((m_Pclist_y) && (!m_clist_y_forced))
980 {
981 free(m_Pclist_y->p);
982 free(m_Pclist_y);
983 m_Pclist_y = NULL;
984 }
985
986 free(m_Psrc_y_count);
987 m_Psrc_y_count = NULL;
988
989 free(m_Psrc_y_flag);
990 m_Psrc_y_flag = NULL;
991
992 if (m_Pscan_buf)
993 {
994 for (i = 0; i < MAX_SCAN_BUF_SIZE; i++)
995 free(m_Pscan_buf->scan_buf_l[i]);
996
997 free(m_Pscan_buf);
998 m_Pscan_buf = NULL;
999 }
1000}
1001
1003{
1004 if (STATUS_OKAY != m_status)
1005 return;
1006
1008
1009 int i, j;
1010 for (i = 0; i < m_resample_src_y; i++)
1011 {
1012 m_Psrc_y_count[i] = 0;
1013 m_Psrc_y_flag[i] = FALSE;
1014 }
1015
1016 for (i = 0; i < m_resample_dst_y; i++)
1017 {
1018 for (j = 0; j < m_Pclist_y[i].n; j++)
1020 }
1021
1022 for (i = 0; i < MAX_SCAN_BUF_SIZE; i++)
1023 {
1024 m_Pscan_buf->scan_buf_y[i] = -1;
1025
1026 free(m_Pscan_buf->scan_buf_l[i]);
1027 m_Pscan_buf->scan_buf_l[i] = NULL;
1028 }
1029}
1030
1031Resampler::Resampler(int src_x, int src_y,
1032 int dst_x, int dst_y,
1033 Boundary_Op boundary_op,
1034 Resample_Real sample_low, Resample_Real sample_high,
1035 const char* Pfilter_name,
1036 Contrib_List* Pclist_x,
1037 Contrib_List* Pclist_y,
1038 Resample_Real filter_x_scale,
1039 Resample_Real filter_y_scale,
1040 Resample_Real src_x_ofs,
1041 Resample_Real src_y_ofs)
1042{
1043 int i, j;
1045
1046 resampler_assert(src_x > 0);
1047 resampler_assert(src_y > 0);
1048 resampler_assert(dst_x > 0);
1049 resampler_assert(dst_y > 0);
1050
1051#if RESAMPLER_DEBUG_OPS
1052 total_ops = 0;
1053#endif
1054
1055 m_lo = sample_low;
1056 m_hi = sample_high;
1057
1058 m_delay_x_resample = false;
1059 m_intermediate_x = 0;
1060 m_Pdst_buf = NULL;
1061 m_Ptmp_buf = NULL;
1062 m_clist_x_forced = false;
1063 m_Pclist_x = NULL;
1064 m_clist_y_forced = false;
1065 m_Pclist_y = NULL;
1066 m_Psrc_y_count = NULL;
1067 m_Psrc_y_flag = NULL;
1068 m_Pscan_buf = NULL;
1070
1071 m_resample_src_x = src_x;
1072 m_resample_src_y = src_y;
1073 m_resample_dst_x = dst_x;
1074 m_resample_dst_y = dst_y;
1075
1076 m_boundary_op = boundary_op;
1077
1078 if ((m_Pdst_buf = (Sample*)malloc(m_resample_dst_x * sizeof(Sample))) == NULL)
1079 {
1081 return;
1082 }
1083
1084 // Find the specified filter.
1085
1086 if (Pfilter_name == NULL)
1087 Pfilter_name = RESAMPLER_DEFAULT_FILTER;
1088
1089 for (i = 0; i < NUM_FILTERS; i++)
1090 if (strcmp(Pfilter_name, g_filters[i].name) == 0)
1091 break;
1092
1093 if (i == NUM_FILTERS)
1094 {
1096 return;
1097 }
1098
1099 func = g_filters[i].func;
1100 support = g_filters[i].support;
1101
1102 /* Create contributor lists, unless the user supplied custom lists. */
1103
1104 if (!Pclist_x)
1105 {
1107 if (!m_Pclist_x)
1108 {
1110 return;
1111 }
1112 }
1113 else
1114 {
1115 m_Pclist_x = Pclist_x;
1116 m_clist_x_forced = true;
1117 }
1118
1119 if (!Pclist_y)
1120 {
1122 if (!m_Pclist_y)
1123 {
1125 return;
1126 }
1127 }
1128 else
1129 {
1130 m_Pclist_y = Pclist_y;
1131 m_clist_y_forced = true;
1132 }
1133
1134 if ((m_Psrc_y_count = (int*)calloc(m_resample_src_y, sizeof(int))) == NULL)
1135 {
1137 return;
1138 }
1139
1140 if ((m_Psrc_y_flag = (unsigned char*)calloc(m_resample_src_y, sizeof(unsigned char))) == NULL)
1141 {
1143 return;
1144 }
1145
1146 /* Count how many times each source line
1147 * contributes to a destination line.
1148 */
1149
1150 for (i = 0; i < m_resample_dst_y; i++)
1151 for (j = 0; j < m_Pclist_y[i].n; j++)
1153
1154 if ((m_Pscan_buf = (Scan_Buf*)malloc(sizeof(Scan_Buf))) == NULL)
1155 {
1157 return;
1158 }
1159
1160 for (i = 0; i < MAX_SCAN_BUF_SIZE; i++)
1161 {
1162 m_Pscan_buf->scan_buf_y[i] = -1;
1163 m_Pscan_buf->scan_buf_l[i] = NULL;
1164 }
1165
1167 {
1168 // Determine which axis to resample first by comparing the number of multiplies required
1169 // for each possibility.
1172
1173 // Hack 10/2000: Weight Y axis ops a little more than X axis ops.
1174 // (Y axis ops use more cache resources.)
1175 int xy_ops = x_ops * m_resample_src_y +
1176 (4 * y_ops * m_resample_dst_x) / 3;
1177
1178 int yx_ops = (4 * y_ops * m_resample_src_x) / 3 +
1179 x_ops * m_resample_dst_y;
1180
1181#if RESAMPLER_DEBUG_OPS
1182 printf("src: %i %i\n", m_resample_src_x, m_resample_src_y);
1183 printf("dst: %i %i\n", m_resample_dst_x, m_resample_dst_y);
1184 printf("x_ops: %i\n", x_ops);
1185 printf("y_ops: %i\n", y_ops);
1186 printf("xy_ops: %i\n", xy_ops);
1187 printf("yx_ops: %i\n", yx_ops);
1188#endif
1189
1190 // Now check which resample order is better. In case of a tie, choose the order
1191 // which buffers the least amount of data.
1192 if ((xy_ops > yx_ops) ||
1193 ((xy_ops == yx_ops) && (m_resample_src_x < m_resample_dst_x))
1194 )
1195 {
1196 m_delay_x_resample = true;
1198 }
1199 else
1200 {
1201 m_delay_x_resample = false;
1203 }
1204#if RESAMPLER_DEBUG_OPS
1205 printf("delaying: %i\n", m_delay_x_resample);
1206#endif
1207 }
1208
1210 {
1211 if ((m_Ptmp_buf = (Sample*)malloc(m_intermediate_x * sizeof(Sample))) == NULL)
1212 {
1214 return;
1215 }
1216 }
1217}
1218
1219void Resampler::get_clists(Contrib_List** ptr_clist_x, Contrib_List** ptr_clist_y)
1220{
1221 if (ptr_clist_x)
1222 *ptr_clist_x = m_Pclist_x;
1223
1224 if (ptr_clist_y)
1225 *ptr_clist_y = m_Pclist_y;
1226}
1227
1229{
1230 return NUM_FILTERS;
1231}
1232
1233char* Resampler::get_filter_name(int filter_num)
1234{
1235 if ((filter_num < 0) || (filter_num >= NUM_FILTERS))
1236 return NULL;
1237 else
1238 return g_filters[filter_num].name;
1239}
1240
Contrib_List * m_Pclist_x
Definition: resampler.h:117
void restart()
Definition: resampler.cpp:1002
void get_clists(Contrib_List **ptr_clist_x, Contrib_List **ptr_clist_y)
Definition: resampler.cpp:1219
Resample_Real Sample
Definition: resampler.h:17
Sample * m_Pdst_buf
Definition: resampler.h:114
static int get_filter_num()
Definition: resampler.cpp:1228
bool m_clist_x_forced
Definition: resampler.h:120
const Sample * get_line()
Definition: resampler.cpp:924
Resample_Real clamp_sample(Resample_Real f) const
Definition: resampler.h:170
int m_resample_src_x
Definition: resampler.h:107
bool m_clist_y_forced
Definition: resampler.h:121
@ BOUNDARY_WRAP
Definition: resampler.h:33
@ BOUNDARY_REFLECT
Definition: resampler.h:34
Boundary_Op m_boundary_op
Definition: resampler.h:112
int count_ops(Contrib_List *Pclist, int k)
Definition: resampler.h:159
Scan_Buf * m_Pscan_buf
Definition: resampler.h:137
int m_resample_dst_x
Definition: resampler.h:109
Contrib_List * make_clist(int src_x, int dst_x, Boundary_Op boundary_op, Resample_Real(*Pfilter)(Resample_Real), Resample_Real filter_support, Resample_Real filter_scale, Resample_Real src_ofs)
Definition: resampler.cpp:439
bool m_delay_x_resample
Definition: resampler.h:123
Resample_Real m_lo
Definition: resampler.h:167
Contrib_List * m_Pclist_y
Definition: resampler.h:118
int m_cur_src_y
Definition: resampler.h:139
static char * get_filter_name(int filter_num)
Definition: resampler.cpp:1233
void scale_y_add(Sample *Ptmp, const Sample *Psrc, Resample_Real weight, int dst_x)
Definition: resampler.cpp:768
bool put_line(const Sample *Psrc)
Definition: resampler.cpp:848
void scale_y_mov(Sample *Ptmp, const Sample *Psrc, Resample_Real weight, int dst_x)
Definition: resampler.cpp:755
int m_intermediate_x
Definition: resampler.h:105
unsigned char * m_Psrc_y_flag
Definition: resampler.h:126
int m_resample_src_y
Definition: resampler.h:108
int m_resample_dst_y
Definition: resampler.h:110
@ MAX_SCAN_BUF_SIZE
Definition: resampler.h:129
Status m_status
Definition: resampler.h:142
int reflect(const int j, const int src_x, const Boundary_Op boundary_op)
Definition: resampler.cpp:399
Resample_Real m_hi
Definition: resampler.h:168
void clamp(Sample *Pdst, int n)
Definition: resampler.cpp:778
int m_cur_dst_y
Definition: resampler.h:140
void resample_x(Sample *Pdst, const Sample *Psrc)
Definition: resampler.cpp:728
@ STATUS_OKAY
Definition: resampler.h:40
@ STATUS_BAD_FILTER_NAME
Definition: resampler.h:42
@ STATUS_SCAN_BUFFER_FULL
Definition: resampler.h:43
@ STATUS_OUT_OF_MEMORY
Definition: resampler.h:41
void resample_y(Sample *Pdst)
Definition: resampler.cpp:788
int total_ops
Definition: resampler.h:102
int * m_Psrc_y_count
Definition: resampler.h:125
Sample * m_Ptmp_buf
Definition: resampler.h:115
CONSTCD14 std::enable_if< detail::no_overflow< Period, typenameTo::period >::value, To >::type floor(const std::chrono::duration< Rep, Period > &d)
Definition: date.h:1251
CONSTCD14 To ceil(const std::chrono::duration< Rep, Period > &d)
Definition: date.h:1302
bool isnan(const value_type &v)
Definition: muParserDef.h:379
#define KAISER_SUPPORT
Definition: resampler.cpp:350
static Resample_Real box_filter(Resample_Real t)
Definition: resampler.cpp:76
static int resampler_range_check(int v, int h)
Definition: resampler.cpp:22
static Resample_Real B_spline_filter(Resample_Real t)
Definition: resampler.cpp:116
static Resample_Real lanczos3_filter(Resample_Real t)
Definition: resampler.cpp:274
static Resample_Real mitchell_filter(Resample_Real t)
Definition: resampler.cpp:209
static Resample_Real lanczos6_filter(Resample_Real t)
Definition: resampler.cpp:298
#define LANCZOS4_SUPPORT
Definition: resampler.cpp:285
#define LANCZOS6_SUPPORT
Definition: resampler.cpp:297
static Resample_Real bell_filter(Resample_Real t)
Definition: resampler.cpp:98
#define BOX_FILTER_SUPPORT
Definition: resampler.cpp:75
static int cast_to_int(Resample_Real i)
Definition: resampler.cpp:50
static Resample_Real clean(double t)
Definition: resampler.cpp:230
Resample_Real support
Definition: resampler.cpp:373
#define LANCZOS3_SUPPORT
Definition: resampler.cpp:273
static Resample_Real quadratic_interp_filter(Resample_Real t)
Definition: resampler.cpp:155
#define MITCHELL_SUPPORT
Definition: resampler.cpp:208
static double sinc(double x)
Definition: resampler.cpp:220
static Resample_Real mitchell(Resample_Real t, const Resample_Real B, const Resample_Real C)
Definition: resampler.cpp:178
#define TENT_FILTER_SUPPORT
Definition: resampler.cpp:85
static double blackman_exact_window(double x)
Definition: resampler.cpp:243
static Resample_Real kaiser_filter(Resample_Real t)
Definition: resampler.cpp:351
#define LANCZOS12_SUPPORT
Definition: resampler.cpp:309
static Resample_Real quadratic_mix_filter(Resample_Real t)
Definition: resampler.cpp:165
#define CATMULL_ROM_SUPPORT
Definition: resampler.cpp:214
static Resample_Real lanczos4_filter(Resample_Real t)
Definition: resampler.cpp:286
#define BELL_SUPPORT
Definition: resampler.cpp:97
static Resample_Real catmull_rom_filter(Resample_Real t)
Definition: resampler.cpp:215
#define resampler_assert
Definition: resampler.cpp:20
#define TRUE
Definition: resampler.cpp:38
#define FALSE
Definition: resampler.cpp:42
static double kaiser(double alpha, double half_width, double x)
Definition: resampler.cpp:344
static Resample_Real lanczos12_filter(Resample_Real t)
Definition: resampler.cpp:310
static Resample_Real blackman_filter(Resample_Real t)
Definition: resampler.cpp:249
static int posmod(int x, int y)
Definition: resampler.cpp:56
static const int NUM_FILTERS
Definition: resampler.cpp:394
char name[32]
Definition: resampler.cpp:371
static const Resample_Real KAISER_ALPHA
Definition: resampler.cpp:343
static double bessel0(double x)
Definition: resampler.cpp:321
static Resample_Real gaussian_filter(Resample_Real t)
Definition: resampler.cpp:262
#define BLACKMAN_SUPPORT
Definition: resampler.cpp:248
#define M_PI
Definition: resampler.cpp:47
static Resample_Real quadratic(Resample_Real t, const Resample_Real R)
Definition: resampler.cpp:139
static struct @9 g_filters[]
Resample_Real(* func)(Resample_Real t)
Definition: resampler.cpp:372
static Resample_Real quadratic_approx_filter(Resample_Real t)
Definition: resampler.cpp:160
static Resample_Real tent_filter(Resample_Real t)
Definition: resampler.cpp:86
#define GAUSSIAN_SUPPORT
Definition: resampler.cpp:261
#define B_SPLINE_SUPPORT
Definition: resampler.cpp:115
#define QUADRATIC_SUPPORT
Definition: resampler.cpp:138
double Resample_Real
Definition: resampler.h:12
#define RESAMPLER_DEFAULT_FILTER
Definition: resampler.h:7
unsigned short n
Definition: resampler.h:27
Resample_Real weight
Definition: resampler.h:21
unsigned short pixel
Definition: resampler.h:22
int scan_buf_y[MAX_SCAN_BUF_SIZE]
Definition: resampler.h:133
Sample * scan_buf_l[MAX_SCAN_BUF_SIZE]
Definition: resampler.h:134