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);
742 const int yi=(int)(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 if (bgColor < 0) {
1707 return NULL;
1708 }
1709
1710 gdRotatedImageSize(src, degrees, &bbox);
1711 new_width = bbox.width;
1712 new_height = bbox.height;
1713
1714 dst = gdImageCreateTrueColor(new_width, new_height);
1715 if (!dst) {
1716 return NULL;
1717 }
1718 dst->saveAlphaFlag = 1;
1719
1720 for (i = 0; i < new_height; i++) {
1721 unsigned int j;
1722 dst_offset_x = 0;
1723 for (j = 0; j < new_width; j++) {
1724 gdFixed f_i = gd_itofx((int)i - (int)new_height/ 2);
1725 gdFixed f_j = gd_itofx((int)j - (int)new_width / 2);
1726 gdFixed f_m = gd_mulfx(f_j,f_sin) + gd_mulfx(f_i,f_cos) + f_0_5 + f_H;
1727 gdFixed f_n = gd_mulfx(f_j,f_cos) - gd_mulfx(f_i,f_sin) + f_0_5 + f_W;
1728 long m = gd_fxtoi(f_m);
1729 long n = gd_fxtoi(f_n);
1730
1731 if (m < -1 || n < -1 || m >= src_h || n >= src_w ) {
1732 dst->tpixels[dst_offset_y][dst_offset_x++] = bgColor;
1733 } else {
1734 dst->tpixels[dst_offset_y][dst_offset_x++] = getPixelInterpolated(src, gd_fxtod(f_n), gd_fxtod(f_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