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    }
1776    if (y >= im->sy) {
1777        y = im->sy - 1;
1778    }
1779
1780    for (i = x; i >= 0; i--) {
1781        if (gdImageGetPixel(im, i, y) == border) {
1782            break;
1783        }
1784        gdImageSetPixel(im, i, y, color);
1785        leftLimit = i;
1786    }
1787    if (leftLimit == -1) {
1788        im->alphaBlendingFlag = restoreAlphaBlending;
1789        return;
1790    }
1791    /* Seek right */
1792    rightLimit = x;
1793    for (i = (x + 1); i < im->sx; i++) {
1794        if (gdImageGetPixel(im, i, y) == border) {
1795            break;
1796        }
1797        gdImageSetPixel(im, i, y, color);
1798        rightLimit = i;
1799    }
1800    /* Look at lines above and below and start paints */
1801    /* Above */
1802    if (y > 0) {
1803        lastBorder = 1;
1804        for (i = leftLimit; i <= rightLimit; i++) {
1805            int c = gdImageGetPixel(im, i, y - 1);
1806            if (lastBorder) {
1807                if ((c != border) && (c != color)) {
1808                    gdImageFillToBorder(im, i, y - 1, border, color);
1809                    lastBorder = 0;
1810                }
1811            } else if ((c == border) || (c == color)) {
1812                lastBorder = 1;
1813            }
1814        }
1815    }
1816
1817    /* Below */
1818    if (y < ((im->sy) - 1)) {
1819        lastBorder = 1;
1820        for (i = leftLimit; i <= rightLimit; i++) {
1821            int c = gdImageGetPixel(im, i, y + 1);
1822
1823            if (lastBorder) {
1824                if ((c != border) && (c != color)) {
1825                    gdImageFillToBorder(im, i, y + 1, border, color);
1826                    lastBorder = 0;
1827                }
1828            } else if ((c == border) || (c == color)) {
1829                lastBorder = 1;
1830            }
1831        }
1832    }
1833    im->alphaBlendingFlag = restoreAlphaBlending;
1834}
1835
1836/*
1837 * set the pixel at (x,y) and its 4-connected neighbors
1838 * with the same pixel value to the new pixel value nc (new color).
1839 * A 4-connected neighbor:  pixel above, below, left, or right of a pixel.
1840 * ideas from comp.graphics discussions.
1841 * For tiled fill, the use of a flag buffer is mandatory. As the tile image can
1842 * contain the same color as the color to fill. To do not bloat normal filling
1843 * code I added a 2nd private function.
1844 */
1845
1846/* horizontal segment of scan line y */
1847struct seg {int y, xl, xr, dy;};
1848
1849/* max depth of stack */
1850#define FILL_MAX ((int)(im->sy*im->sx)/4)
1851#define FILL_PUSH(Y, XL, XR, DY) \
1852    if (sp<stack+FILL_MAX && Y+(DY)>=0 && Y+(DY)<wy2) \
1853    {sp->y = Y; sp->xl = XL; sp->xr = XR; sp->dy = DY; sp++;}
1854
1855#define FILL_POP(Y, XL, XR, DY) \
1856    {sp--; Y = sp->y+(DY = sp->dy); XL = sp->xl; XR = sp->xr;}
1857
1858static void _gdImageFillTiled(gdImagePtr im, int x, int y, int nc);
1859
1860void gdImageFill(gdImagePtr im, int x, int y, int nc)
1861{
1862    int l, x1, x2, dy;
1863    int oc;   /* old pixel value */
1864    int wx2,wy2;
1865
1866    int alphablending_bak;
1867
1868    /* stack of filled segments */
1869    /* struct seg stack[FILL_MAX],*sp = stack;; */
1870    struct seg *stack = NULL;
1871    struct seg *sp;
1872
1873    if (!im->trueColor && nc > (im->colorsTotal -1)) {
1874        return;
1875    }
1876
1877    alphablending_bak = im->alphaBlendingFlag;
1878    im->alphaBlendingFlag = 0;
1879
1880    if (nc==gdTiled){
1881        _gdImageFillTiled(im,x,y,nc);
1882        im->alphaBlendingFlag = alphablending_bak;
1883        return;
1884    }
1885
1886    wx2=im->sx;wy2=im->sy;
1887    oc = gdImageGetPixel(im, x, y);
1888    if (oc==nc || x<0 || x>wx2 || y<0 || y>wy2) {
1889        im->alphaBlendingFlag = alphablending_bak;
1890        return;
1891    }
1892
1893    /* Do not use the 4 neighbors implementation with
1894     * small images
1895     */
1896    if (im->sx < 4) {
1897        int ix = x, iy = y, c;
1898        do {
1899            do {
1900                c = gdImageGetPixel(im, ix, iy);
1901                if (c != oc) {
1902                    goto done;
1903                }
1904                gdImageSetPixel(im, ix, iy, nc);
1905            } while(ix++ < (im->sx -1));
1906            ix = x;
1907        } while(iy++ < (im->sy -1));
1908        goto done;
1909    }
1910
1911    stack = (struct seg *)safe_emalloc(sizeof(struct seg), ((int)(im->sy*im->sx)/4), 1);
1912    sp = stack;
1913
1914    /* required! */
1915    FILL_PUSH(y,x,x,1);
1916    /* seed segment (popped 1st) */
1917    FILL_PUSH(y+1, x, x, -1);
1918    while (sp>stack) {
1919        FILL_POP(y, x1, x2, dy);
1920
1921        for (x=x1; x>=0 && gdImageGetPixel(im,x, y)==oc; x--) {
1922            gdImageSetPixel(im,x, y, nc);
1923        }
1924        if (x>=x1) {
1925            goto skip;
1926        }
1927        l = x+1;
1928
1929                /* leak on left? */
1930        if (l<x1) {
1931            FILL_PUSH(y, l, x1-1, -dy);
1932        }
1933        x = x1+1;
1934        do {
1935            for (; x<=wx2 && gdImageGetPixel(im,x, y)==oc; x++) {
1936                gdImageSetPixel(im, x, y, nc);
1937            }
1938            FILL_PUSH(y, l, x-1, dy);
1939            /* leak on right? */
1940            if (x>x2+1) {
1941                FILL_PUSH(y, x2+1, x-1, -dy);
1942            }
1943skip:           for (x++; x<=x2 && (gdImageGetPixel(im, x, y)!=oc); x++);
1944
1945            l = x;
1946        } while (x<=x2);
1947    }
1948
1949    efree(stack);
1950
1951done:
1952    im->alphaBlendingFlag = alphablending_bak;
1953}
1954
1955static void _gdImageFillTiled(gdImagePtr im, int x, int y, int nc)
1956{
1957    int i, l, x1, x2, dy;
1958    int oc;   /* old pixel value */
1959    int wx2,wy2;
1960    /* stack of filled segments */
1961    struct seg *stack;
1962    struct seg *sp;
1963    char **pts;
1964
1965    if (!im->tile) {
1966        return;
1967    }
1968
1969    wx2=im->sx;wy2=im->sy;
1970
1971    nc =  gdImageTileGet(im,x,y);
1972
1973    pts = (char **) ecalloc(im->sy + 1, sizeof(char *));
1974    for (i = 0; i < im->sy + 1; i++) {
1975        pts[i] = (char *) ecalloc(im->sx + 1, sizeof(char));
1976    }
1977
1978    stack = (struct seg *)safe_emalloc(sizeof(struct seg), ((int)(im->sy*im->sx)/4), 1);
1979    sp = stack;
1980
1981    oc = gdImageGetPixel(im, x, y);
1982
1983    /* required! */
1984    FILL_PUSH(y,x,x,1);
1985    /* seed segment (popped 1st) */
1986    FILL_PUSH(y+1, x, x, -1);
1987    while (sp>stack) {
1988        FILL_POP(y, x1, x2, dy);
1989        for (x=x1; x>=0 && (!pts[y][x] && gdImageGetPixel(im,x,y)==oc); x--) {
1990            nc = gdImageTileGet(im,x,y);
1991            pts[y][x] = 1;
1992            gdImageSetPixel(im,x, y, nc);
1993        }
1994        if (x>=x1) {
1995            goto skip;
1996        }
1997        l = x+1;
1998
1999        /* leak on left? */
2000        if (l<x1) {
2001            FILL_PUSH(y, l, x1-1, -dy);
2002        }
2003        x = x1+1;
2004        do {
2005            for(; x<wx2 && (!pts[y][x] && gdImageGetPixel(im,x, y)==oc); x++) {
2006                nc = gdImageTileGet(im,x,y);
2007                pts[y][x] = 1;
2008                gdImageSetPixel(im, x, y, nc);
2009            }
2010            FILL_PUSH(y, l, x-1, dy);
2011            /* leak on right? */
2012            if (x>x2+1) {
2013                FILL_PUSH(y, x2+1, x-1, -dy);
2014            }
2015skip:       for(x++; x<=x2 && (pts[y][x] || gdImageGetPixel(im,x, y)!=oc); x++);
2016            l = x;
2017        } while (x<=x2);
2018    }
2019
2020    for(i = 0; i < im->sy + 1; i++) {
2021        efree(pts[i]);
2022    }
2023
2024    efree(pts);
2025    efree(stack);
2026}
2027
2028
2029
2030void gdImageRectangle (gdImagePtr im, int x1, int y1, int x2, int y2, int color)
2031{
2032    int x1h = x1, x1v = x1, y1h = y1, y1v = y1, x2h = x2, x2v = x2, y2h = y2, y2v = y2;
2033    int thick = im->thick;
2034    int t;
2035
2036    if (x1 == x2 && y1 == y2 && thick == 1) {
2037        gdImageSetPixel(im, x1, y1, color);
2038        return;
2039    }
2040
2041    if (y2 < y1) {
2042        t=y1;
2043        y1 = y2;
2044        y2 = t;
2045
2046        t = x1;
2047        x1 = x2;
2048        x2 = t;
2049    }
2050
2051    x1h = x1; x1v = x1; y1h = y1; y1v = y1; x2h = x2; x2v = x2; y2h = y2; y2v = y2;
2052    if (thick > 1) {
2053        int cx, cy, x1ul, y1ul, x2lr, y2lr;
2054        int half = thick >> 1;
2055
2056        x1ul = x1 - half;
2057        y1ul = y1 - half;
2058
2059        x2lr = x2 + half;
2060        y2lr = y2 + half;
2061
2062        cy = y1ul + thick;
2063        while (cy-- > y1ul) {
2064            cx = x1ul - 1;
2065            while (cx++ < x2lr) {
2066                gdImageSetPixel(im, cx, cy, color);
2067            }
2068        }
2069
2070        cy = y2lr - thick;
2071        while (cy++ < y2lr) {
2072            cx = x1ul - 1;
2073            while (cx++ < x2lr) {
2074                gdImageSetPixel(im, cx, cy, color);
2075            }
2076        }
2077
2078        cy = y1ul + thick - 1;
2079        while (cy++ < y2lr -thick) {
2080            cx = x1ul - 1;
2081            while (cx++ < x1ul + thick) {
2082                gdImageSetPixel(im, cx, cy, color);
2083            }
2084        }
2085
2086        cy = y1ul + thick - 1;
2087        while (cy++ < y2lr -thick) {
2088            cx = x2lr - thick - 1;
2089            while (cx++ < x2lr) {
2090                gdImageSetPixel(im, cx, cy, color);
2091            }
2092        }
2093
2094        return;
2095    } else {
2096        y1v = y1h + 1;
2097        y2v = y2h - 1;
2098        gdImageLine(im, x1h, y1h, x2h, y1h, color);
2099        gdImageLine(im, x1h, y2h, x2h, y2h, color);
2100        gdImageLine(im, x1v, y1v, x1v, y2v, color);
2101        gdImageLine(im, x2v, y1v, x2v, y2v, color);
2102    }
2103}
2104
2105void gdImageFilledRectangle (gdImagePtr im, int x1, int y1, int x2, int y2, int color)
2106{
2107    int x, y;
2108
2109
2110    if (x1 == x2 && y1 == y2) {
2111        gdImageSetPixel(im, x1, y1, color);
2112        return;
2113    }
2114
2115    if (x1 > x2) {
2116        x = x1;
2117        x1 = x2;
2118        x2 = x;
2119    }
2120
2121    if (y1 > y2) {
2122        y = y1;
2123        y1 = y2;
2124        y2 = y;
2125    }
2126
2127    if (x1 < 0) {
2128        x1 = 0;
2129    }
2130
2131    if (x2 >= gdImageSX(im)) {
2132        x2 = gdImageSX(im) - 1;
2133    }
2134
2135    if (y1 < 0) {
2136        y1 = 0;
2137    }
2138
2139    if (y2 >= gdImageSY(im)) {
2140        y2 = gdImageSY(im) - 1;
2141    }
2142
2143    for (y = y1; (y <= y2); y++) {
2144        for (x = x1; (x <= x2); x++) {
2145            gdImageSetPixel (im, x, y, color);
2146        }
2147    }
2148}
2149
2150void gdImageCopy (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int w, int h)
2151{
2152    int c;
2153    int x, y;
2154    int tox, toy;
2155    int i;
2156    int colorMap[gdMaxColors];
2157
2158    if (dst->trueColor) {
2159        /* 2.0: much easier when the destination is truecolor. */
2160        /* 2.0.10: needs a transparent-index check that is still valid if
2161         * the source is not truecolor. Thanks to Frank Warmerdam.
2162         */
2163
2164        if (src->trueColor) {
2165            for (y = 0; (y < h); y++) {
2166                for (x = 0; (x < w); x++) {
2167                    int c = gdImageGetTrueColorPixel (src, srcX + x, srcY + y);
2168                    gdImageSetPixel (dst, dstX + x, dstY + y, c);
2169                }
2170            }
2171        } else {
2172            /* source is palette based */
2173            for (y = 0; (y < h); y++) {
2174                for (x = 0; (x < w); x++) {
2175                    int c = gdImageGetPixel (src, srcX + x, srcY + y);
2176                    if (c != src->transparent) {
2177                        gdImageSetPixel(dst, dstX + x, dstY + y, gdTrueColorAlpha(src->red[c], src->green[c], src->blue[c], src->alpha[c]));
2178                    }
2179                }
2180            }
2181        }
2182        return;
2183    }
2184
2185    /* Destination is palette based */
2186    if (src->trueColor) { /* But source is truecolor (Ouch!) */
2187        toy = dstY;
2188        for (y = srcY; (y < (srcY + h)); y++) {
2189            tox = dstX;
2190            for (x = srcX; x < (srcX + w); x++) {
2191                int nc;
2192                c = gdImageGetPixel (src, x, y);
2193
2194                /* Get best match possible. */
2195                nc = gdImageColorResolveAlpha(dst, gdTrueColorGetRed(c), gdTrueColorGetGreen(c), gdTrueColorGetBlue(c), gdTrueColorGetAlpha(c));
2196
2197                gdImageSetPixel(dst, tox, toy, nc);
2198                tox++;
2199            }
2200            toy++;
2201        }
2202        return;
2203    }
2204
2205    /* Palette based to palette based */
2206    for (i = 0; i < gdMaxColors; i++) {
2207        colorMap[i] = (-1);
2208    }
2209    toy = dstY;
2210    for (y = srcY; y < (srcY + h); y++) {
2211        tox = dstX;
2212        for (x = srcX; x < (srcX + w); x++) {
2213            int nc;
2214            int mapTo;
2215            c = gdImageGetPixel (src, x, y);
2216            /* Added 7/24/95: support transparent copies */
2217            if (gdImageGetTransparent (src) == c) {
2218                tox++;
2219                continue;
2220            }
2221            /* Have we established a mapping for this color? */
2222            if (src->trueColor) {
2223                /* 2.05: remap to the palette available in the destination image. This is slow and
2224                 * works badly, but it beats crashing! Thanks to Padhrig McCarthy.
2225                 */
2226                mapTo = gdImageColorResolveAlpha (dst, gdTrueColorGetRed (c), gdTrueColorGetGreen (c), gdTrueColorGetBlue (c), gdTrueColorGetAlpha (c));
2227            } else if (colorMap[c] == (-1)) {
2228                /* If it's the same image, mapping is trivial */
2229                if (dst == src) {
2230                    nc = c;
2231                } else {
2232                    /* Get best match possible. This function never returns error. */
2233                    nc = gdImageColorResolveAlpha (dst, src->red[c], src->green[c], src->blue[c], src->alpha[c]);
2234                }
2235                colorMap[c] = nc;
2236                mapTo = colorMap[c];
2237            } else {
2238                mapTo = colorMap[c];
2239            }
2240            gdImageSetPixel (dst, tox, toy, mapTo);
2241            tox++;
2242        }
2243        toy++;
2244    }
2245}
2246
2247/* This function is a substitute for real alpha channel operations,
2248   so it doesn't pay attention to the alpha channel. */
2249void gdImageCopyMerge (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int w, int h, int pct)
2250{
2251    int c, dc;
2252    int x, y;
2253    int tox, toy;
2254    int ncR, ncG, ncB;
2255    toy = dstY;
2256
2257    for (y = srcY; y < (srcY + h); y++) {
2258        tox = dstX;
2259        for (x = srcX; x < (srcX + w); x++) {
2260            int nc;
2261            c = gdImageGetPixel(src, x, y);
2262            /* Added 7/24/95: support transparent copies */
2263            if (gdImageGetTransparent(src) == c) {
2264                tox++;
2265                continue;
2266            }
2267            /* If it's the same image, mapping is trivial */
2268            if (dst == src) {
2269                nc = c;
2270            } else {
2271                dc = gdImageGetPixel(dst, tox, toy);
2272
2273                ncR = (int)(gdImageRed (src, c) * (pct / 100.0) + gdImageRed (dst, dc) * ((100 - pct) / 100.0));
2274                ncG = (int)(gdImageGreen (src, c) * (pct / 100.0) + gdImageGreen (dst, dc) * ((100 - pct) / 100.0));
2275                ncB = (int)(gdImageBlue (src, c) * (pct / 100.0) + gdImageBlue (dst, dc) * ((100 - pct) / 100.0));
2276
2277                /* Find a reasonable color */
2278                nc = gdImageColorResolve (dst, ncR, ncG, ncB);
2279            }
2280            gdImageSetPixel (dst, tox, toy, nc);
2281            tox++;
2282        }
2283        toy++;
2284    }
2285}
2286
2287/* This function is a substitute for real alpha channel operations,
2288   so it doesn't pay attention to the alpha channel. */
2289void gdImageCopyMergeGray (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int w, int h, int pct)
2290{
2291    int c, dc;
2292    int x, y;
2293    int tox, toy;
2294    int ncR, ncG, ncB;
2295    float g;
2296    toy = dstY;
2297
2298    for (y = srcY; (y < (srcY + h)); y++) {
2299        tox = dstX;
2300        for (x = srcX; (x < (srcX + w)); x++) {
2301            int nc;
2302            c = gdImageGetPixel (src, x, y);
2303            /* Added 7/24/95: support transparent copies */
2304            if (gdImageGetTransparent(src) == c) {
2305                tox++;
2306                continue;
2307            }
2308
2309            /*
2310             * If it's the same image, mapping is NOT trivial since we
2311             * merge with greyscale target, but if pct is 100, the grey
2312             * value is not used, so it becomes trivial. pjw 2.0.12.
2313             */
2314            if (dst == src && pct == 100) {
2315                nc = c;
2316            } else {
2317                dc = gdImageGetPixel(dst, tox, toy);
2318                g = (0.29900f * gdImageRed(dst, dc)) + (0.58700f * gdImageGreen(dst, dc)) + (0.11400f * gdImageBlue(dst, dc));
2319
2320                ncR = (int)(gdImageRed (src, c) * (pct / 100.0f) + g * ((100 - pct) / 100.0));
2321                ncG = (int)(gdImageGreen (src, c) * (pct / 100.0f) + g * ((100 - pct) / 100.0));
2322                ncB = (int)(gdImageBlue (src, c) * (pct / 100.0f) + g * ((100 - pct) / 100.0));
2323
2324
2325                /* First look for an exact match */
2326                nc = gdImageColorExact(dst, ncR, ncG, ncB);
2327                if (nc == (-1)) {
2328                    /* No, so try to allocate it */
2329                    nc = gdImageColorAllocate(dst, ncR, ncG, ncB);
2330                    /* If we're out of colors, go for the closest color */
2331                    if (nc == (-1)) {
2332                        nc = gdImageColorClosest(dst, ncR, ncG, ncB);
2333                    }
2334                }
2335            }
2336            gdImageSetPixel(dst, tox, toy, nc);
2337            tox++;
2338        }
2339        toy++;
2340    }
2341}
2342
2343void gdImageCopyResized (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int dstW, int dstH, int srcW, int srcH)
2344{
2345    int c;
2346    int x, y;
2347    int tox, toy;
2348    int ydest;
2349    int i;
2350    int colorMap[gdMaxColors];
2351    /* Stretch vectors */
2352    int *stx, *sty;
2353
2354    if (overflow2(sizeof(int), srcW)) {
2355        return;
2356    }
2357    if (overflow2(sizeof(int), srcH)) {
2358        return;
2359    }
2360
2361    stx = (int *) gdMalloc (sizeof (int) * srcW);
2362    sty = (int *) gdMalloc (sizeof (int) * srcH);
2363
2364    /* Fixed by Mao Morimoto 2.0.16 */
2365    for (i = 0; (i < srcW); i++) {
2366        stx[i] = dstW * (i+1) / srcW - dstW * i / srcW ;
2367    }
2368    for (i = 0; (i < srcH); i++) {
2369        sty[i] = dstH * (i+1) / srcH - dstH * i / srcH ;
2370    }
2371    for (i = 0; (i < gdMaxColors); i++) {
2372        colorMap[i] = (-1);
2373    }
2374    toy = dstY;
2375    for (y = srcY; (y < (srcY + srcH)); y++) {
2376        for (ydest = 0; (ydest < sty[y - srcY]); ydest++) {
2377            tox = dstX;
2378            for (x = srcX; (x < (srcX + srcW)); x++) {
2379                int nc = 0;
2380                int mapTo;
2381                if (!stx[x - srcX]) {
2382                    continue;
2383                }
2384                if (dst->trueColor) {
2385                    /* 2.0.9: Thorben Kundinger: Maybe the source image is not a truecolor image */
2386                    if (!src->trueColor) {
2387                        int tmp = gdImageGetPixel (src, x, y);
2388                        mapTo = gdImageGetTrueColorPixel (src, x, y);
2389                        if (gdImageGetTransparent (src) == tmp) {
2390                            /* 2.0.21, TK: not tox++ */
2391                            tox += stx[x - srcX];
2392                            continue;
2393                        }
2394                    } else {
2395                        /* TK: old code follows */
2396                        mapTo = gdImageGetTrueColorPixel (src, x, y);
2397                        /* Added 7/24/95: support transparent copies */
2398                        if (gdImageGetTransparent (src) == mapTo) {
2399                            /* 2.0.21, TK: not tox++ */
2400                            tox += stx[x - srcX];
2401                            continue;
2402                        }
2403                    }
2404                } else {
2405                    c = gdImageGetPixel (src, x, y);
2406                    /* Added 7/24/95: support transparent copies */
2407                    if (gdImageGetTransparent (src) == c) {
2408                          tox += stx[x - srcX];
2409                          continue;
2410                    }
2411                    if (src->trueColor) {
2412                          /* Remap to the palette available in the destination image. This is slow and works badly. */
2413                          mapTo = gdImageColorResolveAlpha(dst, gdTrueColorGetRed(c),
2414                                                gdTrueColorGetGreen(c),
2415                                                gdTrueColorGetBlue(c),
2416                                                gdTrueColorGetAlpha (c));
2417                    } else {
2418                        /* Have we established a mapping for this color? */
2419                        if (colorMap[c] == (-1)) {
2420                            /* If it's the same image, mapping is trivial */
2421                            if (dst == src) {
2422                                nc = c;
2423                            } else {
2424                                /* Find or create the best match */
2425                                /* 2.0.5: can't use gdTrueColorGetRed, etc with palette */
2426                                nc = gdImageColorResolveAlpha(dst, gdImageRed(src, c),
2427                                                   gdImageGreen(src, c),
2428                                                   gdImageBlue(src, c),
2429                                                   gdImageAlpha(src, c));
2430                            }
2431                            colorMap[c] = nc;
2432                        }
2433                        mapTo = colorMap[c];
2434                    }
2435                }
2436                for (i = 0; (i < stx[x - srcX]); i++) {
2437                    gdImageSetPixel (dst, tox, toy, mapTo);
2438                    tox++;
2439                }
2440            }
2441            toy++;
2442        }
2443    }
2444    gdFree (stx);
2445    gdFree (sty);
2446}
2447
2448/* When gd 1.x was first created, floating point was to be avoided.
2449   These days it is often faster than table lookups or integer
2450   arithmetic. The routine below is shamelessly, gloriously
2451   floating point. TBB */
2452
2453void gdImageCopyResampled (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int dstW, int dstH, int srcW, int srcH)
2454{
2455    int x, y;
2456    double sy1, sy2, sx1, sx2;
2457
2458    if (!dst->trueColor) {
2459        gdImageCopyResized (dst, src, dstX, dstY, srcX, srcY, dstW, dstH, srcW, srcH);
2460        return;
2461    }
2462    for (y = dstY; (y < dstY + dstH); y++) {
2463        sy1 = ((double) y - (double) dstY) * (double) srcH / (double) dstH;
2464        sy2 = ((double) (y + 1) - (double) dstY) * (double) srcH / (double) dstH;
2465        for (x = dstX; (x < dstX + dstW); x++) {
2466            double sx, sy;
2467            double spixels = 0;
2468            double red = 0.0, green = 0.0, blue = 0.0, alpha = 0.0;
2469            double alpha_factor, alpha_sum = 0.0, contrib_sum = 0.0;
2470            sx1 = ((double) x - (double) dstX) * (double) srcW / dstW;
2471            sx2 = ((double) (x + 1) - (double) dstX) * (double) srcW / dstW;
2472            sy = sy1;
2473            do {
2474                double yportion;
2475                if (floor_cast(sy) == floor_cast(sy1)) {
2476                    yportion = 1.0f - (sy - floor_cast(sy));
2477                    if (yportion > sy2 - sy1) {
2478                        yportion = sy2 - sy1;
2479                    }
2480                    sy = floor_cast(sy);
2481                } else if (sy == floorf(sy2)) {
2482                    yportion = sy2 - floor_cast(sy2);
2483                } else {
2484                    yportion = 1.0f;
2485                }
2486                sx = sx1;
2487                do {
2488                    double xportion;
2489                    double pcontribution;
2490                    int p;
2491                    if (floorf(sx) == floor_cast(sx1)) {
2492                        xportion = 1.0f - (sx - floor_cast(sx));
2493                        if (xportion > sx2 - sx1) {
2494                            xportion = sx2 - sx1;
2495                        }
2496                        sx = floor_cast(sx);
2497                    } else if (sx == floorf(sx2)) {
2498                        xportion = sx2 - floor_cast(sx2);
2499                    } else {
2500                        xportion = 1.0f;
2501                    }
2502                    pcontribution = xportion * yportion;
2503                    p = gdImageGetTrueColorPixel(src, (int) sx + srcX, (int) sy + srcY);
2504
2505                    alpha_factor = ((gdAlphaMax - gdTrueColorGetAlpha(p))) * pcontribution;
2506                    red += gdTrueColorGetRed (p) * alpha_factor;
2507                    green += gdTrueColorGetGreen (p) * alpha_factor;
2508                    blue += gdTrueColorGetBlue (p) * alpha_factor;
2509                    alpha += gdTrueColorGetAlpha (p) * pcontribution;
2510                    alpha_sum += alpha_factor;
2511                    contrib_sum += pcontribution;
2512                    spixels += xportion * yportion;
2513                    sx += 1.0f;
2514                }
2515                while (sx < sx2);
2516
2517                sy += 1.0f;
2518            }
2519
2520            while (sy < sy2);
2521
2522            if (spixels != 0.0f) {
2523                red /= spixels;
2524                green /= spixels;
2525                blue /= spixels;
2526                alpha /= spixels;
2527                alpha += 0.5;
2528            }
2529            if ( alpha_sum != 0.0f) {
2530                if( contrib_sum != 0.0f) {
2531                    alpha_sum /= contrib_sum;
2532                }
2533                red /= alpha_sum;
2534                green /= alpha_sum;
2535                blue /= alpha_sum;
2536            }
2537            /* Clamping to allow for rounding errors above */
2538            if (red > 255.0f) {
2539                red = 255.0f;
2540            }
2541            if (green > 255.0f) {
2542                green = 255.0f;
2543            }
2544            if (blue > 255.0f) {
2545                blue = 255.0f;
2546            }
2547            if (alpha > gdAlphaMax) {
2548                alpha = gdAlphaMax;
2549            }
2550            gdImageSetPixel(dst, x, y, gdTrueColorAlpha ((int) red, (int) green, (int) blue, (int) alpha));
2551        }
2552    }
2553}
2554
2555void gdImagePolygon (gdImagePtr im, gdPointPtr p, int n, int c)
2556{
2557    int i;
2558    int lx, ly;
2559    typedef void (*image_line)(gdImagePtr im, int x1, int y1, int x2, int y2, int color);
2560    image_line draw_line;
2561
2562    if (n <= 0) {
2563        return;
2564    }
2565
2566    /* Let it be known that we are drawing a polygon so that the opacity
2567     * mask doesn't get cleared after each line.
2568     */
2569    if (c == gdAntiAliased) {
2570        im->AA_polygon = 1;
2571    }
2572
2573    if ( im->antialias) {
2574        draw_line = gdImageAALine;
2575    } else {
2576        draw_line = gdImageLine;
2577    }
2578    lx = p->x;
2579    ly = p->y;
2580    draw_line(im, lx, ly, p[n - 1].x, p[n - 1].y, c);
2581    for (i = 1; i < n; i++) {
2582        p++;
2583        draw_line(im, lx, ly, p->x, p->y, c);
2584        lx = p->x;
2585        ly = p->y;
2586    }
2587
2588    if (c == gdAntiAliased) {
2589        im->AA_polygon = 0;
2590        gdImageAABlend(im);
2591    }
2592}
2593
2594int gdCompareInt (const void *a, const void *b);
2595
2596/* THANKS to Kirsten Schulz for the polygon fixes! */
2597
2598/* The intersection finding technique of this code could be improved
2599 * by remembering the previous intertersection, and by using the slope.
2600 * That could help to adjust intersections  to produce a nice
2601 * interior_extrema.
2602 */
2603
2604void gdImageFilledPolygon (gdImagePtr im, gdPointPtr p, int n, int c)
2605{
2606    int i;
2607    int y;
2608    int miny, maxy, pmaxy;
2609    int x1, y1;
2610    int x2, y2;
2611    int ind1, ind2;
2612    int ints;
2613    int fill_color;
2614
2615    if (n <= 0) {
2616        return;
2617    }
2618
2619    if (overflow2(sizeof(int), n)) {
2620        return;
2621    }
2622
2623    if (c == gdAntiAliased) {
2624        fill_color = im->AA_color;
2625    } else {
2626        fill_color = c;
2627    }
2628
2629    if (!im->polyAllocated) {
2630        im->polyInts = (int *) gdMalloc(sizeof(int) * n);
2631        im->polyAllocated = n;
2632    }
2633    if (im->polyAllocated < n) {
2634        while (im->polyAllocated < n) {
2635            im->polyAllocated *= 2;
2636        }
2637        if (overflow2(sizeof(int), im->polyAllocated)) {
2638            return;
2639        }
2640        im->polyInts = (int *) gdRealloc(im->polyInts, sizeof(int) * im->polyAllocated);
2641    }
2642    miny = p[0].y;
2643    maxy = p[0].y;
2644    for (i = 1; i < n; i++) {
2645        if (p[i].y < miny) {
2646            miny = p[i].y;
2647        }
2648        if (p[i].y > maxy) {
2649            maxy = p[i].y;
2650        }
2651    }
2652    pmaxy = maxy;
2653    /* 2.0.16: Optimization by Ilia Chipitsine -- don't waste time offscreen */
2654    if (miny < 0) {
2655        miny = 0;
2656    }
2657    if (maxy >= gdImageSY(im)) {
2658        maxy = gdImageSY(im) - 1;
2659    }
2660
2661    /* Fix in 1.3: count a vertex only once */
2662    for (y = miny; y <= maxy; y++) {
2663        /*1.4           int interLast = 0; */
2664        /*              int dirLast = 0; */
2665        /*              int interFirst = 1; */
2666        ints = 0;
2667        for (i = 0; i < n; i++) {
2668            if (!i) {
2669                ind1 = n - 1;
2670                ind2 = 0;
2671            } else {
2672                ind1 = i - 1;
2673                ind2 = i;
2674            }
2675            y1 = p[ind1].y;
2676            y2 = p[ind2].y;
2677            if (y1 < y2) {
2678                x1 = p[ind1].x;
2679                x2 = p[ind2].x;
2680            } else if (y1 > y2) {
2681                y2 = p[ind1].y;
2682                y1 = p[ind2].y;
2683                x2 = p[ind1].x;
2684                x1 = p[ind2].x;
2685            } else {
2686                continue;
2687            }
2688            /* Do the following math as float intermediately, and round to ensure
2689             * that Polygon and FilledPolygon for the same set of points have the
2690             * same footprint.
2691             */
2692            if (y >= y1 && y < y2) {
2693                im->polyInts[ints++] = (float) ((y - y1) * (x2 - x1)) / (float) (y2 - y1) + 0.5 + x1;
2694            } else if (y == pmaxy && y == y2) {
2695                im->polyInts[ints++] = x2;
2696            }
2697        }
2698        qsort(im->polyInts, ints, sizeof(int), gdCompareInt);
2699
2700        for (i = 0; i < ints - 1; i += 2) {
2701            gdImageLine(im, im->polyInts[i], y, im->polyInts[i + 1], y, fill_color);
2702        }
2703    }
2704
2705    /* If we are drawing this AA, then redraw the border with AA lines. */
2706    if (c == gdAntiAliased) {
2707        gdImagePolygon(im, p, n, c);
2708    }
2709}
2710
2711int gdCompareInt (const void *a, const void *b)
2712{
2713    return (*(const int *) a) - (*(const int *) b);
2714}
2715
2716void gdImageSetStyle (gdImagePtr im, int *style, int noOfPixels)
2717{
2718    if (im->style) {
2719        gdFree(im->style);
2720    }
2721    im->style = (int *) gdMalloc(sizeof(int) * noOfPixels);
2722    memcpy(im->style, style, sizeof(int) * noOfPixels);
2723    im->styleLength = noOfPixels;
2724    im->stylePos = 0;
2725}
2726
2727void gdImageSetThickness (gdImagePtr im, int thickness)
2728{
2729    im->thick = thickness;
2730}
2731
2732void gdImageSetBrush (gdImagePtr im, gdImagePtr brush)
2733{
2734    int i;
2735    im->brush = brush;
2736    if (!im->trueColor && !im->brush->trueColor) {
2737        for (i = 0; i < gdImageColorsTotal(brush); i++) {
2738            int index;
2739            index = gdImageColorResolveAlpha(im, gdImageRed(brush, i), gdImageGreen(brush, i), gdImageBlue(brush, i), gdImageAlpha(brush, i));
2740            im->brushColorMap[i] = index;
2741        }
2742    }
2743}
2744
2745void gdImageSetTile (gdImagePtr im, gdImagePtr tile)
2746{
2747    int i;
2748    im->tile = tile;
2749    if (!im->trueColor && !im->tile->trueColor) {
2750        for (i = 0; i < gdImageColorsTotal(tile); i++) {
2751            int index;
2752            index = gdImageColorResolveAlpha(im, gdImageRed(tile, i), gdImageGreen(tile, i), gdImageBlue(tile, i), gdImageAlpha(tile, i));
2753            im->tileColorMap[i] = index;
2754        }
2755    }
2756}
2757
2758void gdImageSetAntiAliased (gdImagePtr im, int c)
2759{
2760    im->AA = 1;
2761    im->AA_color = c;
2762    im->AA_dont_blend = -1;
2763}
2764
2765void gdImageSetAntiAliasedDontBlend (gdImagePtr im, int c, int dont_blend)
2766{
2767    im->AA = 1;
2768    im->AA_color = c;
2769    im->AA_dont_blend = dont_blend;
2770}
2771
2772
2773void gdImageInterlace (gdImagePtr im, int interlaceArg)
2774{
2775    im->interlace = interlaceArg;
2776}
2777
2778int gdImageCompare (gdImagePtr im1, gdImagePtr im2)
2779{
2780    int x, y;
2781    int p1, p2;
2782    int cmpStatus = 0;
2783    int sx, sy;
2784
2785    if (im1->interlace != im2->interlace) {
2786        cmpStatus |= GD_CMP_INTERLACE;
2787    }
2788
2789    if (im1->transparent != im2->transparent) {
2790        cmpStatus |= GD_CMP_TRANSPARENT;
2791    }
2792
2793    if (im1->trueColor != im2->trueColor) {
2794        cmpStatus |= GD_CMP_TRUECOLOR;
2795    }
2796
2797    sx = im1->sx;
2798    if (im1->sx != im2->sx) {
2799        cmpStatus |= GD_CMP_SIZE_X + GD_CMP_IMAGE;
2800        if (im2->sx < im1->sx) {
2801            sx = im2->sx;
2802        }
2803    }
2804
2805    sy = im1->sy;
2806    if (im1->sy != im2->sy) {
2807        cmpStatus |= GD_CMP_SIZE_Y + GD_CMP_IMAGE;
2808        if (im2->sy < im1->sy) {
2809            sy = im2->sy;
2810        }
2811    }
2812
2813    if (im1->colorsTotal != im2->colorsTotal) {
2814        cmpStatus |= GD_CMP_NUM_COLORS;
2815    }
2816
2817    for (y = 0; y < sy; y++) {
2818        for (x = 0; x < sx; x++) {
2819            p1 = im1->trueColor ? gdImageTrueColorPixel(im1, x, y) : gdImagePalettePixel(im1, x, y);
2820            p2 = im2->trueColor ? gdImageTrueColorPixel(im2, x, y) : gdImagePalettePixel(im2, x, y);
2821
2822            if (gdImageRed(im1, p1) != gdImageRed(im2, p2)) {
2823                cmpStatus |= GD_CMP_COLOR + GD_CMP_IMAGE;
2824                break;
2825            }
2826            if (gdImageGreen(im1, p1) != gdImageGreen(im2, p2)) {
2827                cmpStatus |= GD_CMP_COLOR + GD_CMP_IMAGE;
2828                break;
2829            }
2830            if (gdImageBlue(im1, p1) != gdImageBlue(im2, p2)) {
2831                cmpStatus |= GD_CMP_COLOR + GD_CMP_IMAGE;
2832                break;
2833            }
2834#if 0
2835            /* Soon we'll add alpha channel to palettes */
2836            if (gdImageAlpha(im1, p1) != gdImageAlpha(im2, p2)) {
2837                cmpStatus |= GD_CMP_COLOR + GD_CMP_IMAGE;
2838                break;
2839            }
2840#endif
2841        }
2842        if (cmpStatus & GD_CMP_COLOR) {
2843            break;
2844        }
2845    }
2846
2847    return cmpStatus;
2848}
2849
2850int
2851gdAlphaBlendOld (int dst, int src)
2852{
2853    /* 2.0.12: TBB: alpha in the destination should be a
2854     * component of the result. Thanks to Frank Warmerdam for
2855     * pointing out the issue.
2856     */
2857    return ((((gdTrueColorGetAlpha (src) *
2858         gdTrueColorGetAlpha (dst)) / gdAlphaMax) << 24) +
2859      ((((gdAlphaTransparent - gdTrueColorGetAlpha (src)) *
2860         gdTrueColorGetRed (src) / gdAlphaMax) +
2861        (gdTrueColorGetAlpha (src) *
2862         gdTrueColorGetRed (dst)) / gdAlphaMax) << 16) +
2863      ((((gdAlphaTransparent - gdTrueColorGetAlpha (src)) *
2864         gdTrueColorGetGreen (src) / gdAlphaMax) +
2865        (gdTrueColorGetAlpha (src) *
2866         gdTrueColorGetGreen (dst)) / gdAlphaMax) << 8) +
2867      (((gdAlphaTransparent - gdTrueColorGetAlpha (src)) *
2868        gdTrueColorGetBlue (src) / gdAlphaMax) +
2869       (gdTrueColorGetAlpha (src) *
2870        gdTrueColorGetBlue (dst)) / gdAlphaMax));
2871}
2872
2873int gdAlphaBlend (int dst, int src) {
2874    int src_alpha = gdTrueColorGetAlpha(src);
2875    int dst_alpha, alpha, red, green, blue;
2876    int src_weight, dst_weight, tot_weight;
2877
2878/* -------------------------------------------------------------------- */
2879/*      Simple cases we want to handle fast.                            */
2880/* -------------------------------------------------------------------- */
2881    if( src_alpha == gdAlphaOpaque )
2882        return src;
2883
2884    dst_alpha = gdTrueColorGetAlpha(dst);
2885    if( src_alpha == gdAlphaTransparent )
2886        return dst;
2887    if( dst_alpha == gdAlphaTransparent )
2888        return src;
2889
2890/* -------------------------------------------------------------------- */
2891/*      What will the source and destination alphas be?  Note that      */
2892/*      the destination weighting is substantially reduced as the       */
2893/*      overlay becomes quite opaque.                                   */
2894/* -------------------------------------------------------------------- */
2895    src_weight = gdAlphaTransparent - src_alpha;
2896    dst_weight = (gdAlphaTransparent - dst_alpha) * src_alpha / gdAlphaMax;
2897    tot_weight = src_weight + dst_weight;
2898
2899/* -------------------------------------------------------------------- */
2900/*      What red, green and blue result values will we use?             */
2901/* -------------------------------------------------------------------- */
2902    alpha = src_alpha * dst_alpha / gdAlphaMax;
2903
2904    red = (gdTrueColorGetRed(src) * src_weight
2905           + gdTrueColorGetRed(dst) * dst_weight) / tot_weight;
2906    green = (gdTrueColorGetGreen(src) * src_weight
2907           + gdTrueColorGetGreen(dst) * dst_weight) / tot_weight;
2908    blue = (gdTrueColorGetBlue(src) * src_weight
2909           + gdTrueColorGetBlue(dst) * dst_weight) / tot_weight;
2910
2911/* -------------------------------------------------------------------- */
2912/*      Return merged result.                                           */
2913/* -------------------------------------------------------------------- */
2914    return ((alpha << 24) + (red << 16) + (green << 8) + blue);
2915
2916}
2917
2918void gdImageAlphaBlending (gdImagePtr im, int alphaBlendingArg)
2919{
2920    im->alphaBlendingFlag = alphaBlendingArg;
2921}
2922
2923void gdImageAntialias (gdImagePtr im, int antialias)
2924{
2925    if (im->trueColor){
2926        im->antialias = antialias;
2927    }
2928}
2929
2930void gdImageSaveAlpha (gdImagePtr im, int saveAlphaArg)
2931{
2932    im->saveAlphaFlag = saveAlphaArg;
2933}
2934
2935static int gdLayerOverlay (int dst, int src)
2936{
2937    int a1, a2;
2938    a1 = gdAlphaMax - gdTrueColorGetAlpha(dst);
2939    a2 = gdAlphaMax - gdTrueColorGetAlpha(src);
2940    return ( ((gdAlphaMax - a1*a2/gdAlphaMax) << 24) +
2941        (gdAlphaOverlayColor( gdTrueColorGetRed(src), gdTrueColorGetRed(dst), gdRedMax ) << 16) +
2942        (gdAlphaOverlayColor( gdTrueColorGetGreen(src), gdTrueColorGetGreen(dst), gdGreenMax ) << 8) +
2943        (gdAlphaOverlayColor( gdTrueColorGetBlue(src), gdTrueColorGetBlue(dst), gdBlueMax ))
2944        );
2945}
2946
2947static int gdAlphaOverlayColor (int src, int dst, int max )
2948{
2949    /* this function implements the algorithm
2950     *
2951     * for dst[rgb] < 0.5,
2952     *   c[rgb] = 2.src[rgb].dst[rgb]
2953     * and for dst[rgb] > 0.5,
2954     *   c[rgb] = -2.src[rgb].dst[rgb] + 2.dst[rgb] + 2.src[rgb] - 1
2955     *
2956     */
2957
2958    dst = dst << 1;
2959    if( dst > max ) {
2960        /* in the "light" zone */
2961        return dst + (src << 1) - (dst * src / max) - max;
2962    } else {
2963        /* in the "dark" zone */
2964        return dst * src / max;
2965    }
2966}
2967
2968void gdImageSetClip (gdImagePtr im, int x1, int y1, int x2, int y2)
2969{
2970    if (x1 < 0) {
2971        x1 = 0;
2972    }
2973    if (x1 >= im->sx) {
2974        x1 = im->sx - 1;
2975    }
2976    if (x2 < 0) {
2977        x2 = 0;
2978    }
2979    if (x2 >= im->sx) {
2980        x2 = im->sx - 1;
2981    }
2982    if (y1 < 0) {
2983        y1 = 0;
2984    }
2985    if (y1 >= im->sy) {
2986        y1 = im->sy - 1;
2987    }
2988    if (y2 < 0) {
2989        y2 = 0;
2990    }
2991    if (y2 >= im->sy) {
2992        y2 = im->sy - 1;
2993    }
2994    im->cx1 = x1;
2995    im->cy1 = y1;
2996    im->cx2 = x2;
2997    im->cy2 = y2;
2998}
2999
3000void gdImageGetClip (gdImagePtr im, int *x1P, int *y1P, int *x2P, int *y2P)
3001{
3002    *x1P = im->cx1;
3003    *y1P = im->cy1;
3004    *x2P = im->cx2;
3005    *y2P = im->cy2;
3006}
3007
3008/* convert a palette image to true color */
3009int gdImagePaletteToTrueColor(gdImagePtr src)
3010{
3011    unsigned int y;
3012    unsigned int yy;
3013
3014    if (src == NULL) {
3015        return 0;
3016    }
3017
3018    if (src->trueColor == 1) {
3019        return 1;
3020    } else {
3021        unsigned int x;
3022        const unsigned int sy = gdImageSY(src);
3023        const unsigned int sx = gdImageSX(src);
3024
3025        src->tpixels = (int **) gdMalloc(sizeof(int *) * sy);
3026        if (src->tpixels == NULL) {
3027            return 0;
3028        }
3029
3030        for (y = 0; y < sy; y++) {
3031            const unsigned char *src_row = src->pixels[y];
3032            int * dst_row;
3033
3034            /* no need to calloc it, we overwrite all pxl anyway */
3035            src->tpixels[y] = (int *) gdMalloc(sx * sizeof(int));
3036            if (src->tpixels[y] == NULL) {
3037                goto clean_on_error;
3038            }
3039
3040            dst_row = src->tpixels[y];
3041            for (x = 0; x < sx; x++) {
3042                const unsigned char c = *(src_row + x);
3043                if (c == src->transparent) {
3044                    *(dst_row + x) = gdTrueColorAlpha(0, 0, 0, 127);
3045                } else {
3046                    *(dst_row + x) = gdTrueColorAlpha(src->red[c], src->green[c], src->blue[c], src->alpha[c]);
3047                }
3048            }
3049        }
3050    }
3051
3052    /* free old palette buffer */
3053    for (yy = y - 1; yy >= yy - 1; yy--) {
3054        gdFree(src->pixels[yy]);
3055    }
3056    gdFree(src->pixels);
3057    src->trueColor = 1;
3058    src->pixels = NULL;
3059    src->alphaBlendingFlag = 0;
3060    src->saveAlphaFlag = 1;
3061
3062    if (src->transparent >= 0) {
3063        const unsigned char c = src->transparent;
3064        src->transparent =  gdTrueColorAlpha(src->red[c], src->green[c], src->blue[c], src->alpha[c]);
3065    }
3066
3067    return 1;
3068
3069clean_on_error:
3070    if (y > 0) {
3071
3072        for (yy = y; yy >= yy - 1; y--) {
3073            gdFree(src->tpixels[y]);
3074        }
3075        gdFree(src->tpixels);
3076    }
3077    return 0;
3078}
3079
3080