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