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