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 wid = (int)(thick * sin(atan2(dy, dx)));
1396 vert = 1;
1397
1398 d = 2 * dy - dx;
1399 incr1 = 2 * dy;
1400 incr2 = 2 * (dy - dx);
1401 if (x1 > x2) {
1402 x = x2;
1403 y = y2;
1404 ydirflag = (-1);
1405 xend = x1;
1406 } else {
1407 x = x1;
1408 y = y1;
1409 ydirflag = 1;
1410 xend = x2;
1411 }
1412 dashedSet(im, x, y, color, &on, &dashStep, wid, vert);
1413 if (((y2 - y1) * ydirflag) > 0) {
1414 while (x < xend) {
1415 x++;
1416 if (d < 0) {
1417 d += incr1;
1418 } else {
1419 y++;
1420 d += incr2;
1421 }
1422 dashedSet(im, x, y, color, &on, &dashStep, wid, vert);
1423 }
1424 } else {
1425 while (x < xend) {
1426 x++;
1427 if (d < 0) {
1428 d += incr1;
1429 } else {
1430 y--;
1431 d += incr2;
1432 }
1433 dashedSet(im, x, y, color, &on, &dashStep, wid, vert);
1434 }
1435 }
1436 } else {
1437 /* 2.0.12: Michael Schwartz: divide rather than multiply;
1438 TBB: but watch out for /0! */
1439 double as = sin (atan2 (dy, dx));
1440 if (as != 0) {
1441 wid = thick / as;
1442 } else {
1443 wid = 1;
1444 }
1445 vert = 0;
1446
1447 d = 2 * dx - dy;
1448 incr1 = 2 * dx;
1449 incr2 = 2 * (dx - dy);
1450 if (y1 > y2) {
1451 y = y2;
1452 x = x2;
1453 yend = y1;
1454 xdirflag = (-1);
1455 } else {
1456 y = y1;
1457 x = x1;
1458 yend = y2;
1459 xdirflag = 1;
1460 }
1461 dashedSet(im, x, y, color, &on, &dashStep, wid, vert);
1462 if (((x2 - x1) * xdirflag) > 0) {
1463 while (y < yend) {
1464 y++;
1465 if (d < 0) {
1466 d += incr1;
1467 } else {
1468 x++;
1469 d += incr2;
1470 }
1471 dashedSet(im, x, y, color, &on, &dashStep, wid, vert);
1472 }
1473 } else {
1474 while (y < yend) {
1475 y++;
1476 if (d < 0) {
1477 d += incr1;
1478 } else {
1479 x--;
1480 d += incr2;
1481 }
1482 dashedSet(im, x, y, color, &on, &dashStep, wid, vert);
1483 }
1484 }
1485 }
1486 }
1487
dashedSet(gdImagePtr im,int x,int y,int color,int * onP,int * dashStepP,int wid,int vert)1488 static void dashedSet (gdImagePtr im, int x, int y, int color, int *onP, int *dashStepP, int wid, int vert)
1489 {
1490 int dashStep = *dashStepP;
1491 int on = *onP;
1492 int w, wstart;
1493
1494 dashStep++;
1495 if (dashStep == gdDashSize) {
1496 dashStep = 0;
1497 on = !on;
1498 }
1499 if (on) {
1500 if (vert) {
1501 wstart = y - wid / 2;
1502 for (w = wstart; w < wstart + wid; w++) {
1503 gdImageSetPixel(im, x, w, color);
1504 }
1505 } else {
1506 wstart = x - wid / 2;
1507 for (w = wstart; w < wstart + wid; w++) {
1508 gdImageSetPixel(im, w, y, color);
1509 }
1510 }
1511 }
1512 *dashStepP = dashStep;
1513 *onP = on;
1514 }
1515
gdImageChar(gdImagePtr im,gdFontPtr f,int x,int y,int c,int color)1516 void gdImageChar (gdImagePtr im, gdFontPtr f, int x, int y, int c, int color)
1517 {
1518 int cx, cy;
1519 int px, py;
1520 int fline;
1521 cx = 0;
1522 cy = 0;
1523 #ifdef CHARSET_EBCDIC
1524 c = ASC (c);
1525 #endif /*CHARSET_EBCDIC */
1526 if ((c < f->offset) || (c >= (f->offset + f->nchars))) {
1527 return;
1528 }
1529 fline = (c - f->offset) * f->h * f->w;
1530 for (py = y; (py < (y + f->h)); py++) {
1531 for (px = x; (px < (x + f->w)); px++) {
1532 if (f->data[fline + cy * f->w + cx]) {
1533 gdImageSetPixel(im, px, py, color);
1534 }
1535 cx++;
1536 }
1537 cx = 0;
1538 cy++;
1539 }
1540 }
1541
gdImageCharUp(gdImagePtr im,gdFontPtr f,int x,int y,int c,int color)1542 void gdImageCharUp (gdImagePtr im, gdFontPtr f, int x, int y, int c, int color)
1543 {
1544 int cx, cy;
1545 int px, py;
1546 int fline;
1547 cx = 0;
1548 cy = 0;
1549 #ifdef CHARSET_EBCDIC
1550 c = ASC (c);
1551 #endif /*CHARSET_EBCDIC */
1552 if ((c < f->offset) || (c >= (f->offset + f->nchars))) {
1553 return;
1554 }
1555 fline = (c - f->offset) * f->h * f->w;
1556 for (py = y; py > (y - f->w); py--) {
1557 for (px = x; px < (x + f->h); px++) {
1558 if (f->data[fline + cy * f->w + cx]) {
1559 gdImageSetPixel(im, px, py, color);
1560 }
1561 cy++;
1562 }
1563 cy = 0;
1564 cx++;
1565 }
1566 }
1567
gdImageString(gdImagePtr im,gdFontPtr f,int x,int y,unsigned char * s,int color)1568 void gdImageString (gdImagePtr im, gdFontPtr f, int x, int y, unsigned char *s, int color)
1569 {
1570 int i;
1571 int l;
1572 l = strlen ((char *) s);
1573 for (i = 0; (i < l); i++) {
1574 gdImageChar(im, f, x, y, s[i], color);
1575 x += f->w;
1576 }
1577 }
1578
gdImageStringUp(gdImagePtr im,gdFontPtr f,int x,int y,unsigned char * s,int color)1579 void gdImageStringUp (gdImagePtr im, gdFontPtr f, int x, int y, unsigned char *s, int color)
1580 {
1581 int i;
1582 int l;
1583 l = strlen ((char *) s);
1584 for (i = 0; (i < l); i++) {
1585 gdImageCharUp(im, f, x, y, s[i], color);
1586 y -= f->w;
1587 }
1588 }
1589
1590 static int strlen16 (unsigned short *s);
1591
gdImageString16(gdImagePtr im,gdFontPtr f,int x,int y,unsigned short * s,int color)1592 void gdImageString16 (gdImagePtr im, gdFontPtr f, int x, int y, unsigned short *s, int color)
1593 {
1594 int i;
1595 int l;
1596 l = strlen16(s);
1597 for (i = 0; (i < l); i++) {
1598 gdImageChar(im, f, x, y, s[i], color);
1599 x += f->w;
1600 }
1601 }
1602
gdImageStringUp16(gdImagePtr im,gdFontPtr f,int x,int y,unsigned short * s,int color)1603 void gdImageStringUp16 (gdImagePtr im, gdFontPtr f, int x, int y, unsigned short *s, int color)
1604 {
1605 int i;
1606 int l;
1607 l = strlen16(s);
1608 for (i = 0; i < l; i++) {
1609 gdImageCharUp(im, f, x, y, s[i], color);
1610 y -= f->w;
1611 }
1612 }
1613
strlen16(unsigned short * s)1614 static int strlen16 (unsigned short *s)
1615 {
1616 int len = 0;
1617 while (*s) {
1618 s++;
1619 len++;
1620 }
1621 return len;
1622 }
1623
1624 #ifndef HAVE_LSQRT
1625 /* If you don't have a nice square root function for longs, you can use
1626 ** this hack
1627 */
lsqrt(long n)1628 long lsqrt (long n)
1629 {
1630 long result = (long) sqrt ((double) n);
1631 return result;
1632 }
1633 #endif
1634
1635 /* s and e are integers modulo 360 (degrees), with 0 degrees
1636 being the rightmost extreme and degrees changing clockwise.
1637 cx and cy are the center in pixels; w and h are the horizontal
1638 and vertical diameter in pixels. */
1639
gdImageArc(gdImagePtr im,int cx,int cy,int w,int h,int s,int e,int color)1640 void gdImageArc (gdImagePtr im, int cx, int cy, int w, int h, int s, int e, int color)
1641 {
1642 gdImageFilledArc(im, cx, cy, w, h, s, e, color, gdNoFill);
1643 }
1644
gdImageFilledArc(gdImagePtr im,int cx,int cy,int w,int h,int s,int e,int color,int style)1645 void gdImageFilledArc (gdImagePtr im, int cx, int cy, int w, int h, int s, int e, int color, int style)
1646 {
1647 gdPoint pts[363];
1648 int i, pti;
1649 int lx = 0, ly = 0;
1650 int fx = 0, fy = 0;
1651
1652
1653 if ((s % 360) == (e % 360)) {
1654 s = 0; e = 360;
1655 } else {
1656 if (s > 360) {
1657 s = s % 360;
1658 }
1659
1660 if (e > 360) {
1661 e = e % 360;
1662 }
1663
1664 while (s < 0) {
1665 s += 360;
1666 }
1667
1668 while (e < s) {
1669 e += 360;
1670 }
1671 if (s == e) {
1672 s = 0; e = 360;
1673 }
1674 }
1675
1676 for (i = s, pti = 1; i <= e; i++, pti++) {
1677 int x, y;
1678 x = ((long) gdCosT[i % 360] * (long) w / (2 * 1024)) + cx;
1679 y = ((long) gdSinT[i % 360] * (long) h / (2 * 1024)) + cy;
1680 if (i != s) {
1681 if (!(style & gdChord)) {
1682 if (style & gdNoFill) {
1683 gdImageLine(im, lx, ly, x, y, color);
1684 } else {
1685 if (y == ly) {
1686 pti--; /* don't add this point */
1687 if (((i > 270 || i < 90) && x > lx) || ((i > 90 && i < 270) && x < lx)) {
1688 /* replace the old x coord, if increasing on the
1689 right side or decreasing on the left side */
1690 pts[pti].x = x;
1691 }
1692 } else {
1693 pts[pti].x = x;
1694 pts[pti].y = y;
1695 }
1696 }
1697 }
1698 } else {
1699 fx = x;
1700 fy = y;
1701 if (!(style & (gdChord | gdNoFill))) {
1702 pts[0].x = cx;
1703 pts[0].y = cy;
1704 pts[pti].x = x;
1705 pts[pti].y = y;
1706 }
1707 }
1708 lx = x;
1709 ly = y;
1710 }
1711 if (style & gdChord) {
1712 if (style & gdNoFill) {
1713 if (style & gdEdged) {
1714 gdImageLine(im, cx, cy, lx, ly, color);
1715 gdImageLine(im, cx, cy, fx, fy, color);
1716 }
1717 gdImageLine(im, fx, fy, lx, ly, color);
1718 } else {
1719 pts[0].x = fx;
1720 pts[0].y = fy;
1721 pts[1].x = lx;
1722 pts[1].y = ly;
1723 pts[2].x = cx;
1724 pts[2].y = cy;
1725 gdImageFilledPolygon(im, pts, 3, color);
1726 }
1727 } else {
1728 if (style & gdNoFill) {
1729 if (style & gdEdged) {
1730 gdImageLine(im, cx, cy, lx, ly, color);
1731 gdImageLine(im, cx, cy, fx, fy, color);
1732 }
1733 } else {
1734 pts[pti].x = cx;
1735 pts[pti].y = cy;
1736 gdImageFilledPolygon(im, pts, pti+1, color);
1737 }
1738 }
1739 }
1740
gdImageFillToBorder(gdImagePtr im,int x,int y,int border,int color)1741 void gdImageFillToBorder (gdImagePtr im, int x, int y, int border, int color)
1742 {
1743 int lastBorder;
1744 /* Seek left */
1745 int leftLimit = -1, rightLimit;
1746 int i, restoreAlphaBlending = 0;
1747
1748 if (border < 0 || color < 0) {
1749 /* Refuse to fill to a non-solid border */
1750 return;
1751 }
1752
1753 if (!im->trueColor) {
1754 if ((color > (im->colorsTotal - 1)) || (border > (im->colorsTotal - 1)) || (color < 0)) {
1755 return;
1756 }
1757 }
1758
1759 restoreAlphaBlending = im->alphaBlendingFlag;
1760 im->alphaBlendingFlag = 0;
1761
1762 if (x >= im->sx) {
1763 x = im->sx - 1;
1764 } else if (x < 0) {
1765 x = 0;
1766 }
1767 if (y >= im->sy) {
1768 y = im->sy - 1;
1769 } else if (y < 0) {
1770 y = 0;
1771 }
1772
1773 for (i = x; i >= 0; i--) {
1774 if (gdImageGetPixel(im, i, y) == border) {
1775 break;
1776 }
1777 gdImageSetPixel(im, i, y, color);
1778 leftLimit = i;
1779 }
1780 if (leftLimit == -1) {
1781 im->alphaBlendingFlag = restoreAlphaBlending;
1782 return;
1783 }
1784 /* Seek right */
1785 rightLimit = x;
1786 for (i = (x + 1); i < im->sx; i++) {
1787 if (gdImageGetPixel(im, i, y) == border) {
1788 break;
1789 }
1790 gdImageSetPixel(im, i, y, color);
1791 rightLimit = i;
1792 }
1793 /* Look at lines above and below and start paints */
1794 /* Above */
1795 if (y > 0) {
1796 lastBorder = 1;
1797 for (i = leftLimit; i <= rightLimit; i++) {
1798 int c = gdImageGetPixel(im, i, y - 1);
1799 if (lastBorder) {
1800 if ((c != border) && (c != color)) {
1801 gdImageFillToBorder(im, i, y - 1, border, color);
1802 lastBorder = 0;
1803 }
1804 } else if ((c == border) || (c == color)) {
1805 lastBorder = 1;
1806 }
1807 }
1808 }
1809
1810 /* Below */
1811 if (y < ((im->sy) - 1)) {
1812 lastBorder = 1;
1813 for (i = leftLimit; i <= rightLimit; i++) {
1814 int c = gdImageGetPixel(im, i, y + 1);
1815
1816 if (lastBorder) {
1817 if ((c != border) && (c != color)) {
1818 gdImageFillToBorder(im, i, y + 1, border, color);
1819 lastBorder = 0;
1820 }
1821 } else if ((c == border) || (c == color)) {
1822 lastBorder = 1;
1823 }
1824 }
1825 }
1826 im->alphaBlendingFlag = restoreAlphaBlending;
1827 }
1828
1829 /*
1830 * set the pixel at (x,y) and its 4-connected neighbors
1831 * with the same pixel value to the new pixel value nc (new color).
1832 * A 4-connected neighbor: pixel above, below, left, or right of a pixel.
1833 * ideas from comp.graphics discussions.
1834 * For tiled fill, the use of a flag buffer is mandatory. As the tile image can
1835 * contain the same color as the color to fill. To do not bloat normal filling
1836 * code I added a 2nd private function.
1837 */
1838
1839 /* horizontal segment of scan line y */
1840 struct seg {int y, xl, xr, dy;};
1841
1842 /* max depth of stack */
1843 #define FILL_MAX ((int)(im->sy*im->sx)/4)
1844 #define FILL_PUSH(Y, XL, XR, DY) \
1845 if (sp<stack+FILL_MAX && Y+(DY)>=0 && Y+(DY)<wy2) \
1846 {sp->y = Y; sp->xl = XL; sp->xr = XR; sp->dy = DY; sp++;}
1847
1848 #define FILL_POP(Y, XL, XR, DY) \
1849 {sp--; Y = sp->y+(DY = sp->dy); XL = sp->xl; XR = sp->xr;}
1850
1851 static void _gdImageFillTiled(gdImagePtr im, int x, int y, int nc);
1852
gdImageFill(gdImagePtr im,int x,int y,int nc)1853 void gdImageFill(gdImagePtr im, int x, int y, int nc)
1854 {
1855 int l, x1, x2, dy;
1856 int oc; /* old pixel value */
1857 int wx2,wy2;
1858
1859 int alphablending_bak;
1860
1861 /* stack of filled segments */
1862 /* struct seg stack[FILL_MAX],*sp = stack;; */
1863 struct seg *stack = NULL;
1864 struct seg *sp;
1865
1866 if (!im->trueColor && nc > (im->colorsTotal -1)) {
1867 return;
1868 }
1869
1870 alphablending_bak = im->alphaBlendingFlag;
1871 im->alphaBlendingFlag = 0;
1872
1873 if (nc==gdTiled){
1874 _gdImageFillTiled(im,x,y,nc);
1875 im->alphaBlendingFlag = alphablending_bak;
1876 return;
1877 }
1878
1879 wx2=im->sx;wy2=im->sy;
1880 oc = gdImageGetPixel(im, x, y);
1881 if (oc==nc || x<0 || x>wx2 || y<0 || y>wy2) {
1882 im->alphaBlendingFlag = alphablending_bak;
1883 return;
1884 }
1885
1886 /* Do not use the 4 neighbors implementation with
1887 * small images
1888 */
1889 if (im->sx < 4) {
1890 int ix = x, iy = y, c;
1891 do {
1892 do {
1893 c = gdImageGetPixel(im, ix, iy);
1894 if (c != oc) {
1895 goto done;
1896 }
1897 gdImageSetPixel(im, ix, iy, nc);
1898 } while(ix++ < (im->sx -1));
1899 ix = x;
1900 } while(iy++ < (im->sy -1));
1901 goto done;
1902 }
1903
1904 stack = (struct seg *)safe_emalloc(sizeof(struct seg), ((int)(im->sy*im->sx)/4), 1);
1905 sp = stack;
1906
1907 /* required! */
1908 FILL_PUSH(y,x,x,1);
1909 /* seed segment (popped 1st) */
1910 FILL_PUSH(y+1, x, x, -1);
1911 while (sp>stack) {
1912 FILL_POP(y, x1, x2, dy);
1913
1914 for (x=x1; x>=0 && gdImageGetPixel(im,x, y)==oc; x--) {
1915 gdImageSetPixel(im,x, y, nc);
1916 }
1917 if (x>=x1) {
1918 goto skip;
1919 }
1920 l = x+1;
1921
1922 /* leak on left? */
1923 if (l<x1) {
1924 FILL_PUSH(y, l, x1-1, -dy);
1925 }
1926 x = x1+1;
1927 do {
1928 for (; x<=wx2 && gdImageGetPixel(im,x, y)==oc; x++) {
1929 gdImageSetPixel(im, x, y, nc);
1930 }
1931 FILL_PUSH(y, l, x-1, dy);
1932 /* leak on right? */
1933 if (x>x2+1) {
1934 FILL_PUSH(y, x2+1, x-1, -dy);
1935 }
1936 skip: for (x++; x<=x2 && (gdImageGetPixel(im, x, y)!=oc); x++);
1937
1938 l = x;
1939 } while (x<=x2);
1940 }
1941
1942 efree(stack);
1943
1944 done:
1945 im->alphaBlendingFlag = alphablending_bak;
1946 }
1947
_gdImageFillTiled(gdImagePtr im,int x,int y,int nc)1948 static void _gdImageFillTiled(gdImagePtr im, int x, int y, int nc)
1949 {
1950 int i, l, x1, x2, dy;
1951 int oc; /* old pixel value */
1952 int wx2,wy2;
1953 /* stack of filled segments */
1954 struct seg *stack;
1955 struct seg *sp;
1956 char **pts;
1957
1958 if (!im->tile) {
1959 return;
1960 }
1961
1962 wx2=im->sx;wy2=im->sy;
1963
1964 nc = gdImageTileGet(im,x,y);
1965
1966 pts = (char **) ecalloc(im->sy + 1, sizeof(char *));
1967 for (i = 0; i < im->sy + 1; i++) {
1968 pts[i] = (char *) ecalloc(im->sx + 1, sizeof(char));
1969 }
1970
1971 stack = (struct seg *)safe_emalloc(sizeof(struct seg), ((int)(im->sy*im->sx)/4), 1);
1972 sp = stack;
1973
1974 oc = gdImageGetPixel(im, x, y);
1975
1976 /* required! */
1977 FILL_PUSH(y,x,x,1);
1978 /* seed segment (popped 1st) */
1979 FILL_PUSH(y+1, x, x, -1);
1980 while (sp>stack) {
1981 FILL_POP(y, x1, x2, dy);
1982 for (x=x1; x>=0 && (!pts[y][x] && gdImageGetPixel(im,x,y)==oc); x--) {
1983 nc = gdImageTileGet(im,x,y);
1984 pts[y][x] = 1;
1985 gdImageSetPixel(im,x, y, nc);
1986 }
1987 if (x>=x1) {
1988 goto skip;
1989 }
1990 l = x+1;
1991
1992 /* leak on left? */
1993 if (l<x1) {
1994 FILL_PUSH(y, l, x1-1, -dy);
1995 }
1996 x = x1+1;
1997 do {
1998 for(; x<wx2 && (!pts[y][x] && gdImageGetPixel(im,x, y)==oc); x++) {
1999 nc = gdImageTileGet(im,x,y);
2000 pts[y][x] = 1;
2001 gdImageSetPixel(im, x, y, nc);
2002 }
2003 FILL_PUSH(y, l, x-1, dy);
2004 /* leak on right? */
2005 if (x>x2+1) {
2006 FILL_PUSH(y, x2+1, x-1, -dy);
2007 }
2008 skip: for(x++; x<=x2 && (pts[y][x] || gdImageGetPixel(im,x, y)!=oc); x++);
2009 l = x;
2010 } while (x<=x2);
2011 }
2012
2013 for(i = 0; i < im->sy + 1; i++) {
2014 efree(pts[i]);
2015 }
2016
2017 efree(pts);
2018 efree(stack);
2019 }
2020
2021
2022
gdImageRectangle(gdImagePtr im,int x1,int y1,int x2,int y2,int color)2023 void gdImageRectangle (gdImagePtr im, int x1, int y1, int x2, int y2, int color)
2024 {
2025 int x1h = x1, x1v = x1, y1h = y1, y1v = y1, x2h = x2, x2v = x2, y2h = y2, y2v = y2;
2026 int thick = im->thick;
2027 int t;
2028
2029 if (x1 == x2 && y1 == y2 && thick == 1) {
2030 gdImageSetPixel(im, x1, y1, color);
2031 return;
2032 }
2033
2034 if (y2 < y1) {
2035 t=y1;
2036 y1 = y2;
2037 y2 = t;
2038 }
2039
2040 if (x2 < x1) {
2041 t = x1;
2042 x1 = x2;
2043 x2 = t;
2044 }
2045
2046 x1h = x1; x1v = x1; y1h = y1; y1v = y1; x2h = x2; x2v = x2; y2h = y2; y2v = y2;
2047 if (thick > 1) {
2048 int cx, cy, x1ul, y1ul, x2lr, y2lr;
2049 int half = thick >> 1;
2050
2051 x1ul = x1 - half;
2052 y1ul = y1 - half;
2053
2054 x2lr = x2 + half;
2055 y2lr = y2 + half;
2056
2057 cy = y1ul + thick;
2058 while (cy-- > y1ul) {
2059 cx = x1ul - 1;
2060 while (cx++ < x2lr) {
2061 gdImageSetPixel(im, cx, cy, color);
2062 }
2063 }
2064
2065 cy = y2lr - thick;
2066 while (cy++ < y2lr) {
2067 cx = x1ul - 1;
2068 while (cx++ < x2lr) {
2069 gdImageSetPixel(im, cx, cy, color);
2070 }
2071 }
2072
2073 cy = y1ul + thick - 1;
2074 while (cy++ < y2lr -thick) {
2075 cx = x1ul - 1;
2076 while (cx++ < x1ul + thick) {
2077 gdImageSetPixel(im, cx, cy, color);
2078 }
2079 }
2080
2081 cy = y1ul + thick - 1;
2082 while (cy++ < y2lr -thick) {
2083 cx = x2lr - thick - 1;
2084 while (cx++ < x2lr) {
2085 gdImageSetPixel(im, cx, cy, color);
2086 }
2087 }
2088
2089 return;
2090 } else {
2091 if (x1 == x2 || y1 == y2) {
2092 gdImageLine(im, x1, y1, x2, y2, color);
2093 } else {
2094 y1v = y1h + 1;
2095 y2v = y2h - 1;
2096 gdImageLine(im, x1h, y1h, x2h, y1h, color);
2097 gdImageLine(im, x1h, y2h, x2h, y2h, color);
2098 gdImageLine(im, x1v, y1v, x1v, y2v, color);
2099 gdImageLine(im, x2v, y1v, x2v, y2v, color);
2100 }
2101 }
2102 }
2103
_gdImageFilledHRectangle(gdImagePtr im,int x1,int y1,int x2,int y2,int color)2104 static void _gdImageFilledHRectangle (gdImagePtr im, int x1, int y1, int x2, int y2, int color)
2105 {
2106 int x, y;
2107
2108 if (x1 == x2 && y1 == y2) {
2109 gdImageSetPixel(im, x1, y1, color);
2110 return;
2111 }
2112
2113 if (x1 > x2) {
2114 x = x1;
2115 x1 = x2;
2116 x2 = x;
2117 }
2118
2119 if (y1 > y2) {
2120 y = y1;
2121 y1 = y2;
2122 y2 = y;
2123 }
2124
2125 if (x1 < 0) {
2126 x1 = 0;
2127 }
2128
2129 if (x2 >= gdImageSX(im)) {
2130 x2 = gdImageSX(im) - 1;
2131 }
2132
2133 if (y1 < 0) {
2134 y1 = 0;
2135 }
2136
2137 if (y2 >= gdImageSY(im)) {
2138 y2 = gdImageSY(im) - 1;
2139 }
2140
2141 for (x = x1; (x <= x2); x++) {
2142 for (y = y1; (y <= y2); y++) {
2143 gdImageSetPixel (im, x, y, color);
2144 }
2145 }
2146 }
2147
_gdImageFilledVRectangle(gdImagePtr im,int x1,int y1,int x2,int y2,int color)2148 static void _gdImageFilledVRectangle (gdImagePtr im, int x1, int y1, int x2, int y2, int color)
2149 {
2150 int x, y;
2151
2152 if (x1 == x2 && y1 == y2) {
2153 gdImageSetPixel(im, x1, y1, color);
2154 return;
2155 }
2156
2157 if (x1 > x2) {
2158 x = x1;
2159 x1 = x2;
2160 x2 = x;
2161 }
2162
2163 if (y1 > y2) {
2164 y = y1;
2165 y1 = y2;
2166 y2 = y;
2167 }
2168
2169 if (x1 < 0) {
2170 x1 = 0;
2171 }
2172
2173 if (x2 >= gdImageSX(im)) {
2174 x2 = gdImageSX(im) - 1;
2175 }
2176
2177 if (y1 < 0) {
2178 y1 = 0;
2179 }
2180
2181 if (y2 >= gdImageSY(im)) {
2182 y2 = gdImageSY(im) - 1;
2183 }
2184
2185 for (y = y1; (y <= y2); y++) {
2186 for (x = x1; (x <= x2); x++) {
2187 gdImageSetPixel (im, x, y, color);
2188 }
2189 }
2190 }
2191
gdImageFilledRectangle(gdImagePtr im,int x1,int y1,int x2,int y2,int color)2192 void gdImageFilledRectangle (gdImagePtr im, int x1, int y1, int x2, int y2, int color)
2193 {
2194 _gdImageFilledVRectangle(im, x1, y1, x2, y2, color);
2195 }
2196
gdImageCopy(gdImagePtr dst,gdImagePtr src,int dstX,int dstY,int srcX,int srcY,int w,int h)2197 void gdImageCopy (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int w, int h)
2198 {
2199 int c;
2200 int x, y;
2201 int tox, toy;
2202 int i;
2203 int colorMap[gdMaxColors];
2204
2205 if (dst->trueColor) {
2206 /* 2.0: much easier when the destination is truecolor. */
2207 /* 2.0.10: needs a transparent-index check that is still valid if
2208 * the source is not truecolor. Thanks to Frank Warmerdam.
2209 */
2210
2211 if (src->trueColor) {
2212 for (y = 0; (y < h); y++) {
2213 for (x = 0; (x < w); x++) {
2214 int c = gdImageGetTrueColorPixel (src, srcX + x, srcY + y);
2215 if (c != src->transparent) {
2216 gdImageSetPixel (dst, dstX + x, dstY + y, c);
2217 }
2218 }
2219 }
2220 } else {
2221 /* source is palette based */
2222 for (y = 0; (y < h); y++) {
2223 for (x = 0; (x < w); x++) {
2224 int c = gdImageGetPixel (src, srcX + x, srcY + y);
2225 if (c != src->transparent) {
2226 gdImageSetPixel(dst, dstX + x, dstY + y, gdTrueColorAlpha(src->red[c], src->green[c], src->blue[c], src->alpha[c]));
2227 }
2228 }
2229 }
2230 }
2231 return;
2232 }
2233
2234 /* Palette based to palette based */
2235 for (i = 0; i < gdMaxColors; i++) {
2236 colorMap[i] = (-1);
2237 }
2238 toy = dstY;
2239 for (y = srcY; y < (srcY + h); y++) {
2240 tox = dstX;
2241 for (x = srcX; x < (srcX + w); x++) {
2242 int nc;
2243 int mapTo;
2244 c = gdImageGetPixel (src, x, y);
2245 /* Added 7/24/95: support transparent copies */
2246 if (gdImageGetTransparent (src) == c) {
2247 tox++;
2248 continue;
2249 }
2250 /* Have we established a mapping for this color? */
2251 if (src->trueColor) {
2252 /* 2.05: remap to the palette available in the destination image. This is slow and
2253 * works badly, but it beats crashing! Thanks to Padhrig McCarthy.
2254 */
2255 mapTo = gdImageColorResolveAlpha (dst, gdTrueColorGetRed (c), gdTrueColorGetGreen (c), gdTrueColorGetBlue (c), gdTrueColorGetAlpha (c));
2256 } else if (colorMap[c] == (-1)) {
2257 /* If it's the same image, mapping is trivial */
2258 if (dst == src) {
2259 nc = c;
2260 } else {
2261 /* Get best match possible. This function never returns error. */
2262 nc = gdImageColorResolveAlpha (dst, src->red[c], src->green[c], src->blue[c], src->alpha[c]);
2263 }
2264 colorMap[c] = nc;
2265 mapTo = colorMap[c];
2266 } else {
2267 mapTo = colorMap[c];
2268 }
2269 gdImageSetPixel (dst, tox, toy, mapTo);
2270 tox++;
2271 }
2272 toy++;
2273 }
2274 }
2275
2276 /* This function is a substitute for real alpha channel operations,
2277 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)2278 void gdImageCopyMerge (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int w, int h, int pct)
2279 {
2280 int c, dc;
2281 int x, y;
2282 int tox, toy;
2283 int ncR, ncG, ncB;
2284 toy = dstY;
2285
2286 for (y = srcY; y < (srcY + h); y++) {
2287 tox = dstX;
2288 for (x = srcX; x < (srcX + w); x++) {
2289 int nc;
2290 c = gdImageGetPixel(src, x, y);
2291 /* Added 7/24/95: support transparent copies */
2292 if (gdImageGetTransparent(src) == c) {
2293 tox++;
2294 continue;
2295 }
2296 /* If it's the same image, mapping is trivial */
2297 if (dst == src) {
2298 nc = c;
2299 } else {
2300 dc = gdImageGetPixel(dst, tox, toy);
2301
2302 ncR = (int)(gdImageRed (src, c) * (pct / 100.0) + gdImageRed (dst, dc) * ((100 - pct) / 100.0));
2303 ncG = (int)(gdImageGreen (src, c) * (pct / 100.0) + gdImageGreen (dst, dc) * ((100 - pct) / 100.0));
2304 ncB = (int)(gdImageBlue (src, c) * (pct / 100.0) + gdImageBlue (dst, dc) * ((100 - pct) / 100.0));
2305
2306 /* Find a reasonable color */
2307 nc = gdImageColorResolve (dst, ncR, ncG, ncB);
2308 }
2309 gdImageSetPixel (dst, tox, toy, nc);
2310 tox++;
2311 }
2312 toy++;
2313 }
2314 }
2315
2316 /* This function is a substitute for real alpha channel operations,
2317 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)2318 void gdImageCopyMergeGray (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int w, int h, int pct)
2319 {
2320 int c, dc;
2321 int x, y;
2322 int tox, toy;
2323 int ncR, ncG, ncB;
2324 float g;
2325 toy = dstY;
2326
2327 for (y = srcY; (y < (srcY + h)); y++) {
2328 tox = dstX;
2329 for (x = srcX; (x < (srcX + w)); x++) {
2330 int nc;
2331 c = gdImageGetPixel (src, x, y);
2332 /* Added 7/24/95: support transparent copies */
2333 if (gdImageGetTransparent(src) == c) {
2334 tox++;
2335 continue;
2336 }
2337
2338 /*
2339 * If it's the same image, mapping is NOT trivial since we
2340 * merge with greyscale target, but if pct is 100, the grey
2341 * value is not used, so it becomes trivial. pjw 2.0.12.
2342 */
2343 if (dst == src && pct == 100) {
2344 nc = c;
2345 } else {
2346 dc = gdImageGetPixel(dst, tox, toy);
2347 g = (0.29900f * gdImageRed(dst, dc)) + (0.58700f * gdImageGreen(dst, dc)) + (0.11400f * gdImageBlue(dst, dc));
2348
2349 ncR = (int)(gdImageRed (src, c) * (pct / 100.0f) + g * ((100 - pct) / 100.0));
2350 ncG = (int)(gdImageGreen (src, c) * (pct / 100.0f) + g * ((100 - pct) / 100.0));
2351 ncB = (int)(gdImageBlue (src, c) * (pct / 100.0f) + g * ((100 - pct) / 100.0));
2352
2353
2354 /* First look for an exact match */
2355 nc = gdImageColorExact(dst, ncR, ncG, ncB);
2356 if (nc == (-1)) {
2357 /* No, so try to allocate it */
2358 nc = gdImageColorAllocate(dst, ncR, ncG, ncB);
2359 /* If we're out of colors, go for the closest color */
2360 if (nc == (-1)) {
2361 nc = gdImageColorClosest(dst, ncR, ncG, ncB);
2362 }
2363 }
2364 }
2365 gdImageSetPixel(dst, tox, toy, nc);
2366 tox++;
2367 }
2368 toy++;
2369 }
2370 }
2371
gdImageCopyResized(gdImagePtr dst,gdImagePtr src,int dstX,int dstY,int srcX,int srcY,int dstW,int dstH,int srcW,int srcH)2372 void gdImageCopyResized (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int dstW, int dstH, int srcW, int srcH)
2373 {
2374 int c;
2375 int x, y;
2376 int tox, toy;
2377 int ydest;
2378 int i;
2379 int colorMap[gdMaxColors];
2380 /* Stretch vectors */
2381 int *stx, *sty;
2382
2383 if (overflow2(sizeof(int), srcW)) {
2384 return;
2385 }
2386 if (overflow2(sizeof(int), srcH)) {
2387 return;
2388 }
2389
2390 stx = (int *) gdMalloc (sizeof (int) * srcW);
2391 sty = (int *) gdMalloc (sizeof (int) * srcH);
2392
2393 /* Fixed by Mao Morimoto 2.0.16 */
2394 for (i = 0; (i < srcW); i++) {
2395 stx[i] = dstW * (i+1) / srcW - dstW * i / srcW ;
2396 }
2397 for (i = 0; (i < srcH); i++) {
2398 sty[i] = dstH * (i+1) / srcH - dstH * i / srcH ;
2399 }
2400 for (i = 0; (i < gdMaxColors); i++) {
2401 colorMap[i] = (-1);
2402 }
2403 toy = dstY;
2404 for (y = srcY; (y < (srcY + srcH)); y++) {
2405 for (ydest = 0; (ydest < sty[y - srcY]); ydest++) {
2406 tox = dstX;
2407 for (x = srcX; (x < (srcX + srcW)); x++) {
2408 int nc = 0;
2409 int mapTo;
2410 if (!stx[x - srcX]) {
2411 continue;
2412 }
2413 if (dst->trueColor) {
2414 /* 2.0.9: Thorben Kundinger: Maybe the source image is not a truecolor image */
2415 if (!src->trueColor) {
2416 int tmp = gdImageGetPixel (src, x, y);
2417 mapTo = gdImageGetTrueColorPixel (src, x, y);
2418 if (gdImageGetTransparent (src) == tmp) {
2419 /* 2.0.21, TK: not tox++ */
2420 tox += stx[x - srcX];
2421 continue;
2422 }
2423 } else {
2424 /* TK: old code follows */
2425 mapTo = gdImageGetTrueColorPixel (src, x, y);
2426 /* Added 7/24/95: support transparent copies */
2427 if (gdImageGetTransparent (src) == mapTo) {
2428 /* 2.0.21, TK: not tox++ */
2429 tox += stx[x - srcX];
2430 continue;
2431 }
2432 }
2433 } else {
2434 c = gdImageGetPixel (src, x, y);
2435 /* Added 7/24/95: support transparent copies */
2436 if (gdImageGetTransparent (src) == c) {
2437 tox += stx[x - srcX];
2438 continue;
2439 }
2440 if (src->trueColor) {
2441 /* Remap to the palette available in the destination image. This is slow and works badly. */
2442 mapTo = gdImageColorResolveAlpha(dst, gdTrueColorGetRed(c),
2443 gdTrueColorGetGreen(c),
2444 gdTrueColorGetBlue(c),
2445 gdTrueColorGetAlpha (c));
2446 } else {
2447 /* Have we established a mapping for this color? */
2448 if (colorMap[c] == (-1)) {
2449 /* If it's the same image, mapping is trivial */
2450 if (dst == src) {
2451 nc = c;
2452 } else {
2453 /* Find or create the best match */
2454 /* 2.0.5: can't use gdTrueColorGetRed, etc with palette */
2455 nc = gdImageColorResolveAlpha(dst, gdImageRed(src, c),
2456 gdImageGreen(src, c),
2457 gdImageBlue(src, c),
2458 gdImageAlpha(src, c));
2459 }
2460 colorMap[c] = nc;
2461 }
2462 mapTo = colorMap[c];
2463 }
2464 }
2465 for (i = 0; (i < stx[x - srcX]); i++) {
2466 gdImageSetPixel (dst, tox, toy, mapTo);
2467 tox++;
2468 }
2469 }
2470 toy++;
2471 }
2472 }
2473 gdFree (stx);
2474 gdFree (sty);
2475 }
2476
2477 /* When gd 1.x was first created, floating point was to be avoided.
2478 These days it is often faster than table lookups or integer
2479 arithmetic. The routine below is shamelessly, gloriously
2480 floating point. TBB */
2481
gdImageCopyResampled(gdImagePtr dst,gdImagePtr src,int dstX,int dstY,int srcX,int srcY,int dstW,int dstH,int srcW,int srcH)2482 void gdImageCopyResampled (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int dstW, int dstH, int srcW, int srcH)
2483 {
2484 int x, y;
2485 double sy1, sy2, sx1, sx2;
2486
2487 if (!dst->trueColor) {
2488 gdImageCopyResized (dst, src, dstX, dstY, srcX, srcY, dstW, dstH, srcW, srcH);
2489 return;
2490 }
2491 for (y = dstY; (y < dstY + dstH); y++) {
2492 sy1 = ((double) y - (double) dstY) * (double) srcH / (double) dstH;
2493 sy2 = ((double) (y + 1) - (double) dstY) * (double) srcH / (double) dstH;
2494 for (x = dstX; (x < dstX + dstW); x++) {
2495 double sx, sy;
2496 double spixels = 0;
2497 double red = 0.0, green = 0.0, blue = 0.0, alpha = 0.0;
2498 double alpha_factor, alpha_sum = 0.0, contrib_sum = 0.0;
2499 sx1 = ((double) x - (double) dstX) * (double) srcW / dstW;
2500 sx2 = ((double) (x + 1) - (double) dstX) * (double) srcW / dstW;
2501 sy = sy1;
2502 do {
2503 double yportion;
2504 if (floor_cast(sy) == floor_cast(sy1)) {
2505 yportion = 1.0f - (sy - floor_cast(sy));
2506 if (yportion > sy2 - sy1) {
2507 yportion = sy2 - sy1;
2508 }
2509 sy = floor_cast(sy);
2510 } else if (sy == floorf(sy2)) {
2511 yportion = sy2 - floor_cast(sy2);
2512 } else {
2513 yportion = 1.0f;
2514 }
2515 sx = sx1;
2516 do {
2517 double xportion;
2518 double pcontribution;
2519 int p;
2520 if (floorf(sx) == floor_cast(sx1)) {
2521 xportion = 1.0f - (sx - floor_cast(sx));
2522 if (xportion > sx2 - sx1) {
2523 xportion = sx2 - sx1;
2524 }
2525 sx = floor_cast(sx);
2526 } else if (sx == floorf(sx2)) {
2527 xportion = sx2 - floor_cast(sx2);
2528 } else {
2529 xportion = 1.0f;
2530 }
2531 pcontribution = xportion * yportion;
2532 p = gdImageGetTrueColorPixel(src, (int) sx + srcX, (int) sy + srcY);
2533
2534 alpha_factor = ((gdAlphaMax - gdTrueColorGetAlpha(p))) * pcontribution;
2535 red += gdTrueColorGetRed (p) * alpha_factor;
2536 green += gdTrueColorGetGreen (p) * alpha_factor;
2537 blue += gdTrueColorGetBlue (p) * alpha_factor;
2538 alpha += gdTrueColorGetAlpha (p) * pcontribution;
2539 alpha_sum += alpha_factor;
2540 contrib_sum += pcontribution;
2541 spixels += xportion * yportion;
2542 sx += 1.0f;
2543 }
2544 while (sx < sx2);
2545
2546 sy += 1.0f;
2547 }
2548
2549 while (sy < sy2);
2550
2551 if (spixels != 0.0f) {
2552 red /= spixels;
2553 green /= spixels;
2554 blue /= spixels;
2555 alpha /= spixels;
2556 alpha += 0.5;
2557 }
2558 if ( alpha_sum != 0.0f) {
2559 if( contrib_sum != 0.0f) {
2560 alpha_sum /= contrib_sum;
2561 }
2562 red /= alpha_sum;
2563 green /= alpha_sum;
2564 blue /= alpha_sum;
2565 }
2566 /* Clamping to allow for rounding errors above */
2567 if (red > 255.0f) {
2568 red = 255.0f;
2569 }
2570 if (green > 255.0f) {
2571 green = 255.0f;
2572 }
2573 if (blue > 255.0f) {
2574 blue = 255.0f;
2575 }
2576 if (alpha > gdAlphaMax) {
2577 alpha = gdAlphaMax;
2578 }
2579 gdImageSetPixel(dst, x, y, gdTrueColorAlpha ((int) red, (int) green, (int) blue, (int) alpha));
2580 }
2581 }
2582 }
2583
gdImagePolygon(gdImagePtr im,gdPointPtr p,int n,int c)2584 void gdImagePolygon (gdImagePtr im, gdPointPtr p, int n, int c)
2585 {
2586 int i;
2587 int lx, ly;
2588 typedef void (*image_line)(gdImagePtr im, int x1, int y1, int x2, int y2, int color);
2589 image_line draw_line;
2590
2591 if (n <= 0) {
2592 return;
2593 }
2594
2595 /* Let it be known that we are drawing a polygon so that the opacity
2596 * mask doesn't get cleared after each line.
2597 */
2598 if (c == gdAntiAliased) {
2599 im->AA_polygon = 1;
2600 }
2601
2602 if ( im->antialias) {
2603 draw_line = gdImageAALine;
2604 } else {
2605 draw_line = gdImageLine;
2606 }
2607 lx = p->x;
2608 ly = p->y;
2609 draw_line(im, lx, ly, p[n - 1].x, p[n - 1].y, c);
2610 for (i = 1; i < n; i++) {
2611 p++;
2612 draw_line(im, lx, ly, p->x, p->y, c);
2613 lx = p->x;
2614 ly = p->y;
2615 }
2616
2617 if (c == gdAntiAliased) {
2618 im->AA_polygon = 0;
2619 gdImageAABlend(im);
2620 }
2621 }
2622
2623 int gdCompareInt (const void *a, const void *b);
2624
2625 /* THANKS to Kirsten Schulz for the polygon fixes! */
2626
2627 /* The intersection finding technique of this code could be improved
2628 * by remembering the previous intertersection, and by using the slope.
2629 * That could help to adjust intersections to produce a nice
2630 * interior_extrema.
2631 */
2632
gdImageFilledPolygon(gdImagePtr im,gdPointPtr p,int n,int c)2633 void gdImageFilledPolygon (gdImagePtr im, gdPointPtr p, int n, int c)
2634 {
2635 int i;
2636 int y;
2637 int miny, maxy, pmaxy;
2638 int x1, y1;
2639 int x2, y2;
2640 int ind1, ind2;
2641 int ints;
2642 int fill_color;
2643
2644 if (n <= 0) {
2645 return;
2646 }
2647
2648 if (overflow2(sizeof(int), n)) {
2649 return;
2650 }
2651
2652 if (c == gdAntiAliased) {
2653 fill_color = im->AA_color;
2654 } else {
2655 fill_color = c;
2656 }
2657
2658 if (!im->polyAllocated) {
2659 im->polyInts = (int *) gdMalloc(sizeof(int) * n);
2660 im->polyAllocated = n;
2661 }
2662 if (im->polyAllocated < n) {
2663 while (im->polyAllocated < n) {
2664 im->polyAllocated *= 2;
2665 }
2666 if (overflow2(sizeof(int), im->polyAllocated)) {
2667 return;
2668 }
2669 im->polyInts = (int *) gdRealloc(im->polyInts, sizeof(int) * im->polyAllocated);
2670 }
2671 miny = p[0].y;
2672 maxy = p[0].y;
2673 for (i = 1; i < n; i++) {
2674 if (p[i].y < miny) {
2675 miny = p[i].y;
2676 }
2677 if (p[i].y > maxy) {
2678 maxy = p[i].y;
2679 }
2680 }
2681 /* necessary special case: horizontal line */
2682 if (n > 1 && miny == maxy) {
2683 x1 = x2 = p[0].x;
2684 for (i = 1; (i < n); i++) {
2685 if (p[i].x < x1) {
2686 x1 = p[i].x;
2687 } else if (p[i].x > x2) {
2688 x2 = p[i].x;
2689 }
2690 }
2691 gdImageLine(im, x1, miny, x2, miny, c);
2692 return;
2693 }
2694 pmaxy = maxy;
2695 /* 2.0.16: Optimization by Ilia Chipitsine -- don't waste time offscreen */
2696 if (miny < 0) {
2697 miny = 0;
2698 }
2699 if (maxy >= gdImageSY(im)) {
2700 maxy = gdImageSY(im) - 1;
2701 }
2702
2703 /* Fix in 1.3: count a vertex only once */
2704 for (y = miny; y <= maxy; y++) {
2705 /*1.4 int interLast = 0; */
2706 /* int dirLast = 0; */
2707 /* int interFirst = 1; */
2708 ints = 0;
2709 for (i = 0; i < n; i++) {
2710 if (!i) {
2711 ind1 = n - 1;
2712 ind2 = 0;
2713 } else {
2714 ind1 = i - 1;
2715 ind2 = i;
2716 }
2717 y1 = p[ind1].y;
2718 y2 = p[ind2].y;
2719 if (y1 < y2) {
2720 x1 = p[ind1].x;
2721 x2 = p[ind2].x;
2722 } else if (y1 > y2) {
2723 y2 = p[ind1].y;
2724 y1 = p[ind2].y;
2725 x2 = p[ind1].x;
2726 x1 = p[ind2].x;
2727 } else {
2728 continue;
2729 }
2730 /* Do the following math as float intermediately, and round to ensure
2731 * that Polygon and FilledPolygon for the same set of points have the
2732 * same footprint.
2733 */
2734 if (y >= y1 && y < y2) {
2735 im->polyInts[ints++] = (float) ((y - y1) * (x2 - x1)) / (float) (y2 - y1) + 0.5 + x1;
2736 } else if (y == pmaxy && y == y2) {
2737 im->polyInts[ints++] = x2;
2738 }
2739 }
2740 qsort(im->polyInts, ints, sizeof(int), gdCompareInt);
2741
2742 for (i = 0; i < ints - 1; i += 2) {
2743 gdImageLine(im, im->polyInts[i], y, im->polyInts[i + 1], y, fill_color);
2744 }
2745 }
2746
2747 /* If we are drawing this AA, then redraw the border with AA lines. */
2748 if (c == gdAntiAliased) {
2749 gdImagePolygon(im, p, n, c);
2750 }
2751 }
2752
gdCompareInt(const void * a,const void * b)2753 int gdCompareInt (const void *a, const void *b)
2754 {
2755 return (*(const int *) a) - (*(const int *) b);
2756 }
2757
gdImageSetStyle(gdImagePtr im,int * style,int noOfPixels)2758 void gdImageSetStyle (gdImagePtr im, int *style, int noOfPixels)
2759 {
2760 if (im->style) {
2761 gdFree(im->style);
2762 }
2763 im->style = (int *) gdMalloc(sizeof(int) * noOfPixels);
2764 memcpy(im->style, style, sizeof(int) * noOfPixels);
2765 im->styleLength = noOfPixels;
2766 im->stylePos = 0;
2767 }
2768
gdImageSetThickness(gdImagePtr im,int thickness)2769 void gdImageSetThickness (gdImagePtr im, int thickness)
2770 {
2771 im->thick = thickness;
2772 }
2773
gdImageSetBrush(gdImagePtr im,gdImagePtr brush)2774 void gdImageSetBrush (gdImagePtr im, gdImagePtr brush)
2775 {
2776 int i;
2777 im->brush = brush;
2778 if (!im->trueColor && !im->brush->trueColor) {
2779 for (i = 0; i < gdImageColorsTotal(brush); i++) {
2780 int index;
2781 index = gdImageColorResolveAlpha(im, gdImageRed(brush, i), gdImageGreen(brush, i), gdImageBlue(brush, i), gdImageAlpha(brush, i));
2782 im->brushColorMap[i] = index;
2783 }
2784 }
2785 }
2786
gdImageSetTile(gdImagePtr im,gdImagePtr tile)2787 void gdImageSetTile (gdImagePtr im, gdImagePtr tile)
2788 {
2789 int i;
2790 im->tile = tile;
2791 if (!im->trueColor && !im->tile->trueColor) {
2792 for (i = 0; i < gdImageColorsTotal(tile); i++) {
2793 int index;
2794 index = gdImageColorResolveAlpha(im, gdImageRed(tile, i), gdImageGreen(tile, i), gdImageBlue(tile, i), gdImageAlpha(tile, i));
2795 im->tileColorMap[i] = index;
2796 }
2797 }
2798 }
2799
gdImageSetAntiAliased(gdImagePtr im,int c)2800 void gdImageSetAntiAliased (gdImagePtr im, int c)
2801 {
2802 im->AA = 1;
2803 im->AA_color = c;
2804 im->AA_dont_blend = -1;
2805 }
2806
gdImageSetAntiAliasedDontBlend(gdImagePtr im,int c,int dont_blend)2807 void gdImageSetAntiAliasedDontBlend (gdImagePtr im, int c, int dont_blend)
2808 {
2809 im->AA = 1;
2810 im->AA_color = c;
2811 im->AA_dont_blend = dont_blend;
2812 }
2813
2814
gdImageInterlace(gdImagePtr im,int interlaceArg)2815 void gdImageInterlace (gdImagePtr im, int interlaceArg)
2816 {
2817 im->interlace = interlaceArg;
2818 }
2819
gdImageCompare(gdImagePtr im1,gdImagePtr im2)2820 int gdImageCompare (gdImagePtr im1, gdImagePtr im2)
2821 {
2822 int x, y;
2823 int p1, p2;
2824 int cmpStatus = 0;
2825 int sx, sy;
2826
2827 if (im1->interlace != im2->interlace) {
2828 cmpStatus |= GD_CMP_INTERLACE;
2829 }
2830
2831 if (im1->transparent != im2->transparent) {
2832 cmpStatus |= GD_CMP_TRANSPARENT;
2833 }
2834
2835 if (im1->trueColor != im2->trueColor) {
2836 cmpStatus |= GD_CMP_TRUECOLOR;
2837 }
2838
2839 sx = im1->sx;
2840 if (im1->sx != im2->sx) {
2841 cmpStatus |= GD_CMP_SIZE_X + GD_CMP_IMAGE;
2842 if (im2->sx < im1->sx) {
2843 sx = im2->sx;
2844 }
2845 }
2846
2847 sy = im1->sy;
2848 if (im1->sy != im2->sy) {
2849 cmpStatus |= GD_CMP_SIZE_Y + GD_CMP_IMAGE;
2850 if (im2->sy < im1->sy) {
2851 sy = im2->sy;
2852 }
2853 }
2854
2855 if (im1->colorsTotal != im2->colorsTotal) {
2856 cmpStatus |= GD_CMP_NUM_COLORS;
2857 }
2858
2859 for (y = 0; y < sy; y++) {
2860 for (x = 0; x < sx; x++) {
2861 p1 = im1->trueColor ? gdImageTrueColorPixel(im1, x, y) : gdImagePalettePixel(im1, x, y);
2862 p2 = im2->trueColor ? gdImageTrueColorPixel(im2, x, y) : gdImagePalettePixel(im2, x, y);
2863
2864 if (gdImageRed(im1, p1) != gdImageRed(im2, p2)) {
2865 cmpStatus |= GD_CMP_COLOR + GD_CMP_IMAGE;
2866 break;
2867 }
2868 if (gdImageGreen(im1, p1) != gdImageGreen(im2, p2)) {
2869 cmpStatus |= GD_CMP_COLOR + GD_CMP_IMAGE;
2870 break;
2871 }
2872 if (gdImageBlue(im1, p1) != gdImageBlue(im2, p2)) {
2873 cmpStatus |= GD_CMP_COLOR + GD_CMP_IMAGE;
2874 break;
2875 }
2876 #if 0
2877 /* Soon we'll add alpha channel to palettes */
2878 if (gdImageAlpha(im1, p1) != gdImageAlpha(im2, p2)) {
2879 cmpStatus |= GD_CMP_COLOR + GD_CMP_IMAGE;
2880 break;
2881 }
2882 #endif
2883 }
2884 if (cmpStatus & GD_CMP_COLOR) {
2885 break;
2886 }
2887 }
2888
2889 return cmpStatus;
2890 }
2891
2892 int
gdAlphaBlendOld(int dst,int src)2893 gdAlphaBlendOld (int dst, int src)
2894 {
2895 /* 2.0.12: TBB: alpha in the destination should be a
2896 * component of the result. Thanks to Frank Warmerdam for
2897 * pointing out the issue.
2898 */
2899 return ((((gdTrueColorGetAlpha (src) *
2900 gdTrueColorGetAlpha (dst)) / gdAlphaMax) << 24) +
2901 ((((gdAlphaTransparent - gdTrueColorGetAlpha (src)) *
2902 gdTrueColorGetRed (src) / gdAlphaMax) +
2903 (gdTrueColorGetAlpha (src) *
2904 gdTrueColorGetRed (dst)) / gdAlphaMax) << 16) +
2905 ((((gdAlphaTransparent - gdTrueColorGetAlpha (src)) *
2906 gdTrueColorGetGreen (src) / gdAlphaMax) +
2907 (gdTrueColorGetAlpha (src) *
2908 gdTrueColorGetGreen (dst)) / gdAlphaMax) << 8) +
2909 (((gdAlphaTransparent - gdTrueColorGetAlpha (src)) *
2910 gdTrueColorGetBlue (src) / gdAlphaMax) +
2911 (gdTrueColorGetAlpha (src) *
2912 gdTrueColorGetBlue (dst)) / gdAlphaMax));
2913 }
2914
gdAlphaBlend(int dst,int src)2915 int gdAlphaBlend (int dst, int src) {
2916 int src_alpha = gdTrueColorGetAlpha(src);
2917 int dst_alpha, alpha, red, green, blue;
2918 int src_weight, dst_weight, tot_weight;
2919
2920 /* -------------------------------------------------------------------- */
2921 /* Simple cases we want to handle fast. */
2922 /* -------------------------------------------------------------------- */
2923 if( src_alpha == gdAlphaOpaque )
2924 return src;
2925
2926 dst_alpha = gdTrueColorGetAlpha(dst);
2927 if( src_alpha == gdAlphaTransparent )
2928 return dst;
2929 if( dst_alpha == gdAlphaTransparent )
2930 return src;
2931
2932 /* -------------------------------------------------------------------- */
2933 /* What will the source and destination alphas be? Note that */
2934 /* the destination weighting is substantially reduced as the */
2935 /* overlay becomes quite opaque. */
2936 /* -------------------------------------------------------------------- */
2937 src_weight = gdAlphaTransparent - src_alpha;
2938 dst_weight = (gdAlphaTransparent - dst_alpha) * src_alpha / gdAlphaMax;
2939 tot_weight = src_weight + dst_weight;
2940
2941 /* -------------------------------------------------------------------- */
2942 /* What red, green and blue result values will we use? */
2943 /* -------------------------------------------------------------------- */
2944 alpha = src_alpha * dst_alpha / gdAlphaMax;
2945
2946 red = (gdTrueColorGetRed(src) * src_weight
2947 + gdTrueColorGetRed(dst) * dst_weight) / tot_weight;
2948 green = (gdTrueColorGetGreen(src) * src_weight
2949 + gdTrueColorGetGreen(dst) * dst_weight) / tot_weight;
2950 blue = (gdTrueColorGetBlue(src) * src_weight
2951 + gdTrueColorGetBlue(dst) * dst_weight) / tot_weight;
2952
2953 /* -------------------------------------------------------------------- */
2954 /* Return merged result. */
2955 /* -------------------------------------------------------------------- */
2956 return ((alpha << 24) + (red << 16) + (green << 8) + blue);
2957
2958 }
2959
gdImageAlphaBlending(gdImagePtr im,int alphaBlendingArg)2960 void gdImageAlphaBlending (gdImagePtr im, int alphaBlendingArg)
2961 {
2962 im->alphaBlendingFlag = alphaBlendingArg;
2963 }
2964
gdImageAntialias(gdImagePtr im,int antialias)2965 void gdImageAntialias (gdImagePtr im, int antialias)
2966 {
2967 if (im->trueColor){
2968 im->antialias = antialias;
2969 }
2970 }
2971
gdImageSaveAlpha(gdImagePtr im,int saveAlphaArg)2972 void gdImageSaveAlpha (gdImagePtr im, int saveAlphaArg)
2973 {
2974 im->saveAlphaFlag = saveAlphaArg;
2975 }
2976
gdLayerOverlay(int dst,int src)2977 static int gdLayerOverlay (int dst, int src)
2978 {
2979 int a1, a2;
2980 a1 = gdAlphaMax - gdTrueColorGetAlpha(dst);
2981 a2 = gdAlphaMax - gdTrueColorGetAlpha(src);
2982 return ( ((gdAlphaMax - a1*a2/gdAlphaMax) << 24) +
2983 (gdAlphaOverlayColor( gdTrueColorGetRed(src), gdTrueColorGetRed(dst), gdRedMax ) << 16) +
2984 (gdAlphaOverlayColor( gdTrueColorGetGreen(src), gdTrueColorGetGreen(dst), gdGreenMax ) << 8) +
2985 (gdAlphaOverlayColor( gdTrueColorGetBlue(src), gdTrueColorGetBlue(dst), gdBlueMax ))
2986 );
2987 }
2988
gdAlphaOverlayColor(int src,int dst,int max)2989 static int gdAlphaOverlayColor (int src, int dst, int max )
2990 {
2991 /* this function implements the algorithm
2992 *
2993 * for dst[rgb] < 0.5,
2994 * c[rgb] = 2.src[rgb].dst[rgb]
2995 * and for dst[rgb] > 0.5,
2996 * c[rgb] = -2.src[rgb].dst[rgb] + 2.dst[rgb] + 2.src[rgb] - 1
2997 *
2998 */
2999
3000 dst = dst << 1;
3001 if( dst > max ) {
3002 /* in the "light" zone */
3003 return dst + (src << 1) - (dst * src / max) - max;
3004 } else {
3005 /* in the "dark" zone */
3006 return dst * src / max;
3007 }
3008 }
3009
gdImageSetClip(gdImagePtr im,int x1,int y1,int x2,int y2)3010 void gdImageSetClip (gdImagePtr im, int x1, int y1, int x2, int y2)
3011 {
3012 if (x1 < 0) {
3013 x1 = 0;
3014 }
3015 if (x1 >= im->sx) {
3016 x1 = im->sx - 1;
3017 }
3018 if (x2 < 0) {
3019 x2 = 0;
3020 }
3021 if (x2 >= im->sx) {
3022 x2 = im->sx - 1;
3023 }
3024 if (y1 < 0) {
3025 y1 = 0;
3026 }
3027 if (y1 >= im->sy) {
3028 y1 = im->sy - 1;
3029 }
3030 if (y2 < 0) {
3031 y2 = 0;
3032 }
3033 if (y2 >= im->sy) {
3034 y2 = im->sy - 1;
3035 }
3036 im->cx1 = x1;
3037 im->cy1 = y1;
3038 im->cx2 = x2;
3039 im->cy2 = y2;
3040 }
3041
gdImageGetClip(gdImagePtr im,int * x1P,int * y1P,int * x2P,int * y2P)3042 void gdImageGetClip (gdImagePtr im, int *x1P, int *y1P, int *x2P, int *y2P)
3043 {
3044 *x1P = im->cx1;
3045 *y1P = im->cy1;
3046 *x2P = im->cx2;
3047 *y2P = im->cy2;
3048 }
3049
3050 /* convert a palette image to true color */
gdImagePaletteToTrueColor(gdImagePtr src)3051 int gdImagePaletteToTrueColor(gdImagePtr src)
3052 {
3053 unsigned int y;
3054 unsigned int yy;
3055
3056 if (src == NULL) {
3057 return 0;
3058 }
3059
3060 if (src->trueColor == 1) {
3061 return 1;
3062 } else {
3063 unsigned int x;
3064 const unsigned int sy = gdImageSY(src);
3065 const unsigned int sx = gdImageSX(src);
3066
3067 src->tpixels = (int **) gdMalloc(sizeof(int *) * sy);
3068 if (src->tpixels == NULL) {
3069 return 0;
3070 }
3071
3072 for (y = 0; y < sy; y++) {
3073 const unsigned char *src_row = src->pixels[y];
3074 int * dst_row;
3075
3076 /* no need to calloc it, we overwrite all pxl anyway */
3077 src->tpixels[y] = (int *) gdMalloc(sx * sizeof(int));
3078 if (src->tpixels[y] == NULL) {
3079 goto clean_on_error;
3080 }
3081
3082 dst_row = src->tpixels[y];
3083 for (x = 0; x < sx; x++) {
3084 const unsigned char c = *(src_row + x);
3085 if (c == src->transparent) {
3086 *(dst_row + x) = gdTrueColorAlpha(0, 0, 0, 127);
3087 } else {
3088 *(dst_row + x) = gdTrueColorAlpha(src->red[c], src->green[c], src->blue[c], src->alpha[c]);
3089 }
3090 }
3091 }
3092 }
3093
3094 /* free old palette buffer (y is sy) */
3095 for (yy = 0; yy < y; yy++) {
3096 gdFree(src->pixels[yy]);
3097 }
3098 gdFree(src->pixels);
3099 src->trueColor = 1;
3100 src->pixels = NULL;
3101 src->alphaBlendingFlag = 0;
3102 src->saveAlphaFlag = 1;
3103
3104 if (src->transparent >= 0) {
3105 const unsigned char c = src->transparent;
3106 src->transparent = gdTrueColorAlpha(src->red[c], src->green[c], src->blue[c], src->alpha[c]);
3107 }
3108
3109 return 1;
3110
3111 clean_on_error:
3112 /* free new true color buffer (y is not allocated, have failed) */
3113 for (yy = 0; yy < y; yy++) {
3114 gdFree(src->tpixels[yy]);
3115 }
3116 gdFree(src->tpixels);
3117 return 0;
3118 }
3119
3120