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