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