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