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