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