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