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 < gdMaxColors) {
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