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 (p == im->tile->transparent) {
934 tileColor = im->transparent;
935 } else if (im->trueColor) {
936 if (im->tile->trueColor) {
937 tileColor = p;
938 } else {
939 tileColor = gdTrueColorAlpha( gdImageRed(im->tile,p), gdImageGreen(im->tile,p), gdImageBlue (im->tile,p), gdImageAlpha (im->tile,p));
940 }
941 } else {
942 if (im->tile->trueColor) {
943 tileColor = gdImageColorResolveAlpha(im, gdTrueColorGetRed (p), gdTrueColorGetGreen (p), gdTrueColorGetBlue (p), gdTrueColorGetAlpha (p));
944 } else {
945 tileColor = p;
946 tileColor = gdImageColorResolveAlpha(im, gdImageRed (im->tile,p), gdImageGreen (im->tile,p), gdImageBlue (im->tile,p), gdImageAlpha (im->tile,p));
947 }
948 }
949 return tileColor;
950 }
951
952
gdImageGetPixel(gdImagePtr im,int x,int y)953 int gdImageGetPixel (gdImagePtr im, int x, int y)
954 {
955 if (gdImageBoundsSafe(im, x, y)) {
956 if (im->trueColor) {
957 return im->tpixels[y][x];
958 } else {
959 return im->pixels[y][x];
960 }
961 } else {
962 return 0;
963 }
964 }
965
gdImageAABlend(gdImagePtr im)966 void gdImageAABlend (gdImagePtr im)
967 {
968 (void)im;
969 }
970
971 static void _gdImageFilledHRectangle (gdImagePtr im, int x1, int y1, int x2, int y2, int color);
972
gdImageClone(gdImagePtr src)973 gdImagePtr gdImageClone (gdImagePtr src) {
974 gdImagePtr dst;
975 register int i, x;
976
977 if (src->trueColor) {
978 dst = gdImageCreateTrueColor(src->sx , src->sy);
979 } else {
980 dst = gdImageCreate(src->sx , src->sy);
981 }
982
983 if (dst == NULL) {
984 return NULL;
985 }
986
987 if (src->trueColor == 0) {
988 dst->colorsTotal = src->colorsTotal;
989 for (i = 0; i < gdMaxColors; i++) {
990 dst->red[i] = src->red[i];
991 dst->green[i] = src->green[i];
992 dst->blue[i] = src->blue[i];
993 dst->alpha[i] = src->alpha[i];
994 dst->open[i] = src->open[i];
995 }
996 for (i = 0; i < src->sy; i++) {
997 for (x = 0; x < src->sx; x++) {
998 dst->pixels[i][x] = src->pixels[i][x];
999 }
1000 }
1001 } else {
1002 for (i = 0; i < src->sy; i++) {
1003 for (x = 0; x < src->sx; x++) {
1004 dst->tpixels[i][x] = src->tpixels[i][x];
1005 }
1006 }
1007 }
1008
1009 dst->interlace = src->interlace;
1010
1011 dst->alphaBlendingFlag = src->alphaBlendingFlag;
1012 dst->saveAlphaFlag = src->saveAlphaFlag;
1013 dst->AA = src->AA;
1014 dst->AA_color = src->AA_color;
1015 dst->AA_dont_blend = src->AA_dont_blend;
1016
1017 dst->cx1 = src->cx1;
1018 dst->cy1 = src->cy1;
1019 dst->cx2 = src->cx2;
1020 dst->cy2 = src->cy2;
1021
1022 dst->res_x = src->res_x;
1023 dst->res_y = src->res_y;
1024
1025 dst->interpolation_id = src->interpolation_id;
1026 dst->interpolation = src->interpolation;
1027
1028 if (src->brush) {
1029 dst->brush = gdImageClone(src->brush);
1030 }
1031
1032 if (src->tile) {
1033 dst->tile = gdImageClone(src->tile);
1034 }
1035
1036 if (src->style) {
1037 gdImageSetStyle(dst, src->style, src->styleLength);
1038 dst->stylePos = src->stylePos;
1039 }
1040
1041 for (i = 0; i < gdMaxColors; i++) {
1042 dst->brushColorMap[i] = src->brushColorMap[i];
1043 dst->tileColorMap[i] = src->tileColorMap[i];
1044 }
1045
1046 if (src->polyAllocated > 0 && overflow2(sizeof(int), src->polyAllocated) == 0) {
1047 dst->polyInts = gdMalloc (sizeof (int) * src->polyAllocated);
1048 dst->polyAllocated = src->polyAllocated;
1049 for (i = 0; i < src->polyAllocated; i++) {
1050 dst->polyInts[i] = src->polyInts[i];
1051 }
1052 }
1053
1054 return dst;
1055 }
1056
gdImageHLine(gdImagePtr im,int y,int x1,int x2,int col)1057 static void gdImageHLine(gdImagePtr im, int y, int x1, int x2, int col)
1058 {
1059 if (im->thick > 1) {
1060 int thickhalf = im->thick >> 1;
1061 _gdImageFilledHRectangle(im, x1, y - thickhalf, x2, y + im->thick - thickhalf - 1, col);
1062 } else {
1063 if (x2 < x1) {
1064 int t = x2;
1065 x2 = x1;
1066 x1 = t;
1067 }
1068
1069 for (;x1 <= x2; x1++) {
1070 gdImageSetPixel(im, x1, y, col);
1071 }
1072 }
1073 return;
1074 }
1075
gdImageVLine(gdImagePtr im,int x,int y1,int y2,int col)1076 static void gdImageVLine(gdImagePtr im, int x, int y1, int y2, int col)
1077 {
1078 if (im->thick > 1) {
1079 int thickhalf = im->thick >> 1;
1080 gdImageFilledRectangle(im, x - thickhalf, y1, x + im->thick - thickhalf - 1, y2, col);
1081 } else {
1082 if (y2 < y1) {
1083 int t = y1;
1084 y1 = y2;
1085 y2 = t;
1086 }
1087
1088 for (;y1 <= y2; y1++) {
1089 gdImageSetPixel(im, x, y1, col);
1090 }
1091 }
1092 return;
1093 }
1094
1095 /* Bresenham as presented in Foley & Van Dam */
gdImageLine(gdImagePtr im,int x1,int y1,int x2,int y2,int color)1096 void gdImageLine (gdImagePtr im, int x1, int y1, int x2, int y2, int color)
1097 {
1098 int dx, dy, incr1, incr2, d, x, y, xend, yend, xdirflag, ydirflag;
1099 int wid;
1100 int w, wstart;
1101 int thick = im->thick;
1102
1103 if (color == gdAntiAliased) {
1104 /*
1105 gdAntiAliased passed as color: use the much faster, much cheaper
1106 and equally attractive gdImageAALine implementation. That
1107 clips too, so don't clip twice.
1108 */
1109 gdImageAALine(im, x1, y1, x2, y2, im->AA_color);
1110 return;
1111 }
1112
1113 /* 2.0.10: Nick Atty: clip to edges of drawing rectangle, return if no points need to be drawn */
1114 if (!clip_1d(&x1,&y1,&x2,&y2,gdImageSX(im)-1) || !clip_1d(&y1,&x1,&y2,&x2,gdImageSY(im)-1)) {
1115 return;
1116 }
1117
1118 dx = abs (x2 - x1);
1119 dy = abs (y2 - y1);
1120
1121 if (dx == 0) {
1122 gdImageVLine(im, x1, y1, y2, color);
1123 return;
1124 } else if (dy == 0) {
1125 gdImageHLine(im, y1, x1, x2, color);
1126 return;
1127 }
1128
1129 if (dy <= dx) {
1130 /* More-or-less horizontal. use wid for vertical stroke */
1131 /* Doug Claar: watch out for NaN in atan2 (2.0.5) */
1132 if ((dx == 0) && (dy == 0)) {
1133 wid = 1;
1134 } else {
1135 /* 2.0.12: Michael Schwartz: divide rather than multiply;
1136 TBB: but watch out for /0! */
1137 double ac = cos (atan2 (dy, dx));
1138 if (ac != 0) {
1139 wid = thick / ac;
1140 } else {
1141 wid = 1;
1142 }
1143 if (wid == 0) {
1144 wid = 1;
1145 }
1146 }
1147 d = 2 * dy - dx;
1148 incr1 = 2 * dy;
1149 incr2 = 2 * (dy - dx);
1150 if (x1 > x2) {
1151 x = x2;
1152 y = y2;
1153 ydirflag = (-1);
1154 xend = x1;
1155 } else {
1156 x = x1;
1157 y = y1;
1158 ydirflag = 1;
1159 xend = x2;
1160 }
1161
1162 /* Set up line thickness */
1163 wstart = y - wid / 2;
1164 for (w = wstart; w < wstart + wid; w++) {
1165 gdImageSetPixel(im, x, w, color);
1166 }
1167
1168 if (((y2 - y1) * ydirflag) > 0) {
1169 while (x < xend) {
1170 x++;
1171 if (d < 0) {
1172 d += incr1;
1173 } else {
1174 y++;
1175 d += incr2;
1176 }
1177 wstart = y - wid / 2;
1178 for (w = wstart; w < wstart + wid; w++) {
1179 gdImageSetPixel (im, x, w, color);
1180 }
1181 }
1182 } else {
1183 while (x < xend) {
1184 x++;
1185 if (d < 0) {
1186 d += incr1;
1187 } else {
1188 y--;
1189 d += incr2;
1190 }
1191 wstart = y - wid / 2;
1192 for (w = wstart; w < wstart + wid; w++) {
1193 gdImageSetPixel (im, x, w, color);
1194 }
1195 }
1196 }
1197 } else {
1198 /* More-or-less vertical. use wid for horizontal stroke */
1199 /* 2.0.12: Michael Schwartz: divide rather than multiply;
1200 TBB: but watch out for /0! */
1201 double as = sin (atan2 (dy, dx));
1202 if (as != 0) {
1203 wid = thick / as;
1204 } else {
1205 wid = 1;
1206 }
1207 if (wid == 0) {
1208 wid = 1;
1209 }
1210
1211 d = 2 * dx - dy;
1212 incr1 = 2 * dx;
1213 incr2 = 2 * (dx - dy);
1214 if (y1 > y2) {
1215 y = y2;
1216 x = x2;
1217 yend = y1;
1218 xdirflag = (-1);
1219 } else {
1220 y = y1;
1221 x = x1;
1222 yend = y2;
1223 xdirflag = 1;
1224 }
1225
1226 /* Set up line thickness */
1227 wstart = x - wid / 2;
1228 for (w = wstart; w < wstart + wid; w++) {
1229 gdImageSetPixel (im, w, y, color);
1230 }
1231
1232 if (((x2 - x1) * xdirflag) > 0) {
1233 while (y < yend) {
1234 y++;
1235 if (d < 0) {
1236 d += incr1;
1237 } else {
1238 x++;
1239 d += incr2;
1240 }
1241 wstart = x - wid / 2;
1242 for (w = wstart; w < wstart + wid; w++) {
1243 gdImageSetPixel (im, w, y, color);
1244 }
1245 }
1246 } else {
1247 while (y < yend) {
1248 y++;
1249 if (d < 0) {
1250 d += incr1;
1251 } else {
1252 x--;
1253 d += incr2;
1254 }
1255 wstart = x - wid / 2;
1256 for (w = wstart; w < wstart + wid; w++) {
1257 gdImageSetPixel (im, w, y, color);
1258 }
1259 }
1260 }
1261 }
1262 }
1263
1264
1265 /*
1266 * Added on 2003/12 by Pierre-Alain Joye (pajoye@pearfr.org)
1267 * */
1268 #define BLEND_COLOR(a, nc, c, cc) \
1269 nc = (cc) + (((((c) - (cc)) * (a)) + ((((c) - (cc)) * (a)) >> 8) + 0x80) >> 8);
1270
gdImageSetAAPixelColor(gdImagePtr im,int x,int y,int color,int t)1271 inline static void gdImageSetAAPixelColor(gdImagePtr im, int x, int y, int color, int t)
1272 {
1273 int dr,dg,db,p,r,g,b;
1274 dr = gdTrueColorGetRed(color);
1275 dg = gdTrueColorGetGreen(color);
1276 db = gdTrueColorGetBlue(color);
1277
1278 p = gdImageGetPixel(im,x,y);
1279 r = gdTrueColorGetRed(p);
1280 g = gdTrueColorGetGreen(p);
1281 b = gdTrueColorGetBlue(p);
1282
1283 BLEND_COLOR(t, dr, r, dr);
1284 BLEND_COLOR(t, dg, g, dg);
1285 BLEND_COLOR(t, db, b, db);
1286 im->tpixels[y][x]=gdTrueColorAlpha(dr, dg, db, gdAlphaOpaque);
1287 }
1288
1289 /*
1290 * Added on 2003/12 by Pierre-Alain Joye (pajoye@pearfr.org)
1291 **/
gdImageAALine(gdImagePtr im,int x1,int y1,int x2,int y2,int col)1292 void gdImageAALine (gdImagePtr im, int x1, int y1, int x2, int y2, int col)
1293 {
1294 /* keep them as 32bits */
1295 long x, y, inc, frac;
1296 long dx, dy,tmp;
1297
1298 if (!im->trueColor) {
1299 /* TBB: don't crash when the image is of the wrong type */
1300 gdImageLine(im, x1, y1, x2, y2, col);
1301 return;
1302 }
1303
1304 /* 2.0.10: Nick Atty: clip to edges of drawing rectangle, return if no points need to be drawn */
1305 if (!clip_1d(&x1,&y1,&x2,&y2,gdImageSX(im)-1) || !clip_1d(&y1,&x1,&y2,&x2,gdImageSY(im)-1)) {
1306 return;
1307 }
1308
1309 dx = x2 - x1;
1310 dy = y2 - y1;
1311
1312 if (dx == 0 && dy == 0) {
1313 return;
1314 }
1315 if (abs((int)dx) > abs((int)dy)) {
1316 if (dx < 0) {
1317 tmp = x1;
1318 x1 = x2;
1319 x2 = tmp;
1320 tmp = y1;
1321 y1 = y2;
1322 y2 = tmp;
1323 dx = x2 - x1;
1324 dy = y2 - y1;
1325 }
1326 y = y1;
1327 inc = (dy * 65536) / dx;
1328 frac = 0;
1329 for (x = x1; x <= x2; x++) {
1330 gdImageSetAAPixelColor(im, x, y, col, (frac >> 8) & 0xFF);
1331 if (y + 1 < im->sy) {
1332 gdImageSetAAPixelColor(im, x, y + 1, col, (~frac >> 8) & 0xFF);
1333 }
1334 frac += inc;
1335 if (frac >= 65536) {
1336 frac -= 65536;
1337 y++;
1338 } else if (frac < 0) {
1339 frac += 65536;
1340 y--;
1341 }
1342 }
1343 } else {
1344 if (dy < 0) {
1345 tmp = x1;
1346 x1 = x2;
1347 x2 = tmp;
1348 tmp = y1;
1349 y1 = y2;
1350 y2 = tmp;
1351 dx = x2 - x1;
1352 dy = y2 - y1;
1353 }
1354 x = x1;
1355 inc = (dx * 65536) / dy;
1356 frac = 0;
1357 for (y = y1; y <= y2; y++) {
1358 gdImageSetAAPixelColor(im, x, y, col, (frac >> 8) & 0xFF);
1359 if (x + 1 < im->sx) {
1360 gdImageSetAAPixelColor(im, x + 1, y, col, (~frac >> 8) & 0xFF);
1361 }
1362 frac += inc;
1363 if (frac >= 65536) {
1364 frac -= 65536;
1365 x++;
1366 } else if (frac < 0) {
1367 frac += 65536;
1368 x--;
1369 }
1370 }
1371 }
1372 }
1373
1374 static void dashedSet (gdImagePtr im, int x, int y, int color, int *onP, int *dashStepP, int wid, int vert);
1375
gdImageDashedLine(gdImagePtr im,int x1,int y1,int x2,int y2,int color)1376 void gdImageDashedLine (gdImagePtr im, int x1, int y1, int x2, int y2, int color)
1377 {
1378 int dx, dy, incr1, incr2, d, x, y, xend, yend, xdirflag, ydirflag;
1379 int dashStep = 0;
1380 int on = 1;
1381 int wid;
1382 int vert;
1383 int thick = im->thick;
1384
1385 dx = abs(x2 - x1);
1386 dy = abs(y2 - y1);
1387 if (dy <= dx) {
1388 /* More-or-less horizontal. use wid for vertical stroke */
1389 /* 2.0.12: Michael Schwartz: divide rather than multiply;
1390 TBB: but watch out for /0! */
1391 double as = sin(atan2(dy, dx));
1392 if (as != 0) {
1393 wid = thick / as;
1394 } else {
1395 wid = 1;
1396 }
1397 vert = 1;
1398
1399 d = 2 * dy - dx;
1400 incr1 = 2 * dy;
1401 incr2 = 2 * (dy - dx);
1402 if (x1 > x2) {
1403 x = x2;
1404 y = y2;
1405 ydirflag = (-1);
1406 xend = x1;
1407 } else {
1408 x = x1;
1409 y = y1;
1410 ydirflag = 1;
1411 xend = x2;
1412 }
1413 dashedSet(im, x, y, color, &on, &dashStep, wid, vert);
1414 if (((y2 - y1) * ydirflag) > 0) {
1415 while (x < xend) {
1416 x++;
1417 if (d < 0) {
1418 d += incr1;
1419 } else {
1420 y++;
1421 d += incr2;
1422 }
1423 dashedSet(im, x, y, color, &on, &dashStep, wid, vert);
1424 }
1425 } else {
1426 while (x < xend) {
1427 x++;
1428 if (d < 0) {
1429 d += incr1;
1430 } else {
1431 y--;
1432 d += incr2;
1433 }
1434 dashedSet(im, x, y, color, &on, &dashStep, wid, vert);
1435 }
1436 }
1437 } else {
1438 /* 2.0.12: Michael Schwartz: divide rather than multiply;
1439 TBB: but watch out for /0! */
1440 double as = sin (atan2 (dy, dx));
1441 if (as != 0) {
1442 wid = thick / as;
1443 } else {
1444 wid = 1;
1445 }
1446 vert = 0;
1447
1448 d = 2 * dx - dy;
1449 incr1 = 2 * dx;
1450 incr2 = 2 * (dx - dy);
1451 if (y1 > y2) {
1452 y = y2;
1453 x = x2;
1454 yend = y1;
1455 xdirflag = (-1);
1456 } else {
1457 y = y1;
1458 x = x1;
1459 yend = y2;
1460 xdirflag = 1;
1461 }
1462 dashedSet(im, x, y, color, &on, &dashStep, wid, vert);
1463 if (((x2 - x1) * xdirflag) > 0) {
1464 while (y < yend) {
1465 y++;
1466 if (d < 0) {
1467 d += incr1;
1468 } else {
1469 x++;
1470 d += incr2;
1471 }
1472 dashedSet(im, x, y, color, &on, &dashStep, wid, vert);
1473 }
1474 } else {
1475 while (y < yend) {
1476 y++;
1477 if (d < 0) {
1478 d += incr1;
1479 } else {
1480 x--;
1481 d += incr2;
1482 }
1483 dashedSet(im, x, y, color, &on, &dashStep, wid, vert);
1484 }
1485 }
1486 }
1487 }
1488
dashedSet(gdImagePtr im,int x,int y,int color,int * onP,int * dashStepP,int wid,int vert)1489 static void dashedSet (gdImagePtr im, int x, int y, int color, int *onP, int *dashStepP, int wid, int vert)
1490 {
1491 int dashStep = *dashStepP;
1492 int on = *onP;
1493 int w, wstart;
1494
1495 dashStep++;
1496 if (dashStep == gdDashSize) {
1497 dashStep = 0;
1498 on = !on;
1499 }
1500 if (on) {
1501 if (vert) {
1502 wstart = y - wid / 2;
1503 for (w = wstart; w < wstart + wid; w++) {
1504 gdImageSetPixel(im, x, w, color);
1505 }
1506 } else {
1507 wstart = x - wid / 2;
1508 for (w = wstart; w < wstart + wid; w++) {
1509 gdImageSetPixel(im, w, y, color);
1510 }
1511 }
1512 }
1513 *dashStepP = dashStep;
1514 *onP = on;
1515 }
1516
gdImageChar(gdImagePtr im,gdFontPtr f,int x,int y,int c,int color)1517 void gdImageChar (gdImagePtr im, gdFontPtr f, int x, int y, int c, int color)
1518 {
1519 int cx, cy;
1520 int px, py;
1521 int fline;
1522 const int xuppper = (x > INT_MAX - f->w) ? INT_MAX : x + f->w;
1523 const int yuppper = (y > INT_MAX - f->h) ? INT_MAX : y + f->h;
1524 cx = 0;
1525 cy = 0;
1526 #ifdef CHARSET_EBCDIC
1527 c = ASC (c);
1528 #endif /*CHARSET_EBCDIC */
1529 if ((c < f->offset) || (c >= (f->offset + f->nchars))) {
1530 return;
1531 }
1532 fline = (c - f->offset) * f->h * f->w;
1533 for (py = y; py < yuppper; py++) {
1534 for (px = x; px < xuppper; px++) {
1535 if (f->data[fline + cy * f->w + cx]) {
1536 gdImageSetPixel(im, px, py, color);
1537 }
1538 cx++;
1539 }
1540 cx = 0;
1541 cy++;
1542 }
1543 }
1544
gdImageCharUp(gdImagePtr im,gdFontPtr f,int x,int y,int c,int color)1545 void gdImageCharUp (gdImagePtr im, gdFontPtr f, int x, int y, int c, int color)
1546 {
1547 int cx, cy;
1548 int px, py;
1549 int fline;
1550 const int xuppper = (x > INT_MAX - f->h) ? INT_MAX : x + f->h;
1551 const int ylower = (y < INT_MIN + f->w) ? INT_MIN : y - f->w;
1552 cx = 0;
1553 cy = 0;
1554 #ifdef CHARSET_EBCDIC
1555 c = ASC (c);
1556 #endif /*CHARSET_EBCDIC */
1557 if ((c < f->offset) || (c >= (f->offset + f->nchars))) {
1558 return;
1559 }
1560 fline = (c - f->offset) * f->h * f->w;
1561 for (py = y; py > ylower; py--) {
1562 for (px = x; px < xuppper; px++) {
1563 if (f->data[fline + cy * f->w + cx]) {
1564 gdImageSetPixel(im, px, py, color);
1565 }
1566 cy++;
1567 }
1568 cy = 0;
1569 cx++;
1570 }
1571 }
1572
gdImageString(gdImagePtr im,gdFontPtr f,int x,int y,unsigned char * s,int color)1573 void gdImageString (gdImagePtr im, gdFontPtr f, int x, int y, unsigned char *s, int color)
1574 {
1575 int i;
1576 int l;
1577 l = strlen ((char *) s);
1578 for (i = 0; (i < l); i++) {
1579 gdImageChar(im, f, x, y, s[i], color);
1580 x += f->w;
1581 }
1582 }
1583
gdImageStringUp(gdImagePtr im,gdFontPtr f,int x,int y,unsigned char * s,int color)1584 void gdImageStringUp (gdImagePtr im, gdFontPtr f, int x, int y, unsigned char *s, int color)
1585 {
1586 int i;
1587 int l;
1588 l = strlen ((char *) s);
1589 for (i = 0; (i < l); i++) {
1590 gdImageCharUp(im, f, x, y, s[i], color);
1591 y -= f->w;
1592 }
1593 }
1594
1595 static int strlen16 (unsigned short *s);
1596
gdImageString16(gdImagePtr im,gdFontPtr f,int x,int y,unsigned short * s,int color)1597 void gdImageString16 (gdImagePtr im, gdFontPtr f, int x, int y, unsigned short *s, int color)
1598 {
1599 int i;
1600 int l;
1601 l = strlen16(s);
1602 for (i = 0; (i < l); i++) {
1603 gdImageChar(im, f, x, y, s[i], color);
1604 x += f->w;
1605 }
1606 }
1607
gdImageStringUp16(gdImagePtr im,gdFontPtr f,int x,int y,unsigned short * s,int color)1608 void gdImageStringUp16 (gdImagePtr im, gdFontPtr f, int x, int y, unsigned short *s, int color)
1609 {
1610 int i;
1611 int l;
1612 l = strlen16(s);
1613 for (i = 0; i < l; i++) {
1614 gdImageCharUp(im, f, x, y, s[i], color);
1615 y -= f->w;
1616 }
1617 }
1618
strlen16(unsigned short * s)1619 static int strlen16 (unsigned short *s)
1620 {
1621 int len = 0;
1622 while (*s) {
1623 s++;
1624 len++;
1625 }
1626 return len;
1627 }
1628
1629 #ifndef HAVE_LSQRT
1630 /* If you don't have a nice square root function for longs, you can use
1631 ** this hack
1632 */
lsqrt(long n)1633 long lsqrt (long n)
1634 {
1635 long result = (long) sqrt ((double) n);
1636 return result;
1637 }
1638 #endif
1639
1640 /* s and e are integers modulo 360 (degrees), with 0 degrees
1641 being the rightmost extreme and degrees changing clockwise.
1642 cx and cy are the center in pixels; w and h are the horizontal
1643 and vertical diameter in pixels. */
1644
gdImageArc(gdImagePtr im,int cx,int cy,int w,int h,int s,int e,int color)1645 void gdImageArc (gdImagePtr im, int cx, int cy, int w, int h, int s, int e, int color)
1646 {
1647 gdImageFilledArc(im, cx, cy, w, h, s, e, color, gdNoFill);
1648 }
1649
gdImageFilledArc(gdImagePtr im,int cx,int cy,int w,int h,int s,int e,int color,int style)1650 void gdImageFilledArc (gdImagePtr im, int cx, int cy, int w, int h, int s, int e, int color, int style)
1651 {
1652 gdPoint pts[363];
1653 int i, pti;
1654 int lx = 0, ly = 0;
1655 int fx = 0, fy = 0;
1656 int startx = -1, starty = -1, endx = -1, endy = -1;
1657
1658 if ((s % 360) == (e % 360)) {
1659 s = 0; e = 360;
1660 } else {
1661 if (s > 360) {
1662 s = s % 360;
1663 }
1664
1665 if (e > 360) {
1666 e = e % 360;
1667 }
1668
1669 while (s < 0) {
1670 s += 360;
1671 }
1672
1673 while (e < s) {
1674 e += 360;
1675 }
1676 if (s == e) {
1677 s = 0; e = 360;
1678 }
1679 }
1680
1681 for (i = s, pti = 1; i <= e; i++, pti++) {
1682 int x, y;
1683 x = endx = ((long) gdCosT[i % 360] * (long) w / (2 * 1024)) + cx;
1684 y = endy = ((long) gdSinT[i % 360] * (long) h / (2 * 1024)) + cy;
1685 if (i != s) {
1686 if (!(style & gdChord)) {
1687 if (style & gdNoFill) {
1688 gdImageLine(im, lx, ly, x, y, color);
1689 } else {
1690 if (y == ly) {
1691 pti--; /* don't add this point */
1692 if (((i > 270 || i < 90) && x > lx) || ((i > 90 && i < 270) && x < lx)) {
1693 /* replace the old x coord, if increasing on the
1694 right side or decreasing on the left side */
1695 pts[pti].x = x;
1696 }
1697 } else {
1698 pts[pti].x = x;
1699 pts[pti].y = y;
1700 }
1701 }
1702 }
1703 } else {
1704 fx = x;
1705 fy = y;
1706 if (!(style & (gdChord | gdNoFill))) {
1707 pts[0].x = cx;
1708 pts[0].y = cy;
1709 pts[pti].x = startx = x;
1710 pts[pti].y = starty = y;
1711 }
1712 }
1713 lx = x;
1714 ly = y;
1715 }
1716 if (style & gdChord) {
1717 if (style & gdNoFill) {
1718 if (style & gdEdged) {
1719 gdImageLine(im, cx, cy, lx, ly, color);
1720 gdImageLine(im, cx, cy, fx, fy, color);
1721 }
1722 gdImageLine(im, fx, fy, lx, ly, color);
1723 } else {
1724 pts[0].x = fx;
1725 pts[0].y = fy;
1726 pts[1].x = lx;
1727 pts[1].y = ly;
1728 pts[2].x = cx;
1729 pts[2].y = cy;
1730 gdImageFilledPolygon(im, pts, 3, color);
1731 }
1732 } else {
1733 if (style & gdNoFill) {
1734 if (style & gdEdged) {
1735 gdImageLine(im, cx, cy, lx, ly, color);
1736 gdImageLine(im, cx, cy, fx, fy, color);
1737 }
1738 } else {
1739 if (e - s < 360) {
1740 if (pts[1].x != startx && pts[1].y == starty) {
1741 /* start point has been removed due to y-coord fix => insert it */
1742 for (i = pti; i > 1; i--) {
1743 pts[i].x = pts[i-1].x;
1744 pts[i].y = pts[i-1].y;
1745 }
1746 pts[1].x = startx;
1747 pts[1].y = starty;
1748 pti++;
1749 }
1750 if (pts[pti-1].x != endx && pts[pti-1].y == endy) {
1751 /* end point has been removed due to y-coord fix => insert it */
1752 pts[pti].x = endx;
1753 pts[pti].y = endy;
1754 pti++;
1755 }
1756 }
1757 pts[pti].x = cx;
1758 pts[pti].y = cy;
1759 gdImageFilledPolygon(im, pts, pti+1, color);
1760 }
1761 }
1762 }
1763
1764 /**
1765 * Integer Ellipse functions (gdImageEllipse and gdImageFilledEllipse)
1766 * Function added by Pierre-Alain Joye 02/08/2003 (paj@pearfr.org)
1767 * See the ellipse function simplification for the equation
1768 * as well as the midpoint algorithm.
1769 */
1770
gdImageEllipse(gdImagePtr im,int mx,int my,int w,int h,int c)1771 void gdImageEllipse(gdImagePtr im, int mx, int my, int w, int h, int c)
1772 {
1773 int x=0,mx1=0,mx2=0,my1=0,my2=0;
1774 int64_t aq,bq,dx,dy,r,rx,ry,a,b;
1775
1776 a=w>>1;
1777 b=h>>1;
1778 gdImageSetPixel(im,mx+a, my, c);
1779 gdImageSetPixel(im,mx-a, my, c);
1780 mx1 = mx-a;my1 = my;
1781 mx2 = mx+a;my2 = my;
1782
1783 aq = a * a;
1784 bq = b * b;
1785 dx = aq << 1;
1786 dy = bq << 1;
1787 r = a * bq;
1788 rx = r << 1;
1789 ry = 0;
1790 x = a;
1791 while (x > 0){
1792 if (r > 0) {
1793 my1++;my2--;
1794 ry +=dx;
1795 r -=ry;
1796 }
1797 if (r <= 0){
1798 x--;
1799 mx1++;mx2--;
1800 rx -=dy;
1801 r +=rx;
1802 }
1803 gdImageSetPixel(im,mx1, my1, c);
1804 gdImageSetPixel(im,mx1, my2, c);
1805 gdImageSetPixel(im,mx2, my1, c);
1806 gdImageSetPixel(im,mx2, my2, c);
1807 }
1808 }
1809
gdImageFilledEllipse(gdImagePtr im,int mx,int my,int w,int h,int c)1810 void gdImageFilledEllipse (gdImagePtr im, int mx, int my, int w, int h, int c)
1811 {
1812 int x=0,mx1=0,mx2=0,my1=0,my2=0;
1813 int64_t aq,bq,dx,dy,r,rx,ry,a,b;
1814 int i;
1815 int old_y2;
1816
1817 a=w>>1;
1818 b=h>>1;
1819
1820 for (x = mx-a; x <= mx+a; x++) {
1821 gdImageSetPixel(im, x, my, c);
1822 }
1823
1824 mx1 = mx-a;my1 = my;
1825 mx2 = mx+a;my2 = my;
1826
1827 aq = a * a;
1828 bq = b * b;
1829 dx = aq << 1;
1830 dy = bq << 1;
1831 r = a * bq;
1832 rx = r << 1;
1833 ry = 0;
1834 x = a;
1835 old_y2=-2;
1836 while (x > 0){
1837 if (r > 0) {
1838 my1++;my2--;
1839 ry +=dx;
1840 r -=ry;
1841 }
1842 if (r <= 0){
1843 x--;
1844 mx1++;mx2--;
1845 rx -=dy;
1846 r +=rx;
1847 }
1848 if(old_y2!=my2){
1849 for(i=mx1;i<=mx2;i++){
1850 gdImageSetPixel(im,i,my1,c);
1851 gdImageSetPixel(im,i,my2,c);
1852 }
1853 }
1854 old_y2 = my2;
1855 }
1856 }
1857
gdImageFillToBorder(gdImagePtr im,int x,int y,int border,int color)1858 void gdImageFillToBorder (gdImagePtr im, int x, int y, int border, int color)
1859 {
1860 int lastBorder;
1861 /* Seek left */
1862 int leftLimit = -1, rightLimit;
1863 int i, restoreAlphaBlending = 0;
1864
1865 if (border < 0 || color < 0) {
1866 /* Refuse to fill to a non-solid border */
1867 return;
1868 }
1869
1870 if (!im->trueColor) {
1871 if ((color > (im->colorsTotal - 1)) || (border > (im->colorsTotal - 1)) || (color < 0)) {
1872 return;
1873 }
1874 }
1875
1876 restoreAlphaBlending = im->alphaBlendingFlag;
1877 im->alphaBlendingFlag = 0;
1878
1879 if (x >= im->sx) {
1880 x = im->sx - 1;
1881 } else if (x < 0) {
1882 x = 0;
1883 }
1884 if (y >= im->sy) {
1885 y = im->sy - 1;
1886 } else if (y < 0) {
1887 y = 0;
1888 }
1889
1890 for (i = x; i >= 0; i--) {
1891 if (gdImageGetPixel(im, i, y) == border) {
1892 break;
1893 }
1894 gdImageSetPixel(im, i, y, color);
1895 leftLimit = i;
1896 }
1897 if (leftLimit == -1) {
1898 im->alphaBlendingFlag = restoreAlphaBlending;
1899 return;
1900 }
1901 /* Seek right */
1902 rightLimit = x;
1903 for (i = (x + 1); i < im->sx; i++) {
1904 if (gdImageGetPixel(im, i, y) == border) {
1905 break;
1906 }
1907 gdImageSetPixel(im, i, y, color);
1908 rightLimit = i;
1909 }
1910 /* Look at lines above and below and start paints */
1911 /* Above */
1912 if (y > 0) {
1913 lastBorder = 1;
1914 for (i = leftLimit; i <= rightLimit; i++) {
1915 int c = gdImageGetPixel(im, i, y - 1);
1916 if (lastBorder) {
1917 if ((c != border) && (c != color)) {
1918 gdImageFillToBorder(im, i, y - 1, border, color);
1919 lastBorder = 0;
1920 }
1921 } else if ((c == border) || (c == color)) {
1922 lastBorder = 1;
1923 }
1924 }
1925 }
1926
1927 /* Below */
1928 if (y < ((im->sy) - 1)) {
1929 lastBorder = 1;
1930 for (i = leftLimit; i <= rightLimit; i++) {
1931 int c = gdImageGetPixel(im, i, y + 1);
1932
1933 if (lastBorder) {
1934 if ((c != border) && (c != color)) {
1935 gdImageFillToBorder(im, i, y + 1, border, color);
1936 lastBorder = 0;
1937 }
1938 } else if ((c == border) || (c == color)) {
1939 lastBorder = 1;
1940 }
1941 }
1942 }
1943 im->alphaBlendingFlag = restoreAlphaBlending;
1944 }
1945
1946 /*
1947 * set the pixel at (x,y) and its 4-connected neighbors
1948 * with the same pixel value to the new pixel value nc (new color).
1949 * A 4-connected neighbor: pixel above, below, left, or right of a pixel.
1950 * ideas from comp.graphics discussions.
1951 * For tiled fill, the use of a flag buffer is mandatory. As the tile image can
1952 * contain the same color as the color to fill. To do not bloat normal filling
1953 * code I added a 2nd private function.
1954 */
1955
1956 /* horizontal segment of scan line y */
1957 struct seg {int y, xl, xr, dy;};
1958
1959 /* max depth of stack */
1960 #define FILL_MAX ((int)(im->sy*im->sx)/4)
1961 #define FILL_PUSH(Y, XL, XR, DY) \
1962 if (sp<stack+FILL_MAX && Y+(DY)>=0 && Y+(DY)<wy2) \
1963 {sp->y = Y; sp->xl = XL; sp->xr = XR; sp->dy = DY; sp++;}
1964
1965 #define FILL_POP(Y, XL, XR, DY) \
1966 {sp--; Y = sp->y+(DY = sp->dy); XL = sp->xl; XR = sp->xr;}
1967
1968 static void _gdImageFillTiled(gdImagePtr im, int x, int y, int nc);
1969
gdImageFill(gdImagePtr im,int x,int y,int nc)1970 void gdImageFill(gdImagePtr im, int x, int y, int nc)
1971 {
1972 int l, x1, x2, dy;
1973 int oc; /* old pixel value */
1974 int wx2,wy2;
1975
1976 int alphablending_bak;
1977
1978 /* stack of filled segments */
1979 /* struct seg stack[FILL_MAX],*sp = stack; */
1980 struct seg *stack = NULL;
1981 struct seg *sp;
1982
1983 if (!im->trueColor && nc > (im->colorsTotal -1)) {
1984 return;
1985 }
1986
1987 alphablending_bak = im->alphaBlendingFlag;
1988 im->alphaBlendingFlag = 0;
1989
1990 if (nc==gdTiled){
1991 _gdImageFillTiled(im,x,y,nc);
1992 im->alphaBlendingFlag = alphablending_bak;
1993 return;
1994 }
1995
1996 wx2=im->sx;wy2=im->sy;
1997 oc = gdImageGetPixel(im, x, y);
1998 if (oc==nc || x<0 || x>wx2 || y<0 || y>wy2) {
1999 im->alphaBlendingFlag = alphablending_bak;
2000 return;
2001 }
2002
2003 /* Do not use the 4 neighbors implementation with
2004 * small images
2005 */
2006 if (im->sx < 4) {
2007 int ix = x, iy = y, c;
2008 do {
2009 do {
2010 c = gdImageGetPixel(im, ix, iy);
2011 if (c != oc) {
2012 goto done;
2013 }
2014 gdImageSetPixel(im, ix, iy, nc);
2015 } while(ix++ < (im->sx -1));
2016 ix = x;
2017 } while(iy++ < (im->sy -1));
2018 goto done;
2019 }
2020
2021 stack = (struct seg *)safe_emalloc(sizeof(struct seg), ((int)(im->sy*im->sx)/4), 1);
2022 sp = stack;
2023
2024 /* required! */
2025 FILL_PUSH(y,x,x,1);
2026 /* seed segment (popped 1st) */
2027 FILL_PUSH(y+1, x, x, -1);
2028 while (sp>stack) {
2029 FILL_POP(y, x1, x2, dy);
2030
2031 for (x=x1; x>=0 && gdImageGetPixel(im,x, y)==oc; x--) {
2032 gdImageSetPixel(im,x, y, nc);
2033 }
2034 if (x>=x1) {
2035 goto skip;
2036 }
2037 l = x+1;
2038
2039 /* leak on left? */
2040 if (l<x1) {
2041 FILL_PUSH(y, l, x1-1, -dy);
2042 }
2043 x = x1+1;
2044 do {
2045 for (; x<=wx2 && gdImageGetPixel(im,x, y)==oc; x++) {
2046 gdImageSetPixel(im, x, y, nc);
2047 }
2048 FILL_PUSH(y, l, x-1, dy);
2049 /* leak on right? */
2050 if (x>x2+1) {
2051 FILL_PUSH(y, x2+1, x-1, -dy);
2052 }
2053 skip:
2054 for (x++; x<=x2 && (gdImageGetPixel(im, x, y)!=oc); x++);
2055
2056 l = x;
2057 } while (x<=x2);
2058 }
2059
2060 efree(stack);
2061
2062 done:
2063 im->alphaBlendingFlag = alphablending_bak;
2064 }
2065
_gdImageFillTiled(gdImagePtr im,int x,int y,int nc)2066 static void _gdImageFillTiled(gdImagePtr im, int x, int y, int nc)
2067 {
2068 int i, l, x1, x2, dy;
2069 int oc; /* old pixel value */
2070 int wx2,wy2;
2071 /* stack of filled segments */
2072 struct seg *stack;
2073 struct seg *sp;
2074 char **pts;
2075
2076 if (!im->tile) {
2077 return;
2078 }
2079
2080 wx2=im->sx;wy2=im->sy;
2081
2082 nc = gdImageTileGet(im,x,y);
2083
2084 pts = (char **) ecalloc(im->sy + 1, sizeof(char *));
2085 for (i = 0; i < im->sy + 1; i++) {
2086 pts[i] = (char *) ecalloc(im->sx + 1, sizeof(char));
2087 }
2088
2089 stack = (struct seg *)safe_emalloc(sizeof(struct seg), ((int)(im->sy*im->sx)/4), 1);
2090 sp = stack;
2091
2092 oc = gdImageGetPixel(im, x, y);
2093
2094 /* required! */
2095 FILL_PUSH(y,x,x,1);
2096 /* seed segment (popped 1st) */
2097 FILL_PUSH(y+1, x, x, -1);
2098 while (sp>stack) {
2099 FILL_POP(y, x1, x2, dy);
2100 for (x=x1; x>=0 && (!pts[y][x] && gdImageGetPixel(im,x,y)==oc); x--) {
2101 nc = gdImageTileGet(im,x,y);
2102 pts[y][x] = 1;
2103 gdImageSetPixel(im,x, y, nc);
2104 }
2105 if (x>=x1) {
2106 goto skip;
2107 }
2108 l = x+1;
2109
2110 /* leak on left? */
2111 if (l<x1) {
2112 FILL_PUSH(y, l, x1-1, -dy);
2113 }
2114 x = x1+1;
2115 do {
2116 for(; x<wx2 && (!pts[y][x] && gdImageGetPixel(im,x, y)==oc); x++) {
2117 nc = gdImageTileGet(im,x,y);
2118 pts[y][x] = 1;
2119 gdImageSetPixel(im, x, y, nc);
2120 }
2121 FILL_PUSH(y, l, x-1, dy);
2122 /* leak on right? */
2123 if (x>x2+1) {
2124 FILL_PUSH(y, x2+1, x-1, -dy);
2125 }
2126 skip:
2127 for(x++; x<=x2 && (pts[y][x] || gdImageGetPixel(im,x, y)!=oc); x++);
2128 l = x;
2129 } while (x<=x2);
2130 }
2131
2132 for(i = 0; i < im->sy + 1; i++) {
2133 efree(pts[i]);
2134 }
2135
2136 efree(pts);
2137 efree(stack);
2138 }
2139
2140
2141
gdImageRectangle(gdImagePtr im,int x1,int y1,int x2,int y2,int color)2142 void gdImageRectangle (gdImagePtr im, int x1, int y1, int x2, int y2, int color)
2143 {
2144 int thick = im->thick;
2145 int t;
2146
2147 if (x1 == x2 && y1 == y2 && thick == 1) {
2148 gdImageSetPixel(im, x1, y1, color);
2149 return;
2150 }
2151
2152 if (y2 < y1) {
2153 t=y1;
2154 y1 = y2;
2155 y2 = t;
2156 }
2157
2158 if (x2 < x1) {
2159 t = x1;
2160 x1 = x2;
2161 x2 = t;
2162 }
2163
2164 if (thick > 1) {
2165 int cx, cy, x1ul, y1ul, x2lr, y2lr;
2166 int half = thick >> 1;
2167
2168 x1ul = x1 - half;
2169 y1ul = y1 - half;
2170
2171 x2lr = x2 + half;
2172 y2lr = y2 + half;
2173
2174 cy = y1ul + thick;
2175 while (cy-- > y1ul) {
2176 cx = x1ul - 1;
2177 while (cx++ < x2lr) {
2178 gdImageSetPixel(im, cx, cy, color);
2179 }
2180 }
2181
2182 cy = y2lr - thick;
2183 while (cy++ < y2lr) {
2184 cx = x1ul - 1;
2185 while (cx++ < x2lr) {
2186 gdImageSetPixel(im, cx, cy, color);
2187 }
2188 }
2189
2190 cy = y1ul + thick - 1;
2191 while (cy++ < y2lr -thick) {
2192 cx = x1ul - 1;
2193 while (cx++ < x1ul + thick) {
2194 gdImageSetPixel(im, cx, cy, color);
2195 }
2196 }
2197
2198 cy = y1ul + thick - 1;
2199 while (cy++ < y2lr -thick) {
2200 cx = x2lr - thick - 1;
2201 while (cx++ < x2lr) {
2202 gdImageSetPixel(im, cx, cy, color);
2203 }
2204 }
2205
2206 return;
2207 } else {
2208 if (x1 == x2 || y1 == y2) {
2209 gdImageLine(im, x1, y1, x2, y2, color);
2210 } else {
2211 gdImageLine(im, x1, y1, x2, y1, color);
2212 gdImageLine(im, x1, y2, x2, y2, color);
2213 gdImageLine(im, x1, y1 + 1, x1, y2 - 1, color);
2214 gdImageLine(im, x2, y1 + 1, x2, y2 - 1, color);
2215 }
2216 }
2217 }
2218
_gdImageFilledHRectangle(gdImagePtr im,int x1,int y1,int x2,int y2,int color)2219 static void _gdImageFilledHRectangle (gdImagePtr im, int x1, int y1, int x2, int y2, int color)
2220 {
2221 int x, y;
2222
2223 if (x1 == x2 && y1 == y2) {
2224 gdImageSetPixel(im, x1, y1, color);
2225 return;
2226 }
2227
2228 if (x1 > x2) {
2229 x = x1;
2230 x1 = x2;
2231 x2 = x;
2232 }
2233
2234 if (y1 > y2) {
2235 y = y1;
2236 y1 = y2;
2237 y2 = y;
2238 }
2239
2240 if (x1 < 0) {
2241 x1 = 0;
2242 }
2243
2244 if (x2 >= gdImageSX(im)) {
2245 x2 = gdImageSX(im) - 1;
2246 }
2247
2248 if (y1 < 0) {
2249 y1 = 0;
2250 }
2251
2252 if (y2 >= gdImageSY(im)) {
2253 y2 = gdImageSY(im) - 1;
2254 }
2255
2256 for (x = x1; (x <= x2); x++) {
2257 for (y = y1; (y <= y2); y++) {
2258 gdImageSetPixel (im, x, y, color);
2259 }
2260 }
2261 }
2262
_gdImageFilledVRectangle(gdImagePtr im,int x1,int y1,int x2,int y2,int color)2263 static void _gdImageFilledVRectangle (gdImagePtr im, int x1, int y1, int x2, int y2, int color)
2264 {
2265 int x, y;
2266
2267 if (x1 == x2 && y1 == y2) {
2268 gdImageSetPixel(im, x1, y1, color);
2269 return;
2270 }
2271
2272 if (x1 > x2) {
2273 x = x1;
2274 x1 = x2;
2275 x2 = x;
2276 }
2277
2278 if (y1 > y2) {
2279 y = y1;
2280 y1 = y2;
2281 y2 = y;
2282 }
2283
2284 if (x1 < 0) {
2285 x1 = 0;
2286 }
2287
2288 if (x2 >= gdImageSX(im)) {
2289 x2 = gdImageSX(im) - 1;
2290 }
2291
2292 if (y1 < 0) {
2293 y1 = 0;
2294 }
2295
2296 if (y2 >= gdImageSY(im)) {
2297 y2 = gdImageSY(im) - 1;
2298 }
2299
2300 for (y = y1; (y <= y2); y++) {
2301 for (x = x1; (x <= x2); x++) {
2302 gdImageSetPixel (im, x, y, color);
2303 }
2304 }
2305 }
2306
gdImageFilledRectangle(gdImagePtr im,int x1,int y1,int x2,int y2,int color)2307 void gdImageFilledRectangle (gdImagePtr im, int x1, int y1, int x2, int y2, int color)
2308 {
2309 _gdImageFilledVRectangle(im, x1, y1, x2, y2, color);
2310 }
2311
gdImageCopy(gdImagePtr dst,gdImagePtr src,int dstX,int dstY,int srcX,int srcY,int w,int h)2312 void gdImageCopy (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int w, int h)
2313 {
2314 int c;
2315 int x, y;
2316 int tox, toy;
2317 int i;
2318 int colorMap[gdMaxColors];
2319
2320 if (dst->trueColor) {
2321 /* 2.0: much easier when the destination is truecolor. */
2322 /* 2.0.10: needs a transparent-index check that is still valid if
2323 * the source is not truecolor. Thanks to Frank Warmerdam.
2324 */
2325
2326 if (src->trueColor) {
2327 for (y = 0; (y < h); y++) {
2328 for (x = 0; (x < w); x++) {
2329 int c = gdImageGetTrueColorPixel (src, srcX + x, srcY + y);
2330 if (c != src->transparent) {
2331 gdImageSetPixel (dst, dstX + x, dstY + y, c);
2332 }
2333 }
2334 }
2335 } else {
2336 /* source is palette based */
2337 for (y = 0; (y < h); y++) {
2338 for (x = 0; (x < w); x++) {
2339 int c = gdImageGetPixel (src, srcX + x, srcY + y);
2340 if (c != src->transparent) {
2341 gdImageSetPixel(dst, dstX + x, dstY + y, gdTrueColorAlpha(src->red[c], src->green[c], src->blue[c], src->alpha[c]));
2342 }
2343 }
2344 }
2345 }
2346 return;
2347 }
2348
2349 /* Palette based to palette based */
2350 for (i = 0; i < gdMaxColors; i++) {
2351 colorMap[i] = (-1);
2352 }
2353 toy = dstY;
2354 for (y = srcY; y < (srcY + h); y++) {
2355 tox = dstX;
2356 for (x = srcX; x < (srcX + w); x++) {
2357 int nc;
2358 int mapTo;
2359 c = gdImageGetPixel (src, x, y);
2360 /* Added 7/24/95: support transparent copies */
2361 if (gdImageGetTransparent (src) == c) {
2362 tox++;
2363 continue;
2364 }
2365 /* Have we established a mapping for this color? */
2366 if (src->trueColor) {
2367 /* 2.05: remap to the palette available in the destination image. This is slow and
2368 * works badly, but it beats crashing! Thanks to Padhrig McCarthy.
2369 */
2370 mapTo = gdImageColorResolveAlpha (dst, gdTrueColorGetRed (c), gdTrueColorGetGreen (c), gdTrueColorGetBlue (c), gdTrueColorGetAlpha (c));
2371 } else if (colorMap[c] == (-1)) {
2372 /* If it's the same image, mapping is trivial */
2373 if (dst == src) {
2374 nc = c;
2375 } else {
2376 /* Get best match possible. This function never returns error. */
2377 nc = gdImageColorResolveAlpha (dst, src->red[c], src->green[c], src->blue[c], src->alpha[c]);
2378 }
2379 colorMap[c] = nc;
2380 mapTo = colorMap[c];
2381 } else {
2382 mapTo = colorMap[c];
2383 }
2384 gdImageSetPixel (dst, tox, toy, mapTo);
2385 tox++;
2386 }
2387 toy++;
2388 }
2389 }
2390
2391 /* This function is a substitute for real alpha channel operations,
2392 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)2393 void gdImageCopyMerge (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int w, int h, int pct)
2394 {
2395 int c, dc;
2396 int x, y;
2397 int tox, toy;
2398 int ncR, ncG, ncB;
2399 toy = dstY;
2400
2401 for (y = srcY; y < (srcY + h); y++) {
2402 tox = dstX;
2403 for (x = srcX; x < (srcX + w); x++) {
2404 int nc;
2405 c = gdImageGetPixel(src, x, y);
2406 /* Added 7/24/95: support transparent copies */
2407 if (gdImageGetTransparent(src) == c) {
2408 tox++;
2409 continue;
2410 }
2411 /* If it's the same image, mapping is trivial */
2412 if (dst == src) {
2413 nc = c;
2414 } else {
2415 dc = gdImageGetPixel(dst, tox, toy);
2416
2417 ncR = (int)(gdImageRed (src, c) * (pct / 100.0) + gdImageRed (dst, dc) * ((100 - pct) / 100.0));
2418 ncG = (int)(gdImageGreen (src, c) * (pct / 100.0) + gdImageGreen (dst, dc) * ((100 - pct) / 100.0));
2419 ncB = (int)(gdImageBlue (src, c) * (pct / 100.0) + gdImageBlue (dst, dc) * ((100 - pct) / 100.0));
2420
2421 /* Find a reasonable color */
2422 nc = gdImageColorResolve (dst, ncR, ncG, ncB);
2423 }
2424 gdImageSetPixel (dst, tox, toy, nc);
2425 tox++;
2426 }
2427 toy++;
2428 }
2429 }
2430
2431 /* This function is a substitute for real alpha channel operations,
2432 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)2433 void gdImageCopyMergeGray (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int w, int h, int pct)
2434 {
2435 int c, dc;
2436 int x, y;
2437 int tox, toy;
2438 int ncR, ncG, ncB;
2439 float g;
2440 toy = dstY;
2441
2442 for (y = srcY; (y < (srcY + h)); y++) {
2443 tox = dstX;
2444 for (x = srcX; (x < (srcX + w)); x++) {
2445 int nc;
2446 c = gdImageGetPixel (src, x, y);
2447 /* Added 7/24/95: support transparent copies */
2448 if (gdImageGetTransparent(src) == c) {
2449 tox++;
2450 continue;
2451 }
2452
2453 /*
2454 * If it's the same image, mapping is NOT trivial since we
2455 * merge with greyscale target, but if pct is 100, the grey
2456 * value is not used, so it becomes trivial. pjw 2.0.12.
2457 */
2458 if (dst == src && pct == 100) {
2459 nc = c;
2460 } else {
2461 dc = gdImageGetPixel(dst, tox, toy);
2462 g = (0.29900f * gdImageRed(dst, dc)) + (0.58700f * gdImageGreen(dst, dc)) + (0.11400f * gdImageBlue(dst, dc));
2463
2464 ncR = (int)(gdImageRed (src, c) * (pct / 100.0f) + g * ((100 - pct) / 100.0));
2465 ncG = (int)(gdImageGreen (src, c) * (pct / 100.0f) + g * ((100 - pct) / 100.0));
2466 ncB = (int)(gdImageBlue (src, c) * (pct / 100.0f) + g * ((100 - pct) / 100.0));
2467
2468
2469 /* First look for an exact match */
2470 nc = gdImageColorExact(dst, ncR, ncG, ncB);
2471 if (nc == (-1)) {
2472 /* No, so try to allocate it */
2473 nc = gdImageColorAllocate(dst, ncR, ncG, ncB);
2474 /* If we're out of colors, go for the closest color */
2475 if (nc == (-1)) {
2476 nc = gdImageColorClosest(dst, ncR, ncG, ncB);
2477 }
2478 }
2479 }
2480 gdImageSetPixel(dst, tox, toy, nc);
2481 tox++;
2482 }
2483 toy++;
2484 }
2485 }
2486
gdImageCopyResized(gdImagePtr dst,gdImagePtr src,int dstX,int dstY,int srcX,int srcY,int dstW,int dstH,int srcW,int srcH)2487 void gdImageCopyResized (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int dstW, int dstH, int srcW, int srcH)
2488 {
2489 int c;
2490 int x, y;
2491 int tox, toy;
2492 int ydest;
2493 int i;
2494 int colorMap[gdMaxColors];
2495 /* Stretch vectors */
2496 int *stx, *sty;
2497
2498 if (overflow2(sizeof(int), srcW)) {
2499 return;
2500 }
2501 if (overflow2(sizeof(int), srcH)) {
2502 return;
2503 }
2504
2505 stx = (int *) gdMalloc (sizeof (int) * srcW);
2506 sty = (int *) gdMalloc (sizeof (int) * srcH);
2507
2508 /* Fixed by Mao Morimoto 2.0.16 */
2509 for (i = 0; (i < srcW); i++) {
2510 stx[i] = dstW * (i+1) / srcW - dstW * i / srcW ;
2511 }
2512 for (i = 0; (i < srcH); i++) {
2513 sty[i] = dstH * (i+1) / srcH - dstH * i / srcH ;
2514 }
2515 for (i = 0; (i < gdMaxColors); i++) {
2516 colorMap[i] = (-1);
2517 }
2518 toy = dstY;
2519 for (y = srcY; (y < (srcY + srcH)); y++) {
2520 for (ydest = 0; (ydest < sty[y - srcY]); ydest++) {
2521 tox = dstX;
2522 for (x = srcX; (x < (srcX + srcW)); x++) {
2523 int nc = 0;
2524 int mapTo;
2525 if (!stx[x - srcX]) {
2526 continue;
2527 }
2528 if (dst->trueColor) {
2529 /* 2.0.9: Thorben Kundinger: Maybe the source image is not a truecolor image */
2530 if (!src->trueColor) {
2531 int tmp = gdImageGetPixel (src, x, y);
2532 mapTo = gdImageGetTrueColorPixel (src, x, y);
2533 if (gdImageGetTransparent (src) == tmp) {
2534 /* 2.0.21, TK: not tox++ */
2535 tox += stx[x - srcX];
2536 continue;
2537 }
2538 } else {
2539 /* TK: old code follows */
2540 mapTo = gdImageGetTrueColorPixel (src, x, y);
2541 /* Added 7/24/95: support transparent copies */
2542 if (gdImageGetTransparent (src) == mapTo) {
2543 /* 2.0.21, TK: not tox++ */
2544 tox += stx[x - srcX];
2545 continue;
2546 }
2547 }
2548 } else {
2549 c = gdImageGetPixel (src, x, y);
2550 /* Added 7/24/95: support transparent copies */
2551 if (gdImageGetTransparent (src) == c) {
2552 tox += stx[x - srcX];
2553 continue;
2554 }
2555 if (src->trueColor) {
2556 /* Remap to the palette available in the destination image. This is slow and works badly. */
2557 mapTo = gdImageColorResolveAlpha(dst, gdTrueColorGetRed(c),
2558 gdTrueColorGetGreen(c),
2559 gdTrueColorGetBlue(c),
2560 gdTrueColorGetAlpha (c));
2561 } else {
2562 /* Have we established a mapping for this color? */
2563 if (colorMap[c] == (-1)) {
2564 /* If it's the same image, mapping is trivial */
2565 if (dst == src) {
2566 nc = c;
2567 } else {
2568 /* Find or create the best match */
2569 /* 2.0.5: can't use gdTrueColorGetRed, etc with palette */
2570 nc = gdImageColorResolveAlpha(dst, gdImageRed(src, c),
2571 gdImageGreen(src, c),
2572 gdImageBlue(src, c),
2573 gdImageAlpha(src, c));
2574 }
2575 colorMap[c] = nc;
2576 }
2577 mapTo = colorMap[c];
2578 }
2579 }
2580 for (i = 0; (i < stx[x - srcX]); i++) {
2581 gdImageSetPixel (dst, tox, toy, mapTo);
2582 tox++;
2583 }
2584 }
2585 toy++;
2586 }
2587 }
2588 gdFree (stx);
2589 gdFree (sty);
2590 }
2591
2592 /* When gd 1.x was first created, floating point was to be avoided.
2593 These days it is often faster than table lookups or integer
2594 arithmetic. The routine below is shamelessly, gloriously
2595 floating point. TBB */
2596
gdImageCopyResampled(gdImagePtr dst,gdImagePtr src,int dstX,int dstY,int srcX,int srcY,int dstW,int dstH,int srcW,int srcH)2597 void gdImageCopyResampled (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int dstW, int dstH, int srcW, int srcH)
2598 {
2599 int x, y;
2600 double sy1, sy2, sx1, sx2;
2601
2602 if (!dst->trueColor) {
2603 gdImageCopyResized (dst, src, dstX, dstY, srcX, srcY, dstW, dstH, srcW, srcH);
2604 return;
2605 }
2606 for (y = dstY; (y < dstY + dstH); y++) {
2607 sy1 = ((double) y - (double) dstY) * (double) srcH / (double) dstH;
2608 sy2 = ((double) (y + 1) - (double) dstY) * (double) srcH / (double) dstH;
2609 for (x = dstX; (x < dstX + dstW); x++) {
2610 double sx, sy;
2611 double spixels = 0;
2612 double red = 0.0, green = 0.0, blue = 0.0, alpha = 0.0;
2613 double alpha_factor, alpha_sum = 0.0, contrib_sum = 0.0;
2614 sx1 = ((double) x - (double) dstX) * (double) srcW / dstW;
2615 sx2 = ((double) (x + 1) - (double) dstX) * (double) srcW / dstW;
2616 sy = sy1;
2617 do {
2618 double yportion;
2619 if (floor_cast(sy) == floor_cast(sy1)) {
2620 yportion = 1.0f - (sy - floor_cast(sy));
2621 if (yportion > sy2 - sy1) {
2622 yportion = sy2 - sy1;
2623 }
2624 sy = floor_cast(sy);
2625 } else if (sy == floorf(sy2)) {
2626 yportion = sy2 - floor_cast(sy2);
2627 } else {
2628 yportion = 1.0f;
2629 }
2630 sx = sx1;
2631 do {
2632 double xportion;
2633 double pcontribution;
2634 int p;
2635 if (floorf(sx) == floor_cast(sx1)) {
2636 xportion = 1.0f - (sx - floor_cast(sx));
2637 if (xportion > sx2 - sx1) {
2638 xportion = sx2 - sx1;
2639 }
2640 sx = floor_cast(sx);
2641 } else if (sx == floorf(sx2)) {
2642 xportion = sx2 - floor_cast(sx2);
2643 } else {
2644 xportion = 1.0f;
2645 }
2646 pcontribution = xportion * yportion;
2647 p = gdImageGetTrueColorPixel(src, (int) sx + srcX, (int) sy + srcY);
2648
2649 alpha_factor = ((gdAlphaMax - gdTrueColorGetAlpha(p))) * pcontribution;
2650 red += gdTrueColorGetRed (p) * alpha_factor;
2651 green += gdTrueColorGetGreen (p) * alpha_factor;
2652 blue += gdTrueColorGetBlue (p) * alpha_factor;
2653 alpha += gdTrueColorGetAlpha (p) * pcontribution;
2654 alpha_sum += alpha_factor;
2655 contrib_sum += pcontribution;
2656 spixels += xportion * yportion;
2657 sx += 1.0f;
2658 }
2659 while (sx < sx2);
2660
2661 sy += 1.0f;
2662 }
2663
2664 while (sy < sy2);
2665
2666 if (spixels != 0.0f) {
2667 red /= spixels;
2668 green /= spixels;
2669 blue /= spixels;
2670 alpha /= spixels;
2671 }
2672 if ( alpha_sum != 0.0f) {
2673 if( contrib_sum != 0.0f) {
2674 alpha_sum /= contrib_sum;
2675 }
2676 red /= alpha_sum;
2677 green /= alpha_sum;
2678 blue /= alpha_sum;
2679 }
2680 /* Round up closest next channel value and clamp to max channel value */
2681 red = red >= 255.5 ? 255 : red+0.5;
2682 blue = blue >= 255.5 ? 255 : blue+0.5;
2683 green = green >= 255.5 ? 255 : green+0.5;
2684 alpha = alpha >= gdAlphaMax+0.5 ? gdAlphaMax : alpha+0.5;
2685 gdImageSetPixel(dst, x, y, gdTrueColorAlpha ((int)red, (int)green, (int)blue, (int)alpha));
2686 }
2687 }
2688 }
2689
gdImagePolygon(gdImagePtr im,gdPointPtr p,int n,int c)2690 void gdImagePolygon (gdImagePtr im, gdPointPtr p, int n, int c)
2691 {
2692 if (n <= 0) {
2693 return;
2694 }
2695
2696
2697 gdImageLine (im, p->x, p->y, p[n - 1].x, p[n - 1].y, c);
2698 gdImageOpenPolygon (im, p, n, c);
2699 }
2700
gdImageOpenPolygon(gdImagePtr im,gdPointPtr p,int n,int c)2701 void gdImageOpenPolygon (gdImagePtr im, gdPointPtr p, int n, int c)
2702 {
2703 int i;
2704 int lx, ly;
2705
2706 if (n <= 0) {
2707 return;
2708 }
2709
2710 lx = p->x;
2711 ly = p->y;
2712 for (i = 1; i < n; i++) {
2713 p++;
2714 gdImageLine(im, lx, ly, p->x, p->y, c);
2715 lx = p->x;
2716 ly = p->y;
2717 }
2718 }
2719
2720 int gdCompareInt (const void *a, const void *b);
2721
2722 /* THANKS to Kirsten Schulz for the polygon fixes! */
2723
2724 /* The intersection finding technique of this code could be improved
2725 * by remembering the previous intertersection, and by using the slope.
2726 * That could help to adjust intersections to produce a nice
2727 * interior_extrema.
2728 */
2729
gdImageFilledPolygon(gdImagePtr im,gdPointPtr p,int n,int c)2730 void gdImageFilledPolygon (gdImagePtr im, gdPointPtr p, int n, int c)
2731 {
2732 int i;
2733 int y;
2734 int miny, maxy, pmaxy;
2735 int x1, y1;
2736 int x2, y2;
2737 int ind1, ind2;
2738 int ints;
2739 int fill_color;
2740
2741 if (n <= 0) {
2742 return;
2743 }
2744
2745 if (overflow2(sizeof(int), n)) {
2746 return;
2747 }
2748
2749 if (c == gdAntiAliased) {
2750 fill_color = im->AA_color;
2751 } else {
2752 fill_color = c;
2753 }
2754
2755 if (!im->polyAllocated) {
2756 im->polyInts = (int *) gdMalloc(sizeof(int) * n);
2757 im->polyAllocated = n;
2758 }
2759 if (im->polyAllocated < n) {
2760 while (im->polyAllocated < n) {
2761 im->polyAllocated *= 2;
2762 }
2763 if (overflow2(sizeof(int), im->polyAllocated)) {
2764 return;
2765 }
2766 im->polyInts = (int *) gdRealloc(im->polyInts, sizeof(int) * im->polyAllocated);
2767 }
2768 miny = p[0].y;
2769 maxy = p[0].y;
2770 for (i = 1; i < n; i++) {
2771 if (p[i].y < miny) {
2772 miny = p[i].y;
2773 }
2774 if (p[i].y > maxy) {
2775 maxy = p[i].y;
2776 }
2777 }
2778 /* necessary special case: horizontal line */
2779 if (n > 1 && miny == maxy) {
2780 x1 = x2 = p[0].x;
2781 for (i = 1; (i < n); i++) {
2782 if (p[i].x < x1) {
2783 x1 = p[i].x;
2784 } else if (p[i].x > x2) {
2785 x2 = p[i].x;
2786 }
2787 }
2788 gdImageLine(im, x1, miny, x2, miny, c);
2789 return;
2790 }
2791 pmaxy = maxy;
2792 /* 2.0.16: Optimization by Ilia Chipitsine -- don't waste time offscreen */
2793 if (miny < 0) {
2794 miny = 0;
2795 }
2796 if (maxy >= gdImageSY(im)) {
2797 maxy = gdImageSY(im) - 1;
2798 }
2799
2800 /* Fix in 1.3: count a vertex only once */
2801 for (y = miny; y <= maxy; y++) {
2802 /*1.4 int interLast = 0; */
2803 /* int dirLast = 0; */
2804 /* int interFirst = 1; */
2805 ints = 0;
2806 for (i = 0; i < n; i++) {
2807 if (!i) {
2808 ind1 = n - 1;
2809 ind2 = 0;
2810 } else {
2811 ind1 = i - 1;
2812 ind2 = i;
2813 }
2814 y1 = p[ind1].y;
2815 y2 = p[ind2].y;
2816 if (y1 < y2) {
2817 x1 = p[ind1].x;
2818 x2 = p[ind2].x;
2819 } else if (y1 > y2) {
2820 y2 = p[ind1].y;
2821 y1 = p[ind2].y;
2822 x2 = p[ind1].x;
2823 x1 = p[ind2].x;
2824 } else {
2825 continue;
2826 }
2827 /* Do the following math as float intermediately, and round to ensure
2828 * that Polygon and FilledPolygon for the same set of points have the
2829 * same footprint.
2830 */
2831 if (y >= y1 && y < y2) {
2832 im->polyInts[ints++] = (float) ((y - y1) * (x2 - x1)) / (float) (y2 - y1) + 0.5 + x1;
2833 } else if (y == pmaxy && y == y2) {
2834 im->polyInts[ints++] = x2;
2835 }
2836 }
2837 qsort(im->polyInts, ints, sizeof(int), gdCompareInt);
2838
2839 for (i = 0; i < ints - 1; i += 2) {
2840 gdImageLine(im, im->polyInts[i], y, im->polyInts[i + 1], y, fill_color);
2841 }
2842 }
2843
2844 /* If we are drawing this AA, then redraw the border with AA lines. */
2845 if (c == gdAntiAliased) {
2846 gdImagePolygon(im, p, n, c);
2847 }
2848 }
2849
gdCompareInt(const void * a,const void * b)2850 int gdCompareInt (const void *a, const void *b)
2851 {
2852 return (*(const int *) a) - (*(const int *) b);
2853 }
2854
gdImageSetStyle(gdImagePtr im,int * style,int noOfPixels)2855 void gdImageSetStyle (gdImagePtr im, int *style, int noOfPixels)
2856 {
2857 if (im->style) {
2858 gdFree(im->style);
2859 }
2860 if (overflow2(sizeof (int), noOfPixels)) {
2861 return;
2862 }
2863 im->style = (int *) gdMalloc(sizeof(int) * noOfPixels);
2864 memcpy(im->style, style, sizeof(int) * noOfPixels);
2865 im->styleLength = noOfPixels;
2866 im->stylePos = 0;
2867 }
2868
gdImageSetThickness(gdImagePtr im,int thickness)2869 void gdImageSetThickness (gdImagePtr im, int thickness)
2870 {
2871 im->thick = thickness;
2872 }
2873
gdImageSetBrush(gdImagePtr im,gdImagePtr brush)2874 void gdImageSetBrush (gdImagePtr im, gdImagePtr brush)
2875 {
2876 int i;
2877 im->brush = brush;
2878 if (!im->trueColor && !im->brush->trueColor) {
2879 for (i = 0; i < gdImageColorsTotal(brush); i++) {
2880 int index;
2881 index = gdImageColorResolveAlpha(im, gdImageRed(brush, i), gdImageGreen(brush, i), gdImageBlue(brush, i), gdImageAlpha(brush, i));
2882 im->brushColorMap[i] = index;
2883 }
2884 }
2885 }
2886
gdImageSetTile(gdImagePtr im,gdImagePtr tile)2887 void gdImageSetTile (gdImagePtr im, gdImagePtr tile)
2888 {
2889 int i;
2890 im->tile = tile;
2891 if (!im->trueColor && !im->tile->trueColor) {
2892 for (i = 0; i < gdImageColorsTotal(tile); i++) {
2893 int index;
2894 index = gdImageColorResolveAlpha(im, gdImageRed(tile, i), gdImageGreen(tile, i), gdImageBlue(tile, i), gdImageAlpha(tile, i));
2895 im->tileColorMap[i] = index;
2896 }
2897 }
2898 }
2899
gdImageSetAntiAliased(gdImagePtr im,int c)2900 void gdImageSetAntiAliased (gdImagePtr im, int c)
2901 {
2902 im->AA = 1;
2903 im->AA_color = c;
2904 im->AA_dont_blend = -1;
2905 }
2906
gdImageSetAntiAliasedDontBlend(gdImagePtr im,int c,int dont_blend)2907 void gdImageSetAntiAliasedDontBlend (gdImagePtr im, int c, int dont_blend)
2908 {
2909 im->AA = 1;
2910 im->AA_color = c;
2911 im->AA_dont_blend = dont_blend;
2912 }
2913
2914
gdImageInterlace(gdImagePtr im,int interlaceArg)2915 void gdImageInterlace (gdImagePtr im, int interlaceArg)
2916 {
2917 im->interlace = interlaceArg;
2918 }
2919
gdImageCompare(gdImagePtr im1,gdImagePtr im2)2920 int gdImageCompare (gdImagePtr im1, gdImagePtr im2)
2921 {
2922 int x, y;
2923 int p1, p2;
2924 int cmpStatus = 0;
2925 int sx, sy;
2926
2927 if (im1->interlace != im2->interlace) {
2928 cmpStatus |= GD_CMP_INTERLACE;
2929 }
2930
2931 if (im1->transparent != im2->transparent) {
2932 cmpStatus |= GD_CMP_TRANSPARENT;
2933 }
2934
2935 if (im1->trueColor != im2->trueColor) {
2936 cmpStatus |= GD_CMP_TRUECOLOR;
2937 }
2938
2939 sx = im1->sx;
2940 if (im1->sx != im2->sx) {
2941 cmpStatus |= GD_CMP_SIZE_X + GD_CMP_IMAGE;
2942 if (im2->sx < im1->sx) {
2943 sx = im2->sx;
2944 }
2945 }
2946
2947 sy = im1->sy;
2948 if (im1->sy != im2->sy) {
2949 cmpStatus |= GD_CMP_SIZE_Y + GD_CMP_IMAGE;
2950 if (im2->sy < im1->sy) {
2951 sy = im2->sy;
2952 }
2953 }
2954
2955 if (im1->colorsTotal != im2->colorsTotal) {
2956 cmpStatus |= GD_CMP_NUM_COLORS;
2957 }
2958
2959 for (y = 0; y < sy; y++) {
2960 for (x = 0; x < sx; x++) {
2961 p1 = im1->trueColor ? gdImageTrueColorPixel(im1, x, y) : gdImagePalettePixel(im1, x, y);
2962 p2 = im2->trueColor ? gdImageTrueColorPixel(im2, x, y) : gdImagePalettePixel(im2, x, y);
2963
2964 if (gdImageRed(im1, p1) != gdImageRed(im2, p2)) {
2965 cmpStatus |= GD_CMP_COLOR + GD_CMP_IMAGE;
2966 break;
2967 }
2968 if (gdImageGreen(im1, p1) != gdImageGreen(im2, p2)) {
2969 cmpStatus |= GD_CMP_COLOR + GD_CMP_IMAGE;
2970 break;
2971 }
2972 if (gdImageBlue(im1, p1) != gdImageBlue(im2, p2)) {
2973 cmpStatus |= GD_CMP_COLOR + GD_CMP_IMAGE;
2974 break;
2975 }
2976 #if 0
2977 /* Soon we'll add alpha channel to palettes */
2978 if (gdImageAlpha(im1, p1) != gdImageAlpha(im2, p2)) {
2979 cmpStatus |= GD_CMP_COLOR + GD_CMP_IMAGE;
2980 break;
2981 }
2982 #endif
2983 }
2984 if (cmpStatus & GD_CMP_COLOR) {
2985 break;
2986 }
2987 }
2988
2989 return cmpStatus;
2990 }
2991
gdAlphaBlend(int dst,int src)2992 int gdAlphaBlend (int dst, int src) {
2993 int src_alpha = gdTrueColorGetAlpha(src);
2994 int dst_alpha, alpha, red, green, blue;
2995 int src_weight, dst_weight, tot_weight;
2996
2997 /* -------------------------------------------------------------------- */
2998 /* Simple cases we want to handle fast. */
2999 /* -------------------------------------------------------------------- */
3000 if( src_alpha == gdAlphaOpaque )
3001 return src;
3002
3003 dst_alpha = gdTrueColorGetAlpha(dst);
3004 if( src_alpha == gdAlphaTransparent )
3005 return dst;
3006 if( dst_alpha == gdAlphaTransparent )
3007 return src;
3008
3009 /* -------------------------------------------------------------------- */
3010 /* What will the source and destination alphas be? Note that */
3011 /* the destination weighting is substantially reduced as the */
3012 /* overlay becomes quite opaque. */
3013 /* -------------------------------------------------------------------- */
3014 src_weight = gdAlphaTransparent - src_alpha;
3015 dst_weight = (gdAlphaTransparent - dst_alpha) * src_alpha / gdAlphaMax;
3016 tot_weight = src_weight + dst_weight;
3017
3018 /* -------------------------------------------------------------------- */
3019 /* What red, green and blue result values will we use? */
3020 /* -------------------------------------------------------------------- */
3021 alpha = src_alpha * dst_alpha / gdAlphaMax;
3022
3023 red = (gdTrueColorGetRed(src) * src_weight
3024 + gdTrueColorGetRed(dst) * dst_weight) / tot_weight;
3025 green = (gdTrueColorGetGreen(src) * src_weight
3026 + gdTrueColorGetGreen(dst) * dst_weight) / tot_weight;
3027 blue = (gdTrueColorGetBlue(src) * src_weight
3028 + gdTrueColorGetBlue(dst) * dst_weight) / tot_weight;
3029
3030 /* -------------------------------------------------------------------- */
3031 /* Return merged result. */
3032 /* -------------------------------------------------------------------- */
3033 return ((alpha << 24) + (red << 16) + (green << 8) + blue);
3034
3035 }
3036
gdImageAlphaBlending(gdImagePtr im,int alphaBlendingArg)3037 void gdImageAlphaBlending (gdImagePtr im, int alphaBlendingArg)
3038 {
3039 im->alphaBlendingFlag = alphaBlendingArg;
3040 }
3041
gdImageSaveAlpha(gdImagePtr im,int saveAlphaArg)3042 void gdImageSaveAlpha (gdImagePtr im, int saveAlphaArg)
3043 {
3044 im->saveAlphaFlag = saveAlphaArg;
3045 }
3046
gdLayerOverlay(int dst,int src)3047 int gdLayerOverlay (int dst, int src)
3048 {
3049 int a1, a2;
3050 a1 = gdAlphaMax - gdTrueColorGetAlpha(dst);
3051 a2 = gdAlphaMax - gdTrueColorGetAlpha(src);
3052 return ( ((gdAlphaMax - a1*a2/gdAlphaMax) << 24) +
3053 (gdAlphaOverlayColor( gdTrueColorGetRed(src), gdTrueColorGetRed(dst), gdRedMax ) << 16) +
3054 (gdAlphaOverlayColor( gdTrueColorGetGreen(src), gdTrueColorGetGreen(dst), gdGreenMax ) << 8) +
3055 (gdAlphaOverlayColor( gdTrueColorGetBlue(src), gdTrueColorGetBlue(dst), gdBlueMax ))
3056 );
3057 }
3058
gdAlphaOverlayColor(int src,int dst,int max)3059 static int gdAlphaOverlayColor (int src, int dst, int max )
3060 {
3061 /* this function implements the algorithm
3062 *
3063 * for dst[rgb] < 0.5,
3064 * c[rgb] = 2.src[rgb].dst[rgb]
3065 * and for dst[rgb] > 0.5,
3066 * c[rgb] = -2.src[rgb].dst[rgb] + 2.dst[rgb] + 2.src[rgb] - 1
3067 *
3068 */
3069
3070 dst = dst << 1;
3071 if( dst > max ) {
3072 /* in the "light" zone */
3073 return dst + (src << 1) - (dst * src / max) - max;
3074 } else {
3075 /* in the "dark" zone */
3076 return dst * src / max;
3077 }
3078 }
3079
gdLayerMultiply(int dst,int src)3080 int gdLayerMultiply (int dst, int src)
3081 {
3082 int a1, a2, r1, r2, g1, g2, b1, b2;
3083 a1 = gdAlphaMax - gdTrueColorGetAlpha(src);
3084 a2 = gdAlphaMax - gdTrueColorGetAlpha(dst);
3085
3086 r1 = gdRedMax - (a1 * (gdRedMax - gdTrueColorGetRed(src))) / gdAlphaMax;
3087 r2 = gdRedMax - (a2 * (gdRedMax - gdTrueColorGetRed(dst))) / gdAlphaMax;
3088 g1 = gdGreenMax - (a1 * (gdGreenMax - gdTrueColorGetGreen(src))) / gdAlphaMax;
3089 g2 = gdGreenMax - (a2 * (gdGreenMax - gdTrueColorGetGreen(dst))) / gdAlphaMax;
3090 b1 = gdBlueMax - (a1 * (gdBlueMax - gdTrueColorGetBlue(src))) / gdAlphaMax;
3091 b2 = gdBlueMax - (a2 * (gdBlueMax - gdTrueColorGetBlue(dst))) / gdAlphaMax ;
3092
3093 a1 = gdAlphaMax - a1;
3094 a2 = gdAlphaMax - a2;
3095 return ( ((a1*a2/gdAlphaMax) << 24) +
3096 ((r1*r2/gdRedMax) << 16) +
3097 ((g1*g2/gdGreenMax) << 8) +
3098 ((b1*b2/gdBlueMax))
3099 );
3100 }
3101
gdImageSetClip(gdImagePtr im,int x1,int y1,int x2,int y2)3102 void gdImageSetClip (gdImagePtr im, int x1, int y1, int x2, int y2)
3103 {
3104 if (x1 < 0) {
3105 x1 = 0;
3106 }
3107 if (x1 >= im->sx) {
3108 x1 = im->sx - 1;
3109 }
3110 if (x2 < 0) {
3111 x2 = 0;
3112 }
3113 if (x2 >= im->sx) {
3114 x2 = im->sx - 1;
3115 }
3116 if (y1 < 0) {
3117 y1 = 0;
3118 }
3119 if (y1 >= im->sy) {
3120 y1 = im->sy - 1;
3121 }
3122 if (y2 < 0) {
3123 y2 = 0;
3124 }
3125 if (y2 >= im->sy) {
3126 y2 = im->sy - 1;
3127 }
3128 im->cx1 = x1;
3129 im->cy1 = y1;
3130 im->cx2 = x2;
3131 im->cy2 = y2;
3132 }
3133
gdImageGetClip(gdImagePtr im,int * x1P,int * y1P,int * x2P,int * y2P)3134 void gdImageGetClip (gdImagePtr im, int *x1P, int *y1P, int *x2P, int *y2P)
3135 {
3136 *x1P = im->cx1;
3137 *y1P = im->cy1;
3138 *x2P = im->cx2;
3139 *y2P = im->cy2;
3140 }
3141
gdImageSetResolution(gdImagePtr im,const unsigned int res_x,const unsigned int res_y)3142 void gdImageSetResolution(gdImagePtr im, const unsigned int res_x, const unsigned int res_y)
3143 {
3144 if (res_x > 0) im->res_x = res_x;
3145 if (res_y > 0) im->res_y = res_y;
3146 }
3147
3148 /* convert a palette image to true color */
gdImagePaletteToTrueColor(gdImagePtr src)3149 int gdImagePaletteToTrueColor(gdImagePtr src)
3150 {
3151 unsigned int y;
3152 unsigned int yy;
3153
3154 if (src == NULL) {
3155 return 0;
3156 }
3157
3158 if (src->trueColor == 1) {
3159 return 1;
3160 } else {
3161 unsigned int x;
3162 const unsigned int sy = gdImageSY(src);
3163 const unsigned int sx = gdImageSX(src);
3164
3165 // Note: do not revert back to gdMalloc() below ; reason here,
3166 // due to a bug with a certain memory_limit INI value treshold,
3167 // imagepalettetotruecolor crashes with even unrelated ZendMM allocations.
3168 // See GH-17772 for an use case.
3169 src->tpixels = (int **) gdCalloc(sizeof(int *), sy);
3170 if (src->tpixels == NULL) {
3171 return 0;
3172 }
3173
3174 for (y = 0; y < sy; y++) {
3175 const unsigned char *src_row = src->pixels[y];
3176 int * dst_row;
3177
3178 /* no need to calloc it, we overwrite all pxl anyway */
3179 src->tpixels[y] = (int *) gdMalloc(sx * sizeof(int));
3180 if (src->tpixels[y] == NULL) {
3181 goto clean_on_error;
3182 }
3183
3184 dst_row = src->tpixels[y];
3185 for (x = 0; x < sx; x++) {
3186 const unsigned char c = *(src_row + x);
3187 if (c == src->transparent) {
3188 *(dst_row + x) = gdTrueColorAlpha(0, 0, 0, 127);
3189 } else {
3190 *(dst_row + x) = gdTrueColorAlpha(src->red[c], src->green[c], src->blue[c], src->alpha[c]);
3191 }
3192 }
3193 }
3194 }
3195
3196 /* free old palette buffer (y is sy) */
3197 for (yy = 0; yy < y; yy++) {
3198 gdFree(src->pixels[yy]);
3199 }
3200 gdFree(src->pixels);
3201 src->trueColor = 1;
3202 src->pixels = NULL;
3203 src->alphaBlendingFlag = 0;
3204 src->saveAlphaFlag = 1;
3205
3206 if (src->transparent >= 0) {
3207 const unsigned char c = src->transparent;
3208 src->transparent = gdTrueColorAlpha(src->red[c], src->green[c], src->blue[c], src->alpha[c]);
3209 }
3210
3211 return 1;
3212
3213 clean_on_error:
3214 /* free new true color buffer (y is not allocated, have failed) */
3215 for (yy = 0; yy < y; yy++) {
3216 gdFree(src->tpixels[yy]);
3217 }
3218 gdFree(src->tpixels);
3219 return 0;
3220 }
3221