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(dx) > abs(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, starty, endx, endy;
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 long 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 long 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: for (x++; x<=x2 && (gdImageGetPixel(im, x, y)!=oc); x++);
1998
1999 l = x;
2000 } while (x<=x2);
2001 }
2002
2003 efree(stack);
2004
2005 done:
2006 im->alphaBlendingFlag = alphablending_bak;
2007 }
2008
_gdImageFillTiled(gdImagePtr im,int x,int y,int nc)2009 static void _gdImageFillTiled(gdImagePtr im, int x, int y, int nc)
2010 {
2011 int i, l, x1, x2, dy;
2012 int oc; /* old pixel value */
2013 int wx2,wy2;
2014 /* stack of filled segments */
2015 struct seg *stack;
2016 struct seg *sp;
2017 char **pts;
2018
2019 if (!im->tile) {
2020 return;
2021 }
2022
2023 wx2=im->sx;wy2=im->sy;
2024
2025 nc = gdImageTileGet(im,x,y);
2026
2027 pts = (char **) ecalloc(im->sy + 1, sizeof(char *));
2028 for (i = 0; i < im->sy + 1; i++) {
2029 pts[i] = (char *) ecalloc(im->sx + 1, sizeof(char));
2030 }
2031
2032 stack = (struct seg *)safe_emalloc(sizeof(struct seg), ((int)(im->sy*im->sx)/4), 1);
2033 sp = stack;
2034
2035 oc = gdImageGetPixel(im, x, y);
2036
2037 /* required! */
2038 FILL_PUSH(y,x,x,1);
2039 /* seed segment (popped 1st) */
2040 FILL_PUSH(y+1, x, x, -1);
2041 while (sp>stack) {
2042 FILL_POP(y, x1, x2, dy);
2043 for (x=x1; x>=0 && (!pts[y][x] && gdImageGetPixel(im,x,y)==oc); x--) {
2044 nc = gdImageTileGet(im,x,y);
2045 pts[y][x] = 1;
2046 gdImageSetPixel(im,x, y, nc);
2047 }
2048 if (x>=x1) {
2049 goto skip;
2050 }
2051 l = x+1;
2052
2053 /* leak on left? */
2054 if (l<x1) {
2055 FILL_PUSH(y, l, x1-1, -dy);
2056 }
2057 x = x1+1;
2058 do {
2059 for(; x<wx2 && (!pts[y][x] && gdImageGetPixel(im,x, y)==oc); x++) {
2060 nc = gdImageTileGet(im,x,y);
2061 pts[y][x] = 1;
2062 gdImageSetPixel(im, x, y, nc);
2063 }
2064 FILL_PUSH(y, l, x-1, dy);
2065 /* leak on right? */
2066 if (x>x2+1) {
2067 FILL_PUSH(y, x2+1, x-1, -dy);
2068 }
2069 skip: for(x++; x<=x2 && (pts[y][x] || gdImageGetPixel(im,x, y)!=oc); x++);
2070 l = x;
2071 } while (x<=x2);
2072 }
2073
2074 for(i = 0; i < im->sy + 1; i++) {
2075 efree(pts[i]);
2076 }
2077
2078 efree(pts);
2079 efree(stack);
2080 }
2081
2082
2083
gdImageRectangle(gdImagePtr im,int x1,int y1,int x2,int y2,int color)2084 void gdImageRectangle (gdImagePtr im, int x1, int y1, int x2, int y2, int color)
2085 {
2086 int thick = im->thick;
2087 int t;
2088
2089 if (x1 == x2 && y1 == y2 && thick == 1) {
2090 gdImageSetPixel(im, x1, y1, color);
2091 return;
2092 }
2093
2094 if (y2 < y1) {
2095 t=y1;
2096 y1 = y2;
2097 y2 = t;
2098 }
2099
2100 if (x2 < x1) {
2101 t = x1;
2102 x1 = x2;
2103 x2 = t;
2104 }
2105
2106 if (thick > 1) {
2107 int cx, cy, x1ul, y1ul, x2lr, y2lr;
2108 int half = thick >> 1;
2109
2110 x1ul = x1 - half;
2111 y1ul = y1 - half;
2112
2113 x2lr = x2 + half;
2114 y2lr = y2 + half;
2115
2116 cy = y1ul + thick;
2117 while (cy-- > y1ul) {
2118 cx = x1ul - 1;
2119 while (cx++ < x2lr) {
2120 gdImageSetPixel(im, cx, cy, color);
2121 }
2122 }
2123
2124 cy = y2lr - thick;
2125 while (cy++ < y2lr) {
2126 cx = x1ul - 1;
2127 while (cx++ < x2lr) {
2128 gdImageSetPixel(im, cx, cy, color);
2129 }
2130 }
2131
2132 cy = y1ul + thick - 1;
2133 while (cy++ < y2lr -thick) {
2134 cx = x1ul - 1;
2135 while (cx++ < x1ul + thick) {
2136 gdImageSetPixel(im, cx, cy, color);
2137 }
2138 }
2139
2140 cy = y1ul + thick - 1;
2141 while (cy++ < y2lr -thick) {
2142 cx = x2lr - thick - 1;
2143 while (cx++ < x2lr) {
2144 gdImageSetPixel(im, cx, cy, color);
2145 }
2146 }
2147
2148 return;
2149 } else {
2150 if (x1 == x2 || y1 == y2) {
2151 gdImageLine(im, x1, y1, x2, y2, color);
2152 } else {
2153 gdImageLine(im, x1, y1, x2, y1, color);
2154 gdImageLine(im, x1, y2, x2, y2, color);
2155 gdImageLine(im, x1, y1 + 1, x1, y2 - 1, color);
2156 gdImageLine(im, x2, y1 + 1, x2, y2 - 1, color);
2157 }
2158 }
2159 }
2160
_gdImageFilledHRectangle(gdImagePtr im,int x1,int y1,int x2,int y2,int color)2161 static void _gdImageFilledHRectangle (gdImagePtr im, int x1, int y1, int x2, int y2, int color)
2162 {
2163 int x, y;
2164
2165 if (x1 == x2 && y1 == y2) {
2166 gdImageSetPixel(im, x1, y1, color);
2167 return;
2168 }
2169
2170 if (x1 > x2) {
2171 x = x1;
2172 x1 = x2;
2173 x2 = x;
2174 }
2175
2176 if (y1 > y2) {
2177 y = y1;
2178 y1 = y2;
2179 y2 = y;
2180 }
2181
2182 if (x1 < 0) {
2183 x1 = 0;
2184 }
2185
2186 if (x2 >= gdImageSX(im)) {
2187 x2 = gdImageSX(im) - 1;
2188 }
2189
2190 if (y1 < 0) {
2191 y1 = 0;
2192 }
2193
2194 if (y2 >= gdImageSY(im)) {
2195 y2 = gdImageSY(im) - 1;
2196 }
2197
2198 for (x = x1; (x <= x2); x++) {
2199 for (y = y1; (y <= y2); y++) {
2200 gdImageSetPixel (im, x, y, color);
2201 }
2202 }
2203 }
2204
_gdImageFilledVRectangle(gdImagePtr im,int x1,int y1,int x2,int y2,int color)2205 static void _gdImageFilledVRectangle (gdImagePtr im, int x1, int y1, int x2, int y2, int color)
2206 {
2207 int x, y;
2208
2209 if (x1 == x2 && y1 == y2) {
2210 gdImageSetPixel(im, x1, y1, color);
2211 return;
2212 }
2213
2214 if (x1 > x2) {
2215 x = x1;
2216 x1 = x2;
2217 x2 = x;
2218 }
2219
2220 if (y1 > y2) {
2221 y = y1;
2222 y1 = y2;
2223 y2 = y;
2224 }
2225
2226 if (x1 < 0) {
2227 x1 = 0;
2228 }
2229
2230 if (x2 >= gdImageSX(im)) {
2231 x2 = gdImageSX(im) - 1;
2232 }
2233
2234 if (y1 < 0) {
2235 y1 = 0;
2236 }
2237
2238 if (y2 >= gdImageSY(im)) {
2239 y2 = gdImageSY(im) - 1;
2240 }
2241
2242 for (y = y1; (y <= y2); y++) {
2243 for (x = x1; (x <= x2); x++) {
2244 gdImageSetPixel (im, x, y, color);
2245 }
2246 }
2247 }
2248
gdImageFilledRectangle(gdImagePtr im,int x1,int y1,int x2,int y2,int color)2249 void gdImageFilledRectangle (gdImagePtr im, int x1, int y1, int x2, int y2, int color)
2250 {
2251 _gdImageFilledVRectangle(im, x1, y1, x2, y2, color);
2252 }
2253
gdImageCopy(gdImagePtr dst,gdImagePtr src,int dstX,int dstY,int srcX,int srcY,int w,int h)2254 void gdImageCopy (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int w, int h)
2255 {
2256 int c;
2257 int x, y;
2258 int tox, toy;
2259 int i;
2260 int colorMap[gdMaxColors];
2261
2262 if (dst->trueColor) {
2263 /* 2.0: much easier when the destination is truecolor. */
2264 /* 2.0.10: needs a transparent-index check that is still valid if
2265 * the source is not truecolor. Thanks to Frank Warmerdam.
2266 */
2267
2268 if (src->trueColor) {
2269 for (y = 0; (y < h); y++) {
2270 for (x = 0; (x < w); x++) {
2271 int c = gdImageGetTrueColorPixel (src, srcX + x, srcY + y);
2272 if (c != src->transparent) {
2273 gdImageSetPixel (dst, dstX + x, dstY + y, c);
2274 }
2275 }
2276 }
2277 } else {
2278 /* source is palette based */
2279 for (y = 0; (y < h); y++) {
2280 for (x = 0; (x < w); x++) {
2281 int c = gdImageGetPixel (src, srcX + x, srcY + y);
2282 if (c != src->transparent) {
2283 gdImageSetPixel(dst, dstX + x, dstY + y, gdTrueColorAlpha(src->red[c], src->green[c], src->blue[c], src->alpha[c]));
2284 }
2285 }
2286 }
2287 }
2288 return;
2289 }
2290
2291 /* Palette based to palette based */
2292 for (i = 0; i < gdMaxColors; i++) {
2293 colorMap[i] = (-1);
2294 }
2295 toy = dstY;
2296 for (y = srcY; y < (srcY + h); y++) {
2297 tox = dstX;
2298 for (x = srcX; x < (srcX + w); x++) {
2299 int nc;
2300 int mapTo;
2301 c = gdImageGetPixel (src, x, y);
2302 /* Added 7/24/95: support transparent copies */
2303 if (gdImageGetTransparent (src) == c) {
2304 tox++;
2305 continue;
2306 }
2307 /* Have we established a mapping for this color? */
2308 if (src->trueColor) {
2309 /* 2.05: remap to the palette available in the destination image. This is slow and
2310 * works badly, but it beats crashing! Thanks to Padhrig McCarthy.
2311 */
2312 mapTo = gdImageColorResolveAlpha (dst, gdTrueColorGetRed (c), gdTrueColorGetGreen (c), gdTrueColorGetBlue (c), gdTrueColorGetAlpha (c));
2313 } else if (colorMap[c] == (-1)) {
2314 /* If it's the same image, mapping is trivial */
2315 if (dst == src) {
2316 nc = c;
2317 } else {
2318 /* Get best match possible. This function never returns error. */
2319 nc = gdImageColorResolveAlpha (dst, src->red[c], src->green[c], src->blue[c], src->alpha[c]);
2320 }
2321 colorMap[c] = nc;
2322 mapTo = colorMap[c];
2323 } else {
2324 mapTo = colorMap[c];
2325 }
2326 gdImageSetPixel (dst, tox, toy, mapTo);
2327 tox++;
2328 }
2329 toy++;
2330 }
2331 }
2332
2333 /* This function is a substitute for real alpha channel operations,
2334 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)2335 void gdImageCopyMerge (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int w, int h, int pct)
2336 {
2337 int c, dc;
2338 int x, y;
2339 int tox, toy;
2340 int ncR, ncG, ncB;
2341 toy = dstY;
2342
2343 for (y = srcY; y < (srcY + h); y++) {
2344 tox = dstX;
2345 for (x = srcX; x < (srcX + w); x++) {
2346 int nc;
2347 c = gdImageGetPixel(src, x, y);
2348 /* Added 7/24/95: support transparent copies */
2349 if (gdImageGetTransparent(src) == c) {
2350 tox++;
2351 continue;
2352 }
2353 /* If it's the same image, mapping is trivial */
2354 if (dst == src) {
2355 nc = c;
2356 } else {
2357 dc = gdImageGetPixel(dst, tox, toy);
2358
2359 ncR = (int)(gdImageRed (src, c) * (pct / 100.0) + gdImageRed (dst, dc) * ((100 - pct) / 100.0));
2360 ncG = (int)(gdImageGreen (src, c) * (pct / 100.0) + gdImageGreen (dst, dc) * ((100 - pct) / 100.0));
2361 ncB = (int)(gdImageBlue (src, c) * (pct / 100.0) + gdImageBlue (dst, dc) * ((100 - pct) / 100.0));
2362
2363 /* Find a reasonable color */
2364 nc = gdImageColorResolve (dst, ncR, ncG, ncB);
2365 }
2366 gdImageSetPixel (dst, tox, toy, nc);
2367 tox++;
2368 }
2369 toy++;
2370 }
2371 }
2372
2373 /* This function is a substitute for real alpha channel operations,
2374 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)2375 void gdImageCopyMergeGray (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int w, int h, int pct)
2376 {
2377 int c, dc;
2378 int x, y;
2379 int tox, toy;
2380 int ncR, ncG, ncB;
2381 float g;
2382 toy = dstY;
2383
2384 for (y = srcY; (y < (srcY + h)); y++) {
2385 tox = dstX;
2386 for (x = srcX; (x < (srcX + w)); x++) {
2387 int nc;
2388 c = gdImageGetPixel (src, x, y);
2389 /* Added 7/24/95: support transparent copies */
2390 if (gdImageGetTransparent(src) == c) {
2391 tox++;
2392 continue;
2393 }
2394
2395 /*
2396 * If it's the same image, mapping is NOT trivial since we
2397 * merge with greyscale target, but if pct is 100, the grey
2398 * value is not used, so it becomes trivial. pjw 2.0.12.
2399 */
2400 if (dst == src && pct == 100) {
2401 nc = c;
2402 } else {
2403 dc = gdImageGetPixel(dst, tox, toy);
2404 g = (0.29900f * gdImageRed(dst, dc)) + (0.58700f * gdImageGreen(dst, dc)) + (0.11400f * gdImageBlue(dst, dc));
2405
2406 ncR = (int)(gdImageRed (src, c) * (pct / 100.0f) + g * ((100 - pct) / 100.0));
2407 ncG = (int)(gdImageGreen (src, c) * (pct / 100.0f) + g * ((100 - pct) / 100.0));
2408 ncB = (int)(gdImageBlue (src, c) * (pct / 100.0f) + g * ((100 - pct) / 100.0));
2409
2410
2411 /* First look for an exact match */
2412 nc = gdImageColorExact(dst, ncR, ncG, ncB);
2413 if (nc == (-1)) {
2414 /* No, so try to allocate it */
2415 nc = gdImageColorAllocate(dst, ncR, ncG, ncB);
2416 /* If we're out of colors, go for the closest color */
2417 if (nc == (-1)) {
2418 nc = gdImageColorClosest(dst, ncR, ncG, ncB);
2419 }
2420 }
2421 }
2422 gdImageSetPixel(dst, tox, toy, nc);
2423 tox++;
2424 }
2425 toy++;
2426 }
2427 }
2428
gdImageCopyResized(gdImagePtr dst,gdImagePtr src,int dstX,int dstY,int srcX,int srcY,int dstW,int dstH,int srcW,int srcH)2429 void gdImageCopyResized (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int dstW, int dstH, int srcW, int srcH)
2430 {
2431 int c;
2432 int x, y;
2433 int tox, toy;
2434 int ydest;
2435 int i;
2436 int colorMap[gdMaxColors];
2437 /* Stretch vectors */
2438 int *stx, *sty;
2439
2440 if (overflow2(sizeof(int), srcW)) {
2441 return;
2442 }
2443 if (overflow2(sizeof(int), srcH)) {
2444 return;
2445 }
2446
2447 stx = (int *) gdMalloc (sizeof (int) * srcW);
2448 sty = (int *) gdMalloc (sizeof (int) * srcH);
2449
2450 /* Fixed by Mao Morimoto 2.0.16 */
2451 for (i = 0; (i < srcW); i++) {
2452 stx[i] = dstW * (i+1) / srcW - dstW * i / srcW ;
2453 }
2454 for (i = 0; (i < srcH); i++) {
2455 sty[i] = dstH * (i+1) / srcH - dstH * i / srcH ;
2456 }
2457 for (i = 0; (i < gdMaxColors); i++) {
2458 colorMap[i] = (-1);
2459 }
2460 toy = dstY;
2461 for (y = srcY; (y < (srcY + srcH)); y++) {
2462 for (ydest = 0; (ydest < sty[y - srcY]); ydest++) {
2463 tox = dstX;
2464 for (x = srcX; (x < (srcX + srcW)); x++) {
2465 int nc = 0;
2466 int mapTo;
2467 if (!stx[x - srcX]) {
2468 continue;
2469 }
2470 if (dst->trueColor) {
2471 /* 2.0.9: Thorben Kundinger: Maybe the source image is not a truecolor image */
2472 if (!src->trueColor) {
2473 int tmp = gdImageGetPixel (src, x, y);
2474 mapTo = gdImageGetTrueColorPixel (src, x, y);
2475 if (gdImageGetTransparent (src) == tmp) {
2476 /* 2.0.21, TK: not tox++ */
2477 tox += stx[x - srcX];
2478 continue;
2479 }
2480 } else {
2481 /* TK: old code follows */
2482 mapTo = gdImageGetTrueColorPixel (src, x, y);
2483 /* Added 7/24/95: support transparent copies */
2484 if (gdImageGetTransparent (src) == mapTo) {
2485 /* 2.0.21, TK: not tox++ */
2486 tox += stx[x - srcX];
2487 continue;
2488 }
2489 }
2490 } else {
2491 c = gdImageGetPixel (src, x, y);
2492 /* Added 7/24/95: support transparent copies */
2493 if (gdImageGetTransparent (src) == c) {
2494 tox += stx[x - srcX];
2495 continue;
2496 }
2497 if (src->trueColor) {
2498 /* Remap to the palette available in the destination image. This is slow and works badly. */
2499 mapTo = gdImageColorResolveAlpha(dst, gdTrueColorGetRed(c),
2500 gdTrueColorGetGreen(c),
2501 gdTrueColorGetBlue(c),
2502 gdTrueColorGetAlpha (c));
2503 } else {
2504 /* Have we established a mapping for this color? */
2505 if (colorMap[c] == (-1)) {
2506 /* If it's the same image, mapping is trivial */
2507 if (dst == src) {
2508 nc = c;
2509 } else {
2510 /* Find or create the best match */
2511 /* 2.0.5: can't use gdTrueColorGetRed, etc with palette */
2512 nc = gdImageColorResolveAlpha(dst, gdImageRed(src, c),
2513 gdImageGreen(src, c),
2514 gdImageBlue(src, c),
2515 gdImageAlpha(src, c));
2516 }
2517 colorMap[c] = nc;
2518 }
2519 mapTo = colorMap[c];
2520 }
2521 }
2522 for (i = 0; (i < stx[x - srcX]); i++) {
2523 gdImageSetPixel (dst, tox, toy, mapTo);
2524 tox++;
2525 }
2526 }
2527 toy++;
2528 }
2529 }
2530 gdFree (stx);
2531 gdFree (sty);
2532 }
2533
2534 /* When gd 1.x was first created, floating point was to be avoided.
2535 These days it is often faster than table lookups or integer
2536 arithmetic. The routine below is shamelessly, gloriously
2537 floating point. TBB */
2538
gdImageCopyResampled(gdImagePtr dst,gdImagePtr src,int dstX,int dstY,int srcX,int srcY,int dstW,int dstH,int srcW,int srcH)2539 void gdImageCopyResampled (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int dstW, int dstH, int srcW, int srcH)
2540 {
2541 int x, y;
2542 double sy1, sy2, sx1, sx2;
2543
2544 if (!dst->trueColor) {
2545 gdImageCopyResized (dst, src, dstX, dstY, srcX, srcY, dstW, dstH, srcW, srcH);
2546 return;
2547 }
2548 for (y = dstY; (y < dstY + dstH); y++) {
2549 sy1 = ((double) y - (double) dstY) * (double) srcH / (double) dstH;
2550 sy2 = ((double) (y + 1) - (double) dstY) * (double) srcH / (double) dstH;
2551 for (x = dstX; (x < dstX + dstW); x++) {
2552 double sx, sy;
2553 double spixels = 0;
2554 double red = 0.0, green = 0.0, blue = 0.0, alpha = 0.0;
2555 double alpha_factor, alpha_sum = 0.0, contrib_sum = 0.0;
2556 sx1 = ((double) x - (double) dstX) * (double) srcW / dstW;
2557 sx2 = ((double) (x + 1) - (double) dstX) * (double) srcW / dstW;
2558 sy = sy1;
2559 do {
2560 double yportion;
2561 if (floor_cast(sy) == floor_cast(sy1)) {
2562 yportion = 1.0f - (sy - floor_cast(sy));
2563 if (yportion > sy2 - sy1) {
2564 yportion = sy2 - sy1;
2565 }
2566 sy = floor_cast(sy);
2567 } else if (sy == floorf(sy2)) {
2568 yportion = sy2 - floor_cast(sy2);
2569 } else {
2570 yportion = 1.0f;
2571 }
2572 sx = sx1;
2573 do {
2574 double xportion;
2575 double pcontribution;
2576 int p;
2577 if (floorf(sx) == floor_cast(sx1)) {
2578 xportion = 1.0f - (sx - floor_cast(sx));
2579 if (xportion > sx2 - sx1) {
2580 xportion = sx2 - sx1;
2581 }
2582 sx = floor_cast(sx);
2583 } else if (sx == floorf(sx2)) {
2584 xportion = sx2 - floor_cast(sx2);
2585 } else {
2586 xportion = 1.0f;
2587 }
2588 pcontribution = xportion * yportion;
2589 p = gdImageGetTrueColorPixel(src, (int) sx + srcX, (int) sy + srcY);
2590
2591 alpha_factor = ((gdAlphaMax - gdTrueColorGetAlpha(p))) * pcontribution;
2592 red += gdTrueColorGetRed (p) * alpha_factor;
2593 green += gdTrueColorGetGreen (p) * alpha_factor;
2594 blue += gdTrueColorGetBlue (p) * alpha_factor;
2595 alpha += gdTrueColorGetAlpha (p) * pcontribution;
2596 alpha_sum += alpha_factor;
2597 contrib_sum += pcontribution;
2598 spixels += xportion * yportion;
2599 sx += 1.0f;
2600 }
2601 while (sx < sx2);
2602
2603 sy += 1.0f;
2604 }
2605
2606 while (sy < sy2);
2607
2608 if (spixels != 0.0f) {
2609 red /= spixels;
2610 green /= spixels;
2611 blue /= spixels;
2612 alpha /= spixels;
2613 alpha += 0.5;
2614 }
2615 if ( alpha_sum != 0.0f) {
2616 if( contrib_sum != 0.0f) {
2617 alpha_sum /= contrib_sum;
2618 }
2619 red /= alpha_sum;
2620 green /= alpha_sum;
2621 blue /= alpha_sum;
2622 }
2623 /* Clamping to allow for rounding errors above */
2624 if (red > 255.0f) {
2625 red = 255.0f;
2626 }
2627 if (green > 255.0f) {
2628 green = 255.0f;
2629 }
2630 if (blue > 255.0f) {
2631 blue = 255.0f;
2632 }
2633 if (alpha > gdAlphaMax) {
2634 alpha = gdAlphaMax;
2635 }
2636 gdImageSetPixel(dst, x, y, gdTrueColorAlpha ((int) red, (int) green, (int) blue, (int) alpha));
2637 }
2638 }
2639 }
2640
gdImagePolygon(gdImagePtr im,gdPointPtr p,int n,int c)2641 void gdImagePolygon (gdImagePtr im, gdPointPtr p, int n, int c)
2642 {
2643 if (n <= 0) {
2644 return;
2645 }
2646
2647
2648 gdImageLine (im, p->x, p->y, p[n - 1].x, p[n - 1].y, c);
2649 gdImageOpenPolygon (im, p, n, c);
2650 }
2651
gdImageOpenPolygon(gdImagePtr im,gdPointPtr p,int n,int c)2652 void gdImageOpenPolygon (gdImagePtr im, gdPointPtr p, int n, int c)
2653 {
2654 int i;
2655 int lx, ly;
2656
2657 if (n <= 0) {
2658 return;
2659 }
2660
2661 lx = p->x;
2662 ly = p->y;
2663 for (i = 1; i < n; i++) {
2664 p++;
2665 gdImageLine(im, lx, ly, p->x, p->y, c);
2666 lx = p->x;
2667 ly = p->y;
2668 }
2669 }
2670
2671 int gdCompareInt (const void *a, const void *b);
2672
2673 /* THANKS to Kirsten Schulz for the polygon fixes! */
2674
2675 /* The intersection finding technique of this code could be improved
2676 * by remembering the previous intertersection, and by using the slope.
2677 * That could help to adjust intersections to produce a nice
2678 * interior_extrema.
2679 */
2680
gdImageFilledPolygon(gdImagePtr im,gdPointPtr p,int n,int c)2681 void gdImageFilledPolygon (gdImagePtr im, gdPointPtr p, int n, int c)
2682 {
2683 int i;
2684 int y;
2685 int miny, maxy, pmaxy;
2686 int x1, y1;
2687 int x2, y2;
2688 int ind1, ind2;
2689 int ints;
2690 int fill_color;
2691
2692 if (n <= 0) {
2693 return;
2694 }
2695
2696 if (overflow2(sizeof(int), n)) {
2697 return;
2698 }
2699
2700 if (c == gdAntiAliased) {
2701 fill_color = im->AA_color;
2702 } else {
2703 fill_color = c;
2704 }
2705
2706 if (!im->polyAllocated) {
2707 im->polyInts = (int *) gdMalloc(sizeof(int) * n);
2708 im->polyAllocated = n;
2709 }
2710 if (im->polyAllocated < n) {
2711 while (im->polyAllocated < n) {
2712 im->polyAllocated *= 2;
2713 }
2714 if (overflow2(sizeof(int), im->polyAllocated)) {
2715 return;
2716 }
2717 im->polyInts = (int *) gdRealloc(im->polyInts, sizeof(int) * im->polyAllocated);
2718 }
2719 miny = p[0].y;
2720 maxy = p[0].y;
2721 for (i = 1; i < n; i++) {
2722 if (p[i].y < miny) {
2723 miny = p[i].y;
2724 }
2725 if (p[i].y > maxy) {
2726 maxy = p[i].y;
2727 }
2728 }
2729 /* necessary special case: horizontal line */
2730 if (n > 1 && miny == maxy) {
2731 x1 = x2 = p[0].x;
2732 for (i = 1; (i < n); i++) {
2733 if (p[i].x < x1) {
2734 x1 = p[i].x;
2735 } else if (p[i].x > x2) {
2736 x2 = p[i].x;
2737 }
2738 }
2739 gdImageLine(im, x1, miny, x2, miny, c);
2740 return;
2741 }
2742 pmaxy = maxy;
2743 /* 2.0.16: Optimization by Ilia Chipitsine -- don't waste time offscreen */
2744 if (miny < 0) {
2745 miny = 0;
2746 }
2747 if (maxy >= gdImageSY(im)) {
2748 maxy = gdImageSY(im) - 1;
2749 }
2750
2751 /* Fix in 1.3: count a vertex only once */
2752 for (y = miny; y <= maxy; y++) {
2753 /*1.4 int interLast = 0; */
2754 /* int dirLast = 0; */
2755 /* int interFirst = 1; */
2756 ints = 0;
2757 for (i = 0; i < n; i++) {
2758 if (!i) {
2759 ind1 = n - 1;
2760 ind2 = 0;
2761 } else {
2762 ind1 = i - 1;
2763 ind2 = i;
2764 }
2765 y1 = p[ind1].y;
2766 y2 = p[ind2].y;
2767 if (y1 < y2) {
2768 x1 = p[ind1].x;
2769 x2 = p[ind2].x;
2770 } else if (y1 > y2) {
2771 y2 = p[ind1].y;
2772 y1 = p[ind2].y;
2773 x2 = p[ind1].x;
2774 x1 = p[ind2].x;
2775 } else {
2776 continue;
2777 }
2778 /* Do the following math as float intermediately, and round to ensure
2779 * that Polygon and FilledPolygon for the same set of points have the
2780 * same footprint.
2781 */
2782 if (y >= y1 && y < y2) {
2783 im->polyInts[ints++] = (float) ((y - y1) * (x2 - x1)) / (float) (y2 - y1) + 0.5 + x1;
2784 } else if (y == pmaxy && y == y2) {
2785 im->polyInts[ints++] = x2;
2786 }
2787 }
2788 qsort(im->polyInts, ints, sizeof(int), gdCompareInt);
2789
2790 for (i = 0; i < ints - 1; i += 2) {
2791 gdImageLine(im, im->polyInts[i], y, im->polyInts[i + 1], y, fill_color);
2792 }
2793 }
2794
2795 /* If we are drawing this AA, then redraw the border with AA lines. */
2796 if (c == gdAntiAliased) {
2797 gdImagePolygon(im, p, n, c);
2798 }
2799 }
2800
gdCompareInt(const void * a,const void * b)2801 int gdCompareInt (const void *a, const void *b)
2802 {
2803 return (*(const int *) a) - (*(const int *) b);
2804 }
2805
gdImageSetStyle(gdImagePtr im,int * style,int noOfPixels)2806 void gdImageSetStyle (gdImagePtr im, int *style, int noOfPixels)
2807 {
2808 if (im->style) {
2809 gdFree(im->style);
2810 }
2811 if (overflow2(sizeof (int), noOfPixels)) {
2812 return;
2813 }
2814 im->style = (int *) gdMalloc(sizeof(int) * noOfPixels);
2815 memcpy(im->style, style, sizeof(int) * noOfPixels);
2816 im->styleLength = noOfPixels;
2817 im->stylePos = 0;
2818 }
2819
gdImageSetThickness(gdImagePtr im,int thickness)2820 void gdImageSetThickness (gdImagePtr im, int thickness)
2821 {
2822 im->thick = thickness;
2823 }
2824
gdImageSetBrush(gdImagePtr im,gdImagePtr brush)2825 void gdImageSetBrush (gdImagePtr im, gdImagePtr brush)
2826 {
2827 int i;
2828 im->brush = brush;
2829 if (!im->trueColor && !im->brush->trueColor) {
2830 for (i = 0; i < gdImageColorsTotal(brush); i++) {
2831 int index;
2832 index = gdImageColorResolveAlpha(im, gdImageRed(brush, i), gdImageGreen(brush, i), gdImageBlue(brush, i), gdImageAlpha(brush, i));
2833 im->brushColorMap[i] = index;
2834 }
2835 }
2836 }
2837
gdImageSetTile(gdImagePtr im,gdImagePtr tile)2838 void gdImageSetTile (gdImagePtr im, gdImagePtr tile)
2839 {
2840 int i;
2841 im->tile = tile;
2842 if (!im->trueColor && !im->tile->trueColor) {
2843 for (i = 0; i < gdImageColorsTotal(tile); i++) {
2844 int index;
2845 index = gdImageColorResolveAlpha(im, gdImageRed(tile, i), gdImageGreen(tile, i), gdImageBlue(tile, i), gdImageAlpha(tile, i));
2846 im->tileColorMap[i] = index;
2847 }
2848 }
2849 }
2850
gdImageSetAntiAliased(gdImagePtr im,int c)2851 void gdImageSetAntiAliased (gdImagePtr im, int c)
2852 {
2853 im->AA = 1;
2854 im->AA_color = c;
2855 im->AA_dont_blend = -1;
2856 }
2857
gdImageSetAntiAliasedDontBlend(gdImagePtr im,int c,int dont_blend)2858 void gdImageSetAntiAliasedDontBlend (gdImagePtr im, int c, int dont_blend)
2859 {
2860 im->AA = 1;
2861 im->AA_color = c;
2862 im->AA_dont_blend = dont_blend;
2863 }
2864
2865
gdImageInterlace(gdImagePtr im,int interlaceArg)2866 void gdImageInterlace (gdImagePtr im, int interlaceArg)
2867 {
2868 im->interlace = interlaceArg;
2869 }
2870
gdImageCompare(gdImagePtr im1,gdImagePtr im2)2871 int gdImageCompare (gdImagePtr im1, gdImagePtr im2)
2872 {
2873 int x, y;
2874 int p1, p2;
2875 int cmpStatus = 0;
2876 int sx, sy;
2877
2878 if (im1->interlace != im2->interlace) {
2879 cmpStatus |= GD_CMP_INTERLACE;
2880 }
2881
2882 if (im1->transparent != im2->transparent) {
2883 cmpStatus |= GD_CMP_TRANSPARENT;
2884 }
2885
2886 if (im1->trueColor != im2->trueColor) {
2887 cmpStatus |= GD_CMP_TRUECOLOR;
2888 }
2889
2890 sx = im1->sx;
2891 if (im1->sx != im2->sx) {
2892 cmpStatus |= GD_CMP_SIZE_X + GD_CMP_IMAGE;
2893 if (im2->sx < im1->sx) {
2894 sx = im2->sx;
2895 }
2896 }
2897
2898 sy = im1->sy;
2899 if (im1->sy != im2->sy) {
2900 cmpStatus |= GD_CMP_SIZE_Y + GD_CMP_IMAGE;
2901 if (im2->sy < im1->sy) {
2902 sy = im2->sy;
2903 }
2904 }
2905
2906 if (im1->colorsTotal != im2->colorsTotal) {
2907 cmpStatus |= GD_CMP_NUM_COLORS;
2908 }
2909
2910 for (y = 0; y < sy; y++) {
2911 for (x = 0; x < sx; x++) {
2912 p1 = im1->trueColor ? gdImageTrueColorPixel(im1, x, y) : gdImagePalettePixel(im1, x, y);
2913 p2 = im2->trueColor ? gdImageTrueColorPixel(im2, x, y) : gdImagePalettePixel(im2, x, y);
2914
2915 if (gdImageRed(im1, p1) != gdImageRed(im2, p2)) {
2916 cmpStatus |= GD_CMP_COLOR + GD_CMP_IMAGE;
2917 break;
2918 }
2919 if (gdImageGreen(im1, p1) != gdImageGreen(im2, p2)) {
2920 cmpStatus |= GD_CMP_COLOR + GD_CMP_IMAGE;
2921 break;
2922 }
2923 if (gdImageBlue(im1, p1) != gdImageBlue(im2, p2)) {
2924 cmpStatus |= GD_CMP_COLOR + GD_CMP_IMAGE;
2925 break;
2926 }
2927 #if 0
2928 /* Soon we'll add alpha channel to palettes */
2929 if (gdImageAlpha(im1, p1) != gdImageAlpha(im2, p2)) {
2930 cmpStatus |= GD_CMP_COLOR + GD_CMP_IMAGE;
2931 break;
2932 }
2933 #endif
2934 }
2935 if (cmpStatus & GD_CMP_COLOR) {
2936 break;
2937 }
2938 }
2939
2940 return cmpStatus;
2941 }
2942
gdAlphaBlend(int dst,int src)2943 int gdAlphaBlend (int dst, int src) {
2944 int src_alpha = gdTrueColorGetAlpha(src);
2945 int dst_alpha, alpha, red, green, blue;
2946 int src_weight, dst_weight, tot_weight;
2947
2948 /* -------------------------------------------------------------------- */
2949 /* Simple cases we want to handle fast. */
2950 /* -------------------------------------------------------------------- */
2951 if( src_alpha == gdAlphaOpaque )
2952 return src;
2953
2954 dst_alpha = gdTrueColorGetAlpha(dst);
2955 if( src_alpha == gdAlphaTransparent )
2956 return dst;
2957 if( dst_alpha == gdAlphaTransparent )
2958 return src;
2959
2960 /* -------------------------------------------------------------------- */
2961 /* What will the source and destination alphas be? Note that */
2962 /* the destination weighting is substantially reduced as the */
2963 /* overlay becomes quite opaque. */
2964 /* -------------------------------------------------------------------- */
2965 src_weight = gdAlphaTransparent - src_alpha;
2966 dst_weight = (gdAlphaTransparent - dst_alpha) * src_alpha / gdAlphaMax;
2967 tot_weight = src_weight + dst_weight;
2968
2969 /* -------------------------------------------------------------------- */
2970 /* What red, green and blue result values will we use? */
2971 /* -------------------------------------------------------------------- */
2972 alpha = src_alpha * dst_alpha / gdAlphaMax;
2973
2974 red = (gdTrueColorGetRed(src) * src_weight
2975 + gdTrueColorGetRed(dst) * dst_weight) / tot_weight;
2976 green = (gdTrueColorGetGreen(src) * src_weight
2977 + gdTrueColorGetGreen(dst) * dst_weight) / tot_weight;
2978 blue = (gdTrueColorGetBlue(src) * src_weight
2979 + gdTrueColorGetBlue(dst) * dst_weight) / tot_weight;
2980
2981 /* -------------------------------------------------------------------- */
2982 /* Return merged result. */
2983 /* -------------------------------------------------------------------- */
2984 return ((alpha << 24) + (red << 16) + (green << 8) + blue);
2985
2986 }
2987
gdImageAlphaBlending(gdImagePtr im,int alphaBlendingArg)2988 void gdImageAlphaBlending (gdImagePtr im, int alphaBlendingArg)
2989 {
2990 im->alphaBlendingFlag = alphaBlendingArg;
2991 }
2992
gdImageSaveAlpha(gdImagePtr im,int saveAlphaArg)2993 void gdImageSaveAlpha (gdImagePtr im, int saveAlphaArg)
2994 {
2995 im->saveAlphaFlag = saveAlphaArg;
2996 }
2997
gdLayerOverlay(int dst,int src)2998 int gdLayerOverlay (int dst, int src)
2999 {
3000 int a1, a2;
3001 a1 = gdAlphaMax - gdTrueColorGetAlpha(dst);
3002 a2 = gdAlphaMax - gdTrueColorGetAlpha(src);
3003 return ( ((gdAlphaMax - a1*a2/gdAlphaMax) << 24) +
3004 (gdAlphaOverlayColor( gdTrueColorGetRed(src), gdTrueColorGetRed(dst), gdRedMax ) << 16) +
3005 (gdAlphaOverlayColor( gdTrueColorGetGreen(src), gdTrueColorGetGreen(dst), gdGreenMax ) << 8) +
3006 (gdAlphaOverlayColor( gdTrueColorGetBlue(src), gdTrueColorGetBlue(dst), gdBlueMax ))
3007 );
3008 }
3009
gdAlphaOverlayColor(int src,int dst,int max)3010 static int gdAlphaOverlayColor (int src, int dst, int max )
3011 {
3012 /* this function implements the algorithm
3013 *
3014 * for dst[rgb] < 0.5,
3015 * c[rgb] = 2.src[rgb].dst[rgb]
3016 * and for dst[rgb] > 0.5,
3017 * c[rgb] = -2.src[rgb].dst[rgb] + 2.dst[rgb] + 2.src[rgb] - 1
3018 *
3019 */
3020
3021 dst = dst << 1;
3022 if( dst > max ) {
3023 /* in the "light" zone */
3024 return dst + (src << 1) - (dst * src / max) - max;
3025 } else {
3026 /* in the "dark" zone */
3027 return dst * src / max;
3028 }
3029 }
3030
gdLayerMultiply(int dst,int src)3031 int gdLayerMultiply (int dst, int src)
3032 {
3033 int a1, a2, r1, r2, g1, g2, b1, b2;
3034 a1 = gdAlphaMax - gdTrueColorGetAlpha(src);
3035 a2 = gdAlphaMax - gdTrueColorGetAlpha(dst);
3036
3037 r1 = gdRedMax - (a1 * (gdRedMax - gdTrueColorGetRed(src))) / gdAlphaMax;
3038 r2 = gdRedMax - (a2 * (gdRedMax - gdTrueColorGetRed(dst))) / gdAlphaMax;
3039 g1 = gdGreenMax - (a1 * (gdGreenMax - gdTrueColorGetGreen(src))) / gdAlphaMax;
3040 g2 = gdGreenMax - (a2 * (gdGreenMax - gdTrueColorGetGreen(dst))) / gdAlphaMax;
3041 b1 = gdBlueMax - (a1 * (gdBlueMax - gdTrueColorGetBlue(src))) / gdAlphaMax;
3042 b2 = gdBlueMax - (a2 * (gdBlueMax - gdTrueColorGetBlue(dst))) / gdAlphaMax ;
3043
3044 a1 = gdAlphaMax - a1;
3045 a2 = gdAlphaMax - a2;
3046 return ( ((a1*a2/gdAlphaMax) << 24) +
3047 ((r1*r2/gdRedMax) << 16) +
3048 ((g1*g2/gdGreenMax) << 8) +
3049 ((b1*b2/gdBlueMax))
3050 );
3051 }
3052
gdImageSetClip(gdImagePtr im,int x1,int y1,int x2,int y2)3053 void gdImageSetClip (gdImagePtr im, int x1, int y1, int x2, int y2)
3054 {
3055 if (x1 < 0) {
3056 x1 = 0;
3057 }
3058 if (x1 >= im->sx) {
3059 x1 = im->sx - 1;
3060 }
3061 if (x2 < 0) {
3062 x2 = 0;
3063 }
3064 if (x2 >= im->sx) {
3065 x2 = im->sx - 1;
3066 }
3067 if (y1 < 0) {
3068 y1 = 0;
3069 }
3070 if (y1 >= im->sy) {
3071 y1 = im->sy - 1;
3072 }
3073 if (y2 < 0) {
3074 y2 = 0;
3075 }
3076 if (y2 >= im->sy) {
3077 y2 = im->sy - 1;
3078 }
3079 im->cx1 = x1;
3080 im->cy1 = y1;
3081 im->cx2 = x2;
3082 im->cy2 = y2;
3083 }
3084
gdImageGetClip(gdImagePtr im,int * x1P,int * y1P,int * x2P,int * y2P)3085 void gdImageGetClip (gdImagePtr im, int *x1P, int *y1P, int *x2P, int *y2P)
3086 {
3087 *x1P = im->cx1;
3088 *y1P = im->cy1;
3089 *x2P = im->cx2;
3090 *y2P = im->cy2;
3091 }
3092
gdImageSetResolution(gdImagePtr im,const unsigned int res_x,const unsigned int res_y)3093 void gdImageSetResolution(gdImagePtr im, const unsigned int res_x, const unsigned int res_y)
3094 {
3095 if (res_x > 0) im->res_x = res_x;
3096 if (res_y > 0) im->res_y = res_y;
3097 }
3098
3099 /* convert a palette image to true color */
gdImagePaletteToTrueColor(gdImagePtr src)3100 int gdImagePaletteToTrueColor(gdImagePtr src)
3101 {
3102 unsigned int y;
3103 unsigned int yy;
3104
3105 if (src == NULL) {
3106 return 0;
3107 }
3108
3109 if (src->trueColor == 1) {
3110 return 1;
3111 } else {
3112 unsigned int x;
3113 const unsigned int sy = gdImageSY(src);
3114 const unsigned int sx = gdImageSX(src);
3115
3116 src->tpixels = (int **) gdMalloc(sizeof(int *) * sy);
3117 if (src->tpixels == NULL) {
3118 return 0;
3119 }
3120
3121 for (y = 0; y < sy; y++) {
3122 const unsigned char *src_row = src->pixels[y];
3123 int * dst_row;
3124
3125 /* no need to calloc it, we overwrite all pxl anyway */
3126 src->tpixels[y] = (int *) gdMalloc(sx * sizeof(int));
3127 if (src->tpixels[y] == NULL) {
3128 goto clean_on_error;
3129 }
3130
3131 dst_row = src->tpixels[y];
3132 for (x = 0; x < sx; x++) {
3133 const unsigned char c = *(src_row + x);
3134 if (c == src->transparent) {
3135 *(dst_row + x) = gdTrueColorAlpha(0, 0, 0, 127);
3136 } else {
3137 *(dst_row + x) = gdTrueColorAlpha(src->red[c], src->green[c], src->blue[c], src->alpha[c]);
3138 }
3139 }
3140 }
3141 }
3142
3143 /* free old palette buffer (y is sy) */
3144 for (yy = 0; yy < y; yy++) {
3145 gdFree(src->pixels[yy]);
3146 }
3147 gdFree(src->pixels);
3148 src->trueColor = 1;
3149 src->pixels = NULL;
3150 src->alphaBlendingFlag = 0;
3151 src->saveAlphaFlag = 1;
3152
3153 if (src->transparent >= 0) {
3154 const unsigned char c = src->transparent;
3155 src->transparent = gdTrueColorAlpha(src->red[c], src->green[c], src->blue[c], src->alpha[c]);
3156 }
3157
3158 return 1;
3159
3160 clean_on_error:
3161 /* free new true color buffer (y is not allocated, have failed) */
3162 for (yy = 0; yy < y; yy++) {
3163 gdFree(src->tpixels[yy]);
3164 }
3165 gdFree(src->tpixels);
3166 return 0;
3167 }
3168