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