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