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