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