1/*
2 * The two pass scaling function is based on:
3 * Filtered Image Rescaling
4 * Based on Gems III
5 *  - Schumacher general filtered image rescaling
6 * (pp. 414-424)
7 * by Dale Schumacher
8 *
9 *  Additional changes by Ray Gardener, Daylon Graphics Ltd.
10 *  December 4, 1999
11 *
12 *  Ported to libgd by Pierre Joye. Support for multiple channels
13 *  added (argb for now).
14 *
15 *  Initial sources code is avaibable in the Gems Source Code Packages:
16 *  http://www.acm.org/pubs/tog/GraphicsGems/GGemsIII.tar.gz
17 *
18 */
19
20/*
21    Summary:
22
23        - Horizontal filter contributions are calculated on the fly,
24          as each column is mapped from src to dst image. This lets
25          us omit having to allocate a temporary full horizontal stretch
26          of the src image.
27
28        - If none of the src pixels within a sampling region differ,
29          then the output pixel is forced to equal (any of) the source pixel.
30          This ensures that filters do not corrupt areas of constant color.
31
32        - Filter weight contribution results, after summing, are
33          rounded to the nearest pixel color value instead of
34          being casted to ILubyte (usually an int or char). Otherwise,
35          artifacting occurs.
36
37*/
38
39/*
40    Additional functions are available for simple rotation or up/downscaling.
41    downscaling using the fixed point implementations are usually much faster
42    than the existing gdImageCopyResampled while having a similar or better
43    quality.
44
45    For image rotations, the optimized versions have a lazy antialiasing for
46    the edges of the images. For a much better antialiased result, the affine
47    function is recommended.
48*/
49
50/*
51TODO:
52 - Optimize pixel accesses and loops once we have continuous buffer
53 - Add scale support for a portion only of an image (equivalent of copyresized/resampled)
54 */
55
56#include <stdio.h>
57#include <stdlib.h>
58#include <string.h>
59#include <math.h>
60
61#include <gd.h>
62#include "gdhelpers.h"
63
64#ifdef _MSC_VER
65# pragma optimize("t", on)
66# include <emmintrin.h>
67#endif
68
69#ifndef MIN
70#define MIN(a,b) ((a)<(b)?(a):(b))
71#endif
72#define MIN3(a,b,c) ((a)<(b)?(MIN(a,c)):(MIN(b,c)))
73#ifndef MAX
74#define MAX(a,b) ((a)<(b)?(b):(a))
75#endif
76#define MAX3(a,b,c) ((a)<(b)?(MAX(b,c)):(MAX(a,c)))
77
78#define CLAMP(x, low, high)  (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
79
80/* only used here, let do a generic fixed point integers later if required by other
81   part of GD */
82typedef long gdFixed;
83/* Integer to fixed point */
84#define gd_itofx(x) ((x) << 8)
85
86/* Float to fixed point */
87#define gd_ftofx(x) (long)((x) * 256)
88
89/*  Double to fixed point */
90#define gd_dtofx(x) (long)((x) * 256)
91
92/* Fixed point to integer */
93#define gd_fxtoi(x) ((x) >> 8)
94
95/* Fixed point to float */
96# define gd_fxtof(x) ((float)(x) / 256)
97
98/* Fixed point to double */
99#define gd_fxtod(x) ((double)(x) / 256)
100
101/* Multiply a fixed by a fixed */
102#define gd_mulfx(x,y) (((x) * (y)) >> 8)
103
104/* Divide a fixed by a fixed */
105#define gd_divfx(x,y) (((x) << 8) / (y))
106
107typedef struct
108{
109   double *Weights;  /* Normalized weights of neighboring pixels */
110   int Left,Right;   /* Bounds of source pixels window */
111} ContributionType;  /* Contirbution information for a single pixel */
112
113typedef struct
114{
115   ContributionType *ContribRow; /* Row (or column) of contribution weights */
116   unsigned int WindowSize,      /* Filter window size (of affecting source pixels) */
117                LineLength;      /* Length of line (no. or rows / cols) */
118} LineContribType;
119
120/* Each core filter has its own radius */
121#define DEFAULT_FILTER_BICUBIC              3.0
122#define DEFAULT_FILTER_BOX                  0.5
123#define DEFAULT_FILTER_GENERALIZED_CUBIC    0.5
124#define DEFAULT_FILTER_RADIUS               1.0
125#define DEFAULT_LANCZOS8_RADIUS             8.0
126#define DEFAULT_LANCZOS3_RADIUS             3.0
127#define DEFAULT_HERMITE_RADIUS              1.0
128#define DEFAULT_BOX_RADIUS                  0.5
129#define DEFAULT_TRIANGLE_RADIUS             1.0
130#define DEFAULT_BELL_RADIUS                 1.5
131#define DEFAULT_CUBICSPLINE_RADIUS          2.0
132#define DEFAULT_MITCHELL_RADIUS             2.0
133#define DEFAULT_COSINE_RADIUS               1.0
134#define DEFAULT_CATMULLROM_RADIUS           2.0
135#define DEFAULT_QUADRATIC_RADIUS            1.5
136#define DEFAULT_QUADRATICBSPLINE_RADIUS     1.5
137#define DEFAULT_CUBICCONVOLUTION_RADIUS     3.0
138#define DEFAULT_GAUSSIAN_RADIUS             1.0
139#define DEFAULT_HANNING_RADIUS              1.0
140#define DEFAULT_HAMMING_RADIUS              1.0
141#define DEFAULT_SINC_RADIUS                 1.0
142#define DEFAULT_WELSH_RADIUS                1.0
143
144enum GD_RESIZE_FILTER_TYPE{
145    FILTER_DEFAULT          = 0,
146    FILTER_BELL,
147    FILTER_BESSEL,
148    FILTER_BLACKMAN,
149    FILTER_BOX,
150    FILTER_BSPLINE,
151    FILTER_CATMULLROM,
152    FILTER_COSINE,
153    FILTER_CUBICCONVOLUTION,
154    FILTER_CUBICSPLINE,
155    FILTER_HERMITE,
156    FILTER_LANCZOS3,
157    FILTER_LANCZOS8,
158    FILTER_MITCHELL,
159    FILTER_QUADRATIC,
160    FILTER_QUADRATICBSPLINE,
161    FILTER_TRIANGLE,
162    FILTER_GAUSSIAN,
163    FILTER_HANNING,
164    FILTER_HAMMING,
165    FILTER_SINC,
166    FILTER_WELSH,
167
168    FILTER_CALLBACK        = 999
169};
170
171typedef enum GD_RESIZE_FILTER_TYPE gdResizeFilterType;
172
173static double KernelBessel_J1(const double x)
174{
175    double p, q;
176
177    register long i;
178
179    static const double
180    Pone[] =
181    {
182        0.581199354001606143928050809e+21,
183        -0.6672106568924916298020941484e+20,
184        0.2316433580634002297931815435e+19,
185        -0.3588817569910106050743641413e+17,
186        0.2908795263834775409737601689e+15,
187        -0.1322983480332126453125473247e+13,
188        0.3413234182301700539091292655e+10,
189        -0.4695753530642995859767162166e+7,
190        0.270112271089232341485679099e+4
191    },
192    Qone[] =
193    {
194        0.11623987080032122878585294e+22,
195        0.1185770712190320999837113348e+20,
196        0.6092061398917521746105196863e+17,
197        0.2081661221307607351240184229e+15,
198        0.5243710262167649715406728642e+12,
199        0.1013863514358673989967045588e+10,
200        0.1501793594998585505921097578e+7,
201        0.1606931573481487801970916749e+4,
202        0.1e+1
203    };
204
205    p = Pone[8];
206    q = Qone[8];
207    for (i=7; i >= 0; i--)
208    {
209        p = p*x*x+Pone[i];
210        q = q*x*x+Qone[i];
211    }
212    return (double)(p/q);
213}
214
215static double KernelBessel_P1(const double x)
216{
217    double p, q;
218
219    register long i;
220
221    static const double
222    Pone[] =
223    {
224        0.352246649133679798341724373e+5,
225        0.62758845247161281269005675e+5,
226        0.313539631109159574238669888e+5,
227        0.49854832060594338434500455e+4,
228        0.2111529182853962382105718e+3,
229        0.12571716929145341558495e+1
230    },
231    Qone[] =
232    {
233        0.352246649133679798068390431e+5,
234        0.626943469593560511888833731e+5,
235        0.312404063819041039923015703e+5,
236        0.4930396490181088979386097e+4,
237        0.2030775189134759322293574e+3,
238        0.1e+1
239    };
240
241    p = Pone[5];
242    q = Qone[5];
243    for (i=4; i >= 0; i--)
244    {
245        p = p*(8.0/x)*(8.0/x)+Pone[i];
246        q = q*(8.0/x)*(8.0/x)+Qone[i];
247    }
248    return (double)(p/q);
249}
250
251static double KernelBessel_Q1(const double x)
252{
253    double p, q;
254
255    register long i;
256
257    static const double
258    Pone[] =
259    {
260        0.3511751914303552822533318e+3,
261        0.7210391804904475039280863e+3,
262        0.4259873011654442389886993e+3,
263        0.831898957673850827325226e+2,
264        0.45681716295512267064405e+1,
265        0.3532840052740123642735e-1
266    },
267    Qone[] =
268    {
269        0.74917374171809127714519505e+4,
270        0.154141773392650970499848051e+5,
271        0.91522317015169922705904727e+4,
272        0.18111867005523513506724158e+4,
273        0.1038187585462133728776636e+3,
274        0.1e+1
275    };
276
277    p = Pone[5];
278    q = Qone[5];
279    for (i=4; i >= 0; i--)
280    {
281        p = p*(8.0/x)*(8.0/x)+Pone[i];
282        q = q*(8.0/x)*(8.0/x)+Qone[i];
283    }
284    return (double)(p/q);
285}
286
287static double KernelBessel_Order1(double x)
288{
289    double p, q;
290
291    if (x == 0.0)
292        return (0.0f);
293    p = x;
294    if (x < 0.0)
295        x=(-x);
296    if (x < 8.0)
297        return (p*KernelBessel_J1(x));
298    q = (double)sqrt(2.0f/(M_PI*x))*(double)(KernelBessel_P1(x)*(1.0f/sqrt(2.0f)*(sin(x)-cos(x)))-8.0f/x*KernelBessel_Q1(x)*
299        (-1.0f/sqrt(2.0f)*(sin(x)+cos(x))));
300    if (p < 0.0f)
301        q = (-q);
302    return (q);
303}
304
305static double filter_bessel(const double x)
306{
307    if (x == 0.0f)
308        return (double)(M_PI/4.0f);
309    return (KernelBessel_Order1((double)M_PI*x)/(2.0f*x));
310}
311
312
313static double filter_blackman(const double x)
314{
315    return (0.42f+0.5f*(double)cos(M_PI*x)+0.08f*(double)cos(2.0f*M_PI*x));
316}
317
318/**
319 * Bicubic interpolation kernel (a=-1):
320  \verbatim
321          /
322         | 1-2|t|**2+|t|**3          , if |t| < 1
323  h(t) = | 4-8|t|+5|t|**2-|t|**3     , if 1<=|t|<2
324         | 0                         , otherwise
325          \
326  \endverbatim
327 * ***bd*** 2.2004
328 */
329static double filter_bicubic(const double t)
330{
331  const double abs_t = (double)fabs(t);
332  const double abs_t_sq = abs_t * abs_t;
333  if (abs_t<1) return 1-2*abs_t_sq+abs_t_sq*abs_t;
334  if (abs_t<2) return 4 - 8*abs_t +5*abs_t_sq - abs_t_sq*abs_t;
335  return 0;
336}
337
338/**
339 * Generalized cubic kernel (for a=-1 it is the same as BicubicKernel):
340  \verbatim
341          /
342         | (a+2)|t|**3 - (a+3)|t|**2 + 1     , |t| <= 1
343  h(t) = | a|t|**3 - 5a|t|**2 + 8a|t| - 4a   , 1 < |t| <= 2
344         | 0                                 , otherwise
345          \
346  \endverbatim
347 * Often used values for a are -1 and -1/2.
348 */
349static double filter_generalized_cubic(const double t)
350{
351    const double a = -DEFAULT_FILTER_GENERALIZED_CUBIC;
352    double abs_t = (double)fabs(t);
353    double abs_t_sq = abs_t * abs_t;
354    if (abs_t < 1) return (a + 2) * abs_t_sq * abs_t - (a + 3) * abs_t_sq + 1;
355    if (abs_t < 2) return a * abs_t_sq * abs_t - 5 * a * abs_t_sq + 8 * a * abs_t - 4 * a;
356    return 0;
357}
358
359/* CubicSpline filter, default radius 2 */
360static double filter_cubic_spline(const double x1)
361{
362    const double x = x1 < 0.0 ? -x1 : x1;
363
364    if (x < 1.0 ) {
365        const double x2 = x*x;
366
367        return (0.5 * x2 * x - x2 + 2.0 / 3.0);
368    }
369    if (x < 2.0) {
370        return (pow(2.0 - x, 3.0)/6.0);
371    }
372    return 0;
373}
374
375/* CubicConvolution filter, default radius 3 */
376static double filter_cubic_convolution(const double x1)
377{
378    const double x = x1 < 0.0 ? -x1 : x1;
379    const double x2 = x1 * x1;
380    const double x2_x = x2 * x;
381
382    if (x <= 1.0) return ((4.0 / 3.0)* x2_x - (7.0 / 3.0) * x2 + 1.0);
383    if (x <= 2.0) return (- (7.0 / 12.0) * x2_x + 3 * x2 - (59.0 / 12.0) * x + 2.5);
384    if (x <= 3.0) return ( (1.0/12.0) * x2_x - (2.0 / 3.0) * x2 + 1.75 * x - 1.5);
385    return 0;
386}
387
388static double filter_box(double x) {
389    if (x < - DEFAULT_FILTER_BOX)
390        return 0.0f;
391    if (x < DEFAULT_FILTER_BOX)
392        return 1.0f;
393    return 0.0f;
394}
395
396static double filter_catmullrom(const double x)
397{
398    if (x < -2.0)
399        return(0.0f);
400    if (x < -1.0)
401        return(0.5f*(4.0f+x*(8.0f+x*(5.0f+x))));
402    if (x < 0.0)
403        return(0.5f*(2.0f+x*x*(-5.0f-3.0f*x)));
404    if (x < 1.0)
405        return(0.5f*(2.0f+x*x*(-5.0f+3.0f*x)));
406    if (x < 2.0)
407        return(0.5f*(4.0f+x*(-8.0f+x*(5.0f-x))));
408    return(0.0f);
409}
410
411static double filter_filter(double t)
412{
413    /* f(t) = 2|t|^3 - 3|t|^2 + 1, -1 <= t <= 1 */
414    if(t < 0.0) t = -t;
415    if(t < 1.0) return((2.0 * t - 3.0) * t * t + 1.0);
416    return(0.0);
417}
418
419
420/* Lanczos8 filter, default radius 8 */
421static double filter_lanczos8(const double x1)
422{
423    const double x = x1 < 0.0 ? -x1 : x1;
424#define R DEFAULT_LANCZOS8_RADIUS
425
426    if ( x == 0.0) return 1;
427
428    if ( x < R) {
429        return R * sin(x*M_PI) * sin(x * M_PI/ R) / (x * M_PI * x * M_PI);
430    }
431    return 0.0;
432#undef R
433}
434
435
436/* Lanczos3 filter, default radius 3 */
437static double filter_lanczos3(const double x1)
438{
439    const double x = x1 < 0.0 ? -x1 : x1;
440#define R DEFAULT_LANCZOS3_RADIUS
441
442    if ( x == 0.0) return 1;
443
444    if ( x < R)
445    {
446        return R * sin(x*M_PI) * sin(x * M_PI / R) / (x * M_PI * x * M_PI);
447    }
448    return 0.0;
449#undef R
450}
451
452/* Hermite filter, default radius 1 */
453static double filter_hermite(const double x1)
454{
455    const double x = x1 < 0.0 ? -x1 : x1;
456
457    if (x < 1.0) return ((2.0 * x - 3) * x * x + 1.0 );
458
459    return 0.0;
460}
461
462/* Trangle filter, default radius 1 */
463static double filter_triangle(const double x1)
464{
465    const double x = x1 < 0.0 ? -x1 : x1;
466    if (x < 1.0) return (1.0 - x);
467    return 0.0;
468}
469
470/* Bell filter, default radius 1.5 */
471static double filter_bell(const double x1)
472{
473    const double x = x1 < 0.0 ? -x1 : x1;
474
475    if (x < 0.5) return (0.75 - x*x);
476    if (x < 1.5) return (0.5 * pow(x - 1.5, 2.0));
477    return 0.0;
478}
479
480/* Mitchell filter, default radius 2.0 */
481static double filter_mitchell(const double x)
482{
483#define KM_B (1.0f/3.0f)
484#define KM_C (1.0f/3.0f)
485#define KM_P0 ((  6.0f - 2.0f * KM_B ) / 6.0f)
486#define KM_P2 ((-18.0f + 12.0f * KM_B + 6.0f * KM_C) / 6.0f)
487#define KM_P3 (( 12.0f - 9.0f  * KM_B - 6.0f * KM_C) / 6.0f)
488#define KM_Q0 ((  8.0f * KM_B + 24.0f * KM_C) / 6.0f)
489#define KM_Q1 ((-12.0f * KM_B - 48.0f * KM_C) / 6.0f)
490#define KM_Q2 ((  6.0f * KM_B + 30.0f * KM_C) / 6.0f)
491#define KM_Q3 (( -1.0f * KM_B -  6.0f * KM_C) / 6.0f)
492
493    if (x < -2.0)
494        return(0.0f);
495    if (x < -1.0)
496        return(KM_Q0-x*(KM_Q1-x*(KM_Q2-x*KM_Q3)));
497    if (x < 0.0f)
498        return(KM_P0+x*x*(KM_P2-x*KM_P3));
499    if (x < 1.0f)
500        return(KM_P0+x*x*(KM_P2+x*KM_P3));
501    if (x < 2.0f)
502        return(KM_Q0+x*(KM_Q1+x*(KM_Q2+x*KM_Q3)));
503    return(0.0f);
504}
505
506
507
508/* Cosine filter, default radius 1 */
509static double filter_cosine(const double x)
510{
511    if ((x >= -1.0) && (x <= 1.0)) return ((cos(x * M_PI) + 1.0)/2.0);
512
513    return 0;
514}
515
516/* Quadratic filter, default radius 1.5 */
517static double filter_quadratic(const double x1)
518{
519    const double x = x1 < 0.0 ? -x1 : x1;
520
521    if (x <= 0.5) return (- 2.0 * x * x + 1);
522    if (x <= 1.5) return (x * x - 2.5* x + 1.5);
523    return 0.0;
524}
525
526static double filter_bspline(const double x)
527{
528    if (x>2.0f) {
529        return 0.0f;
530    } else {
531        double a, b, c, d;
532        /* Was calculated anyway cause the "if((x-1.0f) < 0)" */
533        const double xm1 = x - 1.0f;
534        const double xp1 = x + 1.0f;
535        const double xp2 = x + 2.0f;
536
537        if ((xp2) <= 0.0f) a = 0.0f; else a = xp2*xp2*xp2;
538        if ((xp1) <= 0.0f) b = 0.0f; else b = xp1*xp1*xp1;
539        if (x <= 0) c = 0.0f; else c = x*x*x;
540        if ((xm1) <= 0.0f) d = 0.0f; else d = xm1*xm1*xm1;
541
542        return (0.16666666666666666667f * (a - (4.0f * b) + (6.0f * c) - (4.0f * d)));
543    }
544}
545
546/* QuadraticBSpline filter, default radius 1.5 */
547static double filter_quadratic_bspline(const double x1)
548{
549    const double x = x1 < 0.0 ? -x1 : x1;
550
551    if (x <= 0.5) return (- x * x + 0.75);
552    if (x <= 1.5) return (0.5 * x * x - 1.5 * x + 1.125);
553    return 0.0;
554}
555
556static double filter_gaussian(const double x)
557{
558    /* return(exp((double) (-2.0 * x * x)) * sqrt(2.0 / M_PI)); */
559    return (double)(exp(-2.0f * x * x) * 0.79788456080287f);
560}
561
562static double filter_hanning(const double x)
563{
564    /* A Cosine windowing function */
565    return(0.5 + 0.5 * cos(M_PI * x));
566}
567
568static double filter_hamming(const double x)
569{
570    /* should be
571    (0.54+0.46*cos(M_PI*(double) x));
572    but this approximation is sufficient */
573    if (x < -1.0f)
574        return 0.0f;
575    if (x < 0.0f)
576        return 0.92f*(-2.0f*x-3.0f)*x*x+1.0f;
577    if (x < 1.0f)
578        return 0.92f*(2.0f*x-3.0f)*x*x+1.0f;
579    return 0.0f;
580}
581
582static double filter_power(const double x)
583{
584    const double a = 2.0f;
585    if (fabs(x)>1) return 0.0f;
586    return (1.0f - (double)fabs(pow(x,a)));
587}
588
589static double filter_sinc(const double x)
590{
591    /* X-scaled Sinc(x) function. */
592    if (x == 0.0) return(1.0);
593    return (sin(M_PI * (double) x) / (M_PI * (double) x));
594}
595
596static double filter_welsh(const double x)
597{
598    /* Welsh parabolic windowing filter */
599    if (x <  1.0)
600        return(1 - x*x);
601    return(0.0);
602}
603
604
605/* Copied from upstream's libgd */
606static inline int _color_blend (const int dst, const int src)
607{
608    const int src_alpha = gdTrueColorGetAlpha(src);
609
610    if( src_alpha == gdAlphaOpaque ) {
611        return src;
612    } else {
613        const int dst_alpha = gdTrueColorGetAlpha(dst);
614
615        if( src_alpha == gdAlphaTransparent ) return dst;
616        if( dst_alpha == gdAlphaTransparent ) {
617            return src;
618        } else {
619            register int alpha, red, green, blue;
620            const int src_weight = gdAlphaTransparent - src_alpha;
621            const int dst_weight = (gdAlphaTransparent - dst_alpha) * src_alpha / gdAlphaMax;
622            const int tot_weight = src_weight + dst_weight;
623
624            alpha = src_alpha * dst_alpha / gdAlphaMax;
625
626            red = (gdTrueColorGetRed(src) * src_weight
627                   + gdTrueColorGetRed(dst) * dst_weight) / tot_weight;
628            green = (gdTrueColorGetGreen(src) * src_weight
629                   + gdTrueColorGetGreen(dst) * dst_weight) / tot_weight;
630            blue = (gdTrueColorGetBlue(src) * src_weight
631                   + gdTrueColorGetBlue(dst) * dst_weight) / tot_weight;
632
633            return ((alpha << 24) + (red << 16) + (green << 8) + blue);
634        }
635    }
636}
637
638static inline int _setEdgePixel(const gdImagePtr src, unsigned int x, unsigned int y, gdFixed coverage, const int bgColor)
639{
640    const gdFixed f_127 = gd_itofx(127);
641    register int c = src->tpixels[y][x];
642    c = c | (( (int) (gd_fxtof(gd_mulfx(coverage, f_127)) + 50.5f)) << 24);
643    return _color_blend(bgColor, c);
644}
645
646static inline int getPixelOverflowTC(gdImagePtr im, const int x, const int y, const int bgColor)
647{
648    if (gdImageBoundsSafe(im, x, y)) {
649        const int c = im->tpixels[y][x];
650        if (c == im->transparent) {
651            return bgColor == -1 ? gdTrueColorAlpha(0, 0, 0, 127) : bgColor;
652        }
653        return c;
654    } else {
655        register int border = 0;
656
657        if (y < im->cy1) {
658            border = im->tpixels[0][im->cx1];
659            goto processborder;
660        }
661
662        if (y < im->cy1) {
663            border = im->tpixels[0][im->cx1];
664            goto processborder;
665        }
666
667        if (y > im->cy2) {
668            if (x >= im->cx1 && x <= im->cx1) {
669                border = im->tpixels[im->cy2][x];
670                goto processborder;
671            } else {
672                return gdTrueColorAlpha(0, 0, 0, 127);
673            }
674        }
675
676        /* y is bound safe at this point */
677        if (x < im->cx1) {
678            border = im->tpixels[y][im->cx1];
679            goto processborder;
680        }
681
682        if (x > im->cx2) {
683            border = im->tpixels[y][im->cx2];
684        }
685
686processborder:
687        if (border == im->transparent) {
688            return gdTrueColorAlpha(0, 0, 0, 127);
689        } else{
690            return gdTrueColorAlpha(gdTrueColorGetRed(border), gdTrueColorGetGreen(border), gdTrueColorGetBlue(border), 127);
691        }
692    }
693}
694
695#define colorIndex2RGBA(c) gdTrueColorAlpha(im->red[(c)], im->green[(c)], im->blue[(c)], im->alpha[(c)])
696#define colorIndex2RGBcustomA(c, a) gdTrueColorAlpha(im->red[(c)], im->green[(c)], im->blue[(c)], im->alpha[(a)])
697static inline int getPixelOverflowPalette(gdImagePtr im, const int x, const int y, const int bgColor)
698{
699    if (gdImageBoundsSafe(im, x, y)) {
700        const int c = im->pixels[y][x];
701        if (c == im->transparent) {
702            return bgColor == -1 ? gdTrueColorAlpha(0, 0, 0, 127) : bgColor;
703        }
704        return colorIndex2RGBA(c);
705    } else {
706        register int border = 0;
707        if (y < im->cy1) {
708            border = gdImageGetPixel(im, im->cx1, 0);
709            goto processborder;
710        }
711
712        if (y < im->cy1) {
713            border = gdImageGetPixel(im, im->cx1, 0);
714            goto processborder;
715        }
716
717        if (y > im->cy2) {
718            if (x >= im->cx1 && x <= im->cx1) {
719                border = gdImageGetPixel(im, x,  im->cy2);
720                goto processborder;
721            } else {
722                return gdTrueColorAlpha(0, 0, 0, 127);
723            }
724        }
725
726        /* y is bound safe at this point */
727        if (x < im->cx1) {
728            border = gdImageGetPixel(im, im->cx1, y);
729            goto processborder;
730        }
731
732        if (x > im->cx2) {
733            border = gdImageGetPixel(im, im->cx2, y);
734        }
735
736processborder:
737        if (border == im->transparent) {
738            return gdTrueColorAlpha(0, 0, 0, 127);
739        } else{
740            return colorIndex2RGBcustomA(border, 127);
741        }
742    }
743}
744
745static int getPixelInterpolateWeight(gdImagePtr im, const double x, const double y, const int bgColor)
746{
747    /* Closest pixel <= (xf,yf) */
748    int sx = (int)(x);
749    int sy = (int)(y);
750    const double xf = x - (double)sx;
751    const double yf = y - (double)sy;
752    const double nxf = (double) 1.0 - xf;
753    const double nyf = (double) 1.0 - yf;
754    const double m1 = xf * yf;
755    const double m2 = nxf * yf;
756    const double m3 = xf * nyf;
757    const double m4 = nxf * nyf;
758
759    /* get color values of neighbouring pixels */
760    const int c1 = im->trueColor == 1 ? getPixelOverflowTC(im, sx, sy, bgColor)         : getPixelOverflowPalette(im, sx, sy, bgColor);
761    const int c2 = im->trueColor == 1 ? getPixelOverflowTC(im, sx - 1, sy, bgColor)     : getPixelOverflowPalette(im, sx - 1, sy, bgColor);
762    const int c3 = im->trueColor == 1 ? getPixelOverflowTC(im, sx, sy - 1, bgColor)     : getPixelOverflowPalette(im, sx, sy - 1, bgColor);
763    const int c4 = im->trueColor == 1 ? getPixelOverflowTC(im, sx - 1, sy - 1, bgColor) : getPixelOverflowPalette(im, sx, sy - 1, bgColor);
764    int r, g, b, a;
765
766    if (x < 0) sx--;
767    if (y < 0) sy--;
768
769    /* component-wise summing-up of color values */
770    if (im->trueColor) {
771        r = (int)(m1*gdTrueColorGetRed(c1)   + m2*gdTrueColorGetRed(c2)   + m3*gdTrueColorGetRed(c3)   + m4*gdTrueColorGetRed(c4));
772        g = (int)(m1*gdTrueColorGetGreen(c1) + m2*gdTrueColorGetGreen(c2) + m3*gdTrueColorGetGreen(c3) + m4*gdTrueColorGetGreen(c4));
773        b = (int)(m1*gdTrueColorGetBlue(c1)  + m2*gdTrueColorGetBlue(c2)  + m3*gdTrueColorGetBlue(c3)  + m4*gdTrueColorGetBlue(c4));
774        a = (int)(m1*gdTrueColorGetAlpha(c1) + m2*gdTrueColorGetAlpha(c2) + m3*gdTrueColorGetAlpha(c3) + m4*gdTrueColorGetAlpha(c4));
775    } else {
776        r = (int)(m1*im->red[(c1)]   + m2*im->red[(c2)]   + m3*im->red[(c3)]   + m4*im->red[(c4)]);
777        g = (int)(m1*im->green[(c1)] + m2*im->green[(c2)] + m3*im->green[(c3)] + m4*im->green[(c4)]);
778        b = (int)(m1*im->blue[(c1)]  + m2*im->blue[(c2)]  + m3*im->blue[(c3)]  + m4*im->blue[(c4)]);
779        a = (int)(m1*im->alpha[(c1)] + m2*im->alpha[(c2)] + m3*im->alpha[(c3)] + m4*im->alpha[(c4)]);
780    }
781
782    r = CLAMP(r, 0, 255);
783    g = CLAMP(g, 0, 255);
784    b = CLAMP(b, 0, 255);
785    a = CLAMP(a, 0, gdAlphaMax);
786    return gdTrueColorAlpha(r, g, b, a);
787}
788
789/**
790 * Function: getPixelInterpolated
791 *  Returns the interpolated color value using the default interpolation
792 *  method. The returned color is always in the ARGB format (truecolor).
793 *
794 * Parameters:
795 *  im - Image to set the default interpolation method
796 *  y - X value of the ideal position
797 *  y - Y value of the ideal position
798 *  method - Interpolation method <gdInterpolationMethod>
799 *
800 * Returns:
801 *  GD_TRUE if the affine is rectilinear or GD_FALSE
802 *
803 * See also:
804 *  <gdSetInterpolationMethod>
805 */
806int getPixelInterpolated(gdImagePtr im, const double x, const double y, const int bgColor)
807{
808    const int xi=(int)((x) < 0 ? x - 1: x);
809    const int yi=(int)((y) < 0 ? y - 1: y);
810    int yii;
811    int i;
812    double kernel, kernel_cache_y;
813    double kernel_x[12], kernel_y[4];
814    double new_r = 0.0f, new_g = 0.0f, new_b = 0.0f, new_a = 0.0f;
815
816    /* These methods use special implementations */
817    if (im->interpolation_id == GD_BILINEAR_FIXED || im->interpolation_id == GD_BICUBIC_FIXED || im->interpolation_id == GD_NEAREST_NEIGHBOUR) {
818        return -1;
819    }
820
821    if (im->interpolation_id == GD_WEIGHTED4) {
822        return getPixelInterpolateWeight(im, x, y, bgColor);
823    }
824
825    if (im->interpolation_id == GD_NEAREST_NEIGHBOUR) {
826        if (im->trueColor == 1) {
827            return getPixelOverflowTC(im, xi, yi, bgColor);
828        } else {
829            return getPixelOverflowPalette(im, xi, yi, bgColor);
830        }
831    }
832    if (im->interpolation) {
833        for (i=0; i<4; i++) {
834            kernel_x[i] = (double) im->interpolation((double)(xi+i-1-x));
835            kernel_y[i] = (double) im->interpolation((double)(yi+i-1-y));
836        }
837    } else {
838        return -1;
839    }
840
841    /*
842     * TODO: use the known fast rgba multiplication implementation once
843     * the new formats are in place
844     */
845    for (yii = yi-1; yii < yi+3; yii++) {
846        int xii;
847        kernel_cache_y = kernel_y[yii-(yi-1)];
848        if (im->trueColor) {
849            for (xii=xi-1; xii<xi+3; xii++) {
850                const int rgbs = getPixelOverflowTC(im, xii, yii, bgColor);
851
852                kernel = kernel_cache_y * kernel_x[xii-(xi-1)];
853                new_r += kernel * gdTrueColorGetRed(rgbs);
854                new_g += kernel * gdTrueColorGetGreen(rgbs);
855                new_b += kernel * gdTrueColorGetBlue(rgbs);
856                new_a += kernel * gdTrueColorGetAlpha(rgbs);
857            }
858        } else {
859            for (xii=xi-1; xii<xi+3; xii++) {
860                const int rgbs = getPixelOverflowPalette(im, xii, yii, bgColor);
861
862                kernel = kernel_cache_y * kernel_x[xii-(xi-1)];
863                new_r += kernel * gdTrueColorGetRed(rgbs);
864                new_g += kernel * gdTrueColorGetGreen(rgbs);
865                new_b += kernel * gdTrueColorGetBlue(rgbs);
866                new_a += kernel * gdTrueColorGetAlpha(rgbs);
867            }
868        }
869    }
870
871    new_r = CLAMP(new_r, 0, 255);
872    new_g = CLAMP(new_g, 0, 255);
873    new_b = CLAMP(new_b, 0, 255);
874    new_a = CLAMP(new_a, 0, gdAlphaMax);
875
876    return gdTrueColorAlpha(((int)new_r), ((int)new_g), ((int)new_b), ((int)new_a));
877}
878
879static inline LineContribType * _gdContributionsAlloc(unsigned int line_length, unsigned int windows_size)
880{
881    unsigned int u = 0;
882    LineContribType *res;
883
884    res = (LineContribType *) gdMalloc(sizeof(LineContribType));
885    if (!res) {
886        return NULL;
887    }
888    res->WindowSize = windows_size;
889    res->LineLength = line_length;
890    res->ContribRow = (ContributionType *) gdMalloc(line_length * sizeof(ContributionType));
891
892    for (u = 0 ; u < line_length ; u++) {
893        res->ContribRow[u].Weights = (double *) gdMalloc(windows_size * sizeof(double));
894    }
895    return res;
896}
897
898static inline void _gdContributionsFree(LineContribType * p)
899{
900    unsigned int u;
901    for (u = 0; u < p->LineLength; u++)  {
902        gdFree(p->ContribRow[u].Weights);
903    }
904    gdFree(p->ContribRow);
905    gdFree(p);
906}
907
908static inline LineContribType *_gdContributionsCalc(unsigned int line_size, unsigned int src_size, double scale_d,  const interpolation_method pFilter)
909{
910    double width_d;
911    double scale_f_d = 1.0;
912    const double filter_width_d = DEFAULT_BOX_RADIUS;
913    int windows_size;
914    unsigned int u;
915    LineContribType *res;
916
917    if (scale_d < 1.0) {
918        width_d = filter_width_d / scale_d;
919        scale_f_d = scale_d;
920    }  else {
921        width_d= filter_width_d;
922    }
923
924    windows_size = 2 * (int)ceil(width_d) + 1;
925    res = _gdContributionsAlloc(line_size, windows_size);
926
927    for (u = 0; u < line_size; u++) {
928        const double dCenter = (double)u / scale_d;
929        /* get the significant edge points affecting the pixel */
930        register int iLeft = MAX(0, (int)floor (dCenter - width_d));
931        int iRight = MIN((int)ceil(dCenter + width_d), (int)src_size - 1);
932        double dTotalWeight = 0.0;
933        int iSrc;
934
935        res->ContribRow[u].Left = iLeft;
936        res->ContribRow[u].Right = iRight;
937
938        /* Cut edge points to fit in filter window in case of spill-off */
939        if (iRight - iLeft + 1 > windows_size)  {
940            if (iLeft < ((int)src_size - 1 / 2))  {
941                iLeft++;
942            } else {
943                iRight--;
944            }
945        }
946
947        for (iSrc = iLeft; iSrc <= iRight; iSrc++) {
948            dTotalWeight += (res->ContribRow[u].Weights[iSrc-iLeft] =  scale_f_d * (*pFilter)(scale_f_d * (dCenter - (double)iSrc)));
949        }
950
951        if (dTotalWeight < 0.0) {
952            _gdContributionsFree(res);
953            return NULL;
954        }
955
956        if (dTotalWeight > 0.0) {
957            for (iSrc = iLeft; iSrc <= iRight; iSrc++) {
958                res->ContribRow[u].Weights[iSrc-iLeft] /= dTotalWeight;
959            }
960        }
961   }
962   return res;
963}
964
965static inline void _gdScaleRow(gdImagePtr pSrc,  unsigned int src_width, gdImagePtr dst, unsigned int dst_width, unsigned int row, LineContribType *contrib)
966{
967    int *p_src_row = pSrc->tpixels[row];
968    int *p_dst_row = dst->tpixels[row];
969    unsigned int x;
970
971    for (x = 0; x < dst_width - 1; x++) {
972        register unsigned char r = 0, g = 0, b = 0, a = 0;
973        const int left = contrib->ContribRow[x].Left;
974        const int right = contrib->ContribRow[x].Right;
975        int i;
976
977        /* Accumulate each channel */
978        for (i = left; i <= right; i++) {
979            const int left_channel = i - left;
980            r += (unsigned char)(contrib->ContribRow[x].Weights[left_channel] * (double)(gdTrueColorGetRed(p_src_row[i])));
981            g += (unsigned char)(contrib->ContribRow[x].Weights[left_channel] * (double)(gdTrueColorGetGreen(p_src_row[i])));
982            b += (unsigned char)(contrib->ContribRow[x].Weights[left_channel] * (double)(gdTrueColorGetBlue(p_src_row[i])));
983            a += (unsigned char)(contrib->ContribRow[x].Weights[left_channel] * (double)(gdTrueColorGetAlpha(p_src_row[i])));
984        }
985        p_dst_row[x] = gdTrueColorAlpha(r, g, b, a);
986    }
987}
988
989static inline void _gdScaleHoriz(gdImagePtr pSrc, unsigned int src_width, unsigned int src_height, gdImagePtr pDst,  unsigned int dst_width, unsigned int dst_height)
990{
991    unsigned int u;
992    LineContribType * contrib;
993
994    /* same width, just copy it */
995    if (dst_width == src_width) {
996        unsigned int y;
997        for (y = 0; y < src_height - 1; ++y) {
998            memcpy(pDst->tpixels[y], pSrc->tpixels[y], src_width);
999        }
1000    }
1001
1002    contrib = _gdContributionsCalc(dst_width, src_width, (double)dst_width / (double)src_width, pSrc->interpolation);
1003    if (contrib == NULL) {
1004        return;
1005    }
1006    /* Scale each row */
1007    for (u = 0; u < dst_height - 1; u++) {
1008        _gdScaleRow(pSrc, src_width, pDst, dst_width, u, contrib);
1009    }
1010    _gdContributionsFree (contrib);
1011}
1012
1013static inline void _gdScaleCol (gdImagePtr pSrc,  unsigned int src_width, gdImagePtr pRes, unsigned int dst_width, unsigned int dst_height, unsigned int uCol, LineContribType *contrib)
1014{
1015    unsigned int y;
1016    for (y = 0; y < dst_height - 1; y++) {
1017        register unsigned char r = 0, g = 0, b = 0, a = 0;
1018        const int iLeft = contrib->ContribRow[y].Left;
1019        const int iRight = contrib->ContribRow[y].Right;
1020        int i;
1021        int *row = pRes->tpixels[y];
1022
1023        /* Accumulate each channel */
1024        for (i = iLeft; i <= iRight; i++) {
1025            const int pCurSrc = pSrc->tpixels[i][uCol];
1026            const int i_iLeft = i - iLeft;
1027            r += (unsigned char)(contrib->ContribRow[y].Weights[i_iLeft] * (double)(gdTrueColorGetRed(pCurSrc)));
1028            g += (unsigned char)(contrib->ContribRow[y].Weights[i_iLeft] * (double)(gdTrueColorGetGreen(pCurSrc)));
1029            b += (unsigned char)(contrib->ContribRow[y].Weights[i_iLeft] * (double)(gdTrueColorGetBlue(pCurSrc)));
1030            a += (unsigned char)(contrib->ContribRow[y].Weights[i_iLeft] * (double)(gdTrueColorGetAlpha(pCurSrc)));
1031        }
1032        pRes->tpixels[y][uCol] = gdTrueColorAlpha(r, g, b, a);
1033    }
1034}
1035
1036static inline void _gdScaleVert (const gdImagePtr pSrc, const unsigned int src_width, const unsigned int src_height, const gdImagePtr pDst, const unsigned int dst_width, const unsigned int dst_height)
1037{
1038    unsigned int u;
1039    LineContribType * contrib;
1040
1041    /* same height, copy it */
1042    if (src_height == dst_height) {
1043        unsigned int y;
1044        for (y = 0; y < src_height - 1; ++y) {
1045            memcpy(pDst->tpixels[y], pSrc->tpixels[y], src_width);
1046        }
1047    }
1048
1049    contrib = _gdContributionsCalc(dst_height, src_height, (double)(dst_height) / (double)(src_height), pSrc->interpolation);
1050    /* scale each column */
1051    for (u = 0; u < dst_width - 1; u++) {
1052        _gdScaleCol(pSrc, src_width, pDst, dst_width, dst_height, u, contrib);
1053    }
1054    _gdContributionsFree(contrib);
1055}
1056
1057gdImagePtr gdImageScaleTwoPass(const gdImagePtr src, const unsigned int src_width, const unsigned int src_height, const unsigned int new_width, const unsigned int new_height)
1058{
1059    gdImagePtr tmp_im;
1060    gdImagePtr dst;
1061
1062    tmp_im = gdImageCreateTrueColor(new_width, src_height);
1063    if (tmp_im == NULL) {
1064        return NULL;
1065    }
1066    gdImageSetInterpolationMethod(tmp_im, src->interpolation_id);
1067    _gdScaleHoriz(src, src_width, src_height, tmp_im, new_width, src_height);
1068
1069    dst = gdImageCreateTrueColor(new_width, new_height);
1070    if (dst == NULL) {
1071        gdFree(tmp_im);
1072        return NULL;
1073    }
1074    gdImageSetInterpolationMethod(dst, src->interpolation_id);
1075    _gdScaleVert(tmp_im, new_width, src_height, dst, new_width, new_height);
1076    gdFree(tmp_im);
1077
1078    return dst;
1079}
1080
1081gdImagePtr Scale(const gdImagePtr src, const unsigned int src_width, const unsigned int src_height, const gdImagePtr dst, const unsigned int new_width, const unsigned int new_height)
1082{
1083    gdImagePtr tmp_im;
1084
1085    tmp_im = gdImageCreateTrueColor(new_width, src_height);
1086    if (tmp_im == NULL) {
1087        return NULL;
1088    }
1089    gdImageSetInterpolationMethod(tmp_im, src->interpolation_id);
1090
1091    _gdScaleHoriz(src, src_width, src_height, tmp_im, new_width, src_height);
1092    _gdScaleVert(tmp_im, new_width, src_height, dst, new_width, new_height);
1093
1094    gdFree(tmp_im);
1095    return dst;
1096}
1097
1098/*
1099    BilinearFixed, BicubicFixed and nearest implementations are rewamped versions of the implementation in CBitmapEx
1100    http://www.codeproject.com/Articles/29121/CBitmapEx-Free-C-Bitmap-Manipulation-Class
1101    Integer only implementation, good to have for common usages like pre scale very large
1102    images before using another interpolation methods for the last step.
1103*/
1104gdImagePtr gdImageScaleNearestNeighbour(gdImagePtr im, const unsigned int width, const unsigned int height)
1105{
1106    const unsigned long new_width = MAX(1, width);
1107    const unsigned long new_height = MAX(1, height);
1108    const float dx = (float)im->sx / (float)new_width;
1109    const float dy = (float)im->sy / (float)new_height;
1110    const gdFixed f_dx = gd_ftofx(dx);
1111    const gdFixed f_dy = gd_ftofx(dy);
1112
1113    gdImagePtr dst_img;
1114    unsigned long  dst_offset_x;
1115    unsigned long  dst_offset_y = 0;
1116    unsigned int i;
1117
1118    dst_img = gdImageCreateTrueColor(new_width, new_height);
1119
1120    if (dst_img == NULL) {
1121        return NULL;
1122    }
1123
1124    for (i=0; i<new_height; i++) {
1125        unsigned int j;
1126        dst_offset_x = 0;
1127        if (im->trueColor) {
1128            for (j=0; j<new_width; j++) {
1129                const gdFixed f_i = gd_itofx(i);
1130                const gdFixed f_j = gd_itofx(j);
1131                const gdFixed f_a = gd_mulfx(f_i, f_dy);
1132                const gdFixed f_b = gd_mulfx(f_j, f_dx);
1133                const long m = gd_fxtoi(f_a);
1134                const long n = gd_fxtoi(f_b);
1135
1136                dst_img->tpixels[dst_offset_y][dst_offset_x++] = im->tpixels[m][n];
1137            }
1138        } else {
1139            for (j=0; j<new_width; j++) {
1140                const gdFixed f_i = gd_itofx(i);
1141                const gdFixed f_j = gd_itofx(j);
1142                const gdFixed f_a = gd_mulfx(f_i, f_dy);
1143                const gdFixed f_b = gd_mulfx(f_j, f_dx);
1144                const long m = gd_fxtoi(f_a);
1145                const long n = gd_fxtoi(f_b);
1146
1147                dst_img->tpixels[dst_offset_y][dst_offset_x++] = colorIndex2RGBA(im->pixels[m][n]);
1148            }
1149        }
1150        dst_offset_y++;
1151    }
1152    return dst_img;
1153}
1154
1155static inline int getPixelOverflowColorTC(gdImagePtr im, const int x, const int y, const int color)
1156{
1157    if (gdImageBoundsSafe(im, x, y)) {
1158        const int c = im->tpixels[y][x];
1159        if (c == im->transparent) {
1160            return gdTrueColorAlpha(0, 0, 0, 127);
1161        }
1162        return c;
1163    } else {
1164        register int border = 0;
1165        if (y < im->cy1) {
1166            border = im->tpixels[0][im->cx1];
1167            goto processborder;
1168        }
1169
1170        if (y < im->cy1) {
1171            border = im->tpixels[0][im->cx1];
1172            goto processborder;
1173        }
1174
1175        if (y > im->cy2) {
1176            if (x >= im->cx1 && x <= im->cx1) {
1177                border = im->tpixels[im->cy2][x];
1178                goto processborder;
1179            } else {
1180                return gdTrueColorAlpha(0, 0, 0, 127);
1181            }
1182        }
1183
1184        /* y is bound safe at this point */
1185        if (x < im->cx1) {
1186            border = im->tpixels[y][im->cx1];
1187            goto processborder;
1188        }
1189
1190        if (x > im->cx2) {
1191            border = im->tpixels[y][im->cx2];
1192        }
1193
1194processborder:
1195        if (border == im->transparent) {
1196            return gdTrueColorAlpha(0, 0, 0, 127);
1197        } else{
1198            return gdTrueColorAlpha(gdTrueColorGetRed(border), gdTrueColorGetGreen(border), gdTrueColorGetBlue(border), 127);
1199        }
1200    }
1201}
1202
1203static gdImagePtr gdImageScaleBilinearPalette(gdImagePtr im, const unsigned int new_width, const unsigned int new_height)
1204{
1205    long _width = MAX(1, new_width);
1206    long _height = MAX(1, new_height);
1207    float dx = (float)gdImageSX(im) / (float)_width;
1208    float dy = (float)gdImageSY(im) / (float)_height;
1209    gdFixed f_dx = gd_ftofx(dx);
1210    gdFixed f_dy = gd_ftofx(dy);
1211    gdFixed f_1 = gd_itofx(1);
1212
1213    int dst_offset_h;
1214    int dst_offset_v = 0;
1215    long i;
1216    gdImagePtr new_img;
1217    const int transparent = im->transparent;
1218
1219    new_img = gdImageCreateTrueColor(new_width, new_height);
1220    if (new_img == NULL) {
1221        return NULL;
1222    }
1223    new_img->transparent = gdTrueColorAlpha(im->red[transparent], im->green[transparent], im->blue[transparent], im->alpha[transparent]);
1224
1225    for (i=0; i < _height; i++) {
1226        long j;
1227        const gdFixed f_i = gd_itofx(i);
1228        const gdFixed f_a = gd_mulfx(f_i, f_dy);
1229        register long m = gd_fxtoi(f_a);
1230
1231        dst_offset_h = 0;
1232
1233        for (j=0; j < _width; j++) {
1234            /* Update bitmap */
1235            gdFixed f_j = gd_itofx(j);
1236            gdFixed f_b = gd_mulfx(f_j, f_dx);
1237
1238            const long n = gd_fxtoi(f_b);
1239            gdFixed f_f = f_a - gd_itofx(m);
1240            gdFixed f_g = f_b - gd_itofx(n);
1241
1242            const gdFixed f_w1 = gd_mulfx(f_1-f_f, f_1-f_g);
1243            const gdFixed f_w2 = gd_mulfx(f_1-f_f, f_g);
1244            const gdFixed f_w3 = gd_mulfx(f_f, f_1-f_g);
1245            const gdFixed f_w4 = gd_mulfx(f_f, f_g);
1246            unsigned int pixel1;
1247            unsigned int pixel2;
1248            unsigned int pixel3;
1249            unsigned int pixel4;
1250            register gdFixed f_r1, f_r2, f_r3, f_r4,
1251                    f_g1, f_g2, f_g3, f_g4,
1252                    f_b1, f_b2, f_b3, f_b4,
1253                    f_a1, f_a2, f_a3, f_a4;
1254
1255            /* zero for the background color, nothig gets outside anyway */
1256            pixel1 = getPixelOverflowPalette(im, n, m, 0);
1257            pixel2 = getPixelOverflowPalette(im, n + 1, m, 0);
1258            pixel3 = getPixelOverflowPalette(im, n, m + 1, 0);
1259            pixel4 = getPixelOverflowPalette(im, n + 1, m + 1, 0);
1260
1261            f_r1 = gd_itofx(gdTrueColorGetRed(pixel1));
1262            f_r2 = gd_itofx(gdTrueColorGetRed(pixel2));
1263            f_r3 = gd_itofx(gdTrueColorGetRed(pixel3));
1264            f_r4 = gd_itofx(gdTrueColorGetRed(pixel4));
1265            f_g1 = gd_itofx(gdTrueColorGetGreen(pixel1));
1266            f_g2 = gd_itofx(gdTrueColorGetGreen(pixel2));
1267            f_g3 = gd_itofx(gdTrueColorGetGreen(pixel3));
1268            f_g4 = gd_itofx(gdTrueColorGetGreen(pixel4));
1269            f_b1 = gd_itofx(gdTrueColorGetBlue(pixel1));
1270            f_b2 = gd_itofx(gdTrueColorGetBlue(pixel2));
1271            f_b3 = gd_itofx(gdTrueColorGetBlue(pixel3));
1272            f_b4 = gd_itofx(gdTrueColorGetBlue(pixel4));
1273            f_a1 = gd_itofx(gdTrueColorGetAlpha(pixel1));
1274            f_a2 = gd_itofx(gdTrueColorGetAlpha(pixel2));
1275            f_a3 = gd_itofx(gdTrueColorGetAlpha(pixel3));
1276            f_a4 = gd_itofx(gdTrueColorGetAlpha(pixel4));
1277
1278            {
1279                const char red = (char) gd_fxtoi(gd_mulfx(f_w1, f_r1) + gd_mulfx(f_w2, f_r2) + gd_mulfx(f_w3, f_r3) + gd_mulfx(f_w4, f_r4));
1280                const char green = (char) gd_fxtoi(gd_mulfx(f_w1, f_g1) + gd_mulfx(f_w2, f_g2) + gd_mulfx(f_w3, f_g3) + gd_mulfx(f_w4, f_g4));
1281                const char blue = (char) gd_fxtoi(gd_mulfx(f_w1, f_b1) + gd_mulfx(f_w2, f_b2) + gd_mulfx(f_w3, f_b3) + gd_mulfx(f_w4, f_b4));
1282                const char alpha = (char) gd_fxtoi(gd_mulfx(f_w1, f_a1) + gd_mulfx(f_w2, f_a2) + gd_mulfx(f_w3, f_a3) + gd_mulfx(f_w4, f_a4));
1283
1284                new_img->tpixels[dst_offset_v][dst_offset_h] = gdTrueColorAlpha(red, green, blue, alpha);
1285            }
1286
1287            dst_offset_h++;
1288        }
1289
1290        dst_offset_v++;
1291    }
1292    return new_img;
1293}
1294
1295static gdImagePtr gdImageScaleBilinearTC(gdImagePtr im, const unsigned int new_width, const unsigned int new_height)
1296{
1297    long dst_w = MAX(1, new_width);
1298    long dst_h = MAX(1, new_height);
1299    float dx = (float)gdImageSX(im) / (float)dst_w;
1300    float dy = (float)gdImageSY(im) / (float)dst_h;
1301    gdFixed f_dx = gd_ftofx(dx);
1302    gdFixed f_dy = gd_ftofx(dy);
1303    gdFixed f_1 = gd_itofx(1);
1304
1305    int dst_offset_h;
1306    int dst_offset_v = 0;
1307    int dwSrcTotalOffset;
1308    long i;
1309    gdImagePtr new_img;
1310
1311    new_img = gdImageCreateTrueColor(new_width, new_height);
1312    if (!new_img){
1313        return NULL;
1314    }
1315
1316    for (i=0; i < dst_h; i++) {
1317        long j;
1318        dst_offset_h = 0;
1319        for (j=0; j < dst_w; j++) {
1320            /* Update bitmap */
1321            gdFixed f_i = gd_itofx(i);
1322            gdFixed f_j = gd_itofx(j);
1323            gdFixed f_a = gd_mulfx(f_i, f_dy);
1324            gdFixed f_b = gd_mulfx(f_j, f_dx);
1325            const gdFixed m = gd_fxtoi(f_a);
1326            const gdFixed n = gd_fxtoi(f_b);
1327            gdFixed f_f = f_a - gd_itofx(m);
1328            gdFixed f_g = f_b - gd_itofx(n);
1329
1330            const gdFixed f_w1 = gd_mulfx(f_1-f_f, f_1-f_g);
1331            const gdFixed f_w2 = gd_mulfx(f_1-f_f, f_g);
1332            const gdFixed f_w3 = gd_mulfx(f_f, f_1-f_g);
1333            const gdFixed f_w4 = gd_mulfx(f_f, f_g);
1334            unsigned int pixel1;
1335            unsigned int pixel2;
1336            unsigned int pixel3;
1337            unsigned int pixel4;
1338            register gdFixed f_r1, f_r2, f_r3, f_r4,
1339                    f_g1, f_g2, f_g3, f_g4,
1340                    f_b1, f_b2, f_b3, f_b4,
1341                    f_a1, f_a2, f_a3, f_a4;
1342            dwSrcTotalOffset = m + n;
1343            /* 0 for bgColor, nothing gets outside anyway */
1344            pixel1 = getPixelOverflowTC(im, n, m, 0);
1345            pixel2 = getPixelOverflowTC(im, n + 1, m, 0);
1346            pixel3 = getPixelOverflowTC(im, n, m + 1, 0);
1347            pixel4 = getPixelOverflowTC(im, n + 1, m + 1, 0);
1348
1349            f_r1 = gd_itofx(gdTrueColorGetRed(pixel1));
1350            f_r2 = gd_itofx(gdTrueColorGetRed(pixel2));
1351            f_r3 = gd_itofx(gdTrueColorGetRed(pixel3));
1352            f_r4 = gd_itofx(gdTrueColorGetRed(pixel4));
1353            f_g1 = gd_itofx(gdTrueColorGetGreen(pixel1));
1354            f_g2 = gd_itofx(gdTrueColorGetGreen(pixel2));
1355            f_g3 = gd_itofx(gdTrueColorGetGreen(pixel3));
1356            f_g4 = gd_itofx(gdTrueColorGetGreen(pixel4));
1357            f_b1 = gd_itofx(gdTrueColorGetBlue(pixel1));
1358            f_b2 = gd_itofx(gdTrueColorGetBlue(pixel2));
1359            f_b3 = gd_itofx(gdTrueColorGetBlue(pixel3));
1360            f_b4 = gd_itofx(gdTrueColorGetBlue(pixel4));
1361            f_a1 = gd_itofx(gdTrueColorGetAlpha(pixel1));
1362            f_a2 = gd_itofx(gdTrueColorGetAlpha(pixel2));
1363            f_a3 = gd_itofx(gdTrueColorGetAlpha(pixel3));
1364            f_a4 = gd_itofx(gdTrueColorGetAlpha(pixel4));
1365            {
1366                const unsigned char red   = (unsigned char) gd_fxtoi(gd_mulfx(f_w1, f_r1) + gd_mulfx(f_w2, f_r2) + gd_mulfx(f_w3, f_r3) + gd_mulfx(f_w4, f_r4));
1367                const unsigned char green = (unsigned char) gd_fxtoi(gd_mulfx(f_w1, f_g1) + gd_mulfx(f_w2, f_g2) + gd_mulfx(f_w3, f_g3) + gd_mulfx(f_w4, f_g4));
1368                const unsigned char blue  = (unsigned char) gd_fxtoi(gd_mulfx(f_w1, f_b1) + gd_mulfx(f_w2, f_b2) + gd_mulfx(f_w3, f_b3) + gd_mulfx(f_w4, f_b4));
1369                const unsigned char alpha = (unsigned char) gd_fxtoi(gd_mulfx(f_w1, f_a1) + gd_mulfx(f_w2, f_a2) + gd_mulfx(f_w3, f_a3) + gd_mulfx(f_w4, f_a4));
1370
1371                new_img->tpixels[dst_offset_v][dst_offset_h] = gdTrueColorAlpha(red, green, blue, alpha);
1372            }
1373
1374            dst_offset_h++;
1375        }
1376
1377        dst_offset_v++;
1378    }
1379    return new_img;
1380}
1381
1382gdImagePtr gdImageScaleBilinear(gdImagePtr im, const unsigned int new_width, const unsigned int new_height)
1383{
1384    if (im->trueColor) {
1385        return gdImageScaleBilinearTC(im, new_width, new_height);
1386    } else {
1387        return gdImageScaleBilinearPalette(im, new_width, new_height);
1388    }
1389}
1390
1391gdImagePtr gdImageScaleBicubicFixed(gdImagePtr src, const unsigned int width, const unsigned int height)
1392{
1393    const long new_width = MAX(1, width);
1394    const long new_height = MAX(1, height);
1395    const int src_w = gdImageSX(src);
1396    const int src_h = gdImageSY(src);
1397    const gdFixed f_dx = gd_ftofx((float)src_w / (float)new_width);
1398    const gdFixed f_dy = gd_ftofx((float)src_h / (float)new_height);
1399    const gdFixed f_1 = gd_itofx(1);
1400    const gdFixed f_2 = gd_itofx(2);
1401    const gdFixed f_4 = gd_itofx(4);
1402    const gdFixed f_6 = gd_itofx(6);
1403    const gdFixed f_gamma = gd_ftofx(1.04f);
1404    gdImagePtr dst;
1405
1406    unsigned int dst_offset_x;
1407    unsigned int dst_offset_y = 0;
1408    long i;
1409
1410    /* impact perf a bit, but not that much. Implementation for palette
1411       images can be done at a later point.
1412    */
1413    if (src->trueColor == 0) {
1414        gdImagePaletteToTrueColor(src);
1415    }
1416
1417    dst = gdImageCreateTrueColor(new_width, new_height);
1418    if (!dst) {
1419        return NULL;
1420    }
1421
1422    dst->saveAlphaFlag = 1;
1423
1424    for (i=0; i < new_height; i++) {
1425        long j;
1426        dst_offset_x = 0;
1427
1428        for (j=0; j < new_width; j++) {
1429            const gdFixed f_a = gd_mulfx(gd_itofx(i), f_dy);
1430            const gdFixed f_b = gd_mulfx(gd_itofx(j), f_dx);
1431            const long m = gd_fxtoi(f_a);
1432            const long n = gd_fxtoi(f_b);
1433            const gdFixed f_f = f_a - gd_itofx(m);
1434            const gdFixed f_g = f_b - gd_itofx(n);
1435            unsigned int src_offset_x[16], src_offset_y[16];
1436            long k;
1437            register gdFixed f_red = 0, f_green = 0, f_blue = 0, f_alpha = 0;
1438            unsigned char red, green, blue, alpha = 0;
1439            int *dst_row = dst->tpixels[dst_offset_y];
1440
1441            if ((m < 1) || (n < 1)) {
1442                src_offset_x[0] = n;
1443                src_offset_y[0] = m;
1444            } else {
1445                src_offset_x[0] = n - 1;
1446                src_offset_y[0] = m;
1447            }
1448
1449            if (m < 1) {
1450                src_offset_x[1] = n;
1451                src_offset_y[1] = m;
1452            } else {
1453                src_offset_x[1] = n;
1454                src_offset_y[1] = m;
1455            }
1456
1457            if ((m < 1) || (n >= src_w - 1)) {
1458                src_offset_x[2] = n;
1459                src_offset_y[2] = m;
1460            } else {
1461                src_offset_x[2] = n + 1;
1462                src_offset_y[2] = m;
1463            }
1464
1465            if ((m < 1) || (n >= src_w - 2)) {
1466                src_offset_x[3] = n;
1467                src_offset_y[3] = m;
1468            } else {
1469                src_offset_x[3] = n + 1 + 1;
1470                src_offset_y[3] = m;
1471            }
1472
1473            if (n < 1) {
1474                src_offset_x[4] = n;
1475                src_offset_y[4] = m;
1476            } else {
1477                src_offset_x[4] = n - 1;
1478                src_offset_y[4] = m;
1479            }
1480
1481            src_offset_x[5] = n;
1482            src_offset_y[5] = m;
1483            if (n >= src_w-1) {
1484                src_offset_x[6] = n;
1485                src_offset_y[6] = m;
1486            } else {
1487                src_offset_x[6] = n + 1;
1488                src_offset_y[6] = m;
1489            }
1490
1491            if (n >= src_w - 2) {
1492                src_offset_x[7] = n;
1493                src_offset_y[7] = m;
1494            } else {
1495                src_offset_x[7] = n + 1 + 1;
1496                src_offset_y[7] = m;
1497            }
1498
1499            if ((m >= src_h - 1) || (n < 1)) {
1500                src_offset_x[8] = n;
1501                src_offset_y[8] = m;
1502            } else {
1503                src_offset_x[8] = n - 1;
1504                src_offset_y[8] = m;
1505            }
1506
1507            if (m >= src_h - 1) {
1508                src_offset_x[8] = n;
1509                src_offset_y[8] = m;
1510            } else {
1511                src_offset_x[9] = n;
1512                src_offset_y[9] = m;
1513            }
1514
1515            if ((m >= src_h-1) || (n >= src_w-1)) {
1516                src_offset_x[10] = n;
1517                src_offset_y[10] = m;
1518            } else {
1519                src_offset_x[10] = n + 1;
1520                src_offset_y[10] = m;
1521            }
1522
1523            if ((m >= src_h - 1) || (n >= src_w - 2)) {
1524                src_offset_x[11] = n;
1525                src_offset_y[11] = m;
1526            } else {
1527                src_offset_x[11] = n + 1 + 1;
1528                src_offset_y[11] = m;
1529            }
1530
1531            if ((m >= src_h - 2) || (n < 1)) {
1532                src_offset_x[12] = n;
1533                src_offset_y[12] = m;
1534            } else {
1535                src_offset_x[12] = n - 1;
1536                src_offset_y[12] = m;
1537            }
1538
1539            if (m >= src_h - 2) {
1540                src_offset_x[13] = n;
1541                src_offset_y[13] = m;
1542            } else {
1543                src_offset_x[13] = n;
1544                src_offset_y[13] = m;
1545            }
1546
1547            if ((m >= src_h - 2) || (n >= src_w - 1)) {
1548                src_offset_x[14] = n;
1549                src_offset_y[14] = m;
1550            } else {
1551                src_offset_x[14] = n + 1;
1552                src_offset_y[14] = m;
1553            }
1554
1555            if ((m >= src_h - 2) || (n >= src_w - 2)) {
1556                src_offset_x[15] = n;
1557                src_offset_y[15] = m;
1558            } else {
1559                src_offset_x[15] = n  + 1 + 1;
1560                src_offset_y[15] = m;
1561            }
1562
1563            for (k = -1; k < 3; k++) {
1564                const gdFixed f = gd_itofx(k)-f_f;
1565                const gdFixed f_fm1 = f - f_1;
1566                const gdFixed f_fp1 = f + f_1;
1567                const gdFixed f_fp2 = f + f_2;
1568                register gdFixed f_a = 0, f_b = 0, f_d = 0, f_c = 0;
1569                register gdFixed f_RY;
1570                int l;
1571
1572                if (f_fp2 > 0) f_a = gd_mulfx(f_fp2, gd_mulfx(f_fp2,f_fp2));
1573                if (f_fp1 > 0) f_b = gd_mulfx(f_fp1, gd_mulfx(f_fp1,f_fp1));
1574                if (f > 0)     f_c = gd_mulfx(f, gd_mulfx(f,f));
1575                if (f_fm1 > 0) f_d = gd_mulfx(f_fm1, gd_mulfx(f_fm1,f_fm1));
1576
1577                f_RY = gd_divfx((f_a - gd_mulfx(f_4,f_b) + gd_mulfx(f_6,f_c) - gd_mulfx(f_4,f_d)),f_6);
1578
1579                for (l = -1; l < 3; l++) {
1580                    const gdFixed f = gd_itofx(l) - f_g;
1581                    const gdFixed f_fm1 = f - f_1;
1582                    const gdFixed f_fp1 = f + f_1;
1583                    const gdFixed f_fp2 = f + f_2;
1584                    register gdFixed f_a = 0, f_b = 0, f_c = 0, f_d = 0;
1585                    register gdFixed f_RX, f_R, f_rs, f_gs, f_bs, f_ba;
1586                    register int c;
1587                    const int _k = ((k+1)*4) + (l+1);
1588
1589                    if (f_fp2 > 0) f_a = gd_mulfx(f_fp2,gd_mulfx(f_fp2,f_fp2));
1590
1591                    if (f_fp1 > 0) f_b = gd_mulfx(f_fp1,gd_mulfx(f_fp1,f_fp1));
1592
1593                    if (f > 0) f_c = gd_mulfx(f,gd_mulfx(f,f));
1594
1595                    if (f_fm1 > 0) f_d = gd_mulfx(f_fm1,gd_mulfx(f_fm1,f_fm1));
1596
1597                    f_RX = gd_divfx((f_a-gd_mulfx(f_4,f_b)+gd_mulfx(f_6,f_c)-gd_mulfx(f_4,f_d)),f_6);
1598                    f_R = gd_mulfx(f_RY,f_RX);
1599
1600                    c = src->tpixels[*(src_offset_y + _k)][*(src_offset_x + _k)];
1601                    f_rs = gd_itofx(gdTrueColorGetRed(c));
1602                    f_gs = gd_itofx(gdTrueColorGetGreen(c));
1603                    f_bs = gd_itofx(gdTrueColorGetBlue(c));
1604                    f_ba = gd_itofx(gdTrueColorGetAlpha(c));
1605
1606                    f_red += gd_mulfx(f_rs,f_R);
1607                    f_green += gd_mulfx(f_gs,f_R);
1608                    f_blue += gd_mulfx(f_bs,f_R);
1609                    f_alpha += gd_mulfx(f_ba,f_R);
1610                }
1611            }
1612
1613            red    = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_red,   f_gamma)),  0, 255);
1614            green  = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_green, f_gamma)),  0, 255);
1615            blue   = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_blue,  f_gamma)),  0, 255);
1616            alpha  = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_alpha,  f_gamma)), 0, 127);
1617
1618            *(dst_row + dst_offset_x) = gdTrueColorAlpha(red, green, blue, alpha);
1619
1620            dst_offset_x++;
1621        }
1622        dst_offset_y++;
1623    }
1624    return dst;
1625}
1626
1627gdImagePtr gdImageScale(const gdImagePtr src, const unsigned int new_width, const unsigned int new_height)
1628{
1629    gdImagePtr im_scaled = NULL;
1630
1631    if (src == NULL || src->interpolation_id < 0 || src->interpolation_id > GD_METHOD_COUNT) {
1632        return 0;
1633    }
1634
1635    switch (src->interpolation_id) {
1636        /*Special cases, optimized implementations */
1637        case GD_NEAREST_NEIGHBOUR:
1638            im_scaled = gdImageScaleNearestNeighbour(src, new_width, new_height);
1639            break;
1640
1641        case GD_BILINEAR_FIXED:
1642            im_scaled = gdImageScaleBilinear(src, new_width, new_height);
1643            break;
1644
1645        case GD_BICUBIC_FIXED:
1646            im_scaled = gdImageScaleBicubicFixed(src, new_width, new_height);
1647            break;
1648
1649        /* generic */
1650        default:
1651            if (src->interpolation == NULL) {
1652                return NULL;
1653            }
1654            im_scaled = gdImageScaleTwoPass(src, src->sx, src->sy, new_width, new_height);
1655            break;
1656    }
1657    return im_scaled;
1658}
1659
1660gdImagePtr gdImageRotateNearestNeighbour(gdImagePtr src, const float degrees, const int bgColor)
1661{
1662    float _angle = ((float) (-degrees / 180.0f) * (float)M_PI);
1663    const int src_w  = gdImageSX(src);
1664    const int src_h = gdImageSY(src);
1665    const unsigned int new_width = (unsigned int)(abs((int)(src_w * cos(_angle))) + abs((int)(src_h * sin(_angle))) + 0.5f);
1666    const unsigned int new_height = (unsigned int)(abs((int)(src_w * sin(_angle))) + abs((int)(src_h * cos(_angle))) + 0.5f);
1667    const gdFixed f_0_5 = gd_ftofx(0.5f);
1668    const gdFixed f_H = gd_itofx(src_h/2);
1669    const gdFixed f_W = gd_itofx(src_w/2);
1670    const gdFixed f_cos = gd_ftofx(cos(-_angle));
1671    const gdFixed f_sin = gd_ftofx(sin(-_angle));
1672
1673    unsigned int dst_offset_x;
1674    unsigned int dst_offset_y = 0;
1675    unsigned int i;
1676    gdImagePtr dst;
1677
1678    dst = gdImageCreateTrueColor(new_width, new_height);
1679    if (!dst) {
1680        return NULL;
1681    }
1682    dst->saveAlphaFlag = 1;
1683    for (i = 0; i < new_height; i++) {
1684        unsigned int j;
1685        dst_offset_x = 0;
1686        for (j = 0; j < new_width; j++) {
1687            gdFixed f_i = gd_itofx((int)i - (int)new_height/2);
1688            gdFixed f_j = gd_itofx((int)j - (int)new_width/2);
1689            gdFixed f_m = gd_mulfx(f_j,f_sin) + gd_mulfx(f_i,f_cos) + f_0_5 + f_H;
1690            gdFixed f_n = gd_mulfx(f_j,f_cos) - gd_mulfx(f_i,f_sin) + f_0_5 + f_W;
1691            long m = gd_fxtoi(f_m);
1692            long n = gd_fxtoi(f_n);
1693
1694            if ((m > 0) && (m < src_h-1) && (n > 0) && (n < src_w-1)) {
1695                if (dst_offset_y < new_height) {
1696                    dst->tpixels[dst_offset_y][dst_offset_x++] = src->tpixels[m][n];
1697                }
1698            } else {
1699                if (dst_offset_y < new_height) {
1700                    dst->tpixels[dst_offset_y][dst_offset_x++] = bgColor;
1701                }
1702            }
1703        }
1704        dst_offset_y++;
1705    }
1706    return dst;
1707}
1708
1709gdImagePtr gdImageRotateGeneric(gdImagePtr src, const float degrees, const int bgColor)
1710{
1711    float _angle = ((float) (-degrees / 180.0f) * (float)M_PI);
1712    const int angle_rounded = (int)floor(degrees * 100);
1713    const int src_w  = gdImageSX(src);
1714    const int src_h = gdImageSY(src);
1715    const unsigned int new_width = (unsigned int)(abs((int)(src_w * cos(_angle))) + abs((int)(src_h * sin(_angle))) + 0.5f);
1716    const unsigned int new_height = (unsigned int)(abs((int)(src_w * sin(_angle))) + abs((int)(src_h * cos(_angle))) + 0.5f);
1717    const gdFixed f_0_5 = gd_ftofx(0.5f);
1718    const gdFixed f_H = gd_itofx(src_h/2);
1719    const gdFixed f_W = gd_itofx(src_w/2);
1720    const gdFixed f_cos = gd_ftofx(cos(-_angle));
1721    const gdFixed f_sin = gd_ftofx(sin(-_angle));
1722
1723    unsigned int dst_offset_x;
1724    unsigned int dst_offset_y = 0;
1725    unsigned int i;
1726    gdImagePtr dst;
1727
1728    const gdFixed f_slop_y = f_sin;
1729    const gdFixed f_slop_x = f_cos;
1730    const gdFixed f_slop = f_slop_x > 0 && f_slop_x > 0 ?
1731                            f_slop_x > f_slop_y ? gd_divfx(f_slop_y, f_slop_x) : gd_divfx(f_slop_x, f_slop_y)
1732                        : 0;
1733
1734
1735    if (bgColor < 0) {
1736        return NULL;
1737    }
1738
1739    dst = gdImageCreateTrueColor(new_width, new_height);
1740    if (!dst) {
1741        return NULL;
1742    }
1743    dst->saveAlphaFlag = 1;
1744
1745    for (i = 0; i < new_height; i++) {
1746        unsigned int j;
1747        dst_offset_x = 0;
1748        for (j = 0; j < new_width; j++) {
1749            gdFixed f_i = gd_itofx((int)i - (int)new_height/ 2);
1750            gdFixed f_j = gd_itofx((int)j - (int)new_width / 2);
1751            gdFixed f_m = gd_mulfx(f_j,f_sin) + gd_mulfx(f_i,f_cos) + f_0_5 + f_H;
1752            gdFixed f_n = gd_mulfx(f_j,f_cos) - gd_mulfx(f_i,f_sin) + f_0_5 + f_W;
1753            long m = gd_fxtoi(f_m);
1754            long n = gd_fxtoi(f_n);
1755
1756            if ((n <= 0) || (m <= 0) || (m >= src_h) || (n >= src_w)) {
1757                dst->tpixels[dst_offset_y][dst_offset_x++] = bgColor;
1758            } else if ((n <= 1) || (m <= 1) || (m >= src_h - 1) || (n >= src_w - 1)) {
1759                gdFixed f_127 = gd_itofx(127);
1760                register int c = getPixelInterpolated(src, n, m, bgColor);
1761                c = c | (( gdTrueColorGetAlpha(c) + ((int)(127* gd_fxtof(f_slop)))) << 24);
1762
1763                dst->tpixels[dst_offset_y][dst_offset_x++] = _color_blend(bgColor, c);
1764            } else {
1765                dst->tpixels[dst_offset_y][dst_offset_x++] = getPixelInterpolated(src, n, m, bgColor);
1766            }
1767        }
1768        dst_offset_y++;
1769    }
1770    return dst;
1771}
1772
1773gdImagePtr gdImageRotateBilinear(gdImagePtr src, const float degrees, const int bgColor)
1774{
1775    float _angle = (float)((- degrees / 180.0f) * M_PI);
1776    const unsigned int src_w = gdImageSX(src);
1777    const unsigned int src_h = gdImageSY(src);
1778    unsigned int new_width = abs((int)(src_w*cos(_angle))) + abs((int)(src_h*sin(_angle) + 0.5f));
1779    unsigned int new_height = abs((int)(src_w*sin(_angle))) + abs((int)(src_h*cos(_angle) + 0.5f));
1780    const gdFixed f_0_5 = gd_ftofx(0.5f);
1781    const gdFixed f_H = gd_itofx(src_h/2);
1782    const gdFixed f_W = gd_itofx(src_w/2);
1783    const gdFixed f_cos = gd_ftofx(cos(-_angle));
1784    const gdFixed f_sin = gd_ftofx(sin(-_angle));
1785    const gdFixed f_1 = gd_itofx(1);
1786    unsigned int i;
1787    unsigned int dst_offset_x;
1788    unsigned int dst_offset_y = 0;
1789    unsigned int src_offset_x, src_offset_y;
1790    gdImagePtr dst;
1791
1792    dst = gdImageCreateTrueColor(new_width, new_height);
1793    if (dst == NULL) {
1794        return NULL;
1795    }
1796    dst->saveAlphaFlag = 1;
1797
1798    for (i = 0; i < new_height; i++) {
1799        unsigned int j;
1800        dst_offset_x = 0;
1801
1802        for (j=0; j < new_width; j++) {
1803            const gdFixed f_i = gd_itofx((int)i - (int)new_height/2);
1804            const gdFixed f_j = gd_itofx((int)j - (int)new_width/2);
1805            const gdFixed f_m = gd_mulfx(f_j,f_sin) + gd_mulfx(f_i,f_cos) + f_0_5 + f_H;
1806            const gdFixed f_n = gd_mulfx(f_j,f_cos) - gd_mulfx(f_i,f_sin) + f_0_5 + f_W;
1807            const unsigned int m = gd_fxtoi(f_m);
1808            const unsigned int n = gd_fxtoi(f_n);
1809
1810            if ((m > 0) && (m < src_h - 1) && (n > 0) && (n < src_w - 1)) {
1811                const gdFixed f_f = f_m - gd_itofx(m);
1812                const gdFixed f_g = f_n - gd_itofx(n);
1813                const gdFixed f_w1 = gd_mulfx(f_1-f_f, f_1-f_g);
1814                const gdFixed f_w2 = gd_mulfx(f_1-f_f, f_g);
1815                const gdFixed f_w3 = gd_mulfx(f_f, f_1-f_g);
1816                const gdFixed f_w4 = gd_mulfx(f_f, f_g);
1817
1818                if (n < src_w - 1) {
1819                    src_offset_x = n + 1;
1820                    src_offset_y = m;
1821                }
1822
1823                if (m < src_h-1) {
1824                    src_offset_x = n;
1825                    src_offset_y = m + 1;
1826                }
1827
1828                if (!((n >= src_w-1) || (m >= src_h-1))) {
1829                    src_offset_x = n + 1;
1830                    src_offset_y = m + 1;
1831                }
1832                {
1833                    const int pixel1 = src->tpixels[src_offset_y][src_offset_x];
1834                    register int pixel2, pixel3, pixel4;
1835
1836                    if (src_offset_y + 1 >= src_h) {
1837                        pixel2 = bgColor;
1838                        pixel3 = bgColor;
1839                        pixel4 = bgColor;
1840                    } else if (src_offset_x + 1 >= src_w) {
1841                        pixel2 = bgColor;
1842                        pixel3 = bgColor;
1843                        pixel4 = bgColor;
1844                    } else {
1845                        pixel2 = src->tpixels[src_offset_y][src_offset_x + 1];
1846                        pixel3 = src->tpixels[src_offset_y + 1][src_offset_x];
1847                        pixel4 = src->tpixels[src_offset_y + 1][src_offset_x + 1];
1848                    }
1849                    {
1850                        const gdFixed f_r1 = gd_itofx(gdTrueColorGetRed(pixel1));
1851                        const gdFixed f_r2 = gd_itofx(gdTrueColorGetRed(pixel2));
1852                        const gdFixed f_r3 = gd_itofx(gdTrueColorGetRed(pixel3));
1853                        const gdFixed f_r4 = gd_itofx(gdTrueColorGetRed(pixel4));
1854                        const gdFixed f_g1 = gd_itofx(gdTrueColorGetGreen(pixel1));
1855                        const gdFixed f_g2 = gd_itofx(gdTrueColorGetGreen(pixel2));
1856                        const gdFixed f_g3 = gd_itofx(gdTrueColorGetGreen(pixel3));
1857                        const gdFixed f_g4 = gd_itofx(gdTrueColorGetGreen(pixel4));
1858                        const gdFixed f_b1 = gd_itofx(gdTrueColorGetBlue(pixel1));
1859                        const gdFixed f_b2 = gd_itofx(gdTrueColorGetBlue(pixel2));
1860                        const gdFixed f_b3 = gd_itofx(gdTrueColorGetBlue(pixel3));
1861                        const gdFixed f_b4 = gd_itofx(gdTrueColorGetBlue(pixel4));
1862                        const gdFixed f_a1 = gd_itofx(gdTrueColorGetAlpha(pixel1));
1863                        const gdFixed f_a2 = gd_itofx(gdTrueColorGetAlpha(pixel2));
1864                        const gdFixed f_a3 = gd_itofx(gdTrueColorGetAlpha(pixel3));
1865                        const gdFixed f_a4 = gd_itofx(gdTrueColorGetAlpha(pixel4));
1866                        const gdFixed f_red = gd_mulfx(f_w1, f_r1) + gd_mulfx(f_w2, f_r2) + gd_mulfx(f_w3, f_r3) + gd_mulfx(f_w4, f_r4);
1867                        const gdFixed f_green = gd_mulfx(f_w1, f_g1) + gd_mulfx(f_w2, f_g2) + gd_mulfx(f_w3, f_g3) + gd_mulfx(f_w4, f_g4);
1868                        const gdFixed f_blue = gd_mulfx(f_w1, f_b1) + gd_mulfx(f_w2, f_b2) + gd_mulfx(f_w3, f_b3) + gd_mulfx(f_w4, f_b4);
1869                        const gdFixed f_alpha = gd_mulfx(f_w1, f_a1) + gd_mulfx(f_w2, f_a2) + gd_mulfx(f_w3, f_a3) + gd_mulfx(f_w4, f_a4);
1870
1871                        const unsigned char red   = (unsigned char) CLAMP(gd_fxtoi(f_red),   0, 255);
1872                        const unsigned char green = (unsigned char) CLAMP(gd_fxtoi(f_green), 0, 255);
1873                        const unsigned char blue  = (unsigned char) CLAMP(gd_fxtoi(f_blue),  0, 255);
1874                        const unsigned char alpha = (unsigned char) CLAMP(gd_fxtoi(f_alpha), 0, 127);
1875
1876                        dst->tpixels[dst_offset_y][dst_offset_x++] = gdTrueColorAlpha(red, green, blue, alpha);
1877                    }
1878                }
1879            } else {
1880                dst->tpixels[dst_offset_y][dst_offset_x++] = bgColor;
1881            }
1882        }
1883        dst_offset_y++;
1884    }
1885    return dst;
1886}
1887
1888gdImagePtr gdImageRotateBicubicFixed(gdImagePtr src, const float degrees, const int bgColor)
1889{
1890    const float _angle = (float)((- degrees / 180.0f) * M_PI);
1891    const int src_w = gdImageSX(src);
1892    const int src_h = gdImageSY(src);
1893    const unsigned int new_width = abs((int)(src_w*cos(_angle))) + abs((int)(src_h*sin(_angle) + 0.5f));
1894    const unsigned int new_height = abs((int)(src_w*sin(_angle))) + abs((int)(src_h*cos(_angle) + 0.5f));
1895    const gdFixed f_0_5 = gd_ftofx(0.5f);
1896    const gdFixed f_H = gd_itofx(src_h/2);
1897    const gdFixed f_W = gd_itofx(src_w/2);
1898    const gdFixed f_cos = gd_ftofx(cos(-_angle));
1899    const gdFixed f_sin = gd_ftofx(sin(-_angle));
1900    const gdFixed f_1 = gd_itofx(1);
1901    const gdFixed f_2 = gd_itofx(2);
1902    const gdFixed f_4 = gd_itofx(4);
1903    const gdFixed f_6 = gd_itofx(6);
1904    const gdFixed f_gama = gd_ftofx(1.04f);
1905
1906    unsigned int dst_offset_x;
1907    unsigned int dst_offset_y = 0;
1908    unsigned int i;
1909    gdImagePtr dst;
1910
1911    dst = gdImageCreateTrueColor(new_width, new_height);
1912
1913    if (dst == NULL) {
1914        return NULL;
1915    }
1916    dst->saveAlphaFlag = 1;
1917
1918    for (i=0; i < new_height; i++) {
1919        unsigned int j;
1920        dst_offset_x = 0;
1921
1922        for (j=0; j < new_width; j++) {
1923            const gdFixed f_i = gd_itofx((int)i - (int)new_height/2);
1924            const gdFixed f_j = gd_itofx((int)j - (int)new_width/2);
1925            const gdFixed f_m = gd_mulfx(f_j,f_sin) + gd_mulfx(f_i,f_cos) + f_0_5 + f_H;
1926            const gdFixed f_n = gd_mulfx(f_j,f_cos) - gd_mulfx(f_i,f_sin) + f_0_5 + f_W;
1927            const int m = gd_fxtoi(f_m);
1928            const int n = gd_fxtoi(f_n);
1929
1930            if ((m > 0) && (m < src_h - 1) && (n > 0) && (n < src_w-1)) {
1931                const gdFixed f_f = f_m - gd_itofx(m);
1932                const gdFixed f_g = f_n - gd_itofx(n);
1933                unsigned int src_offset_x[16], src_offset_y[16];
1934                unsigned char red, green, blue, alpha;
1935                gdFixed f_red=0, f_green=0, f_blue=0, f_alpha=0;
1936                int k;
1937
1938                if ((m < 1) || (n < 1)) {
1939                    src_offset_x[0] = n;
1940                    src_offset_y[0] = m;
1941                } else {
1942                    src_offset_x[0] = n - 1;
1943                    src_offset_y[0] = m;
1944                }
1945
1946                if (m < 1) {
1947                    src_offset_x[1] = n;
1948                    src_offset_y[1] = m;
1949                } else {
1950                    src_offset_x[1] = n;
1951                    src_offset_y[1] = m ;
1952                }
1953
1954                if ((m < 1) || (n >= src_w-1)) {
1955                    src_offset_x[2] = - 1;
1956                    src_offset_y[2] = - 1;
1957                } else {
1958                    src_offset_x[2] = n + 1;
1959                    src_offset_y[2] = m ;
1960                }
1961
1962                if ((m < 1) || (n >= src_w-2)) {
1963                    src_offset_x[3] = - 1;
1964                    src_offset_y[3] = - 1;
1965                } else {
1966                    src_offset_x[3] = n + 1 + 1;
1967                    src_offset_y[3] = m ;
1968                }
1969
1970                if (n < 1) {
1971                    src_offset_x[4] = - 1;
1972                    src_offset_y[4] = - 1;
1973                } else {
1974                    src_offset_x[4] = n - 1;
1975                    src_offset_y[4] = m;
1976                }
1977
1978                src_offset_x[5] = n;
1979                src_offset_y[5] = m;
1980                if (n >= src_w-1) {
1981                    src_offset_x[6] = - 1;
1982                    src_offset_y[6] = - 1;
1983                } else {
1984                    src_offset_x[6] = n + 1;
1985                    src_offset_y[6] = m;
1986                }
1987
1988                if (n >= src_w-2) {
1989                    src_offset_x[7] = - 1;
1990                    src_offset_y[7] = - 1;
1991                } else {
1992                    src_offset_x[7] = n + 1 + 1;
1993                    src_offset_y[7] = m;
1994                }
1995
1996                if ((m >= src_h-1) || (n < 1)) {
1997                    src_offset_x[8] = - 1;
1998                    src_offset_y[8] = - 1;
1999                } else {
2000                    src_offset_x[8] = n - 1;
2001                    src_offset_y[8] = m;
2002                }
2003
2004                if (m >= src_h-1) {
2005                    src_offset_x[8] = - 1;
2006                    src_offset_y[8] = - 1;
2007                } else {
2008                    src_offset_x[9] = n;
2009                    src_offset_y[9] = m;
2010                }
2011
2012                if ((m >= src_h-1) || (n >= src_w-1)) {
2013                    src_offset_x[10] = - 1;
2014                    src_offset_y[10] = - 1;
2015                } else {
2016                    src_offset_x[10] = n + 1;
2017                    src_offset_y[10] = m;
2018                }
2019
2020                if ((m >= src_h-1) || (n >= src_w-2)) {
2021                    src_offset_x[11] = - 1;
2022                    src_offset_y[11] = - 1;
2023                } else {
2024                    src_offset_x[11] = n + 1 + 1;
2025                    src_offset_y[11] = m;
2026                }
2027
2028                if ((m >= src_h-2) || (n < 1)) {
2029                    src_offset_x[12] = - 1;
2030                    src_offset_y[12] = - 1;
2031                } else {
2032                    src_offset_x[12] = n - 1;
2033                    src_offset_y[12] = m;
2034                }
2035
2036                if (m >= src_h-2) {
2037                    src_offset_x[13] = - 1;
2038                    src_offset_y[13] = - 1;
2039                } else {
2040                    src_offset_x[13] = n;
2041                    src_offset_y[13] = m;
2042                }
2043
2044                if ((m >= src_h-2) || (n >= src_w - 1)) {
2045                    src_offset_x[14] = - 1;
2046                    src_offset_y[14] = - 1;
2047                } else {
2048                    src_offset_x[14] = n + 1;
2049                    src_offset_y[14] = m;
2050                }
2051
2052                if ((m >= src_h-2) || (n >= src_w-2)) {
2053                    src_offset_x[15] = - 1;
2054                    src_offset_y[15] = - 1;
2055                } else {
2056                    src_offset_x[15] = n  + 1 + 1;
2057                    src_offset_y[15] = m;
2058                }
2059
2060                for (k=-1; k<3; k++) {
2061                    const gdFixed f = gd_itofx(k)-f_f;
2062                    const gdFixed f_fm1 = f - f_1;
2063                    const gdFixed f_fp1 = f + f_1;
2064                    const gdFixed f_fp2 = f + f_2;
2065                    gdFixed f_a = 0, f_b = 0,f_c = 0, f_d = 0;
2066                    gdFixed f_RY;
2067                    int l;
2068
2069                    if (f_fp2 > 0) {
2070                        f_a = gd_mulfx(f_fp2,gd_mulfx(f_fp2,f_fp2));
2071                    }
2072
2073                    if (f_fp1 > 0) {
2074                        f_b = gd_mulfx(f_fp1,gd_mulfx(f_fp1,f_fp1));
2075                    }
2076
2077                    if (f > 0) {
2078                        f_c = gd_mulfx(f,gd_mulfx(f,f));
2079                    }
2080
2081                    if (f_fm1 > 0) {
2082                        f_d = gd_mulfx(f_fm1,gd_mulfx(f_fm1,f_fm1));
2083                    }
2084                    f_RY = gd_divfx((f_a-gd_mulfx(f_4,f_b)+gd_mulfx(f_6,f_c)-gd_mulfx(f_4,f_d)),f_6);
2085
2086                    for (l=-1;  l< 3; l++) {
2087                        const gdFixed f = gd_itofx(l) - f_g;
2088                        const gdFixed f_fm1 = f - f_1;
2089                        const gdFixed f_fp1 = f + f_1;
2090                        const gdFixed f_fp2 = f + f_2;
2091                        gdFixed f_a = 0, f_b = 0, f_c = 0, f_d = 0;
2092                        gdFixed f_RX, f_R;
2093                        const int _k = ((k + 1) * 4) + (l + 1);
2094                        register gdFixed f_rs, f_gs, f_bs, f_as;
2095                        register int c;
2096
2097                        if (f_fp2 > 0) {
2098                            f_a = gd_mulfx(f_fp2,gd_mulfx(f_fp2,f_fp2));
2099                        }
2100
2101                        if (f_fp1 > 0) {
2102                            f_b = gd_mulfx(f_fp1,gd_mulfx(f_fp1,f_fp1));
2103                        }
2104
2105                        if (f > 0) {
2106                            f_c = gd_mulfx(f,gd_mulfx(f,f));
2107                        }
2108
2109                        if (f_fm1 > 0) {
2110                            f_d = gd_mulfx(f_fm1,gd_mulfx(f_fm1,f_fm1));
2111                        }
2112
2113                        f_RX = gd_divfx((f_a - gd_mulfx(f_4, f_b) + gd_mulfx(f_6, f_c) - gd_mulfx(f_4, f_d)), f_6);
2114                        f_R = gd_mulfx(f_RY, f_RX);
2115
2116                        if ((src_offset_x[_k] <= 0) || (src_offset_y[_k] <= 0) || (src_offset_y[_k] >= src_h) || (src_offset_x[_k] >= src_w)) {
2117                            c = bgColor;
2118                        } else if ((src_offset_x[_k] <= 1) || (src_offset_y[_k] <= 1) || (src_offset_y[_k] >= (int)src_h - 1) || (src_offset_x[_k] >= (int)src_w - 1)) {
2119                            gdFixed f_127 = gd_itofx(127);
2120                            c = src->tpixels[src_offset_y[_k]][src_offset_x[_k]];
2121                            c = c | (( (int) (gd_fxtof(gd_mulfx(f_R, f_127)) + 50.5f)) << 24);
2122                            c = _color_blend(bgColor, c);
2123                        } else {
2124                            c = src->tpixels[src_offset_y[_k]][src_offset_x[_k]];
2125                        }
2126
2127                        f_rs = gd_itofx(gdTrueColorGetRed(c));
2128                        f_gs = gd_itofx(gdTrueColorGetGreen(c));
2129                        f_bs = gd_itofx(gdTrueColorGetBlue(c));
2130                        f_as = gd_itofx(gdTrueColorGetAlpha(c));
2131
2132                        f_red   += gd_mulfx(f_rs, f_R);
2133                        f_green += gd_mulfx(f_gs, f_R);
2134                        f_blue  += gd_mulfx(f_bs, f_R);
2135                        f_alpha += gd_mulfx(f_as, f_R);
2136                    }
2137                }
2138
2139                red   = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_red, f_gama)),   0, 255);
2140                green = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_green, f_gama)), 0, 255);
2141                blue  = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_blue, f_gama)),  0, 255);
2142                alpha = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_alpha, f_gama)), 0, 127);
2143
2144                dst->tpixels[dst_offset_y][dst_offset_x] =  gdTrueColorAlpha(red, green, blue, alpha);
2145            } else {
2146                dst->tpixels[dst_offset_y][dst_offset_x] =  bgColor;
2147            }
2148            dst_offset_x++;
2149        }
2150
2151        dst_offset_y++;
2152    }
2153    return dst;
2154}
2155
2156gdImagePtr gdImageRotateInterpolated(const gdImagePtr src, const float angle, int bgcolor)
2157{
2158    const int angle_rounded = (int)floor(angle * 100);
2159
2160    if (bgcolor < 0) {
2161        return NULL;
2162    }
2163
2164    /* impact perf a bit, but not that much. Implementation for palette
2165       images can be done at a later point.
2166    */
2167    if (src->trueColor == 0) {
2168        if (bgcolor >= 0) {
2169            bgcolor =  gdTrueColorAlpha(src->red[bgcolor], src->green[bgcolor], src->blue[bgcolor], src->alpha[bgcolor]);
2170        }
2171        gdImagePaletteToTrueColor(src);
2172    }
2173
2174    /* no interpolation needed here */
2175    switch (angle_rounded) {
2176        case 9000:
2177            return gdImageRotate90(src, 0);
2178        case 18000:
2179            return gdImageRotate180(src, 0);
2180        case 27000:
2181            return gdImageRotate270(src, 0);
2182    }
2183
2184    if (src == NULL || src->interpolation_id < 1 || src->interpolation_id > GD_METHOD_COUNT) {
2185        return NULL;
2186    }
2187
2188    switch (src->interpolation_id) {
2189        case GD_NEAREST_NEIGHBOUR:
2190            return gdImageRotateNearestNeighbour(src, angle, bgcolor);
2191            break;
2192
2193        case GD_BILINEAR_FIXED:
2194            return gdImageRotateBilinear(src, angle, bgcolor);
2195            break;
2196
2197        case GD_BICUBIC_FIXED:
2198            return gdImageRotateBicubicFixed(src, angle, bgcolor);
2199            break;
2200
2201        default:
2202            return gdImageRotateGeneric(src, angle, bgcolor);
2203    }
2204    return NULL;
2205}
2206
2207/**
2208 * Title: Affine transformation
2209 **/
2210
2211/**
2212 * Group: Transform
2213 **/
2214
2215 static void gdImageClipRectangle(gdImagePtr im, gdRectPtr r)
2216{
2217    int c1x, c1y, c2x, c2y;
2218    int x1,y1;
2219
2220    gdImageGetClip(im, &c1x, &c1y, &c2x, &c2y);
2221    x1 = r->x + r->width - 1;
2222    y1 = r->y + r->height - 1;
2223    r->x = CLAMP(r->x, c1x, c2x);
2224    r->y = CLAMP(r->y, c1y, c2y);
2225    r->width = CLAMP(x1, c1x, c2x) - r->x + 1;
2226    r->height = CLAMP(y1, c1y, c2y) - r->y + 1;
2227}
2228
2229void gdDumpRect(const char *msg, gdRectPtr r)
2230{
2231    printf("%s (%i, %i) (%i, %i)\n", msg, r->x, r->y, r->width, r->height);
2232}
2233
2234/**
2235 * Function: gdTransformAffineGetImage
2236 *  Applies an affine transformation to a region and return an image
2237 *  containing the complete transformation.
2238 *
2239 * Parameters:
2240 *  dst - Pointer to a gdImagePtr to store the created image, NULL when
2241 *        the creation or the transformation failed
2242 *  src - Source image
2243 *  src_area - rectangle defining the source region to transform
2244 *  dstY - Y position in the destination image
2245 *  affine - The desired affine transformation
2246 *
2247 * Returns:
2248 *  GD_TRUE if the affine is rectilinear or GD_FALSE
2249 */
2250int gdTransformAffineGetImage(gdImagePtr *dst,
2251          const gdImagePtr src,
2252          gdRectPtr src_area,
2253          const double affine[6])
2254{
2255    int res;
2256    double m[6];
2257    gdRect bbox;
2258    gdRect area_full;
2259
2260    if (src_area == NULL) {
2261        area_full.x = 0;
2262        area_full.y = 0;
2263        area_full.width  = gdImageSX(src);
2264        area_full.height = gdImageSY(src);
2265        src_area = &area_full;
2266    }
2267
2268    gdTransformAffineBoundingBox(src_area, affine, &bbox);
2269
2270    *dst = gdImageCreateTrueColor(bbox.width, bbox.height);
2271    if (*dst == NULL) {
2272        return GD_FALSE;
2273    }
2274    (*dst)->saveAlphaFlag = 1;
2275
2276    if (!src->trueColor) {
2277        gdImagePaletteToTrueColor(src);
2278    }
2279
2280    /* Translate to dst origin (0,0) */
2281    gdAffineTranslate(m, -bbox.x, -bbox.y);
2282    gdAffineConcat(m, affine, m);
2283
2284    gdImageAlphaBlending(*dst, 0);
2285
2286    res = gdTransformAffineCopy(*dst,
2287          0,0,
2288          src,
2289          src_area,
2290          m);
2291
2292    if (res != GD_TRUE) {
2293        gdImageDestroy(*dst);
2294        dst = NULL;
2295        return GD_FALSE;
2296    } else {
2297        return GD_TRUE;
2298    }
2299}
2300
2301/**
2302 * Function: gdTransformAffineCopy
2303 *  Applies an affine transformation to a region and copy the result
2304 *  in a destination to the given position.
2305 *
2306 * Parameters:
2307 *  dst - Image to draw the transformed image
2308 *  src - Source image
2309 *  dstX - X position in the destination image
2310 *  dstY - Y position in the destination image
2311 *  src_area - Rectangular region to rotate in the src image
2312 *
2313 * Returns:
2314 *  GD_TRUE if the affine is rectilinear or GD_FALSE
2315 */
2316int gdTransformAffineCopy(gdImagePtr dst,
2317          int dst_x, int dst_y,
2318          const gdImagePtr src,
2319          gdRectPtr src_region,
2320          const double affine[6])
2321{
2322    int c1x,c1y,c2x,c2y;
2323    int backclip = 0;
2324    int backup_clipx1, backup_clipy1, backup_clipx2, backup_clipy2;
2325    register int x, y, src_offset_x, src_offset_y;
2326    double inv[6];
2327    int *dst_p;
2328    gdPointF pt, src_pt;
2329    gdRect bbox;
2330    int end_x, end_y;
2331    gdInterpolationMethod interpolation_id_bak = GD_DEFAULT;
2332    interpolation_method interpolation_bak;
2333
2334    /* These methods use special implementations */
2335    if (src->interpolation_id == GD_BILINEAR_FIXED || src->interpolation_id == GD_BICUBIC_FIXED || src->interpolation_id == GD_NEAREST_NEIGHBOUR) {
2336        interpolation_id_bak = src->interpolation_id;
2337        interpolation_bak = src->interpolation;
2338
2339        gdImageSetInterpolationMethod(src, GD_BICUBIC);
2340    }
2341
2342
2343    gdImageClipRectangle(src, src_region);
2344
2345    if (src_region->x > 0 || src_region->y > 0
2346        || src_region->width < gdImageSX(src)
2347        || src_region->height < gdImageSY(src)) {
2348        backclip = 1;
2349
2350        gdImageGetClip(src, &backup_clipx1, &backup_clipy1,
2351        &backup_clipx2, &backup_clipy2);
2352
2353        gdImageSetClip(src, src_region->x, src_region->y,
2354            src_region->x + src_region->width - 1,
2355            src_region->y + src_region->height - 1);
2356    }
2357
2358    if (!gdTransformAffineBoundingBox(src_region, affine, &bbox)) {
2359        if (backclip) {
2360            gdImageSetClip(src, backup_clipx1, backup_clipy1,
2361                    backup_clipx2, backup_clipy2);
2362        }
2363        gdImageSetInterpolationMethod(src, interpolation_id_bak);
2364        return GD_FALSE;
2365    }
2366
2367    gdImageGetClip(dst, &c1x, &c1y, &c2x, &c2y);
2368
2369    end_x = bbox.width  + (int) fabs(bbox.x);
2370    end_y = bbox.height + (int) fabs(bbox.y);
2371
2372    /* Get inverse affine to let us work with destination -> source */
2373    gdAffineInvert(inv, affine);
2374
2375    src_offset_x =  src_region->x;
2376    src_offset_y =  src_region->y;
2377
2378    if (dst->alphaBlendingFlag) {
2379        for (y = bbox.y; y <= end_y; y++) {
2380            pt.y = y + 0.5;
2381            for (x = 0; x <= end_x; x++) {
2382                pt.x = x + 0.5;
2383                gdAffineApplyToPointF(&src_pt, &pt, inv);
2384                gdImageSetPixel(dst, dst_x + x, dst_y + y, getPixelInterpolated(src, src_offset_x + src_pt.x, src_offset_y + src_pt.y, 0));
2385            }
2386        }
2387    } else {
2388        for (y = 0; y <= end_y; y++) {
2389            pt.y = y + 0.5 + bbox.y;
2390            if ((dst_y + y) < 0 || ((dst_y + y) > gdImageSY(dst) -1)) {
2391                continue;
2392            }
2393            dst_p = dst->tpixels[dst_y + y] + dst_x;
2394
2395            for (x = 0; x <= end_x; x++) {
2396                pt.x = x + 0.5 + bbox.x;
2397                gdAffineApplyToPointF(&src_pt, &pt, inv);
2398
2399                if ((dst_x + x) < 0 || (dst_x + x) > (gdImageSX(dst) - 1)) {
2400                    break;
2401                }
2402                *(dst_p++) = getPixelInterpolated(src, src_offset_x + src_pt.x, src_offset_y + src_pt.y, -1);
2403            }
2404        }
2405    }
2406
2407    /* Restore clip if required */
2408    if (backclip) {
2409        gdImageSetClip(src, backup_clipx1, backup_clipy1,
2410                backup_clipx2, backup_clipy2);
2411    }
2412
2413    gdImageSetInterpolationMethod(src, interpolation_id_bak);
2414    return GD_TRUE;
2415}
2416
2417/**
2418 * Function: gdTransformAffineBoundingBox
2419 *  Returns the bounding box of an affine transformation applied to a
2420 *  rectangular area <gdRect>
2421 *
2422 * Parameters:
2423 *  src - Rectangular source area for the affine transformation
2424 *  affine - the affine transformation
2425 *  bbox - the resulting bounding box
2426 *
2427 * Returns:
2428 *  GD_TRUE if the affine is rectilinear or GD_FALSE
2429 */
2430int gdTransformAffineBoundingBox(gdRectPtr src, const double affine[6], gdRectPtr bbox)
2431{
2432    gdPointF extent[4], min, max, point;
2433    int i;
2434
2435    extent[0].x=0.0;
2436    extent[0].y=0.0;
2437    extent[1].x=(double) src->width;
2438    extent[1].y=0.0;
2439    extent[2].x=(double) src->width;
2440    extent[2].y=(double) src->height;
2441    extent[3].x=0.0;
2442    extent[3].y=(double) src->height;
2443
2444    for (i=0; i < 4; i++) {
2445        point=extent[i];
2446        if (gdAffineApplyToPointF(&extent[i], &point, affine) != GD_TRUE) {
2447            return GD_FALSE;
2448        }
2449    }
2450    min=extent[0];
2451    max=extent[0];
2452
2453    for (i=1; i < 4; i++) {
2454        if (min.x > extent[i].x)
2455            min.x=extent[i].x;
2456        if (min.y > extent[i].y)
2457            min.y=extent[i].y;
2458        if (max.x < extent[i].x)
2459            max.x=extent[i].x;
2460        if (max.y < extent[i].y)
2461            max.y=extent[i].y;
2462    }
2463    bbox->x = (int) min.x;
2464    bbox->y = (int) min.y;
2465    bbox->width  = (int) floor(max.x - min.x) - 1;
2466    bbox->height = (int) floor(max.y - min.y);
2467    return GD_TRUE;
2468}
2469
2470int gdImageSetInterpolationMethod(gdImagePtr im, gdInterpolationMethod id)
2471{
2472    if (im == NULL || id < 0 || id > GD_METHOD_COUNT) {
2473        return 0;
2474    }
2475
2476    switch (id) {
2477        case GD_DEFAULT:
2478            id = GD_BILINEAR_FIXED;
2479        /* Optimized versions */
2480        case GD_BILINEAR_FIXED:
2481        case GD_BICUBIC_FIXED:
2482        case GD_NEAREST_NEIGHBOUR:
2483        case GD_WEIGHTED4:
2484            im->interpolation = NULL;
2485            break;
2486
2487        /* generic versions*/
2488        case GD_BELL:
2489            im->interpolation = filter_bell;
2490            break;
2491        case GD_BESSEL:
2492            im->interpolation = filter_bessel;
2493            break;
2494        case GD_BICUBIC:
2495            im->interpolation = filter_bicubic;
2496            break;
2497        case GD_BLACKMAN:
2498            im->interpolation = filter_blackman;
2499            break;
2500        case GD_BOX:
2501            im->interpolation = filter_box;
2502            break;
2503        case GD_BSPLINE:
2504            im->interpolation = filter_bspline;
2505            break;
2506        case GD_CATMULLROM:
2507            im->interpolation = filter_catmullrom;
2508            break;
2509        case GD_GAUSSIAN:
2510            im->interpolation = filter_gaussian;
2511            break;
2512        case GD_GENERALIZED_CUBIC:
2513            im->interpolation = filter_generalized_cubic;
2514            break;
2515        case GD_HERMITE:
2516            im->interpolation = filter_hermite;
2517            break;
2518        case GD_HAMMING:
2519            im->interpolation = filter_hamming;
2520            break;
2521        case GD_HANNING:
2522            im->interpolation = filter_hanning;
2523            break;
2524        case GD_MITCHELL:
2525            im->interpolation = filter_mitchell;
2526            break;
2527        case GD_POWER:
2528            im->interpolation = filter_power;
2529            break;
2530        case GD_QUADRATIC:
2531            im->interpolation = filter_quadratic;
2532            break;
2533        case GD_SINC:
2534            im->interpolation = filter_sinc;
2535            break;
2536        case GD_TRIANGLE:
2537            im->interpolation = filter_triangle;
2538            break;
2539
2540        default:
2541            return 0;
2542            break;
2543    }
2544    im->interpolation_id = id;
2545    return 1;
2546}
2547
2548#ifdef _MSC_VER
2549# pragma optimize("", on)
2550#endif
2551