LCOV - code coverage report
Current view: top level - core/bitmap - SPBitmapResample.cc (source / functions) Hit Total Coverage
Test: coverage.info Lines: 473 537 88.1 %
Date: 2024-05-12 00:16:13 Functions: 48 49 98.0 %

          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             : }

Generated by: LCOV version 1.14