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 | |
20 | namespace 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 | |