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