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