xref: /PHP-7.2/ext/gd/libgd/gd_interpolation.c (revision 6b4cdbaa)
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 	int overflow_error = 0;
878 
879 	if (scale_d < 1.0) {
880 		width_d = filter_width_d / scale_d;
881 		scale_f_d = scale_d;
882 	}  else {
883 		width_d= filter_width_d;
884 	}
885 
886 	windows_size = 2 * (int)ceil(width_d) + 1;
887 	res = _gdContributionsAlloc(line_size, windows_size);
888 	if (res == NULL) {
889 		return NULL;
890 	}
891 	for (u = 0; u < line_size; u++) {
892 	const double dCenter = (double)u / scale_d;
893 	/* get the significant edge points affecting the pixel */
894 	register int iLeft = MAX(0, (int)floor (dCenter - width_d));
895 	int iRight = MIN((int)ceil(dCenter + width_d), (int)src_size - 1);
896 	double dTotalWeight = 0.0;
897 		int iSrc;
898 
899 	/* Cut edge points to fit in filter window in case of spill-off */
900 	if (iRight - iLeft + 1 > windows_size)  {
901 		if (iLeft < ((int)src_size - 1 / 2))  {
902 			iLeft++;
903 		} else {
904 			iRight--;
905 		}
906 	}
907 
908 	res->ContribRow[u].Left = iLeft;
909 	res->ContribRow[u].Right = iRight;
910 
911 	for (iSrc = iLeft; iSrc <= iRight; iSrc++) {
912 		dTotalWeight += (res->ContribRow[u].Weights[iSrc-iLeft] =  scale_f_d * (*pFilter)(scale_f_d * (dCenter - (double)iSrc)));
913 	}
914 
915 		if (dTotalWeight < 0.0) {
916 			_gdContributionsFree(res);
917 			return NULL;
918 		}
919 
920 	if (dTotalWeight > 0.0) {
921 		for (iSrc = iLeft; iSrc <= iRight; iSrc++) {
922 			res->ContribRow[u].Weights[iSrc-iLeft] /= dTotalWeight;
923 		}
924 	}
925 	}
926 	return res;
927 }
928 
_gdScaleRow(gdImagePtr pSrc,unsigned int src_width,gdImagePtr dst,unsigned int dst_width,unsigned int row,LineContribType * contrib)929 static inline void _gdScaleRow(gdImagePtr pSrc,  unsigned int src_width, gdImagePtr dst, unsigned int dst_width, unsigned int row, LineContribType *contrib)
930 {
931     int *p_src_row = pSrc->tpixels[row];
932     int *p_dst_row = dst->tpixels[row];
933 	unsigned int x;
934 
935     for (x = 0; x < dst_width - 1; x++) {
936 		register unsigned char r = 0, g = 0, b = 0, a = 0;
937         const int left = contrib->ContribRow[x].Left;
938         const int right = contrib->ContribRow[x].Right;
939 		int i;
940 
941 		/* Accumulate each channel */
942         for (i = left; i <= right; i++) {
943 			const int left_channel = i - left;
944             r += (unsigned char)(contrib->ContribRow[x].Weights[left_channel] * (double)(gdTrueColorGetRed(p_src_row[i])));
945             g += (unsigned char)(contrib->ContribRow[x].Weights[left_channel] * (double)(gdTrueColorGetGreen(p_src_row[i])));
946             b += (unsigned char)(contrib->ContribRow[x].Weights[left_channel] * (double)(gdTrueColorGetBlue(p_src_row[i])));
947 			a += (unsigned char)(contrib->ContribRow[x].Weights[left_channel] * (double)(gdTrueColorGetAlpha(p_src_row[i])));
948         }
949         p_dst_row[x] = gdTrueColorAlpha(r, g, b, a);
950     }
951 }
952 
_gdScaleHoriz(gdImagePtr pSrc,unsigned int src_width,unsigned int src_height,gdImagePtr pDst,unsigned int dst_width,unsigned int dst_height)953 static inline int _gdScaleHoriz(gdImagePtr pSrc, unsigned int src_width, unsigned int src_height, gdImagePtr pDst,  unsigned int dst_width, unsigned int dst_height)
954 {
955 	unsigned int u;
956 	LineContribType * contrib;
957 
958 	/* same width, just copy it */
959 	if (dst_width == src_width) {
960 		unsigned int y;
961 		for (y = 0; y < src_height - 1; ++y) {
962 			memcpy(pDst->tpixels[y], pSrc->tpixels[y], src_width);
963 		}
964 	}
965 
966 	contrib = _gdContributionsCalc(dst_width, src_width, (double)dst_width / (double)src_width, pSrc->interpolation);
967 	if (contrib == NULL) {
968 		return 0;
969 	}
970 	/* Scale each row */
971 	for (u = 0; u < dst_height - 1; u++) {
972 		_gdScaleRow(pSrc, src_width, pDst, dst_width, u, contrib);
973 	}
974 	_gdContributionsFree (contrib);
975 	return 1;
976 }
977 
_gdScaleCol(gdImagePtr pSrc,unsigned int src_width,gdImagePtr pRes,unsigned int dst_width,unsigned int dst_height,unsigned int uCol,LineContribType * contrib)978 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)
979 {
980 	unsigned int y;
981 	for (y = 0; y < dst_height - 1; y++) {
982 		register unsigned char r = 0, g = 0, b = 0, a = 0;
983 		const int iLeft = contrib->ContribRow[y].Left;
984 		const int iRight = contrib->ContribRow[y].Right;
985 		int i;
986 		int *row = pRes->tpixels[y];
987 
988 		/* Accumulate each channel */
989 		for (i = iLeft; i <= iRight; i++) {
990 			const int pCurSrc = pSrc->tpixels[i][uCol];
991 			const int i_iLeft = i - iLeft;
992 			r += (unsigned char)(contrib->ContribRow[y].Weights[i_iLeft] * (double)(gdTrueColorGetRed(pCurSrc)));
993 			g += (unsigned char)(contrib->ContribRow[y].Weights[i_iLeft] * (double)(gdTrueColorGetGreen(pCurSrc)));
994 			b += (unsigned char)(contrib->ContribRow[y].Weights[i_iLeft] * (double)(gdTrueColorGetBlue(pCurSrc)));
995 			a += (unsigned char)(contrib->ContribRow[y].Weights[i_iLeft] * (double)(gdTrueColorGetAlpha(pCurSrc)));
996 		}
997 		pRes->tpixels[y][uCol] = gdTrueColorAlpha(r, g, b, a);
998 	}
999 }
1000 
_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)1001 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)
1002 {
1003 	unsigned int u;
1004 	LineContribType * contrib;
1005 
1006 	/* same height, copy it */
1007 	if (src_height == dst_height) {
1008 		unsigned int y;
1009 		for (y = 0; y < src_height - 1; ++y) {
1010 			memcpy(pDst->tpixels[y], pSrc->tpixels[y], src_width);
1011 		}
1012 	}
1013 
1014 	contrib = _gdContributionsCalc(dst_height, src_height, (double)(dst_height) / (double)(src_height), pSrc->interpolation);
1015 	if (contrib == NULL) {
1016 		return 0;
1017 	}
1018 	/* scale each column */
1019 	for (u = 0; u < dst_width - 1; u++) {
1020 		_gdScaleCol(pSrc, src_width, pDst, dst_width, dst_height, u, contrib);
1021 	}
1022 	_gdContributionsFree(contrib);
1023 	return 1;
1024 }
1025 
gdImageScaleTwoPass(const gdImagePtr src,const unsigned int src_width,const unsigned int src_height,const unsigned int new_width,const unsigned int new_height)1026 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)
1027 {
1028 	gdImagePtr tmp_im;
1029 	gdImagePtr dst;
1030 	int scale_pass_res;
1031 
1032 	if (new_width == 0 || new_height == 0) {
1033 		return NULL;
1034 	}
1035 
1036 	/* Convert to truecolor if it isn't; this code requires it. */
1037 	if (!src->trueColor) {
1038 		gdImagePaletteToTrueColor(src);
1039 	}
1040 
1041 	tmp_im = gdImageCreateTrueColor(new_width, src_height);
1042 	if (tmp_im == NULL) {
1043 		return NULL;
1044 	}
1045 	gdImageSetInterpolationMethod(tmp_im, src->interpolation_id);
1046 	scale_pass_res = _gdScaleHoriz(src, src_width, src_height, tmp_im, new_width, src_height);
1047 	if (scale_pass_res != 1) {
1048 		gdImageDestroy(tmp_im);
1049 		return NULL;
1050 	}
1051 
1052 	dst = gdImageCreateTrueColor(new_width, new_height);
1053 	if (dst == NULL) {
1054 		gdImageDestroy(tmp_im);
1055 		return NULL;
1056 	}
1057 	gdImageSetInterpolationMethod(dst, src->interpolation_id);
1058 	scale_pass_res = _gdScaleVert(tmp_im, new_width, src_height, dst, new_width, new_height);
1059 	if (scale_pass_res != 1) {
1060 		gdImageDestroy(dst);
1061 		gdImageDestroy(tmp_im);
1062 		return NULL;
1063 	}
1064 	gdImageDestroy(tmp_im);
1065 
1066 	return dst;
1067 }
1068 
1069 /*
1070 	BilinearFixed, BicubicFixed and nearest implementations are rewamped versions of the implementation in CBitmapEx
1071 	http://www.codeproject.com/Articles/29121/CBitmapEx-Free-C-Bitmap-Manipulation-Class
1072 	Integer only implementation, good to have for common usages like pre scale very large
1073 	images before using another interpolation methods for the last step.
1074 */
gdImageScaleNearestNeighbour(gdImagePtr im,const unsigned int width,const unsigned int height)1075 gdImagePtr gdImageScaleNearestNeighbour(gdImagePtr im, const unsigned int width, const unsigned int height)
1076 {
1077 	const unsigned long new_width = MAX(1, width);
1078 	const unsigned long new_height = MAX(1, height);
1079 	const float dx = (float)im->sx / (float)new_width;
1080 	const float dy = (float)im->sy / (float)new_height;
1081 	const gdFixed f_dx = gd_ftofx(dx);
1082 	const gdFixed f_dy = gd_ftofx(dy);
1083 
1084 	gdImagePtr dst_img;
1085 	unsigned long  dst_offset_x;
1086 	unsigned long  dst_offset_y = 0;
1087 	unsigned int i;
1088 
1089 	if (new_width == 0 || new_height == 0) {
1090 		return NULL;
1091 	}
1092 
1093 	dst_img = gdImageCreateTrueColor(new_width, new_height);
1094 
1095 	if (dst_img == NULL) {
1096 		return NULL;
1097 	}
1098 
1099 	for (i=0; i<new_height; i++) {
1100 		unsigned int j;
1101 		dst_offset_x = 0;
1102 		if (im->trueColor) {
1103 			for (j=0; j<new_width; j++) {
1104 				const gdFixed f_i = gd_itofx(i);
1105 				const gdFixed f_j = gd_itofx(j);
1106 				const gdFixed f_a = gd_mulfx(f_i, f_dy);
1107 				const gdFixed f_b = gd_mulfx(f_j, f_dx);
1108 				const long m = gd_fxtoi(f_a);
1109 				const long n = gd_fxtoi(f_b);
1110 
1111 				dst_img->tpixels[dst_offset_y][dst_offset_x++] = im->tpixels[m][n];
1112 			}
1113 		} else {
1114 			for (j=0; j<new_width; j++) {
1115 				const gdFixed f_i = gd_itofx(i);
1116 				const gdFixed f_j = gd_itofx(j);
1117 				const gdFixed f_a = gd_mulfx(f_i, f_dy);
1118 				const gdFixed f_b = gd_mulfx(f_j, f_dx);
1119 				const long m = gd_fxtoi(f_a);
1120 				const long n = gd_fxtoi(f_b);
1121 
1122 				dst_img->tpixels[dst_offset_y][dst_offset_x++] = colorIndex2RGBA(im->pixels[m][n]);
1123 			}
1124 		}
1125 		dst_offset_y++;
1126 	}
1127 	return dst_img;
1128 }
1129 
getPixelOverflowColorTC(gdImagePtr im,const int x,const int y,const int color)1130 static inline int getPixelOverflowColorTC(gdImagePtr im, const int x, const int y, const int color)
1131 {
1132 	if (gdImageBoundsSafe(im, x, y)) {
1133 		const int c = im->tpixels[y][x];
1134 		if (c == im->transparent) {
1135 			return gdTrueColorAlpha(0, 0, 0, 127);
1136 		}
1137 		return c;
1138 	} else {
1139 		register int border = 0;
1140 		if (y < im->cy1) {
1141 			border = im->tpixels[0][im->cx1];
1142 			goto processborder;
1143 		}
1144 
1145 		if (y < im->cy1) {
1146 			border = im->tpixels[0][im->cx1];
1147 			goto processborder;
1148 		}
1149 
1150 		if (y > im->cy2) {
1151 			if (x >= im->cx1 && x <= im->cx1) {
1152 				border = im->tpixels[im->cy2][x];
1153 				goto processborder;
1154 			} else {
1155 				return gdTrueColorAlpha(0, 0, 0, 127);
1156 			}
1157 		}
1158 
1159 		/* y is bound safe at this point */
1160 		if (x < im->cx1) {
1161 			border = im->tpixels[y][im->cx1];
1162 			goto processborder;
1163 		}
1164 
1165 		if (x > im->cx2) {
1166 			border = im->tpixels[y][im->cx2];
1167 		}
1168 
1169 processborder:
1170 		if (border == im->transparent) {
1171 			return gdTrueColorAlpha(0, 0, 0, 127);
1172 		} else{
1173 			return gdTrueColorAlpha(gdTrueColorGetRed(border), gdTrueColorGetGreen(border), gdTrueColorGetBlue(border), 127);
1174 		}
1175 	}
1176 }
1177 
gdImageScaleBilinearPalette(gdImagePtr im,const unsigned int new_width,const unsigned int new_height)1178 static gdImagePtr gdImageScaleBilinearPalette(gdImagePtr im, const unsigned int new_width, const unsigned int new_height)
1179 {
1180 	long _width = MAX(1, new_width);
1181 	long _height = MAX(1, new_height);
1182 	float dx = (float)gdImageSX(im) / (float)_width;
1183 	float dy = (float)gdImageSY(im) / (float)_height;
1184 	gdFixed f_dx = gd_ftofx(dx);
1185 	gdFixed f_dy = gd_ftofx(dy);
1186 	gdFixed f_1 = gd_itofx(1);
1187 
1188 	int dst_offset_h;
1189 	int dst_offset_v = 0;
1190 	long i;
1191 	gdImagePtr new_img;
1192 	const int transparent = im->transparent;
1193 
1194 	if (new_width == 0 || new_height == 0) {
1195 		return NULL;
1196 	}
1197 
1198 	new_img = gdImageCreateTrueColor(new_width, new_height);
1199 	if (new_img == NULL) {
1200 		return NULL;
1201 	}
1202 
1203 	if (transparent < 0) {
1204 		/* uninitialized */
1205 		new_img->transparent = -1;
1206 	} else {
1207 		new_img->transparent = gdTrueColorAlpha(im->red[transparent], im->green[transparent], im->blue[transparent], im->alpha[transparent]);
1208 	}
1209 
1210 	for (i=0; i < _height; i++) {
1211 		long j;
1212 		const gdFixed f_i = gd_itofx(i);
1213 		const gdFixed f_a = gd_mulfx(f_i, f_dy);
1214 		register long m = gd_fxtoi(f_a);
1215 
1216 		dst_offset_h = 0;
1217 
1218 		for (j=0; j < _width; j++) {
1219 			/* Update bitmap */
1220 			gdFixed f_j = gd_itofx(j);
1221 			gdFixed f_b = gd_mulfx(f_j, f_dx);
1222 
1223 			const long n = gd_fxtoi(f_b);
1224 			gdFixed f_f = f_a - gd_itofx(m);
1225 			gdFixed f_g = f_b - gd_itofx(n);
1226 
1227 			const gdFixed f_w1 = gd_mulfx(f_1-f_f, f_1-f_g);
1228 			const gdFixed f_w2 = gd_mulfx(f_1-f_f, f_g);
1229 			const gdFixed f_w3 = gd_mulfx(f_f, f_1-f_g);
1230 			const gdFixed f_w4 = gd_mulfx(f_f, f_g);
1231 			unsigned int pixel1;
1232 			unsigned int pixel2;
1233 			unsigned int pixel3;
1234 			unsigned int pixel4;
1235 			register gdFixed f_r1, f_r2, f_r3, f_r4,
1236 					f_g1, f_g2, f_g3, f_g4,
1237 					f_b1, f_b2, f_b3, f_b4,
1238 					f_a1, f_a2, f_a3, f_a4;
1239 
1240 			/* 0 for bgColor; (n,m) is supposed to be valid anyway */
1241 			pixel1 = getPixelOverflowPalette(im, n, m, 0);
1242 			pixel2 = getPixelOverflowPalette(im, n + 1, m, pixel1);
1243 			pixel3 = getPixelOverflowPalette(im, n, m + 1, pixel1);
1244 			pixel4 = getPixelOverflowPalette(im, n + 1, m + 1, pixel1);
1245 
1246 			f_r1 = gd_itofx(gdTrueColorGetRed(pixel1));
1247 			f_r2 = gd_itofx(gdTrueColorGetRed(pixel2));
1248 			f_r3 = gd_itofx(gdTrueColorGetRed(pixel3));
1249 			f_r4 = gd_itofx(gdTrueColorGetRed(pixel4));
1250 			f_g1 = gd_itofx(gdTrueColorGetGreen(pixel1));
1251 			f_g2 = gd_itofx(gdTrueColorGetGreen(pixel2));
1252 			f_g3 = gd_itofx(gdTrueColorGetGreen(pixel3));
1253 			f_g4 = gd_itofx(gdTrueColorGetGreen(pixel4));
1254 			f_b1 = gd_itofx(gdTrueColorGetBlue(pixel1));
1255 			f_b2 = gd_itofx(gdTrueColorGetBlue(pixel2));
1256 			f_b3 = gd_itofx(gdTrueColorGetBlue(pixel3));
1257 			f_b4 = gd_itofx(gdTrueColorGetBlue(pixel4));
1258 			f_a1 = gd_itofx(gdTrueColorGetAlpha(pixel1));
1259 			f_a2 = gd_itofx(gdTrueColorGetAlpha(pixel2));
1260 			f_a3 = gd_itofx(gdTrueColorGetAlpha(pixel3));
1261 			f_a4 = gd_itofx(gdTrueColorGetAlpha(pixel4));
1262 
1263 			{
1264 				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));
1265 				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));
1266 				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));
1267 				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));
1268 
1269 				new_img->tpixels[dst_offset_v][dst_offset_h] = gdTrueColorAlpha(red, green, blue, alpha);
1270 			}
1271 
1272 			dst_offset_h++;
1273 		}
1274 
1275 		dst_offset_v++;
1276 	}
1277 	return new_img;
1278 }
1279 
gdImageScaleBilinearTC(gdImagePtr im,const unsigned int new_width,const unsigned int new_height)1280 static gdImagePtr gdImageScaleBilinearTC(gdImagePtr im, const unsigned int new_width, const unsigned int new_height)
1281 {
1282 	long dst_w = MAX(1, new_width);
1283 	long dst_h = MAX(1, new_height);
1284 	float dx = (float)gdImageSX(im) / (float)dst_w;
1285 	float dy = (float)gdImageSY(im) / (float)dst_h;
1286 	gdFixed f_dx = gd_ftofx(dx);
1287 	gdFixed f_dy = gd_ftofx(dy);
1288 	gdFixed f_1 = gd_itofx(1);
1289 
1290 	int dst_offset_h;
1291 	int dst_offset_v = 0;
1292 	int dwSrcTotalOffset;
1293 	long i;
1294 	gdImagePtr new_img;
1295 
1296 	if (new_width == 0 || new_height == 0) {
1297 		return NULL;
1298 	}
1299 
1300 	new_img = gdImageCreateTrueColor(new_width, new_height);
1301 	if (!new_img){
1302 		return NULL;
1303 	}
1304 
1305 	for (i=0; i < dst_h; i++) {
1306 		long j;
1307 		dst_offset_h = 0;
1308 		for (j=0; j < dst_w; j++) {
1309 			/* Update bitmap */
1310 			gdFixed f_i = gd_itofx(i);
1311 			gdFixed f_j = gd_itofx(j);
1312 			gdFixed f_a = gd_mulfx(f_i, f_dy);
1313 			gdFixed f_b = gd_mulfx(f_j, f_dx);
1314 			const gdFixed m = gd_fxtoi(f_a);
1315 			const gdFixed n = gd_fxtoi(f_b);
1316 			gdFixed f_f = f_a - gd_itofx(m);
1317 			gdFixed f_g = f_b - gd_itofx(n);
1318 
1319 			const gdFixed f_w1 = gd_mulfx(f_1-f_f, f_1-f_g);
1320 			const gdFixed f_w2 = gd_mulfx(f_1-f_f, f_g);
1321 			const gdFixed f_w3 = gd_mulfx(f_f, f_1-f_g);
1322 			const gdFixed f_w4 = gd_mulfx(f_f, f_g);
1323 			unsigned int pixel1;
1324 			unsigned int pixel2;
1325 			unsigned int pixel3;
1326 			unsigned int pixel4;
1327 			register gdFixed f_r1, f_r2, f_r3, f_r4,
1328 					f_g1, f_g2, f_g3, f_g4,
1329 					f_b1, f_b2, f_b3, f_b4,
1330 					f_a1, f_a2, f_a3, f_a4;
1331 			dwSrcTotalOffset = m + n;
1332 			/* 0 for bgColor; (n,m) is supposed to be valid anyway */
1333 			pixel1 = getPixelOverflowTC(im, n, m, 0);
1334 			pixel2 = getPixelOverflowTC(im, n + 1, m, pixel1);
1335 			pixel3 = getPixelOverflowTC(im, n, m + 1, pixel1);
1336 			pixel4 = getPixelOverflowTC(im, n + 1, m + 1, pixel1);
1337 
1338 			f_r1 = gd_itofx(gdTrueColorGetRed(pixel1));
1339 			f_r2 = gd_itofx(gdTrueColorGetRed(pixel2));
1340 			f_r3 = gd_itofx(gdTrueColorGetRed(pixel3));
1341 			f_r4 = gd_itofx(gdTrueColorGetRed(pixel4));
1342 			f_g1 = gd_itofx(gdTrueColorGetGreen(pixel1));
1343 			f_g2 = gd_itofx(gdTrueColorGetGreen(pixel2));
1344 			f_g3 = gd_itofx(gdTrueColorGetGreen(pixel3));
1345 			f_g4 = gd_itofx(gdTrueColorGetGreen(pixel4));
1346 			f_b1 = gd_itofx(gdTrueColorGetBlue(pixel1));
1347 			f_b2 = gd_itofx(gdTrueColorGetBlue(pixel2));
1348 			f_b3 = gd_itofx(gdTrueColorGetBlue(pixel3));
1349 			f_b4 = gd_itofx(gdTrueColorGetBlue(pixel4));
1350 			f_a1 = gd_itofx(gdTrueColorGetAlpha(pixel1));
1351 			f_a2 = gd_itofx(gdTrueColorGetAlpha(pixel2));
1352 			f_a3 = gd_itofx(gdTrueColorGetAlpha(pixel3));
1353 			f_a4 = gd_itofx(gdTrueColorGetAlpha(pixel4));
1354 			{
1355 				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));
1356 				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));
1357 				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));
1358 				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));
1359 
1360 				new_img->tpixels[dst_offset_v][dst_offset_h] = gdTrueColorAlpha(red, green, blue, alpha);
1361 			}
1362 
1363 			dst_offset_h++;
1364 		}
1365 
1366 		dst_offset_v++;
1367 	}
1368 	return new_img;
1369 }
1370 
gdImageScaleBilinear(gdImagePtr im,const unsigned int new_width,const unsigned int new_height)1371 gdImagePtr gdImageScaleBilinear(gdImagePtr im, const unsigned int new_width, const unsigned int new_height)
1372 {
1373 	if (im->trueColor) {
1374 		return gdImageScaleBilinearTC(im, new_width, new_height);
1375 	} else {
1376 		return gdImageScaleBilinearPalette(im, new_width, new_height);
1377 	}
1378 }
1379 
gdImageScaleBicubicFixed(gdImagePtr src,const unsigned int width,const unsigned int height)1380 gdImagePtr gdImageScaleBicubicFixed(gdImagePtr src, const unsigned int width, const unsigned int height)
1381 {
1382 	const long new_width = MAX(1, width);
1383 	const long new_height = MAX(1, height);
1384 	const int src_w = gdImageSX(src);
1385 	const int src_h = gdImageSY(src);
1386 	const gdFixed f_dx = gd_ftofx((float)src_w / (float)new_width);
1387 	const gdFixed f_dy = gd_ftofx((float)src_h / (float)new_height);
1388 	const gdFixed f_1 = gd_itofx(1);
1389 	const gdFixed f_2 = gd_itofx(2);
1390 	const gdFixed f_4 = gd_itofx(4);
1391 	const gdFixed f_6 = gd_itofx(6);
1392 	const gdFixed f_gamma = gd_ftofx(1.04f);
1393 	gdImagePtr dst;
1394 
1395 	unsigned int dst_offset_x;
1396 	unsigned int dst_offset_y = 0;
1397 	long i;
1398 
1399 	if (new_width == 0 || new_height == 0) {
1400 		return NULL;
1401 	}
1402 
1403 	/* impact perf a bit, but not that much. Implementation for palette
1404 	   images can be done at a later point.
1405 	*/
1406 	if (src->trueColor == 0) {
1407 		gdImagePaletteToTrueColor(src);
1408 	}
1409 
1410 	dst = gdImageCreateTrueColor(new_width, new_height);
1411 	if (!dst) {
1412 		return NULL;
1413 	}
1414 
1415 	dst->saveAlphaFlag = 1;
1416 
1417 	for (i=0; i < new_height; i++) {
1418 		long j;
1419 		dst_offset_x = 0;
1420 
1421 		for (j=0; j < new_width; j++) {
1422 			const gdFixed f_a = gd_mulfx(gd_itofx(i), f_dy);
1423 			const gdFixed f_b = gd_mulfx(gd_itofx(j), f_dx);
1424 			const long m = gd_fxtoi(f_a);
1425 			const long n = gd_fxtoi(f_b);
1426 			const gdFixed f_f = f_a - gd_itofx(m);
1427 			const gdFixed f_g = f_b - gd_itofx(n);
1428 			unsigned int src_offset_x[16], src_offset_y[16];
1429 			long k;
1430 			register gdFixed f_red = 0, f_green = 0, f_blue = 0, f_alpha = 0;
1431 			unsigned char red, green, blue, alpha = 0;
1432 			int *dst_row = dst->tpixels[dst_offset_y];
1433 
1434 			if ((m < 1) || (n < 1)) {
1435 				src_offset_x[0] = n;
1436 				src_offset_y[0] = m;
1437 			} else {
1438 				src_offset_x[0] = n - 1;
1439 				src_offset_y[0] = m;
1440 			}
1441 
1442 			src_offset_x[1] = n;
1443 			src_offset_y[1] = m;
1444 
1445 			if ((m < 1) || (n >= src_w - 1)) {
1446 				src_offset_x[2] = n;
1447 				src_offset_y[2] = m;
1448 			} else {
1449 				src_offset_x[2] = n + 1;
1450 				src_offset_y[2] = m;
1451 			}
1452 
1453 			if ((m < 1) || (n >= src_w - 2)) {
1454 				src_offset_x[3] = n;
1455 				src_offset_y[3] = m;
1456 			} else {
1457 				src_offset_x[3] = n + 1 + 1;
1458 				src_offset_y[3] = m;
1459 			}
1460 
1461 			if (n < 1) {
1462 				src_offset_x[4] = n;
1463 				src_offset_y[4] = m;
1464 			} else {
1465 				src_offset_x[4] = n - 1;
1466 				src_offset_y[4] = m;
1467 			}
1468 
1469 			src_offset_x[5] = n;
1470 			src_offset_y[5] = m;
1471 			if (n >= src_w-1) {
1472 				src_offset_x[6] = n;
1473 				src_offset_y[6] = m;
1474 			} else {
1475 				src_offset_x[6] = n + 1;
1476 				src_offset_y[6] = m;
1477 			}
1478 
1479 			if (n >= src_w - 2) {
1480 				src_offset_x[7] = n;
1481 				src_offset_y[7] = m;
1482 			} else {
1483 				src_offset_x[7] = n + 1 + 1;
1484 				src_offset_y[7] = m;
1485 			}
1486 
1487 			if ((m >= src_h - 1) || (n < 1)) {
1488 				src_offset_x[8] = n;
1489 				src_offset_y[8] = m;
1490 			} else {
1491 				src_offset_x[8] = n - 1;
1492 				src_offset_y[8] = m;
1493 			}
1494 
1495 			src_offset_x[9] = n;
1496 			src_offset_y[9] = m;
1497 
1498 			if ((m >= src_h-1) || (n >= src_w-1)) {
1499 				src_offset_x[10] = n;
1500 				src_offset_y[10] = m;
1501 			} else {
1502 				src_offset_x[10] = n + 1;
1503 				src_offset_y[10] = m;
1504 			}
1505 
1506 			if ((m >= src_h - 1) || (n >= src_w - 2)) {
1507 				src_offset_x[11] = n;
1508 				src_offset_y[11] = m;
1509 			} else {
1510 				src_offset_x[11] = n + 1 + 1;
1511 				src_offset_y[11] = m;
1512 			}
1513 
1514 			if ((m >= src_h - 2) || (n < 1)) {
1515 				src_offset_x[12] = n;
1516 				src_offset_y[12] = m;
1517 			} else {
1518 				src_offset_x[12] = n - 1;
1519 				src_offset_y[12] = m;
1520 			}
1521 
1522 			src_offset_x[13] = n;
1523 			src_offset_y[13] = m;
1524 
1525 			if ((m >= src_h - 2) || (n >= src_w - 1)) {
1526 				src_offset_x[14] = n;
1527 				src_offset_y[14] = m;
1528 			} else {
1529 				src_offset_x[14] = n + 1;
1530 				src_offset_y[14] = m;
1531 			}
1532 
1533 			if ((m >= src_h - 2) || (n >= src_w - 2)) {
1534 				src_offset_x[15] = n;
1535 				src_offset_y[15] = m;
1536 			} else {
1537 				src_offset_x[15] = n  + 1 + 1;
1538 				src_offset_y[15] = m;
1539 			}
1540 
1541 			for (k = -1; k < 3; k++) {
1542 				const gdFixed f = gd_itofx(k)-f_f;
1543 				const gdFixed f_fm1 = f - f_1;
1544 				const gdFixed f_fp1 = f + f_1;
1545 				const gdFixed f_fp2 = f + f_2;
1546 				register gdFixed f_a = 0, f_b = 0, f_d = 0, f_c = 0;
1547 				register gdFixed f_RY;
1548 				int l;
1549 
1550 				if (f_fp2 > 0) f_a = gd_mulfx(f_fp2, gd_mulfx(f_fp2,f_fp2));
1551 				if (f_fp1 > 0) f_b = gd_mulfx(f_fp1, gd_mulfx(f_fp1,f_fp1));
1552 				if (f > 0)     f_c = gd_mulfx(f, gd_mulfx(f,f));
1553 				if (f_fm1 > 0) f_d = gd_mulfx(f_fm1, gd_mulfx(f_fm1,f_fm1));
1554 
1555 				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);
1556 
1557 				for (l = -1; l < 3; l++) {
1558 					const gdFixed f = gd_itofx(l) - f_g;
1559 					const gdFixed f_fm1 = f - f_1;
1560 					const gdFixed f_fp1 = f + f_1;
1561 					const gdFixed f_fp2 = f + f_2;
1562 					register gdFixed f_a = 0, f_b = 0, f_c = 0, f_d = 0;
1563 					register gdFixed f_RX, f_R, f_rs, f_gs, f_bs, f_ba;
1564 					register int c;
1565 					const int _k = ((k+1)*4) + (l+1);
1566 
1567 					if (f_fp2 > 0) f_a = gd_mulfx(f_fp2,gd_mulfx(f_fp2,f_fp2));
1568 
1569 					if (f_fp1 > 0) f_b = gd_mulfx(f_fp1,gd_mulfx(f_fp1,f_fp1));
1570 
1571 					if (f > 0) f_c = gd_mulfx(f,gd_mulfx(f,f));
1572 
1573 					if (f_fm1 > 0) f_d = gd_mulfx(f_fm1,gd_mulfx(f_fm1,f_fm1));
1574 
1575 					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);
1576 					f_R = gd_mulfx(f_RY,f_RX);
1577 
1578 					c = src->tpixels[*(src_offset_y + _k)][*(src_offset_x + _k)];
1579 					f_rs = gd_itofx(gdTrueColorGetRed(c));
1580 					f_gs = gd_itofx(gdTrueColorGetGreen(c));
1581 					f_bs = gd_itofx(gdTrueColorGetBlue(c));
1582 					f_ba = gd_itofx(gdTrueColorGetAlpha(c));
1583 
1584 					f_red += gd_mulfx(f_rs,f_R);
1585 					f_green += gd_mulfx(f_gs,f_R);
1586 					f_blue += gd_mulfx(f_bs,f_R);
1587 					f_alpha += gd_mulfx(f_ba,f_R);
1588 				}
1589 			}
1590 
1591 			red    = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_red,   f_gamma)),  0, 255);
1592 			green  = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_green, f_gamma)),  0, 255);
1593 			blue   = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_blue,  f_gamma)),  0, 255);
1594 			alpha  = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_alpha,  f_gamma)), 0, 127);
1595 
1596 			*(dst_row + dst_offset_x) = gdTrueColorAlpha(red, green, blue, alpha);
1597 
1598 			dst_offset_x++;
1599 		}
1600 		dst_offset_y++;
1601 	}
1602 	return dst;
1603 }
1604 
gdImageScale(const gdImagePtr src,const unsigned int new_width,const unsigned int new_height)1605 gdImagePtr gdImageScale(const gdImagePtr src, const unsigned int new_width, const unsigned int new_height)
1606 {
1607 	gdImagePtr im_scaled = NULL;
1608 
1609 	if (src == NULL || src->interpolation_id < 0 || src->interpolation_id > GD_METHOD_COUNT) {
1610 		return NULL;
1611 	}
1612 
1613 	if (new_width == 0 || new_height == 0) {
1614 		return NULL;
1615 	}
1616 
1617 	switch (src->interpolation_id) {
1618 		/*Special cases, optimized implementations */
1619 		case GD_NEAREST_NEIGHBOUR:
1620 			im_scaled = gdImageScaleNearestNeighbour(src, new_width, new_height);
1621 			break;
1622 
1623 		case GD_BILINEAR_FIXED:
1624 			im_scaled = gdImageScaleBilinear(src, new_width, new_height);
1625 			break;
1626 
1627 		case GD_BICUBIC_FIXED:
1628 			im_scaled = gdImageScaleBicubicFixed(src, new_width, new_height);
1629 			break;
1630 
1631 		/* generic */
1632 		default:
1633 			if (src->interpolation == NULL) {
1634 				return NULL;
1635 			}
1636 			im_scaled = gdImageScaleTwoPass(src, src->sx, src->sy, new_width, new_height);
1637 			break;
1638 	}
1639 	return im_scaled;
1640 }
1641 
gdRotatedImageSize(gdImagePtr src,const float angle,gdRectPtr bbox)1642 static int gdRotatedImageSize(gdImagePtr src, const float angle, gdRectPtr bbox)
1643 {
1644     gdRect src_area;
1645     double m[6];
1646 
1647     gdAffineRotate(m, angle);
1648     src_area.x = 0;
1649     src_area.y = 0;
1650     src_area.width = gdImageSX(src);
1651     src_area.height = gdImageSY(src);
1652     if (gdTransformAffineBoundingBox(&src_area, m, bbox) != GD_TRUE) {
1653         return GD_FALSE;
1654     }
1655 
1656     return GD_TRUE;
1657 }
1658 
gdImageRotateNearestNeighbour(gdImagePtr src,const float degrees,const int bgColor)1659 gdImagePtr gdImageRotateNearestNeighbour(gdImagePtr src, const float degrees, const int bgColor)
1660 {
1661 	float _angle = ((float) (-degrees / 180.0f) * (float)M_PI);
1662 	const int src_w  = gdImageSX(src);
1663 	const int src_h = gdImageSY(src);
1664 	const gdFixed f_0_5 = gd_ftofx(0.5f);
1665 	const gdFixed f_H = gd_itofx(src_h/2);
1666 	const gdFixed f_W = gd_itofx(src_w/2);
1667 	const gdFixed f_cos = gd_ftofx(cos(-_angle));
1668 	const gdFixed f_sin = gd_ftofx(sin(-_angle));
1669 
1670 	unsigned int dst_offset_x;
1671 	unsigned int dst_offset_y = 0;
1672 	unsigned int i;
1673 	gdImagePtr dst;
1674 	gdRect bbox;
1675 	int new_height, new_width;
1676 
1677     gdRotatedImageSize(src, degrees, &bbox);
1678     new_width = bbox.width;
1679     new_height = bbox.height;
1680 
1681 	if (new_width == 0 || new_height == 0) {
1682 		return NULL;
1683 	}
1684 
1685 	dst = gdImageCreateTrueColor(new_width, new_height);
1686 	if (!dst) {
1687 		return NULL;
1688 	}
1689 	dst->saveAlphaFlag = 1;
1690 	for (i = 0; i < new_height; i++) {
1691 		unsigned int j;
1692 		dst_offset_x = 0;
1693 		for (j = 0; j < new_width; j++) {
1694 			gdFixed f_i = gd_itofx((int)i - (int)new_height/2);
1695 			gdFixed f_j = gd_itofx((int)j - (int)new_width/2);
1696 			gdFixed f_m = gd_mulfx(f_j,f_sin) + gd_mulfx(f_i,f_cos) + f_0_5 + f_H;
1697 			gdFixed f_n = gd_mulfx(f_j,f_cos) - gd_mulfx(f_i,f_sin) + f_0_5 + f_W;
1698 			long m = gd_fxtoi(f_m);
1699 			long n = gd_fxtoi(f_n);
1700 
1701 			if ((m > 0) && (m < src_h-1) && (n > 0) && (n < src_w-1)) {
1702 				if (dst_offset_y < new_height) {
1703 					dst->tpixels[dst_offset_y][dst_offset_x++] = src->tpixels[m][n];
1704 				}
1705 			} else {
1706 				if (dst_offset_y < new_height) {
1707 					dst->tpixels[dst_offset_y][dst_offset_x++] = bgColor;
1708 				}
1709 			}
1710 		}
1711 		dst_offset_y++;
1712 	}
1713 	return dst;
1714 }
1715 
gdImageRotateGeneric(gdImagePtr src,const float degrees,const int bgColor)1716 gdImagePtr gdImageRotateGeneric(gdImagePtr src, const float degrees, const int bgColor)
1717 {
1718 	float _angle = ((float) (-degrees / 180.0f) * (float)M_PI);
1719 	const int angle_rounded = (int)floor(degrees * 100);
1720 	const int src_w  = gdImageSX(src);
1721 	const int src_h = gdImageSY(src);
1722 	const gdFixed f_0_5 = gd_ftofx(0.5f);
1723 	const gdFixed f_H = gd_itofx(src_h/2);
1724 	const gdFixed f_W = gd_itofx(src_w/2);
1725 	const gdFixed f_cos = gd_ftofx(cos(-_angle));
1726 	const gdFixed f_sin = gd_ftofx(sin(-_angle));
1727 
1728 	unsigned int dst_offset_x;
1729 	unsigned int dst_offset_y = 0;
1730 	unsigned int i;
1731 	gdImagePtr dst;
1732 	int new_width, new_height;
1733 	gdRect bbox;
1734 
1735 	const gdFixed f_slop_y = f_sin;
1736 	const gdFixed f_slop_x = f_cos;
1737 	const gdFixed f_slop = f_slop_x > 0 && f_slop_y > 0 ?
1738 							(f_slop_x > f_slop_y ? gd_divfx(f_slop_y, f_slop_x) : gd_divfx(f_slop_x, f_slop_y))
1739 						: 0;
1740 
1741 
1742 	if (bgColor < 0) {
1743 		return NULL;
1744 	}
1745 
1746     gdRotatedImageSize(src, degrees, &bbox);
1747     new_width = bbox.width;
1748     new_height = bbox.height;
1749 
1750 	dst = gdImageCreateTrueColor(new_width, new_height);
1751 	if (!dst) {
1752 		return NULL;
1753 	}
1754 	dst->saveAlphaFlag = 1;
1755 
1756 	for (i = 0; i < new_height; i++) {
1757 		unsigned int j;
1758 		dst_offset_x = 0;
1759 		for (j = 0; j < new_width; j++) {
1760 			gdFixed f_i = gd_itofx((int)i - (int)new_height/ 2);
1761 			gdFixed f_j = gd_itofx((int)j - (int)new_width / 2);
1762 			gdFixed f_m = gd_mulfx(f_j,f_sin) + gd_mulfx(f_i,f_cos) + f_0_5 + f_H;
1763 			gdFixed f_n = gd_mulfx(f_j,f_cos) - gd_mulfx(f_i,f_sin) + f_0_5 + f_W;
1764 			long m = gd_fxtoi(f_m);
1765 			long n = gd_fxtoi(f_n);
1766 
1767 			if ((n <= 0) || (m <= 0) || (m >= src_h) || (n >= src_w)) {
1768 				dst->tpixels[dst_offset_y][dst_offset_x++] = bgColor;
1769 			} else if ((n <= 1) || (m <= 1) || (m >= src_h - 1) || (n >= src_w - 1)) {
1770 				gdFixed f_127 = gd_itofx(127);
1771 				register int c = getPixelInterpolated(src, n, m, bgColor);
1772 				c = c | (( gdTrueColorGetAlpha(c) + ((int)(127* gd_fxtof(f_slop)))) << 24);
1773 
1774 				dst->tpixels[dst_offset_y][dst_offset_x++] = _color_blend(bgColor, c);
1775 			} else {
1776 				dst->tpixels[dst_offset_y][dst_offset_x++] = getPixelInterpolated(src, n, m, bgColor);
1777 			}
1778 		}
1779 		dst_offset_y++;
1780 	}
1781 	return dst;
1782 }
1783 
gdImageRotateBilinear(gdImagePtr src,const float degrees,const int bgColor)1784 gdImagePtr gdImageRotateBilinear(gdImagePtr src, const float degrees, const int bgColor)
1785 {
1786 	float _angle = (float)((- degrees / 180.0f) * M_PI);
1787 	const unsigned int src_w = gdImageSX(src);
1788 	const unsigned int src_h = gdImageSY(src);
1789 	unsigned int new_width, new_height;
1790 	const gdFixed f_0_5 = gd_ftofx(0.5f);
1791 	const gdFixed f_H = gd_itofx(src_h/2);
1792 	const gdFixed f_W = gd_itofx(src_w/2);
1793 	const gdFixed f_cos = gd_ftofx(cos(-_angle));
1794 	const gdFixed f_sin = gd_ftofx(sin(-_angle));
1795 	const gdFixed f_1 = gd_itofx(1);
1796 	unsigned int i;
1797 	unsigned int dst_offset_x;
1798 	unsigned int dst_offset_y = 0;
1799 	unsigned int src_offset_x, src_offset_y;
1800 	gdImagePtr dst;
1801 	gdRect bbox;
1802 
1803 	gdRotatedImageSize(src, degrees, &bbox);
1804 
1805 	new_width = bbox.width;
1806 	new_height = bbox.height;
1807 
1808 	dst = gdImageCreateTrueColor(new_width, new_height);
1809 	if (dst == NULL) {
1810 		return NULL;
1811 	}
1812 	dst->saveAlphaFlag = 1;
1813 
1814 	for (i = 0; i < new_height; i++) {
1815 		unsigned int j;
1816 		dst_offset_x = 0;
1817 
1818 		for (j=0; j < new_width; j++) {
1819 			const gdFixed f_i = gd_itofx((int)i - (int)new_height/2);
1820 			const gdFixed f_j = gd_itofx((int)j - (int)new_width/2);
1821 			const gdFixed f_m = gd_mulfx(f_j,f_sin) + gd_mulfx(f_i,f_cos) + f_0_5 + f_H;
1822 			const gdFixed f_n = gd_mulfx(f_j,f_cos) - gd_mulfx(f_i,f_sin) + f_0_5 + f_W;
1823 			const unsigned int m = gd_fxtoi(f_m);
1824 			const unsigned int n = gd_fxtoi(f_n);
1825 
1826 			if ((m >= 0) && (m < src_h - 1) && (n >= 0) && (n < src_w - 1)) {
1827 				const gdFixed f_f = f_m - gd_itofx(m);
1828 				const gdFixed f_g = f_n - gd_itofx(n);
1829 				const gdFixed f_w1 = gd_mulfx(f_1-f_f, f_1-f_g);
1830 				const gdFixed f_w2 = gd_mulfx(f_1-f_f, f_g);
1831 				const gdFixed f_w3 = gd_mulfx(f_f, f_1-f_g);
1832 				const gdFixed f_w4 = gd_mulfx(f_f, f_g);
1833 
1834 				if (m < src_h-1) {
1835 					src_offset_x = n;
1836 					src_offset_y = m + 1;
1837 				}
1838 
1839 				if (!((n >= src_w-1) || (m >= src_h-1))) {
1840 					src_offset_x = n + 1;
1841 					src_offset_y = m + 1;
1842 				}
1843 				{
1844 					const int pixel1 = src->tpixels[src_offset_y][src_offset_x];
1845 					register int pixel2, pixel3, pixel4;
1846 
1847 					if (src_offset_y + 1 >= src_h) {
1848 						pixel2 = pixel1;
1849 						pixel3 = pixel1;
1850 						pixel4 = pixel1;
1851 					} else if (src_offset_x + 1 >= src_w) {
1852 						pixel2 = pixel1;
1853 						pixel3 = pixel1;
1854 						pixel4 = pixel1;
1855 					} else {
1856 					    pixel2 = src->tpixels[src_offset_y][src_offset_x + 1];
1857 						pixel3 = src->tpixels[src_offset_y + 1][src_offset_x];
1858 						pixel4 = src->tpixels[src_offset_y + 1][src_offset_x + 1];
1859 					}
1860 					{
1861 						const gdFixed f_r1 = gd_itofx(gdTrueColorGetRed(pixel1));
1862 						const gdFixed f_r2 = gd_itofx(gdTrueColorGetRed(pixel2));
1863 						const gdFixed f_r3 = gd_itofx(gdTrueColorGetRed(pixel3));
1864 						const gdFixed f_r4 = gd_itofx(gdTrueColorGetRed(pixel4));
1865 						const gdFixed f_g1 = gd_itofx(gdTrueColorGetGreen(pixel1));
1866 						const gdFixed f_g2 = gd_itofx(gdTrueColorGetGreen(pixel2));
1867 						const gdFixed f_g3 = gd_itofx(gdTrueColorGetGreen(pixel3));
1868 						const gdFixed f_g4 = gd_itofx(gdTrueColorGetGreen(pixel4));
1869 						const gdFixed f_b1 = gd_itofx(gdTrueColorGetBlue(pixel1));
1870 						const gdFixed f_b2 = gd_itofx(gdTrueColorGetBlue(pixel2));
1871 						const gdFixed f_b3 = gd_itofx(gdTrueColorGetBlue(pixel3));
1872 						const gdFixed f_b4 = gd_itofx(gdTrueColorGetBlue(pixel4));
1873 						const gdFixed f_a1 = gd_itofx(gdTrueColorGetAlpha(pixel1));
1874 						const gdFixed f_a2 = gd_itofx(gdTrueColorGetAlpha(pixel2));
1875 						const gdFixed f_a3 = gd_itofx(gdTrueColorGetAlpha(pixel3));
1876 						const gdFixed f_a4 = gd_itofx(gdTrueColorGetAlpha(pixel4));
1877 						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);
1878 						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);
1879 						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);
1880 						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);
1881 
1882 						const unsigned char red   = (unsigned char) CLAMP(gd_fxtoi(f_red),   0, 255);
1883 						const unsigned char green = (unsigned char) CLAMP(gd_fxtoi(f_green), 0, 255);
1884 						const unsigned char blue  = (unsigned char) CLAMP(gd_fxtoi(f_blue),  0, 255);
1885 						const unsigned char alpha = (unsigned char) CLAMP(gd_fxtoi(f_alpha), 0, 127);
1886 
1887 						dst->tpixels[dst_offset_y][dst_offset_x++] = gdTrueColorAlpha(red, green, blue, alpha);
1888 					}
1889 				}
1890 			} else {
1891 				dst->tpixels[dst_offset_y][dst_offset_x++] = bgColor;
1892 			}
1893 		}
1894 		dst_offset_y++;
1895 	}
1896 	return dst;
1897 }
1898 
gdImageRotateBicubicFixed(gdImagePtr src,const float degrees,const int bgColor)1899 gdImagePtr gdImageRotateBicubicFixed(gdImagePtr src, const float degrees, const int bgColor)
1900 {
1901 	const float _angle = (float)((- degrees / 180.0f) * M_PI);
1902 	const int src_w = gdImageSX(src);
1903 	const int src_h = gdImageSY(src);
1904 	unsigned int new_width, new_height;
1905 	const gdFixed f_0_5 = gd_ftofx(0.5f);
1906 	const gdFixed f_H = gd_itofx(src_h/2);
1907 	const gdFixed f_W = gd_itofx(src_w/2);
1908 	const gdFixed f_cos = gd_ftofx(cos(-_angle));
1909 	const gdFixed f_sin = gd_ftofx(sin(-_angle));
1910 	const gdFixed f_1 = gd_itofx(1);
1911 	const gdFixed f_2 = gd_itofx(2);
1912 	const gdFixed f_4 = gd_itofx(4);
1913 	const gdFixed f_6 = gd_itofx(6);
1914 	const gdFixed f_gama = gd_ftofx(1.04f);
1915 
1916 	unsigned int dst_offset_x;
1917 	unsigned int dst_offset_y = 0;
1918 	unsigned int i;
1919 	gdImagePtr dst;
1920 	gdRect bbox;
1921 
1922 	gdRotatedImageSize(src, degrees, &bbox);
1923 	new_width = bbox.width;
1924 	new_height = bbox.height;
1925 	dst = gdImageCreateTrueColor(new_width, new_height);
1926 
1927 	if (dst == NULL) {
1928 		return NULL;
1929 	}
1930 	dst->saveAlphaFlag = 1;
1931 
1932 	for (i=0; i < new_height; i++) {
1933 		unsigned int j;
1934 		dst_offset_x = 0;
1935 
1936 		for (j=0; j < new_width; j++) {
1937 			const gdFixed f_i = gd_itofx((int)i - (int)new_height/2);
1938 			const gdFixed f_j = gd_itofx((int)j - (int)new_width/2);
1939 			const gdFixed f_m = gd_mulfx(f_j,f_sin) + gd_mulfx(f_i,f_cos) + f_0_5 + f_H;
1940 			const gdFixed f_n = gd_mulfx(f_j,f_cos) - gd_mulfx(f_i,f_sin) + f_0_5 + f_W;
1941 			const int m = gd_fxtoi(f_m);
1942 			const int n = gd_fxtoi(f_n);
1943 
1944 			if ((m > 0) && (m < src_h - 1) && (n > 0) && (n < src_w-1)) {
1945 				const gdFixed f_f = f_m - gd_itofx(m);
1946 				const gdFixed f_g = f_n - gd_itofx(n);
1947 				unsigned int src_offset_x[16], src_offset_y[16];
1948 				unsigned char red, green, blue, alpha;
1949 				gdFixed f_red=0, f_green=0, f_blue=0, f_alpha=0;
1950 				int k;
1951 
1952 				if ((m < 1) || (n < 1)) {
1953 					src_offset_x[0] = n;
1954 					src_offset_y[0] = m;
1955 				} else {
1956 					src_offset_x[0] = n - 1;
1957 					src_offset_y[0] = m;
1958 				}
1959 
1960 				src_offset_x[1] = n;
1961 				src_offset_y[1] = m;
1962 
1963 				if ((m < 1) || (n >= src_w-1)) {
1964 					src_offset_x[2] = - 1;
1965 					src_offset_y[2] = - 1;
1966 				} else {
1967 					src_offset_x[2] = n + 1;
1968 					src_offset_y[2] = m ;
1969 				}
1970 
1971 				if ((m < 1) || (n >= src_w-2)) {
1972 					src_offset_x[3] = - 1;
1973 					src_offset_y[3] = - 1;
1974 				} else {
1975 					src_offset_x[3] = n + 1 + 1;
1976 					src_offset_y[3] = m ;
1977 				}
1978 
1979 				if (n < 1) {
1980 					src_offset_x[4] = - 1;
1981 					src_offset_y[4] = - 1;
1982 				} else {
1983 					src_offset_x[4] = n - 1;
1984 					src_offset_y[4] = m;
1985 				}
1986 
1987 				src_offset_x[5] = n;
1988 				src_offset_y[5] = m;
1989 				if (n >= src_w-1) {
1990 					src_offset_x[6] = - 1;
1991 					src_offset_y[6] = - 1;
1992 				} else {
1993 					src_offset_x[6] = n + 1;
1994 					src_offset_y[6] = m;
1995 				}
1996 
1997 				if (n >= src_w-2) {
1998 					src_offset_x[7] = - 1;
1999 					src_offset_y[7] = - 1;
2000 				} else {
2001 					src_offset_x[7] = n + 1 + 1;
2002 					src_offset_y[7] = m;
2003 				}
2004 
2005 				if ((m >= src_h-1) || (n < 1)) {
2006 					src_offset_x[8] = - 1;
2007 					src_offset_y[8] = - 1;
2008 				} else {
2009 					src_offset_x[8] = n - 1;
2010 					src_offset_y[8] = m;
2011 				}
2012 
2013 				if (m >= src_h-1) {
2014 					src_offset_x[9] = - 1;
2015 					src_offset_y[9] = - 1;
2016 				} else {
2017 					src_offset_x[9] = n;
2018 					src_offset_y[9] = m;
2019 				}
2020 
2021 				if ((m >= src_h-1) || (n >= src_w-1)) {
2022 					src_offset_x[10] = - 1;
2023 					src_offset_y[10] = - 1;
2024 				} else {
2025 					src_offset_x[10] = n + 1;
2026 					src_offset_y[10] = m;
2027 				}
2028 
2029 				if ((m >= src_h-1) || (n >= src_w-2)) {
2030 					src_offset_x[11] = - 1;
2031 					src_offset_y[11] = - 1;
2032 				} else {
2033 					src_offset_x[11] = n + 1 + 1;
2034 					src_offset_y[11] = m;
2035 				}
2036 
2037 				if ((m >= src_h-2) || (n < 1)) {
2038 					src_offset_x[12] = - 1;
2039 					src_offset_y[12] = - 1;
2040 				} else {
2041 					src_offset_x[12] = n - 1;
2042 					src_offset_y[12] = m;
2043 				}
2044 
2045 				if (m >= src_h-2) {
2046 					src_offset_x[13] = - 1;
2047 					src_offset_y[13] = - 1;
2048 				} else {
2049 					src_offset_x[13] = n;
2050 					src_offset_y[13] = m;
2051 				}
2052 
2053 				if ((m >= src_h-2) || (n >= src_w - 1)) {
2054 					src_offset_x[14] = - 1;
2055 					src_offset_y[14] = - 1;
2056 				} else {
2057 					src_offset_x[14] = n + 1;
2058 					src_offset_y[14] = m;
2059 				}
2060 
2061 				if ((m >= src_h-2) || (n >= src_w-2)) {
2062 					src_offset_x[15] = - 1;
2063 					src_offset_y[15] = - 1;
2064 				} else {
2065 					src_offset_x[15] = n  + 1 + 1;
2066 					src_offset_y[15] = m;
2067 				}
2068 
2069 				for (k=-1; k<3; k++) {
2070 					const gdFixed f = gd_itofx(k)-f_f;
2071 					const gdFixed f_fm1 = f - f_1;
2072 					const gdFixed f_fp1 = f + f_1;
2073 					const gdFixed f_fp2 = f + f_2;
2074 					gdFixed f_a = 0, f_b = 0,f_c = 0, f_d = 0;
2075 					gdFixed f_RY;
2076 					int l;
2077 
2078 					if (f_fp2 > 0) {
2079 						f_a = gd_mulfx(f_fp2,gd_mulfx(f_fp2,f_fp2));
2080 					}
2081 
2082 					if (f_fp1 > 0) {
2083 						f_b = gd_mulfx(f_fp1,gd_mulfx(f_fp1,f_fp1));
2084 					}
2085 
2086 					if (f > 0) {
2087 						f_c = gd_mulfx(f,gd_mulfx(f,f));
2088 					}
2089 
2090 					if (f_fm1 > 0) {
2091 						f_d = gd_mulfx(f_fm1,gd_mulfx(f_fm1,f_fm1));
2092 					}
2093 					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);
2094 
2095 					for (l=-1;  l< 3; l++) {
2096 						const gdFixed f = gd_itofx(l) - f_g;
2097 						const gdFixed f_fm1 = f - f_1;
2098 						const gdFixed f_fp1 = f + f_1;
2099 						const gdFixed f_fp2 = f + f_2;
2100 						gdFixed f_a = 0, f_b = 0, f_c = 0, f_d = 0;
2101 						gdFixed f_RX, f_R;
2102 						const int _k = ((k + 1) * 4) + (l + 1);
2103 						register gdFixed f_rs, f_gs, f_bs, f_as;
2104 						register int c;
2105 
2106 						if (f_fp2 > 0) {
2107 							f_a = gd_mulfx(f_fp2,gd_mulfx(f_fp2,f_fp2));
2108 						}
2109 
2110 						if (f_fp1 > 0) {
2111 							f_b = gd_mulfx(f_fp1,gd_mulfx(f_fp1,f_fp1));
2112 						}
2113 
2114 						if (f > 0) {
2115 							f_c = gd_mulfx(f,gd_mulfx(f,f));
2116 						}
2117 
2118 						if (f_fm1 > 0) {
2119 							f_d = gd_mulfx(f_fm1,gd_mulfx(f_fm1,f_fm1));
2120 						}
2121 
2122 						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);
2123 						f_R = gd_mulfx(f_RY, f_RX);
2124 
2125 						if ((src_offset_x[_k] <= 0) || (src_offset_y[_k] <= 0) || (src_offset_y[_k] >= src_h) || (src_offset_x[_k] >= src_w)) {
2126 							c = bgColor;
2127 						} 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)) {
2128 							gdFixed f_127 = gd_itofx(127);
2129 							c = src->tpixels[src_offset_y[_k]][src_offset_x[_k]];
2130 							c = c | (( (int) (gd_fxtof(gd_mulfx(f_R, f_127)) + 50.5f)) << 24);
2131 							c = _color_blend(bgColor, c);
2132 						} else {
2133 							c = src->tpixels[src_offset_y[_k]][src_offset_x[_k]];
2134 						}
2135 
2136 						f_rs = gd_itofx(gdTrueColorGetRed(c));
2137 						f_gs = gd_itofx(gdTrueColorGetGreen(c));
2138 						f_bs = gd_itofx(gdTrueColorGetBlue(c));
2139 						f_as = gd_itofx(gdTrueColorGetAlpha(c));
2140 
2141 						f_red   += gd_mulfx(f_rs, f_R);
2142 						f_green += gd_mulfx(f_gs, f_R);
2143 						f_blue  += gd_mulfx(f_bs, f_R);
2144 						f_alpha += gd_mulfx(f_as, f_R);
2145 					}
2146 				}
2147 
2148 				red   = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_red, f_gama)),   0, 255);
2149 				green = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_green, f_gama)), 0, 255);
2150 				blue  = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_blue, f_gama)),  0, 255);
2151 				alpha = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_alpha, f_gama)), 0, 127);
2152 
2153 				dst->tpixels[dst_offset_y][dst_offset_x] =  gdTrueColorAlpha(red, green, blue, alpha);
2154 			} else {
2155 				dst->tpixels[dst_offset_y][dst_offset_x] =  bgColor;
2156 			}
2157 			dst_offset_x++;
2158 		}
2159 
2160 		dst_offset_y++;
2161 	}
2162 	return dst;
2163 }
2164 
gdImageRotateInterpolated(const gdImagePtr src,const float angle,int bgcolor)2165 gdImagePtr gdImageRotateInterpolated(const gdImagePtr src, const float angle, int bgcolor)
2166 {
2167 	/* round to two decimals and keep the 100x multiplication to use it in the common square angles
2168 	   case later. Keep the two decimal precisions so smaller rotation steps can be done, useful for
2169 	   slow animations, f.e. */
2170 	const int angle_rounded = fmod((int) floorf(angle * 100), 360 * 100);
2171 
2172 	if (bgcolor < 0) {
2173 		return NULL;
2174 	}
2175 
2176 	/* impact perf a bit, but not that much. Implementation for palette
2177 	   images can be done at a later point.
2178 	*/
2179 	if (src->trueColor == 0) {
2180 		if (bgcolor < gdMaxColors) {
2181 			bgcolor =  gdTrueColorAlpha(src->red[bgcolor], src->green[bgcolor], src->blue[bgcolor], src->alpha[bgcolor]);
2182 		}
2183 		gdImagePaletteToTrueColor(src);
2184 	}
2185 
2186 	/* no interpolation needed here */
2187 	switch (angle_rounded) {
2188 		case    0: {
2189 			gdImagePtr dst = gdImageCreateTrueColor(src->sx, src->sy);
2190 			if (dst == NULL) {
2191 				return NULL;
2192 			}
2193 			dst->transparent = src->transparent;
2194 			dst->saveAlphaFlag = 1;
2195 			dst->alphaBlendingFlag = gdEffectReplace;
2196 
2197 			gdImageCopy(dst, src, 0,0,0,0,src->sx,src->sy);
2198 			return dst;
2199 		}
2200 		case -27000:
2201 		case   9000:
2202 			return gdImageRotate90(src, 0);
2203 		case -18000:
2204 		case  18000:
2205 			return gdImageRotate180(src, 0);
2206 		case -9000:
2207 		case 27000:
2208 			return gdImageRotate270(src, 0);
2209 	}
2210 
2211 	if (src == NULL || src->interpolation_id < 1 || src->interpolation_id > GD_METHOD_COUNT) {
2212 		return NULL;
2213 	}
2214 
2215 	switch (src->interpolation_id) {
2216 		case GD_NEAREST_NEIGHBOUR:
2217 			return gdImageRotateNearestNeighbour(src, angle, bgcolor);
2218 			break;
2219 
2220 		case GD_BILINEAR_FIXED:
2221 			return gdImageRotateBilinear(src, angle, bgcolor);
2222 			break;
2223 
2224 		case GD_BICUBIC_FIXED:
2225 			return gdImageRotateBicubicFixed(src, angle, bgcolor);
2226 			break;
2227 
2228 		default:
2229 			return gdImageRotateGeneric(src, angle, bgcolor);
2230 	}
2231 	return NULL;
2232 }
2233 
2234 /**
2235  * Title: Affine transformation
2236  **/
2237 
2238 /**
2239  * Group: Transform
2240  **/
2241 
gdImageClipRectangle(gdImagePtr im,gdRectPtr r)2242  static void gdImageClipRectangle(gdImagePtr im, gdRectPtr r)
2243 {
2244 	int c1x, c1y, c2x, c2y;
2245 	int x1,y1;
2246 
2247 	gdImageGetClip(im, &c1x, &c1y, &c2x, &c2y);
2248 	x1 = r->x + r->width - 1;
2249 	y1 = r->y + r->height - 1;
2250 	r->x = CLAMP(r->x, c1x, c2x);
2251 	r->y = CLAMP(r->y, c1y, c2y);
2252 	r->width = CLAMP(x1, c1x, c2x) - r->x + 1;
2253 	r->height = CLAMP(y1, c1y, c2y) - r->y + 1;
2254 }
2255 
gdDumpRect(const char * msg,gdRectPtr r)2256 void gdDumpRect(const char *msg, gdRectPtr r)
2257 {
2258 	printf("%s (%i, %i) (%i, %i)\n", msg, r->x, r->y, r->width, r->height);
2259 }
2260 
2261 /**
2262  * Function: gdTransformAffineGetImage
2263  *  Applies an affine transformation to a region and return an image
2264  *  containing the complete transformation.
2265  *
2266  * Parameters:
2267  * 	dst - Pointer to a gdImagePtr to store the created image, NULL when
2268  *        the creation or the transformation failed
2269  *  src - Source image
2270  *  src_area - rectangle defining the source region to transform
2271  *  dstY - Y position in the destination image
2272  *  affine - The desired affine transformation
2273  *
2274  * Returns:
2275  *  GD_TRUE if the affine is rectilinear or GD_FALSE
2276  */
gdTransformAffineGetImage(gdImagePtr * dst,const gdImagePtr src,gdRectPtr src_area,const double affine[6])2277 int gdTransformAffineGetImage(gdImagePtr *dst,
2278 		  const gdImagePtr src,
2279 		  gdRectPtr src_area,
2280 		  const double affine[6])
2281 {
2282 	int res;
2283 	double m[6];
2284 	gdRect bbox;
2285 	gdRect area_full;
2286 
2287 	if (src_area == NULL) {
2288 		area_full.x = 0;
2289 		area_full.y = 0;
2290 		area_full.width  = gdImageSX(src);
2291 		area_full.height = gdImageSY(src);
2292 		src_area = &area_full;
2293 	}
2294 
2295 	gdTransformAffineBoundingBox(src_area, affine, &bbox);
2296 
2297 	*dst = gdImageCreateTrueColor(bbox.width, bbox.height);
2298 	if (*dst == NULL) {
2299 		return GD_FALSE;
2300 	}
2301 	(*dst)->saveAlphaFlag = 1;
2302 
2303 	if (!src->trueColor) {
2304 		gdImagePaletteToTrueColor(src);
2305 	}
2306 
2307 	/* Translate to dst origin (0,0) */
2308 	gdAffineTranslate(m, -bbox.x, -bbox.y);
2309 	gdAffineConcat(m, affine, m);
2310 
2311 	gdImageAlphaBlending(*dst, 0);
2312 
2313 	res = gdTransformAffineCopy(*dst,
2314 		  0,0,
2315 		  src,
2316 		  src_area,
2317 		  m);
2318 
2319 	if (res != GD_TRUE) {
2320 		gdImageDestroy(*dst);
2321 		dst = NULL;
2322 		return GD_FALSE;
2323 	} else {
2324 		return GD_TRUE;
2325 	}
2326 }
2327 
2328 /**
2329  * Function: gdTransformAffineCopy
2330  *  Applies an affine transformation to a region and copy the result
2331  *  in a destination to the given position.
2332  *
2333  * Parameters:
2334  * 	dst - Image to draw the transformed image
2335  *  src - Source image
2336  *  dstX - X position in the destination image
2337  *  dstY - Y position in the destination image
2338  *  src_area - Rectangular region to rotate in the src image
2339  *
2340  * Returns:
2341  *  GD_TRUE if the affine is rectilinear or GD_FALSE
2342  */
gdTransformAffineCopy(gdImagePtr dst,int dst_x,int dst_y,const gdImagePtr src,gdRectPtr src_region,const double affine[6])2343 int gdTransformAffineCopy(gdImagePtr dst,
2344 		  int dst_x, int dst_y,
2345 		  const gdImagePtr src,
2346 		  gdRectPtr src_region,
2347 		  const double affine[6])
2348 {
2349 	int c1x,c1y,c2x,c2y;
2350 	int backclip = 0;
2351 	int backup_clipx1, backup_clipy1, backup_clipx2, backup_clipy2;
2352 	register int x, y, src_offset_x, src_offset_y;
2353 	double inv[6];
2354 	int *dst_p;
2355 	gdPointF pt, src_pt;
2356 	gdRect bbox;
2357 	int end_x, end_y;
2358 	gdInterpolationMethod interpolation_id_bak = GD_DEFAULT;
2359 	interpolation_method interpolation_bak;
2360 
2361 	/* These methods use special implementations */
2362 	if (src->interpolation_id == GD_BILINEAR_FIXED || src->interpolation_id == GD_BICUBIC_FIXED || src->interpolation_id == GD_NEAREST_NEIGHBOUR) {
2363 		interpolation_id_bak = src->interpolation_id;
2364 		interpolation_bak = src->interpolation;
2365 
2366 		gdImageSetInterpolationMethod(src, GD_BICUBIC);
2367 	}
2368 
2369 
2370 	gdImageClipRectangle(src, src_region);
2371 
2372 	if (src_region->x > 0 || src_region->y > 0
2373 		|| src_region->width < gdImageSX(src)
2374 		|| src_region->height < gdImageSY(src)) {
2375 		backclip = 1;
2376 
2377 		gdImageGetClip(src, &backup_clipx1, &backup_clipy1,
2378 		&backup_clipx2, &backup_clipy2);
2379 
2380 		gdImageSetClip(src, src_region->x, src_region->y,
2381 			src_region->x + src_region->width - 1,
2382 			src_region->y + src_region->height - 1);
2383 	}
2384 
2385 	if (!gdTransformAffineBoundingBox(src_region, affine, &bbox)) {
2386 		if (backclip) {
2387 			gdImageSetClip(src, backup_clipx1, backup_clipy1,
2388 					backup_clipx2, backup_clipy2);
2389 		}
2390 		gdImageSetInterpolationMethod(src, interpolation_id_bak);
2391 		return GD_FALSE;
2392 	}
2393 
2394 	gdImageGetClip(dst, &c1x, &c1y, &c2x, &c2y);
2395 
2396 	end_x = bbox.width  + (int) fabs(bbox.x);
2397 	end_y = bbox.height + (int) fabs(bbox.y);
2398 
2399 	/* Get inverse affine to let us work with destination -> source */
2400 	gdAffineInvert(inv, affine);
2401 
2402 	src_offset_x =  src_region->x;
2403 	src_offset_y =  src_region->y;
2404 
2405 	if (dst->alphaBlendingFlag) {
2406 		for (y = bbox.y; y <= end_y; y++) {
2407 			pt.y = y + 0.5;
2408 			for (x = 0; x <= end_x; x++) {
2409 				pt.x = x + 0.5;
2410 				gdAffineApplyToPointF(&src_pt, &pt, inv);
2411 				gdImageSetPixel(dst, dst_x + x, dst_y + y, getPixelInterpolated(src, src_offset_x + src_pt.x, src_offset_y + src_pt.y, 0));
2412 			}
2413 		}
2414 	} else {
2415 		for (y = 0; y <= end_y; y++) {
2416 			pt.y = y + 0.5 + bbox.y;
2417 			if ((dst_y + y) < 0 || ((dst_y + y) > gdImageSY(dst) -1)) {
2418 				continue;
2419 			}
2420 			dst_p = dst->tpixels[dst_y + y] + dst_x;
2421 
2422 			for (x = 0; x <= end_x; x++) {
2423 				pt.x = x + 0.5 + bbox.x;
2424 				gdAffineApplyToPointF(&src_pt, &pt, inv);
2425 
2426 				if ((dst_x + x) < 0 || (dst_x + x) > (gdImageSX(dst) - 1)) {
2427 					break;
2428 				}
2429 				*(dst_p++) = getPixelInterpolated(src, src_offset_x + src_pt.x, src_offset_y + src_pt.y, -1);
2430 			}
2431 		}
2432 	}
2433 
2434 	/* Restore clip if required */
2435 	if (backclip) {
2436 		gdImageSetClip(src, backup_clipx1, backup_clipy1,
2437 				backup_clipx2, backup_clipy2);
2438 	}
2439 
2440 	gdImageSetInterpolationMethod(src, interpolation_id_bak);
2441 	return GD_TRUE;
2442 }
2443 
2444 /**
2445  * Function: gdTransformAffineBoundingBox
2446  *  Returns the bounding box of an affine transformation applied to a
2447  *  rectangular area <gdRect>
2448  *
2449  * Parameters:
2450  * 	src - Rectangular source area for the affine transformation
2451  *  affine - the affine transformation
2452  *  bbox - the resulting bounding box
2453  *
2454  * Returns:
2455  *  GD_TRUE if the affine is rectilinear or GD_FALSE
2456  */
gdTransformAffineBoundingBox(gdRectPtr src,const double affine[6],gdRectPtr bbox)2457 int gdTransformAffineBoundingBox(gdRectPtr src, const double affine[6], gdRectPtr bbox)
2458 {
2459 	gdPointF extent[4], min, max, point;
2460 	int i;
2461 
2462 	extent[0].x=0.0;
2463 	extent[0].y=0.0;
2464 	extent[1].x=(double) src->width;
2465 	extent[1].y=0.0;
2466 	extent[2].x=(double) src->width;
2467 	extent[2].y=(double) src->height;
2468 	extent[3].x=0.0;
2469 	extent[3].y=(double) src->height;
2470 
2471 	for (i=0; i < 4; i++) {
2472 		point=extent[i];
2473 		if (gdAffineApplyToPointF(&extent[i], &point, affine) != GD_TRUE) {
2474 			return GD_FALSE;
2475 		}
2476 	}
2477 	min=extent[0];
2478 	max=extent[0];
2479 
2480 	for (i=1; i < 4; i++) {
2481 		if (min.x > extent[i].x)
2482 			min.x=extent[i].x;
2483 		if (min.y > extent[i].y)
2484 			min.y=extent[i].y;
2485 		if (max.x < extent[i].x)
2486 			max.x=extent[i].x;
2487 		if (max.y < extent[i].y)
2488 			max.y=extent[i].y;
2489 	}
2490 	bbox->x = (int) min.x;
2491 	bbox->y = (int) min.y;
2492 	bbox->width  = (int) floor(max.x - min.x) - 1;
2493 	bbox->height = (int) floor(max.y - min.y);
2494 	return GD_TRUE;
2495 }
2496 
gdImageSetInterpolationMethod(gdImagePtr im,gdInterpolationMethod id)2497 int gdImageSetInterpolationMethod(gdImagePtr im, gdInterpolationMethod id)
2498 {
2499 	if (im == NULL || id < 0 || id > GD_METHOD_COUNT) {
2500 		return 0;
2501 	}
2502 
2503 	switch (id) {
2504 		case GD_DEFAULT:
2505 			id = GD_BILINEAR_FIXED;
2506 		/* Optimized versions */
2507 		case GD_BILINEAR_FIXED:
2508 		case GD_BICUBIC_FIXED:
2509 		case GD_NEAREST_NEIGHBOUR:
2510 		case GD_WEIGHTED4:
2511 			im->interpolation = NULL;
2512 			break;
2513 
2514 		/* generic versions*/
2515 		case GD_BELL:
2516 			im->interpolation = filter_bell;
2517 			break;
2518 		case GD_BESSEL:
2519 			im->interpolation = filter_bessel;
2520 			break;
2521 		case GD_BICUBIC:
2522 			im->interpolation = filter_bicubic;
2523 			break;
2524 		case GD_BLACKMAN:
2525 			im->interpolation = filter_blackman;
2526 			break;
2527 		case GD_BOX:
2528 			im->interpolation = filter_box;
2529 			break;
2530 		case GD_BSPLINE:
2531 			im->interpolation = filter_bspline;
2532 			break;
2533 		case GD_CATMULLROM:
2534 			im->interpolation = filter_catmullrom;
2535 			break;
2536 		case GD_GAUSSIAN:
2537 			im->interpolation = filter_gaussian;
2538 			break;
2539 		case GD_GENERALIZED_CUBIC:
2540 			im->interpolation = filter_generalized_cubic;
2541 			break;
2542 		case GD_HERMITE:
2543 			im->interpolation = filter_hermite;
2544 			break;
2545 		case GD_HAMMING:
2546 			im->interpolation = filter_hamming;
2547 			break;
2548 		case GD_HANNING:
2549 			im->interpolation = filter_hanning;
2550 			break;
2551 		case GD_MITCHELL:
2552 			im->interpolation = filter_mitchell;
2553 			break;
2554 		case GD_POWER:
2555 			im->interpolation = filter_power;
2556 			break;
2557 		case GD_QUADRATIC:
2558 			im->interpolation = filter_quadratic;
2559 			break;
2560 		case GD_SINC:
2561 			im->interpolation = filter_sinc;
2562 			break;
2563 		case GD_TRIANGLE:
2564 			im->interpolation = filter_triangle;
2565 			break;
2566 
2567 		default:
2568 			return 0;
2569 			break;
2570 	}
2571 	im->interpolation_id = id;
2572 	return 1;
2573 }
2574 
2575 #ifdef _MSC_VER
2576 # pragma optimize("", on)
2577 #endif
2578