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 TSRMLS_FETCH();
106
107 va_start(args, format);
108 php_verror(NULL, "", type, format, args TSRMLS_CC);
109 va_end(args);
110 }
111
php_gd_error(const char * format,...)112 void php_gd_error(const char *format, ...)
113 {
114 va_list args;
115
116 TSRMLS_FETCH();
117
118 va_start(args, format);
119 php_verror(NULL, "", E_WARNING, format, args TSRMLS_CC);
120 va_end(args);
121 }
122
gdImageCreate(int sx,int sy)123 gdImagePtr gdImageCreate (int sx, int sy)
124 {
125 int i;
126 gdImagePtr im;
127
128 if (overflow2(sx, sy)) {
129 return NULL;
130 }
131
132 if (overflow2(sizeof(unsigned char *), sy)) {
133 return NULL;
134 }
135
136 if (overflow2(sizeof(unsigned char *), sx)) {
137 return NULL;
138 }
139
140 im = (gdImage *) gdCalloc(1, sizeof(gdImage));
141
142 /* Row-major ever since gd 1.3 */
143 im->pixels = (unsigned char **) gdMalloc(sizeof(unsigned char *) * sy);
144 im->AA_opacity = (unsigned char **) gdMalloc(sizeof(unsigned char *) * sy);
145 im->polyInts = 0;
146 im->polyAllocated = 0;
147 im->brush = 0;
148 im->tile = 0;
149 im->style = 0;
150 for (i = 0; i < sy; i++) {
151 /* Row-major ever since gd 1.3 */
152 im->pixels[i] = (unsigned char *) gdCalloc(sx, sizeof(unsigned char));
153 im->AA_opacity[i] = (unsigned char *) gdCalloc(sx, sizeof(unsigned char));
154 }
155 im->sx = sx;
156 im->sy = sy;
157 im->colorsTotal = 0;
158 im->transparent = (-1);
159 im->interlace = 0;
160 im->thick = 1;
161 im->AA = 0;
162 im->AA_polygon = 0;
163 for (i = 0; i < gdMaxColors; i++) {
164 im->open[i] = 1;
165 im->red[i] = 0;
166 im->green[i] = 0;
167 im->blue[i] = 0;
168 }
169 im->trueColor = 0;
170 im->tpixels = 0;
171 im->cx1 = 0;
172 im->cy1 = 0;
173 im->cx2 = im->sx - 1;
174 im->cy2 = im->sy - 1;
175 im->interpolation = NULL;
176 im->interpolation_id = GD_BILINEAR_FIXED;
177 return im;
178 }
179
gdImageCreateTrueColor(int sx,int sy)180 gdImagePtr gdImageCreateTrueColor (int sx, int sy)
181 {
182 int i;
183 gdImagePtr im;
184
185 if (overflow2(sx, sy)) {
186 return NULL;
187 }
188
189 if (overflow2(sizeof(unsigned char *), sy)) {
190 return NULL;
191 }
192
193 if (overflow2(sizeof(int *), sx)) {
194 return NULL;
195 }
196
197 im = (gdImage *) gdMalloc(sizeof(gdImage));
198 memset(im, 0, sizeof(gdImage));
199 im->tpixels = (int **) gdMalloc(sizeof(int *) * sy);
200 im->AA_opacity = (unsigned char **) gdMalloc(sizeof(unsigned char *) * sy);
201 im->polyInts = 0;
202 im->polyAllocated = 0;
203 im->brush = 0;
204 im->tile = 0;
205 im->style = 0;
206 for (i = 0; i < sy; i++) {
207 im->tpixels[i] = (int *) gdCalloc(sx, sizeof(int));
208 im->AA_opacity[i] = (unsigned char *) gdCalloc(sx, sizeof(unsigned char));
209 }
210 im->sx = sx;
211 im->sy = sy;
212 im->transparent = (-1);
213 im->interlace = 0;
214 im->trueColor = 1;
215 /* 2.0.2: alpha blending is now on by default, and saving of alpha is
216 * off by default. This allows font antialiasing to work as expected
217 * on the first try in JPEGs -- quite important -- and also allows
218 * for smaller PNGs when saving of alpha channel is not really
219 * desired, which it usually isn't!
220 */
221 im->saveAlphaFlag = 0;
222 im->alphaBlendingFlag = 1;
223 im->thick = 1;
224 im->AA = 0;
225 im->AA_polygon = 0;
226 im->cx1 = 0;
227 im->cy1 = 0;
228 im->cx2 = im->sx - 1;
229 im->cy2 = im->sy - 1;
230 im->interpolation = NULL;
231 im->interpolation_id = GD_BILINEAR_FIXED;
232 return im;
233 }
234
gdImageDestroy(gdImagePtr im)235 void gdImageDestroy (gdImagePtr im)
236 {
237 int i;
238 if (im->pixels) {
239 for (i = 0; i < im->sy; i++) {
240 gdFree(im->pixels[i]);
241 }
242 gdFree(im->pixels);
243 }
244 if (im->tpixels) {
245 for (i = 0; i < im->sy; i++) {
246 gdFree(im->tpixels[i]);
247 }
248 gdFree(im->tpixels);
249 }
250 if (im->AA_opacity) {
251 for (i = 0; i < im->sy; i++) {
252 gdFree(im->AA_opacity[i]);
253 }
254 gdFree(im->AA_opacity);
255 }
256 if (im->polyInts) {
257 gdFree(im->polyInts);
258 }
259 if (im->style) {
260 gdFree(im->style);
261 }
262 gdFree(im);
263 }
264
gdImageColorClosest(gdImagePtr im,int r,int g,int b)265 int gdImageColorClosest (gdImagePtr im, int r, int g, int b)
266 {
267 return gdImageColorClosestAlpha (im, r, g, b, gdAlphaOpaque);
268 }
269
gdImageColorClosestAlpha(gdImagePtr im,int r,int g,int b,int a)270 int gdImageColorClosestAlpha (gdImagePtr im, int r, int g, int b, int a)
271 {
272 int i;
273 long rd, gd, bd, ad;
274 int ct = (-1);
275 int first = 1;
276 long mindist = 0;
277
278 if (im->trueColor) {
279 return gdTrueColorAlpha(r, g, b, a);
280 }
281 for (i = 0; i < im->colorsTotal; i++) {
282 long dist;
283 if (im->open[i]) {
284 continue;
285 }
286 rd = im->red[i] - r;
287 gd = im->green[i] - g;
288 bd = im->blue[i] - b;
289 /* gd 2.02: whoops, was - b (thanks to David Marwood) */
290 ad = im->alpha[i] - a;
291 dist = rd * rd + gd * gd + bd * bd + ad * ad;
292 if (first || (dist < mindist)) {
293 mindist = dist;
294 ct = i;
295 first = 0;
296 }
297 }
298 return ct;
299 }
300
301 /* This code is taken from http://www.acm.org/jgt/papers/SmithLyons96/hwb_rgb.html, an article
302 * on colour conversion to/from RBG and HWB colour systems.
303 * It has been modified to return the converted value as a * parameter.
304 */
305
306 #define RETURN_HWB(h, w, b) {HWB->H = h; HWB->W = w; HWB->B = b; return HWB;}
307 #define RETURN_RGB(r, g, b) {RGB->R = r; RGB->G = g; RGB->B = b; return RGB;}
308 #define HWB_UNDEFINED -1
309 #define SETUP_RGB(s, r, g, b) {s.R = r/255.0f; s.G = g/255.0f; s.B = b/255.0f;}
310
311 #ifndef MIN
312 #define MIN(a,b) ((a)<(b)?(a):(b))
313 #endif
314 #define MIN3(a,b,c) ((a)<(b)?(MIN(a,c)):(MIN(b,c)))
315 #ifndef MAX
316 #define MAX(a,b) ((a)<(b)?(b):(a))
317 #endif
318 #define MAX3(a,b,c) ((a)<(b)?(MAX(b,c)):(MAX(a,c)))
319
320
321 /*
322 * Theoretically, hue 0 (pure red) is identical to hue 6 in these transforms. Pure
323 * red always maps to 6 in this implementation. Therefore UNDEFINED can be
324 * defined as 0 in situations where only unsigned numbers are desired.
325 */
326 typedef struct
327 {
328 float R, G, B;
329 }
330 RGBType;
331 typedef struct
332 {
333 float H, W, B;
334 }
335 HWBType;
336
RGB_to_HWB(RGBType RGB,HWBType * HWB)337 static HWBType * RGB_to_HWB (RGBType RGB, HWBType * HWB)
338 {
339 /*
340 * RGB are each on [0, 1]. W and B are returned on [0, 1] and H is
341 * returned on [0, 6]. Exception: H is returned UNDEFINED if W == 1 - B.
342 */
343
344 float R = RGB.R, G = RGB.G, B = RGB.B, w, v, b, f;
345 int i;
346
347 w = MIN3 (R, G, B);
348 v = MAX3 (R, G, B);
349 b = 1 - v;
350 if (v == w) {
351 RETURN_HWB(HWB_UNDEFINED, w, b);
352 }
353 f = (R == w) ? G - B : ((G == w) ? B - R : R - G);
354 i = (R == w) ? 3 : ((G == w) ? 5 : 1);
355
356 RETURN_HWB(i - f / (v - w), w, b);
357 }
358
HWB_Diff(int r1,int g1,int b1,int r2,int g2,int b2)359 static float HWB_Diff (int r1, int g1, int b1, int r2, int g2, int b2)
360 {
361 RGBType RGB1, RGB2;
362 HWBType HWB1, HWB2;
363 float diff;
364
365 SETUP_RGB(RGB1, r1, g1, b1);
366 SETUP_RGB(RGB2, r2, g2, b2);
367
368 RGB_to_HWB(RGB1, &HWB1);
369 RGB_to_HWB(RGB2, &HWB2);
370
371 /*
372 * I made this bit up; it seems to produce OK results, and it is certainly
373 * more visually correct than the current RGB metric. (PJW)
374 */
375
376 if ((HWB1.H == HWB_UNDEFINED) || (HWB2.H == HWB_UNDEFINED)) {
377 diff = 0.0f; /* Undefined hues always match... */
378 } else {
379 diff = fabsf(HWB1.H - HWB2.H);
380 if (diff > 3.0f) {
381 diff = 6.0f - diff; /* Remember, it's a colour circle */
382 }
383 }
384
385 diff = diff * diff + (HWB1.W - HWB2.W) * (HWB1.W - HWB2.W) + (HWB1.B - HWB2.B) * (HWB1.B - HWB2.B);
386
387 return diff;
388 }
389
390
391 #if 0
392 /*
393 * This is not actually used, but is here for completeness, in case someone wants to
394 * use the HWB stuff for anything else...
395 */
396 static RGBType * HWB_to_RGB (HWBType HWB, RGBType * RGB)
397 {
398 /*
399 * H is given on [0, 6] or UNDEFINED. W and B are given on [0, 1].
400 * RGB are each returned on [0, 1].
401 */
402
403 float h = HWB.H, w = HWB.W, b = HWB.B, v, n, f;
404 int i;
405
406 v = 1 - b;
407 if (h == HWB_UNDEFINED) {
408 RETURN_RGB(v, v, v);
409 }
410 i = floor(h);
411 f = h - i;
412 if (i & 1) {
413 f = 1 - f; /* if i is odd */
414 }
415 n = w + f * (v - w); /* linear interpolation between w and v */
416 switch (i) {
417 case 6:
418 case 0:
419 RETURN_RGB(v, n, w);
420 case 1:
421 RETURN_RGB(n, v, w);
422 case 2:
423 RETURN_RGB(w, v, n);
424 case 3:
425 RETURN_RGB(w, n, v);
426 case 4:
427 RETURN_RGB(n, w, v);
428 case 5:
429 RETURN_RGB(v, w, n);
430 }
431
432 return RGB;
433 }
434 #endif
435
gdImageColorClosestHWB(gdImagePtr im,int r,int g,int b)436 int gdImageColorClosestHWB (gdImagePtr im, int r, int g, int b)
437 {
438 int i;
439 /* long rd, gd, bd; */
440 int ct = (-1);
441 int first = 1;
442 float mindist = 0;
443 if (im->trueColor) {
444 return gdTrueColor(r, g, b);
445 }
446 for (i = 0; i < im->colorsTotal; i++) {
447 float dist;
448 if (im->open[i]) {
449 continue;
450 }
451 dist = HWB_Diff(im->red[i], im->green[i], im->blue[i], r, g, b);
452 if (first || (dist < mindist)) {
453 mindist = dist;
454 ct = i;
455 first = 0;
456 }
457 }
458 return ct;
459 }
460
gdImageColorExact(gdImagePtr im,int r,int g,int b)461 int gdImageColorExact (gdImagePtr im, int r, int g, int b)
462 {
463 return gdImageColorExactAlpha (im, r, g, b, gdAlphaOpaque);
464 }
465
gdImageColorExactAlpha(gdImagePtr im,int r,int g,int b,int a)466 int gdImageColorExactAlpha (gdImagePtr im, int r, int g, int b, int a)
467 {
468 int i;
469 if (im->trueColor) {
470 return gdTrueColorAlpha(r, g, b, a);
471 }
472 for (i = 0; i < im->colorsTotal; i++) {
473 if (im->open[i]) {
474 continue;
475 }
476 if ((im->red[i] == r) && (im->green[i] == g) && (im->blue[i] == b) && (im->alpha[i] == a)) {
477 return i;
478 }
479 }
480 return -1;
481 }
482
gdImageColorAllocate(gdImagePtr im,int r,int g,int b)483 int gdImageColorAllocate (gdImagePtr im, int r, int g, int b)
484 {
485 return gdImageColorAllocateAlpha (im, r, g, b, gdAlphaOpaque);
486 }
487
gdImageColorAllocateAlpha(gdImagePtr im,int r,int g,int b,int a)488 int gdImageColorAllocateAlpha (gdImagePtr im, int r, int g, int b, int a)
489 {
490 int i;
491 int ct = (-1);
492 if (im->trueColor) {
493 return gdTrueColorAlpha(r, g, b, a);
494 }
495 for (i = 0; i < im->colorsTotal; i++) {
496 if (im->open[i]) {
497 ct = i;
498 break;
499 }
500 }
501 if (ct == (-1)) {
502 ct = im->colorsTotal;
503 if (ct == gdMaxColors) {
504 return -1;
505 }
506 im->colorsTotal++;
507 }
508 im->red[ct] = r;
509 im->green[ct] = g;
510 im->blue[ct] = b;
511 im->alpha[ct] = a;
512 im->open[ct] = 0;
513
514 return ct;
515 }
516
517 /*
518 * gdImageColorResolve is an alternative for the code fragment:
519 *
520 * if ((color=gdImageColorExact(im,R,G,B)) < 0)
521 * if ((color=gdImageColorAllocate(im,R,G,B)) < 0)
522 * color=gdImageColorClosest(im,R,G,B);
523 *
524 * in a single function. Its advantage is that it is guaranteed to
525 * return a color index in one search over the color table.
526 */
527
gdImageColorResolve(gdImagePtr im,int r,int g,int b)528 int gdImageColorResolve (gdImagePtr im, int r, int g, int b)
529 {
530 return gdImageColorResolveAlpha(im, r, g, b, gdAlphaOpaque);
531 }
532
gdImageColorResolveAlpha(gdImagePtr im,int r,int g,int b,int a)533 int gdImageColorResolveAlpha (gdImagePtr im, int r, int g, int b, int a)
534 {
535 int c;
536 int ct = -1;
537 int op = -1;
538 long rd, gd, bd, ad, dist;
539 long mindist = 4 * 255 * 255; /* init to max poss dist */
540 if (im->trueColor)
541 {
542 return gdTrueColorAlpha (r, g, b, a);
543 }
544
545 for (c = 0; c < im->colorsTotal; c++)
546 {
547 if (im->open[c])
548 {
549 op = c; /* Save open slot */
550 continue; /* Color not in use */
551 }
552 if (c == im->transparent)
553 {
554 /* don't ever resolve to the color that has
555 * been designated as the transparent color */
556 continue;
557 }
558 rd = (long) (im->red[c] - r);
559 gd = (long) (im->green[c] - g);
560 bd = (long) (im->blue[c] - b);
561 ad = (long) (im->alpha[c] - a);
562 dist = rd * rd + gd * gd + bd * bd + ad * ad;
563 if (dist < mindist)
564 {
565 if (dist == 0)
566 {
567 return c; /* Return exact match color */
568 }
569 mindist = dist;
570 ct = c;
571 }
572 }
573 /* no exact match. We now know closest, but first try to allocate exact */
574 if (op == -1)
575 {
576 op = im->colorsTotal;
577 if (op == gdMaxColors)
578 { /* No room for more colors */
579 return ct; /* Return closest available color */
580 }
581 im->colorsTotal++;
582 }
583 im->red[op] = r;
584 im->green[op] = g;
585 im->blue[op] = b;
586 im->alpha[op] = a;
587 im->open[op] = 0;
588 return op; /* Return newly allocated color */
589 }
590
gdImageColorDeallocate(gdImagePtr im,int color)591 void gdImageColorDeallocate (gdImagePtr im, int color)
592 {
593 if (im->trueColor) {
594 return;
595 }
596 /* Mark it open. */
597 im->open[color] = 1;
598 }
599
gdImageColorTransparent(gdImagePtr im,int color)600 void gdImageColorTransparent (gdImagePtr im, int color)
601 {
602 if (color < 0) {
603 return;
604 }
605 if (!im->trueColor) {
606 if((color >= im->colorsTotal)) {
607 return;
608 }
609 /* Make the old transparent color opaque again */
610 if (im->transparent != -1) {
611 im->alpha[im->transparent] = gdAlphaOpaque;
612 }
613 im->alpha[color] = gdAlphaTransparent;
614 }
615 im->transparent = color;
616 }
617
gdImagePaletteCopy(gdImagePtr to,gdImagePtr from)618 void gdImagePaletteCopy (gdImagePtr to, gdImagePtr from)
619 {
620 int i;
621 int x, y, p;
622 int xlate[256];
623 if (to->trueColor || from->trueColor) {
624 return;
625 }
626
627 for (i = 0; i < 256; i++) {
628 xlate[i] = -1;
629 }
630
631 for (y = 0; y < to->sy; y++) {
632 for (x = 0; x < to->sx; x++) {
633 p = gdImageGetPixel(to, x, y);
634 if (xlate[p] == -1) {
635 /* This ought to use HWB, but we don't have an alpha-aware version of that yet. */
636 xlate[p] = gdImageColorClosestAlpha (from, to->red[p], to->green[p], to->blue[p], to->alpha[p]);
637 }
638 gdImageSetPixel(to, x, y, xlate[p]);
639 }
640 }
641
642 for (i = 0; i < from->colorsTotal; i++) {
643 to->red[i] = from->red[i];
644 to->blue[i] = from->blue[i];
645 to->green[i] = from->green[i];
646 to->alpha[i] = from->alpha[i];
647 to->open[i] = 0;
648 }
649
650 for (i = from->colorsTotal; i < to->colorsTotal; i++) {
651 to->open[i] = 1;
652 }
653
654 to->colorsTotal = from->colorsTotal;
655 }
656
657 /* 2.0.10: before the drawing routines, some code to clip points that are
658 * outside the drawing window. Nick Atty (nick@canalplan.org.uk)
659 *
660 * This is the Sutherland Hodgman Algorithm, as implemented by
661 * Duvanenko, Robbins and Gyurcsik - SH(DRG) for short. See Dr Dobb's
662 * Journal, January 1996, pp107-110 and 116-117
663 *
664 * Given the end points of a line, and a bounding rectangle (which we
665 * know to be from (0,0) to (SX,SY)), adjust the endpoints to be on
666 * the edges of the rectangle if the line should be drawn at all,
667 * otherwise return a failure code
668 */
669
670 /* this does "one-dimensional" clipping: note that the second time it
671 * is called, all the x parameters refer to height and the y to width
672 * - the comments ignore this (if you can understand it when it's
673 * looking at the X parameters, it should become clear what happens on
674 * the second call!) The code is simplified from that in the article,
675 * as we know that gd images always start at (0,0)
676 */
677
clip_1d(int * x0,int * y0,int * x1,int * y1,int maxdim)678 static int clip_1d(int *x0, int *y0, int *x1, int *y1, int maxdim) {
679 double m; /* gradient of line */
680
681 if (*x0 < 0) { /* start of line is left of window */
682 if(*x1 < 0) { /* as is the end, so the line never cuts the window */
683 return 0;
684 }
685 m = (*y1 - *y0)/(double)(*x1 - *x0); /* calculate the slope of the line */
686 /* adjust x0 to be on the left boundary (ie to be zero), and y0 to match */
687 *y0 -= (int)(m * *x0);
688 *x0 = 0;
689 /* now, perhaps, adjust the far end of the line as well */
690 if (*x1 > maxdim) {
691 *y1 += (int)(m * (maxdim - *x1));
692 *x1 = maxdim;
693 }
694 return 1;
695 }
696 if (*x0 > maxdim) { /* start of line is right of window - complement of above */
697 if (*x1 > maxdim) { /* as is the end, so the line misses the window */
698 return 0;
699 }
700 m = (*y1 - *y0)/(double)(*x1 - *x0); /* calculate the slope of the line */
701 *y0 += (int)(m * (maxdim - *x0)); /* adjust so point is on the right boundary */
702 *x0 = maxdim;
703 /* now, perhaps, adjust the end of the line */
704 if (*x1 < 0) {
705 *y1 -= (int)(m * *x1);
706 *x1 = 0;
707 }
708 return 1;
709 }
710 /* the final case - the start of the line is inside the window */
711 if (*x1 > maxdim) { /* other end is outside to the right */
712 m = (*y1 - *y0)/(double)(*x1 - *x0); /* calculate the slope of the line */
713 *y1 += (int)(m * (maxdim - *x1));
714 *x1 = maxdim;
715 return 1;
716 }
717 if (*x1 < 0) { /* other end is outside to the left */
718 m = (*y1 - *y0)/(double)(*x1 - *x0); /* calculate the slope of the line */
719 *y1 -= (int)(m * *x1);
720 *x1 = 0;
721 return 1;
722 }
723 /* only get here if both points are inside the window */
724 return 1;
725 }
726
gdImageSetPixel(gdImagePtr im,int x,int y,int color)727 void gdImageSetPixel (gdImagePtr im, int x, int y, int color)
728 {
729 int p;
730 switch (color) {
731 case gdStyled:
732 if (!im->style) {
733 /* Refuse to draw if no style is set. */
734 return;
735 } else {
736 p = im->style[im->stylePos++];
737 }
738 if (p != gdTransparent) {
739 gdImageSetPixel(im, x, y, p);
740 }
741 im->stylePos = im->stylePos % im->styleLength;
742 break;
743 case gdStyledBrushed:
744 if (!im->style) {
745 /* Refuse to draw if no style is set. */
746 return;
747 }
748 p = im->style[im->stylePos++];
749 if (p != gdTransparent && p != 0) {
750 gdImageSetPixel(im, x, y, gdBrushed);
751 }
752 im->stylePos = im->stylePos % im->styleLength;
753 break;
754 case gdBrushed:
755 gdImageBrushApply(im, x, y);
756 break;
757 case gdTiled:
758 gdImageTileApply(im, x, y);
759 break;
760 case gdAntiAliased:
761 gdImageAntiAliasedApply(im, x, y);
762 break;
763 default:
764 if (gdImageBoundsSafe(im, x, y)) {
765 if (im->trueColor) {
766 switch (im->alphaBlendingFlag) {
767 default:
768 case gdEffectReplace:
769 im->tpixels[y][x] = color;
770 break;
771 case gdEffectAlphaBlend:
772 im->tpixels[y][x] = gdAlphaBlend(im->tpixels[y][x], color);
773 break;
774 case gdEffectNormal:
775 im->tpixels[y][x] = gdAlphaBlend(im->tpixels[y][x], color);
776 break;
777 case gdEffectOverlay :
778 im->tpixels[y][x] = gdLayerOverlay(im->tpixels[y][x], color);
779 break;
780 }
781 } else {
782 im->pixels[y][x] = color;
783 }
784 }
785 break;
786 }
787 }
788
gdImageGetTrueColorPixel(gdImagePtr im,int x,int y)789 int gdImageGetTrueColorPixel (gdImagePtr im, int x, int y)
790 {
791 int p = gdImageGetPixel(im, x, y);
792
793 if (!im->trueColor) {
794 return gdTrueColorAlpha(im->red[p], im->green[p], im->blue[p], (im->transparent == p) ? gdAlphaTransparent : im->alpha[p]);
795 } else {
796 return p;
797 }
798 }
799
gdImageBrushApply(gdImagePtr im,int x,int y)800 static void gdImageBrushApply (gdImagePtr im, int x, int y)
801 {
802 int lx, ly;
803 int hy, hx;
804 int x1, y1, x2, y2;
805 int srcx, srcy;
806
807 if (!im->brush) {
808 return;
809 }
810
811 hy = gdImageSY(im->brush) / 2;
812 y1 = y - hy;
813 y2 = y1 + gdImageSY(im->brush);
814 hx = gdImageSX(im->brush) / 2;
815 x1 = x - hx;
816 x2 = x1 + gdImageSX(im->brush);
817 srcy = 0;
818
819 if (im->trueColor) {
820 if (im->brush->trueColor) {
821 for (ly = y1; ly < y2; ly++) {
822 srcx = 0;
823 for (lx = x1; (lx < x2); lx++) {
824 int p;
825 p = gdImageGetTrueColorPixel(im->brush, srcx, srcy);
826 /* 2.0.9, Thomas Winzig: apply simple full transparency */
827 if (p != gdImageGetTransparent(im->brush)) {
828 gdImageSetPixel(im, lx, ly, p);
829 }
830 srcx++;
831 }
832 srcy++;
833 }
834 } else {
835 /* 2.0.12: Brush palette, image truecolor (thanks to Thorben Kundinger for pointing out the issue) */
836 for (ly = y1; ly < y2; ly++) {
837 srcx = 0;
838 for (lx = x1; lx < x2; lx++) {
839 int p, tc;
840 p = gdImageGetPixel(im->brush, srcx, srcy);
841 tc = gdImageGetTrueColorPixel(im->brush, srcx, srcy);
842 /* 2.0.9, Thomas Winzig: apply simple full transparency */
843 if (p != gdImageGetTransparent(im->brush)) {
844 gdImageSetPixel(im, lx, ly, tc);
845 }
846 srcx++;
847 }
848 srcy++;
849 }
850 }
851 } else {
852 for (ly = y1; ly < y2; ly++) {
853 srcx = 0;
854 for (lx = x1; lx < x2; lx++) {
855 int p;
856 p = gdImageGetPixel(im->brush, srcx, srcy);
857 /* Allow for non-square brushes! */
858 if (p != gdImageGetTransparent(im->brush)) {
859 /* Truecolor brush. Very slow on a palette destination. */
860 if (im->brush->trueColor) {
861 gdImageSetPixel(im, lx, ly, gdImageColorResolveAlpha(im, gdTrueColorGetRed(p),
862 gdTrueColorGetGreen(p),
863 gdTrueColorGetBlue(p),
864 gdTrueColorGetAlpha(p)));
865 } else {
866 gdImageSetPixel(im, lx, ly, im->brushColorMap[p]);
867 }
868 }
869 srcx++;
870 }
871 srcy++;
872 }
873 }
874 }
875
gdImageTileApply(gdImagePtr im,int x,int y)876 static void gdImageTileApply (gdImagePtr im, int x, int y)
877 {
878 gdImagePtr tile = im->tile;
879 int srcx, srcy;
880 int p;
881 if (!tile) {
882 return;
883 }
884 srcx = x % gdImageSX(tile);
885 srcy = y % gdImageSY(tile);
886 if (im->trueColor) {
887 p = gdImageGetPixel(tile, srcx, srcy);
888 if (p != gdImageGetTransparent (tile)) {
889 if (!tile->trueColor) {
890 p = gdTrueColorAlpha(tile->red[p], tile->green[p], tile->blue[p], tile->alpha[p]);
891 }
892 gdImageSetPixel(im, x, y, p);
893 }
894 } else {
895 p = gdImageGetPixel(tile, srcx, srcy);
896 /* Allow for transparency */
897 if (p != gdImageGetTransparent(tile)) {
898 if (tile->trueColor) {
899 /* Truecolor tile. Very slow on a palette destination. */
900 gdImageSetPixel(im, x, y, gdImageColorResolveAlpha(im,
901 gdTrueColorGetRed(p),
902 gdTrueColorGetGreen(p),
903 gdTrueColorGetBlue(p),
904 gdTrueColorGetAlpha(p)));
905 } else {
906 gdImageSetPixel(im, x, y, im->tileColorMap[p]);
907 }
908 }
909 }
910 }
911
912
gdImageTileGet(gdImagePtr im,int x,int y)913 static int gdImageTileGet (gdImagePtr im, int x, int y)
914 {
915 int srcx, srcy;
916 int tileColor,p;
917 if (!im->tile) {
918 return -1;
919 }
920 srcx = x % gdImageSX(im->tile);
921 srcy = y % gdImageSY(im->tile);
922 p = gdImageGetPixel(im->tile, srcx, srcy);
923
924 if (im->trueColor) {
925 if (im->tile->trueColor) {
926 tileColor = p;
927 } else {
928 tileColor = gdTrueColorAlpha( gdImageRed(im->tile,p), gdImageGreen(im->tile,p), gdImageBlue (im->tile,p), gdImageAlpha (im->tile,p));
929 }
930 } else {
931 if (im->tile->trueColor) {
932 tileColor = gdImageColorResolveAlpha(im, gdTrueColorGetRed (p), gdTrueColorGetGreen (p), gdTrueColorGetBlue (p), gdTrueColorGetAlpha (p));
933 } else {
934 tileColor = p;
935 tileColor = gdImageColorResolveAlpha(im, gdImageRed (im->tile,p), gdImageGreen (im->tile,p), gdImageBlue (im->tile,p), gdImageAlpha (im->tile,p));
936 }
937 }
938 return tileColor;
939 }
940
941
gdImageAntiAliasedApply(gdImagePtr im,int px,int py)942 static void gdImageAntiAliasedApply (gdImagePtr im, int px, int py)
943 {
944 float p_dist, p_alpha;
945 unsigned char opacity;
946
947 /*
948 * Find the perpendicular distance from point C (px, py) to the line
949 * segment AB that is being drawn. (Adapted from an algorithm from the
950 * comp.graphics.algorithms FAQ.)
951 */
952
953 int LAC_2, LBC_2;
954
955 int Ax_Cx = im->AAL_x1 - px;
956 int Ay_Cy = im->AAL_y1 - py;
957
958 int Bx_Cx = im->AAL_x2 - px;
959 int By_Cy = im->AAL_y2 - py;
960
961 /* 2.0.13: bounds check! AA_opacity is just as capable of
962 * overflowing as the main pixel array. Arne Jorgensen.
963 * 2.0.14: typo fixed. 2.0.15: moved down below declarations
964 * to satisfy non-C++ compilers.
965 */
966 if (!gdImageBoundsSafe(im, px, py)) {
967 return;
968 }
969
970 /* Get the squares of the lengths of the segemnts AC and BC. */
971 LAC_2 = (Ax_Cx * Ax_Cx) + (Ay_Cy * Ay_Cy);
972 LBC_2 = (Bx_Cx * Bx_Cx) + (By_Cy * By_Cy);
973
974 if (((im->AAL_LAB_2 + LAC_2) >= LBC_2) && ((im->AAL_LAB_2 + LBC_2) >= LAC_2)) {
975 /* The two angles are acute. The point lies inside the portion of the
976 * plane spanned by the line segment.
977 */
978 p_dist = fabs ((float) ((Ay_Cy * im->AAL_Bx_Ax) - (Ax_Cx * im->AAL_By_Ay)) / im->AAL_LAB);
979 } else {
980 /* The point is past an end of the line segment. It's length from the
981 * segment is the shorter of the lengths from the endpoints, but call
982 * the distance -1, so as not to compute the alpha nor draw the pixel.
983 */
984 p_dist = -1;
985 }
986
987 if ((p_dist >= 0) && (p_dist <= (float) (im->thick))) {
988 p_alpha = pow (1.0 - (p_dist / 1.5), 2);
989
990 if (p_alpha > 0) {
991 if (p_alpha >= 1) {
992 opacity = 255;
993 } else {
994 opacity = (unsigned char) (p_alpha * 255.0);
995 }
996 if (!im->AA_polygon || (im->AA_opacity[py][px] < opacity)) {
997 im->AA_opacity[py][px] = opacity;
998 }
999 }
1000 }
1001 }
1002
1003
gdImageGetPixel(gdImagePtr im,int x,int y)1004 int gdImageGetPixel (gdImagePtr im, int x, int y)
1005 {
1006 if (gdImageBoundsSafe(im, x, y)) {
1007 if (im->trueColor) {
1008 return im->tpixels[y][x];
1009 } else {
1010 return im->pixels[y][x];
1011 }
1012 } else {
1013 return 0;
1014 }
1015 }
1016
gdImageAABlend(gdImagePtr im)1017 void gdImageAABlend (gdImagePtr im)
1018 {
1019 float p_alpha, old_alpha;
1020 int color = im->AA_color, color_red, color_green, color_blue;
1021 int old_color, old_red, old_green, old_blue;
1022 int p_color, p_red, p_green, p_blue;
1023 int px, py;
1024
1025 color_red = gdImageRed(im, color);
1026 color_green = gdImageGreen(im, color);
1027 color_blue = gdImageBlue(im, color);
1028
1029 /* Impose the anti-aliased drawing on the image. */
1030 for (py = 0; py < im->sy; py++) {
1031 for (px = 0; px < im->sx; px++) {
1032 if (im->AA_opacity[py][px] != 0) {
1033 old_color = gdImageGetPixel(im, px, py);
1034
1035 if ((old_color != color) && ((old_color != im->AA_dont_blend) || (im->AA_opacity[py][px] == 255))) {
1036 /* Only blend with different colors that aren't the dont_blend color. */
1037 p_alpha = (float) (im->AA_opacity[py][px]) / 255.0;
1038 old_alpha = 1.0 - p_alpha;
1039
1040 if (p_alpha >= 1.0) {
1041 p_color = color;
1042 } else {
1043 old_red = gdImageRed(im, old_color);
1044 old_green = gdImageGreen(im, old_color);
1045 old_blue = gdImageBlue(im, old_color);
1046
1047 p_red = (int) (((float) color_red * p_alpha) + ((float) old_red * old_alpha));
1048 p_green = (int) (((float) color_green * p_alpha) + ((float) old_green * old_alpha));
1049 p_blue = (int) (((float) color_blue * p_alpha) + ((float) old_blue * old_alpha));
1050 p_color = gdImageColorResolve(im, p_red, p_green, p_blue);
1051 }
1052 gdImageSetPixel(im, px, py, p_color);
1053 }
1054 }
1055 }
1056 /* Clear the AA_opacity array behind us. */
1057 memset(im->AA_opacity[py], 0, im->sx);
1058 }
1059 }
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 gdImageFilledRectangle(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)) || !clip_1d(&y1,&x1,&y2,&x2,gdImageSY(im))) {
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;
1300 long dx, dy,tmp;
1301
1302 if (y1 < 0 && y2 < 0) {
1303 return;
1304 }
1305 if (y1 < 0) {
1306 x1 += (y1 * (x1 - x2)) / (y2 - y1);
1307 y1 = 0;
1308 }
1309 if (y2 < 0) {
1310 x2 += (y2 * (x1 - x2)) / (y2 - y1);
1311 y2 = 0;
1312 }
1313
1314 /* bottom edge */
1315 if (y1 >= im->sy && y2 >= im->sy) {
1316 return;
1317 }
1318 if (y1 >= im->sy) {
1319 x1 -= ((im->sy - y1) * (x1 - x2)) / (y2 - y1);
1320 y1 = im->sy - 1;
1321 }
1322 if (y2 >= im->sy) {
1323 x2 -= ((im->sy - y2) * (x1 - x2)) / (y2 - y1);
1324 y2 = im->sy - 1;
1325 }
1326
1327 /* left edge */
1328 if (x1 < 0 && x2 < 0) {
1329 return;
1330 }
1331 if (x1 < 0) {
1332 y1 += (x1 * (y1 - y2)) / (x2 - x1);
1333 x1 = 0;
1334 }
1335 if (x2 < 0) {
1336 y2 += (x2 * (y1 - y2)) / (x2 - x1);
1337 x2 = 0;
1338 }
1339 /* right edge */
1340 if (x1 >= im->sx && x2 >= im->sx) {
1341 return;
1342 }
1343 if (x1 >= im->sx) {
1344 y1 -= ((im->sx - x1) * (y1 - y2)) / (x2 - x1);
1345 x1 = im->sx - 1;
1346 }
1347 if (x2 >= im->sx) {
1348 y2 -= ((im->sx - x2) * (y1 - y2)) / (x2 - x1);
1349 x2 = im->sx - 1;
1350 }
1351
1352 dx = x2 - x1;
1353 dy = y2 - y1;
1354
1355 if (dx == 0 && dy == 0) {
1356 return;
1357 }
1358 if (abs(dx) > abs(dy)) {
1359 if (dx < 0) {
1360 tmp = x1;
1361 x1 = x2;
1362 x2 = tmp;
1363 tmp = y1;
1364 y1 = y2;
1365 y2 = tmp;
1366 dx = x2 - x1;
1367 dy = y2 - y1;
1368 }
1369 x = x1 << 16;
1370 y = y1 << 16;
1371 inc = (dy * 65536) / dx;
1372 while ((x >> 16) <= x2) {
1373 gdImageSetAAPixelColor(im, x >> 16, y >> 16, col, (y >> 8) & 0xFF);
1374 if ((y >> 16) + 1 < im->sy) {
1375 gdImageSetAAPixelColor(im, x >> 16, (y >> 16) + 1,col, (~y >> 8) & 0xFF);
1376 }
1377 x += (1 << 16);
1378 y += inc;
1379 }
1380 } else {
1381 if (dy < 0) {
1382 tmp = x1;
1383 x1 = x2;
1384 x2 = tmp;
1385 tmp = y1;
1386 y1 = y2;
1387 y2 = tmp;
1388 dx = x2 - x1;
1389 dy = y2 - y1;
1390 }
1391 x = x1 << 16;
1392 y = y1 << 16;
1393 inc = (dx * 65536) / dy;
1394 while ((y>>16) <= y2) {
1395 gdImageSetAAPixelColor(im, x >> 16, y >> 16, col, (x >> 8) & 0xFF);
1396 if ((x >> 16) + 1 < im->sx) {
1397 gdImageSetAAPixelColor(im, (x >> 16) + 1, (y >> 16),col, (~x >> 8) & 0xFF);
1398 }
1399 x += inc;
1400 y += (1<<16);
1401 }
1402 }
1403 }
1404
1405 static void dashedSet (gdImagePtr im, int x, int y, int color, int *onP, int *dashStepP, int wid, int vert);
1406
gdImageDashedLine(gdImagePtr im,int x1,int y1,int x2,int y2,int color)1407 void gdImageDashedLine (gdImagePtr im, int x1, int y1, int x2, int y2, int color)
1408 {
1409 int dx, dy, incr1, incr2, d, x, y, xend, yend, xdirflag, ydirflag;
1410 int dashStep = 0;
1411 int on = 1;
1412 int wid;
1413 int vert;
1414 int thick = im->thick;
1415
1416 dx = abs(x2 - x1);
1417 dy = abs(y2 - y1);
1418 if (dy <= dx) {
1419 /* More-or-less horizontal. use wid for vertical stroke */
1420 /* 2.0.12: Michael Schwartz: divide rather than multiply;
1421 TBB: but watch out for /0! */
1422 double as = sin(atan2(dy, dx));
1423 if (as != 0) {
1424 wid = thick / as;
1425 } else {
1426 wid = 1;
1427 }
1428 wid = (int)(thick * sin(atan2(dy, dx)));
1429 vert = 1;
1430
1431 d = 2 * dy - dx;
1432 incr1 = 2 * dy;
1433 incr2 = 2 * (dy - dx);
1434 if (x1 > x2) {
1435 x = x2;
1436 y = y2;
1437 ydirflag = (-1);
1438 xend = x1;
1439 } else {
1440 x = x1;
1441 y = y1;
1442 ydirflag = 1;
1443 xend = x2;
1444 }
1445 dashedSet(im, x, y, color, &on, &dashStep, wid, vert);
1446 if (((y2 - y1) * ydirflag) > 0) {
1447 while (x < xend) {
1448 x++;
1449 if (d < 0) {
1450 d += incr1;
1451 } else {
1452 y++;
1453 d += incr2;
1454 }
1455 dashedSet(im, x, y, color, &on, &dashStep, wid, vert);
1456 }
1457 } else {
1458 while (x < xend) {
1459 x++;
1460 if (d < 0) {
1461 d += incr1;
1462 } else {
1463 y--;
1464 d += incr2;
1465 }
1466 dashedSet(im, x, y, color, &on, &dashStep, wid, vert);
1467 }
1468 }
1469 } else {
1470 /* 2.0.12: Michael Schwartz: divide rather than multiply;
1471 TBB: but watch out for /0! */
1472 double as = sin (atan2 (dy, dx));
1473 if (as != 0) {
1474 wid = thick / as;
1475 } else {
1476 wid = 1;
1477 }
1478 vert = 0;
1479
1480 d = 2 * dx - dy;
1481 incr1 = 2 * dx;
1482 incr2 = 2 * (dx - dy);
1483 if (y1 > y2) {
1484 y = y2;
1485 x = x2;
1486 yend = y1;
1487 xdirflag = (-1);
1488 } else {
1489 y = y1;
1490 x = x1;
1491 yend = y2;
1492 xdirflag = 1;
1493 }
1494 dashedSet(im, x, y, color, &on, &dashStep, wid, vert);
1495 if (((x2 - x1) * xdirflag) > 0) {
1496 while (y < yend) {
1497 y++;
1498 if (d < 0) {
1499 d += incr1;
1500 } else {
1501 x++;
1502 d += incr2;
1503 }
1504 dashedSet(im, x, y, color, &on, &dashStep, wid, vert);
1505 }
1506 } else {
1507 while (y < yend) {
1508 y++;
1509 if (d < 0) {
1510 d += incr1;
1511 } else {
1512 x--;
1513 d += incr2;
1514 }
1515 dashedSet(im, x, y, color, &on, &dashStep, wid, vert);
1516 }
1517 }
1518 }
1519 }
1520
dashedSet(gdImagePtr im,int x,int y,int color,int * onP,int * dashStepP,int wid,int vert)1521 static void dashedSet (gdImagePtr im, int x, int y, int color, int *onP, int *dashStepP, int wid, int vert)
1522 {
1523 int dashStep = *dashStepP;
1524 int on = *onP;
1525 int w, wstart;
1526
1527 dashStep++;
1528 if (dashStep == gdDashSize) {
1529 dashStep = 0;
1530 on = !on;
1531 }
1532 if (on) {
1533 if (vert) {
1534 wstart = y - wid / 2;
1535 for (w = wstart; w < wstart + wid; w++) {
1536 gdImageSetPixel(im, x, w, color);
1537 }
1538 } else {
1539 wstart = x - wid / 2;
1540 for (w = wstart; w < wstart + wid; w++) {
1541 gdImageSetPixel(im, w, y, color);
1542 }
1543 }
1544 }
1545 *dashStepP = dashStep;
1546 *onP = on;
1547 }
1548
gdImageChar(gdImagePtr im,gdFontPtr f,int x,int y,int c,int color)1549 void gdImageChar (gdImagePtr im, gdFontPtr f, int x, int y, int c, int color)
1550 {
1551 int cx, cy;
1552 int px, py;
1553 int fline;
1554 cx = 0;
1555 cy = 0;
1556 #ifdef CHARSET_EBCDIC
1557 c = ASC (c);
1558 #endif /*CHARSET_EBCDIC */
1559 if ((c < f->offset) || (c >= (f->offset + f->nchars))) {
1560 return;
1561 }
1562 fline = (c - f->offset) * f->h * f->w;
1563 for (py = y; (py < (y + f->h)); py++) {
1564 for (px = x; (px < (x + f->w)); px++) {
1565 if (f->data[fline + cy * f->w + cx]) {
1566 gdImageSetPixel(im, px, py, color);
1567 }
1568 cx++;
1569 }
1570 cx = 0;
1571 cy++;
1572 }
1573 }
1574
gdImageCharUp(gdImagePtr im,gdFontPtr f,int x,int y,int c,int color)1575 void gdImageCharUp (gdImagePtr im, gdFontPtr f, int x, int y, int c, int color)
1576 {
1577 int cx, cy;
1578 int px, py;
1579 int fline;
1580 cx = 0;
1581 cy = 0;
1582 #ifdef CHARSET_EBCDIC
1583 c = ASC (c);
1584 #endif /*CHARSET_EBCDIC */
1585 if ((c < f->offset) || (c >= (f->offset + f->nchars))) {
1586 return;
1587 }
1588 fline = (c - f->offset) * f->h * f->w;
1589 for (py = y; py > (y - f->w); py--) {
1590 for (px = x; px < (x + f->h); px++) {
1591 if (f->data[fline + cy * f->w + cx]) {
1592 gdImageSetPixel(im, px, py, color);
1593 }
1594 cy++;
1595 }
1596 cy = 0;
1597 cx++;
1598 }
1599 }
1600
gdImageString(gdImagePtr im,gdFontPtr f,int x,int y,unsigned char * s,int color)1601 void gdImageString (gdImagePtr im, gdFontPtr f, int x, int y, unsigned char *s, int color)
1602 {
1603 int i;
1604 int l;
1605 l = strlen ((char *) s);
1606 for (i = 0; (i < l); i++) {
1607 gdImageChar(im, f, x, y, s[i], color);
1608 x += f->w;
1609 }
1610 }
1611
gdImageStringUp(gdImagePtr im,gdFontPtr f,int x,int y,unsigned char * s,int color)1612 void gdImageStringUp (gdImagePtr im, gdFontPtr f, int x, int y, unsigned char *s, int color)
1613 {
1614 int i;
1615 int l;
1616 l = strlen ((char *) s);
1617 for (i = 0; (i < l); i++) {
1618 gdImageCharUp(im, f, x, y, s[i], color);
1619 y -= f->w;
1620 }
1621 }
1622
1623 static int strlen16 (unsigned short *s);
1624
gdImageString16(gdImagePtr im,gdFontPtr f,int x,int y,unsigned short * s,int color)1625 void gdImageString16 (gdImagePtr im, gdFontPtr f, int x, int y, unsigned short *s, int color)
1626 {
1627 int i;
1628 int l;
1629 l = strlen16(s);
1630 for (i = 0; (i < l); i++) {
1631 gdImageChar(im, f, x, y, s[i], color);
1632 x += f->w;
1633 }
1634 }
1635
gdImageStringUp16(gdImagePtr im,gdFontPtr f,int x,int y,unsigned short * s,int color)1636 void gdImageStringUp16 (gdImagePtr im, gdFontPtr f, int x, int y, unsigned short *s, int color)
1637 {
1638 int i;
1639 int l;
1640 l = strlen16(s);
1641 for (i = 0; i < l; i++) {
1642 gdImageCharUp(im, f, x, y, s[i], color);
1643 y -= f->w;
1644 }
1645 }
1646
strlen16(unsigned short * s)1647 static int strlen16 (unsigned short *s)
1648 {
1649 int len = 0;
1650 while (*s) {
1651 s++;
1652 len++;
1653 }
1654 return len;
1655 }
1656
1657 #ifndef HAVE_LSQRT
1658 /* If you don't have a nice square root function for longs, you can use
1659 ** this hack
1660 */
lsqrt(long n)1661 long lsqrt (long n)
1662 {
1663 long result = (long) sqrt ((double) n);
1664 return result;
1665 }
1666 #endif
1667
1668 /* s and e are integers modulo 360 (degrees), with 0 degrees
1669 being the rightmost extreme and degrees changing clockwise.
1670 cx and cy are the center in pixels; w and h are the horizontal
1671 and vertical diameter in pixels. Nice interface, but slow.
1672 See gd_arc_f_buggy.c for a better version that doesn't
1673 seem to be bug-free yet. */
1674
gdImageArc(gdImagePtr im,int cx,int cy,int w,int h,int s,int e,int color)1675 void gdImageArc (gdImagePtr im, int cx, int cy, int w, int h, int s, int e, int color)
1676 {
1677 if ((s % 360) == (e % 360)) {
1678 gdImageEllipse(im, cx, cy, w, h, color);
1679 } else {
1680 gdImageFilledArc(im, cx, cy, w, h, s, e, color, gdNoFill);
1681 }
1682 }
1683
gdImageFilledArc(gdImagePtr im,int cx,int cy,int w,int h,int s,int e,int color,int style)1684 void gdImageFilledArc (gdImagePtr im, int cx, int cy, int w, int h, int s, int e, int color, int style)
1685 {
1686 gdPoint pts[3];
1687 int i;
1688 int lx = 0, ly = 0;
1689 int fx = 0, fy = 0;
1690
1691
1692 if ((s % 360) == (e % 360)) {
1693 s = 0; e = 360;
1694 } else {
1695 if (s > 360) {
1696 s = s % 360;
1697 }
1698
1699 if (e > 360) {
1700 e = e % 360;
1701 }
1702
1703 while (s < 0) {
1704 s += 360;
1705 }
1706
1707 while (e < s) {
1708 e += 360;
1709 }
1710 if (s == e) {
1711 s = 0; e = 360;
1712 }
1713 }
1714
1715 for (i = s; i <= e; i++) {
1716 int x, y;
1717 x = ((long) gdCosT[i % 360] * (long) w / (2 * 1024)) + cx;
1718 y = ((long) gdSinT[i % 360] * (long) h / (2 * 1024)) + cy;
1719 if (i != s) {
1720 if (!(style & gdChord)) {
1721 if (style & gdNoFill) {
1722 gdImageLine(im, lx, ly, x, y, color);
1723 } else {
1724 /* This is expensive! */
1725 pts[0].x = lx;
1726 pts[0].y = ly;
1727 pts[1].x = x;
1728 pts[1].y = y;
1729 pts[2].x = cx;
1730 pts[2].y = cy;
1731 gdImageFilledPolygon(im, pts, 3, color);
1732 }
1733 }
1734 } else {
1735 fx = x;
1736 fy = y;
1737 }
1738 lx = x;
1739 ly = y;
1740 }
1741 if (style & gdChord) {
1742 if (style & gdNoFill) {
1743 if (style & gdEdged) {
1744 gdImageLine(im, cx, cy, lx, ly, color);
1745 gdImageLine(im, cx, cy, fx, fy, color);
1746 }
1747 gdImageLine(im, fx, fy, lx, ly, color);
1748 } else {
1749 pts[0].x = fx;
1750 pts[0].y = fy;
1751 pts[1].x = lx;
1752 pts[1].y = ly;
1753 pts[2].x = cx;
1754 pts[2].y = cy;
1755 gdImageFilledPolygon(im, pts, 3, color);
1756 }
1757 } else {
1758 if (style & gdNoFill) {
1759 if (style & gdEdged) {
1760 gdImageLine(im, cx, cy, lx, ly, color);
1761 gdImageLine(im, cx, cy, fx, fy, color);
1762 }
1763 }
1764 }
1765 }
1766
gdImageFillToBorder(gdImagePtr im,int x,int y,int border,int color)1767 void gdImageFillToBorder (gdImagePtr im, int x, int y, int border, int color)
1768 {
1769 int lastBorder;
1770 /* Seek left */
1771 int leftLimit = -1, rightLimit;
1772 int i, restoreAlphaBlending = 0;
1773
1774 if (border < 0) {
1775 /* Refuse to fill to a non-solid border */
1776 return;
1777 }
1778
1779 restoreAlphaBlending = im->alphaBlendingFlag;
1780 im->alphaBlendingFlag = 0;
1781
1782 if (x >= im->sx) {
1783 x = im->sx - 1;
1784 } else if (x < 0) {
1785 x = 0;
1786 }
1787 if (y >= im->sy) {
1788 y = im->sy - 1;
1789 } else if (y < 0) {
1790 y = 0;
1791 }
1792
1793 for (i = x; i >= 0; i--) {
1794 if (gdImageGetPixel(im, i, y) == border) {
1795 break;
1796 }
1797 gdImageSetPixel(im, i, y, color);
1798 leftLimit = i;
1799 }
1800 if (leftLimit == -1) {
1801 im->alphaBlendingFlag = restoreAlphaBlending;
1802 return;
1803 }
1804 /* Seek right */
1805 rightLimit = x;
1806 for (i = (x + 1); i < im->sx; i++) {
1807 if (gdImageGetPixel(im, i, y) == border) {
1808 break;
1809 }
1810 gdImageSetPixel(im, i, y, color);
1811 rightLimit = i;
1812 }
1813 /* Look at lines above and below and start paints */
1814 /* Above */
1815 if (y > 0) {
1816 lastBorder = 1;
1817 for (i = leftLimit; i <= rightLimit; i++) {
1818 int c = gdImageGetPixel(im, i, y - 1);
1819 if (lastBorder) {
1820 if ((c != border) && (c != color)) {
1821 gdImageFillToBorder(im, i, y - 1, border, color);
1822 lastBorder = 0;
1823 }
1824 } else if ((c == border) || (c == color)) {
1825 lastBorder = 1;
1826 }
1827 }
1828 }
1829
1830 /* Below */
1831 if (y < ((im->sy) - 1)) {
1832 lastBorder = 1;
1833 for (i = leftLimit; i <= rightLimit; i++) {
1834 int c = gdImageGetPixel(im, i, y + 1);
1835
1836 if (lastBorder) {
1837 if ((c != border) && (c != color)) {
1838 gdImageFillToBorder(im, i, y + 1, border, color);
1839 lastBorder = 0;
1840 }
1841 } else if ((c == border) || (c == color)) {
1842 lastBorder = 1;
1843 }
1844 }
1845 }
1846 im->alphaBlendingFlag = restoreAlphaBlending;
1847 }
1848
1849 /*
1850 * set the pixel at (x,y) and its 4-connected neighbors
1851 * with the same pixel value to the new pixel value nc (new color).
1852 * A 4-connected neighbor: pixel above, below, left, or right of a pixel.
1853 * ideas from comp.graphics discussions.
1854 * For tiled fill, the use of a flag buffer is mandatory. As the tile image can
1855 * contain the same color as the color to fill. To do not bloat normal filling
1856 * code I added a 2nd private function.
1857 */
1858
1859 /* horizontal segment of scan line y */
1860 struct seg {int y, xl, xr, dy;};
1861
1862 /* max depth of stack */
1863 #define FILL_MAX ((int)(im->sy*im->sx)/4)
1864 #define FILL_PUSH(Y, XL, XR, DY) \
1865 if (sp<stack+FILL_MAX && Y+(DY)>=0 && Y+(DY)<wy2) \
1866 {sp->y = Y; sp->xl = XL; sp->xr = XR; sp->dy = DY; sp++;}
1867
1868 #define FILL_POP(Y, XL, XR, DY) \
1869 {sp--; Y = sp->y+(DY = sp->dy); XL = sp->xl; XR = sp->xr;}
1870
1871 static void _gdImageFillTiled(gdImagePtr im, int x, int y, int nc);
1872
gdImageFill(gdImagePtr im,int x,int y,int nc)1873 void gdImageFill(gdImagePtr im, int x, int y, int nc)
1874 {
1875 int l, x1, x2, dy;
1876 int oc; /* old pixel value */
1877 int wx2,wy2;
1878
1879 int alphablending_bak;
1880
1881 /* stack of filled segments */
1882 /* struct seg stack[FILL_MAX],*sp = stack;; */
1883 struct seg *stack = NULL;
1884 struct seg *sp;
1885
1886 if (!im->trueColor && nc > (im->colorsTotal -1)) {
1887 return;
1888 }
1889
1890 alphablending_bak = im->alphaBlendingFlag;
1891 im->alphaBlendingFlag = 0;
1892
1893 if (nc==gdTiled){
1894 _gdImageFillTiled(im,x,y,nc);
1895 im->alphaBlendingFlag = alphablending_bak;
1896 return;
1897 }
1898
1899 wx2=im->sx;wy2=im->sy;
1900 oc = gdImageGetPixel(im, x, y);
1901 if (oc==nc || x<0 || x>wx2 || y<0 || y>wy2) {
1902 im->alphaBlendingFlag = alphablending_bak;
1903 return;
1904 }
1905
1906 /* Do not use the 4 neighbors implementation with
1907 * small images
1908 */
1909 if (im->sx < 4) {
1910 int ix = x, iy = y, c;
1911 do {
1912 do {
1913 c = gdImageGetPixel(im, ix, iy);
1914 if (c != oc) {
1915 goto done;
1916 }
1917 gdImageSetPixel(im, ix, iy, nc);
1918 } while(ix++ < (im->sx -1));
1919 ix = x;
1920 } while(iy++ < (im->sy -1));
1921 goto done;
1922 }
1923
1924 stack = (struct seg *)safe_emalloc(sizeof(struct seg), ((int)(im->sy*im->sx)/4), 1);
1925 sp = stack;
1926
1927 /* required! */
1928 FILL_PUSH(y,x,x,1);
1929 /* seed segment (popped 1st) */
1930 FILL_PUSH(y+1, x, x, -1);
1931 while (sp>stack) {
1932 FILL_POP(y, x1, x2, dy);
1933
1934 for (x=x1; x>=0 && gdImageGetPixel(im,x, y)==oc; x--) {
1935 gdImageSetPixel(im,x, y, nc);
1936 }
1937 if (x>=x1) {
1938 goto skip;
1939 }
1940 l = x+1;
1941
1942 /* leak on left? */
1943 if (l<x1) {
1944 FILL_PUSH(y, l, x1-1, -dy);
1945 }
1946 x = x1+1;
1947 do {
1948 for (; x<=wx2 && gdImageGetPixel(im,x, y)==oc; x++) {
1949 gdImageSetPixel(im, x, y, nc);
1950 }
1951 FILL_PUSH(y, l, x-1, dy);
1952 /* leak on right? */
1953 if (x>x2+1) {
1954 FILL_PUSH(y, x2+1, x-1, -dy);
1955 }
1956 skip: for (x++; x<=x2 && (gdImageGetPixel(im, x, y)!=oc); x++);
1957
1958 l = x;
1959 } while (x<=x2);
1960 }
1961
1962 efree(stack);
1963
1964 done:
1965 im->alphaBlendingFlag = alphablending_bak;
1966 }
1967
_gdImageFillTiled(gdImagePtr im,int x,int y,int nc)1968 static void _gdImageFillTiled(gdImagePtr im, int x, int y, int nc)
1969 {
1970 int i, l, x1, x2, dy;
1971 int oc; /* old pixel value */
1972 int wx2,wy2;
1973 /* stack of filled segments */
1974 struct seg *stack;
1975 struct seg *sp;
1976 char **pts;
1977
1978 if (!im->tile) {
1979 return;
1980 }
1981
1982 wx2=im->sx;wy2=im->sy;
1983
1984 nc = gdImageTileGet(im,x,y);
1985
1986 pts = (char **) ecalloc(im->sy + 1, sizeof(char *));
1987 for (i = 0; i < im->sy + 1; i++) {
1988 pts[i] = (char *) ecalloc(im->sx + 1, sizeof(char));
1989 }
1990
1991 stack = (struct seg *)safe_emalloc(sizeof(struct seg), ((int)(im->sy*im->sx)/4), 1);
1992 sp = stack;
1993
1994 oc = gdImageGetPixel(im, x, y);
1995
1996 /* required! */
1997 FILL_PUSH(y,x,x,1);
1998 /* seed segment (popped 1st) */
1999 FILL_PUSH(y+1, x, x, -1);
2000 while (sp>stack) {
2001 FILL_POP(y, x1, x2, dy);
2002 for (x=x1; x>=0 && (!pts[y][x] && gdImageGetPixel(im,x,y)==oc); x--) {
2003 nc = gdImageTileGet(im,x,y);
2004 pts[y][x] = 1;
2005 gdImageSetPixel(im,x, y, nc);
2006 }
2007 if (x>=x1) {
2008 goto skip;
2009 }
2010 l = x+1;
2011
2012 /* leak on left? */
2013 if (l<x1) {
2014 FILL_PUSH(y, l, x1-1, -dy);
2015 }
2016 x = x1+1;
2017 do {
2018 for(; x<wx2 && (!pts[y][x] && gdImageGetPixel(im,x, y)==oc); x++) {
2019 nc = gdImageTileGet(im,x,y);
2020 pts[y][x] = 1;
2021 gdImageSetPixel(im, x, y, nc);
2022 }
2023 FILL_PUSH(y, l, x-1, dy);
2024 /* leak on right? */
2025 if (x>x2+1) {
2026 FILL_PUSH(y, x2+1, x-1, -dy);
2027 }
2028 skip: for(x++; x<=x2 && (pts[y][x] || gdImageGetPixel(im,x, y)!=oc); x++);
2029 l = x;
2030 } while (x<=x2);
2031 }
2032
2033 for(i = 0; i < im->sy + 1; i++) {
2034 efree(pts[i]);
2035 }
2036
2037 efree(pts);
2038 efree(stack);
2039 }
2040
2041
2042
gdImageRectangle(gdImagePtr im,int x1,int y1,int x2,int y2,int color)2043 void gdImageRectangle (gdImagePtr im, int x1, int y1, int x2, int y2, int color)
2044 {
2045 int x1h = x1, x1v = x1, y1h = y1, y1v = y1, x2h = x2, x2v = x2, y2h = y2, y2v = y2;
2046 int thick = im->thick;
2047 int t;
2048
2049 if (x1 == x2 && y1 == y2 && thick == 1) {
2050 gdImageSetPixel(im, x1, y1, color);
2051 return;
2052 }
2053
2054 if (y2 < y1) {
2055 t=y1;
2056 y1 = y2;
2057 y2 = t;
2058
2059 t = x1;
2060 x1 = x2;
2061 x2 = t;
2062 }
2063
2064 x1h = x1; x1v = x1; y1h = y1; y1v = y1; x2h = x2; x2v = x2; y2h = y2; y2v = y2;
2065 if (thick > 1) {
2066 int cx, cy, x1ul, y1ul, x2lr, y2lr;
2067 int half = thick >> 1;
2068
2069 x1ul = x1 - half;
2070 y1ul = y1 - half;
2071
2072 x2lr = x2 + half;
2073 y2lr = y2 + half;
2074
2075 cy = y1ul + thick;
2076 while (cy-- > y1ul) {
2077 cx = x1ul - 1;
2078 while (cx++ < x2lr) {
2079 gdImageSetPixel(im, cx, cy, color);
2080 }
2081 }
2082
2083 cy = y2lr - thick;
2084 while (cy++ < y2lr) {
2085 cx = x1ul - 1;
2086 while (cx++ < x2lr) {
2087 gdImageSetPixel(im, cx, cy, color);
2088 }
2089 }
2090
2091 cy = y1ul + thick - 1;
2092 while (cy++ < y2lr -thick) {
2093 cx = x1ul - 1;
2094 while (cx++ < x1ul + thick) {
2095 gdImageSetPixel(im, cx, cy, color);
2096 }
2097 }
2098
2099 cy = y1ul + thick - 1;
2100 while (cy++ < y2lr -thick) {
2101 cx = x2lr - thick - 1;
2102 while (cx++ < x2lr) {
2103 gdImageSetPixel(im, cx, cy, color);
2104 }
2105 }
2106
2107 return;
2108 } else {
2109 y1v = y1h + 1;
2110 y2v = y2h - 1;
2111 gdImageLine(im, x1h, y1h, x2h, y1h, color);
2112 gdImageLine(im, x1h, y2h, x2h, y2h, color);
2113 gdImageLine(im, x1v, y1v, x1v, y2v, color);
2114 gdImageLine(im, x2v, y1v, x2v, y2v, color);
2115 }
2116 }
2117
gdImageFilledRectangle(gdImagePtr im,int x1,int y1,int x2,int y2,int color)2118 void gdImageFilledRectangle (gdImagePtr im, int x1, int y1, int x2, int y2, int color)
2119 {
2120 int x, y;
2121
2122
2123 if (x1 == x2 && y1 == y2) {
2124 gdImageSetPixel(im, x1, y1, color);
2125 return;
2126 }
2127
2128 if (x1 > x2) {
2129 x = x1;
2130 x1 = x2;
2131 x2 = x;
2132 }
2133
2134 if (y1 > y2) {
2135 y = y1;
2136 y1 = y2;
2137 y2 = y;
2138 }
2139
2140 if (x1 < 0) {
2141 x1 = 0;
2142 }
2143
2144 if (x2 >= gdImageSX(im)) {
2145 x2 = gdImageSX(im) - 1;
2146 }
2147
2148 if (y1 < 0) {
2149 y1 = 0;
2150 }
2151
2152 if (y2 >= gdImageSY(im)) {
2153 y2 = gdImageSY(im) - 1;
2154 }
2155
2156 for (y = y1; (y <= y2); y++) {
2157 for (x = x1; (x <= x2); x++) {
2158 gdImageSetPixel (im, x, y, color);
2159 }
2160 }
2161 }
2162
gdImageCopy(gdImagePtr dst,gdImagePtr src,int dstX,int dstY,int srcX,int srcY,int w,int h)2163 void gdImageCopy (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int w, int h)
2164 {
2165 int c;
2166 int x, y;
2167 int tox, toy;
2168 int i;
2169 int colorMap[gdMaxColors];
2170
2171 if (dst->trueColor) {
2172 /* 2.0: much easier when the destination is truecolor. */
2173 /* 2.0.10: needs a transparent-index check that is still valid if
2174 * the source is not truecolor. Thanks to Frank Warmerdam.
2175 */
2176
2177 if (src->trueColor) {
2178 for (y = 0; (y < h); y++) {
2179 for (x = 0; (x < w); x++) {
2180 int c = gdImageGetTrueColorPixel (src, srcX + x, srcY + y);
2181 gdImageSetPixel (dst, dstX + x, dstY + y, c);
2182 }
2183 }
2184 } else {
2185 /* source is palette based */
2186 for (y = 0; (y < h); y++) {
2187 for (x = 0; (x < w); x++) {
2188 int c = gdImageGetPixel (src, srcX + x, srcY + y);
2189 if (c != src->transparent) {
2190 gdImageSetPixel(dst, dstX + x, dstY + y, gdTrueColorAlpha(src->red[c], src->green[c], src->blue[c], src->alpha[c]));
2191 }
2192 }
2193 }
2194 }
2195 return;
2196 }
2197
2198 /* Destination is palette based */
2199 if (src->trueColor) { /* But source is truecolor (Ouch!) */
2200 toy = dstY;
2201 for (y = srcY; (y < (srcY + h)); y++) {
2202 tox = dstX;
2203 for (x = srcX; x < (srcX + w); x++) {
2204 int nc;
2205 c = gdImageGetPixel (src, x, y);
2206
2207 /* Get best match possible. */
2208 nc = gdImageColorResolveAlpha(dst, gdTrueColorGetRed(c), gdTrueColorGetGreen(c), gdTrueColorGetBlue(c), gdTrueColorGetAlpha(c));
2209
2210 gdImageSetPixel(dst, tox, toy, nc);
2211 tox++;
2212 }
2213 toy++;
2214 }
2215 return;
2216 }
2217
2218 /* Palette based to palette based */
2219 for (i = 0; i < gdMaxColors; i++) {
2220 colorMap[i] = (-1);
2221 }
2222 toy = dstY;
2223 for (y = srcY; y < (srcY + h); y++) {
2224 tox = dstX;
2225 for (x = srcX; x < (srcX + w); x++) {
2226 int nc;
2227 int mapTo;
2228 c = gdImageGetPixel (src, x, y);
2229 /* Added 7/24/95: support transparent copies */
2230 if (gdImageGetTransparent (src) == c) {
2231 tox++;
2232 continue;
2233 }
2234 /* Have we established a mapping for this color? */
2235 if (src->trueColor) {
2236 /* 2.05: remap to the palette available in the destination image. This is slow and
2237 * works badly, but it beats crashing! Thanks to Padhrig McCarthy.
2238 */
2239 mapTo = gdImageColorResolveAlpha (dst, gdTrueColorGetRed (c), gdTrueColorGetGreen (c), gdTrueColorGetBlue (c), gdTrueColorGetAlpha (c));
2240 } else if (colorMap[c] == (-1)) {
2241 /* If it's the same image, mapping is trivial */
2242 if (dst == src) {
2243 nc = c;
2244 } else {
2245 /* Get best match possible. This function never returns error. */
2246 nc = gdImageColorResolveAlpha (dst, src->red[c], src->green[c], src->blue[c], src->alpha[c]);
2247 }
2248 colorMap[c] = nc;
2249 mapTo = colorMap[c];
2250 } else {
2251 mapTo = colorMap[c];
2252 }
2253 gdImageSetPixel (dst, tox, toy, mapTo);
2254 tox++;
2255 }
2256 toy++;
2257 }
2258 }
2259
2260 /* This function is a substitute for real alpha channel operations,
2261 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)2262 void gdImageCopyMerge (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int w, int h, int pct)
2263 {
2264 int c, dc;
2265 int x, y;
2266 int tox, toy;
2267 int ncR, ncG, ncB;
2268 toy = dstY;
2269
2270 for (y = srcY; y < (srcY + h); y++) {
2271 tox = dstX;
2272 for (x = srcX; x < (srcX + w); x++) {
2273 int nc;
2274 c = gdImageGetPixel(src, x, y);
2275 /* Added 7/24/95: support transparent copies */
2276 if (gdImageGetTransparent(src) == c) {
2277 tox++;
2278 continue;
2279 }
2280 /* If it's the same image, mapping is trivial */
2281 if (dst == src) {
2282 nc = c;
2283 } else {
2284 dc = gdImageGetPixel(dst, tox, toy);
2285
2286 ncR = (int)(gdImageRed (src, c) * (pct / 100.0) + gdImageRed (dst, dc) * ((100 - pct) / 100.0));
2287 ncG = (int)(gdImageGreen (src, c) * (pct / 100.0) + gdImageGreen (dst, dc) * ((100 - pct) / 100.0));
2288 ncB = (int)(gdImageBlue (src, c) * (pct / 100.0) + gdImageBlue (dst, dc) * ((100 - pct) / 100.0));
2289
2290 /* Find a reasonable color */
2291 nc = gdImageColorResolve (dst, ncR, ncG, ncB);
2292 }
2293 gdImageSetPixel (dst, tox, toy, nc);
2294 tox++;
2295 }
2296 toy++;
2297 }
2298 }
2299
2300 /* This function is a substitute for real alpha channel operations,
2301 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)2302 void gdImageCopyMergeGray (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int w, int h, int pct)
2303 {
2304 int c, dc;
2305 int x, y;
2306 int tox, toy;
2307 int ncR, ncG, ncB;
2308 float g;
2309 toy = dstY;
2310
2311 for (y = srcY; (y < (srcY + h)); y++) {
2312 tox = dstX;
2313 for (x = srcX; (x < (srcX + w)); x++) {
2314 int nc;
2315 c = gdImageGetPixel (src, x, y);
2316 /* Added 7/24/95: support transparent copies */
2317 if (gdImageGetTransparent(src) == c) {
2318 tox++;
2319 continue;
2320 }
2321
2322 /*
2323 * If it's the same image, mapping is NOT trivial since we
2324 * merge with greyscale target, but if pct is 100, the grey
2325 * value is not used, so it becomes trivial. pjw 2.0.12.
2326 */
2327 if (dst == src && pct == 100) {
2328 nc = c;
2329 } else {
2330 dc = gdImageGetPixel(dst, tox, toy);
2331 g = (0.29900f * gdImageRed(dst, dc)) + (0.58700f * gdImageGreen(dst, dc)) + (0.11400f * gdImageBlue(dst, dc));
2332
2333 ncR = (int)(gdImageRed (src, c) * (pct / 100.0f) + g * ((100 - pct) / 100.0));
2334 ncG = (int)(gdImageGreen (src, c) * (pct / 100.0f) + g * ((100 - pct) / 100.0));
2335 ncB = (int)(gdImageBlue (src, c) * (pct / 100.0f) + g * ((100 - pct) / 100.0));
2336
2337
2338 /* First look for an exact match */
2339 nc = gdImageColorExact(dst, ncR, ncG, ncB);
2340 if (nc == (-1)) {
2341 /* No, so try to allocate it */
2342 nc = gdImageColorAllocate(dst, ncR, ncG, ncB);
2343 /* If we're out of colors, go for the closest color */
2344 if (nc == (-1)) {
2345 nc = gdImageColorClosest(dst, ncR, ncG, ncB);
2346 }
2347 }
2348 }
2349 gdImageSetPixel(dst, tox, toy, nc);
2350 tox++;
2351 }
2352 toy++;
2353 }
2354 }
2355
gdImageCopyResized(gdImagePtr dst,gdImagePtr src,int dstX,int dstY,int srcX,int srcY,int dstW,int dstH,int srcW,int srcH)2356 void gdImageCopyResized (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int dstW, int dstH, int srcW, int srcH)
2357 {
2358 int c;
2359 int x, y;
2360 int tox, toy;
2361 int ydest;
2362 int i;
2363 int colorMap[gdMaxColors];
2364 /* Stretch vectors */
2365 int *stx, *sty;
2366
2367 if (overflow2(sizeof(int), srcW)) {
2368 return;
2369 }
2370 if (overflow2(sizeof(int), srcH)) {
2371 return;
2372 }
2373
2374 stx = (int *) gdMalloc (sizeof (int) * srcW);
2375 sty = (int *) gdMalloc (sizeof (int) * srcH);
2376
2377 /* Fixed by Mao Morimoto 2.0.16 */
2378 for (i = 0; (i < srcW); i++) {
2379 stx[i] = dstW * (i+1) / srcW - dstW * i / srcW ;
2380 }
2381 for (i = 0; (i < srcH); i++) {
2382 sty[i] = dstH * (i+1) / srcH - dstH * i / srcH ;
2383 }
2384 for (i = 0; (i < gdMaxColors); i++) {
2385 colorMap[i] = (-1);
2386 }
2387 toy = dstY;
2388 for (y = srcY; (y < (srcY + srcH)); y++) {
2389 for (ydest = 0; (ydest < sty[y - srcY]); ydest++) {
2390 tox = dstX;
2391 for (x = srcX; (x < (srcX + srcW)); x++) {
2392 int nc = 0;
2393 int mapTo;
2394 if (!stx[x - srcX]) {
2395 continue;
2396 }
2397 if (dst->trueColor) {
2398 /* 2.0.9: Thorben Kundinger: Maybe the source image is not a truecolor image */
2399 if (!src->trueColor) {
2400 int tmp = gdImageGetPixel (src, x, y);
2401 mapTo = gdImageGetTrueColorPixel (src, x, y);
2402 if (gdImageGetTransparent (src) == tmp) {
2403 /* 2.0.21, TK: not tox++ */
2404 tox += stx[x - srcX];
2405 continue;
2406 }
2407 } else {
2408 /* TK: old code follows */
2409 mapTo = gdImageGetTrueColorPixel (src, x, y);
2410 /* Added 7/24/95: support transparent copies */
2411 if (gdImageGetTransparent (src) == mapTo) {
2412 /* 2.0.21, TK: not tox++ */
2413 tox += stx[x - srcX];
2414 continue;
2415 }
2416 }
2417 } else {
2418 c = gdImageGetPixel (src, x, y);
2419 /* Added 7/24/95: support transparent copies */
2420 if (gdImageGetTransparent (src) == c) {
2421 tox += stx[x - srcX];
2422 continue;
2423 }
2424 if (src->trueColor) {
2425 /* Remap to the palette available in the destination image. This is slow and works badly. */
2426 mapTo = gdImageColorResolveAlpha(dst, gdTrueColorGetRed(c),
2427 gdTrueColorGetGreen(c),
2428 gdTrueColorGetBlue(c),
2429 gdTrueColorGetAlpha (c));
2430 } else {
2431 /* Have we established a mapping for this color? */
2432 if (colorMap[c] == (-1)) {
2433 /* If it's the same image, mapping is trivial */
2434 if (dst == src) {
2435 nc = c;
2436 } else {
2437 /* Find or create the best match */
2438 /* 2.0.5: can't use gdTrueColorGetRed, etc with palette */
2439 nc = gdImageColorResolveAlpha(dst, gdImageRed(src, c),
2440 gdImageGreen(src, c),
2441 gdImageBlue(src, c),
2442 gdImageAlpha(src, c));
2443 }
2444 colorMap[c] = nc;
2445 }
2446 mapTo = colorMap[c];
2447 }
2448 }
2449 for (i = 0; (i < stx[x - srcX]); i++) {
2450 gdImageSetPixel (dst, tox, toy, mapTo);
2451 tox++;
2452 }
2453 }
2454 toy++;
2455 }
2456 }
2457 gdFree (stx);
2458 gdFree (sty);
2459 }
2460
2461 /* When gd 1.x was first created, floating point was to be avoided.
2462 These days it is often faster than table lookups or integer
2463 arithmetic. The routine below is shamelessly, gloriously
2464 floating point. TBB */
2465
gdImageCopyResampled(gdImagePtr dst,gdImagePtr src,int dstX,int dstY,int srcX,int srcY,int dstW,int dstH,int srcW,int srcH)2466 void gdImageCopyResampled (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int dstW, int dstH, int srcW, int srcH)
2467 {
2468 int x, y;
2469 double sy1, sy2, sx1, sx2;
2470
2471 if (!dst->trueColor) {
2472 gdImageCopyResized (dst, src, dstX, dstY, srcX, srcY, dstW, dstH, srcW, srcH);
2473 return;
2474 }
2475 for (y = dstY; (y < dstY + dstH); y++) {
2476 sy1 = ((double) y - (double) dstY) * (double) srcH / (double) dstH;
2477 sy2 = ((double) (y + 1) - (double) dstY) * (double) srcH / (double) dstH;
2478 for (x = dstX; (x < dstX + dstW); x++) {
2479 double sx, sy;
2480 double spixels = 0;
2481 double red = 0.0, green = 0.0, blue = 0.0, alpha = 0.0;
2482 double alpha_factor, alpha_sum = 0.0, contrib_sum = 0.0;
2483 sx1 = ((double) x - (double) dstX) * (double) srcW / dstW;
2484 sx2 = ((double) (x + 1) - (double) dstX) * (double) srcW / dstW;
2485 sy = sy1;
2486 do {
2487 double yportion;
2488 if (floor_cast(sy) == floor_cast(sy1)) {
2489 yportion = 1.0f - (sy - floor_cast(sy));
2490 if (yportion > sy2 - sy1) {
2491 yportion = sy2 - sy1;
2492 }
2493 sy = floor_cast(sy);
2494 } else if (sy == floorf(sy2)) {
2495 yportion = sy2 - floor_cast(sy2);
2496 } else {
2497 yportion = 1.0f;
2498 }
2499 sx = sx1;
2500 do {
2501 double xportion;
2502 double pcontribution;
2503 int p;
2504 if (floorf(sx) == floor_cast(sx1)) {
2505 xportion = 1.0f - (sx - floor_cast(sx));
2506 if (xportion > sx2 - sx1) {
2507 xportion = sx2 - sx1;
2508 }
2509 sx = floor_cast(sx);
2510 } else if (sx == floorf(sx2)) {
2511 xportion = sx2 - floor_cast(sx2);
2512 } else {
2513 xportion = 1.0f;
2514 }
2515 pcontribution = xportion * yportion;
2516 p = gdImageGetTrueColorPixel(src, (int) sx + srcX, (int) sy + srcY);
2517
2518 alpha_factor = ((gdAlphaMax - gdTrueColorGetAlpha(p))) * pcontribution;
2519 red += gdTrueColorGetRed (p) * alpha_factor;
2520 green += gdTrueColorGetGreen (p) * alpha_factor;
2521 blue += gdTrueColorGetBlue (p) * alpha_factor;
2522 alpha += gdTrueColorGetAlpha (p) * pcontribution;
2523 alpha_sum += alpha_factor;
2524 contrib_sum += pcontribution;
2525 spixels += xportion * yportion;
2526 sx += 1.0f;
2527 }
2528 while (sx < sx2);
2529
2530 sy += 1.0f;
2531 }
2532
2533 while (sy < sy2);
2534
2535 if (spixels != 0.0f) {
2536 red /= spixels;
2537 green /= spixels;
2538 blue /= spixels;
2539 alpha /= spixels;
2540 alpha += 0.5;
2541 }
2542 if ( alpha_sum != 0.0f) {
2543 if( contrib_sum != 0.0f) {
2544 alpha_sum /= contrib_sum;
2545 }
2546 red /= alpha_sum;
2547 green /= alpha_sum;
2548 blue /= alpha_sum;
2549 }
2550 /* Clamping to allow for rounding errors above */
2551 if (red > 255.0f) {
2552 red = 255.0f;
2553 }
2554 if (green > 255.0f) {
2555 green = 255.0f;
2556 }
2557 if (blue > 255.0f) {
2558 blue = 255.0f;
2559 }
2560 if (alpha > gdAlphaMax) {
2561 alpha = gdAlphaMax;
2562 }
2563 gdImageSetPixel(dst, x, y, gdTrueColorAlpha ((int) red, (int) green, (int) blue, (int) alpha));
2564 }
2565 }
2566 }
2567
gdImagePolygon(gdImagePtr im,gdPointPtr p,int n,int c)2568 void gdImagePolygon (gdImagePtr im, gdPointPtr p, int n, int c)
2569 {
2570 int i;
2571 int lx, ly;
2572 typedef void (*image_line)(gdImagePtr im, int x1, int y1, int x2, int y2, int color);
2573 image_line draw_line;
2574
2575 if (n <= 0) {
2576 return;
2577 }
2578
2579 /* Let it be known that we are drawing a polygon so that the opacity
2580 * mask doesn't get cleared after each line.
2581 */
2582 if (c == gdAntiAliased) {
2583 im->AA_polygon = 1;
2584 }
2585
2586 if ( im->antialias) {
2587 draw_line = gdImageAALine;
2588 } else {
2589 draw_line = gdImageLine;
2590 }
2591 lx = p->x;
2592 ly = p->y;
2593 draw_line(im, lx, ly, p[n - 1].x, p[n - 1].y, c);
2594 for (i = 1; i < n; i++) {
2595 p++;
2596 draw_line(im, lx, ly, p->x, p->y, c);
2597 lx = p->x;
2598 ly = p->y;
2599 }
2600
2601 if (c == gdAntiAliased) {
2602 im->AA_polygon = 0;
2603 gdImageAABlend(im);
2604 }
2605 }
2606
2607 int gdCompareInt (const void *a, const void *b);
2608
2609 /* THANKS to Kirsten Schulz for the polygon fixes! */
2610
2611 /* The intersection finding technique of this code could be improved
2612 * by remembering the previous intertersection, and by using the slope.
2613 * That could help to adjust intersections to produce a nice
2614 * interior_extrema.
2615 */
2616
gdImageFilledPolygon(gdImagePtr im,gdPointPtr p,int n,int c)2617 void gdImageFilledPolygon (gdImagePtr im, gdPointPtr p, int n, int c)
2618 {
2619 int i;
2620 int y;
2621 int miny, maxy, pmaxy;
2622 int x1, y1;
2623 int x2, y2;
2624 int ind1, ind2;
2625 int ints;
2626 int fill_color;
2627
2628 if (n <= 0) {
2629 return;
2630 }
2631
2632 if (overflow2(sizeof(int), n)) {
2633 return;
2634 }
2635
2636 if (c == gdAntiAliased) {
2637 fill_color = im->AA_color;
2638 } else {
2639 fill_color = c;
2640 }
2641
2642 if (!im->polyAllocated) {
2643 im->polyInts = (int *) gdMalloc(sizeof(int) * n);
2644 im->polyAllocated = n;
2645 }
2646 if (im->polyAllocated < n) {
2647 while (im->polyAllocated < n) {
2648 im->polyAllocated *= 2;
2649 }
2650 if (overflow2(sizeof(int), im->polyAllocated)) {
2651 return;
2652 }
2653 im->polyInts = (int *) gdRealloc(im->polyInts, sizeof(int) * im->polyAllocated);
2654 }
2655 miny = p[0].y;
2656 maxy = p[0].y;
2657 for (i = 1; i < n; i++) {
2658 if (p[i].y < miny) {
2659 miny = p[i].y;
2660 }
2661 if (p[i].y > maxy) {
2662 maxy = p[i].y;
2663 }
2664 }
2665 pmaxy = maxy;
2666 /* 2.0.16: Optimization by Ilia Chipitsine -- don't waste time offscreen */
2667 if (miny < 0) {
2668 miny = 0;
2669 }
2670 if (maxy >= gdImageSY(im)) {
2671 maxy = gdImageSY(im) - 1;
2672 }
2673
2674 /* Fix in 1.3: count a vertex only once */
2675 for (y = miny; y <= maxy; y++) {
2676 /*1.4 int interLast = 0; */
2677 /* int dirLast = 0; */
2678 /* int interFirst = 1; */
2679 ints = 0;
2680 for (i = 0; i < n; i++) {
2681 if (!i) {
2682 ind1 = n - 1;
2683 ind2 = 0;
2684 } else {
2685 ind1 = i - 1;
2686 ind2 = i;
2687 }
2688 y1 = p[ind1].y;
2689 y2 = p[ind2].y;
2690 if (y1 < y2) {
2691 x1 = p[ind1].x;
2692 x2 = p[ind2].x;
2693 } else if (y1 > y2) {
2694 y2 = p[ind1].y;
2695 y1 = p[ind2].y;
2696 x2 = p[ind1].x;
2697 x1 = p[ind2].x;
2698 } else {
2699 continue;
2700 }
2701 /* Do the following math as float intermediately, and round to ensure
2702 * that Polygon and FilledPolygon for the same set of points have the
2703 * same footprint.
2704 */
2705 if (y >= y1 && y < y2) {
2706 im->polyInts[ints++] = (float) ((y - y1) * (x2 - x1)) / (float) (y2 - y1) + 0.5 + x1;
2707 } else if (y == pmaxy && y == y2) {
2708 im->polyInts[ints++] = x2;
2709 }
2710 }
2711 qsort(im->polyInts, ints, sizeof(int), gdCompareInt);
2712
2713 for (i = 0; i < ints - 1; i += 2) {
2714 gdImageLine(im, im->polyInts[i], y, im->polyInts[i + 1], y, fill_color);
2715 }
2716 }
2717
2718 /* If we are drawing this AA, then redraw the border with AA lines. */
2719 if (c == gdAntiAliased) {
2720 gdImagePolygon(im, p, n, c);
2721 }
2722 }
2723
gdCompareInt(const void * a,const void * b)2724 int gdCompareInt (const void *a, const void *b)
2725 {
2726 return (*(const int *) a) - (*(const int *) b);
2727 }
2728
gdImageSetStyle(gdImagePtr im,int * style,int noOfPixels)2729 void gdImageSetStyle (gdImagePtr im, int *style, int noOfPixels)
2730 {
2731 if (im->style) {
2732 gdFree(im->style);
2733 }
2734 im->style = (int *) gdMalloc(sizeof(int) * noOfPixels);
2735 memcpy(im->style, style, sizeof(int) * noOfPixels);
2736 im->styleLength = noOfPixels;
2737 im->stylePos = 0;
2738 }
2739
gdImageSetThickness(gdImagePtr im,int thickness)2740 void gdImageSetThickness (gdImagePtr im, int thickness)
2741 {
2742 im->thick = thickness;
2743 }
2744
gdImageSetBrush(gdImagePtr im,gdImagePtr brush)2745 void gdImageSetBrush (gdImagePtr im, gdImagePtr brush)
2746 {
2747 int i;
2748 im->brush = brush;
2749 if (!im->trueColor && !im->brush->trueColor) {
2750 for (i = 0; i < gdImageColorsTotal(brush); i++) {
2751 int index;
2752 index = gdImageColorResolveAlpha(im, gdImageRed(brush, i), gdImageGreen(brush, i), gdImageBlue(brush, i), gdImageAlpha(brush, i));
2753 im->brushColorMap[i] = index;
2754 }
2755 }
2756 }
2757
gdImageSetTile(gdImagePtr im,gdImagePtr tile)2758 void gdImageSetTile (gdImagePtr im, gdImagePtr tile)
2759 {
2760 int i;
2761 im->tile = tile;
2762 if (!im->trueColor && !im->tile->trueColor) {
2763 for (i = 0; i < gdImageColorsTotal(tile); i++) {
2764 int index;
2765 index = gdImageColorResolveAlpha(im, gdImageRed(tile, i), gdImageGreen(tile, i), gdImageBlue(tile, i), gdImageAlpha(tile, i));
2766 im->tileColorMap[i] = index;
2767 }
2768 }
2769 }
2770
gdImageSetAntiAliased(gdImagePtr im,int c)2771 void gdImageSetAntiAliased (gdImagePtr im, int c)
2772 {
2773 im->AA = 1;
2774 im->AA_color = c;
2775 im->AA_dont_blend = -1;
2776 }
2777
gdImageSetAntiAliasedDontBlend(gdImagePtr im,int c,int dont_blend)2778 void gdImageSetAntiAliasedDontBlend (gdImagePtr im, int c, int dont_blend)
2779 {
2780 im->AA = 1;
2781 im->AA_color = c;
2782 im->AA_dont_blend = dont_blend;
2783 }
2784
2785
gdImageInterlace(gdImagePtr im,int interlaceArg)2786 void gdImageInterlace (gdImagePtr im, int interlaceArg)
2787 {
2788 im->interlace = interlaceArg;
2789 }
2790
gdImageCompare(gdImagePtr im1,gdImagePtr im2)2791 int gdImageCompare (gdImagePtr im1, gdImagePtr im2)
2792 {
2793 int x, y;
2794 int p1, p2;
2795 int cmpStatus = 0;
2796 int sx, sy;
2797
2798 if (im1->interlace != im2->interlace) {
2799 cmpStatus |= GD_CMP_INTERLACE;
2800 }
2801
2802 if (im1->transparent != im2->transparent) {
2803 cmpStatus |= GD_CMP_TRANSPARENT;
2804 }
2805
2806 if (im1->trueColor != im2->trueColor) {
2807 cmpStatus |= GD_CMP_TRUECOLOR;
2808 }
2809
2810 sx = im1->sx;
2811 if (im1->sx != im2->sx) {
2812 cmpStatus |= GD_CMP_SIZE_X + GD_CMP_IMAGE;
2813 if (im2->sx < im1->sx) {
2814 sx = im2->sx;
2815 }
2816 }
2817
2818 sy = im1->sy;
2819 if (im1->sy != im2->sy) {
2820 cmpStatus |= GD_CMP_SIZE_Y + GD_CMP_IMAGE;
2821 if (im2->sy < im1->sy) {
2822 sy = im2->sy;
2823 }
2824 }
2825
2826 if (im1->colorsTotal != im2->colorsTotal) {
2827 cmpStatus |= GD_CMP_NUM_COLORS;
2828 }
2829
2830 for (y = 0; y < sy; y++) {
2831 for (x = 0; x < sx; x++) {
2832 p1 = im1->trueColor ? gdImageTrueColorPixel(im1, x, y) : gdImagePalettePixel(im1, x, y);
2833 p2 = im2->trueColor ? gdImageTrueColorPixel(im2, x, y) : gdImagePalettePixel(im2, x, y);
2834
2835 if (gdImageRed(im1, p1) != gdImageRed(im2, p2)) {
2836 cmpStatus |= GD_CMP_COLOR + GD_CMP_IMAGE;
2837 break;
2838 }
2839 if (gdImageGreen(im1, p1) != gdImageGreen(im2, p2)) {
2840 cmpStatus |= GD_CMP_COLOR + GD_CMP_IMAGE;
2841 break;
2842 }
2843 if (gdImageBlue(im1, p1) != gdImageBlue(im2, p2)) {
2844 cmpStatus |= GD_CMP_COLOR + GD_CMP_IMAGE;
2845 break;
2846 }
2847 #if 0
2848 /* Soon we'll add alpha channel to palettes */
2849 if (gdImageAlpha(im1, p1) != gdImageAlpha(im2, p2)) {
2850 cmpStatus |= GD_CMP_COLOR + GD_CMP_IMAGE;
2851 break;
2852 }
2853 #endif
2854 }
2855 if (cmpStatus & GD_CMP_COLOR) {
2856 break;
2857 }
2858 }
2859
2860 return cmpStatus;
2861 }
2862
2863 int
gdAlphaBlendOld(int dst,int src)2864 gdAlphaBlendOld (int dst, int src)
2865 {
2866 /* 2.0.12: TBB: alpha in the destination should be a
2867 * component of the result. Thanks to Frank Warmerdam for
2868 * pointing out the issue.
2869 */
2870 return ((((gdTrueColorGetAlpha (src) *
2871 gdTrueColorGetAlpha (dst)) / gdAlphaMax) << 24) +
2872 ((((gdAlphaTransparent - gdTrueColorGetAlpha (src)) *
2873 gdTrueColorGetRed (src) / gdAlphaMax) +
2874 (gdTrueColorGetAlpha (src) *
2875 gdTrueColorGetRed (dst)) / gdAlphaMax) << 16) +
2876 ((((gdAlphaTransparent - gdTrueColorGetAlpha (src)) *
2877 gdTrueColorGetGreen (src) / gdAlphaMax) +
2878 (gdTrueColorGetAlpha (src) *
2879 gdTrueColorGetGreen (dst)) / gdAlphaMax) << 8) +
2880 (((gdAlphaTransparent - gdTrueColorGetAlpha (src)) *
2881 gdTrueColorGetBlue (src) / gdAlphaMax) +
2882 (gdTrueColorGetAlpha (src) *
2883 gdTrueColorGetBlue (dst)) / gdAlphaMax));
2884 }
2885
gdAlphaBlend(int dst,int src)2886 int gdAlphaBlend (int dst, int src) {
2887 int src_alpha = gdTrueColorGetAlpha(src);
2888 int dst_alpha, alpha, red, green, blue;
2889 int src_weight, dst_weight, tot_weight;
2890
2891 /* -------------------------------------------------------------------- */
2892 /* Simple cases we want to handle fast. */
2893 /* -------------------------------------------------------------------- */
2894 if( src_alpha == gdAlphaOpaque )
2895 return src;
2896
2897 dst_alpha = gdTrueColorGetAlpha(dst);
2898 if( src_alpha == gdAlphaTransparent )
2899 return dst;
2900 if( dst_alpha == gdAlphaTransparent )
2901 return src;
2902
2903 /* -------------------------------------------------------------------- */
2904 /* What will the source and destination alphas be? Note that */
2905 /* the destination weighting is substantially reduced as the */
2906 /* overlay becomes quite opaque. */
2907 /* -------------------------------------------------------------------- */
2908 src_weight = gdAlphaTransparent - src_alpha;
2909 dst_weight = (gdAlphaTransparent - dst_alpha) * src_alpha / gdAlphaMax;
2910 tot_weight = src_weight + dst_weight;
2911
2912 /* -------------------------------------------------------------------- */
2913 /* What red, green and blue result values will we use? */
2914 /* -------------------------------------------------------------------- */
2915 alpha = src_alpha * dst_alpha / gdAlphaMax;
2916
2917 red = (gdTrueColorGetRed(src) * src_weight
2918 + gdTrueColorGetRed(dst) * dst_weight) / tot_weight;
2919 green = (gdTrueColorGetGreen(src) * src_weight
2920 + gdTrueColorGetGreen(dst) * dst_weight) / tot_weight;
2921 blue = (gdTrueColorGetBlue(src) * src_weight
2922 + gdTrueColorGetBlue(dst) * dst_weight) / tot_weight;
2923
2924 /* -------------------------------------------------------------------- */
2925 /* Return merged result. */
2926 /* -------------------------------------------------------------------- */
2927 return ((alpha << 24) + (red << 16) + (green << 8) + blue);
2928
2929 }
2930
gdImageAlphaBlending(gdImagePtr im,int alphaBlendingArg)2931 void gdImageAlphaBlending (gdImagePtr im, int alphaBlendingArg)
2932 {
2933 im->alphaBlendingFlag = alphaBlendingArg;
2934 }
2935
gdImageAntialias(gdImagePtr im,int antialias)2936 void gdImageAntialias (gdImagePtr im, int antialias)
2937 {
2938 if (im->trueColor){
2939 im->antialias = antialias;
2940 }
2941 }
2942
gdImageSaveAlpha(gdImagePtr im,int saveAlphaArg)2943 void gdImageSaveAlpha (gdImagePtr im, int saveAlphaArg)
2944 {
2945 im->saveAlphaFlag = saveAlphaArg;
2946 }
2947
gdLayerOverlay(int dst,int src)2948 static int gdLayerOverlay (int dst, int src)
2949 {
2950 int a1, a2;
2951 a1 = gdAlphaMax - gdTrueColorGetAlpha(dst);
2952 a2 = gdAlphaMax - gdTrueColorGetAlpha(src);
2953 return ( ((gdAlphaMax - a1*a2/gdAlphaMax) << 24) +
2954 (gdAlphaOverlayColor( gdTrueColorGetRed(src), gdTrueColorGetRed(dst), gdRedMax ) << 16) +
2955 (gdAlphaOverlayColor( gdTrueColorGetGreen(src), gdTrueColorGetGreen(dst), gdGreenMax ) << 8) +
2956 (gdAlphaOverlayColor( gdTrueColorGetBlue(src), gdTrueColorGetBlue(dst), gdBlueMax ))
2957 );
2958 }
2959
gdAlphaOverlayColor(int src,int dst,int max)2960 static int gdAlphaOverlayColor (int src, int dst, int max )
2961 {
2962 /* this function implements the algorithm
2963 *
2964 * for dst[rgb] < 0.5,
2965 * c[rgb] = 2.src[rgb].dst[rgb]
2966 * and for dst[rgb] > 0.5,
2967 * c[rgb] = -2.src[rgb].dst[rgb] + 2.dst[rgb] + 2.src[rgb] - 1
2968 *
2969 */
2970
2971 dst = dst << 1;
2972 if( dst > max ) {
2973 /* in the "light" zone */
2974 return dst + (src << 1) - (dst * src / max) - max;
2975 } else {
2976 /* in the "dark" zone */
2977 return dst * src / max;
2978 }
2979 }
2980
gdImageSetClip(gdImagePtr im,int x1,int y1,int x2,int y2)2981 void gdImageSetClip (gdImagePtr im, int x1, int y1, int x2, int y2)
2982 {
2983 if (x1 < 0) {
2984 x1 = 0;
2985 }
2986 if (x1 >= im->sx) {
2987 x1 = im->sx - 1;
2988 }
2989 if (x2 < 0) {
2990 x2 = 0;
2991 }
2992 if (x2 >= im->sx) {
2993 x2 = im->sx - 1;
2994 }
2995 if (y1 < 0) {
2996 y1 = 0;
2997 }
2998 if (y1 >= im->sy) {
2999 y1 = im->sy - 1;
3000 }
3001 if (y2 < 0) {
3002 y2 = 0;
3003 }
3004 if (y2 >= im->sy) {
3005 y2 = im->sy - 1;
3006 }
3007 im->cx1 = x1;
3008 im->cy1 = y1;
3009 im->cx2 = x2;
3010 im->cy2 = y2;
3011 }
3012
gdImageGetClip(gdImagePtr im,int * x1P,int * y1P,int * x2P,int * y2P)3013 void gdImageGetClip (gdImagePtr im, int *x1P, int *y1P, int *x2P, int *y2P)
3014 {
3015 *x1P = im->cx1;
3016 *y1P = im->cy1;
3017 *x2P = im->cx2;
3018 *y2P = im->cy2;
3019 }
3020
3021 /* convert a palette image to true color */
gdImagePaletteToTrueColor(gdImagePtr src)3022 int gdImagePaletteToTrueColor(gdImagePtr src)
3023 {
3024 unsigned int y;
3025 unsigned int yy;
3026
3027 if (src == NULL) {
3028 return 0;
3029 }
3030
3031 if (src->trueColor == 1) {
3032 return 1;
3033 } else {
3034 unsigned int x;
3035 const unsigned int sy = gdImageSY(src);
3036 const unsigned int sx = gdImageSX(src);
3037
3038 src->tpixels = (int **) gdMalloc(sizeof(int *) * sy);
3039 if (src->tpixels == NULL) {
3040 return 0;
3041 }
3042
3043 for (y = 0; y < sy; y++) {
3044 const unsigned char *src_row = src->pixels[y];
3045 int * dst_row;
3046
3047 /* no need to calloc it, we overwrite all pxl anyway */
3048 src->tpixels[y] = (int *) gdMalloc(sx * sizeof(int));
3049 if (src->tpixels[y] == NULL) {
3050 goto clean_on_error;
3051 }
3052
3053 dst_row = src->tpixels[y];
3054 for (x = 0; x < sx; x++) {
3055 const unsigned char c = *(src_row + x);
3056 if (c == src->transparent) {
3057 *(dst_row + x) = gdTrueColorAlpha(0, 0, 0, 127);
3058 } else {
3059 *(dst_row + x) = gdTrueColorAlpha(src->red[c], src->green[c], src->blue[c], src->alpha[c]);
3060 }
3061 }
3062 }
3063 }
3064
3065 /* free old palette buffer (y is sy) */
3066 for (yy = 0; yy < y; yy++) {
3067 gdFree(src->pixels[yy]);
3068 }
3069 gdFree(src->pixels);
3070 src->trueColor = 1;
3071 src->pixels = NULL;
3072 src->alphaBlendingFlag = 0;
3073 src->saveAlphaFlag = 1;
3074
3075 if (src->transparent >= 0) {
3076 const unsigned char c = src->transparent;
3077 src->transparent = gdTrueColorAlpha(src->red[c], src->green[c], src->blue[c], src->alpha[c]);
3078 }
3079
3080 return 1;
3081
3082 clean_on_error:
3083 if (y > 0) {
3084
3085 for (yy = y; yy >= yy - 1; y--) {
3086 gdFree(src->tpixels[y]);
3087 }
3088 gdFree(src->tpixels);
3089 }
3090 return 0;
3091 }
3092
3093