basisu_resampler.cpp 19 KB

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