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