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