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