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