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