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