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
878 if (scale_d < 1.0) {
879 width_d = filter_width_d / scale_d;
880 scale_f_d = scale_d;
881 } else {
882 width_d= filter_width_d;
883 }
884
885 windows_size = 2 * (int)ceil(width_d) + 1;
886 res = _gdContributionsAlloc(line_size, windows_size);
887 if (res == NULL) {
888 return NULL;
889 }
890 for (u = 0; u < line_size; u++) {
891 const double dCenter = (double)u / scale_d;
892 /* get the significant edge points affecting the pixel */
893 register int iLeft = MAX(0, (int)floor (dCenter - width_d));
894 int iRight = MIN((int)ceil(dCenter + width_d), (int)src_size - 1);
895 double dTotalWeight = 0.0;
896 int iSrc;
897
898 /* Cut edge points to fit in filter window in case of spill-off */
899 if (iRight - iLeft + 1 > windows_size) {
900 if (iLeft < ((int)src_size - 1 / 2)) {
901 iLeft++;
902 } else {
903 iRight--;
904 }
905 }
906
907 res->ContribRow[u].Left = iLeft;
908 res->ContribRow[u].Right = iRight;
909
910 for (iSrc = iLeft; iSrc <= iRight; iSrc++) {
911 dTotalWeight += (res->ContribRow[u].Weights[iSrc-iLeft] = scale_f_d * (*pFilter)(scale_f_d * (dCenter - (double)iSrc)));
912 }
913
914 if (dTotalWeight < 0.0) {
915 _gdContributionsFree(res);
916 return NULL;
917 }
918
919 if (dTotalWeight > 0.0) {
920 for (iSrc = iLeft; iSrc <= iRight; iSrc++) {
921 res->ContribRow[u].Weights[iSrc-iLeft] /= dTotalWeight;
922 }
923 }
924 }
925 return res;
926 }
927
_gdScaleRow(gdImagePtr pSrc,unsigned int src_width,gdImagePtr dst,unsigned int dst_width,unsigned int row,LineContribType * contrib)928 static inline void _gdScaleRow(gdImagePtr pSrc, unsigned int src_width, gdImagePtr dst, unsigned int dst_width, unsigned int row, LineContribType *contrib)
929 {
930 int *p_src_row = pSrc->tpixels[row];
931 int *p_dst_row = dst->tpixels[row];
932 unsigned int x;
933
934 for (x = 0; x < dst_width; x++) {
935 register unsigned char r = 0, g = 0, b = 0, a = 0;
936 const int left = contrib->ContribRow[x].Left;
937 const int right = contrib->ContribRow[x].Right;
938 int i;
939
940 /* Accumulate each channel */
941 for (i = left; i <= right; i++) {
942 const int left_channel = i - left;
943 r += (unsigned char)(contrib->ContribRow[x].Weights[left_channel] * (double)(gdTrueColorGetRed(p_src_row[i])));
944 g += (unsigned char)(contrib->ContribRow[x].Weights[left_channel] * (double)(gdTrueColorGetGreen(p_src_row[i])));
945 b += (unsigned char)(contrib->ContribRow[x].Weights[left_channel] * (double)(gdTrueColorGetBlue(p_src_row[i])));
946 a += (unsigned char)(contrib->ContribRow[x].Weights[left_channel] * (double)(gdTrueColorGetAlpha(p_src_row[i])));
947 }
948 p_dst_row[x] = gdTrueColorAlpha(r, g, b, a);
949 }
950 }
951
_gdScaleHoriz(gdImagePtr pSrc,unsigned int src_width,unsigned int src_height,gdImagePtr pDst,unsigned int dst_width,unsigned int dst_height)952 static inline int _gdScaleHoriz(gdImagePtr pSrc, unsigned int src_width, unsigned int src_height, gdImagePtr pDst, unsigned int dst_width, unsigned int dst_height)
953 {
954 unsigned int u;
955 LineContribType * contrib;
956
957 /* same width, just copy it */
958 if (dst_width == src_width) {
959 unsigned int y;
960 for (y = 0; y < src_height - 1; ++y) {
961 memcpy(pDst->tpixels[y], pSrc->tpixels[y], src_width);
962 }
963 }
964
965 contrib = _gdContributionsCalc(dst_width, src_width, (double)dst_width / (double)src_width, pSrc->interpolation);
966 if (contrib == NULL) {
967 return 0;
968 }
969 /* Scale each row */
970 for (u = 0; u < dst_height; u++) {
971 _gdScaleRow(pSrc, src_width, pDst, dst_width, u, contrib);
972 }
973 _gdContributionsFree (contrib);
974 return 1;
975 }
976
_gdScaleCol(gdImagePtr pSrc,unsigned int src_width,gdImagePtr pRes,unsigned int dst_width,unsigned int dst_height,unsigned int uCol,LineContribType * contrib)977 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)
978 {
979 unsigned int y;
980 for (y = 0; y < dst_height; y++) {
981 register unsigned char r = 0, g = 0, b = 0, a = 0;
982 const int iLeft = contrib->ContribRow[y].Left;
983 const int iRight = contrib->ContribRow[y].Right;
984 int i;
985
986 /* Accumulate each channel */
987 for (i = iLeft; i <= iRight; i++) {
988 const int pCurSrc = pSrc->tpixels[i][uCol];
989 const int i_iLeft = i - iLeft;
990 r += (unsigned char)(contrib->ContribRow[y].Weights[i_iLeft] * (double)(gdTrueColorGetRed(pCurSrc)));
991 g += (unsigned char)(contrib->ContribRow[y].Weights[i_iLeft] * (double)(gdTrueColorGetGreen(pCurSrc)));
992 b += (unsigned char)(contrib->ContribRow[y].Weights[i_iLeft] * (double)(gdTrueColorGetBlue(pCurSrc)));
993 a += (unsigned char)(contrib->ContribRow[y].Weights[i_iLeft] * (double)(gdTrueColorGetAlpha(pCurSrc)));
994 }
995 pRes->tpixels[y][uCol] = gdTrueColorAlpha(r, g, b, a);
996 }
997 }
998
_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)999 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)
1000 {
1001 unsigned int u;
1002 LineContribType * contrib;
1003
1004 /* same height, copy it */
1005 if (src_height == dst_height) {
1006 unsigned int y;
1007 for (y = 0; y < src_height - 1; ++y) {
1008 memcpy(pDst->tpixels[y], pSrc->tpixels[y], src_width);
1009 }
1010 }
1011
1012 contrib = _gdContributionsCalc(dst_height, src_height, (double)(dst_height) / (double)(src_height), pSrc->interpolation);
1013 if (contrib == NULL) {
1014 return 0;
1015 }
1016 /* scale each column */
1017 for (u = 0; u < dst_width; u++) {
1018 _gdScaleCol(pSrc, src_width, pDst, dst_width, dst_height, u, contrib);
1019 }
1020 _gdContributionsFree(contrib);
1021 return 1;
1022 }
1023
gdImageScaleTwoPass(const gdImagePtr src,const unsigned int src_width,const unsigned int src_height,const unsigned int new_width,const unsigned int new_height)1024 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)
1025 {
1026 gdImagePtr tmp_im;
1027 gdImagePtr dst;
1028 int scale_pass_res;
1029
1030 if (new_width == 0 || new_height == 0) {
1031 return NULL;
1032 }
1033
1034 /* Convert to truecolor if it isn't; this code requires it. */
1035 if (!src->trueColor) {
1036 gdImagePaletteToTrueColor(src);
1037 }
1038
1039 tmp_im = gdImageCreateTrueColor(new_width, src_height);
1040 if (tmp_im == NULL) {
1041 return NULL;
1042 }
1043 gdImageSetInterpolationMethod(tmp_im, src->interpolation_id);
1044 scale_pass_res = _gdScaleHoriz(src, src_width, src_height, tmp_im, new_width, src_height);
1045 if (scale_pass_res != 1) {
1046 gdImageDestroy(tmp_im);
1047 return NULL;
1048 }
1049
1050 dst = gdImageCreateTrueColor(new_width, new_height);
1051 if (dst == NULL) {
1052 gdImageDestroy(tmp_im);
1053 return NULL;
1054 }
1055 gdImageSetInterpolationMethod(dst, src->interpolation_id);
1056 scale_pass_res = _gdScaleVert(tmp_im, new_width, src_height, dst, new_width, new_height);
1057 if (scale_pass_res != 1) {
1058 gdImageDestroy(dst);
1059 gdImageDestroy(tmp_im);
1060 return NULL;
1061 }
1062 gdImageDestroy(tmp_im);
1063
1064 return dst;
1065 }
1066
1067 /*
1068 BilinearFixed, BicubicFixed and nearest implementations are rewamped versions of the implementation in CBitmapEx
1069 http://www.codeproject.com/Articles/29121/CBitmapEx-Free-C-Bitmap-Manipulation-Class
1070 Integer only implementation, good to have for common usages like pre scale very large
1071 images before using another interpolation methods for the last step.
1072 */
gdImageScaleNearestNeighbour(gdImagePtr im,const unsigned int width,const unsigned int height)1073 gdImagePtr gdImageScaleNearestNeighbour(gdImagePtr im, const unsigned int width, const unsigned int height)
1074 {
1075 const unsigned long new_width = MAX(1, width);
1076 const unsigned long new_height = MAX(1, height);
1077 const float dx = (float)im->sx / (float)new_width;
1078 const float dy = (float)im->sy / (float)new_height;
1079 const gdFixed f_dx = gd_ftofx(dx);
1080 const gdFixed f_dy = gd_ftofx(dy);
1081
1082 gdImagePtr dst_img;
1083 unsigned long dst_offset_x;
1084 unsigned long dst_offset_y = 0;
1085 unsigned int i;
1086
1087 if (new_width == 0 || new_height == 0) {
1088 return NULL;
1089 }
1090
1091 dst_img = gdImageCreateTrueColor(new_width, new_height);
1092
1093 if (dst_img == NULL) {
1094 return NULL;
1095 }
1096
1097 for (i=0; i<new_height; i++) {
1098 unsigned int j;
1099 dst_offset_x = 0;
1100 if (im->trueColor) {
1101 for (j=0; j<new_width; j++) {
1102 const gdFixed f_i = gd_itofx(i);
1103 const gdFixed f_j = gd_itofx(j);
1104 const gdFixed f_a = gd_mulfx(f_i, f_dy);
1105 const gdFixed f_b = gd_mulfx(f_j, f_dx);
1106 const long m = gd_fxtoi(f_a);
1107 const long n = gd_fxtoi(f_b);
1108
1109 dst_img->tpixels[dst_offset_y][dst_offset_x++] = im->tpixels[m][n];
1110 }
1111 } else {
1112 for (j=0; j<new_width; j++) {
1113 const gdFixed f_i = gd_itofx(i);
1114 const gdFixed f_j = gd_itofx(j);
1115 const gdFixed f_a = gd_mulfx(f_i, f_dy);
1116 const gdFixed f_b = gd_mulfx(f_j, f_dx);
1117 const long m = gd_fxtoi(f_a);
1118 const long n = gd_fxtoi(f_b);
1119
1120 dst_img->tpixels[dst_offset_y][dst_offset_x++] = colorIndex2RGBA(im->pixels[m][n]);
1121 }
1122 }
1123 dst_offset_y++;
1124 }
1125 return dst_img;
1126 }
1127
getPixelOverflowColorTC(gdImagePtr im,const int x,const int y,const int color)1128 static inline int getPixelOverflowColorTC(gdImagePtr im, const int x, const int y, const int color)
1129 {
1130 if (gdImageBoundsSafe(im, x, y)) {
1131 const int c = im->tpixels[y][x];
1132 if (c == im->transparent) {
1133 return gdTrueColorAlpha(0, 0, 0, 127);
1134 }
1135 return c;
1136 } else {
1137 register int border = 0;
1138 if (y < im->cy1) {
1139 border = im->tpixels[0][im->cx1];
1140 goto processborder;
1141 }
1142
1143 if (y < im->cy1) {
1144 border = im->tpixels[0][im->cx1];
1145 goto processborder;
1146 }
1147
1148 if (y > im->cy2) {
1149 if (x >= im->cx1 && x <= im->cx1) {
1150 border = im->tpixels[im->cy2][x];
1151 goto processborder;
1152 } else {
1153 return gdTrueColorAlpha(0, 0, 0, 127);
1154 }
1155 }
1156
1157 /* y is bound safe at this point */
1158 if (x < im->cx1) {
1159 border = im->tpixels[y][im->cx1];
1160 goto processborder;
1161 }
1162
1163 if (x > im->cx2) {
1164 border = im->tpixels[y][im->cx2];
1165 }
1166
1167 processborder:
1168 if (border == im->transparent) {
1169 return gdTrueColorAlpha(0, 0, 0, 127);
1170 } else{
1171 return gdTrueColorAlpha(gdTrueColorGetRed(border), gdTrueColorGetGreen(border), gdTrueColorGetBlue(border), 127);
1172 }
1173 }
1174 }
1175
gdImageScaleBilinearPalette(gdImagePtr im,const unsigned int new_width,const unsigned int new_height)1176 static gdImagePtr gdImageScaleBilinearPalette(gdImagePtr im, const unsigned int new_width, const unsigned int new_height)
1177 {
1178 long _width = MAX(1, new_width);
1179 long _height = MAX(1, new_height);
1180 float dx = (float)gdImageSX(im) / (float)_width;
1181 float dy = (float)gdImageSY(im) / (float)_height;
1182 gdFixed f_dx = gd_ftofx(dx);
1183 gdFixed f_dy = gd_ftofx(dy);
1184 gdFixed f_1 = gd_itofx(1);
1185
1186 int dst_offset_h;
1187 int dst_offset_v = 0;
1188 long i;
1189 gdImagePtr new_img;
1190 const int transparent = im->transparent;
1191
1192 if (new_width == 0 || new_height == 0) {
1193 return NULL;
1194 }
1195
1196 new_img = gdImageCreateTrueColor(new_width, new_height);
1197 if (new_img == NULL) {
1198 return NULL;
1199 }
1200
1201 if (transparent < 0) {
1202 /* uninitialized */
1203 new_img->transparent = -1;
1204 } else {
1205 new_img->transparent = gdTrueColorAlpha(im->red[transparent], im->green[transparent], im->blue[transparent], im->alpha[transparent]);
1206 }
1207
1208 for (i=0; i < _height; i++) {
1209 long j;
1210 const gdFixed f_i = gd_itofx(i);
1211 const gdFixed f_a = gd_mulfx(f_i, f_dy);
1212 register long m = gd_fxtoi(f_a);
1213
1214 dst_offset_h = 0;
1215
1216 for (j=0; j < _width; j++) {
1217 /* Update bitmap */
1218 gdFixed f_j = gd_itofx(j);
1219 gdFixed f_b = gd_mulfx(f_j, f_dx);
1220
1221 const long n = gd_fxtoi(f_b);
1222 gdFixed f_f = f_a - gd_itofx(m);
1223 gdFixed f_g = f_b - gd_itofx(n);
1224
1225 const gdFixed f_w1 = gd_mulfx(f_1-f_f, f_1-f_g);
1226 const gdFixed f_w2 = gd_mulfx(f_1-f_f, f_g);
1227 const gdFixed f_w3 = gd_mulfx(f_f, f_1-f_g);
1228 const gdFixed f_w4 = gd_mulfx(f_f, f_g);
1229 unsigned int pixel1;
1230 unsigned int pixel2;
1231 unsigned int pixel3;
1232 unsigned int pixel4;
1233 register gdFixed f_r1, f_r2, f_r3, f_r4,
1234 f_g1, f_g2, f_g3, f_g4,
1235 f_b1, f_b2, f_b3, f_b4,
1236 f_a1, f_a2, f_a3, f_a4;
1237
1238 /* 0 for bgColor; (n,m) is supposed to be valid anyway */
1239 pixel1 = getPixelOverflowPalette(im, n, m, 0);
1240 pixel2 = getPixelOverflowPalette(im, n + 1, m, pixel1);
1241 pixel3 = getPixelOverflowPalette(im, n, m + 1, pixel1);
1242 pixel4 = getPixelOverflowPalette(im, n + 1, m + 1, pixel1);
1243
1244 f_r1 = gd_itofx(gdTrueColorGetRed(pixel1));
1245 f_r2 = gd_itofx(gdTrueColorGetRed(pixel2));
1246 f_r3 = gd_itofx(gdTrueColorGetRed(pixel3));
1247 f_r4 = gd_itofx(gdTrueColorGetRed(pixel4));
1248 f_g1 = gd_itofx(gdTrueColorGetGreen(pixel1));
1249 f_g2 = gd_itofx(gdTrueColorGetGreen(pixel2));
1250 f_g3 = gd_itofx(gdTrueColorGetGreen(pixel3));
1251 f_g4 = gd_itofx(gdTrueColorGetGreen(pixel4));
1252 f_b1 = gd_itofx(gdTrueColorGetBlue(pixel1));
1253 f_b2 = gd_itofx(gdTrueColorGetBlue(pixel2));
1254 f_b3 = gd_itofx(gdTrueColorGetBlue(pixel3));
1255 f_b4 = gd_itofx(gdTrueColorGetBlue(pixel4));
1256 f_a1 = gd_itofx(gdTrueColorGetAlpha(pixel1));
1257 f_a2 = gd_itofx(gdTrueColorGetAlpha(pixel2));
1258 f_a3 = gd_itofx(gdTrueColorGetAlpha(pixel3));
1259 f_a4 = gd_itofx(gdTrueColorGetAlpha(pixel4));
1260
1261 {
1262 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));
1263 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));
1264 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));
1265 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));
1266
1267 new_img->tpixels[dst_offset_v][dst_offset_h] = gdTrueColorAlpha(red, green, blue, alpha);
1268 }
1269
1270 dst_offset_h++;
1271 }
1272
1273 dst_offset_v++;
1274 }
1275 return new_img;
1276 }
1277
gdImageScaleBilinearTC(gdImagePtr im,const unsigned int new_width,const unsigned int new_height)1278 static gdImagePtr gdImageScaleBilinearTC(gdImagePtr im, const unsigned int new_width, const unsigned int new_height)
1279 {
1280 long dst_w = MAX(1, new_width);
1281 long dst_h = MAX(1, new_height);
1282 float dx = (float)gdImageSX(im) / (float)dst_w;
1283 float dy = (float)gdImageSY(im) / (float)dst_h;
1284 gdFixed f_dx = gd_ftofx(dx);
1285 gdFixed f_dy = gd_ftofx(dy);
1286 gdFixed f_1 = gd_itofx(1);
1287
1288 int dst_offset_h;
1289 int dst_offset_v = 0;
1290 int dwSrcTotalOffset;
1291 long i;
1292 gdImagePtr new_img;
1293
1294 if (new_width == 0 || new_height == 0) {
1295 return NULL;
1296 }
1297
1298 new_img = gdImageCreateTrueColor(new_width, new_height);
1299 if (!new_img){
1300 return NULL;
1301 }
1302
1303 for (i=0; i < dst_h; i++) {
1304 long j;
1305 dst_offset_h = 0;
1306 for (j=0; j < dst_w; j++) {
1307 /* Update bitmap */
1308 gdFixed f_i = gd_itofx(i);
1309 gdFixed f_j = gd_itofx(j);
1310 gdFixed f_a = gd_mulfx(f_i, f_dy);
1311 gdFixed f_b = gd_mulfx(f_j, f_dx);
1312 const gdFixed m = gd_fxtoi(f_a);
1313 const gdFixed n = gd_fxtoi(f_b);
1314 gdFixed f_f = f_a - gd_itofx(m);
1315 gdFixed f_g = f_b - gd_itofx(n);
1316
1317 const gdFixed f_w1 = gd_mulfx(f_1-f_f, f_1-f_g);
1318 const gdFixed f_w2 = gd_mulfx(f_1-f_f, f_g);
1319 const gdFixed f_w3 = gd_mulfx(f_f, f_1-f_g);
1320 const gdFixed f_w4 = gd_mulfx(f_f, f_g);
1321 unsigned int pixel1;
1322 unsigned int pixel2;
1323 unsigned int pixel3;
1324 unsigned int pixel4;
1325 register gdFixed f_r1, f_r2, f_r3, f_r4,
1326 f_g1, f_g2, f_g3, f_g4,
1327 f_b1, f_b2, f_b3, f_b4,
1328 f_a1, f_a2, f_a3, f_a4;
1329 dwSrcTotalOffset = m + n;
1330 /* 0 for bgColor; (n,m) is supposed to be valid anyway */
1331 pixel1 = getPixelOverflowTC(im, n, m, 0);
1332 pixel2 = getPixelOverflowTC(im, n + 1, m, pixel1);
1333 pixel3 = getPixelOverflowTC(im, n, m + 1, pixel1);
1334 pixel4 = getPixelOverflowTC(im, n + 1, m + 1, pixel1);
1335
1336 f_r1 = gd_itofx(gdTrueColorGetRed(pixel1));
1337 f_r2 = gd_itofx(gdTrueColorGetRed(pixel2));
1338 f_r3 = gd_itofx(gdTrueColorGetRed(pixel3));
1339 f_r4 = gd_itofx(gdTrueColorGetRed(pixel4));
1340 f_g1 = gd_itofx(gdTrueColorGetGreen(pixel1));
1341 f_g2 = gd_itofx(gdTrueColorGetGreen(pixel2));
1342 f_g3 = gd_itofx(gdTrueColorGetGreen(pixel3));
1343 f_g4 = gd_itofx(gdTrueColorGetGreen(pixel4));
1344 f_b1 = gd_itofx(gdTrueColorGetBlue(pixel1));
1345 f_b2 = gd_itofx(gdTrueColorGetBlue(pixel2));
1346 f_b3 = gd_itofx(gdTrueColorGetBlue(pixel3));
1347 f_b4 = gd_itofx(gdTrueColorGetBlue(pixel4));
1348 f_a1 = gd_itofx(gdTrueColorGetAlpha(pixel1));
1349 f_a2 = gd_itofx(gdTrueColorGetAlpha(pixel2));
1350 f_a3 = gd_itofx(gdTrueColorGetAlpha(pixel3));
1351 f_a4 = gd_itofx(gdTrueColorGetAlpha(pixel4));
1352 {
1353 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));
1354 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));
1355 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));
1356 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));
1357
1358 new_img->tpixels[dst_offset_v][dst_offset_h] = gdTrueColorAlpha(red, green, blue, alpha);
1359 }
1360
1361 dst_offset_h++;
1362 }
1363
1364 dst_offset_v++;
1365 }
1366 return new_img;
1367 }
1368
gdImageScaleBilinear(gdImagePtr im,const unsigned int new_width,const unsigned int new_height)1369 gdImagePtr gdImageScaleBilinear(gdImagePtr im, const unsigned int new_width, const unsigned int new_height)
1370 {
1371 if (im->trueColor) {
1372 return gdImageScaleBilinearTC(im, new_width, new_height);
1373 } else {
1374 return gdImageScaleBilinearPalette(im, new_width, new_height);
1375 }
1376 }
1377
gdImageScaleBicubicFixed(gdImagePtr src,const unsigned int width,const unsigned int height)1378 gdImagePtr gdImageScaleBicubicFixed(gdImagePtr src, const unsigned int width, const unsigned int height)
1379 {
1380 const long new_width = MAX(1, width);
1381 const long new_height = MAX(1, height);
1382 const int src_w = gdImageSX(src);
1383 const int src_h = gdImageSY(src);
1384 const gdFixed f_dx = gd_ftofx((float)src_w / (float)new_width);
1385 const gdFixed f_dy = gd_ftofx((float)src_h / (float)new_height);
1386 const gdFixed f_1 = gd_itofx(1);
1387 const gdFixed f_2 = gd_itofx(2);
1388 const gdFixed f_4 = gd_itofx(4);
1389 const gdFixed f_6 = gd_itofx(6);
1390 const gdFixed f_gamma = gd_ftofx(1.04f);
1391 gdImagePtr dst;
1392
1393 unsigned int dst_offset_x;
1394 unsigned int dst_offset_y = 0;
1395 long i;
1396
1397 if (new_width == 0 || new_height == 0) {
1398 return NULL;
1399 }
1400
1401 /* impact perf a bit, but not that much. Implementation for palette
1402 images can be done at a later point.
1403 */
1404 if (src->trueColor == 0) {
1405 gdImagePaletteToTrueColor(src);
1406 }
1407
1408 dst = gdImageCreateTrueColor(new_width, new_height);
1409 if (!dst) {
1410 return NULL;
1411 }
1412
1413 dst->saveAlphaFlag = 1;
1414
1415 for (i=0; i < new_height; i++) {
1416 long j;
1417 dst_offset_x = 0;
1418
1419 for (j=0; j < new_width; j++) {
1420 const gdFixed f_a = gd_mulfx(gd_itofx(i), f_dy);
1421 const gdFixed f_b = gd_mulfx(gd_itofx(j), f_dx);
1422 const long m = gd_fxtoi(f_a);
1423 const long n = gd_fxtoi(f_b);
1424 const gdFixed f_f = f_a - gd_itofx(m);
1425 const gdFixed f_g = f_b - gd_itofx(n);
1426 unsigned int src_offset_x[16], src_offset_y[16];
1427 long k;
1428 register gdFixed f_red = 0, f_green = 0, f_blue = 0, f_alpha = 0;
1429 unsigned char red, green, blue, alpha = 0;
1430 int *dst_row = dst->tpixels[dst_offset_y];
1431
1432 if ((m < 1) || (n < 1)) {
1433 src_offset_x[0] = n;
1434 src_offset_y[0] = m;
1435 } else {
1436 src_offset_x[0] = n - 1;
1437 src_offset_y[0] = m;
1438 }
1439
1440 src_offset_x[1] = n;
1441 src_offset_y[1] = m;
1442
1443 if ((m < 1) || (n >= src_w - 1)) {
1444 src_offset_x[2] = n;
1445 src_offset_y[2] = m;
1446 } else {
1447 src_offset_x[2] = n + 1;
1448 src_offset_y[2] = m;
1449 }
1450
1451 if ((m < 1) || (n >= src_w - 2)) {
1452 src_offset_x[3] = n;
1453 src_offset_y[3] = m;
1454 } else {
1455 src_offset_x[3] = n + 1 + 1;
1456 src_offset_y[3] = m;
1457 }
1458
1459 if (n < 1) {
1460 src_offset_x[4] = n;
1461 src_offset_y[4] = m;
1462 } else {
1463 src_offset_x[4] = n - 1;
1464 src_offset_y[4] = m;
1465 }
1466
1467 src_offset_x[5] = n;
1468 src_offset_y[5] = m;
1469 if (n >= src_w-1) {
1470 src_offset_x[6] = n;
1471 src_offset_y[6] = m;
1472 } else {
1473 src_offset_x[6] = n + 1;
1474 src_offset_y[6] = m;
1475 }
1476
1477 if (n >= src_w - 2) {
1478 src_offset_x[7] = n;
1479 src_offset_y[7] = m;
1480 } else {
1481 src_offset_x[7] = n + 1 + 1;
1482 src_offset_y[7] = m;
1483 }
1484
1485 if ((m >= src_h - 1) || (n < 1)) {
1486 src_offset_x[8] = n;
1487 src_offset_y[8] = m;
1488 } else {
1489 src_offset_x[8] = n - 1;
1490 src_offset_y[8] = m;
1491 }
1492
1493 src_offset_x[9] = n;
1494 src_offset_y[9] = m;
1495
1496 if ((m >= src_h-1) || (n >= src_w-1)) {
1497 src_offset_x[10] = n;
1498 src_offset_y[10] = m;
1499 } else {
1500 src_offset_x[10] = n + 1;
1501 src_offset_y[10] = m;
1502 }
1503
1504 if ((m >= src_h - 1) || (n >= src_w - 2)) {
1505 src_offset_x[11] = n;
1506 src_offset_y[11] = m;
1507 } else {
1508 src_offset_x[11] = n + 1 + 1;
1509 src_offset_y[11] = m;
1510 }
1511
1512 if ((m >= src_h - 2) || (n < 1)) {
1513 src_offset_x[12] = n;
1514 src_offset_y[12] = m;
1515 } else {
1516 src_offset_x[12] = n - 1;
1517 src_offset_y[12] = m;
1518 }
1519
1520 src_offset_x[13] = n;
1521 src_offset_y[13] = m;
1522
1523 if ((m >= src_h - 2) || (n >= src_w - 1)) {
1524 src_offset_x[14] = n;
1525 src_offset_y[14] = m;
1526 } else {
1527 src_offset_x[14] = n + 1;
1528 src_offset_y[14] = m;
1529 }
1530
1531 if ((m >= src_h - 2) || (n >= src_w - 2)) {
1532 src_offset_x[15] = n;
1533 src_offset_y[15] = m;
1534 } else {
1535 src_offset_x[15] = n + 1 + 1;
1536 src_offset_y[15] = m;
1537 }
1538
1539 for (k = -1; k < 3; k++) {
1540 const gdFixed f = gd_itofx(k)-f_f;
1541 const gdFixed f_fm1 = f - f_1;
1542 const gdFixed f_fp1 = f + f_1;
1543 const gdFixed f_fp2 = f + f_2;
1544 register gdFixed f_a = 0, f_b = 0, f_d = 0, f_c = 0;
1545 register gdFixed f_RY;
1546 int l;
1547
1548 if (f_fp2 > 0) f_a = gd_mulfx(f_fp2, gd_mulfx(f_fp2,f_fp2));
1549 if (f_fp1 > 0) f_b = gd_mulfx(f_fp1, gd_mulfx(f_fp1,f_fp1));
1550 if (f > 0) f_c = gd_mulfx(f, gd_mulfx(f,f));
1551 if (f_fm1 > 0) f_d = gd_mulfx(f_fm1, gd_mulfx(f_fm1,f_fm1));
1552
1553 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);
1554
1555 for (l = -1; l < 3; l++) {
1556 const gdFixed f = gd_itofx(l) - f_g;
1557 const gdFixed f_fm1 = f - f_1;
1558 const gdFixed f_fp1 = f + f_1;
1559 const gdFixed f_fp2 = f + f_2;
1560 register gdFixed f_a = 0, f_b = 0, f_c = 0, f_d = 0;
1561 register gdFixed f_RX, f_R, f_rs, f_gs, f_bs, f_ba;
1562 register int c;
1563 const int _k = ((k+1)*4) + (l+1);
1564
1565 if (f_fp2 > 0) f_a = gd_mulfx(f_fp2,gd_mulfx(f_fp2,f_fp2));
1566
1567 if (f_fp1 > 0) f_b = gd_mulfx(f_fp1,gd_mulfx(f_fp1,f_fp1));
1568
1569 if (f > 0) f_c = gd_mulfx(f,gd_mulfx(f,f));
1570
1571 if (f_fm1 > 0) f_d = gd_mulfx(f_fm1,gd_mulfx(f_fm1,f_fm1));
1572
1573 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);
1574 f_R = gd_mulfx(f_RY,f_RX);
1575
1576 c = src->tpixels[*(src_offset_y + _k)][*(src_offset_x + _k)];
1577 f_rs = gd_itofx(gdTrueColorGetRed(c));
1578 f_gs = gd_itofx(gdTrueColorGetGreen(c));
1579 f_bs = gd_itofx(gdTrueColorGetBlue(c));
1580 f_ba = gd_itofx(gdTrueColorGetAlpha(c));
1581
1582 f_red += gd_mulfx(f_rs,f_R);
1583 f_green += gd_mulfx(f_gs,f_R);
1584 f_blue += gd_mulfx(f_bs,f_R);
1585 f_alpha += gd_mulfx(f_ba,f_R);
1586 }
1587 }
1588
1589 red = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_red, f_gamma)), 0, 255);
1590 green = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_green, f_gamma)), 0, 255);
1591 blue = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_blue, f_gamma)), 0, 255);
1592 alpha = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_alpha, f_gamma)), 0, 127);
1593
1594 *(dst_row + dst_offset_x) = gdTrueColorAlpha(red, green, blue, alpha);
1595
1596 dst_offset_x++;
1597 }
1598 dst_offset_y++;
1599 }
1600 return dst;
1601 }
1602
gdImageScale(const gdImagePtr src,const unsigned int new_width,const unsigned int new_height)1603 gdImagePtr gdImageScale(const gdImagePtr src, const unsigned int new_width, const unsigned int new_height)
1604 {
1605 gdImagePtr im_scaled = NULL;
1606
1607 if (src == NULL || src->interpolation_id < 0 || src->interpolation_id > GD_METHOD_COUNT) {
1608 return NULL;
1609 }
1610
1611 if (new_width == 0 || new_height == 0) {
1612 return NULL;
1613 }
1614
1615 switch (src->interpolation_id) {
1616 /*Special cases, optimized implementations */
1617 case GD_NEAREST_NEIGHBOUR:
1618 im_scaled = gdImageScaleNearestNeighbour(src, new_width, new_height);
1619 break;
1620
1621 case GD_BILINEAR_FIXED:
1622 im_scaled = gdImageScaleBilinear(src, new_width, new_height);
1623 break;
1624
1625 case GD_BICUBIC_FIXED:
1626 im_scaled = gdImageScaleBicubicFixed(src, new_width, new_height);
1627 break;
1628
1629 /* generic */
1630 default:
1631 if (src->interpolation == NULL) {
1632 return NULL;
1633 }
1634 im_scaled = gdImageScaleTwoPass(src, src->sx, src->sy, new_width, new_height);
1635 break;
1636 }
1637 return im_scaled;
1638 }
1639
gdRotatedImageSize(gdImagePtr src,const float angle,gdRectPtr bbox)1640 static int gdRotatedImageSize(gdImagePtr src, const float angle, gdRectPtr bbox)
1641 {
1642 gdRect src_area;
1643 double m[6];
1644
1645 gdAffineRotate(m, angle);
1646 src_area.x = 0;
1647 src_area.y = 0;
1648 src_area.width = gdImageSX(src);
1649 src_area.height = gdImageSY(src);
1650 if (gdTransformAffineBoundingBox(&src_area, m, bbox) != GD_TRUE) {
1651 return GD_FALSE;
1652 }
1653
1654 return GD_TRUE;
1655 }
1656
gdImageRotateNearestNeighbour(gdImagePtr src,const float degrees,const int bgColor)1657 gdImagePtr gdImageRotateNearestNeighbour(gdImagePtr src, const float degrees, const int bgColor)
1658 {
1659 float _angle = ((float) (-degrees / 180.0f) * (float)M_PI);
1660 const int src_w = gdImageSX(src);
1661 const int src_h = gdImageSY(src);
1662 const gdFixed f_0_5 = gd_ftofx(0.5f);
1663 const gdFixed f_H = gd_itofx(src_h/2);
1664 const gdFixed f_W = gd_itofx(src_w/2);
1665 const gdFixed f_cos = gd_ftofx(cos(-_angle));
1666 const gdFixed f_sin = gd_ftofx(sin(-_angle));
1667
1668 unsigned int dst_offset_x;
1669 unsigned int dst_offset_y = 0;
1670 unsigned int i;
1671 gdImagePtr dst;
1672 gdRect bbox;
1673 int new_height, new_width;
1674
1675 gdRotatedImageSize(src, degrees, &bbox);
1676 new_width = bbox.width;
1677 new_height = bbox.height;
1678
1679 if (new_width == 0 || new_height == 0) {
1680 return NULL;
1681 }
1682
1683 dst = gdImageCreateTrueColor(new_width, new_height);
1684 if (!dst) {
1685 return NULL;
1686 }
1687 dst->saveAlphaFlag = 1;
1688 for (i = 0; i < new_height; i++) {
1689 unsigned int j;
1690 dst_offset_x = 0;
1691 for (j = 0; j < new_width; j++) {
1692 gdFixed f_i = gd_itofx((int)i - (int)new_height/2);
1693 gdFixed f_j = gd_itofx((int)j - (int)new_width/2);
1694 gdFixed f_m = gd_mulfx(f_j,f_sin) + gd_mulfx(f_i,f_cos) + f_0_5 + f_H;
1695 gdFixed f_n = gd_mulfx(f_j,f_cos) - gd_mulfx(f_i,f_sin) + f_0_5 + f_W;
1696 long m = gd_fxtoi(f_m);
1697 long n = gd_fxtoi(f_n);
1698
1699 if ((m > 0) && (m < src_h-1) && (n > 0) && (n < src_w-1)) {
1700 if (dst_offset_y < new_height) {
1701 dst->tpixels[dst_offset_y][dst_offset_x++] = src->tpixels[m][n];
1702 }
1703 } else {
1704 if (dst_offset_y < new_height) {
1705 dst->tpixels[dst_offset_y][dst_offset_x++] = bgColor;
1706 }
1707 }
1708 }
1709 dst_offset_y++;
1710 }
1711 return dst;
1712 }
1713
gdImageRotateGeneric(gdImagePtr src,const float degrees,const int bgColor)1714 gdImagePtr gdImageRotateGeneric(gdImagePtr src, const float degrees, const int bgColor)
1715 {
1716 float _angle = ((float) (-degrees / 180.0f) * (float)M_PI);
1717 const int src_w = gdImageSX(src);
1718 const int src_h = gdImageSY(src);
1719 const gdFixed f_0_5 = gd_ftofx(0.5f);
1720 const gdFixed f_H = gd_itofx(src_h/2);
1721 const gdFixed f_W = gd_itofx(src_w/2);
1722 const gdFixed f_cos = gd_ftofx(cos(-_angle));
1723 const gdFixed f_sin = gd_ftofx(sin(-_angle));
1724
1725 unsigned int dst_offset_x;
1726 unsigned int dst_offset_y = 0;
1727 unsigned int i;
1728 gdImagePtr dst;
1729 int new_width, new_height;
1730 gdRect bbox;
1731
1732 const gdFixed f_slop_y = f_sin;
1733 const gdFixed f_slop_x = f_cos;
1734 const gdFixed f_slop = f_slop_x > 0 && f_slop_y > 0 ?
1735 (f_slop_x > f_slop_y ? gd_divfx(f_slop_y, f_slop_x) : gd_divfx(f_slop_x, f_slop_y))
1736 : 0;
1737
1738
1739 if (bgColor < 0) {
1740 return NULL;
1741 }
1742
1743 gdRotatedImageSize(src, degrees, &bbox);
1744 new_width = bbox.width;
1745 new_height = bbox.height;
1746
1747 dst = gdImageCreateTrueColor(new_width, new_height);
1748 if (!dst) {
1749 return NULL;
1750 }
1751 dst->saveAlphaFlag = 1;
1752
1753 for (i = 0; i < new_height; i++) {
1754 unsigned int j;
1755 dst_offset_x = 0;
1756 for (j = 0; j < new_width; j++) {
1757 gdFixed f_i = gd_itofx((int)i - (int)new_height/ 2);
1758 gdFixed f_j = gd_itofx((int)j - (int)new_width / 2);
1759 gdFixed f_m = gd_mulfx(f_j,f_sin) + gd_mulfx(f_i,f_cos) + f_0_5 + f_H;
1760 gdFixed f_n = gd_mulfx(f_j,f_cos) - gd_mulfx(f_i,f_sin) + f_0_5 + f_W;
1761 long m = gd_fxtoi(f_m);
1762 long n = gd_fxtoi(f_n);
1763
1764 if ((n <= 0) || (m <= 0) || (m >= src_h) || (n >= src_w)) {
1765 dst->tpixels[dst_offset_y][dst_offset_x++] = bgColor;
1766 } else if ((n <= 1) || (m <= 1) || (m >= src_h - 1) || (n >= src_w - 1)) {
1767 register int c = getPixelInterpolated(src, n, m, bgColor);
1768 c = c | (( gdTrueColorGetAlpha(c) + ((int)(127* gd_fxtof(f_slop)))) << 24);
1769
1770 dst->tpixels[dst_offset_y][dst_offset_x++] = _color_blend(bgColor, c);
1771 } else {
1772 dst->tpixels[dst_offset_y][dst_offset_x++] = getPixelInterpolated(src, n, m, bgColor);
1773 }
1774 }
1775 dst_offset_y++;
1776 }
1777 return dst;
1778 }
1779
gdImageRotateBilinear(gdImagePtr src,const float degrees,const int bgColor)1780 gdImagePtr gdImageRotateBilinear(gdImagePtr src, const float degrees, const int bgColor)
1781 {
1782 float _angle = (float)((- degrees / 180.0f) * M_PI);
1783 const unsigned int src_w = gdImageSX(src);
1784 const unsigned int src_h = gdImageSY(src);
1785 unsigned int new_width, new_height;
1786 const gdFixed f_0_5 = gd_ftofx(0.5f);
1787 const gdFixed f_H = gd_itofx(src_h/2);
1788 const gdFixed f_W = gd_itofx(src_w/2);
1789 const gdFixed f_cos = gd_ftofx(cos(-_angle));
1790 const gdFixed f_sin = gd_ftofx(sin(-_angle));
1791 const gdFixed f_1 = gd_itofx(1);
1792 unsigned int i;
1793 unsigned int dst_offset_x;
1794 unsigned int dst_offset_y = 0;
1795 unsigned int src_offset_x, src_offset_y;
1796 gdImagePtr dst;
1797 gdRect bbox;
1798
1799 gdRotatedImageSize(src, degrees, &bbox);
1800
1801 new_width = bbox.width;
1802 new_height = bbox.height;
1803
1804 dst = gdImageCreateTrueColor(new_width, new_height);
1805 if (dst == NULL) {
1806 return NULL;
1807 }
1808 dst->saveAlphaFlag = 1;
1809
1810 for (i = 0; i < new_height; i++) {
1811 unsigned int j;
1812 dst_offset_x = 0;
1813
1814 for (j=0; j < new_width; j++) {
1815 const gdFixed f_i = gd_itofx((int)i - (int)new_height/2);
1816 const gdFixed f_j = gd_itofx((int)j - (int)new_width/2);
1817 const gdFixed f_m = gd_mulfx(f_j,f_sin) + gd_mulfx(f_i,f_cos) + f_0_5 + f_H;
1818 const gdFixed f_n = gd_mulfx(f_j,f_cos) - gd_mulfx(f_i,f_sin) + f_0_5 + f_W;
1819 const unsigned int m = gd_fxtoi(f_m);
1820 const unsigned int n = gd_fxtoi(f_n);
1821
1822 if ((m >= 0) && (m < src_h - 1) && (n >= 0) && (n < src_w - 1)) {
1823 const gdFixed f_f = f_m - gd_itofx(m);
1824 const gdFixed f_g = f_n - gd_itofx(n);
1825 const gdFixed f_w1 = gd_mulfx(f_1-f_f, f_1-f_g);
1826 const gdFixed f_w2 = gd_mulfx(f_1-f_f, f_g);
1827 const gdFixed f_w3 = gd_mulfx(f_f, f_1-f_g);
1828 const gdFixed f_w4 = gd_mulfx(f_f, f_g);
1829
1830 if (m < src_h-1) {
1831 src_offset_x = n;
1832 src_offset_y = m + 1;
1833 }
1834
1835 if (!((n >= src_w-1) || (m >= src_h-1))) {
1836 src_offset_x = n + 1;
1837 src_offset_y = m + 1;
1838 }
1839 {
1840 const int pixel1 = src->tpixels[src_offset_y][src_offset_x];
1841 register int pixel2, pixel3, pixel4;
1842
1843 if (src_offset_y + 1 >= src_h) {
1844 pixel2 = pixel1;
1845 pixel3 = pixel1;
1846 pixel4 = pixel1;
1847 } else if (src_offset_x + 1 >= src_w) {
1848 pixel2 = pixel1;
1849 pixel3 = pixel1;
1850 pixel4 = pixel1;
1851 } else {
1852 pixel2 = src->tpixels[src_offset_y][src_offset_x + 1];
1853 pixel3 = src->tpixels[src_offset_y + 1][src_offset_x];
1854 pixel4 = src->tpixels[src_offset_y + 1][src_offset_x + 1];
1855 }
1856 {
1857 const gdFixed f_r1 = gd_itofx(gdTrueColorGetRed(pixel1));
1858 const gdFixed f_r2 = gd_itofx(gdTrueColorGetRed(pixel2));
1859 const gdFixed f_r3 = gd_itofx(gdTrueColorGetRed(pixel3));
1860 const gdFixed f_r4 = gd_itofx(gdTrueColorGetRed(pixel4));
1861 const gdFixed f_g1 = gd_itofx(gdTrueColorGetGreen(pixel1));
1862 const gdFixed f_g2 = gd_itofx(gdTrueColorGetGreen(pixel2));
1863 const gdFixed f_g3 = gd_itofx(gdTrueColorGetGreen(pixel3));
1864 const gdFixed f_g4 = gd_itofx(gdTrueColorGetGreen(pixel4));
1865 const gdFixed f_b1 = gd_itofx(gdTrueColorGetBlue(pixel1));
1866 const gdFixed f_b2 = gd_itofx(gdTrueColorGetBlue(pixel2));
1867 const gdFixed f_b3 = gd_itofx(gdTrueColorGetBlue(pixel3));
1868 const gdFixed f_b4 = gd_itofx(gdTrueColorGetBlue(pixel4));
1869 const gdFixed f_a1 = gd_itofx(gdTrueColorGetAlpha(pixel1));
1870 const gdFixed f_a2 = gd_itofx(gdTrueColorGetAlpha(pixel2));
1871 const gdFixed f_a3 = gd_itofx(gdTrueColorGetAlpha(pixel3));
1872 const gdFixed f_a4 = gd_itofx(gdTrueColorGetAlpha(pixel4));
1873 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);
1874 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);
1875 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);
1876 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);
1877
1878 const unsigned char red = (unsigned char) CLAMP(gd_fxtoi(f_red), 0, 255);
1879 const unsigned char green = (unsigned char) CLAMP(gd_fxtoi(f_green), 0, 255);
1880 const unsigned char blue = (unsigned char) CLAMP(gd_fxtoi(f_blue), 0, 255);
1881 const unsigned char alpha = (unsigned char) CLAMP(gd_fxtoi(f_alpha), 0, 127);
1882
1883 dst->tpixels[dst_offset_y][dst_offset_x++] = gdTrueColorAlpha(red, green, blue, alpha);
1884 }
1885 }
1886 } else {
1887 dst->tpixels[dst_offset_y][dst_offset_x++] = bgColor;
1888 }
1889 }
1890 dst_offset_y++;
1891 }
1892 return dst;
1893 }
1894
gdImageRotateBicubicFixed(gdImagePtr src,const float degrees,const int bgColor)1895 gdImagePtr gdImageRotateBicubicFixed(gdImagePtr src, const float degrees, const int bgColor)
1896 {
1897 const float _angle = (float)((- degrees / 180.0f) * M_PI);
1898 const int src_w = gdImageSX(src);
1899 const int src_h = gdImageSY(src);
1900 unsigned int new_width, new_height;
1901 const gdFixed f_0_5 = gd_ftofx(0.5f);
1902 const gdFixed f_H = gd_itofx(src_h/2);
1903 const gdFixed f_W = gd_itofx(src_w/2);
1904 const gdFixed f_cos = gd_ftofx(cos(-_angle));
1905 const gdFixed f_sin = gd_ftofx(sin(-_angle));
1906 const gdFixed f_1 = gd_itofx(1);
1907 const gdFixed f_2 = gd_itofx(2);
1908 const gdFixed f_4 = gd_itofx(4);
1909 const gdFixed f_6 = gd_itofx(6);
1910 const gdFixed f_gama = gd_ftofx(1.04f);
1911
1912 unsigned int dst_offset_x;
1913 unsigned int dst_offset_y = 0;
1914 unsigned int i;
1915 gdImagePtr dst;
1916 gdRect bbox;
1917
1918 gdRotatedImageSize(src, degrees, &bbox);
1919 new_width = bbox.width;
1920 new_height = bbox.height;
1921 dst = gdImageCreateTrueColor(new_width, new_height);
1922
1923 if (dst == NULL) {
1924 return NULL;
1925 }
1926 dst->saveAlphaFlag = 1;
1927
1928 for (i=0; i < new_height; i++) {
1929 unsigned int j;
1930 dst_offset_x = 0;
1931
1932 for (j=0; j < new_width; j++) {
1933 const gdFixed f_i = gd_itofx((int)i - (int)new_height/2);
1934 const gdFixed f_j = gd_itofx((int)j - (int)new_width/2);
1935 const gdFixed f_m = gd_mulfx(f_j,f_sin) + gd_mulfx(f_i,f_cos) + f_0_5 + f_H;
1936 const gdFixed f_n = gd_mulfx(f_j,f_cos) - gd_mulfx(f_i,f_sin) + f_0_5 + f_W;
1937 const int m = gd_fxtoi(f_m);
1938 const int n = gd_fxtoi(f_n);
1939
1940 if ((m > 0) && (m < src_h - 1) && (n > 0) && (n < src_w-1)) {
1941 const gdFixed f_f = f_m - gd_itofx(m);
1942 const gdFixed f_g = f_n - gd_itofx(n);
1943 unsigned int src_offset_x[16], src_offset_y[16];
1944 unsigned char red, green, blue, alpha;
1945 gdFixed f_red=0, f_green=0, f_blue=0, f_alpha=0;
1946 int k;
1947
1948 if ((m < 1) || (n < 1)) {
1949 src_offset_x[0] = n;
1950 src_offset_y[0] = m;
1951 } else {
1952 src_offset_x[0] = n - 1;
1953 src_offset_y[0] = m;
1954 }
1955
1956 src_offset_x[1] = n;
1957 src_offset_y[1] = m;
1958
1959 if ((m < 1) || (n >= src_w-1)) {
1960 src_offset_x[2] = - 1;
1961 src_offset_y[2] = - 1;
1962 } else {
1963 src_offset_x[2] = n + 1;
1964 src_offset_y[2] = m ;
1965 }
1966
1967 if ((m < 1) || (n >= src_w-2)) {
1968 src_offset_x[3] = - 1;
1969 src_offset_y[3] = - 1;
1970 } else {
1971 src_offset_x[3] = n + 1 + 1;
1972 src_offset_y[3] = m ;
1973 }
1974
1975 if (n < 1) {
1976 src_offset_x[4] = - 1;
1977 src_offset_y[4] = - 1;
1978 } else {
1979 src_offset_x[4] = n - 1;
1980 src_offset_y[4] = m;
1981 }
1982
1983 src_offset_x[5] = n;
1984 src_offset_y[5] = m;
1985 if (n >= src_w-1) {
1986 src_offset_x[6] = - 1;
1987 src_offset_y[6] = - 1;
1988 } else {
1989 src_offset_x[6] = n + 1;
1990 src_offset_y[6] = m;
1991 }
1992
1993 if (n >= src_w-2) {
1994 src_offset_x[7] = - 1;
1995 src_offset_y[7] = - 1;
1996 } else {
1997 src_offset_x[7] = n + 1 + 1;
1998 src_offset_y[7] = m;
1999 }
2000
2001 if ((m >= src_h-1) || (n < 1)) {
2002 src_offset_x[8] = - 1;
2003 src_offset_y[8] = - 1;
2004 } else {
2005 src_offset_x[8] = n - 1;
2006 src_offset_y[8] = m;
2007 }
2008
2009 if (m >= src_h-1) {
2010 src_offset_x[9] = - 1;
2011 src_offset_y[9] = - 1;
2012 } else {
2013 src_offset_x[9] = n;
2014 src_offset_y[9] = m;
2015 }
2016
2017 if ((m >= src_h-1) || (n >= src_w-1)) {
2018 src_offset_x[10] = - 1;
2019 src_offset_y[10] = - 1;
2020 } else {
2021 src_offset_x[10] = n + 1;
2022 src_offset_y[10] = m;
2023 }
2024
2025 if ((m >= src_h-1) || (n >= src_w-2)) {
2026 src_offset_x[11] = - 1;
2027 src_offset_y[11] = - 1;
2028 } else {
2029 src_offset_x[11] = n + 1 + 1;
2030 src_offset_y[11] = m;
2031 }
2032
2033 if ((m >= src_h-2) || (n < 1)) {
2034 src_offset_x[12] = - 1;
2035 src_offset_y[12] = - 1;
2036 } else {
2037 src_offset_x[12] = n - 1;
2038 src_offset_y[12] = m;
2039 }
2040
2041 if (m >= src_h-2) {
2042 src_offset_x[13] = - 1;
2043 src_offset_y[13] = - 1;
2044 } else {
2045 src_offset_x[13] = n;
2046 src_offset_y[13] = m;
2047 }
2048
2049 if ((m >= src_h-2) || (n >= src_w - 1)) {
2050 src_offset_x[14] = - 1;
2051 src_offset_y[14] = - 1;
2052 } else {
2053 src_offset_x[14] = n + 1;
2054 src_offset_y[14] = m;
2055 }
2056
2057 if ((m >= src_h-2) || (n >= src_w-2)) {
2058 src_offset_x[15] = - 1;
2059 src_offset_y[15] = - 1;
2060 } else {
2061 src_offset_x[15] = n + 1 + 1;
2062 src_offset_y[15] = m;
2063 }
2064
2065 for (k=-1; k<3; k++) {
2066 const gdFixed f = gd_itofx(k)-f_f;
2067 const gdFixed f_fm1 = f - f_1;
2068 const gdFixed f_fp1 = f + f_1;
2069 const gdFixed f_fp2 = f + f_2;
2070 gdFixed f_a = 0, f_b = 0,f_c = 0, f_d = 0;
2071 gdFixed f_RY;
2072 int l;
2073
2074 if (f_fp2 > 0) {
2075 f_a = gd_mulfx(f_fp2,gd_mulfx(f_fp2,f_fp2));
2076 }
2077
2078 if (f_fp1 > 0) {
2079 f_b = gd_mulfx(f_fp1,gd_mulfx(f_fp1,f_fp1));
2080 }
2081
2082 if (f > 0) {
2083 f_c = gd_mulfx(f,gd_mulfx(f,f));
2084 }
2085
2086 if (f_fm1 > 0) {
2087 f_d = gd_mulfx(f_fm1,gd_mulfx(f_fm1,f_fm1));
2088 }
2089 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);
2090
2091 for (l=-1; l< 3; l++) {
2092 const gdFixed f = gd_itofx(l) - f_g;
2093 const gdFixed f_fm1 = f - f_1;
2094 const gdFixed f_fp1 = f + f_1;
2095 const gdFixed f_fp2 = f + f_2;
2096 gdFixed f_a = 0, f_b = 0, f_c = 0, f_d = 0;
2097 gdFixed f_RX, f_R;
2098 const int _k = ((k + 1) * 4) + (l + 1);
2099 register gdFixed f_rs, f_gs, f_bs, f_as;
2100 register int c;
2101
2102 if (f_fp2 > 0) {
2103 f_a = gd_mulfx(f_fp2,gd_mulfx(f_fp2,f_fp2));
2104 }
2105
2106 if (f_fp1 > 0) {
2107 f_b = gd_mulfx(f_fp1,gd_mulfx(f_fp1,f_fp1));
2108 }
2109
2110 if (f > 0) {
2111 f_c = gd_mulfx(f,gd_mulfx(f,f));
2112 }
2113
2114 if (f_fm1 > 0) {
2115 f_d = gd_mulfx(f_fm1,gd_mulfx(f_fm1,f_fm1));
2116 }
2117
2118 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);
2119 f_R = gd_mulfx(f_RY, f_RX);
2120
2121 if ((src_offset_x[_k] <= 0) || (src_offset_y[_k] <= 0) || (src_offset_y[_k] >= src_h) || (src_offset_x[_k] >= src_w)) {
2122 c = bgColor;
2123 } 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)) {
2124 gdFixed f_127 = gd_itofx(127);
2125 c = src->tpixels[src_offset_y[_k]][src_offset_x[_k]];
2126 c = c | (( (int) (gd_fxtof(gd_mulfx(f_R, f_127)) + 50.5f)) << 24);
2127 c = _color_blend(bgColor, c);
2128 } else {
2129 c = src->tpixels[src_offset_y[_k]][src_offset_x[_k]];
2130 }
2131
2132 f_rs = gd_itofx(gdTrueColorGetRed(c));
2133 f_gs = gd_itofx(gdTrueColorGetGreen(c));
2134 f_bs = gd_itofx(gdTrueColorGetBlue(c));
2135 f_as = gd_itofx(gdTrueColorGetAlpha(c));
2136
2137 f_red += gd_mulfx(f_rs, f_R);
2138 f_green += gd_mulfx(f_gs, f_R);
2139 f_blue += gd_mulfx(f_bs, f_R);
2140 f_alpha += gd_mulfx(f_as, f_R);
2141 }
2142 }
2143
2144 red = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_red, f_gama)), 0, 255);
2145 green = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_green, f_gama)), 0, 255);
2146 blue = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_blue, f_gama)), 0, 255);
2147 alpha = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_alpha, f_gama)), 0, 127);
2148
2149 dst->tpixels[dst_offset_y][dst_offset_x] = gdTrueColorAlpha(red, green, blue, alpha);
2150 } else {
2151 dst->tpixels[dst_offset_y][dst_offset_x] = bgColor;
2152 }
2153 dst_offset_x++;
2154 }
2155
2156 dst_offset_y++;
2157 }
2158 return dst;
2159 }
2160
gdImageRotateInterpolated(const gdImagePtr src,const float angle,int bgcolor)2161 gdImagePtr gdImageRotateInterpolated(const gdImagePtr src, const float angle, int bgcolor)
2162 {
2163 /* round to two decimals and keep the 100x multiplication to use it in the common square angles
2164 case later. Keep the two decimal precisions so smaller rotation steps can be done, useful for
2165 slow animations, f.e. */
2166 const int angle_rounded = fmod((int) floorf(angle * 100), 360 * 100);
2167
2168 if (bgcolor < 0) {
2169 return NULL;
2170 }
2171
2172 /* impact perf a bit, but not that much. Implementation for palette
2173 images can be done at a later point.
2174 */
2175 if (src->trueColor == 0) {
2176 if (bgcolor < gdMaxColors) {
2177 bgcolor = gdTrueColorAlpha(src->red[bgcolor], src->green[bgcolor], src->blue[bgcolor], src->alpha[bgcolor]);
2178 }
2179 gdImagePaletteToTrueColor(src);
2180 }
2181
2182 /* no interpolation needed here */
2183 switch (angle_rounded) {
2184 case 0: {
2185 gdImagePtr dst = gdImageCreateTrueColor(src->sx, src->sy);
2186 if (dst == NULL) {
2187 return NULL;
2188 }
2189 dst->transparent = src->transparent;
2190 dst->saveAlphaFlag = 1;
2191 dst->alphaBlendingFlag = gdEffectReplace;
2192
2193 gdImageCopy(dst, src, 0,0,0,0,src->sx,src->sy);
2194 return dst;
2195 }
2196 case -27000:
2197 case 9000:
2198 return gdImageRotate90(src, 0);
2199 case -18000:
2200 case 18000:
2201 return gdImageRotate180(src, 0);
2202 case -9000:
2203 case 27000:
2204 return gdImageRotate270(src, 0);
2205 }
2206
2207 if (src == NULL || src->interpolation_id < 1 || src->interpolation_id > GD_METHOD_COUNT) {
2208 return NULL;
2209 }
2210
2211 switch (src->interpolation_id) {
2212 case GD_NEAREST_NEIGHBOUR:
2213 return gdImageRotateNearestNeighbour(src, angle, bgcolor);
2214 break;
2215
2216 case GD_BILINEAR_FIXED:
2217 return gdImageRotateBilinear(src, angle, bgcolor);
2218 break;
2219
2220 case GD_BICUBIC_FIXED:
2221 return gdImageRotateBicubicFixed(src, angle, bgcolor);
2222 break;
2223
2224 default:
2225 return gdImageRotateGeneric(src, angle, bgcolor);
2226 }
2227 return NULL;
2228 }
2229
2230 /**
2231 * Title: Affine transformation
2232 **/
2233
2234 /**
2235 * Group: Transform
2236 **/
2237
gdImageClipRectangle(gdImagePtr im,gdRectPtr r)2238 static void gdImageClipRectangle(gdImagePtr im, gdRectPtr r)
2239 {
2240 int c1x, c1y, c2x, c2y;
2241 int x1,y1;
2242
2243 gdImageGetClip(im, &c1x, &c1y, &c2x, &c2y);
2244 x1 = r->x + r->width - 1;
2245 y1 = r->y + r->height - 1;
2246 r->x = CLAMP(r->x, c1x, c2x);
2247 r->y = CLAMP(r->y, c1y, c2y);
2248 r->width = CLAMP(x1, c1x, c2x) - r->x + 1;
2249 r->height = CLAMP(y1, c1y, c2y) - r->y + 1;
2250 }
2251
gdDumpRect(const char * msg,gdRectPtr r)2252 void gdDumpRect(const char *msg, gdRectPtr r)
2253 {
2254 printf("%s (%i, %i) (%i, %i)\n", msg, r->x, r->y, r->width, r->height);
2255 }
2256
2257 /**
2258 * Function: gdTransformAffineGetImage
2259 * Applies an affine transformation to a region and return an image
2260 * containing the complete transformation.
2261 *
2262 * Parameters:
2263 * dst - Pointer to a gdImagePtr to store the created image, NULL when
2264 * the creation or the transformation failed
2265 * src - Source image
2266 * src_area - rectangle defining the source region to transform
2267 * dstY - Y position in the destination image
2268 * affine - The desired affine transformation
2269 *
2270 * Returns:
2271 * GD_TRUE if the affine is rectilinear or GD_FALSE
2272 */
gdTransformAffineGetImage(gdImagePtr * dst,const gdImagePtr src,gdRectPtr src_area,const double affine[6])2273 int gdTransformAffineGetImage(gdImagePtr *dst,
2274 const gdImagePtr src,
2275 gdRectPtr src_area,
2276 const double affine[6])
2277 {
2278 int res;
2279 double m[6];
2280 gdRect bbox;
2281 gdRect area_full;
2282
2283 if (src_area == NULL) {
2284 area_full.x = 0;
2285 area_full.y = 0;
2286 area_full.width = gdImageSX(src);
2287 area_full.height = gdImageSY(src);
2288 src_area = &area_full;
2289 }
2290
2291 gdTransformAffineBoundingBox(src_area, affine, &bbox);
2292
2293 *dst = gdImageCreateTrueColor(bbox.width, bbox.height);
2294 if (*dst == NULL) {
2295 return GD_FALSE;
2296 }
2297 (*dst)->saveAlphaFlag = 1;
2298
2299 if (!src->trueColor) {
2300 gdImagePaletteToTrueColor(src);
2301 }
2302
2303 /* Translate to dst origin (0,0) */
2304 gdAffineTranslate(m, -bbox.x, -bbox.y);
2305 gdAffineConcat(m, affine, m);
2306
2307 gdImageAlphaBlending(*dst, 0);
2308
2309 res = gdTransformAffineCopy(*dst,
2310 0,0,
2311 src,
2312 src_area,
2313 m);
2314
2315 if (res != GD_TRUE) {
2316 gdImageDestroy(*dst);
2317 dst = NULL;
2318 return GD_FALSE;
2319 } else {
2320 return GD_TRUE;
2321 }
2322 }
2323
2324 /**
2325 * Function: gdTransformAffineCopy
2326 * Applies an affine transformation to a region and copy the result
2327 * in a destination to the given position.
2328 *
2329 * Parameters:
2330 * dst - Image to draw the transformed image
2331 * src - Source image
2332 * dstX - X position in the destination image
2333 * dstY - Y position in the destination image
2334 * src_area - Rectangular region to rotate in the src image
2335 *
2336 * Returns:
2337 * GD_TRUE on success or GD_FALSE on failure
2338 */
gdTransformAffineCopy(gdImagePtr dst,int dst_x,int dst_y,const gdImagePtr src,gdRectPtr src_region,const double affine[6])2339 int gdTransformAffineCopy(gdImagePtr dst,
2340 int dst_x, int dst_y,
2341 const gdImagePtr src,
2342 gdRectPtr src_region,
2343 const double affine[6])
2344 {
2345 int c1x,c1y,c2x,c2y;
2346 int backclip = 0;
2347 int backup_clipx1, backup_clipy1, backup_clipx2, backup_clipy2;
2348 register int x, y, src_offset_x, src_offset_y;
2349 double inv[6];
2350 int *dst_p;
2351 gdPointF pt, src_pt;
2352 gdRect bbox;
2353 int end_x, end_y;
2354 gdInterpolationMethod interpolation_id_bak = src->interpolation_id;
2355 interpolation_method interpolation_bak;
2356
2357 /* These methods use special implementations */
2358 if (src->interpolation_id == GD_BILINEAR_FIXED || src->interpolation_id == GD_BICUBIC_FIXED || src->interpolation_id == GD_NEAREST_NEIGHBOUR) {
2359 interpolation_id_bak = src->interpolation_id;
2360 interpolation_bak = src->interpolation;
2361
2362 gdImageSetInterpolationMethod(src, GD_BICUBIC);
2363 }
2364
2365
2366 gdImageClipRectangle(src, src_region);
2367
2368 if (src_region->x > 0 || src_region->y > 0
2369 || src_region->width < gdImageSX(src)
2370 || src_region->height < gdImageSY(src)) {
2371 backclip = 1;
2372
2373 gdImageGetClip(src, &backup_clipx1, &backup_clipy1,
2374 &backup_clipx2, &backup_clipy2);
2375
2376 gdImageSetClip(src, src_region->x, src_region->y,
2377 src_region->x + src_region->width - 1,
2378 src_region->y + src_region->height - 1);
2379 }
2380
2381 if (!gdTransformAffineBoundingBox(src_region, affine, &bbox)) {
2382 if (backclip) {
2383 gdImageSetClip(src, backup_clipx1, backup_clipy1,
2384 backup_clipx2, backup_clipy2);
2385 }
2386 gdImageSetInterpolationMethod(src, interpolation_id_bak);
2387 return GD_FALSE;
2388 }
2389
2390 gdImageGetClip(dst, &c1x, &c1y, &c2x, &c2y);
2391
2392 end_x = bbox.width + (int) fabs(bbox.x);
2393 end_y = bbox.height + (int) fabs(bbox.y);
2394
2395 /* Get inverse affine to let us work with destination -> source */
2396 if (gdAffineInvert(inv, affine) == GD_FALSE) {
2397 gdImageSetInterpolationMethod(src, interpolation_id_bak);
2398 return GD_FALSE;
2399 }
2400
2401 src_offset_x = src_region->x;
2402 src_offset_y = src_region->y;
2403
2404 if (dst->alphaBlendingFlag) {
2405 for (y = bbox.y; y <= end_y; y++) {
2406 pt.y = y + 0.5;
2407 for (x = 0; x <= end_x; x++) {
2408 pt.x = x + 0.5;
2409 gdAffineApplyToPointF(&src_pt, &pt, inv);
2410 gdImageSetPixel(dst, dst_x + x, dst_y + y, getPixelInterpolated(src, src_offset_x + src_pt.x, src_offset_y + src_pt.y, 0));
2411 }
2412 }
2413 } else {
2414 for (y = 0; y <= end_y; y++) {
2415 pt.y = y + 0.5 + bbox.y;
2416 if ((dst_y + y) < 0 || ((dst_y + y) > gdImageSY(dst) -1)) {
2417 continue;
2418 }
2419 dst_p = dst->tpixels[dst_y + y] + dst_x;
2420
2421 for (x = 0; x <= end_x; x++) {
2422 pt.x = x + 0.5 + bbox.x;
2423 gdAffineApplyToPointF(&src_pt, &pt, inv);
2424
2425 if ((dst_x + x) < 0 || (dst_x + x) > (gdImageSX(dst) - 1)) {
2426 break;
2427 }
2428 *(dst_p++) = getPixelInterpolated(src, src_offset_x + src_pt.x, src_offset_y + src_pt.y, -1);
2429 }
2430 }
2431 }
2432
2433 /* Restore clip if required */
2434 if (backclip) {
2435 gdImageSetClip(src, backup_clipx1, backup_clipy1,
2436 backup_clipx2, backup_clipy2);
2437 }
2438
2439 gdImageSetInterpolationMethod(src, interpolation_id_bak);
2440 return GD_TRUE;
2441 }
2442
2443 /**
2444 * Function: gdTransformAffineBoundingBox
2445 * Returns the bounding box of an affine transformation applied to a
2446 * rectangular area <gdRect>
2447 *
2448 * Parameters:
2449 * src - Rectangular source area for the affine transformation
2450 * affine - the affine transformation
2451 * bbox - the resulting bounding box
2452 *
2453 * Returns:
2454 * GD_TRUE if the affine is rectilinear or GD_FALSE
2455 */
gdTransformAffineBoundingBox(gdRectPtr src,const double affine[6],gdRectPtr bbox)2456 int gdTransformAffineBoundingBox(gdRectPtr src, const double affine[6], gdRectPtr bbox)
2457 {
2458 gdPointF extent[4], min, max, point;
2459 int i;
2460
2461 extent[0].x=0.0;
2462 extent[0].y=0.0;
2463 extent[1].x=(double) src->width;
2464 extent[1].y=0.0;
2465 extent[2].x=(double) src->width;
2466 extent[2].y=(double) src->height;
2467 extent[3].x=0.0;
2468 extent[3].y=(double) src->height;
2469
2470 for (i=0; i < 4; i++) {
2471 point=extent[i];
2472 if (gdAffineApplyToPointF(&extent[i], &point, affine) != GD_TRUE) {
2473 return GD_FALSE;
2474 }
2475 }
2476 min=extent[0];
2477 max=extent[0];
2478
2479 for (i=1; i < 4; i++) {
2480 if (min.x > extent[i].x)
2481 min.x=extent[i].x;
2482 if (min.y > extent[i].y)
2483 min.y=extent[i].y;
2484 if (max.x < extent[i].x)
2485 max.x=extent[i].x;
2486 if (max.y < extent[i].y)
2487 max.y=extent[i].y;
2488 }
2489 bbox->x = (int) min.x;
2490 bbox->y = (int) min.y;
2491 bbox->width = (int) floor(max.x - min.x) - 1;
2492 bbox->height = (int) floor(max.y - min.y);
2493 return GD_TRUE;
2494 }
2495
gdImageSetInterpolationMethod(gdImagePtr im,gdInterpolationMethod id)2496 int gdImageSetInterpolationMethod(gdImagePtr im, gdInterpolationMethod id)
2497 {
2498 if (im == NULL || id < 0 || id > GD_METHOD_COUNT) {
2499 return 0;
2500 }
2501
2502 switch (id) {
2503 case GD_DEFAULT:
2504 id = GD_BILINEAR_FIXED;
2505 /* Optimized versions */
2506 case GD_BILINEAR_FIXED:
2507 case GD_BICUBIC_FIXED:
2508 case GD_NEAREST_NEIGHBOUR:
2509 case GD_WEIGHTED4:
2510 im->interpolation = NULL;
2511 break;
2512
2513 /* generic versions*/
2514 case GD_BELL:
2515 im->interpolation = filter_bell;
2516 break;
2517 case GD_BESSEL:
2518 im->interpolation = filter_bessel;
2519 break;
2520 case GD_BICUBIC:
2521 im->interpolation = filter_bicubic;
2522 break;
2523 case GD_BLACKMAN:
2524 im->interpolation = filter_blackman;
2525 break;
2526 case GD_BOX:
2527 im->interpolation = filter_box;
2528 break;
2529 case GD_BSPLINE:
2530 im->interpolation = filter_bspline;
2531 break;
2532 case GD_CATMULLROM:
2533 im->interpolation = filter_catmullrom;
2534 break;
2535 case GD_GAUSSIAN:
2536 im->interpolation = filter_gaussian;
2537 break;
2538 case GD_GENERALIZED_CUBIC:
2539 im->interpolation = filter_generalized_cubic;
2540 break;
2541 case GD_HERMITE:
2542 im->interpolation = filter_hermite;
2543 break;
2544 case GD_HAMMING:
2545 im->interpolation = filter_hamming;
2546 break;
2547 case GD_HANNING:
2548 im->interpolation = filter_hanning;
2549 break;
2550 case GD_MITCHELL:
2551 im->interpolation = filter_mitchell;
2552 break;
2553 case GD_POWER:
2554 im->interpolation = filter_power;
2555 break;
2556 case GD_QUADRATIC:
2557 im->interpolation = filter_quadratic;
2558 break;
2559 case GD_SINC:
2560 im->interpolation = filter_sinc;
2561 break;
2562 case GD_TRIANGLE:
2563 im->interpolation = filter_triangle;
2564 break;
2565
2566 default:
2567 return 0;
2568 break;
2569 }
2570 im->interpolation_id = id;
2571 return 1;
2572 }
2573
2574 #ifdef _MSC_VER
2575 # pragma optimize("", on)
2576 #endif
2577