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