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