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