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