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