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