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