xref: /PHP-8.3/ext/gd/libgd/gd_interpolation.c (revision 47683487)
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);
752 	const int yi=(int)(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 	if (bgColor < 0) {
1717 		return NULL;
1718 	}
1719 
1720     gdRotatedImageSize(src, degrees, &bbox);
1721     new_width = bbox.width;
1722     new_height = bbox.height;
1723 
1724 	dst = gdImageCreateTrueColor(new_width, new_height);
1725 	if (!dst) {
1726 		return NULL;
1727 	}
1728 	dst->saveAlphaFlag = 1;
1729 
1730 	for (i = 0; i < new_height; i++) {
1731 		unsigned int j;
1732 		dst_offset_x = 0;
1733 		for (j = 0; j < new_width; j++) {
1734 			gdFixed f_i = gd_itofx((int)i - (int)new_height/ 2);
1735 			gdFixed f_j = gd_itofx((int)j - (int)new_width / 2);
1736 			gdFixed f_m = gd_mulfx(f_j,f_sin) + gd_mulfx(f_i,f_cos) + f_0_5 + f_H;
1737 			gdFixed f_n = gd_mulfx(f_j,f_cos) - gd_mulfx(f_i,f_sin) + f_0_5 + f_W;
1738 			long m = gd_fxtoi(f_m);
1739 			long n = gd_fxtoi(f_n);
1740 
1741 			if (m < -1 || n < -1 || m >= src_h || n >= src_w ) {
1742 				dst->tpixels[dst_offset_y][dst_offset_x++] = bgColor;
1743 			} else {
1744 				dst->tpixels[dst_offset_y][dst_offset_x++] = getPixelInterpolated(src, gd_fxtod(f_n), gd_fxtod(f_m), bgColor);
1745 			}
1746 		}
1747 		dst_offset_y++;
1748 	}
1749 	return dst;
1750 }
1751 
gdImageRotateBilinear(gdImagePtr src,const float degrees,const int bgColor)1752 gdImagePtr gdImageRotateBilinear(gdImagePtr src, const float degrees, const int bgColor)
1753 {
1754 	float _angle = (float)((- degrees / 180.0f) * M_PI);
1755 	const unsigned int src_w = gdImageSX(src);
1756 	const unsigned int src_h = gdImageSY(src);
1757 	unsigned int new_width, new_height;
1758 	const gdFixed f_0_5 = gd_ftofx(0.5f);
1759 	const gdFixed f_H = gd_itofx(src_h/2);
1760 	const gdFixed f_W = gd_itofx(src_w/2);
1761 	const gdFixed f_cos = gd_ftofx(cos(-_angle));
1762 	const gdFixed f_sin = gd_ftofx(sin(-_angle));
1763 	const gdFixed f_1 = gd_itofx(1);
1764 	unsigned int i;
1765 	unsigned int dst_offset_x;
1766 	unsigned int dst_offset_y = 0;
1767 	unsigned int src_offset_x, src_offset_y;
1768 	gdImagePtr dst;
1769 	gdRect bbox;
1770 
1771 	gdRotatedImageSize(src, degrees, &bbox);
1772 
1773 	new_width = bbox.width;
1774 	new_height = bbox.height;
1775 
1776 	dst = gdImageCreateTrueColor(new_width, new_height);
1777 	if (dst == NULL) {
1778 		return NULL;
1779 	}
1780 	dst->saveAlphaFlag = 1;
1781 
1782 	for (i = 0; i < new_height; i++) {
1783 		unsigned int j;
1784 		dst_offset_x = 0;
1785 
1786 		for (j=0; j < new_width; j++) {
1787 			const gdFixed f_i = gd_itofx((int)i - (int)new_height/2);
1788 			const gdFixed f_j = gd_itofx((int)j - (int)new_width/2);
1789 			const gdFixed f_m = gd_mulfx(f_j,f_sin) + gd_mulfx(f_i,f_cos) + f_0_5 + f_H;
1790 			const gdFixed f_n = gd_mulfx(f_j,f_cos) - gd_mulfx(f_i,f_sin) + f_0_5 + f_W;
1791 			const int m = gd_fxtoi(f_m);
1792 			const int n = gd_fxtoi(f_n);
1793 
1794 			if ((m >= 0) && (m < src_h - 1) && (n >= 0) && (n < src_w - 1)) {
1795 				const gdFixed f_f = f_m - gd_itofx(m);
1796 				const gdFixed f_g = f_n - gd_itofx(n);
1797 				const gdFixed f_w1 = gd_mulfx(f_1-f_f, f_1-f_g);
1798 				const gdFixed f_w2 = gd_mulfx(f_1-f_f, f_g);
1799 				const gdFixed f_w3 = gd_mulfx(f_f, f_1-f_g);
1800 				const gdFixed f_w4 = gd_mulfx(f_f, f_g);
1801 
1802 				if (m < src_h-1) {
1803 					src_offset_x = n;
1804 					src_offset_y = m + 1;
1805 				}
1806 
1807 				if (!((n >= src_w-1) || (m >= src_h-1))) {
1808 					src_offset_x = n + 1;
1809 					src_offset_y = m + 1;
1810 				}
1811 				{
1812 					const int pixel1 = src->tpixels[src_offset_y][src_offset_x];
1813 					register int pixel2, pixel3, pixel4;
1814 
1815 					if (src_offset_y + 1 >= src_h) {
1816 						pixel2 = pixel1;
1817 						pixel3 = pixel1;
1818 						pixel4 = pixel1;
1819 					} else if (src_offset_x + 1 >= src_w) {
1820 						pixel2 = pixel1;
1821 						pixel3 = pixel1;
1822 						pixel4 = pixel1;
1823 					} else {
1824 					    pixel2 = src->tpixels[src_offset_y][src_offset_x + 1];
1825 						pixel3 = src->tpixels[src_offset_y + 1][src_offset_x];
1826 						pixel4 = src->tpixels[src_offset_y + 1][src_offset_x + 1];
1827 					}
1828 					{
1829 						const gdFixed f_r1 = gd_itofx(gdTrueColorGetRed(pixel1));
1830 						const gdFixed f_r2 = gd_itofx(gdTrueColorGetRed(pixel2));
1831 						const gdFixed f_r3 = gd_itofx(gdTrueColorGetRed(pixel3));
1832 						const gdFixed f_r4 = gd_itofx(gdTrueColorGetRed(pixel4));
1833 						const gdFixed f_g1 = gd_itofx(gdTrueColorGetGreen(pixel1));
1834 						const gdFixed f_g2 = gd_itofx(gdTrueColorGetGreen(pixel2));
1835 						const gdFixed f_g3 = gd_itofx(gdTrueColorGetGreen(pixel3));
1836 						const gdFixed f_g4 = gd_itofx(gdTrueColorGetGreen(pixel4));
1837 						const gdFixed f_b1 = gd_itofx(gdTrueColorGetBlue(pixel1));
1838 						const gdFixed f_b2 = gd_itofx(gdTrueColorGetBlue(pixel2));
1839 						const gdFixed f_b3 = gd_itofx(gdTrueColorGetBlue(pixel3));
1840 						const gdFixed f_b4 = gd_itofx(gdTrueColorGetBlue(pixel4));
1841 						const gdFixed f_a1 = gd_itofx(gdTrueColorGetAlpha(pixel1));
1842 						const gdFixed f_a2 = gd_itofx(gdTrueColorGetAlpha(pixel2));
1843 						const gdFixed f_a3 = gd_itofx(gdTrueColorGetAlpha(pixel3));
1844 						const gdFixed f_a4 = gd_itofx(gdTrueColorGetAlpha(pixel4));
1845 						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);
1846 						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);
1847 						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);
1848 						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);
1849 
1850 						const unsigned char red   = (unsigned char) CLAMP(gd_fxtoi(f_red),   0, 255);
1851 						const unsigned char green = (unsigned char) CLAMP(gd_fxtoi(f_green), 0, 255);
1852 						const unsigned char blue  = (unsigned char) CLAMP(gd_fxtoi(f_blue),  0, 255);
1853 						const unsigned char alpha = (unsigned char) CLAMP(gd_fxtoi(f_alpha), 0, 127);
1854 
1855 						dst->tpixels[dst_offset_y][dst_offset_x++] = gdTrueColorAlpha(red, green, blue, alpha);
1856 					}
1857 				}
1858 			} else {
1859 				dst->tpixels[dst_offset_y][dst_offset_x++] = bgColor;
1860 			}
1861 		}
1862 		dst_offset_y++;
1863 	}
1864 	return dst;
1865 }
1866 
gdImageRotateBicubicFixed(gdImagePtr src,const float degrees,const int bgColor)1867 gdImagePtr gdImageRotateBicubicFixed(gdImagePtr src, const float degrees, const int bgColor)
1868 {
1869 	const float _angle = (float)((- degrees / 180.0f) * M_PI);
1870 	const int src_w = gdImageSX(src);
1871 	const int src_h = gdImageSY(src);
1872 	unsigned int new_width, new_height;
1873 	const gdFixed f_0_5 = gd_ftofx(0.5f);
1874 	const gdFixed f_H = gd_itofx(src_h/2);
1875 	const gdFixed f_W = gd_itofx(src_w/2);
1876 	const gdFixed f_cos = gd_ftofx(cos(-_angle));
1877 	const gdFixed f_sin = gd_ftofx(sin(-_angle));
1878 	const gdFixed f_1 = gd_itofx(1);
1879 	const gdFixed f_2 = gd_itofx(2);
1880 	const gdFixed f_4 = gd_itofx(4);
1881 	const gdFixed f_6 = gd_itofx(6);
1882 	const gdFixed f_gama = gd_ftofx(1.04f);
1883 
1884 	unsigned int dst_offset_x;
1885 	unsigned int dst_offset_y = 0;
1886 	unsigned int i;
1887 	gdImagePtr dst;
1888 	gdRect bbox;
1889 
1890 	gdRotatedImageSize(src, degrees, &bbox);
1891 	new_width = bbox.width;
1892 	new_height = bbox.height;
1893 	dst = gdImageCreateTrueColor(new_width, new_height);
1894 
1895 	if (dst == NULL) {
1896 		return NULL;
1897 	}
1898 	dst->saveAlphaFlag = 1;
1899 
1900 	for (i=0; i < new_height; i++) {
1901 		unsigned int j;
1902 		dst_offset_x = 0;
1903 
1904 		for (j=0; j < new_width; j++) {
1905 			const gdFixed f_i = gd_itofx((int)i - (int)new_height/2);
1906 			const gdFixed f_j = gd_itofx((int)j - (int)new_width/2);
1907 			const gdFixed f_m = gd_mulfx(f_j,f_sin) + gd_mulfx(f_i,f_cos) + f_0_5 + f_H;
1908 			const gdFixed f_n = gd_mulfx(f_j,f_cos) - gd_mulfx(f_i,f_sin) + f_0_5 + f_W;
1909 			const int m = gd_fxtoi(f_m);
1910 			const int n = gd_fxtoi(f_n);
1911 
1912 			if ((m > 0) && (m < src_h - 1) && (n > 0) && (n < src_w-1)) {
1913 				const gdFixed f_f = f_m - gd_itofx(m);
1914 				const gdFixed f_g = f_n - gd_itofx(n);
1915 				unsigned int src_offset_x[16], src_offset_y[16];
1916 				unsigned char red, green, blue, alpha;
1917 				gdFixed f_red=0, f_green=0, f_blue=0, f_alpha=0;
1918 				int k;
1919 
1920 				if ((m < 1) || (n < 1)) {
1921 					src_offset_x[0] = n;
1922 					src_offset_y[0] = m;
1923 				} else {
1924 					src_offset_x[0] = n - 1;
1925 					src_offset_y[0] = m;
1926 				}
1927 
1928 				src_offset_x[1] = n;
1929 				src_offset_y[1] = m;
1930 
1931 				if ((m < 1) || (n >= src_w-1)) {
1932 					src_offset_x[2] = - 1;
1933 					src_offset_y[2] = - 1;
1934 				} else {
1935 					src_offset_x[2] = n + 1;
1936 					src_offset_y[2] = m ;
1937 				}
1938 
1939 				if ((m < 1) || (n >= src_w-2)) {
1940 					src_offset_x[3] = - 1;
1941 					src_offset_y[3] = - 1;
1942 				} else {
1943 					src_offset_x[3] = n + 1 + 1;
1944 					src_offset_y[3] = m ;
1945 				}
1946 
1947 				if (n < 1) {
1948 					src_offset_x[4] = - 1;
1949 					src_offset_y[4] = - 1;
1950 				} else {
1951 					src_offset_x[4] = n - 1;
1952 					src_offset_y[4] = m;
1953 				}
1954 
1955 				src_offset_x[5] = n;
1956 				src_offset_y[5] = m;
1957 				if (n >= src_w-1) {
1958 					src_offset_x[6] = - 1;
1959 					src_offset_y[6] = - 1;
1960 				} else {
1961 					src_offset_x[6] = n + 1;
1962 					src_offset_y[6] = m;
1963 				}
1964 
1965 				if (n >= src_w-2) {
1966 					src_offset_x[7] = - 1;
1967 					src_offset_y[7] = - 1;
1968 				} else {
1969 					src_offset_x[7] = n + 1 + 1;
1970 					src_offset_y[7] = m;
1971 				}
1972 
1973 				if ((m >= src_h-1) || (n < 1)) {
1974 					src_offset_x[8] = - 1;
1975 					src_offset_y[8] = - 1;
1976 				} else {
1977 					src_offset_x[8] = n - 1;
1978 					src_offset_y[8] = m;
1979 				}
1980 
1981 				if (m >= src_h-1) {
1982 					src_offset_x[9] = - 1;
1983 					src_offset_y[9] = - 1;
1984 				} else {
1985 					src_offset_x[9] = n;
1986 					src_offset_y[9] = m;
1987 				}
1988 
1989 				if ((m >= src_h-1) || (n >= src_w-1)) {
1990 					src_offset_x[10] = - 1;
1991 					src_offset_y[10] = - 1;
1992 				} else {
1993 					src_offset_x[10] = n + 1;
1994 					src_offset_y[10] = m;
1995 				}
1996 
1997 				if ((m >= src_h-1) || (n >= src_w-2)) {
1998 					src_offset_x[11] = - 1;
1999 					src_offset_y[11] = - 1;
2000 				} else {
2001 					src_offset_x[11] = n + 1 + 1;
2002 					src_offset_y[11] = m;
2003 				}
2004 
2005 				if ((m >= src_h-2) || (n < 1)) {
2006 					src_offset_x[12] = - 1;
2007 					src_offset_y[12] = - 1;
2008 				} else {
2009 					src_offset_x[12] = n - 1;
2010 					src_offset_y[12] = m;
2011 				}
2012 
2013 				if (m >= src_h-2) {
2014 					src_offset_x[13] = - 1;
2015 					src_offset_y[13] = - 1;
2016 				} else {
2017 					src_offset_x[13] = n;
2018 					src_offset_y[13] = m;
2019 				}
2020 
2021 				if ((m >= src_h-2) || (n >= src_w - 1)) {
2022 					src_offset_x[14] = - 1;
2023 					src_offset_y[14] = - 1;
2024 				} else {
2025 					src_offset_x[14] = n + 1;
2026 					src_offset_y[14] = m;
2027 				}
2028 
2029 				if ((m >= src_h-2) || (n >= src_w-2)) {
2030 					src_offset_x[15] = - 1;
2031 					src_offset_y[15] = - 1;
2032 				} else {
2033 					src_offset_x[15] = n  + 1 + 1;
2034 					src_offset_y[15] = m;
2035 				}
2036 
2037 				for (k=-1; k<3; k++) {
2038 					const gdFixed f = gd_itofx(k)-f_f;
2039 					const gdFixed f_fm1 = f - f_1;
2040 					const gdFixed f_fp1 = f + f_1;
2041 					const gdFixed f_fp2 = f + f_2;
2042 					gdFixed f_a = 0, f_b = 0,f_c = 0, f_d = 0;
2043 					gdFixed f_RY;
2044 					int l;
2045 
2046 					if (f_fp2 > 0) {
2047 						f_a = gd_mulfx(f_fp2,gd_mulfx(f_fp2,f_fp2));
2048 					}
2049 
2050 					if (f_fp1 > 0) {
2051 						f_b = gd_mulfx(f_fp1,gd_mulfx(f_fp1,f_fp1));
2052 					}
2053 
2054 					if (f > 0) {
2055 						f_c = gd_mulfx(f,gd_mulfx(f,f));
2056 					}
2057 
2058 					if (f_fm1 > 0) {
2059 						f_d = gd_mulfx(f_fm1,gd_mulfx(f_fm1,f_fm1));
2060 					}
2061 					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);
2062 
2063 					for (l=-1;  l< 3; l++) {
2064 						const gdFixed f = gd_itofx(l) - f_g;
2065 						const gdFixed f_fm1 = f - f_1;
2066 						const gdFixed f_fp1 = f + f_1;
2067 						const gdFixed f_fp2 = f + f_2;
2068 						gdFixed f_a = 0, f_b = 0, f_c = 0, f_d = 0;
2069 						gdFixed f_RX, f_R;
2070 						const int _k = ((k + 1) * 4) + (l + 1);
2071 						register gdFixed f_rs, f_gs, f_bs, f_as;
2072 						register int c;
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 
2090 						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);
2091 						f_R = gd_mulfx(f_RY, f_RX);
2092 
2093 						if ((src_offset_x[_k] <= 0) || (src_offset_y[_k] <= 0) || (src_offset_y[_k] >= src_h) || (src_offset_x[_k] >= src_w)) {
2094 							c = bgColor;
2095 						} 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)) {
2096 							gdFixed f_127 = gd_itofx(127);
2097 							c = src->tpixels[src_offset_y[_k]][src_offset_x[_k]];
2098 							c = c | (( (int) (gd_fxtof(gd_mulfx(f_R, f_127)) + 50.5f)) << 24);
2099 							c = _color_blend(bgColor, c);
2100 						} else {
2101 							c = src->tpixels[src_offset_y[_k]][src_offset_x[_k]];
2102 						}
2103 
2104 						f_rs = gd_itofx(gdTrueColorGetRed(c));
2105 						f_gs = gd_itofx(gdTrueColorGetGreen(c));
2106 						f_bs = gd_itofx(gdTrueColorGetBlue(c));
2107 						f_as = gd_itofx(gdTrueColorGetAlpha(c));
2108 
2109 						f_red   += gd_mulfx(f_rs, f_R);
2110 						f_green += gd_mulfx(f_gs, f_R);
2111 						f_blue  += gd_mulfx(f_bs, f_R);
2112 						f_alpha += gd_mulfx(f_as, f_R);
2113 					}
2114 				}
2115 
2116 				red   = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_red, f_gama)),   0, 255);
2117 				green = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_green, f_gama)), 0, 255);
2118 				blue  = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_blue, f_gama)),  0, 255);
2119 				alpha = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_alpha, f_gama)), 0, 127);
2120 
2121 				dst->tpixels[dst_offset_y][dst_offset_x] =  gdTrueColorAlpha(red, green, blue, alpha);
2122 			} else {
2123 				dst->tpixels[dst_offset_y][dst_offset_x] =  bgColor;
2124 			}
2125 			dst_offset_x++;
2126 		}
2127 
2128 		dst_offset_y++;
2129 	}
2130 	return dst;
2131 }
2132 
gdImageRotateInterpolated(const gdImagePtr src,const float angle,int bgcolor)2133 gdImagePtr gdImageRotateInterpolated(const gdImagePtr src, const float angle, int bgcolor)
2134 {
2135 	/* round to two decimals and keep the 100x multiplication to use it in the common square angles
2136 	   case later. Keep the two decimal precisions so smaller rotation steps can be done, useful for
2137 	   slow animations. */
2138 	const int angle_rounded = fmod((int) floorf(angle * 100), 360 * 100);
2139 
2140 	if (bgcolor < 0) {
2141 		return NULL;
2142 	}
2143 
2144 	/* impact perf a bit, but not that much. Implementation for palette
2145 	   images can be done at a later point.
2146 	*/
2147 	if (src->trueColor == 0) {
2148 		if (bgcolor < gdMaxColors) {
2149 			bgcolor =  gdTrueColorAlpha(src->red[bgcolor], src->green[bgcolor], src->blue[bgcolor], src->alpha[bgcolor]);
2150 		}
2151 		gdImagePaletteToTrueColor(src);
2152 	}
2153 
2154 	/* no interpolation needed here */
2155 	switch (angle_rounded) {
2156 		case    0: {
2157 			gdImagePtr dst = gdImageCreateTrueColor(src->sx, src->sy);
2158 			if (dst == NULL) {
2159 				return NULL;
2160 			}
2161 			dst->transparent = src->transparent;
2162 			dst->saveAlphaFlag = 1;
2163 			dst->alphaBlendingFlag = gdEffectReplace;
2164 
2165 			gdImageCopy(dst, src, 0,0,0,0,src->sx,src->sy);
2166 			return dst;
2167 		}
2168 		case -27000:
2169 		case   9000:
2170 			return gdImageRotate90(src, 0);
2171 		case -18000:
2172 		case  18000:
2173 			return gdImageRotate180(src, 0);
2174 		case -9000:
2175 		case 27000:
2176 			return gdImageRotate270(src, 0);
2177 	}
2178 
2179 	if (src == NULL || src->interpolation_id < 1 || src->interpolation_id > GD_METHOD_COUNT) {
2180 		return NULL;
2181 	}
2182 
2183 	switch (src->interpolation_id) {
2184 		case GD_NEAREST_NEIGHBOUR:
2185 			return gdImageRotateNearestNeighbour(src, angle, bgcolor);
2186 			break;
2187 
2188 		case GD_BILINEAR_FIXED:
2189 			return gdImageRotateBilinear(src, angle, bgcolor);
2190 			break;
2191 
2192 		case GD_BICUBIC_FIXED:
2193 			return gdImageRotateBicubicFixed(src, angle, bgcolor);
2194 			break;
2195 
2196 		default:
2197 			return gdImageRotateGeneric(src, angle, bgcolor);
2198 	}
2199 	return NULL;
2200 }
2201 
2202 /**
2203  * Title: Affine transformation
2204  **/
2205 
2206 /**
2207  * Group: Transform
2208  **/
2209 
gdImageClipRectangle(gdImagePtr im,gdRectPtr r)2210  static void gdImageClipRectangle(gdImagePtr im, gdRectPtr r)
2211 {
2212 	int c1x, c1y, c2x, c2y;
2213 	int x1,y1;
2214 
2215 	gdImageGetClip(im, &c1x, &c1y, &c2x, &c2y);
2216 	x1 = r->x + r->width - 1;
2217 	y1 = r->y + r->height - 1;
2218 	r->x = CLAMP(r->x, c1x, c2x);
2219 	r->y = CLAMP(r->y, c1y, c2y);
2220 	r->width = CLAMP(x1, c1x, c2x) - r->x + 1;
2221 	r->height = CLAMP(y1, c1y, c2y) - r->y + 1;
2222 }
2223 
gdDumpRect(const char * msg,gdRectPtr r)2224 void gdDumpRect(const char *msg, gdRectPtr r)
2225 {
2226 	printf("%s (%i, %i) (%i, %i)\n", msg, r->x, r->y, r->width, r->height);
2227 }
2228 
2229 /**
2230  * Function: gdTransformAffineGetImage
2231  *  Applies an affine transformation to a region and return an image
2232  *  containing the complete transformation.
2233  *
2234  * Parameters:
2235  * 	dst - Pointer to a gdImagePtr to store the created image, NULL when
2236  *        the creation or the transformation failed
2237  *  src - Source image
2238  *  src_area - rectangle defining the source region to transform
2239  *  dstY - Y position in the destination image
2240  *  affine - The desired affine transformation
2241  *
2242  * Returns:
2243  *  GD_TRUE if the affine is rectilinear or GD_FALSE
2244  */
gdTransformAffineGetImage(gdImagePtr * dst,const gdImagePtr src,gdRectPtr src_area,const double affine[6])2245 int gdTransformAffineGetImage(gdImagePtr *dst,
2246 		  const gdImagePtr src,
2247 		  gdRectPtr src_area,
2248 		  const double affine[6])
2249 {
2250 	int res;
2251 	double m[6];
2252 	gdRect bbox;
2253 	gdRect area_full;
2254 
2255 	if (src_area == NULL) {
2256 		area_full.x = 0;
2257 		area_full.y = 0;
2258 		area_full.width  = gdImageSX(src);
2259 		area_full.height = gdImageSY(src);
2260 		src_area = &area_full;
2261 	}
2262 
2263 	gdTransformAffineBoundingBox(src_area, affine, &bbox);
2264 
2265 	*dst = gdImageCreateTrueColor(bbox.width, bbox.height);
2266 	if (*dst == NULL) {
2267 		return GD_FALSE;
2268 	}
2269 	(*dst)->saveAlphaFlag = 1;
2270 
2271 	if (!src->trueColor) {
2272 		gdImagePaletteToTrueColor(src);
2273 	}
2274 
2275 	/* Translate to dst origin (0,0) */
2276 	gdAffineTranslate(m, -bbox.x, -bbox.y);
2277 	gdAffineConcat(m, affine, m);
2278 
2279 	gdImageAlphaBlending(*dst, 0);
2280 
2281 	res = gdTransformAffineCopy(*dst,
2282 		  0,0,
2283 		  src,
2284 		  src_area,
2285 		  m);
2286 
2287 	if (res != GD_TRUE) {
2288 		gdImageDestroy(*dst);
2289 		dst = NULL;
2290 		return GD_FALSE;
2291 	} else {
2292 		return GD_TRUE;
2293 	}
2294 }
2295 
2296 /**
2297  * Function: gdTransformAffineCopy
2298  *  Applies an affine transformation to a region and copy the result
2299  *  in a destination to the given position.
2300  *
2301  * Parameters:
2302  * 	dst - Image to draw the transformed image
2303  *  src - Source image
2304  *  dstX - X position in the destination image
2305  *  dstY - Y position in the destination image
2306  *  src_area - Rectangular region to rotate in the src image
2307  *
2308  * Returns:
2309  *  GD_TRUE on success or GD_FALSE on failure
2310  */
gdTransformAffineCopy(gdImagePtr dst,int dst_x,int dst_y,const gdImagePtr src,gdRectPtr src_region,const double affine[6])2311 int gdTransformAffineCopy(gdImagePtr dst,
2312 		  int dst_x, int dst_y,
2313 		  const gdImagePtr src,
2314 		  gdRectPtr src_region,
2315 		  const double affine[6])
2316 {
2317 	int c1x,c1y,c2x,c2y;
2318 	int backclip = 0;
2319 	int backup_clipx1, backup_clipy1, backup_clipx2, backup_clipy2;
2320 	register int x, y, src_offset_x, src_offset_y;
2321 	double inv[6];
2322 	gdPointF pt, src_pt;
2323 	gdRect bbox;
2324 	int end_x, end_y;
2325 	gdInterpolationMethod interpolation_id_bak = src->interpolation_id;
2326 
2327 	/* These methods use special implementations */
2328 	if (src->interpolation_id == GD_BILINEAR_FIXED || src->interpolation_id == GD_BICUBIC_FIXED || src->interpolation_id == GD_NEAREST_NEIGHBOUR) {
2329 		interpolation_id_bak = src->interpolation_id;
2330 
2331 		gdImageSetInterpolationMethod(src, GD_BICUBIC);
2332 	}
2333 
2334 
2335 	gdImageClipRectangle(src, src_region);
2336 
2337 	if (src_region->x > 0 || src_region->y > 0
2338 		|| src_region->width < gdImageSX(src)
2339 		|| src_region->height < gdImageSY(src)) {
2340 		backclip = 1;
2341 
2342 		gdImageGetClip(src, &backup_clipx1, &backup_clipy1,
2343 		&backup_clipx2, &backup_clipy2);
2344 
2345 		gdImageSetClip(src, src_region->x, src_region->y,
2346 			src_region->x + src_region->width - 1,
2347 			src_region->y + src_region->height - 1);
2348 	}
2349 
2350 	if (!gdTransformAffineBoundingBox(src_region, affine, &bbox)) {
2351 		if (backclip) {
2352 			gdImageSetClip(src, backup_clipx1, backup_clipy1,
2353 					backup_clipx2, backup_clipy2);
2354 		}
2355 		gdImageSetInterpolationMethod(src, interpolation_id_bak);
2356 		return GD_FALSE;
2357 	}
2358 
2359 	gdImageGetClip(dst, &c1x, &c1y, &c2x, &c2y);
2360 
2361 	end_x = bbox.width  + abs(bbox.x);
2362 	end_y = bbox.height + abs(bbox.y);
2363 
2364 	/* Get inverse affine to let us work with destination -> source */
2365 	if (gdAffineInvert(inv, affine) == GD_FALSE) {
2366 		gdImageSetInterpolationMethod(src, interpolation_id_bak);
2367 		return GD_FALSE;
2368 	}
2369 
2370 	src_offset_x =  src_region->x;
2371 	src_offset_y =  src_region->y;
2372 
2373 	if (dst->alphaBlendingFlag) {
2374 		for (y = bbox.y; y <= end_y; y++) {
2375 			pt.y = y + 0.5;
2376 			for (x = 0; x <= end_x; x++) {
2377 				pt.x = x + 0.5;
2378 				gdAffineApplyToPointF(&src_pt, &pt, inv);
2379 				gdImageSetPixel(dst, dst_x + x, dst_y + y, getPixelInterpolated(src, src_offset_x + src_pt.x, src_offset_y + src_pt.y, 0));
2380 			}
2381 		}
2382 	} else {
2383 		for (y = 0; y <= end_y; y++) {
2384 			unsigned char *dst_p = NULL;
2385 			int *tdst_p = NULL;
2386 
2387 			pt.y = y + 0.5 + bbox.y;
2388 			if ((dst_y + y) < 0 || ((dst_y + y) > gdImageSY(dst) -1)) {
2389 				continue;
2390 			}
2391 			if (dst->trueColor) {
2392 				tdst_p = dst->tpixels[dst_y + y] + dst_x;
2393 			} else {
2394 				dst_p = dst->pixels[dst_y + y] + dst_x;
2395 			}
2396 
2397 			for (x = 0; x <= end_x; x++) {
2398 				pt.x = x + 0.5 + bbox.x;
2399 				gdAffineApplyToPointF(&src_pt, &pt, inv);
2400 
2401 				if ((dst_x + x) < 0 || (dst_x + x) > (gdImageSX(dst) - 1)) {
2402 					break;
2403 				}
2404 				if (dst->trueColor) {
2405 					*(tdst_p++) = getPixelInterpolated(src, src_offset_x + src_pt.x, src_offset_y + src_pt.y, -1);
2406 				} else {
2407 					*(dst_p++) = getPixelInterpolated(src, src_offset_x + src_pt.x, src_offset_y + src_pt.y, -1);
2408 				}
2409 			}
2410 		}
2411 	}
2412 
2413 	/* Restore clip if required */
2414 	if (backclip) {
2415 		gdImageSetClip(src, backup_clipx1, backup_clipy1,
2416 				backup_clipx2, backup_clipy2);
2417 	}
2418 
2419 	gdImageSetInterpolationMethod(src, interpolation_id_bak);
2420 	return GD_TRUE;
2421 }
2422 
2423 /**
2424  * Function: gdTransformAffineBoundingBox
2425  *  Returns the bounding box of an affine transformation applied to a
2426  *  rectangular area <gdRect>
2427  *
2428  * Parameters:
2429  * 	src - Rectangular source area for the affine transformation
2430  *  affine - the affine transformation
2431  *  bbox - the resulting bounding box
2432  *
2433  * Returns:
2434  *  GD_TRUE if the affine is rectilinear or GD_FALSE
2435  */
gdTransformAffineBoundingBox(gdRectPtr src,const double affine[6],gdRectPtr bbox)2436 int gdTransformAffineBoundingBox(gdRectPtr src, const double affine[6], gdRectPtr bbox)
2437 {
2438 	gdPointF extent[4], min, max, point;
2439 	int i;
2440 
2441 	extent[0].x=0.0;
2442 	extent[0].y=0.0;
2443 	extent[1].x=(double) src->width;
2444 	extent[1].y=0.0;
2445 	extent[2].x=(double) src->width;
2446 	extent[2].y=(double) src->height;
2447 	extent[3].x=0.0;
2448 	extent[3].y=(double) src->height;
2449 
2450 	for (i=0; i < 4; i++) {
2451 		point=extent[i];
2452 		if (gdAffineApplyToPointF(&extent[i], &point, affine) != GD_TRUE) {
2453 			return GD_FALSE;
2454 		}
2455 	}
2456 	min=extent[0];
2457 	max=extent[0];
2458 
2459 	for (i=1; i < 4; i++) {
2460 		if (min.x > extent[i].x)
2461 			min.x=extent[i].x;
2462 		if (min.y > extent[i].y)
2463 			min.y=extent[i].y;
2464 		if (max.x < extent[i].x)
2465 			max.x=extent[i].x;
2466 		if (max.y < extent[i].y)
2467 			max.y=extent[i].y;
2468 	}
2469 	bbox->x = (int) min.x;
2470 	bbox->y = (int) min.y;
2471 	bbox->width  = (int) floor(max.x - min.x) - 1;
2472 	bbox->height = (int) floor(max.y - min.y);
2473 	return GD_TRUE;
2474 }
2475 
gdImageSetInterpolationMethod(gdImagePtr im,gdInterpolationMethod id)2476 int gdImageSetInterpolationMethod(gdImagePtr im, gdInterpolationMethod id)
2477 {
2478 	if (im == NULL || id < 0 || id > GD_METHOD_COUNT) {
2479 		return 0;
2480 	}
2481 
2482 	switch (id) {
2483 		case GD_DEFAULT:
2484 			id = GD_BILINEAR_FIXED;
2485 			ZEND_FALLTHROUGH;
2486 		/* Optimized versions */
2487 		case GD_BILINEAR_FIXED:
2488 		case GD_BICUBIC_FIXED:
2489 		case GD_NEAREST_NEIGHBOUR:
2490 		case GD_WEIGHTED4:
2491 			im->interpolation = NULL;
2492 			break;
2493 
2494 		/* generic versions*/
2495 		case GD_BELL:
2496 			im->interpolation = filter_bell;
2497 			break;
2498 		case GD_BESSEL:
2499 			im->interpolation = filter_bessel;
2500 			break;
2501 		case GD_BICUBIC:
2502 			im->interpolation = filter_bicubic;
2503 			break;
2504 		case GD_BLACKMAN:
2505 			im->interpolation = filter_blackman;
2506 			break;
2507 		case GD_BOX:
2508 			im->interpolation = filter_box;
2509 			break;
2510 		case GD_BSPLINE:
2511 			im->interpolation = filter_bspline;
2512 			break;
2513 		case GD_CATMULLROM:
2514 			im->interpolation = filter_catmullrom;
2515 			break;
2516 		case GD_GAUSSIAN:
2517 			im->interpolation = filter_gaussian;
2518 			break;
2519 		case GD_GENERALIZED_CUBIC:
2520 			im->interpolation = filter_generalized_cubic;
2521 			break;
2522 		case GD_HERMITE:
2523 			im->interpolation = filter_hermite;
2524 			break;
2525 		case GD_HAMMING:
2526 			im->interpolation = filter_hamming;
2527 			break;
2528 		case GD_HANNING:
2529 			im->interpolation = filter_hanning;
2530 			break;
2531 		case GD_MITCHELL:
2532 			im->interpolation = filter_mitchell;
2533 			break;
2534 		case GD_POWER:
2535 			im->interpolation = filter_power;
2536 			break;
2537 		case GD_QUADRATIC:
2538 			im->interpolation = filter_quadratic;
2539 			break;
2540 		case GD_SINC:
2541 			im->interpolation = filter_sinc;
2542 			break;
2543 		case GD_TRIANGLE:
2544 			im->interpolation = filter_triangle;
2545 			break;
2546 
2547 		default:
2548 			return 0;
2549 			break;
2550 	}
2551 	im->interpolation_id = id;
2552 	return 1;
2553 }
2554 
2555 /**
2556  * Function: gdImageGetInterpolationMethod
2557  *
2558  * Get the current interpolation method
2559  *
2560  * This is here so that the value can be read via a language or VM with an FFI
2561  * but no (portable) way to extract the value from the struct.
2562  *
2563  * Parameters:
2564  *   im - The image.
2565  *
2566  * Returns:
2567  *   The current interpolation method.
2568  *
2569  * See also:
2570  *   - <gdInterpolationMethod>
2571  *   - <gdImageSetInterpolationMethod>
2572  */
gdImageGetInterpolationMethod(gdImagePtr im)2573 gdInterpolationMethod gdImageGetInterpolationMethod(gdImagePtr im)
2574 {
2575     return im->interpolation_id;
2576 }
2577 
2578 #ifdef _MSC_VER
2579 # pragma optimize("", on)
2580 #endif
2581