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    /* Convert to truecolor if it isn't; this code requires it. */
1063    if (!src->trueColor) {
1064        gdImagePaletteToTrueColor(src);
1065    }
1066
1067    tmp_im = gdImageCreateTrueColor(new_width, src_height);
1068    if (tmp_im == NULL) {
1069        return NULL;
1070    }
1071    gdImageSetInterpolationMethod(tmp_im, src->interpolation_id);
1072    _gdScaleHoriz(src, src_width, src_height, tmp_im, new_width, src_height);
1073
1074    dst = gdImageCreateTrueColor(new_width, new_height);
1075    if (dst == NULL) {
1076        gdImageDestroy(tmp_im);
1077        return NULL;
1078    }
1079    gdImageSetInterpolationMethod(dst, src->interpolation_id);
1080    _gdScaleVert(tmp_im, new_width, src_height, dst, new_width, new_height);
1081    gdImageDestroy(tmp_im);
1082
1083    return dst;
1084}
1085
1086gdImagePtr 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)
1087{
1088    gdImagePtr tmp_im;
1089
1090    tmp_im = gdImageCreateTrueColor(new_width, src_height);
1091    if (tmp_im == NULL) {
1092        return NULL;
1093    }
1094    gdImageSetInterpolationMethod(tmp_im, src->interpolation_id);
1095
1096    _gdScaleHoriz(src, src_width, src_height, tmp_im, new_width, src_height);
1097    _gdScaleVert(tmp_im, new_width, src_height, dst, new_width, new_height);
1098
1099    gdFree(tmp_im);
1100    return dst;
1101}
1102
1103/*
1104    BilinearFixed, BicubicFixed and nearest implementations are rewamped versions of the implementation in CBitmapEx
1105    http://www.codeproject.com/Articles/29121/CBitmapEx-Free-C-Bitmap-Manipulation-Class
1106    Integer only implementation, good to have for common usages like pre scale very large
1107    images before using another interpolation methods for the last step.
1108*/
1109gdImagePtr gdImageScaleNearestNeighbour(gdImagePtr im, const unsigned int width, const unsigned int height)
1110{
1111    const unsigned long new_width = MAX(1, width);
1112    const unsigned long new_height = MAX(1, height);
1113    const float dx = (float)im->sx / (float)new_width;
1114    const float dy = (float)im->sy / (float)new_height;
1115    const gdFixed f_dx = gd_ftofx(dx);
1116    const gdFixed f_dy = gd_ftofx(dy);
1117
1118    gdImagePtr dst_img;
1119    unsigned long  dst_offset_x;
1120    unsigned long  dst_offset_y = 0;
1121    unsigned int i;
1122
1123    dst_img = gdImageCreateTrueColor(new_width, new_height);
1124
1125    if (dst_img == NULL) {
1126        return NULL;
1127    }
1128
1129    for (i=0; i<new_height; i++) {
1130        unsigned int j;
1131        dst_offset_x = 0;
1132        if (im->trueColor) {
1133            for (j=0; j<new_width; j++) {
1134                const gdFixed f_i = gd_itofx(i);
1135                const gdFixed f_j = gd_itofx(j);
1136                const gdFixed f_a = gd_mulfx(f_i, f_dy);
1137                const gdFixed f_b = gd_mulfx(f_j, f_dx);
1138                const long m = gd_fxtoi(f_a);
1139                const long n = gd_fxtoi(f_b);
1140
1141                dst_img->tpixels[dst_offset_y][dst_offset_x++] = im->tpixels[m][n];
1142            }
1143        } else {
1144            for (j=0; j<new_width; j++) {
1145                const gdFixed f_i = gd_itofx(i);
1146                const gdFixed f_j = gd_itofx(j);
1147                const gdFixed f_a = gd_mulfx(f_i, f_dy);
1148                const gdFixed f_b = gd_mulfx(f_j, f_dx);
1149                const long m = gd_fxtoi(f_a);
1150                const long n = gd_fxtoi(f_b);
1151
1152                dst_img->tpixels[dst_offset_y][dst_offset_x++] = colorIndex2RGBA(im->pixels[m][n]);
1153            }
1154        }
1155        dst_offset_y++;
1156    }
1157    return dst_img;
1158}
1159
1160static inline int getPixelOverflowColorTC(gdImagePtr im, const int x, const int y, const int color)
1161{
1162    if (gdImageBoundsSafe(im, x, y)) {
1163        const int c = im->tpixels[y][x];
1164        if (c == im->transparent) {
1165            return gdTrueColorAlpha(0, 0, 0, 127);
1166        }
1167        return c;
1168    } else {
1169        register int border = 0;
1170        if (y < im->cy1) {
1171            border = im->tpixels[0][im->cx1];
1172            goto processborder;
1173        }
1174
1175        if (y < im->cy1) {
1176            border = im->tpixels[0][im->cx1];
1177            goto processborder;
1178        }
1179
1180        if (y > im->cy2) {
1181            if (x >= im->cx1 && x <= im->cx1) {
1182                border = im->tpixels[im->cy2][x];
1183                goto processborder;
1184            } else {
1185                return gdTrueColorAlpha(0, 0, 0, 127);
1186            }
1187        }
1188
1189        /* y is bound safe at this point */
1190        if (x < im->cx1) {
1191            border = im->tpixels[y][im->cx1];
1192            goto processborder;
1193        }
1194
1195        if (x > im->cx2) {
1196            border = im->tpixels[y][im->cx2];
1197        }
1198
1199processborder:
1200        if (border == im->transparent) {
1201            return gdTrueColorAlpha(0, 0, 0, 127);
1202        } else{
1203            return gdTrueColorAlpha(gdTrueColorGetRed(border), gdTrueColorGetGreen(border), gdTrueColorGetBlue(border), 127);
1204        }
1205    }
1206}
1207
1208static gdImagePtr gdImageScaleBilinearPalette(gdImagePtr im, const unsigned int new_width, const unsigned int new_height)
1209{
1210    long _width = MAX(1, new_width);
1211    long _height = MAX(1, new_height);
1212    float dx = (float)gdImageSX(im) / (float)_width;
1213    float dy = (float)gdImageSY(im) / (float)_height;
1214    gdFixed f_dx = gd_ftofx(dx);
1215    gdFixed f_dy = gd_ftofx(dy);
1216    gdFixed f_1 = gd_itofx(1);
1217
1218    int dst_offset_h;
1219    int dst_offset_v = 0;
1220    long i;
1221    gdImagePtr new_img;
1222    const int transparent = im->transparent;
1223
1224    new_img = gdImageCreateTrueColor(new_width, new_height);
1225    if (new_img == NULL) {
1226        return NULL;
1227    }
1228    new_img->transparent = gdTrueColorAlpha(im->red[transparent], im->green[transparent], im->blue[transparent], im->alpha[transparent]);
1229
1230    for (i=0; i < _height; i++) {
1231        long j;
1232        const gdFixed f_i = gd_itofx(i);
1233        const gdFixed f_a = gd_mulfx(f_i, f_dy);
1234        register long m = gd_fxtoi(f_a);
1235
1236        dst_offset_h = 0;
1237
1238        for (j=0; j < _width; j++) {
1239            /* Update bitmap */
1240            gdFixed f_j = gd_itofx(j);
1241            gdFixed f_b = gd_mulfx(f_j, f_dx);
1242
1243            const long n = gd_fxtoi(f_b);
1244            gdFixed f_f = f_a - gd_itofx(m);
1245            gdFixed f_g = f_b - gd_itofx(n);
1246
1247            const gdFixed f_w1 = gd_mulfx(f_1-f_f, f_1-f_g);
1248            const gdFixed f_w2 = gd_mulfx(f_1-f_f, f_g);
1249            const gdFixed f_w3 = gd_mulfx(f_f, f_1-f_g);
1250            const gdFixed f_w4 = gd_mulfx(f_f, f_g);
1251            unsigned int pixel1;
1252            unsigned int pixel2;
1253            unsigned int pixel3;
1254            unsigned int pixel4;
1255            register gdFixed f_r1, f_r2, f_r3, f_r4,
1256                    f_g1, f_g2, f_g3, f_g4,
1257                    f_b1, f_b2, f_b3, f_b4,
1258                    f_a1, f_a2, f_a3, f_a4;
1259
1260            /* zero for the background color, nothig gets outside anyway */
1261            pixel1 = getPixelOverflowPalette(im, n, m, 0);
1262            pixel2 = getPixelOverflowPalette(im, n + 1, m, 0);
1263            pixel3 = getPixelOverflowPalette(im, n, m + 1, 0);
1264            pixel4 = getPixelOverflowPalette(im, n + 1, m + 1, 0);
1265
1266            f_r1 = gd_itofx(gdTrueColorGetRed(pixel1));
1267            f_r2 = gd_itofx(gdTrueColorGetRed(pixel2));
1268            f_r3 = gd_itofx(gdTrueColorGetRed(pixel3));
1269            f_r4 = gd_itofx(gdTrueColorGetRed(pixel4));
1270            f_g1 = gd_itofx(gdTrueColorGetGreen(pixel1));
1271            f_g2 = gd_itofx(gdTrueColorGetGreen(pixel2));
1272            f_g3 = gd_itofx(gdTrueColorGetGreen(pixel3));
1273            f_g4 = gd_itofx(gdTrueColorGetGreen(pixel4));
1274            f_b1 = gd_itofx(gdTrueColorGetBlue(pixel1));
1275            f_b2 = gd_itofx(gdTrueColorGetBlue(pixel2));
1276            f_b3 = gd_itofx(gdTrueColorGetBlue(pixel3));
1277            f_b4 = gd_itofx(gdTrueColorGetBlue(pixel4));
1278            f_a1 = gd_itofx(gdTrueColorGetAlpha(pixel1));
1279            f_a2 = gd_itofx(gdTrueColorGetAlpha(pixel2));
1280            f_a3 = gd_itofx(gdTrueColorGetAlpha(pixel3));
1281            f_a4 = gd_itofx(gdTrueColorGetAlpha(pixel4));
1282
1283            {
1284                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));
1285                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));
1286                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));
1287                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));
1288
1289                new_img->tpixels[dst_offset_v][dst_offset_h] = gdTrueColorAlpha(red, green, blue, alpha);
1290            }
1291
1292            dst_offset_h++;
1293        }
1294
1295        dst_offset_v++;
1296    }
1297    return new_img;
1298}
1299
1300static gdImagePtr gdImageScaleBilinearTC(gdImagePtr im, const unsigned int new_width, const unsigned int new_height)
1301{
1302    long dst_w = MAX(1, new_width);
1303    long dst_h = MAX(1, new_height);
1304    float dx = (float)gdImageSX(im) / (float)dst_w;
1305    float dy = (float)gdImageSY(im) / (float)dst_h;
1306    gdFixed f_dx = gd_ftofx(dx);
1307    gdFixed f_dy = gd_ftofx(dy);
1308    gdFixed f_1 = gd_itofx(1);
1309
1310    int dst_offset_h;
1311    int dst_offset_v = 0;
1312    int dwSrcTotalOffset;
1313    long i;
1314    gdImagePtr new_img;
1315
1316    new_img = gdImageCreateTrueColor(new_width, new_height);
1317    if (!new_img){
1318        return NULL;
1319    }
1320
1321    for (i=0; i < dst_h; i++) {
1322        long j;
1323        dst_offset_h = 0;
1324        for (j=0; j < dst_w; j++) {
1325            /* Update bitmap */
1326            gdFixed f_i = gd_itofx(i);
1327            gdFixed f_j = gd_itofx(j);
1328            gdFixed f_a = gd_mulfx(f_i, f_dy);
1329            gdFixed f_b = gd_mulfx(f_j, f_dx);
1330            const gdFixed m = gd_fxtoi(f_a);
1331            const gdFixed n = gd_fxtoi(f_b);
1332            gdFixed f_f = f_a - gd_itofx(m);
1333            gdFixed f_g = f_b - gd_itofx(n);
1334
1335            const gdFixed f_w1 = gd_mulfx(f_1-f_f, f_1-f_g);
1336            const gdFixed f_w2 = gd_mulfx(f_1-f_f, f_g);
1337            const gdFixed f_w3 = gd_mulfx(f_f, f_1-f_g);
1338            const gdFixed f_w4 = gd_mulfx(f_f, f_g);
1339            unsigned int pixel1;
1340            unsigned int pixel2;
1341            unsigned int pixel3;
1342            unsigned int pixel4;
1343            register gdFixed f_r1, f_r2, f_r3, f_r4,
1344                    f_g1, f_g2, f_g3, f_g4,
1345                    f_b1, f_b2, f_b3, f_b4,
1346                    f_a1, f_a2, f_a3, f_a4;
1347            dwSrcTotalOffset = m + n;
1348            /* 0 for bgColor, nothing gets outside anyway */
1349            pixel1 = getPixelOverflowTC(im, n, m, 0);
1350            pixel2 = getPixelOverflowTC(im, n + 1, m, 0);
1351            pixel3 = getPixelOverflowTC(im, n, m + 1, 0);
1352            pixel4 = getPixelOverflowTC(im, n + 1, m + 1, 0);
1353
1354            f_r1 = gd_itofx(gdTrueColorGetRed(pixel1));
1355            f_r2 = gd_itofx(gdTrueColorGetRed(pixel2));
1356            f_r3 = gd_itofx(gdTrueColorGetRed(pixel3));
1357            f_r4 = gd_itofx(gdTrueColorGetRed(pixel4));
1358            f_g1 = gd_itofx(gdTrueColorGetGreen(pixel1));
1359            f_g2 = gd_itofx(gdTrueColorGetGreen(pixel2));
1360            f_g3 = gd_itofx(gdTrueColorGetGreen(pixel3));
1361            f_g4 = gd_itofx(gdTrueColorGetGreen(pixel4));
1362            f_b1 = gd_itofx(gdTrueColorGetBlue(pixel1));
1363            f_b2 = gd_itofx(gdTrueColorGetBlue(pixel2));
1364            f_b3 = gd_itofx(gdTrueColorGetBlue(pixel3));
1365            f_b4 = gd_itofx(gdTrueColorGetBlue(pixel4));
1366            f_a1 = gd_itofx(gdTrueColorGetAlpha(pixel1));
1367            f_a2 = gd_itofx(gdTrueColorGetAlpha(pixel2));
1368            f_a3 = gd_itofx(gdTrueColorGetAlpha(pixel3));
1369            f_a4 = gd_itofx(gdTrueColorGetAlpha(pixel4));
1370            {
1371                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));
1372                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));
1373                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));
1374                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));
1375
1376                new_img->tpixels[dst_offset_v][dst_offset_h] = gdTrueColorAlpha(red, green, blue, alpha);
1377            }
1378
1379            dst_offset_h++;
1380        }
1381
1382        dst_offset_v++;
1383    }
1384    return new_img;
1385}
1386
1387gdImagePtr gdImageScaleBilinear(gdImagePtr im, const unsigned int new_width, const unsigned int new_height)
1388{
1389    if (im->trueColor) {
1390        return gdImageScaleBilinearTC(im, new_width, new_height);
1391    } else {
1392        return gdImageScaleBilinearPalette(im, new_width, new_height);
1393    }
1394}
1395
1396gdImagePtr gdImageScaleBicubicFixed(gdImagePtr src, const unsigned int width, const unsigned int height)
1397{
1398    const long new_width = MAX(1, width);
1399    const long new_height = MAX(1, height);
1400    const int src_w = gdImageSX(src);
1401    const int src_h = gdImageSY(src);
1402    const gdFixed f_dx = gd_ftofx((float)src_w / (float)new_width);
1403    const gdFixed f_dy = gd_ftofx((float)src_h / (float)new_height);
1404    const gdFixed f_1 = gd_itofx(1);
1405    const gdFixed f_2 = gd_itofx(2);
1406    const gdFixed f_4 = gd_itofx(4);
1407    const gdFixed f_6 = gd_itofx(6);
1408    const gdFixed f_gamma = gd_ftofx(1.04f);
1409    gdImagePtr dst;
1410
1411    unsigned int dst_offset_x;
1412    unsigned int dst_offset_y = 0;
1413    long i;
1414
1415    /* impact perf a bit, but not that much. Implementation for palette
1416       images can be done at a later point.
1417    */
1418    if (src->trueColor == 0) {
1419        gdImagePaletteToTrueColor(src);
1420    }
1421
1422    dst = gdImageCreateTrueColor(new_width, new_height);
1423    if (!dst) {
1424        return NULL;
1425    }
1426
1427    dst->saveAlphaFlag = 1;
1428
1429    for (i=0; i < new_height; i++) {
1430        long j;
1431        dst_offset_x = 0;
1432
1433        for (j=0; j < new_width; j++) {
1434            const gdFixed f_a = gd_mulfx(gd_itofx(i), f_dy);
1435            const gdFixed f_b = gd_mulfx(gd_itofx(j), f_dx);
1436            const long m = gd_fxtoi(f_a);
1437            const long n = gd_fxtoi(f_b);
1438            const gdFixed f_f = f_a - gd_itofx(m);
1439            const gdFixed f_g = f_b - gd_itofx(n);
1440            unsigned int src_offset_x[16], src_offset_y[16];
1441            long k;
1442            register gdFixed f_red = 0, f_green = 0, f_blue = 0, f_alpha = 0;
1443            unsigned char red, green, blue, alpha = 0;
1444            int *dst_row = dst->tpixels[dst_offset_y];
1445
1446            if ((m < 1) || (n < 1)) {
1447                src_offset_x[0] = n;
1448                src_offset_y[0] = m;
1449            } else {
1450                src_offset_x[0] = n - 1;
1451                src_offset_y[0] = m;
1452            }
1453
1454            if (m < 1) {
1455                src_offset_x[1] = n;
1456                src_offset_y[1] = m;
1457            } else {
1458                src_offset_x[1] = n;
1459                src_offset_y[1] = m;
1460            }
1461
1462            if ((m < 1) || (n >= src_w - 1)) {
1463                src_offset_x[2] = n;
1464                src_offset_y[2] = m;
1465            } else {
1466                src_offset_x[2] = n + 1;
1467                src_offset_y[2] = m;
1468            }
1469
1470            if ((m < 1) || (n >= src_w - 2)) {
1471                src_offset_x[3] = n;
1472                src_offset_y[3] = m;
1473            } else {
1474                src_offset_x[3] = n + 1 + 1;
1475                src_offset_y[3] = m;
1476            }
1477
1478            if (n < 1) {
1479                src_offset_x[4] = n;
1480                src_offset_y[4] = m;
1481            } else {
1482                src_offset_x[4] = n - 1;
1483                src_offset_y[4] = m;
1484            }
1485
1486            src_offset_x[5] = n;
1487            src_offset_y[5] = m;
1488            if (n >= src_w-1) {
1489                src_offset_x[6] = n;
1490                src_offset_y[6] = m;
1491            } else {
1492                src_offset_x[6] = n + 1;
1493                src_offset_y[6] = m;
1494            }
1495
1496            if (n >= src_w - 2) {
1497                src_offset_x[7] = n;
1498                src_offset_y[7] = m;
1499            } else {
1500                src_offset_x[7] = n + 1 + 1;
1501                src_offset_y[7] = m;
1502            }
1503
1504            if ((m >= src_h - 1) || (n < 1)) {
1505                src_offset_x[8] = n;
1506                src_offset_y[8] = m;
1507            } else {
1508                src_offset_x[8] = n - 1;
1509                src_offset_y[8] = m;
1510            }
1511
1512            if (m >= src_h - 1) {
1513                src_offset_x[8] = n;
1514                src_offset_y[8] = m;
1515            } else {
1516                src_offset_x[9] = n;
1517                src_offset_y[9] = m;
1518            }
1519
1520            if ((m >= src_h-1) || (n >= src_w-1)) {
1521                src_offset_x[10] = n;
1522                src_offset_y[10] = m;
1523            } else {
1524                src_offset_x[10] = n + 1;
1525                src_offset_y[10] = m;
1526            }
1527
1528            if ((m >= src_h - 1) || (n >= src_w - 2)) {
1529                src_offset_x[11] = n;
1530                src_offset_y[11] = m;
1531            } else {
1532                src_offset_x[11] = n + 1 + 1;
1533                src_offset_y[11] = m;
1534            }
1535
1536            if ((m >= src_h - 2) || (n < 1)) {
1537                src_offset_x[12] = n;
1538                src_offset_y[12] = m;
1539            } else {
1540                src_offset_x[12] = n - 1;
1541                src_offset_y[12] = m;
1542            }
1543
1544            if (m >= src_h - 2) {
1545                src_offset_x[13] = n;
1546                src_offset_y[13] = m;
1547            } else {
1548                src_offset_x[13] = n;
1549                src_offset_y[13] = m;
1550            }
1551
1552            if ((m >= src_h - 2) || (n >= src_w - 1)) {
1553                src_offset_x[14] = n;
1554                src_offset_y[14] = m;
1555            } else {
1556                src_offset_x[14] = n + 1;
1557                src_offset_y[14] = m;
1558            }
1559
1560            if ((m >= src_h - 2) || (n >= src_w - 2)) {
1561                src_offset_x[15] = n;
1562                src_offset_y[15] = m;
1563            } else {
1564                src_offset_x[15] = n  + 1 + 1;
1565                src_offset_y[15] = m;
1566            }
1567
1568            for (k = -1; k < 3; k++) {
1569                const gdFixed f = gd_itofx(k)-f_f;
1570                const gdFixed f_fm1 = f - f_1;
1571                const gdFixed f_fp1 = f + f_1;
1572                const gdFixed f_fp2 = f + f_2;
1573                register gdFixed f_a = 0, f_b = 0, f_d = 0, f_c = 0;
1574                register gdFixed f_RY;
1575                int l;
1576
1577                if (f_fp2 > 0) f_a = gd_mulfx(f_fp2, gd_mulfx(f_fp2,f_fp2));
1578                if (f_fp1 > 0) f_b = gd_mulfx(f_fp1, gd_mulfx(f_fp1,f_fp1));
1579                if (f > 0)     f_c = gd_mulfx(f, gd_mulfx(f,f));
1580                if (f_fm1 > 0) f_d = gd_mulfx(f_fm1, gd_mulfx(f_fm1,f_fm1));
1581
1582                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);
1583
1584                for (l = -1; l < 3; l++) {
1585                    const gdFixed f = gd_itofx(l) - f_g;
1586                    const gdFixed f_fm1 = f - f_1;
1587                    const gdFixed f_fp1 = f + f_1;
1588                    const gdFixed f_fp2 = f + f_2;
1589                    register gdFixed f_a = 0, f_b = 0, f_c = 0, f_d = 0;
1590                    register gdFixed f_RX, f_R, f_rs, f_gs, f_bs, f_ba;
1591                    register int c;
1592                    const int _k = ((k+1)*4) + (l+1);
1593
1594                    if (f_fp2 > 0) f_a = gd_mulfx(f_fp2,gd_mulfx(f_fp2,f_fp2));
1595
1596                    if (f_fp1 > 0) f_b = gd_mulfx(f_fp1,gd_mulfx(f_fp1,f_fp1));
1597
1598                    if (f > 0) f_c = gd_mulfx(f,gd_mulfx(f,f));
1599
1600                    if (f_fm1 > 0) f_d = gd_mulfx(f_fm1,gd_mulfx(f_fm1,f_fm1));
1601
1602                    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);
1603                    f_R = gd_mulfx(f_RY,f_RX);
1604
1605                    c = src->tpixels[*(src_offset_y + _k)][*(src_offset_x + _k)];
1606                    f_rs = gd_itofx(gdTrueColorGetRed(c));
1607                    f_gs = gd_itofx(gdTrueColorGetGreen(c));
1608                    f_bs = gd_itofx(gdTrueColorGetBlue(c));
1609                    f_ba = gd_itofx(gdTrueColorGetAlpha(c));
1610
1611                    f_red += gd_mulfx(f_rs,f_R);
1612                    f_green += gd_mulfx(f_gs,f_R);
1613                    f_blue += gd_mulfx(f_bs,f_R);
1614                    f_alpha += gd_mulfx(f_ba,f_R);
1615                }
1616            }
1617
1618            red    = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_red,   f_gamma)),  0, 255);
1619            green  = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_green, f_gamma)),  0, 255);
1620            blue   = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_blue,  f_gamma)),  0, 255);
1621            alpha  = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_alpha,  f_gamma)), 0, 127);
1622
1623            *(dst_row + dst_offset_x) = gdTrueColorAlpha(red, green, blue, alpha);
1624
1625            dst_offset_x++;
1626        }
1627        dst_offset_y++;
1628    }
1629    return dst;
1630}
1631
1632gdImagePtr gdImageScale(const gdImagePtr src, const unsigned int new_width, const unsigned int new_height)
1633{
1634    gdImagePtr im_scaled = NULL;
1635
1636    if (src == NULL || src->interpolation_id < 0 || src->interpolation_id > GD_METHOD_COUNT) {
1637        return 0;
1638    }
1639
1640    switch (src->interpolation_id) {
1641        /*Special cases, optimized implementations */
1642        case GD_NEAREST_NEIGHBOUR:
1643            im_scaled = gdImageScaleNearestNeighbour(src, new_width, new_height);
1644            break;
1645
1646        case GD_BILINEAR_FIXED:
1647            im_scaled = gdImageScaleBilinear(src, new_width, new_height);
1648            break;
1649
1650        case GD_BICUBIC_FIXED:
1651            im_scaled = gdImageScaleBicubicFixed(src, new_width, new_height);
1652            break;
1653
1654        /* generic */
1655        default:
1656            if (src->interpolation == NULL) {
1657                return NULL;
1658            }
1659            im_scaled = gdImageScaleTwoPass(src, src->sx, src->sy, new_width, new_height);
1660            break;
1661    }
1662    return im_scaled;
1663}
1664
1665gdImagePtr gdImageRotateNearestNeighbour(gdImagePtr src, const float degrees, const int bgColor)
1666{
1667    float _angle = ((float) (-degrees / 180.0f) * (float)M_PI);
1668    const int src_w  = gdImageSX(src);
1669    const int src_h = gdImageSY(src);
1670    const unsigned int new_width = (unsigned int)(abs((int)(src_w * cos(_angle))) + abs((int)(src_h * sin(_angle))) + 0.5f);
1671    const unsigned int new_height = (unsigned int)(abs((int)(src_w * sin(_angle))) + abs((int)(src_h * cos(_angle))) + 0.5f);
1672    const gdFixed f_0_5 = gd_ftofx(0.5f);
1673    const gdFixed f_H = gd_itofx(src_h/2);
1674    const gdFixed f_W = gd_itofx(src_w/2);
1675    const gdFixed f_cos = gd_ftofx(cos(-_angle));
1676    const gdFixed f_sin = gd_ftofx(sin(-_angle));
1677
1678    unsigned int dst_offset_x;
1679    unsigned int dst_offset_y = 0;
1680    unsigned int i;
1681    gdImagePtr dst;
1682
1683    dst = gdImageCreateTrueColor(new_width, new_height);
1684    if (!dst) {
1685        return NULL;
1686    }
1687    dst->saveAlphaFlag = 1;
1688    for (i = 0; i < new_height; i++) {
1689        unsigned int j;
1690        dst_offset_x = 0;
1691        for (j = 0; j < new_width; j++) {
1692            gdFixed f_i = gd_itofx((int)i - (int)new_height/2);
1693            gdFixed f_j = gd_itofx((int)j - (int)new_width/2);
1694            gdFixed f_m = gd_mulfx(f_j,f_sin) + gd_mulfx(f_i,f_cos) + f_0_5 + f_H;
1695            gdFixed f_n = gd_mulfx(f_j,f_cos) - gd_mulfx(f_i,f_sin) + f_0_5 + f_W;
1696            long m = gd_fxtoi(f_m);
1697            long n = gd_fxtoi(f_n);
1698
1699            if ((m > 0) && (m < src_h-1) && (n > 0) && (n < src_w-1)) {
1700                if (dst_offset_y < new_height) {
1701                    dst->tpixels[dst_offset_y][dst_offset_x++] = src->tpixels[m][n];
1702                }
1703            } else {
1704                if (dst_offset_y < new_height) {
1705                    dst->tpixels[dst_offset_y][dst_offset_x++] = bgColor;
1706                }
1707            }
1708        }
1709        dst_offset_y++;
1710    }
1711    return dst;
1712}
1713
1714gdImagePtr gdImageRotateGeneric(gdImagePtr src, const float degrees, const int bgColor)
1715{
1716    float _angle = ((float) (-degrees / 180.0f) * (float)M_PI);
1717    const int angle_rounded = (int)floor(degrees * 100);
1718    const int src_w  = gdImageSX(src);
1719    const int src_h = gdImageSY(src);
1720    const unsigned int new_width = (unsigned int)(abs((int)(src_w * cos(_angle))) + abs((int)(src_h * sin(_angle))) + 0.5f);
1721    const unsigned int new_height = (unsigned int)(abs((int)(src_w * sin(_angle))) + abs((int)(src_h * cos(_angle))) + 0.5f);
1722    const gdFixed f_0_5 = gd_ftofx(0.5f);
1723    const gdFixed f_H = gd_itofx(src_h/2);
1724    const gdFixed f_W = gd_itofx(src_w/2);
1725    const gdFixed f_cos = gd_ftofx(cos(-_angle));
1726    const gdFixed f_sin = gd_ftofx(sin(-_angle));
1727
1728    unsigned int dst_offset_x;
1729    unsigned int dst_offset_y = 0;
1730    unsigned int i;
1731    gdImagePtr dst;
1732
1733    const gdFixed f_slop_y = f_sin;
1734    const gdFixed f_slop_x = f_cos;
1735    const gdFixed f_slop = f_slop_x > 0 && f_slop_x > 0 ?
1736                            f_slop_x > f_slop_y ? gd_divfx(f_slop_y, f_slop_x) : gd_divfx(f_slop_x, f_slop_y)
1737                        : 0;
1738
1739
1740    if (bgColor < 0) {
1741        return NULL;
1742    }
1743
1744    dst = gdImageCreateTrueColor(new_width, new_height);
1745    if (!dst) {
1746        return NULL;
1747    }
1748    dst->saveAlphaFlag = 1;
1749
1750    for (i = 0; i < new_height; i++) {
1751        unsigned int j;
1752        dst_offset_x = 0;
1753        for (j = 0; j < new_width; j++) {
1754            gdFixed f_i = gd_itofx((int)i - (int)new_height/ 2);
1755            gdFixed f_j = gd_itofx((int)j - (int)new_width / 2);
1756            gdFixed f_m = gd_mulfx(f_j,f_sin) + gd_mulfx(f_i,f_cos) + f_0_5 + f_H;
1757            gdFixed f_n = gd_mulfx(f_j,f_cos) - gd_mulfx(f_i,f_sin) + f_0_5 + f_W;
1758            long m = gd_fxtoi(f_m);
1759            long n = gd_fxtoi(f_n);
1760
1761            if ((n <= 0) || (m <= 0) || (m >= src_h) || (n >= src_w)) {
1762                dst->tpixels[dst_offset_y][dst_offset_x++] = bgColor;
1763            } else if ((n <= 1) || (m <= 1) || (m >= src_h - 1) || (n >= src_w - 1)) {
1764                gdFixed f_127 = gd_itofx(127);
1765                register int c = getPixelInterpolated(src, n, m, bgColor);
1766                c = c | (( gdTrueColorGetAlpha(c) + ((int)(127* gd_fxtof(f_slop)))) << 24);
1767
1768                dst->tpixels[dst_offset_y][dst_offset_x++] = _color_blend(bgColor, c);
1769            } else {
1770                dst->tpixels[dst_offset_y][dst_offset_x++] = getPixelInterpolated(src, n, m, bgColor);
1771            }
1772        }
1773        dst_offset_y++;
1774    }
1775    return dst;
1776}
1777
1778gdImagePtr gdImageRotateBilinear(gdImagePtr src, const float degrees, const int bgColor)
1779{
1780    float _angle = (float)((- degrees / 180.0f) * M_PI);
1781    const unsigned int src_w = gdImageSX(src);
1782    const unsigned int src_h = gdImageSY(src);
1783    unsigned int new_width = abs((int)(src_w*cos(_angle))) + abs((int)(src_h*sin(_angle) + 0.5f));
1784    unsigned int new_height = abs((int)(src_w*sin(_angle))) + abs((int)(src_h*cos(_angle) + 0.5f));
1785    const gdFixed f_0_5 = gd_ftofx(0.5f);
1786    const gdFixed f_H = gd_itofx(src_h/2);
1787    const gdFixed f_W = gd_itofx(src_w/2);
1788    const gdFixed f_cos = gd_ftofx(cos(-_angle));
1789    const gdFixed f_sin = gd_ftofx(sin(-_angle));
1790    const gdFixed f_1 = gd_itofx(1);
1791    unsigned int i;
1792    unsigned int dst_offset_x;
1793    unsigned int dst_offset_y = 0;
1794    unsigned int src_offset_x, src_offset_y;
1795    gdImagePtr dst;
1796
1797    dst = gdImageCreateTrueColor(new_width, new_height);
1798    if (dst == NULL) {
1799        return NULL;
1800    }
1801    dst->saveAlphaFlag = 1;
1802
1803    for (i = 0; i < new_height; i++) {
1804        unsigned int j;
1805        dst_offset_x = 0;
1806
1807        for (j=0; j < new_width; j++) {
1808            const gdFixed f_i = gd_itofx((int)i - (int)new_height/2);
1809            const gdFixed f_j = gd_itofx((int)j - (int)new_width/2);
1810            const gdFixed f_m = gd_mulfx(f_j,f_sin) + gd_mulfx(f_i,f_cos) + f_0_5 + f_H;
1811            const gdFixed f_n = gd_mulfx(f_j,f_cos) - gd_mulfx(f_i,f_sin) + f_0_5 + f_W;
1812            const unsigned int m = gd_fxtoi(f_m);
1813            const unsigned int n = gd_fxtoi(f_n);
1814
1815            if ((m > 0) && (m < src_h - 1) && (n > 0) && (n < src_w - 1)) {
1816                const gdFixed f_f = f_m - gd_itofx(m);
1817                const gdFixed f_g = f_n - gd_itofx(n);
1818                const gdFixed f_w1 = gd_mulfx(f_1-f_f, f_1-f_g);
1819                const gdFixed f_w2 = gd_mulfx(f_1-f_f, f_g);
1820                const gdFixed f_w3 = gd_mulfx(f_f, f_1-f_g);
1821                const gdFixed f_w4 = gd_mulfx(f_f, f_g);
1822
1823                if (n < src_w - 1) {
1824                    src_offset_x = n + 1;
1825                    src_offset_y = m;
1826                }
1827
1828                if (m < src_h-1) {
1829                    src_offset_x = n;
1830                    src_offset_y = m + 1;
1831                }
1832
1833                if (!((n >= src_w-1) || (m >= src_h-1))) {
1834                    src_offset_x = n + 1;
1835                    src_offset_y = m + 1;
1836                }
1837                {
1838                    const int pixel1 = src->tpixels[src_offset_y][src_offset_x];
1839                    register int pixel2, pixel3, pixel4;
1840
1841                    if (src_offset_y + 1 >= src_h) {
1842                        pixel2 = bgColor;
1843                        pixel3 = bgColor;
1844                        pixel4 = bgColor;
1845                    } else if (src_offset_x + 1 >= src_w) {
1846                        pixel2 = bgColor;
1847                        pixel3 = bgColor;
1848                        pixel4 = bgColor;
1849                    } else {
1850                        pixel2 = src->tpixels[src_offset_y][src_offset_x + 1];
1851                        pixel3 = src->tpixels[src_offset_y + 1][src_offset_x];
1852                        pixel4 = src->tpixels[src_offset_y + 1][src_offset_x + 1];
1853                    }
1854                    {
1855                        const gdFixed f_r1 = gd_itofx(gdTrueColorGetRed(pixel1));
1856                        const gdFixed f_r2 = gd_itofx(gdTrueColorGetRed(pixel2));
1857                        const gdFixed f_r3 = gd_itofx(gdTrueColorGetRed(pixel3));
1858                        const gdFixed f_r4 = gd_itofx(gdTrueColorGetRed(pixel4));
1859                        const gdFixed f_g1 = gd_itofx(gdTrueColorGetGreen(pixel1));
1860                        const gdFixed f_g2 = gd_itofx(gdTrueColorGetGreen(pixel2));
1861                        const gdFixed f_g3 = gd_itofx(gdTrueColorGetGreen(pixel3));
1862                        const gdFixed f_g4 = gd_itofx(gdTrueColorGetGreen(pixel4));
1863                        const gdFixed f_b1 = gd_itofx(gdTrueColorGetBlue(pixel1));
1864                        const gdFixed f_b2 = gd_itofx(gdTrueColorGetBlue(pixel2));
1865                        const gdFixed f_b3 = gd_itofx(gdTrueColorGetBlue(pixel3));
1866                        const gdFixed f_b4 = gd_itofx(gdTrueColorGetBlue(pixel4));
1867                        const gdFixed f_a1 = gd_itofx(gdTrueColorGetAlpha(pixel1));
1868                        const gdFixed f_a2 = gd_itofx(gdTrueColorGetAlpha(pixel2));
1869                        const gdFixed f_a3 = gd_itofx(gdTrueColorGetAlpha(pixel3));
1870                        const gdFixed f_a4 = gd_itofx(gdTrueColorGetAlpha(pixel4));
1871                        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);
1872                        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);
1873                        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);
1874                        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);
1875
1876                        const unsigned char red   = (unsigned char) CLAMP(gd_fxtoi(f_red),   0, 255);
1877                        const unsigned char green = (unsigned char) CLAMP(gd_fxtoi(f_green), 0, 255);
1878                        const unsigned char blue  = (unsigned char) CLAMP(gd_fxtoi(f_blue),  0, 255);
1879                        const unsigned char alpha = (unsigned char) CLAMP(gd_fxtoi(f_alpha), 0, 127);
1880
1881                        dst->tpixels[dst_offset_y][dst_offset_x++] = gdTrueColorAlpha(red, green, blue, alpha);
1882                    }
1883                }
1884            } else {
1885                dst->tpixels[dst_offset_y][dst_offset_x++] = bgColor;
1886            }
1887        }
1888        dst_offset_y++;
1889    }
1890    return dst;
1891}
1892
1893gdImagePtr gdImageRotateBicubicFixed(gdImagePtr src, const float degrees, const int bgColor)
1894{
1895    const float _angle = (float)((- degrees / 180.0f) * M_PI);
1896    const int src_w = gdImageSX(src);
1897    const int src_h = gdImageSY(src);
1898    const unsigned int new_width = abs((int)(src_w*cos(_angle))) + abs((int)(src_h*sin(_angle) + 0.5f));
1899    const unsigned int new_height = abs((int)(src_w*sin(_angle))) + abs((int)(src_h*cos(_angle) + 0.5f));
1900    const gdFixed f_0_5 = gd_ftofx(0.5f);
1901    const gdFixed f_H = gd_itofx(src_h/2);
1902    const gdFixed f_W = gd_itofx(src_w/2);
1903    const gdFixed f_cos = gd_ftofx(cos(-_angle));
1904    const gdFixed f_sin = gd_ftofx(sin(-_angle));
1905    const gdFixed f_1 = gd_itofx(1);
1906    const gdFixed f_2 = gd_itofx(2);
1907    const gdFixed f_4 = gd_itofx(4);
1908    const gdFixed f_6 = gd_itofx(6);
1909    const gdFixed f_gama = gd_ftofx(1.04f);
1910
1911    unsigned int dst_offset_x;
1912    unsigned int dst_offset_y = 0;
1913    unsigned int i;
1914    gdImagePtr dst;
1915
1916    dst = gdImageCreateTrueColor(new_width, new_height);
1917
1918    if (dst == NULL) {
1919        return NULL;
1920    }
1921    dst->saveAlphaFlag = 1;
1922
1923    for (i=0; i < new_height; i++) {
1924        unsigned int j;
1925        dst_offset_x = 0;
1926
1927        for (j=0; j < new_width; j++) {
1928            const gdFixed f_i = gd_itofx((int)i - (int)new_height/2);
1929            const gdFixed f_j = gd_itofx((int)j - (int)new_width/2);
1930            const gdFixed f_m = gd_mulfx(f_j,f_sin) + gd_mulfx(f_i,f_cos) + f_0_5 + f_H;
1931            const gdFixed f_n = gd_mulfx(f_j,f_cos) - gd_mulfx(f_i,f_sin) + f_0_5 + f_W;
1932            const int m = gd_fxtoi(f_m);
1933            const int n = gd_fxtoi(f_n);
1934
1935            if ((m > 0) && (m < src_h - 1) && (n > 0) && (n < src_w-1)) {
1936                const gdFixed f_f = f_m - gd_itofx(m);
1937                const gdFixed f_g = f_n - gd_itofx(n);
1938                unsigned int src_offset_x[16], src_offset_y[16];
1939                unsigned char red, green, blue, alpha;
1940                gdFixed f_red=0, f_green=0, f_blue=0, f_alpha=0;
1941                int k;
1942
1943                if ((m < 1) || (n < 1)) {
1944                    src_offset_x[0] = n;
1945                    src_offset_y[0] = m;
1946                } else {
1947                    src_offset_x[0] = n - 1;
1948                    src_offset_y[0] = m;
1949                }
1950
1951                if (m < 1) {
1952                    src_offset_x[1] = n;
1953                    src_offset_y[1] = m;
1954                } else {
1955                    src_offset_x[1] = n;
1956                    src_offset_y[1] = m ;
1957                }
1958
1959                if ((m < 1) || (n >= src_w-1)) {
1960                    src_offset_x[2] = - 1;
1961                    src_offset_y[2] = - 1;
1962                } else {
1963                    src_offset_x[2] = n + 1;
1964                    src_offset_y[2] = m ;
1965                }
1966
1967                if ((m < 1) || (n >= src_w-2)) {
1968                    src_offset_x[3] = - 1;
1969                    src_offset_y[3] = - 1;
1970                } else {
1971                    src_offset_x[3] = n + 1 + 1;
1972                    src_offset_y[3] = m ;
1973                }
1974
1975                if (n < 1) {
1976                    src_offset_x[4] = - 1;
1977                    src_offset_y[4] = - 1;
1978                } else {
1979                    src_offset_x[4] = n - 1;
1980                    src_offset_y[4] = m;
1981                }
1982
1983                src_offset_x[5] = n;
1984                src_offset_y[5] = m;
1985                if (n >= src_w-1) {
1986                    src_offset_x[6] = - 1;
1987                    src_offset_y[6] = - 1;
1988                } else {
1989                    src_offset_x[6] = n + 1;
1990                    src_offset_y[6] = m;
1991                }
1992
1993                if (n >= src_w-2) {
1994                    src_offset_x[7] = - 1;
1995                    src_offset_y[7] = - 1;
1996                } else {
1997                    src_offset_x[7] = n + 1 + 1;
1998                    src_offset_y[7] = m;
1999                }
2000
2001                if ((m >= src_h-1) || (n < 1)) {
2002                    src_offset_x[8] = - 1;
2003                    src_offset_y[8] = - 1;
2004                } else {
2005                    src_offset_x[8] = n - 1;
2006                    src_offset_y[8] = m;
2007                }
2008
2009                if (m >= src_h-1) {
2010                    src_offset_x[8] = - 1;
2011                    src_offset_y[8] = - 1;
2012                } else {
2013                    src_offset_x[9] = n;
2014                    src_offset_y[9] = m;
2015                }
2016
2017                if ((m >= src_h-1) || (n >= src_w-1)) {
2018                    src_offset_x[10] = - 1;
2019                    src_offset_y[10] = - 1;
2020                } else {
2021                    src_offset_x[10] = n + 1;
2022                    src_offset_y[10] = m;
2023                }
2024
2025                if ((m >= src_h-1) || (n >= src_w-2)) {
2026                    src_offset_x[11] = - 1;
2027                    src_offset_y[11] = - 1;
2028                } else {
2029                    src_offset_x[11] = n + 1 + 1;
2030                    src_offset_y[11] = m;
2031                }
2032
2033                if ((m >= src_h-2) || (n < 1)) {
2034                    src_offset_x[12] = - 1;
2035                    src_offset_y[12] = - 1;
2036                } else {
2037                    src_offset_x[12] = n - 1;
2038                    src_offset_y[12] = m;
2039                }
2040
2041                if (m >= src_h-2) {
2042                    src_offset_x[13] = - 1;
2043                    src_offset_y[13] = - 1;
2044                } else {
2045                    src_offset_x[13] = n;
2046                    src_offset_y[13] = m;
2047                }
2048
2049                if ((m >= src_h-2) || (n >= src_w - 1)) {
2050                    src_offset_x[14] = - 1;
2051                    src_offset_y[14] = - 1;
2052                } else {
2053                    src_offset_x[14] = n + 1;
2054                    src_offset_y[14] = m;
2055                }
2056
2057                if ((m >= src_h-2) || (n >= src_w-2)) {
2058                    src_offset_x[15] = - 1;
2059                    src_offset_y[15] = - 1;
2060                } else {
2061                    src_offset_x[15] = n  + 1 + 1;
2062                    src_offset_y[15] = m;
2063                }
2064
2065                for (k=-1; k<3; k++) {
2066                    const gdFixed f = gd_itofx(k)-f_f;
2067                    const gdFixed f_fm1 = f - f_1;
2068                    const gdFixed f_fp1 = f + f_1;
2069                    const gdFixed f_fp2 = f + f_2;
2070                    gdFixed f_a = 0, f_b = 0,f_c = 0, f_d = 0;
2071                    gdFixed f_RY;
2072                    int l;
2073
2074                    if (f_fp2 > 0) {
2075                        f_a = gd_mulfx(f_fp2,gd_mulfx(f_fp2,f_fp2));
2076                    }
2077
2078                    if (f_fp1 > 0) {
2079                        f_b = gd_mulfx(f_fp1,gd_mulfx(f_fp1,f_fp1));
2080                    }
2081
2082                    if (f > 0) {
2083                        f_c = gd_mulfx(f,gd_mulfx(f,f));
2084                    }
2085
2086                    if (f_fm1 > 0) {
2087                        f_d = gd_mulfx(f_fm1,gd_mulfx(f_fm1,f_fm1));
2088                    }
2089                    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);
2090
2091                    for (l=-1;  l< 3; l++) {
2092                        const gdFixed f = gd_itofx(l) - f_g;
2093                        const gdFixed f_fm1 = f - f_1;
2094                        const gdFixed f_fp1 = f + f_1;
2095                        const gdFixed f_fp2 = f + f_2;
2096                        gdFixed f_a = 0, f_b = 0, f_c = 0, f_d = 0;
2097                        gdFixed f_RX, f_R;
2098                        const int _k = ((k + 1) * 4) + (l + 1);
2099                        register gdFixed f_rs, f_gs, f_bs, f_as;
2100                        register int c;
2101
2102                        if (f_fp2 > 0) {
2103                            f_a = gd_mulfx(f_fp2,gd_mulfx(f_fp2,f_fp2));
2104                        }
2105
2106                        if (f_fp1 > 0) {
2107                            f_b = gd_mulfx(f_fp1,gd_mulfx(f_fp1,f_fp1));
2108                        }
2109
2110                        if (f > 0) {
2111                            f_c = gd_mulfx(f,gd_mulfx(f,f));
2112                        }
2113
2114                        if (f_fm1 > 0) {
2115                            f_d = gd_mulfx(f_fm1,gd_mulfx(f_fm1,f_fm1));
2116                        }
2117
2118                        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);
2119                        f_R = gd_mulfx(f_RY, f_RX);
2120
2121                        if ((src_offset_x[_k] <= 0) || (src_offset_y[_k] <= 0) || (src_offset_y[_k] >= src_h) || (src_offset_x[_k] >= src_w)) {
2122                            c = bgColor;
2123                        } 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)) {
2124                            gdFixed f_127 = gd_itofx(127);
2125                            c = src->tpixels[src_offset_y[_k]][src_offset_x[_k]];
2126                            c = c | (( (int) (gd_fxtof(gd_mulfx(f_R, f_127)) + 50.5f)) << 24);
2127                            c = _color_blend(bgColor, c);
2128                        } else {
2129                            c = src->tpixels[src_offset_y[_k]][src_offset_x[_k]];
2130                        }
2131
2132                        f_rs = gd_itofx(gdTrueColorGetRed(c));
2133                        f_gs = gd_itofx(gdTrueColorGetGreen(c));
2134                        f_bs = gd_itofx(gdTrueColorGetBlue(c));
2135                        f_as = gd_itofx(gdTrueColorGetAlpha(c));
2136
2137                        f_red   += gd_mulfx(f_rs, f_R);
2138                        f_green += gd_mulfx(f_gs, f_R);
2139                        f_blue  += gd_mulfx(f_bs, f_R);
2140                        f_alpha += gd_mulfx(f_as, f_R);
2141                    }
2142                }
2143
2144                red   = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_red, f_gama)),   0, 255);
2145                green = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_green, f_gama)), 0, 255);
2146                blue  = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_blue, f_gama)),  0, 255);
2147                alpha = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_alpha, f_gama)), 0, 127);
2148
2149                dst->tpixels[dst_offset_y][dst_offset_x] =  gdTrueColorAlpha(red, green, blue, alpha);
2150            } else {
2151                dst->tpixels[dst_offset_y][dst_offset_x] =  bgColor;
2152            }
2153            dst_offset_x++;
2154        }
2155
2156        dst_offset_y++;
2157    }
2158    return dst;
2159}
2160
2161gdImagePtr gdImageRotateInterpolated(const gdImagePtr src, const float angle, int bgcolor)
2162{
2163    const int angle_rounded = (int)floor(angle * 100);
2164
2165    if (bgcolor < 0) {
2166        return NULL;
2167    }
2168
2169    /* impact perf a bit, but not that much. Implementation for palette
2170       images can be done at a later point.
2171    */
2172    if (src->trueColor == 0) {
2173        if (bgcolor >= 0) {
2174            bgcolor =  gdTrueColorAlpha(src->red[bgcolor], src->green[bgcolor], src->blue[bgcolor], src->alpha[bgcolor]);
2175        }
2176        gdImagePaletteToTrueColor(src);
2177    }
2178
2179    /* no interpolation needed here */
2180    switch (angle_rounded) {
2181        case -27000:
2182        case   9000:
2183            return gdImageRotate90(src, 0);
2184        case -18000:
2185        case  18000:
2186            return gdImageRotate180(src, 0);
2187        case -9000:
2188        case 27000:
2189            return gdImageRotate270(src, 0);
2190    }
2191
2192    if (src == NULL || src->interpolation_id < 1 || src->interpolation_id > GD_METHOD_COUNT) {
2193        return NULL;
2194    }
2195
2196    switch (src->interpolation_id) {
2197        case GD_NEAREST_NEIGHBOUR:
2198            return gdImageRotateNearestNeighbour(src, angle, bgcolor);
2199            break;
2200
2201        case GD_BILINEAR_FIXED:
2202            return gdImageRotateBilinear(src, angle, bgcolor);
2203            break;
2204
2205        case GD_BICUBIC_FIXED:
2206            return gdImageRotateBicubicFixed(src, angle, bgcolor);
2207            break;
2208
2209        default:
2210            return gdImageRotateGeneric(src, angle, bgcolor);
2211    }
2212    return NULL;
2213}
2214
2215/**
2216 * Title: Affine transformation
2217 **/
2218
2219/**
2220 * Group: Transform
2221 **/
2222
2223 static void gdImageClipRectangle(gdImagePtr im, gdRectPtr r)
2224{
2225    int c1x, c1y, c2x, c2y;
2226    int x1,y1;
2227
2228    gdImageGetClip(im, &c1x, &c1y, &c2x, &c2y);
2229    x1 = r->x + r->width - 1;
2230    y1 = r->y + r->height - 1;
2231    r->x = CLAMP(r->x, c1x, c2x);
2232    r->y = CLAMP(r->y, c1y, c2y);
2233    r->width = CLAMP(x1, c1x, c2x) - r->x + 1;
2234    r->height = CLAMP(y1, c1y, c2y) - r->y + 1;
2235}
2236
2237void gdDumpRect(const char *msg, gdRectPtr r)
2238{
2239    printf("%s (%i, %i) (%i, %i)\n", msg, r->x, r->y, r->width, r->height);
2240}
2241
2242/**
2243 * Function: gdTransformAffineGetImage
2244 *  Applies an affine transformation to a region and return an image
2245 *  containing the complete transformation.
2246 *
2247 * Parameters:
2248 *  dst - Pointer to a gdImagePtr to store the created image, NULL when
2249 *        the creation or the transformation failed
2250 *  src - Source image
2251 *  src_area - rectangle defining the source region to transform
2252 *  dstY - Y position in the destination image
2253 *  affine - The desired affine transformation
2254 *
2255 * Returns:
2256 *  GD_TRUE if the affine is rectilinear or GD_FALSE
2257 */
2258int gdTransformAffineGetImage(gdImagePtr *dst,
2259          const gdImagePtr src,
2260          gdRectPtr src_area,
2261          const double affine[6])
2262{
2263    int res;
2264    double m[6];
2265    gdRect bbox;
2266    gdRect area_full;
2267
2268    if (src_area == NULL) {
2269        area_full.x = 0;
2270        area_full.y = 0;
2271        area_full.width  = gdImageSX(src);
2272        area_full.height = gdImageSY(src);
2273        src_area = &area_full;
2274    }
2275
2276    gdTransformAffineBoundingBox(src_area, affine, &bbox);
2277
2278    *dst = gdImageCreateTrueColor(bbox.width, bbox.height);
2279    if (*dst == NULL) {
2280        return GD_FALSE;
2281    }
2282    (*dst)->saveAlphaFlag = 1;
2283
2284    if (!src->trueColor) {
2285        gdImagePaletteToTrueColor(src);
2286    }
2287
2288    /* Translate to dst origin (0,0) */
2289    gdAffineTranslate(m, -bbox.x, -bbox.y);
2290    gdAffineConcat(m, affine, m);
2291
2292    gdImageAlphaBlending(*dst, 0);
2293
2294    res = gdTransformAffineCopy(*dst,
2295          0,0,
2296          src,
2297          src_area,
2298          m);
2299
2300    if (res != GD_TRUE) {
2301        gdImageDestroy(*dst);
2302        dst = NULL;
2303        return GD_FALSE;
2304    } else {
2305        return GD_TRUE;
2306    }
2307}
2308
2309/**
2310 * Function: gdTransformAffineCopy
2311 *  Applies an affine transformation to a region and copy the result
2312 *  in a destination to the given position.
2313 *
2314 * Parameters:
2315 *  dst - Image to draw the transformed image
2316 *  src - Source image
2317 *  dstX - X position in the destination image
2318 *  dstY - Y position in the destination image
2319 *  src_area - Rectangular region to rotate in the src image
2320 *
2321 * Returns:
2322 *  GD_TRUE if the affine is rectilinear or GD_FALSE
2323 */
2324int gdTransformAffineCopy(gdImagePtr dst,
2325          int dst_x, int dst_y,
2326          const gdImagePtr src,
2327          gdRectPtr src_region,
2328          const double affine[6])
2329{
2330    int c1x,c1y,c2x,c2y;
2331    int backclip = 0;
2332    int backup_clipx1, backup_clipy1, backup_clipx2, backup_clipy2;
2333    register int x, y, src_offset_x, src_offset_y;
2334    double inv[6];
2335    int *dst_p;
2336    gdPointF pt, src_pt;
2337    gdRect bbox;
2338    int end_x, end_y;
2339    gdInterpolationMethod interpolation_id_bak = GD_DEFAULT;
2340    interpolation_method interpolation_bak;
2341
2342    /* These methods use special implementations */
2343    if (src->interpolation_id == GD_BILINEAR_FIXED || src->interpolation_id == GD_BICUBIC_FIXED || src->interpolation_id == GD_NEAREST_NEIGHBOUR) {
2344        interpolation_id_bak = src->interpolation_id;
2345        interpolation_bak = src->interpolation;
2346
2347        gdImageSetInterpolationMethod(src, GD_BICUBIC);
2348    }
2349
2350
2351    gdImageClipRectangle(src, src_region);
2352
2353    if (src_region->x > 0 || src_region->y > 0
2354        || src_region->width < gdImageSX(src)
2355        || src_region->height < gdImageSY(src)) {
2356        backclip = 1;
2357
2358        gdImageGetClip(src, &backup_clipx1, &backup_clipy1,
2359        &backup_clipx2, &backup_clipy2);
2360
2361        gdImageSetClip(src, src_region->x, src_region->y,
2362            src_region->x + src_region->width - 1,
2363            src_region->y + src_region->height - 1);
2364    }
2365
2366    if (!gdTransformAffineBoundingBox(src_region, affine, &bbox)) {
2367        if (backclip) {
2368            gdImageSetClip(src, backup_clipx1, backup_clipy1,
2369                    backup_clipx2, backup_clipy2);
2370        }
2371        gdImageSetInterpolationMethod(src, interpolation_id_bak);
2372        return GD_FALSE;
2373    }
2374
2375    gdImageGetClip(dst, &c1x, &c1y, &c2x, &c2y);
2376
2377    end_x = bbox.width  + (int) fabs(bbox.x);
2378    end_y = bbox.height + (int) fabs(bbox.y);
2379
2380    /* Get inverse affine to let us work with destination -> source */
2381    gdAffineInvert(inv, affine);
2382
2383    src_offset_x =  src_region->x;
2384    src_offset_y =  src_region->y;
2385
2386    if (dst->alphaBlendingFlag) {
2387        for (y = bbox.y; y <= end_y; y++) {
2388            pt.y = y + 0.5;
2389            for (x = 0; x <= end_x; x++) {
2390                pt.x = x + 0.5;
2391                gdAffineApplyToPointF(&src_pt, &pt, inv);
2392                gdImageSetPixel(dst, dst_x + x, dst_y + y, getPixelInterpolated(src, src_offset_x + src_pt.x, src_offset_y + src_pt.y, 0));
2393            }
2394        }
2395    } else {
2396        for (y = 0; y <= end_y; y++) {
2397            pt.y = y + 0.5 + bbox.y;
2398            if ((dst_y + y) < 0 || ((dst_y + y) > gdImageSY(dst) -1)) {
2399                continue;
2400            }
2401            dst_p = dst->tpixels[dst_y + y] + dst_x;
2402
2403            for (x = 0; x <= end_x; x++) {
2404                pt.x = x + 0.5 + bbox.x;
2405                gdAffineApplyToPointF(&src_pt, &pt, inv);
2406
2407                if ((dst_x + x) < 0 || (dst_x + x) > (gdImageSX(dst) - 1)) {
2408                    break;
2409                }
2410                *(dst_p++) = getPixelInterpolated(src, src_offset_x + src_pt.x, src_offset_y + src_pt.y, -1);
2411            }
2412        }
2413    }
2414
2415    /* Restore clip if required */
2416    if (backclip) {
2417        gdImageSetClip(src, backup_clipx1, backup_clipy1,
2418                backup_clipx2, backup_clipy2);
2419    }
2420
2421    gdImageSetInterpolationMethod(src, interpolation_id_bak);
2422    return GD_TRUE;
2423}
2424
2425/**
2426 * Function: gdTransformAffineBoundingBox
2427 *  Returns the bounding box of an affine transformation applied to a
2428 *  rectangular area <gdRect>
2429 *
2430 * Parameters:
2431 *  src - Rectangular source area for the affine transformation
2432 *  affine - the affine transformation
2433 *  bbox - the resulting bounding box
2434 *
2435 * Returns:
2436 *  GD_TRUE if the affine is rectilinear or GD_FALSE
2437 */
2438int gdTransformAffineBoundingBox(gdRectPtr src, const double affine[6], gdRectPtr bbox)
2439{
2440    gdPointF extent[4], min, max, point;
2441    int i;
2442
2443    extent[0].x=0.0;
2444    extent[0].y=0.0;
2445    extent[1].x=(double) src->width;
2446    extent[1].y=0.0;
2447    extent[2].x=(double) src->width;
2448    extent[2].y=(double) src->height;
2449    extent[3].x=0.0;
2450    extent[3].y=(double) src->height;
2451
2452    for (i=0; i < 4; i++) {
2453        point=extent[i];
2454        if (gdAffineApplyToPointF(&extent[i], &point, affine) != GD_TRUE) {
2455            return GD_FALSE;
2456        }
2457    }
2458    min=extent[0];
2459    max=extent[0];
2460
2461    for (i=1; i < 4; i++) {
2462        if (min.x > extent[i].x)
2463            min.x=extent[i].x;
2464        if (min.y > extent[i].y)
2465            min.y=extent[i].y;
2466        if (max.x < extent[i].x)
2467            max.x=extent[i].x;
2468        if (max.y < extent[i].y)
2469            max.y=extent[i].y;
2470    }
2471    bbox->x = (int) min.x;
2472    bbox->y = (int) min.y;
2473    bbox->width  = (int) floor(max.x - min.x) - 1;
2474    bbox->height = (int) floor(max.y - min.y);
2475    return GD_TRUE;
2476}
2477
2478int gdImageSetInterpolationMethod(gdImagePtr im, gdInterpolationMethod id)
2479{
2480    if (im == NULL || id < 0 || id > GD_METHOD_COUNT) {
2481        return 0;
2482    }
2483
2484    switch (id) {
2485        case GD_DEFAULT:
2486            id = GD_BILINEAR_FIXED;
2487        /* Optimized versions */
2488        case GD_BILINEAR_FIXED:
2489        case GD_BICUBIC_FIXED:
2490        case GD_NEAREST_NEIGHBOUR:
2491        case GD_WEIGHTED4:
2492            im->interpolation = NULL;
2493            break;
2494
2495        /* generic versions*/
2496        case GD_BELL:
2497            im->interpolation = filter_bell;
2498            break;
2499        case GD_BESSEL:
2500            im->interpolation = filter_bessel;
2501            break;
2502        case GD_BICUBIC:
2503            im->interpolation = filter_bicubic;
2504            break;
2505        case GD_BLACKMAN:
2506            im->interpolation = filter_blackman;
2507            break;
2508        case GD_BOX:
2509            im->interpolation = filter_box;
2510            break;
2511        case GD_BSPLINE:
2512            im->interpolation = filter_bspline;
2513            break;
2514        case GD_CATMULLROM:
2515            im->interpolation = filter_catmullrom;
2516            break;
2517        case GD_GAUSSIAN:
2518            im->interpolation = filter_gaussian;
2519            break;
2520        case GD_GENERALIZED_CUBIC:
2521            im->interpolation = filter_generalized_cubic;
2522            break;
2523        case GD_HERMITE:
2524            im->interpolation = filter_hermite;
2525            break;
2526        case GD_HAMMING:
2527            im->interpolation = filter_hamming;
2528            break;
2529        case GD_HANNING:
2530            im->interpolation = filter_hanning;
2531            break;
2532        case GD_MITCHELL:
2533            im->interpolation = filter_mitchell;
2534            break;
2535        case GD_POWER:
2536            im->interpolation = filter_power;
2537            break;
2538        case GD_QUADRATIC:
2539            im->interpolation = filter_quadratic;
2540            break;
2541        case GD_SINC:
2542            im->interpolation = filter_sinc;
2543            break;
2544        case GD_TRIANGLE:
2545            im->interpolation = filter_triangle;
2546            break;
2547
2548        default:
2549            return 0;
2550            break;
2551    }
2552    im->interpolation_id = id;
2553    return 1;
2554}
2555
2556#ifdef _MSC_VER
2557# pragma optimize("", on)
2558#endif
2559