]> git.cworth.org Git - vogl/blob - src/voglcore/vogl_resampler.cpp
Initial vogl checkin
[vogl] / src / voglcore / vogl_resampler.cpp
1 /**************************************************************************
2  *
3  * Copyright 2013-2014 RAD Game Tools and Valve Software
4  * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC
5  * All Rights Reserved.
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy
8  * of this software and associated documentation files (the "Software"), to deal
9  * in the Software without restriction, including without limitation the rights
10  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11  * copies of the Software, and to permit persons to whom the Software is
12  * furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in
15  * all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23  * THE SOFTWARE.
24  *
25  **************************************************************************/
26
27 // File: vogl_resampler.h
28 // RG: This is public domain code, originally derived from Graphics Gems 3, see: http://code.google.com/p/imageresampler/
29 #include "vogl_core.h"
30 #include "vogl_resampler.h"
31 #include "vogl_resample_filters.h"
32
33 namespace vogl
34 {
35 #define resampler_assert VOGL_ASSERT
36
37     static inline int resampler_range_check(int v, int h)
38     {
39         VOGL_NOTE_UNUSED(h);
40         resampler_assert((v >= 0) && (v < h));
41         return v;
42     }
43
44 #ifndef max
45 #define max(a, b) (((a) > (b)) ? (a) : (b))
46 #endif
47
48 #ifndef min
49 #define min(a, b) (((a) < (b)) ? (a) : (b))
50 #endif
51
52 #ifndef TRUE
53 #define TRUE (1)
54 #endif
55
56 #ifndef FALSE
57 #define FALSE (0)
58 #endif
59
60 #define RESAMPLER_DEBUG 0
61
62     // (x mod y) with special handling for negative x values.
63     static inline int posmod(int x, int y)
64     {
65         if (x >= 0)
66             return (x % y);
67         else
68         {
69             int m = (-x) % y;
70
71             if (m != 0)
72                 m = y - m;
73
74             return (m);
75         }
76     }
77
78     // Float to int cast with truncation.
79     static inline int cast_to_int(Resample_Real i)
80     {
81         return (int)i;
82     }
83
84     /* Ensure that the contributing source sample is
85 * within bounds. If not, reflect, clamp, or wrap.
86 */
87     int Resampler::reflect(const int j, const int src_x, const Boundary_Op boundary_op)
88     {
89         int n;
90
91         if (j < 0)
92         {
93             if (boundary_op == BOUNDARY_REFLECT)
94             {
95                 n = -j;
96
97                 if (n >= src_x)
98                     n = src_x - 1;
99             }
100             else if (boundary_op == BOUNDARY_WRAP)
101                 n = posmod(j, src_x);
102             else
103                 n = 0;
104         }
105         else if (j >= src_x)
106         {
107             if (boundary_op == BOUNDARY_REFLECT)
108             {
109                 n = (src_x - j) + (src_x - 1);
110
111                 if (n < 0)
112                     n = 0;
113             }
114             else if (boundary_op == BOUNDARY_WRAP)
115                 n = posmod(j, src_x);
116             else
117                 n = src_x - 1;
118         }
119         else
120             n = j;
121
122         return n;
123     }
124
125     // The make_clist() method generates, for all destination samples,
126     // the list of all source samples with non-zero weighted contributions.
127     Resampler::Contrib_List *Resampler::make_clist(
128         int src_x, int dst_x, Boundary_Op boundary_op,
129         Resample_Real (*Pfilter)(Resample_Real),
130         Resample_Real filter_support,
131         Resample_Real filter_scale,
132         Resample_Real src_ofs)
133     {
134         typedef struct
135         {
136             // The center of the range in DISCRETE coordinates (pixel center = 0.0f).
137             Resample_Real center;
138             int left, right;
139         } Contrib_Bounds;
140
141         int i, j, k, n, left, right;
142         Resample_Real total_weight;
143         Resample_Real xscale, center, half_width, weight;
144         Contrib_List *Pcontrib;
145         Contrib *Pcpool;
146         Contrib *Pcpool_next;
147         Contrib_Bounds *Pcontrib_bounds;
148
149         if ((Pcontrib = (Contrib_List *)vogl_calloc(dst_x, sizeof(Contrib_List))) == NULL)
150             return NULL;
151
152         Pcontrib_bounds = (Contrib_Bounds *)vogl_calloc(dst_x, sizeof(Contrib_Bounds));
153         if (!Pcontrib_bounds)
154         {
155             vogl_free(Pcontrib);
156             return (NULL);
157         }
158
159         const Resample_Real oo_filter_scale = 1.0f / filter_scale;
160
161         const Resample_Real NUDGE = 0.5f;
162         xscale = dst_x / (Resample_Real)src_x;
163
164         if (xscale < 1.0f)
165         {
166             int total;
167             (void)total;
168
169             /* Handle case when there are fewer destination
170                 * samples than source samples (downsampling/minification).
171                 */
172
173             // stretched half width of filter
174             half_width = (filter_support / xscale) * filter_scale;
175
176             // Find the range of source sample(s) that will contribute to each destination sample.
177
178             for (i = 0, n = 0; i < dst_x; i++)
179             {
180                 // Convert from discrete to continuous coordinates, scale, then convert back to discrete.
181                 center = ((Resample_Real)i + NUDGE) / xscale;
182                 center -= NUDGE;
183                 center += src_ofs;
184
185                 left = cast_to_int((Resample_Real)floor(center - half_width));
186                 right = cast_to_int((Resample_Real)ceil(center + half_width));
187
188                 Pcontrib_bounds[i].center = center;
189                 Pcontrib_bounds[i].left = left;
190                 Pcontrib_bounds[i].right = right;
191
192                 n += (right - left + 1);
193             }
194
195             /* Allocate memory for contributors. */
196
197             if ((n == 0) || ((Pcpool = (Contrib *)vogl_calloc(n, sizeof(Contrib))) == NULL))
198             {
199                 vogl_free(Pcontrib);
200                 vogl_free(Pcontrib_bounds);
201                 return NULL;
202             }
203             total = n;
204
205             Pcpool_next = Pcpool;
206
207             /* Create the list of source samples which
208                 * contribute to each destination sample.
209                 */
210
211             for (i = 0; i < dst_x; i++)
212             {
213                 int max_k = -1;
214                 Resample_Real max_w = -1e+20f;
215
216                 center = Pcontrib_bounds[i].center;
217                 left = Pcontrib_bounds[i].left;
218                 right = Pcontrib_bounds[i].right;
219
220                 Pcontrib[i].n = 0;
221                 Pcontrib[i].p = Pcpool_next;
222                 Pcpool_next += (right - left + 1);
223                 resampler_assert((Pcpool_next - Pcpool) <= total);
224
225                 total_weight = 0;
226
227                 for (j = left; j <= right; j++)
228                     total_weight += (*Pfilter)((center - (Resample_Real)j) * xscale * oo_filter_scale);
229                 const Resample_Real norm = static_cast<Resample_Real>(1.0f / total_weight);
230
231                 total_weight = 0;
232
233 #if RESAMPLER_DEBUG
234                 printf("%i: ", i);
235 #endif
236
237                 for (j = left; j <= right; j++)
238                 {
239                     weight = (*Pfilter)((center - (Resample_Real)j) * xscale * oo_filter_scale) * norm;
240                     if (weight == 0.0f)
241                         continue;
242
243                     n = reflect(j, src_x, boundary_op);
244
245 #if RESAMPLER_DEBUG
246                     printf("%i(%f), ", n, weight);
247 #endif
248
249                     /* Increment the number of source
250                                 * samples which contribute to the
251                                 * current destination sample.
252                                 */
253
254                     k = Pcontrib[i].n++;
255
256                     Pcontrib[i].p[k].pixel = (unsigned short)n; /* store src sample number */
257                     Pcontrib[i].p[k].weight = weight;           /* store src sample weight */
258
259                     total_weight += weight; /* total weight of all contributors */
260
261                     if (weight > max_w)
262                     {
263                         max_w = weight;
264                         max_k = k;
265                     }
266                 }
267
268 #if RESAMPLER_DEBUG
269                 printf("\n\n");
270 #endif
271
272                 //resampler_assert(Pcontrib[i].n);
273                 //resampler_assert(max_k != -1);
274                 if ((max_k == -1) || (Pcontrib[i].n == 0))
275                 {
276                     vogl_free(Pcpool);
277                     vogl_free(Pcontrib);
278                     vogl_free(Pcontrib_bounds);
279                     return NULL;
280                 }
281
282                 if (total_weight != 1.0f)
283                     Pcontrib[i].p[max_k].weight += 1.0f - total_weight;
284             }
285         }
286         else
287         {
288             /* Handle case when there are more
289                 * destination samples than source
290                 * samples (upsampling).
291                 */
292
293             half_width = filter_support * filter_scale;
294
295             // Find the source sample(s) that contribute to each destination sample.
296
297             for (i = 0, n = 0; i < dst_x; i++)
298             {
299                 // Convert from discrete to continuous coordinates, scale, then convert back to discrete.
300                 center = ((Resample_Real)i + NUDGE) / xscale;
301                 center -= NUDGE;
302                 center += src_ofs;
303
304                 left = cast_to_int((Resample_Real)floor(center - half_width));
305                 right = cast_to_int((Resample_Real)ceil(center + half_width));
306
307                 Pcontrib_bounds[i].center = center;
308                 Pcontrib_bounds[i].left = left;
309                 Pcontrib_bounds[i].right = right;
310
311                 n += (right - left + 1);
312             }
313
314             /* Allocate memory for contributors. */
315
316             int total = n;
317             if ((total == 0) || ((Pcpool = (Contrib *)vogl_calloc(total, sizeof(Contrib))) == NULL))
318             {
319                 vogl_free(Pcontrib);
320                 vogl_free(Pcontrib_bounds);
321                 return NULL;
322             }
323
324             Pcpool_next = Pcpool;
325
326             /* Create the list of source samples which
327                 * contribute to each destination sample.
328                 */
329
330             for (i = 0; i < dst_x; i++)
331             {
332                 int max_k = -1;
333                 Resample_Real max_w = -1e+20f;
334
335                 center = Pcontrib_bounds[i].center;
336                 left = Pcontrib_bounds[i].left;
337                 right = Pcontrib_bounds[i].right;
338
339                 Pcontrib[i].n = 0;
340                 Pcontrib[i].p = Pcpool_next;
341                 Pcpool_next += (right - left + 1);
342                 resampler_assert((Pcpool_next - Pcpool) <= total);
343
344                 total_weight = 0;
345                 for (j = left; j <= right; j++)
346                     total_weight += (*Pfilter)((center - (Resample_Real)j) * oo_filter_scale);
347
348                 const Resample_Real norm = static_cast<Resample_Real>(1.0f / total_weight);
349
350                 total_weight = 0;
351
352 #if RESAMPLER_DEBUG
353                 printf("%i: ", i);
354 #endif
355
356                 for (j = left; j <= right; j++)
357                 {
358                     weight = (*Pfilter)((center - (Resample_Real)j) * oo_filter_scale) * norm;
359                     if (weight == 0.0f)
360                         continue;
361
362                     n = reflect(j, src_x, boundary_op);
363
364 #if RESAMPLER_DEBUG
365                     printf("%i(%f), ", n, weight);
366 #endif
367
368                     /* Increment the number of source
369                                 * samples which contribute to the
370                                 * current destination sample.
371                                 */
372
373                     k = Pcontrib[i].n++;
374
375                     Pcontrib[i].p[k].pixel = (unsigned short)n; /* store src sample number */
376                     Pcontrib[i].p[k].weight = weight;           /* store src sample weight */
377
378                     total_weight += weight; /* total weight of all contributors */
379
380                     if (weight > max_w)
381                     {
382                         max_w = weight;
383                         max_k = k;
384                     }
385                 }
386
387 #if RESAMPLER_DEBUG
388                 printf("\n\n");
389 #endif
390
391                 //resampler_assert(Pcontrib[i].n);
392                 //resampler_assert(max_k != -1);
393
394                 if ((max_k == -1) || (Pcontrib[i].n == 0))
395                 {
396                     vogl_free(Pcpool);
397                     vogl_free(Pcontrib);
398                     vogl_free(Pcontrib_bounds);
399                     return NULL;
400                 }
401
402                 if (total_weight != 1.0f)
403                     Pcontrib[i].p[max_k].weight += 1.0f - total_weight;
404             }
405         }
406
407 #if RESAMPLER_DEBUG
408         printf("*******\n");
409 #endif
410
411         vogl_free(Pcontrib_bounds);
412
413         return Pcontrib;
414     }
415
416     void Resampler::resample_x(Sample *Pdst, const Sample *Psrc)
417     {
418         resampler_assert(Pdst);
419         resampler_assert(Psrc);
420
421         int i, j;
422         Sample total;
423         Contrib_List *Pclist = m_Pclist_x;
424         Contrib *p;
425
426         for (i = m_resample_dst_x; i > 0; i--, Pclist++)
427         {
428 #if VOGL_RESAMPLER_DEBUG_OPS
429             total_ops += Pclist->n;
430 #endif
431
432             for (j = Pclist->n, p = Pclist->p, total = 0; j > 0; j--, p++)
433                 total += Psrc[p->pixel] * p->weight;
434
435             *Pdst++ = total;
436         }
437     }
438
439     void Resampler::scale_y_mov(Sample *Ptmp, const Sample *Psrc, Resample_Real weight, int dst_x)
440     {
441         int i;
442
443 #if VOGL_RESAMPLER_DEBUG_OPS
444         total_ops += dst_x;
445 #endif
446
447         // Not += because temp buf wasn't cleared.
448         for (i = dst_x; i > 0; i--)
449             *Ptmp++ = *Psrc++ * weight;
450     }
451
452     void Resampler::scale_y_add(Sample *Ptmp, const Sample *Psrc, Resample_Real weight, int dst_x)
453     {
454 #if VOGL_RESAMPLER_DEBUG_OPS
455         total_ops += dst_x;
456 #endif
457
458         for (int i = dst_x; i > 0; i--)
459             (*Ptmp++) += *Psrc++ * weight;
460     }
461
462     void Resampler::clamp(Sample *Pdst, int n)
463     {
464         while (n > 0)
465         {
466             Sample x = *Pdst;
467             *Pdst++ = clamp_sample(x);
468             n--;
469         }
470     }
471
472     void Resampler::resample_y(Sample *Pdst)
473     {
474         int i, j;
475         Sample *Psrc;
476         Contrib_List *Pclist = &m_Pclist_y[m_cur_dst_y];
477
478         Sample *Ptmp = m_delay_x_resample ? m_Ptmp_buf : Pdst;
479         resampler_assert(Ptmp);
480
481         /* Process each contributor. */
482
483         for (i = 0; i < Pclist->n; i++)
484         {
485             /* locate the contributor's location in the scan
486                 * buffer -- the contributor must always be found!
487                 */
488
489             for (j = 0; j < MAX_SCAN_BUF_SIZE; j++)
490                 if (m_Pscan_buf->scan_buf_y[j] == Pclist->p[i].pixel)
491                     break;
492
493             resampler_assert(j < MAX_SCAN_BUF_SIZE);
494
495             Psrc = m_Pscan_buf->scan_buf_l[j];
496
497             if (!i)
498                 scale_y_mov(Ptmp, Psrc, Pclist->p[i].weight, m_intermediate_x);
499             else
500                 scale_y_add(Ptmp, Psrc, Pclist->p[i].weight, m_intermediate_x);
501
502             /* If this source line doesn't contribute to any
503                 * more destination lines then mark the scanline buffer slot
504                 * which holds this source line as free.
505                 * (The max. number of slots used depends on the Y
506                 * axis sampling factor and the scaled filter width.)
507                 */
508
509             if (--m_Psrc_y_count[resampler_range_check(Pclist->p[i].pixel, m_resample_src_y)] == 0)
510             {
511                 m_Psrc_y_flag[resampler_range_check(Pclist->p[i].pixel, m_resample_src_y)] = FALSE;
512                 m_Pscan_buf->scan_buf_y[j] = -1;
513             }
514         }
515
516         /* Now generate the destination line */
517
518         if (m_delay_x_resample) // Was X resampling delayed until after Y resampling?
519         {
520             resampler_assert(Pdst != Ptmp);
521             resample_x(Pdst, Ptmp);
522         }
523         else
524         {
525             resampler_assert(Pdst == Ptmp);
526         }
527
528         if (m_lo < m_hi)
529             clamp(Pdst, m_resample_dst_x);
530     }
531
532     bool Resampler::put_line(const Sample *Psrc)
533     {
534         int i;
535
536         if (m_cur_src_y >= m_resample_src_y)
537             return false;
538
539         /* Does this source line contribute
540         * to any destination line? if not,
541         * exit now.
542         */
543
544         if (!m_Psrc_y_count[resampler_range_check(m_cur_src_y, m_resample_src_y)])
545         {
546             m_cur_src_y++;
547             return true;
548         }
549
550         /* Find an empty slot in the scanline buffer. (FIXME: Perf. is terrible here with extreme scaling ratios.) */
551
552         for (i = 0; i < MAX_SCAN_BUF_SIZE; i++)
553             if (m_Pscan_buf->scan_buf_y[i] == -1)
554                 break;
555
556         /* If the buffer is full, exit with an error. */
557
558         if (i == MAX_SCAN_BUF_SIZE)
559         {
560             m_status = STATUS_SCAN_BUFFER_FULL;
561             return false;
562         }
563
564         m_Psrc_y_flag[resampler_range_check(m_cur_src_y, m_resample_src_y)] = TRUE;
565         m_Pscan_buf->scan_buf_y[i] = m_cur_src_y;
566
567         /* Does this slot have any memory allocated to it? */
568
569         if (!m_Pscan_buf->scan_buf_l[i])
570         {
571             if ((m_Pscan_buf->scan_buf_l[i] = (Sample *)vogl_malloc(m_intermediate_x * sizeof(Sample))) == NULL)
572             {
573                 m_status = STATUS_OUT_OF_MEMORY;
574                 return false;
575             }
576         }
577
578         // Resampling on the X axis first?
579         if (m_delay_x_resample)
580         {
581             resampler_assert(m_intermediate_x == m_resample_src_x);
582
583             // Y-X resampling order
584             memcpy(m_Pscan_buf->scan_buf_l[i], Psrc, m_intermediate_x * sizeof(Sample));
585         }
586         else
587         {
588             resampler_assert(m_intermediate_x == m_resample_dst_x);
589
590             // X-Y resampling order
591             resample_x(m_Pscan_buf->scan_buf_l[i], Psrc);
592         }
593
594         m_cur_src_y++;
595
596         return true;
597     }
598
599     const Resampler::Sample *Resampler::get_line()
600     {
601         int i;
602
603         /* If all the destination lines have been
604         * generated, then always return NULL.
605         */
606
607         if (m_cur_dst_y == m_resample_dst_y)
608             return NULL;
609
610         /* Check to see if all the required
611         * contributors are present, if not,
612         * return NULL.
613         */
614
615         for (i = 0; i < m_Pclist_y[m_cur_dst_y].n; i++)
616             if (!m_Psrc_y_flag[resampler_range_check(m_Pclist_y[m_cur_dst_y].p[i].pixel, m_resample_src_y)])
617                 return NULL;
618
619         resample_y(m_Pdst_buf);
620
621         m_cur_dst_y++;
622
623         return m_Pdst_buf;
624     }
625
626     Resampler::~Resampler()
627     {
628         int i;
629
630 #if VOGL_RESAMPLER_DEBUG_OPS
631         printf("actual ops: %i\n", total_ops);
632 #endif
633
634         vogl_free(m_Pdst_buf);
635         m_Pdst_buf = NULL;
636
637         if (m_Ptmp_buf)
638         {
639             vogl_free(m_Ptmp_buf);
640             m_Ptmp_buf = NULL;
641         }
642
643         /* Don't deallocate a contibutor list
644         * if the user passed us one of their own.
645         */
646
647         if ((m_Pclist_x) && (!m_clist_x_forced))
648         {
649             vogl_free(m_Pclist_x->p);
650             vogl_free(m_Pclist_x);
651             m_Pclist_x = NULL;
652         }
653
654         if ((m_Pclist_y) && (!m_clist_y_forced))
655         {
656             vogl_free(m_Pclist_y->p);
657             vogl_free(m_Pclist_y);
658             m_Pclist_y = NULL;
659         }
660
661         vogl_free(m_Psrc_y_count);
662         m_Psrc_y_count = NULL;
663
664         vogl_free(m_Psrc_y_flag);
665         m_Psrc_y_flag = NULL;
666
667         if (m_Pscan_buf)
668         {
669             for (i = 0; i < MAX_SCAN_BUF_SIZE; i++)
670                 vogl_free(m_Pscan_buf->scan_buf_l[i]);
671
672             vogl_free(m_Pscan_buf);
673             m_Pscan_buf = NULL;
674         }
675     }
676
677     void Resampler::restart()
678     {
679         if (STATUS_OKAY != m_status)
680             return;
681
682         m_cur_src_y = m_cur_dst_y = 0;
683
684         int i, j;
685         for (i = 0; i < m_resample_src_y; i++)
686         {
687             m_Psrc_y_count[i] = 0;
688             m_Psrc_y_flag[i] = FALSE;
689         }
690
691         for (i = 0; i < m_resample_dst_y; i++)
692         {
693             for (j = 0; j < m_Pclist_y[i].n; j++)
694                 m_Psrc_y_count[resampler_range_check(m_Pclist_y[i].p[j].pixel, m_resample_src_y)]++;
695         }
696
697         for (i = 0; i < MAX_SCAN_BUF_SIZE; i++)
698         {
699             m_Pscan_buf->scan_buf_y[i] = -1;
700
701             vogl_free(m_Pscan_buf->scan_buf_l[i]);
702             m_Pscan_buf->scan_buf_l[i] = NULL;
703         }
704     }
705
706     Resampler::Resampler(int src_x, int src_y,
707                          int dst_x, int dst_y,
708                          Boundary_Op boundary_op,
709                          Resample_Real sample_low, Resample_Real sample_high,
710                          const char *Pfilter_name,
711                          Contrib_List *Pclist_x,
712                          Contrib_List *Pclist_y,
713                          Resample_Real filter_x_scale,
714                          Resample_Real filter_y_scale,
715                          Resample_Real src_x_ofs,
716                          Resample_Real src_y_ofs)
717     {
718         int i, j;
719         Resample_Real support, (*func)(Resample_Real);
720
721         resampler_assert(src_x > 0);
722         resampler_assert(src_y > 0);
723         resampler_assert(dst_x > 0);
724         resampler_assert(dst_y > 0);
725
726 #if VOGL_RESAMPLER_DEBUG_OPS
727         total_ops = 0;
728 #endif
729
730         m_lo = sample_low;
731         m_hi = sample_high;
732
733         m_delay_x_resample = false;
734         m_intermediate_x = 0;
735         m_Pdst_buf = NULL;
736         m_Ptmp_buf = NULL;
737         m_clist_x_forced = false;
738         m_Pclist_x = NULL;
739         m_clist_y_forced = false;
740         m_Pclist_y = NULL;
741         m_Psrc_y_count = NULL;
742         m_Psrc_y_flag = NULL;
743         m_Pscan_buf = NULL;
744         m_status = STATUS_OKAY;
745
746         m_resample_src_x = src_x;
747         m_resample_src_y = src_y;
748         m_resample_dst_x = dst_x;
749         m_resample_dst_y = dst_y;
750
751         m_boundary_op = boundary_op;
752
753         if ((m_Pdst_buf = (Sample *)vogl_malloc(m_resample_dst_x * sizeof(Sample))) == NULL)
754         {
755             m_status = STATUS_OUT_OF_MEMORY;
756             return;
757         }
758
759         // Find the specified filter.
760
761         if (Pfilter_name == NULL)
762             Pfilter_name = VOGL_RESAMPLER_DEFAULT_FILTER;
763
764         for (i = 0; i < g_num_resample_filters; i++)
765             if (strcmp(Pfilter_name, g_resample_filters[i].name) == 0)
766                 break;
767
768         if (i == g_num_resample_filters)
769         {
770             m_status = STATUS_BAD_FILTER_NAME;
771             return;
772         }
773
774         func = g_resample_filters[i].func;
775         support = g_resample_filters[i].support;
776
777         /* Create contributor lists, unless the user supplied custom lists. */
778
779         if (!Pclist_x)
780         {
781             m_Pclist_x = make_clist(m_resample_src_x, m_resample_dst_x, m_boundary_op, func, support, filter_x_scale, src_x_ofs);
782             if (!m_Pclist_x)
783             {
784                 m_status = STATUS_OUT_OF_MEMORY;
785                 return;
786             }
787         }
788         else
789         {
790             m_Pclist_x = Pclist_x;
791             m_clist_x_forced = true;
792         }
793
794         if (!Pclist_y)
795         {
796             m_Pclist_y = make_clist(m_resample_src_y, m_resample_dst_y, m_boundary_op, func, support, filter_y_scale, src_y_ofs);
797             if (!m_Pclist_y)
798             {
799                 m_status = STATUS_OUT_OF_MEMORY;
800                 return;
801             }
802         }
803         else
804         {
805             m_Pclist_y = Pclist_y;
806             m_clist_y_forced = true;
807         }
808
809         if ((m_Psrc_y_count = (int *)vogl_calloc(m_resample_src_y, sizeof(int))) == NULL)
810         {
811             m_status = STATUS_OUT_OF_MEMORY;
812             return;
813         }
814
815         if ((m_Psrc_y_flag = (unsigned char *)vogl_calloc(m_resample_src_y, sizeof(unsigned char))) == NULL)
816         {
817             m_status = STATUS_OUT_OF_MEMORY;
818             return;
819         }
820
821         /* Count how many times each source line
822         * contributes to a destination line.
823         */
824
825         for (i = 0; i < m_resample_dst_y; i++)
826             for (j = 0; j < m_Pclist_y[i].n; j++)
827                 m_Psrc_y_count[resampler_range_check(m_Pclist_y[i].p[j].pixel, m_resample_src_y)]++;
828
829         if ((m_Pscan_buf = (Scan_Buf *)vogl_malloc(sizeof(Scan_Buf))) == NULL)
830         {
831             m_status = STATUS_OUT_OF_MEMORY;
832             return;
833         }
834
835         for (i = 0; i < MAX_SCAN_BUF_SIZE; i++)
836         {
837             m_Pscan_buf->scan_buf_y[i] = -1;
838             m_Pscan_buf->scan_buf_l[i] = NULL;
839         }
840
841         m_cur_src_y = m_cur_dst_y = 0;
842         {
843             // Determine which axis to resample first by comparing the number of multiplies required
844             // for each possibility.
845             int x_ops = count_ops(m_Pclist_x, m_resample_dst_x);
846             int y_ops = count_ops(m_Pclist_y, m_resample_dst_y);
847
848             // Hack 10/2000: Weight Y axis ops a little more than X axis ops.
849             // (Y axis ops use more cache resources.)
850             int xy_ops = x_ops * m_resample_src_y +
851                          (4 * y_ops * m_resample_dst_x) / 3;
852
853             int yx_ops = (4 * y_ops * m_resample_src_x) / 3 +
854                          x_ops * m_resample_dst_y;
855
856 #if VOGL_RESAMPLER_DEBUG_OPS
857             printf("src: %i %i\n", m_resample_src_x, m_resample_src_y);
858             printf("dst: %i %i\n", m_resample_dst_x, m_resample_dst_y);
859             printf("x_ops: %i\n", x_ops);
860             printf("y_ops: %i\n", y_ops);
861             printf("xy_ops: %i\n", xy_ops);
862             printf("yx_ops: %i\n", yx_ops);
863 #endif
864
865             // Now check which resample order is better. In case of a tie, choose the order
866             // which buffers the least amount of data.
867             if ((xy_ops > yx_ops) ||
868                 ((xy_ops == yx_ops) && (m_resample_src_x < m_resample_dst_x)))
869             {
870                 m_delay_x_resample = true;
871                 m_intermediate_x = m_resample_src_x;
872             }
873             else
874             {
875                 m_delay_x_resample = false;
876                 m_intermediate_x = m_resample_dst_x;
877             }
878 #if VOGL_RESAMPLER_DEBUG_OPS
879             printf("delaying: %i\n", m_delay_x_resample);
880 #endif
881         }
882
883         if (m_delay_x_resample)
884         {
885             if ((m_Ptmp_buf = (Sample *)vogl_malloc(m_intermediate_x * sizeof(Sample))) == NULL)
886             {
887                 m_status = STATUS_OUT_OF_MEMORY;
888                 return;
889             }
890         }
891     }
892
893     void Resampler::get_clists(Contrib_List **ptr_clist_x, Contrib_List **ptr_clist_y)
894     {
895         if (ptr_clist_x)
896             *ptr_clist_x = m_Pclist_x;
897
898         if (ptr_clist_y)
899             *ptr_clist_y = m_Pclist_y;
900     }
901
902     int Resampler::get_filter_num()
903     {
904         return g_num_resample_filters;
905     }
906
907     const char *Resampler::get_filter_name(int filter_num)
908     {
909         if ((filter_num < 0) || (filter_num >= g_num_resample_filters))
910             return NULL;
911         else
912             return g_resample_filters[filter_num].name;
913     }
914
915 } // namespace vogl