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