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