1
2 #include <math.h>
3 #include <string.h>
4 #include <stdlib.h>
5 #include "gd.h"
6 #include "gdhelpers.h"
7
8 #include "php.h"
9
10 #ifdef _MSC_VER
11 # if _MSC_VER >= 1300
12 /* in MSVC.NET these are available but only for __cplusplus and not _MSC_EXTENSIONS */
13 # if !defined(_MSC_EXTENSIONS) && defined(__cplusplus)
14 # define HAVE_FABSF 1
15 extern float fabsf(float x);
16 # define HAVE_FLOORF 1
17 extern float floorf(float x);
18 # endif /*MSVC.NET */
19 # endif /* MSC */
20 #endif
21 #ifndef HAVE_FABSF
22 # define HAVE_FABSF 0
23 #endif
24 #ifndef HAVE_FLOORF
25 # define HAVE_FLOORF 0
26 #endif
27 #if HAVE_FABSF == 0
28 /* float fabsf(float x); */
29 # ifndef fabsf
30 # define fabsf(x) ((float)(fabs(x)))
31 # endif
32 #endif
33 #if HAVE_FLOORF == 0
34 # ifndef floorf
35 /* float floorf(float x);*/
36 # define floorf(x) ((float)(floor(x)))
37 # endif
38 #endif
39
40 #ifdef _OSD_POSIX /* BS2000 uses the EBCDIC char set instead of ASCII */
41 #define CHARSET_EBCDIC
42 #define __attribute__(any) /*nothing */
43 #endif
44 /*_OSD_POSIX*/
45
46 #ifndef CHARSET_EBCDIC
47 #define ASC(ch) ch
48 #else /*CHARSET_EBCDIC */
49 #define ASC(ch) gd_toascii[(unsigned char)ch]
50 static const unsigned char gd_toascii[256] =
51 {
52 /*00 */ 0x00, 0x01, 0x02, 0x03, 0x85, 0x09, 0x86, 0x7f,
53 0x87, 0x8d, 0x8e, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /*................ */
54 /*10 */ 0x10, 0x11, 0x12, 0x13, 0x8f, 0x0a, 0x08, 0x97,
55 0x18, 0x19, 0x9c, 0x9d, 0x1c, 0x1d, 0x1e, 0x1f, /*................ */
56 /*20 */ 0x80, 0x81, 0x82, 0x83, 0x84, 0x92, 0x17, 0x1b,
57 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x05, 0x06, 0x07, /*................ */
58 /*30 */ 0x90, 0x91, 0x16, 0x93, 0x94, 0x95, 0x96, 0x04,
59 0x98, 0x99, 0x9a, 0x9b, 0x14, 0x15, 0x9e, 0x1a, /*................ */
60 /*40 */ 0x20, 0xa0, 0xe2, 0xe4, 0xe0, 0xe1, 0xe3, 0xe5,
61 0xe7, 0xf1, 0x60, 0x2e, 0x3c, 0x28, 0x2b, 0x7c, /* .........`.<(+| */
62 /*50 */ 0x26, 0xe9, 0xea, 0xeb, 0xe8, 0xed, 0xee, 0xef,
63 0xec, 0xdf, 0x21, 0x24, 0x2a, 0x29, 0x3b, 0x9f, /*&.........!$*);. */
64 /*60 */ 0x2d, 0x2f, 0xc2, 0xc4, 0xc0, 0xc1, 0xc3, 0xc5,
65 0xc7, 0xd1, 0x5e, 0x2c, 0x25, 0x5f, 0x3e, 0x3f,
66 /*-/........^,%_>?*/
67 /*70 */ 0xf8, 0xc9, 0xca, 0xcb, 0xc8, 0xcd, 0xce, 0xcf,
68 0xcc, 0xa8, 0x3a, 0x23, 0x40, 0x27, 0x3d, 0x22, /*..........:#@'=" */
69 /*80 */ 0xd8, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
70 0x68, 0x69, 0xab, 0xbb, 0xf0, 0xfd, 0xfe, 0xb1, /*.abcdefghi...... */
71 /*90 */ 0xb0, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70,
72 0x71, 0x72, 0xaa, 0xba, 0xe6, 0xb8, 0xc6, 0xa4, /*.jklmnopqr...... */
73 /*a0 */ 0xb5, 0xaf, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
74 0x79, 0x7a, 0xa1, 0xbf, 0xd0, 0xdd, 0xde, 0xae, /*..stuvwxyz...... */
75 /*b0 */ 0xa2, 0xa3, 0xa5, 0xb7, 0xa9, 0xa7, 0xb6, 0xbc,
76 0xbd, 0xbe, 0xac, 0x5b, 0x5c, 0x5d, 0xb4, 0xd7, /*...........[\].. */
77 /*c0 */ 0xf9, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
78 0x48, 0x49, 0xad, 0xf4, 0xf6, 0xf2, 0xf3, 0xf5, /*.ABCDEFGHI...... */
79 /*d0 */ 0xa6, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50,
80 0x51, 0x52, 0xb9, 0xfb, 0xfc, 0xdb, 0xfa, 0xff, /*.JKLMNOPQR...... */
81 /*e0 */ 0xd9, 0xf7, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
82 0x59, 0x5a, 0xb2, 0xd4, 0xd6, 0xd2, 0xd3, 0xd5, /*..STUVWXYZ...... */
83 /*f0 */ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
84 0x38, 0x39, 0xb3, 0x7b, 0xdc, 0x7d, 0xda, 0x7e /*0123456789.{.}.~ */
85 };
86 #endif /*CHARSET_EBCDIC */
87
88 /* 2.0.10: cast instead of floor() yields 35% performance improvement. Thanks to John Buckman. */
89 #define floor_cast(exp) ((long) exp)
90
91 extern int gdCosT[];
92 extern int gdSinT[];
93
94 static void gdImageBrushApply(gdImagePtr im, int x, int y);
95 static void gdImageTileApply(gdImagePtr im, int x, int y);
96 static void gdImageAntiAliasedApply(gdImagePtr im, int x, int y);
97 static int gdLayerOverlay(int dst, int src);
98 static int gdAlphaOverlayColor(int src, int dst, int max);
99 int gdImageGetTrueColorPixel(gdImagePtr im, int x, int y);
100
php_gd_error_ex(int type,const char * format,...)101 void php_gd_error_ex(int type, const char *format, ...)
102 {
103 va_list args;
104
105 TSRMLS_FETCH();
106
107 va_start(args, format);
108 php_verror(NULL, "", type, format, args TSRMLS_CC);
109 va_end(args);
110 }
111
php_gd_error(const char * format,...)112 void php_gd_error(const char *format, ...)
113 {
114 va_list args;
115
116 TSRMLS_FETCH();
117
118 va_start(args, format);
119 php_verror(NULL, "", E_WARNING, format, args TSRMLS_CC);
120 va_end(args);
121 }
122
gdImageCreate(int sx,int sy)123 gdImagePtr gdImageCreate (int sx, int sy)
124 {
125 int i;
126 gdImagePtr im;
127
128 if (overflow2(sx, sy)) {
129 return NULL;
130 }
131
132 if (overflow2(sizeof(unsigned char *), sy)) {
133 return NULL;
134 }
135
136 if (overflow2(sizeof(unsigned char *), sx)) {
137 return NULL;
138 }
139
140 im = (gdImage *) gdCalloc(1, sizeof(gdImage));
141
142 /* Row-major ever since gd 1.3 */
143 im->pixels = (unsigned char **) gdMalloc(sizeof(unsigned char *) * sy);
144 im->AA_opacity = (unsigned char **) gdMalloc(sizeof(unsigned char *) * sy);
145 im->polyInts = 0;
146 im->polyAllocated = 0;
147 im->brush = 0;
148 im->tile = 0;
149 im->style = 0;
150 for (i = 0; i < sy; i++) {
151 /* Row-major ever since gd 1.3 */
152 im->pixels[i] = (unsigned char *) gdCalloc(sx, sizeof(unsigned char));
153 im->AA_opacity[i] = (unsigned char *) gdCalloc(sx, sizeof(unsigned char));
154 }
155 im->sx = sx;
156 im->sy = sy;
157 im->colorsTotal = 0;
158 im->transparent = (-1);
159 im->interlace = 0;
160 im->thick = 1;
161 im->AA = 0;
162 im->AA_polygon = 0;
163 for (i = 0; i < gdMaxColors; i++) {
164 im->open[i] = 1;
165 im->red[i] = 0;
166 im->green[i] = 0;
167 im->blue[i] = 0;
168 }
169 im->trueColor = 0;
170 im->tpixels = 0;
171 im->cx1 = 0;
172 im->cy1 = 0;
173 im->cx2 = im->sx - 1;
174 im->cy2 = im->sy - 1;
175 im->interpolation = NULL;
176 im->interpolation_id = GD_BILINEAR_FIXED;
177 return im;
178 }
179
gdImageCreateTrueColor(int sx,int sy)180 gdImagePtr gdImageCreateTrueColor (int sx, int sy)
181 {
182 int i;
183 gdImagePtr im;
184
185 if (overflow2(sx, sy)) {
186 return NULL;
187 }
188
189 if (overflow2(sizeof(unsigned char *), sy)) {
190 return NULL;
191 }
192
193 if (overflow2(sizeof(int *), sx)) {
194 return NULL;
195 }
196
197 im = (gdImage *) gdMalloc(sizeof(gdImage));
198 memset(im, 0, sizeof(gdImage));
199 im->tpixels = (int **) gdMalloc(sizeof(int *) * sy);
200 im->AA_opacity = (unsigned char **) gdMalloc(sizeof(unsigned char *) * sy);
201 im->polyInts = 0;
202 im->polyAllocated = 0;
203 im->brush = 0;
204 im->tile = 0;
205 im->style = 0;
206 for (i = 0; i < sy; i++) {
207 im->tpixels[i] = (int *) gdCalloc(sx, sizeof(int));
208 im->AA_opacity[i] = (unsigned char *) gdCalloc(sx, sizeof(unsigned char));
209 }
210 im->sx = sx;
211 im->sy = sy;
212 im->transparent = (-1);
213 im->interlace = 0;
214 im->trueColor = 1;
215 /* 2.0.2: alpha blending is now on by default, and saving of alpha is
216 * off by default. This allows font antialiasing to work as expected
217 * on the first try in JPEGs -- quite important -- and also allows
218 * for smaller PNGs when saving of alpha channel is not really
219 * desired, which it usually isn't!
220 */
221 im->saveAlphaFlag = 0;
222 im->alphaBlendingFlag = 1;
223 im->thick = 1;
224 im->AA = 0;
225 im->AA_polygon = 0;
226 im->cx1 = 0;
227 im->cy1 = 0;
228 im->cx2 = im->sx - 1;
229 im->cy2 = im->sy - 1;
230 im->interpolation = NULL;
231 im->interpolation_id = GD_BILINEAR_FIXED;
232 return im;
233 }
234
gdImageDestroy(gdImagePtr im)235 void gdImageDestroy (gdImagePtr im)
236 {
237 int i;
238 if (im->pixels) {
239 for (i = 0; i < im->sy; i++) {
240 gdFree(im->pixels[i]);
241 }
242 gdFree(im->pixels);
243 }
244 if (im->tpixels) {
245 for (i = 0; i < im->sy; i++) {
246 gdFree(im->tpixels[i]);
247 }
248 gdFree(im->tpixels);
249 }
250 if (im->AA_opacity) {
251 for (i = 0; i < im->sy; i++) {
252 gdFree(im->AA_opacity[i]);
253 }
254 gdFree(im->AA_opacity);
255 }
256 if (im->polyInts) {
257 gdFree(im->polyInts);
258 }
259 if (im->style) {
260 gdFree(im->style);
261 }
262 gdFree(im);
263 }
264
gdImageColorClosest(gdImagePtr im,int r,int g,int b)265 int gdImageColorClosest (gdImagePtr im, int r, int g, int b)
266 {
267 return gdImageColorClosestAlpha (im, r, g, b, gdAlphaOpaque);
268 }
269
gdImageColorClosestAlpha(gdImagePtr im,int r,int g,int b,int a)270 int gdImageColorClosestAlpha (gdImagePtr im, int r, int g, int b, int a)
271 {
272 int i;
273 long rd, gd, bd, ad;
274 int ct = (-1);
275 int first = 1;
276 long mindist = 0;
277
278 if (im->trueColor) {
279 return gdTrueColorAlpha(r, g, b, a);
280 }
281 for (i = 0; i < im->colorsTotal; i++) {
282 long dist;
283 if (im->open[i]) {
284 continue;
285 }
286 rd = im->red[i] - r;
287 gd = im->green[i] - g;
288 bd = im->blue[i] - b;
289 /* gd 2.02: whoops, was - b (thanks to David Marwood) */
290 ad = im->alpha[i] - a;
291 dist = rd * rd + gd * gd + bd * bd + ad * ad;
292 if (first || (dist < mindist)) {
293 mindist = dist;
294 ct = i;
295 first = 0;
296 }
297 }
298 return ct;
299 }
300
301 /* This code is taken from http://www.acm.org/jgt/papers/SmithLyons96/hwb_rgb.html, an article
302 * on colour conversion to/from RBG and HWB colour systems.
303 * It has been modified to return the converted value as a * parameter.
304 */
305
306 #define RETURN_HWB(h, w, b) {HWB->H = h; HWB->W = w; HWB->B = b; return HWB;}
307 #define RETURN_RGB(r, g, b) {RGB->R = r; RGB->G = g; RGB->B = b; return RGB;}
308 #define HWB_UNDEFINED -1
309 #define SETUP_RGB(s, r, g, b) {s.R = r/255.0f; s.G = g/255.0f; s.B = b/255.0f;}
310
311 #ifndef MIN
312 #define MIN(a,b) ((a)<(b)?(a):(b))
313 #endif
314 #define MIN3(a,b,c) ((a)<(b)?(MIN(a,c)):(MIN(b,c)))
315 #ifndef MAX
316 #define MAX(a,b) ((a)<(b)?(b):(a))
317 #endif
318 #define MAX3(a,b,c) ((a)<(b)?(MAX(b,c)):(MAX(a,c)))
319
320
321 /*
322 * Theoretically, hue 0 (pure red) is identical to hue 6 in these transforms. Pure
323 * red always maps to 6 in this implementation. Therefore UNDEFINED can be
324 * defined as 0 in situations where only unsigned numbers are desired.
325 */
326 typedef struct
327 {
328 float R, G, B;
329 }
330 RGBType;
331 typedef struct
332 {
333 float H, W, B;
334 }
335 HWBType;
336
RGB_to_HWB(RGBType RGB,HWBType * HWB)337 static HWBType * RGB_to_HWB (RGBType RGB, HWBType * HWB)
338 {
339 /*
340 * RGB are each on [0, 1]. W and B are returned on [0, 1] and H is
341 * returned on [0, 6]. Exception: H is returned UNDEFINED if W == 1 - B.
342 */
343
344 float R = RGB.R, G = RGB.G, B = RGB.B, w, v, b, f;
345 int i;
346
347 w = MIN3 (R, G, B);
348 v = MAX3 (R, G, B);
349 b = 1 - v;
350 if (v == w) {
351 RETURN_HWB(HWB_UNDEFINED, w, b);
352 }
353 f = (R == w) ? G - B : ((G == w) ? B - R : R - G);
354 i = (R == w) ? 3 : ((G == w) ? 5 : 1);
355
356 RETURN_HWB(i - f / (v - w), w, b);
357 }
358
HWB_Diff(int r1,int g1,int b1,int r2,int g2,int b2)359 static float HWB_Diff (int r1, int g1, int b1, int r2, int g2, int b2)
360 {
361 RGBType RGB1, RGB2;
362 HWBType HWB1, HWB2;
363 float diff;
364
365 SETUP_RGB(RGB1, r1, g1, b1);
366 SETUP_RGB(RGB2, r2, g2, b2);
367
368 RGB_to_HWB(RGB1, &HWB1);
369 RGB_to_HWB(RGB2, &HWB2);
370
371 /*
372 * I made this bit up; it seems to produce OK results, and it is certainly
373 * more visually correct than the current RGB metric. (PJW)
374 */
375
376 if ((HWB1.H == HWB_UNDEFINED) || (HWB2.H == HWB_UNDEFINED)) {
377 diff = 0.0f; /* Undefined hues always match... */
378 } else {
379 diff = fabsf(HWB1.H - HWB2.H);
380 if (diff > 3.0f) {
381 diff = 6.0f - diff; /* Remember, it's a colour circle */
382 }
383 }
384
385 diff = diff * diff + (HWB1.W - HWB2.W) * (HWB1.W - HWB2.W) + (HWB1.B - HWB2.B) * (HWB1.B - HWB2.B);
386
387 return diff;
388 }
389
390
391 #if 0
392 /*
393 * This is not actually used, but is here for completeness, in case someone wants to
394 * use the HWB stuff for anything else...
395 */
396 static RGBType * HWB_to_RGB (HWBType HWB, RGBType * RGB)
397 {
398 /*
399 * H is given on [0, 6] or UNDEFINED. W and B are given on [0, 1].
400 * RGB are each returned on [0, 1].
401 */
402
403 float h = HWB.H, w = HWB.W, b = HWB.B, v, n, f;
404 int i;
405
406 v = 1 - b;
407 if (h == HWB_UNDEFINED) {
408 RETURN_RGB(v, v, v);
409 }
410 i = floor(h);
411 f = h - i;
412 if (i & 1) {
413 f = 1 - f; /* if i is odd */
414 }
415 n = w + f * (v - w); /* linear interpolation between w and v */
416 switch (i) {
417 case 6:
418 case 0:
419 RETURN_RGB(v, n, w);
420 case 1:
421 RETURN_RGB(n, v, w);
422 case 2:
423 RETURN_RGB(w, v, n);
424 case 3:
425 RETURN_RGB(w, n, v);
426 case 4:
427 RETURN_RGB(n, w, v);
428 case 5:
429 RETURN_RGB(v, w, n);
430 }
431
432 return RGB;
433 }
434 #endif
435
gdImageColorClosestHWB(gdImagePtr im,int r,int g,int b)436 int gdImageColorClosestHWB (gdImagePtr im, int r, int g, int b)
437 {
438 int i;
439 /* long rd, gd, bd; */
440 int ct = (-1);
441 int first = 1;
442 float mindist = 0;
443 if (im->trueColor) {
444 return gdTrueColor(r, g, b);
445 }
446 for (i = 0; i < im->colorsTotal; i++) {
447 float dist;
448 if (im->open[i]) {
449 continue;
450 }
451 dist = HWB_Diff(im->red[i], im->green[i], im->blue[i], r, g, b);
452 if (first || (dist < mindist)) {
453 mindist = dist;
454 ct = i;
455 first = 0;
456 }
457 }
458 return ct;
459 }
460
gdImageColorExact(gdImagePtr im,int r,int g,int b)461 int gdImageColorExact (gdImagePtr im, int r, int g, int b)
462 {
463 return gdImageColorExactAlpha (im, r, g, b, gdAlphaOpaque);
464 }
465
gdImageColorExactAlpha(gdImagePtr im,int r,int g,int b,int a)466 int gdImageColorExactAlpha (gdImagePtr im, int r, int g, int b, int a)
467 {
468 int i;
469 if (im->trueColor) {
470 return gdTrueColorAlpha(r, g, b, a);
471 }
472 for (i = 0; i < im->colorsTotal; i++) {
473 if (im->open[i]) {
474 continue;
475 }
476 if ((im->red[i] == r) && (im->green[i] == g) && (im->blue[i] == b) && (im->alpha[i] == a)) {
477 return i;
478 }
479 }
480 return -1;
481 }
482
gdImageColorAllocate(gdImagePtr im,int r,int g,int b)483 int gdImageColorAllocate (gdImagePtr im, int r, int g, int b)
484 {
485 return gdImageColorAllocateAlpha (im, r, g, b, gdAlphaOpaque);
486 }
487
gdImageColorAllocateAlpha(gdImagePtr im,int r,int g,int b,int a)488 int gdImageColorAllocateAlpha (gdImagePtr im, int r, int g, int b, int a)
489 {
490 int i;
491 int ct = (-1);
492 if (im->trueColor) {
493 return gdTrueColorAlpha(r, g, b, a);
494 }
495 for (i = 0; i < im->colorsTotal; i++) {
496 if (im->open[i]) {
497 ct = i;
498 break;
499 }
500 }
501 if (ct == (-1)) {
502 ct = im->colorsTotal;
503 if (ct == gdMaxColors) {
504 return -1;
505 }
506 im->colorsTotal++;
507 }
508 im->red[ct] = r;
509 im->green[ct] = g;
510 im->blue[ct] = b;
511 im->alpha[ct] = a;
512 im->open[ct] = 0;
513
514 return ct;
515 }
516
517 /*
518 * gdImageColorResolve is an alternative for the code fragment:
519 *
520 * if ((color=gdImageColorExact(im,R,G,B)) < 0)
521 * if ((color=gdImageColorAllocate(im,R,G,B)) < 0)
522 * color=gdImageColorClosest(im,R,G,B);
523 *
524 * in a single function. Its advantage is that it is guaranteed to
525 * return a color index in one search over the color table.
526 */
527
gdImageColorResolve(gdImagePtr im,int r,int g,int b)528 int gdImageColorResolve (gdImagePtr im, int r, int g, int b)
529 {
530 return gdImageColorResolveAlpha(im, r, g, b, gdAlphaOpaque);
531 }
532
gdImageColorResolveAlpha(gdImagePtr im,int r,int g,int b,int a)533 int gdImageColorResolveAlpha (gdImagePtr im, int r, int g, int b, int a)
534 {
535 int c;
536 int ct = -1;
537 int op = -1;
538 long rd, gd, bd, ad, dist;
539 long mindist = 4 * 255 * 255; /* init to max poss dist */
540 if (im->trueColor)
541 {
542 return gdTrueColorAlpha (r, g, b, a);
543 }
544
545 for (c = 0; c < im->colorsTotal; c++)
546 {
547 if (im->open[c])
548 {
549 op = c; /* Save open slot */
550 continue; /* Color not in use */
551 }
552 if (c == im->transparent)
553 {
554 /* don't ever resolve to the color that has
555 * been designated as the transparent color */
556 continue;
557 }
558 rd = (long) (im->red[c] - r);
559 gd = (long) (im->green[c] - g);
560 bd = (long) (im->blue[c] - b);
561 ad = (long) (im->alpha[c] - a);
562 dist = rd * rd + gd * gd + bd * bd + ad * ad;
563 if (dist < mindist)
564 {
565 if (dist == 0)
566 {
567 return c; /* Return exact match color */
568 }
569 mindist = dist;
570 ct = c;
571 }
572 }
573 /* no exact match. We now know closest, but first try to allocate exact */
574 if (op == -1)
575 {
576 op = im->colorsTotal;
577 if (op == gdMaxColors)
578 { /* No room for more colors */
579 return ct; /* Return closest available color */
580 }
581 im->colorsTotal++;
582 }
583 im->red[op] = r;
584 im->green[op] = g;
585 im->blue[op] = b;
586 im->alpha[op] = a;
587 im->open[op] = 0;
588 return op; /* Return newly allocated color */
589 }
590
gdImageColorDeallocate(gdImagePtr im,int color)591 void gdImageColorDeallocate (gdImagePtr im, int color)
592 {
593 if (im->trueColor) {
594 return;
595 }
596 /* Mark it open. */
597 im->open[color] = 1;
598 }
599
gdImageColorTransparent(gdImagePtr im,int color)600 void gdImageColorTransparent (gdImagePtr im, int color)
601 {
602 if (color < 0) {
603 return;
604 }
605 if (!im->trueColor) {
606 if((color >= im->colorsTotal)) {
607 return;
608 }
609 /* Make the old transparent color opaque again */
610 if (im->transparent != -1) {
611 im->alpha[im->transparent] = gdAlphaOpaque;
612 }
613 im->alpha[color] = gdAlphaTransparent;
614 }
615 im->transparent = color;
616 }
617
gdImagePaletteCopy(gdImagePtr to,gdImagePtr from)618 void gdImagePaletteCopy (gdImagePtr to, gdImagePtr from)
619 {
620 int i;
621 int x, y, p;
622 int xlate[256];
623 if (to->trueColor || from->trueColor) {
624 return;
625 }
626
627 for (i = 0; i < 256; i++) {
628 xlate[i] = -1;
629 }
630
631 for (y = 0; y < to->sy; y++) {
632 for (x = 0; x < to->sx; x++) {
633 p = gdImageGetPixel(to, x, y);
634 if (xlate[p] == -1) {
635 /* This ought to use HWB, but we don't have an alpha-aware version of that yet. */
636 xlate[p] = gdImageColorClosestAlpha (from, to->red[p], to->green[p], to->blue[p], to->alpha[p]);
637 }
638 gdImageSetPixel(to, x, y, xlate[p]);
639 }
640 }
641
642 for (i = 0; i < from->colorsTotal; i++) {
643 to->red[i] = from->red[i];
644 to->blue[i] = from->blue[i];
645 to->green[i] = from->green[i];
646 to->alpha[i] = from->alpha[i];
647 to->open[i] = 0;
648 }
649
650 for (i = from->colorsTotal; i < to->colorsTotal; i++) {
651 to->open[i] = 1;
652 }
653
654 to->colorsTotal = from->colorsTotal;
655 }
656
657 /* 2.0.10: before the drawing routines, some code to clip points that are
658 * outside the drawing window. Nick Atty (nick@canalplan.org.uk)
659 *
660 * This is the Sutherland Hodgman Algorithm, as implemented by
661 * Duvanenko, Robbins and Gyurcsik - SH(DRG) for short. See Dr Dobb's
662 * Journal, January 1996, pp107-110 and 116-117
663 *
664 * Given the end points of a line, and a bounding rectangle (which we
665 * know to be from (0,0) to (SX,SY)), adjust the endpoints to be on
666 * the edges of the rectangle if the line should be drawn at all,
667 * otherwise return a failure code
668 */
669
670 /* this does "one-dimensional" clipping: note that the second time it
671 * is called, all the x parameters refer to height and the y to width
672 * - the comments ignore this (if you can understand it when it's
673 * looking at the X parameters, it should become clear what happens on
674 * the second call!) The code is simplified from that in the article,
675 * as we know that gd images always start at (0,0)
676 */
677
clip_1d(int * x0,int * y0,int * x1,int * y1,int maxdim)678 static int clip_1d(int *x0, int *y0, int *x1, int *y1, int maxdim) {
679 double m; /* gradient of line */
680
681 if (*x0 < 0) { /* start of line is left of window */
682 if(*x1 < 0) { /* as is the end, so the line never cuts the window */
683 return 0;
684 }
685 m = (*y1 - *y0)/(double)(*x1 - *x0); /* calculate the slope of the line */
686 /* adjust x0 to be on the left boundary (ie to be zero), and y0 to match */
687 *y0 -= (int)(m * *x0);
688 *x0 = 0;
689 /* now, perhaps, adjust the far end of the line as well */
690 if (*x1 > maxdim) {
691 *y1 += (int)(m * (maxdim - *x1));
692 *x1 = maxdim;
693 }
694 return 1;
695 }
696 if (*x0 > maxdim) { /* start of line is right of window - complement of above */
697 if (*x1 > maxdim) { /* as is the end, so the line misses the window */
698 return 0;
699 }
700 m = (*y1 - *y0)/(double)(*x1 - *x0); /* calculate the slope of the line */
701 *y0 += (int)(m * (maxdim - *x0)); /* adjust so point is on the right boundary */
702 *x0 = maxdim;
703 /* now, perhaps, adjust the end of the line */
704 if (*x1 < 0) {
705 *y1 -= (int)(m * *x1);
706 *x1 = 0;
707 }
708 return 1;
709 }
710 /* the final case - the start of the line is inside the window */
711 if (*x1 > maxdim) { /* other end is outside to the right */
712 m = (*y1 - *y0)/(double)(*x1 - *x0); /* calculate the slope of the line */
713 *y1 += (int)(m * (maxdim - *x1));
714 *x1 = maxdim;
715 return 1;
716 }
717 if (*x1 < 0) { /* other end is outside to the left */
718 m = (*y1 - *y0)/(double)(*x1 - *x0); /* calculate the slope of the line */
719 *y1 -= (int)(m * *x1);
720 *x1 = 0;
721 return 1;
722 }
723 /* only get here if both points are inside the window */
724 return 1;
725 }
726
gdImageSetPixel(gdImagePtr im,int x,int y,int color)727 void gdImageSetPixel (gdImagePtr im, int x, int y, int color)
728 {
729 int p;
730 switch (color) {
731 case gdStyled:
732 if (!im->style) {
733 /* Refuse to draw if no style is set. */
734 return;
735 } else {
736 p = im->style[im->stylePos++];
737 }
738 if (p != gdTransparent) {
739 gdImageSetPixel(im, x, y, p);
740 }
741 im->stylePos = im->stylePos % im->styleLength;
742 break;
743 case gdStyledBrushed:
744 if (!im->style) {
745 /* Refuse to draw if no style is set. */
746 return;
747 }
748 p = im->style[im->stylePos++];
749 if (p != gdTransparent && p != 0) {
750 gdImageSetPixel(im, x, y, gdBrushed);
751 }
752 im->stylePos = im->stylePos % im->styleLength;
753 break;
754 case gdBrushed:
755 gdImageBrushApply(im, x, y);
756 break;
757 case gdTiled:
758 gdImageTileApply(im, x, y);
759 break;
760 case gdAntiAliased:
761 gdImageAntiAliasedApply(im, x, y);
762 break;
763 default:
764 if (gdImageBoundsSafe(im, x, y)) {
765 if (im->trueColor) {
766 switch (im->alphaBlendingFlag) {
767 default:
768 case gdEffectReplace:
769 im->tpixels[y][x] = color;
770 break;
771 case gdEffectAlphaBlend:
772 im->tpixels[y][x] = gdAlphaBlend(im->tpixels[y][x], color);
773 break;
774 case gdEffectNormal:
775 im->tpixels[y][x] = gdAlphaBlend(im->tpixels[y][x], color);
776 break;
777 case gdEffectOverlay :
778 im->tpixels[y][x] = gdLayerOverlay(im->tpixels[y][x], color);
779 break;
780 }
781 } else {
782 im->pixels[y][x] = color;
783 }
784 }
785 break;
786 }
787 }
788
gdImageGetTrueColorPixel(gdImagePtr im,int x,int y)789 int gdImageGetTrueColorPixel (gdImagePtr im, int x, int y)
790 {
791 int p = gdImageGetPixel(im, x, y);
792
793 if (!im->trueColor) {
794 return gdTrueColorAlpha(im->red[p], im->green[p], im->blue[p], (im->transparent == p) ? gdAlphaTransparent : im->alpha[p]);
795 } else {
796 return p;
797 }
798 }
799
gdImageBrushApply(gdImagePtr im,int x,int y)800 static void gdImageBrushApply (gdImagePtr im, int x, int y)
801 {
802 int lx, ly;
803 int hy, hx;
804 int x1, y1, x2, y2;
805 int srcx, srcy;
806
807 if (!im->brush) {
808 return;
809 }
810
811 hy = gdImageSY(im->brush) / 2;
812 y1 = y - hy;
813 y2 = y1 + gdImageSY(im->brush);
814 hx = gdImageSX(im->brush) / 2;
815 x1 = x - hx;
816 x2 = x1 + gdImageSX(im->brush);
817 srcy = 0;
818
819 if (im->trueColor) {
820 if (im->brush->trueColor) {
821 for (ly = y1; ly < y2; ly++) {
822 srcx = 0;
823 for (lx = x1; (lx < x2); lx++) {
824 int p;
825 p = gdImageGetTrueColorPixel(im->brush, srcx, srcy);
826 /* 2.0.9, Thomas Winzig: apply simple full transparency */
827 if (p != gdImageGetTransparent(im->brush)) {
828 gdImageSetPixel(im, lx, ly, p);
829 }
830 srcx++;
831 }
832 srcy++;
833 }
834 } else {
835 /* 2.0.12: Brush palette, image truecolor (thanks to Thorben Kundinger for pointing out the issue) */
836 for (ly = y1; ly < y2; ly++) {
837 srcx = 0;
838 for (lx = x1; lx < x2; lx++) {
839 int p, tc;
840 p = gdImageGetPixel(im->brush, srcx, srcy);
841 tc = gdImageGetTrueColorPixel(im->brush, srcx, srcy);
842 /* 2.0.9, Thomas Winzig: apply simple full transparency */
843 if (p != gdImageGetTransparent(im->brush)) {
844 gdImageSetPixel(im, lx, ly, tc);
845 }
846 srcx++;
847 }
848 srcy++;
849 }
850 }
851 } else {
852 for (ly = y1; ly < y2; ly++) {
853 srcx = 0;
854 for (lx = x1; lx < x2; lx++) {
855 int p;
856 p = gdImageGetPixel(im->brush, srcx, srcy);
857 /* Allow for non-square brushes! */
858 if (p != gdImageGetTransparent(im->brush)) {
859 /* Truecolor brush. Very slow on a palette destination. */
860 if (im->brush->trueColor) {
861 gdImageSetPixel(im, lx, ly, gdImageColorResolveAlpha(im, gdTrueColorGetRed(p),
862 gdTrueColorGetGreen(p),
863 gdTrueColorGetBlue(p),
864 gdTrueColorGetAlpha(p)));
865 } else {
866 gdImageSetPixel(im, lx, ly, im->brushColorMap[p]);
867 }
868 }
869 srcx++;
870 }
871 srcy++;
872 }
873 }
874 }
875
gdImageTileApply(gdImagePtr im,int x,int y)876 static void gdImageTileApply (gdImagePtr im, int x, int y)
877 {
878 gdImagePtr tile = im->tile;
879 int srcx, srcy;
880 int p;
881 if (!tile) {
882 return;
883 }
884 srcx = x % gdImageSX(tile);
885 srcy = y % gdImageSY(tile);
886 if (im->trueColor) {
887 p = gdImageGetPixel(tile, srcx, srcy);
888 if (p != gdImageGetTransparent (tile)) {
889 if (!tile->trueColor) {
890 p = gdTrueColorAlpha(tile->red[p], tile->green[p], tile->blue[p], tile->alpha[p]);
891 }
892 gdImageSetPixel(im, x, y, p);
893 }
894 } else {
895 p = gdImageGetPixel(tile, srcx, srcy);
896 /* Allow for transparency */
897 if (p != gdImageGetTransparent(tile)) {
898 if (tile->trueColor) {
899 /* Truecolor tile. Very slow on a palette destination. */
900 gdImageSetPixel(im, x, y, gdImageColorResolveAlpha(im,
901 gdTrueColorGetRed(p),
902 gdTrueColorGetGreen(p),
903 gdTrueColorGetBlue(p),
904 gdTrueColorGetAlpha(p)));
905 } else {
906 gdImageSetPixel(im, x, y, im->tileColorMap[p]);
907 }
908 }
909 }
910 }
911
912
gdImageTileGet(gdImagePtr im,int x,int y)913 static int gdImageTileGet (gdImagePtr im, int x, int y)
914 {
915 int srcx, srcy;
916 int tileColor,p;
917 if (!im->tile) {
918 return -1;
919 }
920 srcx = x % gdImageSX(im->tile);
921 srcy = y % gdImageSY(im->tile);
922 p = gdImageGetPixel(im->tile, srcx, srcy);
923
924 if (im->trueColor) {
925 if (im->tile->trueColor) {
926 tileColor = p;
927 } else {
928 tileColor = gdTrueColorAlpha( gdImageRed(im->tile,p), gdImageGreen(im->tile,p), gdImageBlue (im->tile,p), gdImageAlpha (im->tile,p));
929 }
930 } else {
931 if (im->tile->trueColor) {
932 tileColor = gdImageColorResolveAlpha(im, gdTrueColorGetRed (p), gdTrueColorGetGreen (p), gdTrueColorGetBlue (p), gdTrueColorGetAlpha (p));
933 } else {
934 tileColor = p;
935 tileColor = gdImageColorResolveAlpha(im, gdImageRed (im->tile,p), gdImageGreen (im->tile,p), gdImageBlue (im->tile,p), gdImageAlpha (im->tile,p));
936 }
937 }
938 return tileColor;
939 }
940
941
gdImageAntiAliasedApply(gdImagePtr im,int px,int py)942 static void gdImageAntiAliasedApply (gdImagePtr im, int px, int py)
943 {
944 float p_dist, p_alpha;
945 unsigned char opacity;
946
947 /*
948 * Find the perpendicular distance from point C (px, py) to the line
949 * segment AB that is being drawn. (Adapted from an algorithm from the
950 * comp.graphics.algorithms FAQ.)
951 */
952
953 int LAC_2, LBC_2;
954
955 int Ax_Cx = im->AAL_x1 - px;
956 int Ay_Cy = im->AAL_y1 - py;
957
958 int Bx_Cx = im->AAL_x2 - px;
959 int By_Cy = im->AAL_y2 - py;
960
961 /* 2.0.13: bounds check! AA_opacity is just as capable of
962 * overflowing as the main pixel array. Arne Jorgensen.
963 * 2.0.14: typo fixed. 2.0.15: moved down below declarations
964 * to satisfy non-C++ compilers.
965 */
966 if (!gdImageBoundsSafe(im, px, py)) {
967 return;
968 }
969
970 /* Get the squares of the lengths of the segemnts AC and BC. */
971 LAC_2 = (Ax_Cx * Ax_Cx) + (Ay_Cy * Ay_Cy);
972 LBC_2 = (Bx_Cx * Bx_Cx) + (By_Cy * By_Cy);
973
974 if (((im->AAL_LAB_2 + LAC_2) >= LBC_2) && ((im->AAL_LAB_2 + LBC_2) >= LAC_2)) {
975 /* The two angles are acute. The point lies inside the portion of the
976 * plane spanned by the line segment.
977 */
978 p_dist = fabs ((float) ((Ay_Cy * im->AAL_Bx_Ax) - (Ax_Cx * im->AAL_By_Ay)) / im->AAL_LAB);
979 } else {
980 /* The point is past an end of the line segment. It's length from the
981 * segment is the shorter of the lengths from the endpoints, but call
982 * the distance -1, so as not to compute the alpha nor draw the pixel.
983 */
984 p_dist = -1;
985 }
986
987 if ((p_dist >= 0) && (p_dist <= (float) (im->thick))) {
988 p_alpha = pow (1.0 - (p_dist / 1.5), 2);
989
990 if (p_alpha > 0) {
991 if (p_alpha >= 1) {
992 opacity = 255;
993 } else {
994 opacity = (unsigned char) (p_alpha * 255.0);
995 }
996 if (!im->AA_polygon || (im->AA_opacity[py][px] < opacity)) {
997 im->AA_opacity[py][px] = opacity;
998 }
999 }
1000 }
1001 }
1002
1003
gdImageGetPixel(gdImagePtr im,int x,int y)1004 int gdImageGetPixel (gdImagePtr im, int x, int y)
1005 {
1006 if (gdImageBoundsSafe(im, x, y)) {
1007 if (im->trueColor) {
1008 return im->tpixels[y][x];
1009 } else {
1010 return im->pixels[y][x];
1011 }
1012 } else {
1013 return 0;
1014 }
1015 }
1016
gdImageAABlend(gdImagePtr im)1017 void gdImageAABlend (gdImagePtr im)
1018 {
1019 float p_alpha, old_alpha;
1020 int color = im->AA_color, color_red, color_green, color_blue;
1021 int old_color, old_red, old_green, old_blue;
1022 int p_color, p_red, p_green, p_blue;
1023 int px, py;
1024
1025 color_red = gdImageRed(im, color);
1026 color_green = gdImageGreen(im, color);
1027 color_blue = gdImageBlue(im, color);
1028
1029 /* Impose the anti-aliased drawing on the image. */
1030 for (py = 0; py < im->sy; py++) {
1031 for (px = 0; px < im->sx; px++) {
1032 if (im->AA_opacity[py][px] != 0) {
1033 old_color = gdImageGetPixel(im, px, py);
1034
1035 if ((old_color != color) && ((old_color != im->AA_dont_blend) || (im->AA_opacity[py][px] == 255))) {
1036 /* Only blend with different colors that aren't the dont_blend color. */
1037 p_alpha = (float) (im->AA_opacity[py][px]) / 255.0;
1038 old_alpha = 1.0 - p_alpha;
1039
1040 if (p_alpha >= 1.0) {
1041 p_color = color;
1042 } else {
1043 old_red = gdImageRed(im, old_color);
1044 old_green = gdImageGreen(im, old_color);
1045 old_blue = gdImageBlue(im, old_color);
1046
1047 p_red = (int) (((float) color_red * p_alpha) + ((float) old_red * old_alpha));
1048 p_green = (int) (((float) color_green * p_alpha) + ((float) old_green * old_alpha));
1049 p_blue = (int) (((float) color_blue * p_alpha) + ((float) old_blue * old_alpha));
1050 p_color = gdImageColorResolve(im, p_red, p_green, p_blue);
1051 }
1052 gdImageSetPixel(im, px, py, p_color);
1053 }
1054 }
1055 }
1056 /* Clear the AA_opacity array behind us. */
1057 memset(im->AA_opacity[py], 0, im->sx);
1058 }
1059 }
1060
1061 static void _gdImageFilledHRectangle (gdImagePtr im, int x1, int y1, int x2, int y2, int color);
1062
gdImageHLine(gdImagePtr im,int y,int x1,int x2,int col)1063 static void gdImageHLine(gdImagePtr im, int y, int x1, int x2, int col)
1064 {
1065 if (im->thick > 1) {
1066 int thickhalf = im->thick >> 1;
1067 _gdImageFilledHRectangle(im, x1, y - thickhalf, x2, y + im->thick - thickhalf - 1, col);
1068 } else {
1069 if (x2 < x1) {
1070 int t = x2;
1071 x2 = x1;
1072 x1 = t;
1073 }
1074
1075 for (;x1 <= x2; x1++) {
1076 gdImageSetPixel(im, x1, y, col);
1077 }
1078 }
1079 return;
1080 }
1081
gdImageVLine(gdImagePtr im,int x,int y1,int y2,int col)1082 static void gdImageVLine(gdImagePtr im, int x, int y1, int y2, int col)
1083 {
1084 if (im->thick > 1) {
1085 int thickhalf = im->thick >> 1;
1086 gdImageFilledRectangle(im, x - thickhalf, y1, x + im->thick - thickhalf - 1, y2, col);
1087 } else {
1088 if (y2 < y1) {
1089 int t = y1;
1090 y1 = y2;
1091 y2 = t;
1092 }
1093
1094 for (;y1 <= y2; y1++) {
1095 gdImageSetPixel(im, x, y1, col);
1096 }
1097 }
1098 return;
1099 }
1100
1101 /* Bresenham as presented in Foley & Van Dam */
gdImageLine(gdImagePtr im,int x1,int y1,int x2,int y2,int color)1102 void gdImageLine (gdImagePtr im, int x1, int y1, int x2, int y2, int color)
1103 {
1104 int dx, dy, incr1, incr2, d, x, y, xend, yend, xdirflag, ydirflag;
1105 int wid;
1106 int w, wstart;
1107 int thick = im->thick;
1108
1109 if (color == gdAntiAliased) {
1110 /*
1111 gdAntiAliased passed as color: use the much faster, much cheaper
1112 and equally attractive gdImageAALine implementation. That
1113 clips too, so don't clip twice.
1114 */
1115 gdImageAALine(im, x1, y1, x2, y2, im->AA_color);
1116 return;
1117 }
1118
1119 /* 2.0.10: Nick Atty: clip to edges of drawing rectangle, return if no points need to be drawn */
1120 if (!clip_1d(&x1,&y1,&x2,&y2,gdImageSX(im)-1) || !clip_1d(&y1,&x1,&y2,&x2,gdImageSY(im)-1)) {
1121 return;
1122 }
1123
1124 dx = abs (x2 - x1);
1125 dy = abs (y2 - y1);
1126
1127 if (dx == 0) {
1128 gdImageVLine(im, x1, y1, y2, color);
1129 return;
1130 } else if (dy == 0) {
1131 gdImageHLine(im, y1, x1, x2, color);
1132 return;
1133 }
1134
1135 if (dy <= dx) {
1136 /* More-or-less horizontal. use wid for vertical stroke */
1137 /* Doug Claar: watch out for NaN in atan2 (2.0.5) */
1138 if ((dx == 0) && (dy == 0)) {
1139 wid = 1;
1140 } else {
1141 /* 2.0.12: Michael Schwartz: divide rather than multiply;
1142 TBB: but watch out for /0! */
1143 double ac = cos (atan2 (dy, dx));
1144 if (ac != 0) {
1145 wid = thick / ac;
1146 } else {
1147 wid = 1;
1148 }
1149 if (wid == 0) {
1150 wid = 1;
1151 }
1152 }
1153 d = 2 * dy - dx;
1154 incr1 = 2 * dy;
1155 incr2 = 2 * (dy - dx);
1156 if (x1 > x2) {
1157 x = x2;
1158 y = y2;
1159 ydirflag = (-1);
1160 xend = x1;
1161 } else {
1162 x = x1;
1163 y = y1;
1164 ydirflag = 1;
1165 xend = x2;
1166 }
1167
1168 /* Set up line thickness */
1169 wstart = y - wid / 2;
1170 for (w = wstart; w < wstart + wid; w++) {
1171 gdImageSetPixel(im, x, w, color);
1172 }
1173
1174 if (((y2 - y1) * ydirflag) > 0) {
1175 while (x < xend) {
1176 x++;
1177 if (d < 0) {
1178 d += incr1;
1179 } else {
1180 y++;
1181 d += incr2;
1182 }
1183 wstart = y - wid / 2;
1184 for (w = wstart; w < wstart + wid; w++) {
1185 gdImageSetPixel (im, x, w, color);
1186 }
1187 }
1188 } else {
1189 while (x < xend) {
1190 x++;
1191 if (d < 0) {
1192 d += incr1;
1193 } else {
1194 y--;
1195 d += incr2;
1196 }
1197 wstart = y - wid / 2;
1198 for (w = wstart; w < wstart + wid; w++) {
1199 gdImageSetPixel (im, x, w, color);
1200 }
1201 }
1202 }
1203 } else {
1204 /* More-or-less vertical. use wid for horizontal stroke */
1205 /* 2.0.12: Michael Schwartz: divide rather than multiply;
1206 TBB: but watch out for /0! */
1207 double as = sin (atan2 (dy, dx));
1208 if (as != 0) {
1209 wid = thick / as;
1210 } else {
1211 wid = 1;
1212 }
1213 if (wid == 0) {
1214 wid = 1;
1215 }
1216
1217 d = 2 * dx - dy;
1218 incr1 = 2 * dx;
1219 incr2 = 2 * (dx - dy);
1220 if (y1 > y2) {
1221 y = y2;
1222 x = x2;
1223 yend = y1;
1224 xdirflag = (-1);
1225 } else {
1226 y = y1;
1227 x = x1;
1228 yend = y2;
1229 xdirflag = 1;
1230 }
1231
1232 /* Set up line thickness */
1233 wstart = x - wid / 2;
1234 for (w = wstart; w < wstart + wid; w++) {
1235 gdImageSetPixel (im, w, y, color);
1236 }
1237
1238 if (((x2 - x1) * xdirflag) > 0) {
1239 while (y < yend) {
1240 y++;
1241 if (d < 0) {
1242 d += incr1;
1243 } else {
1244 x++;
1245 d += incr2;
1246 }
1247 wstart = x - wid / 2;
1248 for (w = wstart; w < wstart + wid; w++) {
1249 gdImageSetPixel (im, w, y, color);
1250 }
1251 }
1252 } else {
1253 while (y < yend) {
1254 y++;
1255 if (d < 0) {
1256 d += incr1;
1257 } else {
1258 x--;
1259 d += incr2;
1260 }
1261 wstart = x - wid / 2;
1262 for (w = wstart; w < wstart + wid; w++) {
1263 gdImageSetPixel (im, w, y, color);
1264 }
1265 }
1266 }
1267 }
1268 }
1269
1270
1271 /*
1272 * Added on 2003/12 by Pierre-Alain Joye (pajoye@pearfr.org)
1273 * */
1274 #define BLEND_COLOR(a, nc, c, cc) \
1275 nc = (cc) + (((((c) - (cc)) * (a)) + ((((c) - (cc)) * (a)) >> 8) + 0x80) >> 8);
1276
gdImageSetAAPixelColor(gdImagePtr im,int x,int y,int color,int t)1277 inline static void gdImageSetAAPixelColor(gdImagePtr im, int x, int y, int color, int t)
1278 {
1279 int dr,dg,db,p,r,g,b;
1280 dr = gdTrueColorGetRed(color);
1281 dg = gdTrueColorGetGreen(color);
1282 db = gdTrueColorGetBlue(color);
1283
1284 p = gdImageGetPixel(im,x,y);
1285 r = gdTrueColorGetRed(p);
1286 g = gdTrueColorGetGreen(p);
1287 b = gdTrueColorGetBlue(p);
1288
1289 BLEND_COLOR(t, dr, r, dr);
1290 BLEND_COLOR(t, dg, g, dg);
1291 BLEND_COLOR(t, db, b, db);
1292 im->tpixels[y][x]=gdTrueColorAlpha(dr, dg, db, gdAlphaOpaque);
1293 }
1294
1295 /*
1296 * Added on 2003/12 by Pierre-Alain Joye (pajoye@pearfr.org)
1297 **/
gdImageAALine(gdImagePtr im,int x1,int y1,int x2,int y2,int col)1298 void gdImageAALine (gdImagePtr im, int x1, int y1, int x2, int y2, int col)
1299 {
1300 /* keep them as 32bits */
1301 long x, y, inc, frac;
1302 long dx, dy,tmp;
1303
1304 /* 2.0.10: Nick Atty: clip to edges of drawing rectangle, return if no points need to be drawn */
1305 if (!clip_1d(&x1,&y1,&x2,&y2,gdImageSX(im)-1) || !clip_1d(&y1,&x1,&y2,&x2,gdImageSY(im)-1)) {
1306 return;
1307 }
1308
1309 dx = x2 - x1;
1310 dy = y2 - y1;
1311
1312 if (dx == 0 && dy == 0) {
1313 return;
1314 }
1315 if (abs(dx) > abs(dy)) {
1316 if (dx < 0) {
1317 tmp = x1;
1318 x1 = x2;
1319 x2 = tmp;
1320 tmp = y1;
1321 y1 = y2;
1322 y2 = tmp;
1323 dx = x2 - x1;
1324 dy = y2 - y1;
1325 }
1326 y = y1;
1327 inc = (dy * 65536) / dx;
1328 frac = 0;
1329 for (x = x1; x <= x2; x++) {
1330 gdImageSetAAPixelColor(im, x, y, col, (frac >> 8) & 0xFF);
1331 if (y + 1 < im->sy) {
1332 gdImageSetAAPixelColor(im, x, y + 1, col, (~frac >> 8) & 0xFF);
1333 }
1334 frac += inc;
1335 if (frac >= 65536) {
1336 frac -= 65536;
1337 y++;
1338 } else if (frac < 0) {
1339 frac += 65536;
1340 y--;
1341 }
1342 }
1343 } else {
1344 if (dy < 0) {
1345 tmp = x1;
1346 x1 = x2;
1347 x2 = tmp;
1348 tmp = y1;
1349 y1 = y2;
1350 y2 = tmp;
1351 dx = x2 - x1;
1352 dy = y2 - y1;
1353 }
1354 x = x1;
1355 inc = (dx * 65536) / dy;
1356 frac = 0;
1357 for (y = y1; y <= y2; y++) {
1358 gdImageSetAAPixelColor(im, x, y, col, (frac >> 8) & 0xFF);
1359 if (x + 1 < im->sx) {
1360 gdImageSetAAPixelColor(im, x + 1, y, col, (~frac >> 8) & 0xFF);
1361 }
1362 frac += inc;
1363 if (frac >= 65536) {
1364 frac -= 65536;
1365 x++;
1366 } else if (frac < 0) {
1367 frac += 65536;
1368 x--;
1369 }
1370 }
1371 }
1372 }
1373
1374 static void dashedSet (gdImagePtr im, int x, int y, int color, int *onP, int *dashStepP, int wid, int vert);
1375
gdImageDashedLine(gdImagePtr im,int x1,int y1,int x2,int y2,int color)1376 void gdImageDashedLine (gdImagePtr im, int x1, int y1, int x2, int y2, int color)
1377 {
1378 int dx, dy, incr1, incr2, d, x, y, xend, yend, xdirflag, ydirflag;
1379 int dashStep = 0;
1380 int on = 1;
1381 int wid;
1382 int vert;
1383 int thick = im->thick;
1384
1385 dx = abs(x2 - x1);
1386 dy = abs(y2 - y1);
1387 if (dy <= dx) {
1388 /* More-or-less horizontal. use wid for vertical stroke */
1389 /* 2.0.12: Michael Schwartz: divide rather than multiply;
1390 TBB: but watch out for /0! */
1391 double as = sin(atan2(dy, dx));
1392 if (as != 0) {
1393 wid = thick / as;
1394 } else {
1395 wid = 1;
1396 }
1397 wid = (int)(thick * sin(atan2(dy, dx)));
1398 vert = 1;
1399
1400 d = 2 * dy - dx;
1401 incr1 = 2 * dy;
1402 incr2 = 2 * (dy - dx);
1403 if (x1 > x2) {
1404 x = x2;
1405 y = y2;
1406 ydirflag = (-1);
1407 xend = x1;
1408 } else {
1409 x = x1;
1410 y = y1;
1411 ydirflag = 1;
1412 xend = x2;
1413 }
1414 dashedSet(im, x, y, color, &on, &dashStep, wid, vert);
1415 if (((y2 - y1) * ydirflag) > 0) {
1416 while (x < xend) {
1417 x++;
1418 if (d < 0) {
1419 d += incr1;
1420 } else {
1421 y++;
1422 d += incr2;
1423 }
1424 dashedSet(im, x, y, color, &on, &dashStep, wid, vert);
1425 }
1426 } else {
1427 while (x < xend) {
1428 x++;
1429 if (d < 0) {
1430 d += incr1;
1431 } else {
1432 y--;
1433 d += incr2;
1434 }
1435 dashedSet(im, x, y, color, &on, &dashStep, wid, vert);
1436 }
1437 }
1438 } else {
1439 /* 2.0.12: Michael Schwartz: divide rather than multiply;
1440 TBB: but watch out for /0! */
1441 double as = sin (atan2 (dy, dx));
1442 if (as != 0) {
1443 wid = thick / as;
1444 } else {
1445 wid = 1;
1446 }
1447 vert = 0;
1448
1449 d = 2 * dx - dy;
1450 incr1 = 2 * dx;
1451 incr2 = 2 * (dx - dy);
1452 if (y1 > y2) {
1453 y = y2;
1454 x = x2;
1455 yend = y1;
1456 xdirflag = (-1);
1457 } else {
1458 y = y1;
1459 x = x1;
1460 yend = y2;
1461 xdirflag = 1;
1462 }
1463 dashedSet(im, x, y, color, &on, &dashStep, wid, vert);
1464 if (((x2 - x1) * xdirflag) > 0) {
1465 while (y < yend) {
1466 y++;
1467 if (d < 0) {
1468 d += incr1;
1469 } else {
1470 x++;
1471 d += incr2;
1472 }
1473 dashedSet(im, x, y, color, &on, &dashStep, wid, vert);
1474 }
1475 } else {
1476 while (y < yend) {
1477 y++;
1478 if (d < 0) {
1479 d += incr1;
1480 } else {
1481 x--;
1482 d += incr2;
1483 }
1484 dashedSet(im, x, y, color, &on, &dashStep, wid, vert);
1485 }
1486 }
1487 }
1488 }
1489
dashedSet(gdImagePtr im,int x,int y,int color,int * onP,int * dashStepP,int wid,int vert)1490 static void dashedSet (gdImagePtr im, int x, int y, int color, int *onP, int *dashStepP, int wid, int vert)
1491 {
1492 int dashStep = *dashStepP;
1493 int on = *onP;
1494 int w, wstart;
1495
1496 dashStep++;
1497 if (dashStep == gdDashSize) {
1498 dashStep = 0;
1499 on = !on;
1500 }
1501 if (on) {
1502 if (vert) {
1503 wstart = y - wid / 2;
1504 for (w = wstart; w < wstart + wid; w++) {
1505 gdImageSetPixel(im, x, w, color);
1506 }
1507 } else {
1508 wstart = x - wid / 2;
1509 for (w = wstart; w < wstart + wid; w++) {
1510 gdImageSetPixel(im, w, y, color);
1511 }
1512 }
1513 }
1514 *dashStepP = dashStep;
1515 *onP = on;
1516 }
1517
gdImageChar(gdImagePtr im,gdFontPtr f,int x,int y,int c,int color)1518 void gdImageChar (gdImagePtr im, gdFontPtr f, int x, int y, int c, int color)
1519 {
1520 int cx, cy;
1521 int px, py;
1522 int fline;
1523 cx = 0;
1524 cy = 0;
1525 #ifdef CHARSET_EBCDIC
1526 c = ASC (c);
1527 #endif /*CHARSET_EBCDIC */
1528 if ((c < f->offset) || (c >= (f->offset + f->nchars))) {
1529 return;
1530 }
1531 fline = (c - f->offset) * f->h * f->w;
1532 for (py = y; (py < (y + f->h)); py++) {
1533 for (px = x; (px < (x + f->w)); px++) {
1534 if (f->data[fline + cy * f->w + cx]) {
1535 gdImageSetPixel(im, px, py, color);
1536 }
1537 cx++;
1538 }
1539 cx = 0;
1540 cy++;
1541 }
1542 }
1543
gdImageCharUp(gdImagePtr im,gdFontPtr f,int x,int y,int c,int color)1544 void gdImageCharUp (gdImagePtr im, gdFontPtr f, int x, int y, int c, int color)
1545 {
1546 int cx, cy;
1547 int px, py;
1548 int fline;
1549 cx = 0;
1550 cy = 0;
1551 #ifdef CHARSET_EBCDIC
1552 c = ASC (c);
1553 #endif /*CHARSET_EBCDIC */
1554 if ((c < f->offset) || (c >= (f->offset + f->nchars))) {
1555 return;
1556 }
1557 fline = (c - f->offset) * f->h * f->w;
1558 for (py = y; py > (y - f->w); py--) {
1559 for (px = x; px < (x + f->h); px++) {
1560 if (f->data[fline + cy * f->w + cx]) {
1561 gdImageSetPixel(im, px, py, color);
1562 }
1563 cy++;
1564 }
1565 cy = 0;
1566 cx++;
1567 }
1568 }
1569
gdImageString(gdImagePtr im,gdFontPtr f,int x,int y,unsigned char * s,int color)1570 void gdImageString (gdImagePtr im, gdFontPtr f, int x, int y, unsigned char *s, int color)
1571 {
1572 int i;
1573 int l;
1574 l = strlen ((char *) s);
1575 for (i = 0; (i < l); i++) {
1576 gdImageChar(im, f, x, y, s[i], color);
1577 x += f->w;
1578 }
1579 }
1580
gdImageStringUp(gdImagePtr im,gdFontPtr f,int x,int y,unsigned char * s,int color)1581 void gdImageStringUp (gdImagePtr im, gdFontPtr f, int x, int y, unsigned char *s, int color)
1582 {
1583 int i;
1584 int l;
1585 l = strlen ((char *) s);
1586 for (i = 0; (i < l); i++) {
1587 gdImageCharUp(im, f, x, y, s[i], color);
1588 y -= f->w;
1589 }
1590 }
1591
1592 static int strlen16 (unsigned short *s);
1593
gdImageString16(gdImagePtr im,gdFontPtr f,int x,int y,unsigned short * s,int color)1594 void gdImageString16 (gdImagePtr im, gdFontPtr f, int x, int y, unsigned short *s, int color)
1595 {
1596 int i;
1597 int l;
1598 l = strlen16(s);
1599 for (i = 0; (i < l); i++) {
1600 gdImageChar(im, f, x, y, s[i], color);
1601 x += f->w;
1602 }
1603 }
1604
gdImageStringUp16(gdImagePtr im,gdFontPtr f,int x,int y,unsigned short * s,int color)1605 void gdImageStringUp16 (gdImagePtr im, gdFontPtr f, int x, int y, unsigned short *s, int color)
1606 {
1607 int i;
1608 int l;
1609 l = strlen16(s);
1610 for (i = 0; i < l; i++) {
1611 gdImageCharUp(im, f, x, y, s[i], color);
1612 y -= f->w;
1613 }
1614 }
1615
strlen16(unsigned short * s)1616 static int strlen16 (unsigned short *s)
1617 {
1618 int len = 0;
1619 while (*s) {
1620 s++;
1621 len++;
1622 }
1623 return len;
1624 }
1625
1626 #ifndef HAVE_LSQRT
1627 /* If you don't have a nice square root function for longs, you can use
1628 ** this hack
1629 */
lsqrt(long n)1630 long lsqrt (long n)
1631 {
1632 long result = (long) sqrt ((double) n);
1633 return result;
1634 }
1635 #endif
1636
1637 /* s and e are integers modulo 360 (degrees), with 0 degrees
1638 being the rightmost extreme and degrees changing clockwise.
1639 cx and cy are the center in pixels; w and h are the horizontal
1640 and vertical diameter in pixels. */
1641
gdImageArc(gdImagePtr im,int cx,int cy,int w,int h,int s,int e,int color)1642 void gdImageArc (gdImagePtr im, int cx, int cy, int w, int h, int s, int e, int color)
1643 {
1644 gdImageFilledArc(im, cx, cy, w, h, s, e, color, gdNoFill);
1645 }
1646
gdImageFilledArc(gdImagePtr im,int cx,int cy,int w,int h,int s,int e,int color,int style)1647 void gdImageFilledArc (gdImagePtr im, int cx, int cy, int w, int h, int s, int e, int color, int style)
1648 {
1649 gdPoint pts[363];
1650 int i, pti;
1651 int lx = 0, ly = 0;
1652 int fx = 0, fy = 0;
1653
1654
1655 if ((s % 360) == (e % 360)) {
1656 s = 0; e = 360;
1657 } else {
1658 if (s > 360) {
1659 s = s % 360;
1660 }
1661
1662 if (e > 360) {
1663 e = e % 360;
1664 }
1665
1666 while (s < 0) {
1667 s += 360;
1668 }
1669
1670 while (e < s) {
1671 e += 360;
1672 }
1673 if (s == e) {
1674 s = 0; e = 360;
1675 }
1676 }
1677
1678 for (i = s, pti = 1; i <= e; i++, pti++) {
1679 int x, y;
1680 x = ((long) gdCosT[i % 360] * (long) w / (2 * 1024)) + cx;
1681 y = ((long) gdSinT[i % 360] * (long) h / (2 * 1024)) + cy;
1682 if (i != s) {
1683 if (!(style & gdChord)) {
1684 if (style & gdNoFill) {
1685 gdImageLine(im, lx, ly, x, y, color);
1686 } else {
1687 if (y == ly) {
1688 pti--; /* don't add this point */
1689 if (((i > 270 || i < 90) && x > lx) || ((i > 90 && i < 270) && x < lx)) {
1690 /* replace the old x coord, if increasing on the
1691 right side or decreasing on the left side */
1692 pts[pti].x = x;
1693 }
1694 } else {
1695 pts[pti].x = x;
1696 pts[pti].y = y;
1697 }
1698 }
1699 }
1700 } else {
1701 fx = x;
1702 fy = y;
1703 if (!(style & (gdChord | gdNoFill))) {
1704 pts[0].x = cx;
1705 pts[0].y = cy;
1706 pts[pti].x = x;
1707 pts[pti].y = y;
1708 }
1709 }
1710 lx = x;
1711 ly = y;
1712 }
1713 if (style & gdChord) {
1714 if (style & gdNoFill) {
1715 if (style & gdEdged) {
1716 gdImageLine(im, cx, cy, lx, ly, color);
1717 gdImageLine(im, cx, cy, fx, fy, color);
1718 }
1719 gdImageLine(im, fx, fy, lx, ly, color);
1720 } else {
1721 pts[0].x = fx;
1722 pts[0].y = fy;
1723 pts[1].x = lx;
1724 pts[1].y = ly;
1725 pts[2].x = cx;
1726 pts[2].y = cy;
1727 gdImageFilledPolygon(im, pts, 3, color);
1728 }
1729 } else {
1730 if (style & gdNoFill) {
1731 if (style & gdEdged) {
1732 gdImageLine(im, cx, cy, lx, ly, color);
1733 gdImageLine(im, cx, cy, fx, fy, color);
1734 }
1735 } else {
1736 pts[pti].x = cx;
1737 pts[pti].y = cy;
1738 gdImageFilledPolygon(im, pts, pti+1, color);
1739 }
1740 }
1741 }
1742
gdImageFillToBorder(gdImagePtr im,int x,int y,int border,int color)1743 void gdImageFillToBorder (gdImagePtr im, int x, int y, int border, int color)
1744 {
1745 int lastBorder;
1746 /* Seek left */
1747 int leftLimit = -1, rightLimit;
1748 int i, restoreAlphaBlending = 0;
1749
1750 if (border < 0 || color < 0) {
1751 /* Refuse to fill to a non-solid border */
1752 return;
1753 }
1754
1755 if (!im->trueColor) {
1756 if ((color > (im->colorsTotal - 1)) || (border > (im->colorsTotal - 1)) || (color < 0)) {
1757 return;
1758 }
1759 }
1760
1761 restoreAlphaBlending = im->alphaBlendingFlag;
1762 im->alphaBlendingFlag = 0;
1763
1764 if (x >= im->sx) {
1765 x = im->sx - 1;
1766 } else if (x < 0) {
1767 x = 0;
1768 }
1769 if (y >= im->sy) {
1770 y = im->sy - 1;
1771 } else if (y < 0) {
1772 y = 0;
1773 }
1774
1775 for (i = x; i >= 0; i--) {
1776 if (gdImageGetPixel(im, i, y) == border) {
1777 break;
1778 }
1779 gdImageSetPixel(im, i, y, color);
1780 leftLimit = i;
1781 }
1782 if (leftLimit == -1) {
1783 im->alphaBlendingFlag = restoreAlphaBlending;
1784 return;
1785 }
1786 /* Seek right */
1787 rightLimit = x;
1788 for (i = (x + 1); i < im->sx; i++) {
1789 if (gdImageGetPixel(im, i, y) == border) {
1790 break;
1791 }
1792 gdImageSetPixel(im, i, y, color);
1793 rightLimit = i;
1794 }
1795 /* Look at lines above and below and start paints */
1796 /* Above */
1797 if (y > 0) {
1798 lastBorder = 1;
1799 for (i = leftLimit; i <= rightLimit; i++) {
1800 int c = gdImageGetPixel(im, i, y - 1);
1801 if (lastBorder) {
1802 if ((c != border) && (c != color)) {
1803 gdImageFillToBorder(im, i, y - 1, border, color);
1804 lastBorder = 0;
1805 }
1806 } else if ((c == border) || (c == color)) {
1807 lastBorder = 1;
1808 }
1809 }
1810 }
1811
1812 /* Below */
1813 if (y < ((im->sy) - 1)) {
1814 lastBorder = 1;
1815 for (i = leftLimit; i <= rightLimit; i++) {
1816 int c = gdImageGetPixel(im, i, y + 1);
1817
1818 if (lastBorder) {
1819 if ((c != border) && (c != color)) {
1820 gdImageFillToBorder(im, i, y + 1, border, color);
1821 lastBorder = 0;
1822 }
1823 } else if ((c == border) || (c == color)) {
1824 lastBorder = 1;
1825 }
1826 }
1827 }
1828 im->alphaBlendingFlag = restoreAlphaBlending;
1829 }
1830
1831 /*
1832 * set the pixel at (x,y) and its 4-connected neighbors
1833 * with the same pixel value to the new pixel value nc (new color).
1834 * A 4-connected neighbor: pixel above, below, left, or right of a pixel.
1835 * ideas from comp.graphics discussions.
1836 * For tiled fill, the use of a flag buffer is mandatory. As the tile image can
1837 * contain the same color as the color to fill. To do not bloat normal filling
1838 * code I added a 2nd private function.
1839 */
1840
1841 /* horizontal segment of scan line y */
1842 struct seg {int y, xl, xr, dy;};
1843
1844 /* max depth of stack */
1845 #define FILL_MAX ((int)(im->sy*im->sx)/4)
1846 #define FILL_PUSH(Y, XL, XR, DY) \
1847 if (sp<stack+FILL_MAX && Y+(DY)>=0 && Y+(DY)<wy2) \
1848 {sp->y = Y; sp->xl = XL; sp->xr = XR; sp->dy = DY; sp++;}
1849
1850 #define FILL_POP(Y, XL, XR, DY) \
1851 {sp--; Y = sp->y+(DY = sp->dy); XL = sp->xl; XR = sp->xr;}
1852
1853 static void _gdImageFillTiled(gdImagePtr im, int x, int y, int nc);
1854
gdImageFill(gdImagePtr im,int x,int y,int nc)1855 void gdImageFill(gdImagePtr im, int x, int y, int nc)
1856 {
1857 int l, x1, x2, dy;
1858 int oc; /* old pixel value */
1859 int wx2,wy2;
1860
1861 int alphablending_bak;
1862
1863 /* stack of filled segments */
1864 /* struct seg stack[FILL_MAX],*sp = stack;; */
1865 struct seg *stack = NULL;
1866 struct seg *sp;
1867
1868 if (!im->trueColor && nc > (im->colorsTotal -1)) {
1869 return;
1870 }
1871
1872 alphablending_bak = im->alphaBlendingFlag;
1873 im->alphaBlendingFlag = 0;
1874
1875 if (nc==gdTiled){
1876 _gdImageFillTiled(im,x,y,nc);
1877 im->alphaBlendingFlag = alphablending_bak;
1878 return;
1879 }
1880
1881 wx2=im->sx;wy2=im->sy;
1882 oc = gdImageGetPixel(im, x, y);
1883 if (oc==nc || x<0 || x>wx2 || y<0 || y>wy2) {
1884 im->alphaBlendingFlag = alphablending_bak;
1885 return;
1886 }
1887
1888 /* Do not use the 4 neighbors implementation with
1889 * small images
1890 */
1891 if (im->sx < 4) {
1892 int ix = x, iy = y, c;
1893 do {
1894 do {
1895 c = gdImageGetPixel(im, ix, iy);
1896 if (c != oc) {
1897 goto done;
1898 }
1899 gdImageSetPixel(im, ix, iy, nc);
1900 } while(ix++ < (im->sx -1));
1901 ix = x;
1902 } while(iy++ < (im->sy -1));
1903 goto done;
1904 }
1905
1906 stack = (struct seg *)safe_emalloc(sizeof(struct seg), ((int)(im->sy*im->sx)/4), 1);
1907 sp = stack;
1908
1909 /* required! */
1910 FILL_PUSH(y,x,x,1);
1911 /* seed segment (popped 1st) */
1912 FILL_PUSH(y+1, x, x, -1);
1913 while (sp>stack) {
1914 FILL_POP(y, x1, x2, dy);
1915
1916 for (x=x1; x>=0 && gdImageGetPixel(im,x, y)==oc; x--) {
1917 gdImageSetPixel(im,x, y, nc);
1918 }
1919 if (x>=x1) {
1920 goto skip;
1921 }
1922 l = x+1;
1923
1924 /* leak on left? */
1925 if (l<x1) {
1926 FILL_PUSH(y, l, x1-1, -dy);
1927 }
1928 x = x1+1;
1929 do {
1930 for (; x<=wx2 && gdImageGetPixel(im,x, y)==oc; x++) {
1931 gdImageSetPixel(im, x, y, nc);
1932 }
1933 FILL_PUSH(y, l, x-1, dy);
1934 /* leak on right? */
1935 if (x>x2+1) {
1936 FILL_PUSH(y, x2+1, x-1, -dy);
1937 }
1938 skip: for (x++; x<=x2 && (gdImageGetPixel(im, x, y)!=oc); x++);
1939
1940 l = x;
1941 } while (x<=x2);
1942 }
1943
1944 efree(stack);
1945
1946 done:
1947 im->alphaBlendingFlag = alphablending_bak;
1948 }
1949
_gdImageFillTiled(gdImagePtr im,int x,int y,int nc)1950 static void _gdImageFillTiled(gdImagePtr im, int x, int y, int nc)
1951 {
1952 int i, l, x1, x2, dy;
1953 int oc; /* old pixel value */
1954 int wx2,wy2;
1955 /* stack of filled segments */
1956 struct seg *stack;
1957 struct seg *sp;
1958 char **pts;
1959
1960 if (!im->tile) {
1961 return;
1962 }
1963
1964 wx2=im->sx;wy2=im->sy;
1965
1966 nc = gdImageTileGet(im,x,y);
1967
1968 pts = (char **) ecalloc(im->sy + 1, sizeof(char *));
1969 for (i = 0; i < im->sy + 1; i++) {
1970 pts[i] = (char *) ecalloc(im->sx + 1, sizeof(char));
1971 }
1972
1973 stack = (struct seg *)safe_emalloc(sizeof(struct seg), ((int)(im->sy*im->sx)/4), 1);
1974 sp = stack;
1975
1976 oc = gdImageGetPixel(im, x, y);
1977
1978 /* required! */
1979 FILL_PUSH(y,x,x,1);
1980 /* seed segment (popped 1st) */
1981 FILL_PUSH(y+1, x, x, -1);
1982 while (sp>stack) {
1983 FILL_POP(y, x1, x2, dy);
1984 for (x=x1; x>=0 && (!pts[y][x] && gdImageGetPixel(im,x,y)==oc); x--) {
1985 nc = gdImageTileGet(im,x,y);
1986 pts[y][x] = 1;
1987 gdImageSetPixel(im,x, y, nc);
1988 }
1989 if (x>=x1) {
1990 goto skip;
1991 }
1992 l = x+1;
1993
1994 /* leak on left? */
1995 if (l<x1) {
1996 FILL_PUSH(y, l, x1-1, -dy);
1997 }
1998 x = x1+1;
1999 do {
2000 for(; x<wx2 && (!pts[y][x] && gdImageGetPixel(im,x, y)==oc); x++) {
2001 nc = gdImageTileGet(im,x,y);
2002 pts[y][x] = 1;
2003 gdImageSetPixel(im, x, y, nc);
2004 }
2005 FILL_PUSH(y, l, x-1, dy);
2006 /* leak on right? */
2007 if (x>x2+1) {
2008 FILL_PUSH(y, x2+1, x-1, -dy);
2009 }
2010 skip: for(x++; x<=x2 && (pts[y][x] || gdImageGetPixel(im,x, y)!=oc); x++);
2011 l = x;
2012 } while (x<=x2);
2013 }
2014
2015 for(i = 0; i < im->sy + 1; i++) {
2016 efree(pts[i]);
2017 }
2018
2019 efree(pts);
2020 efree(stack);
2021 }
2022
2023
2024
gdImageRectangle(gdImagePtr im,int x1,int y1,int x2,int y2,int color)2025 void gdImageRectangle (gdImagePtr im, int x1, int y1, int x2, int y2, int color)
2026 {
2027 int x1h = x1, x1v = x1, y1h = y1, y1v = y1, x2h = x2, x2v = x2, y2h = y2, y2v = y2;
2028 int thick = im->thick;
2029 int t;
2030
2031 if (x1 == x2 && y1 == y2 && thick == 1) {
2032 gdImageSetPixel(im, x1, y1, color);
2033 return;
2034 }
2035
2036 if (y2 < y1) {
2037 t=y1;
2038 y1 = y2;
2039 y2 = t;
2040 }
2041
2042 if (x2 < x1) {
2043 t = x1;
2044 x1 = x2;
2045 x2 = t;
2046 }
2047
2048 x1h = x1; x1v = x1; y1h = y1; y1v = y1; x2h = x2; x2v = x2; y2h = y2; y2v = y2;
2049 if (thick > 1) {
2050 int cx, cy, x1ul, y1ul, x2lr, y2lr;
2051 int half = thick >> 1;
2052
2053 x1ul = x1 - half;
2054 y1ul = y1 - half;
2055
2056 x2lr = x2 + half;
2057 y2lr = y2 + half;
2058
2059 cy = y1ul + thick;
2060 while (cy-- > y1ul) {
2061 cx = x1ul - 1;
2062 while (cx++ < x2lr) {
2063 gdImageSetPixel(im, cx, cy, color);
2064 }
2065 }
2066
2067 cy = y2lr - thick;
2068 while (cy++ < y2lr) {
2069 cx = x1ul - 1;
2070 while (cx++ < x2lr) {
2071 gdImageSetPixel(im, cx, cy, color);
2072 }
2073 }
2074
2075 cy = y1ul + thick - 1;
2076 while (cy++ < y2lr -thick) {
2077 cx = x1ul - 1;
2078 while (cx++ < x1ul + thick) {
2079 gdImageSetPixel(im, cx, cy, color);
2080 }
2081 }
2082
2083 cy = y1ul + thick - 1;
2084 while (cy++ < y2lr -thick) {
2085 cx = x2lr - thick - 1;
2086 while (cx++ < x2lr) {
2087 gdImageSetPixel(im, cx, cy, color);
2088 }
2089 }
2090
2091 return;
2092 } else {
2093 if (x1 == x2 || y1 == y2) {
2094 gdImageLine(im, x1, y1, x2, y2, color);
2095 } else {
2096 y1v = y1h + 1;
2097 y2v = y2h - 1;
2098 gdImageLine(im, x1h, y1h, x2h, y1h, color);
2099 gdImageLine(im, x1h, y2h, x2h, y2h, color);
2100 gdImageLine(im, x1v, y1v, x1v, y2v, color);
2101 gdImageLine(im, x2v, y1v, x2v, y2v, color);
2102 }
2103 }
2104 }
2105
_gdImageFilledHRectangle(gdImagePtr im,int x1,int y1,int x2,int y2,int color)2106 static void _gdImageFilledHRectangle (gdImagePtr im, int x1, int y1, int x2, int y2, int color)
2107 {
2108 int x, y;
2109
2110 if (x1 == x2 && y1 == y2) {
2111 gdImageSetPixel(im, x1, y1, color);
2112 return;
2113 }
2114
2115 if (x1 > x2) {
2116 x = x1;
2117 x1 = x2;
2118 x2 = x;
2119 }
2120
2121 if (y1 > y2) {
2122 y = y1;
2123 y1 = y2;
2124 y2 = y;
2125 }
2126
2127 if (x1 < 0) {
2128 x1 = 0;
2129 }
2130
2131 if (x2 >= gdImageSX(im)) {
2132 x2 = gdImageSX(im) - 1;
2133 }
2134
2135 if (y1 < 0) {
2136 y1 = 0;
2137 }
2138
2139 if (y2 >= gdImageSY(im)) {
2140 y2 = gdImageSY(im) - 1;
2141 }
2142
2143 for (x = x1; (x <= x2); x++) {
2144 for (y = y1; (y <= y2); y++) {
2145 gdImageSetPixel (im, x, y, color);
2146 }
2147 }
2148 }
2149
_gdImageFilledVRectangle(gdImagePtr im,int x1,int y1,int x2,int y2,int color)2150 static void _gdImageFilledVRectangle (gdImagePtr im, int x1, int y1, int x2, int y2, int color)
2151 {
2152 int x, y;
2153
2154 if (x1 == x2 && y1 == y2) {
2155 gdImageSetPixel(im, x1, y1, color);
2156 return;
2157 }
2158
2159 if (x1 > x2) {
2160 x = x1;
2161 x1 = x2;
2162 x2 = x;
2163 }
2164
2165 if (y1 > y2) {
2166 y = y1;
2167 y1 = y2;
2168 y2 = y;
2169 }
2170
2171 if (x1 < 0) {
2172 x1 = 0;
2173 }
2174
2175 if (x2 >= gdImageSX(im)) {
2176 x2 = gdImageSX(im) - 1;
2177 }
2178
2179 if (y1 < 0) {
2180 y1 = 0;
2181 }
2182
2183 if (y2 >= gdImageSY(im)) {
2184 y2 = gdImageSY(im) - 1;
2185 }
2186
2187 for (y = y1; (y <= y2); y++) {
2188 for (x = x1; (x <= x2); x++) {
2189 gdImageSetPixel (im, x, y, color);
2190 }
2191 }
2192 }
2193
gdImageFilledRectangle(gdImagePtr im,int x1,int y1,int x2,int y2,int color)2194 void gdImageFilledRectangle (gdImagePtr im, int x1, int y1, int x2, int y2, int color)
2195 {
2196 _gdImageFilledVRectangle(im, x1, y1, x2, y2, color);
2197 }
2198
gdImageCopy(gdImagePtr dst,gdImagePtr src,int dstX,int dstY,int srcX,int srcY,int w,int h)2199 void gdImageCopy (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int w, int h)
2200 {
2201 int c;
2202 int x, y;
2203 int tox, toy;
2204 int i;
2205 int colorMap[gdMaxColors];
2206
2207 if (dst->trueColor) {
2208 /* 2.0: much easier when the destination is truecolor. */
2209 /* 2.0.10: needs a transparent-index check that is still valid if
2210 * the source is not truecolor. Thanks to Frank Warmerdam.
2211 */
2212
2213 if (src->trueColor) {
2214 for (y = 0; (y < h); y++) {
2215 for (x = 0; (x < w); x++) {
2216 int c = gdImageGetTrueColorPixel (src, srcX + x, srcY + y);
2217 if (c != src->transparent) {
2218 gdImageSetPixel (dst, dstX + x, dstY + y, c);
2219 }
2220 }
2221 }
2222 } else {
2223 /* source is palette based */
2224 for (y = 0; (y < h); y++) {
2225 for (x = 0; (x < w); x++) {
2226 int c = gdImageGetPixel (src, srcX + x, srcY + y);
2227 if (c != src->transparent) {
2228 gdImageSetPixel(dst, dstX + x, dstY + y, gdTrueColorAlpha(src->red[c], src->green[c], src->blue[c], src->alpha[c]));
2229 }
2230 }
2231 }
2232 }
2233 return;
2234 }
2235
2236 /* Palette based to palette based */
2237 for (i = 0; i < gdMaxColors; i++) {
2238 colorMap[i] = (-1);
2239 }
2240 toy = dstY;
2241 for (y = srcY; y < (srcY + h); y++) {
2242 tox = dstX;
2243 for (x = srcX; x < (srcX + w); x++) {
2244 int nc;
2245 int mapTo;
2246 c = gdImageGetPixel (src, x, y);
2247 /* Added 7/24/95: support transparent copies */
2248 if (gdImageGetTransparent (src) == c) {
2249 tox++;
2250 continue;
2251 }
2252 /* Have we established a mapping for this color? */
2253 if (src->trueColor) {
2254 /* 2.05: remap to the palette available in the destination image. This is slow and
2255 * works badly, but it beats crashing! Thanks to Padhrig McCarthy.
2256 */
2257 mapTo = gdImageColorResolveAlpha (dst, gdTrueColorGetRed (c), gdTrueColorGetGreen (c), gdTrueColorGetBlue (c), gdTrueColorGetAlpha (c));
2258 } else if (colorMap[c] == (-1)) {
2259 /* If it's the same image, mapping is trivial */
2260 if (dst == src) {
2261 nc = c;
2262 } else {
2263 /* Get best match possible. This function never returns error. */
2264 nc = gdImageColorResolveAlpha (dst, src->red[c], src->green[c], src->blue[c], src->alpha[c]);
2265 }
2266 colorMap[c] = nc;
2267 mapTo = colorMap[c];
2268 } else {
2269 mapTo = colorMap[c];
2270 }
2271 gdImageSetPixel (dst, tox, toy, mapTo);
2272 tox++;
2273 }
2274 toy++;
2275 }
2276 }
2277
2278 /* This function is a substitute for real alpha channel operations,
2279 so it doesn't pay attention to the alpha channel. */
gdImageCopyMerge(gdImagePtr dst,gdImagePtr src,int dstX,int dstY,int srcX,int srcY,int w,int h,int pct)2280 void gdImageCopyMerge (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int w, int h, int pct)
2281 {
2282 int c, dc;
2283 int x, y;
2284 int tox, toy;
2285 int ncR, ncG, ncB;
2286 toy = dstY;
2287
2288 for (y = srcY; y < (srcY + h); y++) {
2289 tox = dstX;
2290 for (x = srcX; x < (srcX + w); x++) {
2291 int nc;
2292 c = gdImageGetPixel(src, x, y);
2293 /* Added 7/24/95: support transparent copies */
2294 if (gdImageGetTransparent(src) == c) {
2295 tox++;
2296 continue;
2297 }
2298 /* If it's the same image, mapping is trivial */
2299 if (dst == src) {
2300 nc = c;
2301 } else {
2302 dc = gdImageGetPixel(dst, tox, toy);
2303
2304 ncR = (int)(gdImageRed (src, c) * (pct / 100.0) + gdImageRed (dst, dc) * ((100 - pct) / 100.0));
2305 ncG = (int)(gdImageGreen (src, c) * (pct / 100.0) + gdImageGreen (dst, dc) * ((100 - pct) / 100.0));
2306 ncB = (int)(gdImageBlue (src, c) * (pct / 100.0) + gdImageBlue (dst, dc) * ((100 - pct) / 100.0));
2307
2308 /* Find a reasonable color */
2309 nc = gdImageColorResolve (dst, ncR, ncG, ncB);
2310 }
2311 gdImageSetPixel (dst, tox, toy, nc);
2312 tox++;
2313 }
2314 toy++;
2315 }
2316 }
2317
2318 /* This function is a substitute for real alpha channel operations,
2319 so it doesn't pay attention to the alpha channel. */
gdImageCopyMergeGray(gdImagePtr dst,gdImagePtr src,int dstX,int dstY,int srcX,int srcY,int w,int h,int pct)2320 void gdImageCopyMergeGray (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int w, int h, int pct)
2321 {
2322 int c, dc;
2323 int x, y;
2324 int tox, toy;
2325 int ncR, ncG, ncB;
2326 float g;
2327 toy = dstY;
2328
2329 for (y = srcY; (y < (srcY + h)); y++) {
2330 tox = dstX;
2331 for (x = srcX; (x < (srcX + w)); x++) {
2332 int nc;
2333 c = gdImageGetPixel (src, x, y);
2334 /* Added 7/24/95: support transparent copies */
2335 if (gdImageGetTransparent(src) == c) {
2336 tox++;
2337 continue;
2338 }
2339
2340 /*
2341 * If it's the same image, mapping is NOT trivial since we
2342 * merge with greyscale target, but if pct is 100, the grey
2343 * value is not used, so it becomes trivial. pjw 2.0.12.
2344 */
2345 if (dst == src && pct == 100) {
2346 nc = c;
2347 } else {
2348 dc = gdImageGetPixel(dst, tox, toy);
2349 g = (0.29900f * gdImageRed(dst, dc)) + (0.58700f * gdImageGreen(dst, dc)) + (0.11400f * gdImageBlue(dst, dc));
2350
2351 ncR = (int)(gdImageRed (src, c) * (pct / 100.0f) + g * ((100 - pct) / 100.0));
2352 ncG = (int)(gdImageGreen (src, c) * (pct / 100.0f) + g * ((100 - pct) / 100.0));
2353 ncB = (int)(gdImageBlue (src, c) * (pct / 100.0f) + g * ((100 - pct) / 100.0));
2354
2355
2356 /* First look for an exact match */
2357 nc = gdImageColorExact(dst, ncR, ncG, ncB);
2358 if (nc == (-1)) {
2359 /* No, so try to allocate it */
2360 nc = gdImageColorAllocate(dst, ncR, ncG, ncB);
2361 /* If we're out of colors, go for the closest color */
2362 if (nc == (-1)) {
2363 nc = gdImageColorClosest(dst, ncR, ncG, ncB);
2364 }
2365 }
2366 }
2367 gdImageSetPixel(dst, tox, toy, nc);
2368 tox++;
2369 }
2370 toy++;
2371 }
2372 }
2373
gdImageCopyResized(gdImagePtr dst,gdImagePtr src,int dstX,int dstY,int srcX,int srcY,int dstW,int dstH,int srcW,int srcH)2374 void gdImageCopyResized (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int dstW, int dstH, int srcW, int srcH)
2375 {
2376 int c;
2377 int x, y;
2378 int tox, toy;
2379 int ydest;
2380 int i;
2381 int colorMap[gdMaxColors];
2382 /* Stretch vectors */
2383 int *stx, *sty;
2384
2385 if (overflow2(sizeof(int), srcW)) {
2386 return;
2387 }
2388 if (overflow2(sizeof(int), srcH)) {
2389 return;
2390 }
2391
2392 stx = (int *) gdMalloc (sizeof (int) * srcW);
2393 sty = (int *) gdMalloc (sizeof (int) * srcH);
2394
2395 /* Fixed by Mao Morimoto 2.0.16 */
2396 for (i = 0; (i < srcW); i++) {
2397 stx[i] = dstW * (i+1) / srcW - dstW * i / srcW ;
2398 }
2399 for (i = 0; (i < srcH); i++) {
2400 sty[i] = dstH * (i+1) / srcH - dstH * i / srcH ;
2401 }
2402 for (i = 0; (i < gdMaxColors); i++) {
2403 colorMap[i] = (-1);
2404 }
2405 toy = dstY;
2406 for (y = srcY; (y < (srcY + srcH)); y++) {
2407 for (ydest = 0; (ydest < sty[y - srcY]); ydest++) {
2408 tox = dstX;
2409 for (x = srcX; (x < (srcX + srcW)); x++) {
2410 int nc = 0;
2411 int mapTo;
2412 if (!stx[x - srcX]) {
2413 continue;
2414 }
2415 if (dst->trueColor) {
2416 /* 2.0.9: Thorben Kundinger: Maybe the source image is not a truecolor image */
2417 if (!src->trueColor) {
2418 int tmp = gdImageGetPixel (src, x, y);
2419 mapTo = gdImageGetTrueColorPixel (src, x, y);
2420 if (gdImageGetTransparent (src) == tmp) {
2421 /* 2.0.21, TK: not tox++ */
2422 tox += stx[x - srcX];
2423 continue;
2424 }
2425 } else {
2426 /* TK: old code follows */
2427 mapTo = gdImageGetTrueColorPixel (src, x, y);
2428 /* Added 7/24/95: support transparent copies */
2429 if (gdImageGetTransparent (src) == mapTo) {
2430 /* 2.0.21, TK: not tox++ */
2431 tox += stx[x - srcX];
2432 continue;
2433 }
2434 }
2435 } else {
2436 c = gdImageGetPixel (src, x, y);
2437 /* Added 7/24/95: support transparent copies */
2438 if (gdImageGetTransparent (src) == c) {
2439 tox += stx[x - srcX];
2440 continue;
2441 }
2442 if (src->trueColor) {
2443 /* Remap to the palette available in the destination image. This is slow and works badly. */
2444 mapTo = gdImageColorResolveAlpha(dst, gdTrueColorGetRed(c),
2445 gdTrueColorGetGreen(c),
2446 gdTrueColorGetBlue(c),
2447 gdTrueColorGetAlpha (c));
2448 } else {
2449 /* Have we established a mapping for this color? */
2450 if (colorMap[c] == (-1)) {
2451 /* If it's the same image, mapping is trivial */
2452 if (dst == src) {
2453 nc = c;
2454 } else {
2455 /* Find or create the best match */
2456 /* 2.0.5: can't use gdTrueColorGetRed, etc with palette */
2457 nc = gdImageColorResolveAlpha(dst, gdImageRed(src, c),
2458 gdImageGreen(src, c),
2459 gdImageBlue(src, c),
2460 gdImageAlpha(src, c));
2461 }
2462 colorMap[c] = nc;
2463 }
2464 mapTo = colorMap[c];
2465 }
2466 }
2467 for (i = 0; (i < stx[x - srcX]); i++) {
2468 gdImageSetPixel (dst, tox, toy, mapTo);
2469 tox++;
2470 }
2471 }
2472 toy++;
2473 }
2474 }
2475 gdFree (stx);
2476 gdFree (sty);
2477 }
2478
2479 /* When gd 1.x was first created, floating point was to be avoided.
2480 These days it is often faster than table lookups or integer
2481 arithmetic. The routine below is shamelessly, gloriously
2482 floating point. TBB */
2483
gdImageCopyResampled(gdImagePtr dst,gdImagePtr src,int dstX,int dstY,int srcX,int srcY,int dstW,int dstH,int srcW,int srcH)2484 void gdImageCopyResampled (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int dstW, int dstH, int srcW, int srcH)
2485 {
2486 int x, y;
2487 double sy1, sy2, sx1, sx2;
2488
2489 if (!dst->trueColor) {
2490 gdImageCopyResized (dst, src, dstX, dstY, srcX, srcY, dstW, dstH, srcW, srcH);
2491 return;
2492 }
2493 for (y = dstY; (y < dstY + dstH); y++) {
2494 sy1 = ((double) y - (double) dstY) * (double) srcH / (double) dstH;
2495 sy2 = ((double) (y + 1) - (double) dstY) * (double) srcH / (double) dstH;
2496 for (x = dstX; (x < dstX + dstW); x++) {
2497 double sx, sy;
2498 double spixels = 0;
2499 double red = 0.0, green = 0.0, blue = 0.0, alpha = 0.0;
2500 double alpha_factor, alpha_sum = 0.0, contrib_sum = 0.0;
2501 sx1 = ((double) x - (double) dstX) * (double) srcW / dstW;
2502 sx2 = ((double) (x + 1) - (double) dstX) * (double) srcW / dstW;
2503 sy = sy1;
2504 do {
2505 double yportion;
2506 if (floor_cast(sy) == floor_cast(sy1)) {
2507 yportion = 1.0f - (sy - floor_cast(sy));
2508 if (yportion > sy2 - sy1) {
2509 yportion = sy2 - sy1;
2510 }
2511 sy = floor_cast(sy);
2512 } else if (sy == floorf(sy2)) {
2513 yportion = sy2 - floor_cast(sy2);
2514 } else {
2515 yportion = 1.0f;
2516 }
2517 sx = sx1;
2518 do {
2519 double xportion;
2520 double pcontribution;
2521 int p;
2522 if (floorf(sx) == floor_cast(sx1)) {
2523 xportion = 1.0f - (sx - floor_cast(sx));
2524 if (xportion > sx2 - sx1) {
2525 xportion = sx2 - sx1;
2526 }
2527 sx = floor_cast(sx);
2528 } else if (sx == floorf(sx2)) {
2529 xportion = sx2 - floor_cast(sx2);
2530 } else {
2531 xportion = 1.0f;
2532 }
2533 pcontribution = xportion * yportion;
2534 p = gdImageGetTrueColorPixel(src, (int) sx + srcX, (int) sy + srcY);
2535
2536 alpha_factor = ((gdAlphaMax - gdTrueColorGetAlpha(p))) * pcontribution;
2537 red += gdTrueColorGetRed (p) * alpha_factor;
2538 green += gdTrueColorGetGreen (p) * alpha_factor;
2539 blue += gdTrueColorGetBlue (p) * alpha_factor;
2540 alpha += gdTrueColorGetAlpha (p) * pcontribution;
2541 alpha_sum += alpha_factor;
2542 contrib_sum += pcontribution;
2543 spixels += xportion * yportion;
2544 sx += 1.0f;
2545 }
2546 while (sx < sx2);
2547
2548 sy += 1.0f;
2549 }
2550
2551 while (sy < sy2);
2552
2553 if (spixels != 0.0f) {
2554 red /= spixels;
2555 green /= spixels;
2556 blue /= spixels;
2557 alpha /= spixels;
2558 alpha += 0.5;
2559 }
2560 if ( alpha_sum != 0.0f) {
2561 if( contrib_sum != 0.0f) {
2562 alpha_sum /= contrib_sum;
2563 }
2564 red /= alpha_sum;
2565 green /= alpha_sum;
2566 blue /= alpha_sum;
2567 }
2568 /* Clamping to allow for rounding errors above */
2569 if (red > 255.0f) {
2570 red = 255.0f;
2571 }
2572 if (green > 255.0f) {
2573 green = 255.0f;
2574 }
2575 if (blue > 255.0f) {
2576 blue = 255.0f;
2577 }
2578 if (alpha > gdAlphaMax) {
2579 alpha = gdAlphaMax;
2580 }
2581 gdImageSetPixel(dst, x, y, gdTrueColorAlpha ((int) red, (int) green, (int) blue, (int) alpha));
2582 }
2583 }
2584 }
2585
gdImagePolygon(gdImagePtr im,gdPointPtr p,int n,int c)2586 void gdImagePolygon (gdImagePtr im, gdPointPtr p, int n, int c)
2587 {
2588 int i;
2589 int lx, ly;
2590 typedef void (*image_line)(gdImagePtr im, int x1, int y1, int x2, int y2, int color);
2591 image_line draw_line;
2592
2593 if (n <= 0) {
2594 return;
2595 }
2596
2597 /* Let it be known that we are drawing a polygon so that the opacity
2598 * mask doesn't get cleared after each line.
2599 */
2600 if (c == gdAntiAliased) {
2601 im->AA_polygon = 1;
2602 }
2603
2604 if ( im->antialias) {
2605 draw_line = gdImageAALine;
2606 } else {
2607 draw_line = gdImageLine;
2608 }
2609 lx = p->x;
2610 ly = p->y;
2611 draw_line(im, lx, ly, p[n - 1].x, p[n - 1].y, c);
2612 for (i = 1; i < n; i++) {
2613 p++;
2614 draw_line(im, lx, ly, p->x, p->y, c);
2615 lx = p->x;
2616 ly = p->y;
2617 }
2618
2619 if (c == gdAntiAliased) {
2620 im->AA_polygon = 0;
2621 gdImageAABlend(im);
2622 }
2623 }
2624
2625 int gdCompareInt (const void *a, const void *b);
2626
2627 /* THANKS to Kirsten Schulz for the polygon fixes! */
2628
2629 /* The intersection finding technique of this code could be improved
2630 * by remembering the previous intertersection, and by using the slope.
2631 * That could help to adjust intersections to produce a nice
2632 * interior_extrema.
2633 */
2634
gdImageFilledPolygon(gdImagePtr im,gdPointPtr p,int n,int c)2635 void gdImageFilledPolygon (gdImagePtr im, gdPointPtr p, int n, int c)
2636 {
2637 int i;
2638 int y;
2639 int miny, maxy, pmaxy;
2640 int x1, y1;
2641 int x2, y2;
2642 int ind1, ind2;
2643 int ints;
2644 int fill_color;
2645
2646 if (n <= 0) {
2647 return;
2648 }
2649
2650 if (overflow2(sizeof(int), n)) {
2651 return;
2652 }
2653
2654 if (c == gdAntiAliased) {
2655 fill_color = im->AA_color;
2656 } else {
2657 fill_color = c;
2658 }
2659
2660 if (!im->polyAllocated) {
2661 im->polyInts = (int *) gdMalloc(sizeof(int) * n);
2662 im->polyAllocated = n;
2663 }
2664 if (im->polyAllocated < n) {
2665 while (im->polyAllocated < n) {
2666 im->polyAllocated *= 2;
2667 }
2668 if (overflow2(sizeof(int), im->polyAllocated)) {
2669 return;
2670 }
2671 im->polyInts = (int *) gdRealloc(im->polyInts, sizeof(int) * im->polyAllocated);
2672 }
2673 miny = p[0].y;
2674 maxy = p[0].y;
2675 for (i = 1; i < n; i++) {
2676 if (p[i].y < miny) {
2677 miny = p[i].y;
2678 }
2679 if (p[i].y > maxy) {
2680 maxy = p[i].y;
2681 }
2682 }
2683 /* necessary special case: horizontal line */
2684 if (n > 1 && miny == maxy) {
2685 x1 = x2 = p[0].x;
2686 for (i = 1; (i < n); i++) {
2687 if (p[i].x < x1) {
2688 x1 = p[i].x;
2689 } else if (p[i].x > x2) {
2690 x2 = p[i].x;
2691 }
2692 }
2693 gdImageLine(im, x1, miny, x2, miny, c);
2694 return;
2695 }
2696 pmaxy = maxy;
2697 /* 2.0.16: Optimization by Ilia Chipitsine -- don't waste time offscreen */
2698 if (miny < 0) {
2699 miny = 0;
2700 }
2701 if (maxy >= gdImageSY(im)) {
2702 maxy = gdImageSY(im) - 1;
2703 }
2704
2705 /* Fix in 1.3: count a vertex only once */
2706 for (y = miny; y <= maxy; y++) {
2707 /*1.4 int interLast = 0; */
2708 /* int dirLast = 0; */
2709 /* int interFirst = 1; */
2710 ints = 0;
2711 for (i = 0; i < n; i++) {
2712 if (!i) {
2713 ind1 = n - 1;
2714 ind2 = 0;
2715 } else {
2716 ind1 = i - 1;
2717 ind2 = i;
2718 }
2719 y1 = p[ind1].y;
2720 y2 = p[ind2].y;
2721 if (y1 < y2) {
2722 x1 = p[ind1].x;
2723 x2 = p[ind2].x;
2724 } else if (y1 > y2) {
2725 y2 = p[ind1].y;
2726 y1 = p[ind2].y;
2727 x2 = p[ind1].x;
2728 x1 = p[ind2].x;
2729 } else {
2730 continue;
2731 }
2732 /* Do the following math as float intermediately, and round to ensure
2733 * that Polygon and FilledPolygon for the same set of points have the
2734 * same footprint.
2735 */
2736 if (y >= y1 && y < y2) {
2737 im->polyInts[ints++] = (float) ((y - y1) * (x2 - x1)) / (float) (y2 - y1) + 0.5 + x1;
2738 } else if (y == pmaxy && y == y2) {
2739 im->polyInts[ints++] = x2;
2740 }
2741 }
2742 qsort(im->polyInts, ints, sizeof(int), gdCompareInt);
2743
2744 for (i = 0; i < ints - 1; i += 2) {
2745 gdImageLine(im, im->polyInts[i], y, im->polyInts[i + 1], y, fill_color);
2746 }
2747 }
2748
2749 /* If we are drawing this AA, then redraw the border with AA lines. */
2750 if (c == gdAntiAliased) {
2751 gdImagePolygon(im, p, n, c);
2752 }
2753 }
2754
gdCompareInt(const void * a,const void * b)2755 int gdCompareInt (const void *a, const void *b)
2756 {
2757 return (*(const int *) a) - (*(const int *) b);
2758 }
2759
gdImageSetStyle(gdImagePtr im,int * style,int noOfPixels)2760 void gdImageSetStyle (gdImagePtr im, int *style, int noOfPixels)
2761 {
2762 if (im->style) {
2763 gdFree(im->style);
2764 }
2765 im->style = (int *) gdMalloc(sizeof(int) * noOfPixels);
2766 memcpy(im->style, style, sizeof(int) * noOfPixels);
2767 im->styleLength = noOfPixels;
2768 im->stylePos = 0;
2769 }
2770
gdImageSetThickness(gdImagePtr im,int thickness)2771 void gdImageSetThickness (gdImagePtr im, int thickness)
2772 {
2773 im->thick = thickness;
2774 }
2775
gdImageSetBrush(gdImagePtr im,gdImagePtr brush)2776 void gdImageSetBrush (gdImagePtr im, gdImagePtr brush)
2777 {
2778 int i;
2779 im->brush = brush;
2780 if (!im->trueColor && !im->brush->trueColor) {
2781 for (i = 0; i < gdImageColorsTotal(brush); i++) {
2782 int index;
2783 index = gdImageColorResolveAlpha(im, gdImageRed(brush, i), gdImageGreen(brush, i), gdImageBlue(brush, i), gdImageAlpha(brush, i));
2784 im->brushColorMap[i] = index;
2785 }
2786 }
2787 }
2788
gdImageSetTile(gdImagePtr im,gdImagePtr tile)2789 void gdImageSetTile (gdImagePtr im, gdImagePtr tile)
2790 {
2791 int i;
2792 im->tile = tile;
2793 if (!im->trueColor && !im->tile->trueColor) {
2794 for (i = 0; i < gdImageColorsTotal(tile); i++) {
2795 int index;
2796 index = gdImageColorResolveAlpha(im, gdImageRed(tile, i), gdImageGreen(tile, i), gdImageBlue(tile, i), gdImageAlpha(tile, i));
2797 im->tileColorMap[i] = index;
2798 }
2799 }
2800 }
2801
gdImageSetAntiAliased(gdImagePtr im,int c)2802 void gdImageSetAntiAliased (gdImagePtr im, int c)
2803 {
2804 im->AA = 1;
2805 im->AA_color = c;
2806 im->AA_dont_blend = -1;
2807 }
2808
gdImageSetAntiAliasedDontBlend(gdImagePtr im,int c,int dont_blend)2809 void gdImageSetAntiAliasedDontBlend (gdImagePtr im, int c, int dont_blend)
2810 {
2811 im->AA = 1;
2812 im->AA_color = c;
2813 im->AA_dont_blend = dont_blend;
2814 }
2815
2816
gdImageInterlace(gdImagePtr im,int interlaceArg)2817 void gdImageInterlace (gdImagePtr im, int interlaceArg)
2818 {
2819 im->interlace = interlaceArg;
2820 }
2821
gdImageCompare(gdImagePtr im1,gdImagePtr im2)2822 int gdImageCompare (gdImagePtr im1, gdImagePtr im2)
2823 {
2824 int x, y;
2825 int p1, p2;
2826 int cmpStatus = 0;
2827 int sx, sy;
2828
2829 if (im1->interlace != im2->interlace) {
2830 cmpStatus |= GD_CMP_INTERLACE;
2831 }
2832
2833 if (im1->transparent != im2->transparent) {
2834 cmpStatus |= GD_CMP_TRANSPARENT;
2835 }
2836
2837 if (im1->trueColor != im2->trueColor) {
2838 cmpStatus |= GD_CMP_TRUECOLOR;
2839 }
2840
2841 sx = im1->sx;
2842 if (im1->sx != im2->sx) {
2843 cmpStatus |= GD_CMP_SIZE_X + GD_CMP_IMAGE;
2844 if (im2->sx < im1->sx) {
2845 sx = im2->sx;
2846 }
2847 }
2848
2849 sy = im1->sy;
2850 if (im1->sy != im2->sy) {
2851 cmpStatus |= GD_CMP_SIZE_Y + GD_CMP_IMAGE;
2852 if (im2->sy < im1->sy) {
2853 sy = im2->sy;
2854 }
2855 }
2856
2857 if (im1->colorsTotal != im2->colorsTotal) {
2858 cmpStatus |= GD_CMP_NUM_COLORS;
2859 }
2860
2861 for (y = 0; y < sy; y++) {
2862 for (x = 0; x < sx; x++) {
2863 p1 = im1->trueColor ? gdImageTrueColorPixel(im1, x, y) : gdImagePalettePixel(im1, x, y);
2864 p2 = im2->trueColor ? gdImageTrueColorPixel(im2, x, y) : gdImagePalettePixel(im2, x, y);
2865
2866 if (gdImageRed(im1, p1) != gdImageRed(im2, p2)) {
2867 cmpStatus |= GD_CMP_COLOR + GD_CMP_IMAGE;
2868 break;
2869 }
2870 if (gdImageGreen(im1, p1) != gdImageGreen(im2, p2)) {
2871 cmpStatus |= GD_CMP_COLOR + GD_CMP_IMAGE;
2872 break;
2873 }
2874 if (gdImageBlue(im1, p1) != gdImageBlue(im2, p2)) {
2875 cmpStatus |= GD_CMP_COLOR + GD_CMP_IMAGE;
2876 break;
2877 }
2878 #if 0
2879 /* Soon we'll add alpha channel to palettes */
2880 if (gdImageAlpha(im1, p1) != gdImageAlpha(im2, p2)) {
2881 cmpStatus |= GD_CMP_COLOR + GD_CMP_IMAGE;
2882 break;
2883 }
2884 #endif
2885 }
2886 if (cmpStatus & GD_CMP_COLOR) {
2887 break;
2888 }
2889 }
2890
2891 return cmpStatus;
2892 }
2893
2894 int
gdAlphaBlendOld(int dst,int src)2895 gdAlphaBlendOld (int dst, int src)
2896 {
2897 /* 2.0.12: TBB: alpha in the destination should be a
2898 * component of the result. Thanks to Frank Warmerdam for
2899 * pointing out the issue.
2900 */
2901 return ((((gdTrueColorGetAlpha (src) *
2902 gdTrueColorGetAlpha (dst)) / gdAlphaMax) << 24) +
2903 ((((gdAlphaTransparent - gdTrueColorGetAlpha (src)) *
2904 gdTrueColorGetRed (src) / gdAlphaMax) +
2905 (gdTrueColorGetAlpha (src) *
2906 gdTrueColorGetRed (dst)) / gdAlphaMax) << 16) +
2907 ((((gdAlphaTransparent - gdTrueColorGetAlpha (src)) *
2908 gdTrueColorGetGreen (src) / gdAlphaMax) +
2909 (gdTrueColorGetAlpha (src) *
2910 gdTrueColorGetGreen (dst)) / gdAlphaMax) << 8) +
2911 (((gdAlphaTransparent - gdTrueColorGetAlpha (src)) *
2912 gdTrueColorGetBlue (src) / gdAlphaMax) +
2913 (gdTrueColorGetAlpha (src) *
2914 gdTrueColorGetBlue (dst)) / gdAlphaMax));
2915 }
2916
gdAlphaBlend(int dst,int src)2917 int gdAlphaBlend (int dst, int src) {
2918 int src_alpha = gdTrueColorGetAlpha(src);
2919 int dst_alpha, alpha, red, green, blue;
2920 int src_weight, dst_weight, tot_weight;
2921
2922 /* -------------------------------------------------------------------- */
2923 /* Simple cases we want to handle fast. */
2924 /* -------------------------------------------------------------------- */
2925 if( src_alpha == gdAlphaOpaque )
2926 return src;
2927
2928 dst_alpha = gdTrueColorGetAlpha(dst);
2929 if( src_alpha == gdAlphaTransparent )
2930 return dst;
2931 if( dst_alpha == gdAlphaTransparent )
2932 return src;
2933
2934 /* -------------------------------------------------------------------- */
2935 /* What will the source and destination alphas be? Note that */
2936 /* the destination weighting is substantially reduced as the */
2937 /* overlay becomes quite opaque. */
2938 /* -------------------------------------------------------------------- */
2939 src_weight = gdAlphaTransparent - src_alpha;
2940 dst_weight = (gdAlphaTransparent - dst_alpha) * src_alpha / gdAlphaMax;
2941 tot_weight = src_weight + dst_weight;
2942
2943 /* -------------------------------------------------------------------- */
2944 /* What red, green and blue result values will we use? */
2945 /* -------------------------------------------------------------------- */
2946 alpha = src_alpha * dst_alpha / gdAlphaMax;
2947
2948 red = (gdTrueColorGetRed(src) * src_weight
2949 + gdTrueColorGetRed(dst) * dst_weight) / tot_weight;
2950 green = (gdTrueColorGetGreen(src) * src_weight
2951 + gdTrueColorGetGreen(dst) * dst_weight) / tot_weight;
2952 blue = (gdTrueColorGetBlue(src) * src_weight
2953 + gdTrueColorGetBlue(dst) * dst_weight) / tot_weight;
2954
2955 /* -------------------------------------------------------------------- */
2956 /* Return merged result. */
2957 /* -------------------------------------------------------------------- */
2958 return ((alpha << 24) + (red << 16) + (green << 8) + blue);
2959
2960 }
2961
gdImageAlphaBlending(gdImagePtr im,int alphaBlendingArg)2962 void gdImageAlphaBlending (gdImagePtr im, int alphaBlendingArg)
2963 {
2964 im->alphaBlendingFlag = alphaBlendingArg;
2965 }
2966
gdImageAntialias(gdImagePtr im,int antialias)2967 void gdImageAntialias (gdImagePtr im, int antialias)
2968 {
2969 if (im->trueColor){
2970 im->antialias = antialias;
2971 }
2972 }
2973
gdImageSaveAlpha(gdImagePtr im,int saveAlphaArg)2974 void gdImageSaveAlpha (gdImagePtr im, int saveAlphaArg)
2975 {
2976 im->saveAlphaFlag = saveAlphaArg;
2977 }
2978
gdLayerOverlay(int dst,int src)2979 static int gdLayerOverlay (int dst, int src)
2980 {
2981 int a1, a2;
2982 a1 = gdAlphaMax - gdTrueColorGetAlpha(dst);
2983 a2 = gdAlphaMax - gdTrueColorGetAlpha(src);
2984 return ( ((gdAlphaMax - a1*a2/gdAlphaMax) << 24) +
2985 (gdAlphaOverlayColor( gdTrueColorGetRed(src), gdTrueColorGetRed(dst), gdRedMax ) << 16) +
2986 (gdAlphaOverlayColor( gdTrueColorGetGreen(src), gdTrueColorGetGreen(dst), gdGreenMax ) << 8) +
2987 (gdAlphaOverlayColor( gdTrueColorGetBlue(src), gdTrueColorGetBlue(dst), gdBlueMax ))
2988 );
2989 }
2990
gdAlphaOverlayColor(int src,int dst,int max)2991 static int gdAlphaOverlayColor (int src, int dst, int max )
2992 {
2993 /* this function implements the algorithm
2994 *
2995 * for dst[rgb] < 0.5,
2996 * c[rgb] = 2.src[rgb].dst[rgb]
2997 * and for dst[rgb] > 0.5,
2998 * c[rgb] = -2.src[rgb].dst[rgb] + 2.dst[rgb] + 2.src[rgb] - 1
2999 *
3000 */
3001
3002 dst = dst << 1;
3003 if( dst > max ) {
3004 /* in the "light" zone */
3005 return dst + (src << 1) - (dst * src / max) - max;
3006 } else {
3007 /* in the "dark" zone */
3008 return dst * src / max;
3009 }
3010 }
3011
gdImageSetClip(gdImagePtr im,int x1,int y1,int x2,int y2)3012 void gdImageSetClip (gdImagePtr im, int x1, int y1, int x2, int y2)
3013 {
3014 if (x1 < 0) {
3015 x1 = 0;
3016 }
3017 if (x1 >= im->sx) {
3018 x1 = im->sx - 1;
3019 }
3020 if (x2 < 0) {
3021 x2 = 0;
3022 }
3023 if (x2 >= im->sx) {
3024 x2 = im->sx - 1;
3025 }
3026 if (y1 < 0) {
3027 y1 = 0;
3028 }
3029 if (y1 >= im->sy) {
3030 y1 = im->sy - 1;
3031 }
3032 if (y2 < 0) {
3033 y2 = 0;
3034 }
3035 if (y2 >= im->sy) {
3036 y2 = im->sy - 1;
3037 }
3038 im->cx1 = x1;
3039 im->cy1 = y1;
3040 im->cx2 = x2;
3041 im->cy2 = y2;
3042 }
3043
gdImageGetClip(gdImagePtr im,int * x1P,int * y1P,int * x2P,int * y2P)3044 void gdImageGetClip (gdImagePtr im, int *x1P, int *y1P, int *x2P, int *y2P)
3045 {
3046 *x1P = im->cx1;
3047 *y1P = im->cy1;
3048 *x2P = im->cx2;
3049 *y2P = im->cy2;
3050 }
3051
3052 /* convert a palette image to true color */
gdImagePaletteToTrueColor(gdImagePtr src)3053 int gdImagePaletteToTrueColor(gdImagePtr src)
3054 {
3055 unsigned int y;
3056 unsigned int yy;
3057
3058 if (src == NULL) {
3059 return 0;
3060 }
3061
3062 if (src->trueColor == 1) {
3063 return 1;
3064 } else {
3065 unsigned int x;
3066 const unsigned int sy = gdImageSY(src);
3067 const unsigned int sx = gdImageSX(src);
3068
3069 src->tpixels = (int **) gdMalloc(sizeof(int *) * sy);
3070 if (src->tpixels == NULL) {
3071 return 0;
3072 }
3073
3074 for (y = 0; y < sy; y++) {
3075 const unsigned char *src_row = src->pixels[y];
3076 int * dst_row;
3077
3078 /* no need to calloc it, we overwrite all pxl anyway */
3079 src->tpixels[y] = (int *) gdMalloc(sx * sizeof(int));
3080 if (src->tpixels[y] == NULL) {
3081 goto clean_on_error;
3082 }
3083
3084 dst_row = src->tpixels[y];
3085 for (x = 0; x < sx; x++) {
3086 const unsigned char c = *(src_row + x);
3087 if (c == src->transparent) {
3088 *(dst_row + x) = gdTrueColorAlpha(0, 0, 0, 127);
3089 } else {
3090 *(dst_row + x) = gdTrueColorAlpha(src->red[c], src->green[c], src->blue[c], src->alpha[c]);
3091 }
3092 }
3093 }
3094 }
3095
3096 /* free old palette buffer (y is sy) */
3097 for (yy = 0; yy < y; yy++) {
3098 gdFree(src->pixels[yy]);
3099 }
3100 gdFree(src->pixels);
3101 src->trueColor = 1;
3102 src->pixels = NULL;
3103 src->alphaBlendingFlag = 0;
3104 src->saveAlphaFlag = 1;
3105
3106 if (src->transparent >= 0) {
3107 const unsigned char c = src->transparent;
3108 src->transparent = gdTrueColorAlpha(src->red[c], src->green[c], src->blue[c], src->alpha[c]);
3109 }
3110
3111 return 1;
3112
3113 clean_on_error:
3114 if (y > 0) {
3115
3116 for (yy = y; yy >= yy - 1; y--) {
3117 gdFree(src->tpixels[y]);
3118 }
3119 gdFree(src->tpixels);
3120 }
3121 return 0;
3122 }
3123
3124