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