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