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