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