/* Last edited: Mar 11 16:44 1999 (fw) */ #include #include #include #include "wgd/io.h" #include "wgd/gd.h" #include "wgd/mtables.h" #define TRUE 1 #define FALSE 0 /* $Id: libgd.c,v 1.3 1999/03/11 16:47:25 fw Exp $ */ static void gdImageBrushApply(gdImagePtr im, int x, int y); static void gdImageTileApply(gdImagePtr im, int x, int y); gdImagePtr gdImageCreate(int sx, int sy) { int i; gdImagePtr im; im = (gdImage *) malloc(sizeof(gdImage)); im->pixels = (unsigned char **) malloc(sizeof(unsigned char *) * sx); im->polyInts = 0; im->polyAllocated = 0; im->brush = 0; im->tile = 0; im->style = 0; for (i=0; (ipixels[i] = (unsigned char *) calloc( sy, sizeof(unsigned char)); } /* make sure that the colors are zeroed out */ for (i=0; i < gdMaxColors; i++) { im->red[i]=0; im->green[i]=0; im->blue[i]=0; } im->sx = sx; im->sy = sy; im->colorsTotal = 0; im->transparent = (-1); im->interlace = 0; return im; } void gdImageDestroy(gdImagePtr im) { int i; if (im == NULL) return; for (i=0; (isx); i++) { free(im->pixels[i]); } free(im->pixels); if (im->polyInts) { free(im->polyInts); } if (im->style) { free(im->style); } free(im); } int gdImageColorClosest(gdImagePtr im, int r, int g, int b) { int i; long rd, gd, bd; int ct = (-1); long mindist = 0; for (i=0; (i<(im->colorsTotal)); i++) { long dist; if (im->open[i]) { continue; } rd = (im->red[i] - r); gd = (im->green[i] - g); bd = (im->blue[i] - b); dist = rd * rd + gd * gd + bd * bd; if ((i == 0) || (dist < mindist)) { mindist = dist; ct = i; } } return ct; } int gdImageColorExact(gdImagePtr im, int r, int g, int b) { int i; for (i=0; (i<(im->colorsTotal)); i++) { if (im->open[i]) { continue; } if ((im->red[i] == r) && (im->green[i] == g) && (im->blue[i] == b)) { return i; } } return -1; } int gdImageColorAllocate(gdImagePtr im, int r, int g, int b) { int i; int ct = (-1); for (i=0; (i<(im->colorsTotal)); i++) { if (im->open[i]) { ct = i; break; } } if (ct == (-1)) { ct = im->colorsTotal; if (ct == gdMaxColors) { return -1; } im->colorsTotal++; } im->red[ct] = r; im->green[ct] = g; im->blue[ct] = b; im->open[ct] = 0; return ct; } void gdImageColorDeallocate(gdImagePtr im, int color) { /* Mark it open. */ im->open[color] = 1; } void gdImageColorTransparent(gdImagePtr im, int color) { im->transparent = color; } void gdImageSetPixel(gdImagePtr im, int x, int y, int color) { int p; switch(color) { case gdStyled: if (!im->style) { /* Refuse to draw if no style is set. */ return; } else { p = im->style[im->stylePos++]; } if (p != (gdTransparent)) { gdImageSetPixel(im, x, y, p); } im->stylePos = im->stylePos % im->styleLength; break; case gdStyledBrushed: if (!im->style) { /* Refuse to draw if no style is set. */ return; } p = im->style[im->stylePos++]; if ((p != gdTransparent) && (p != 0)) { gdImageSetPixel(im, x, y, gdBrushed); } im->stylePos = im->stylePos % im->styleLength; break; case gdBrushed: gdImageBrushApply(im, x, y); break; case gdTiled: gdImageTileApply(im, x, y); break; default: if (gdImageBoundsSafe(im, x, y)) { im->pixels[x][y] = color; } break; } } static void gdImageBrushApply(gdImagePtr im, int x, int y) { int lx, ly; int hy; int hx; int x1, y1, x2, y2; int srcx, srcy; if (!im->brush) { return; } hy = gdImageSY(im->brush)/2; y1 = y - hy; y2 = y1 + gdImageSY(im->brush); hx = gdImageSX(im->brush)/2; x1 = x - hx; x2 = x1 + gdImageSX(im->brush); srcy = 0; for (ly = y1; (ly < y2); ly++) { srcx = 0; for (lx = x1; (lx < x2); lx++) { int p; p = gdImageGetPixel(im->brush, srcx, srcy); /* Allow for non-square brushes! */ if (p != gdImageGetTransparent(im->brush)) { gdImageSetPixel(im, lx, ly, im->brushColorMap[p]); } srcx++; } srcy++; } } static void gdImageTileApply(gdImagePtr im, int x, int y) { int srcx, srcy; int p; if (!im->tile) { return; } srcx = x % gdImageSX(im->tile); srcy = y % gdImageSY(im->tile); p = gdImageGetPixel(im->tile, srcx, srcy); /* Allow for transparency */ if (p != gdImageGetTransparent(im->tile)) { gdImageSetPixel(im, x, y, im->tileColorMap[p]); } } int gdImageGetPixel(gdImagePtr im, int x, int y) { if (gdImageBoundsSafe(im, x, y)) { return im->pixels[x][y]; } else { return 0; } } /* Bresenham as presented in Foley & Van Dam */ void gdImageLine(gdImagePtr im, int x1, int y1, int x2, int y2, int color) { int dx, dy, incr1, incr2, d, x, y, xend, yend, xdirflag, ydirflag; dx = abs(x2-x1); dy = abs(y2-y1); if (dy <= dx) { d = 2*dy - dx; incr1 = 2*dy; incr2 = 2 * (dy - dx); if (x1 > x2) { x = x2; y = y2; ydirflag = (-1); xend = x1; } else { x = x1; y = y1; ydirflag = 1; xend = x2; } gdImageSetPixel(im, x, y, color); if (((y2 - y1) * ydirflag) > 0) { while (x < xend) { x++; if (d <0) { d+=incr1; } else { y++; d+=incr2; } gdImageSetPixel(im, x, y, color); } } else { while (x < xend) { x++; if (d <0) { d+=incr1; } else { y--; d+=incr2; } gdImageSetPixel(im, x, y, color); } } } else { d = 2*dx - dy; incr1 = 2*dx; incr2 = 2 * (dx - dy); if (y1 > y2) { y = y2; x = x2; yend = y1; xdirflag = (-1); } else { y = y1; x = x1; yend = y2; xdirflag = 1; } gdImageSetPixel(im, x, y, color); if (((x2 - x1) * xdirflag) > 0) { while (y < yend) { y++; if (d <0) { d+=incr1; } else { x++; d+=incr2; } gdImageSetPixel(im, x, y, color); } } else { while (y < yend) { y++; if (d <0) { d+=incr1; } else { x--; d+=incr2; } gdImageSetPixel(im, x, y, color); } } } } /* As above, plus dashing */ #define dashedSet \ { \ dashStep++; \ if (dashStep == gdDashSize) { \ dashStep = 0; \ on = !on; \ } \ if (on) { \ gdImageSetPixel(im, x, y, color); \ } \ } void gdImageDashedLine(gdImagePtr im, int x1, int y1, int x2, int y2, int color) { int dx, dy, incr1, incr2, d, x, y, xend, yend, xdirflag, ydirflag; int dashStep = 0; int on = 1; dx = abs(x2-x1); dy = abs(y2-y1); if (dy <= dx) { d = 2*dy - dx; incr1 = 2*dy; incr2 = 2 * (dy - dx); if (x1 > x2) { x = x2; y = y2; ydirflag = (-1); xend = x1; } else { x = x1; y = y1; ydirflag = 1; xend = x2; } dashedSet; if (((y2 - y1) * ydirflag) > 0) { while (x < xend) { x++; if (d <0) { d+=incr1; } else { y++; d+=incr2; } dashedSet; } } else { while (x < xend) { x++; if (d <0) { d+=incr1; } else { y--; d+=incr2; } dashedSet; } } } else { d = 2*dx - dy; incr1 = 2*dx; incr2 = 2 * (dx - dy); if (y1 > y2) { y = y2; x = x2; yend = y1; xdirflag = (-1); } else { y = y1; x = x1; yend = y2; xdirflag = 1; } dashedSet; if (((x2 - x1) * xdirflag) > 0) { while (y < yend) { y++; if (d <0) { d+=incr1; } else { x++; d+=incr2; } dashedSet; } } else { while (y < yend) { y++; if (d <0) { d+=incr1; } else { x--; d+=incr2; } dashedSet; } } } } int gdImageBoundsSafe(gdImagePtr im, int x, int y) { return (!(((y < 0) || (y >= im->sy)) || ((x < 0) || (x >= im->sx)))); } void gdImageChar(gdImagePtr im, gdFontPtr f, int x, int y, int c, int color) { int cx, cy; int px, py; int fline; cx = 0; cy = 0; if ((c < f->offset) || (c >= (f->offset + f->nchars))) { return; } fline = (c - f->offset) * f->h * f->w; for (py = y; (py < (y + f->h)); py++) { for (px = x; (px < (x + f->w)); px++) { if (f->data[fline + cy * f->w + cx]) { gdImageSetPixel(im, px, py, color); } cx++; } cx = 0; cy++; } } void gdImageCharUp(gdImagePtr im, gdFontPtr f, int x, int y, int c, int color) { int cx, cy; int px, py; int fline; cx = 0; cy = 0; if ((c < f->offset) || (c >= (f->offset + f->nchars))) { return; } fline = (c - f->offset) * f->h * f->w; for (py = y; (py > (y - f->w)); py--) { for (px = x; (px < (x + f->h)); px++) { if (f->data[fline + cy * f->w + cx]) { gdImageSetPixel(im, px, py, color); } cy++; } cy = 0; cx++; } } void gdImageString(gdImagePtr im, gdFontPtr f, int x, int y, char *s, int color) { int i; int l; l = strlen(s); for (i=0; (iw; } } void gdImageStringUp(gdImagePtr im, gdFontPtr f, int x, int y, char *s, int color) { int i; int l; l = strlen(s); for (i=0; (iw; } } /* s and e are integers modulo 360 (degrees), with 0 degrees being the rightmost extreme and degrees changing clockwise. cx and cy are the center in pixels; w and h are the horizontal and vertical diameter in pixels. Nice interface, but slow, since I don't yet use Bresenham (I'm using an inefficient but simple solution with too much work going on in it; generalizing Bresenham to ellipses and partial arcs of ellipses is non-trivial, at least for me) and there are other inefficiencies (small circles do far too much work). */ void gdImageArc(gdImagePtr im, int cx, int cy, int w, int h, int s, int e, int color) { int i; int lx = 0, ly = 0; int w2, h2; w2 = w/2; h2 = h/2; while (e < s) { e += 360; } for (i=s; (i <= e); i++) { int x, y; x = ((long)cost[i % 360] * (long)w2 / costScale) + cx; y = ((long)sint[i % 360] * (long)h2 / sintScale) + cy; if (i != s) { gdImageLine(im, lx, ly, x, y, color); } lx = x; ly = y; } } #if 0 /* Bresenham octant code, which I should use eventually */ int x, y, d; x = 0; y = w; d = 3-2*w; while (x < y) { gdImageSetPixel(im, cx+x, cy+y, color); if (d < 0) { d += 4 * x + 6; } else { d += 4 * (x - y) + 10; y--; } x++; } if (x == y) { gdImageSetPixel(im, cx+x, cy+y, color); } #endif void gdImageFillToBorder(gdImagePtr im, int x, int y, int border, int color) { int lastBorder; /* Seek left */ int leftLimit, rightLimit; int i; leftLimit = (-1); if (border < 0) { /* Refuse to fill to a non-solid border */ return; } for (i = x; (i >= 0); i--) { if (gdImageGetPixel(im, i, y) == border) { break; } gdImageSetPixel(im, i, y, color); leftLimit = i; } if (leftLimit == (-1)) { return; } /* Seek right */ rightLimit = x; for (i = (x+1); (i < im->sx); i++) { if (gdImageGetPixel(im, i, y) == border) { break; } gdImageSetPixel(im, i, y, color); rightLimit = i; } /* Look at lines above and below and start paints */ /* Above */ if (y > 0) { lastBorder = 1; for (i = leftLimit; (i <= rightLimit); i++) { int c; c = gdImageGetPixel(im, i, y-1); if (lastBorder) { if ((c != border) && (c != color)) { gdImageFillToBorder(im, i, y-1, border, color); lastBorder = 0; } } else if ((c == border) || (c == color)) { lastBorder = 1; } } } /* Below */ if (y < ((im->sy) - 1)) { lastBorder = 1; for (i = leftLimit; (i <= rightLimit); i++) { int c; c = gdImageGetPixel(im, i, y+1); if (lastBorder) { if ((c != border) && (c != color)) { gdImageFillToBorder(im, i, y+1, border, color); lastBorder = 0; } } else if ((c == border) || (c == color)) { lastBorder = 1; } } } } void gdImageFill(gdImagePtr im, int x, int y, int color) { int lastBorder; int old; int leftLimit, rightLimit; int i; old = gdImageGetPixel(im, x, y); if (color == gdTiled) { /* Tile fill -- got to watch out! */ int p, tileColor; int srcx, srcy; if (!im->tile) { return; } /* Refuse to flood-fill with a transparent pattern -- I can't do it without allocating another image */ if (gdImageGetTransparent(im->tile) != (-1)) { return; } srcx = x % gdImageSX(im->tile); srcy = y % gdImageSY(im->tile); p = gdImageGetPixel(im->tile, srcx, srcy); tileColor = im->tileColorMap[p]; if (old == tileColor) { /* Nothing to be done */ return; } } else { if (old == color) { /* Nothing to be done */ return; } } /* Seek left */ leftLimit = (-1); for (i = x; (i >= 0); i--) { if (gdImageGetPixel(im, i, y) != old) { break; } gdImageSetPixel(im, i, y, color); leftLimit = i; } if (leftLimit == (-1)) { return; } /* Seek right */ rightLimit = x; for (i = (x+1); (i < im->sx); i++) { if (gdImageGetPixel(im, i, y) != old) { break; } gdImageSetPixel(im, i, y, color); rightLimit = i; } /* Look at lines above and below and start paints */ /* Above */ if (y > 0) { lastBorder = 1; for (i = leftLimit; (i <= rightLimit); i++) { int c; c = gdImageGetPixel(im, i, y-1); if (lastBorder) { if (c == old) { gdImageFill(im, i, y-1, color); lastBorder = 0; } } else if (c != old) { lastBorder = 1; } } } /* Below */ if (y < ((im->sy) - 1)) { lastBorder = 1; for (i = leftLimit; (i <= rightLimit); i++) { int c; c = gdImageGetPixel(im, i, y+1); if (lastBorder) { if (c == old) { gdImageFill(im, i, y+1, color); lastBorder = 0; } } else if (c != old) { lastBorder = 1; } } } } #ifdef TEST_CODE void gdImageDump(gdImagePtr im) { int i, j; for (i=0; (i < im->sy); i++) { for (j=0; (j < im->sx); j++) { printf("%d", im->pixels[j][i]); } printf("\n"); } } #endif /* Code drawn from ppmtogif.c, from the pbmplus package ** ** Based on GIFENCOD by David Rowley . A ** Lempel-Zim compression based on "compress". ** ** Modified by Marcel Wijkstra ** ** Copyright (C) 1989 by Jef Poskanzer. ** ** Permission to use, copy, modify, and distribute this software and its ** documentation for any purpose and without fee is hereby granted, provided ** that the above copyright notice appear in all copies and that both that ** copyright notice and this permission notice appear in supporting ** documentation. This software is provided "as is" without express or ** implied warranty. ** ** The Graphics Interchange Format(c) is the Copyright property of ** CompuServe Incorporated. GIF(sm) is a Service Mark property of ** CompuServe Incorporated. */ /* * a code_int must be able to hold 2**GIFBITS values of type int, and also -1 */ typedef int code_int; #ifdef SIGNED_COMPARE_SLOW typedef unsigned long int count_int; typedef unsigned short int count_short; #else /*SIGNED_COMPARE_SLOW*/ typedef long int count_int; #endif /*SIGNED_COMPARE_SLOW*/ /* this is used for creating GIFs in main memory*/ typedef struct dpStruct { void* data; int logicalSize; int realSize; int dataGood; } dynamicPtr; static int colorstobpp(int colors); static void BumpPixel (void); static int GIFNextPixel (gdImagePtr im); static void GIFEncode (dynamicPtr *dp, int GWidth, int GHeight, int GInterlace, int Background, int Transparent, int BitsPerPixel, int *Red, int *Green, int *Blue, gdImagePtr im); static void Putword (int w, dynamicPtr* dp); static void Putchar(int a, dynamicPtr* dp); static void compress (int init_bits, dynamicPtr* dp, gdImagePtr im); static void output (code_int code); static void cl_block (void); static void cl_hash (register count_int hsize); static void char_init (void); static void char_out (int c); static void flush_char (void); /* Allows for reuse */ static void init_statics(void); /* these functions operate on in-memory dynamic pointers */ static int allocDynamic (dynamicPtr* dp,int initialSize); static int appendDynamic (dynamicPtr* dp, const void* src, int size); static int reallocDynamic (dynamicPtr* dp, int required); static int trimDynamic (dynamicPtr* dp); static void freeDynamic (dynamicPtr* dp); static dynamicPtr* gdImageGifData (gdImagePtr im); /* return data as a dynamic pointer */ static dynamicPtr* gdImageGifData (gdImagePtr im) { int interlace, transparent, BitsPerPixel; dynamicPtr* dp; dp = (dynamicPtr*) malloc(sizeof(dynamicPtr)); if (dp == NULL) { return NULL; } if (!allocDynamic(dp,1024)) return NULL; interlace = im->interlace; transparent = im->transparent; BitsPerPixel = colorstobpp(im->colorsTotal); /* Clear any old values in statics strewn through the GIF code */ init_statics(); /* All set, let's do it. */ GIFEncode( dp, im->sx, im->sy, interlace, 0, transparent, BitsPerPixel, im->red, im->green, im->blue, im); /* if there were problems along the way, we clean up and return NULL */ if (dp->dataGood) { trimDynamic(dp); return dp; } else { freeDynamic(dp); free(dp); return NULL; } } void gdImageGif(gdImagePtr im, FILE *out) { dynamicPtr* dp; dp = gdImageGifData(im); if (dp == NULL) return; /* print the file to output */ fwrite(dp->data,1,dp->logicalSize,out); /* get rid of the data -- we're done with it */ freeDynamic(dp); free(dp); } void* gdImageGifPtr(gdImagePtr im, int* size) { dynamicPtr* dp; void* data; *size = 0; dp = gdImageGifData(im); if (dp == NULL) return NULL; *size = dp->logicalSize; data = dp->data; free(dp); return data; } static int colorstobpp(int colors) { int bpp = 0; if ( colors <= 2 ) bpp = 1; else if ( colors <= 4 ) bpp = 2; else if ( colors <= 8 ) bpp = 3; else if ( colors <= 16 ) bpp = 4; else if ( colors <= 32 ) bpp = 5; else if ( colors <= 64 ) bpp = 6; else if ( colors <= 128 ) bpp = 7; else if ( colors <= 256 ) bpp = 8; return bpp; } /***************************************************************************** * * GIFENCODE.C - GIF Image compression interface * * GIFEncode( FName, GHeight, GWidth, GInterlace, Background, Transparent, * BitsPerPixel, Red, Green, Blue, gdImagePtr ) * *****************************************************************************/ static int Width, Height; static int curx, cury; static long CountDown; static int Pass = 0; static int Interlace; /* * Bump the 'curx' and 'cury' to point to the next pixel */ static void BumpPixel(void) { /* * Bump the current X position */ ++curx; /* * If we are at the end of a scan line, set curx back to the beginning * If we are interlaced, bump the cury to the appropriate spot, * otherwise, just increment it. */ if( curx == Width ) { curx = 0; if( !Interlace ) ++cury; else { switch( Pass ) { case 0: cury += 8; if( cury >= Height ) { ++Pass; cury = 4; } break; case 1: cury += 8; if( cury >= Height ) { ++Pass; cury = 2; } break; case 2: cury += 4; if( cury >= Height ) { ++Pass; cury = 1; } break; case 3: cury += 2; break; } } } } /* * Return the next pixel from the image */ static int GIFNextPixel(gdImagePtr im) { int r; if( CountDown == 0 ) return EOF; --CountDown; r = gdImageGetPixel(im, curx, cury); BumpPixel(); return r; } /* public */ static void GIFEncode(dynamicPtr *dp, int GWidth, int GHeight, int GInterlace, int Background, int Transparent, int BitsPerPixel, int *Red, int *Green, int *Blue, gdImagePtr im) { int B; int RWidth, RHeight; int LeftOfs, TopOfs; int Resolution; int ColorMapSize; int InitCodeSize; int i; Interlace = GInterlace; ColorMapSize = 1 << BitsPerPixel; RWidth = Width = GWidth; RHeight = Height = GHeight; LeftOfs = TopOfs = 0; Resolution = BitsPerPixel; /* * Calculate number of bits we are expecting */ CountDown = (long)Width * (long)Height; /* * Indicate which pass we are on (if interlace) */ Pass = 0; /* * The initial code size */ if( BitsPerPixel <= 1 ) InitCodeSize = 2; else InitCodeSize = BitsPerPixel; /* * Set up the current x and y position */ curx = cury = 0; /* * Write the Magic header */ appendDynamic(dp,Transparent < 0 ? "GIF87a" : "GIF89a",6); /* * Write out the screen width and height */ Putword( RWidth, dp ); Putword( RHeight, dp ); /* * Indicate that there is a global colour map */ B = 0x80; /* Yes, there is a color map */ /* * OR in the resolution */ B |= (Resolution - 1) << 5; /* * OR in the Bits per Pixel */ B |= (BitsPerPixel - 1); /* * Write it out */ Putchar( B, dp ); /* * Write out the Background colour */ Putchar ( Background, dp ); /* * Byte of 0's (future expansion) */ Putchar( 0, dp ); /* * Write out the Global Colour Map */ for( i=0; i= 0 ) { Putchar( '!', dp ); Putchar( 0xf9, dp ); Putchar( 4, dp ); Putchar( 1, dp ); Putchar( 0, dp ); Putchar( 0, dp ); Putchar( (unsigned char) Transparent, dp ); Putchar( 0, dp ); } /* * Write an Image separator */ Putchar( ',', dp ); /* * Write the Image header */ Putword( LeftOfs, dp ); Putword( TopOfs, dp ); Putword( Width, dp ); Putword( Height, dp ); /* * Write out whether or not the image is interlaced */ if( Interlace ) Putchar( 0x40, dp ); else Putchar( 0x00, dp ); /* * Write out the initial code size */ Putchar( InitCodeSize, dp ); /* * Go and actually compress the data */ compress( InitCodeSize+1, dp, im ); /* * Write out a Zero-length packet (to end the series) */ Putchar( 0, dp ); /* * Write the GIF file terminator */ Putchar( ';', dp ); } /* * Write out a word to the GIF pointer */ static void Putword(int w, dynamicPtr* dp) { Putchar( w & 0xff, dp ); Putchar( (w / 256) & 0xff, dp ); } /* * Write out a character to the GIF pointer */ static void Putchar(int a, dynamicPtr* dp) { unsigned char b; b = a; appendDynamic(dp,&b,1); } /*************************************************************************** * * GIFCOMPR.C - GIF Image compression routines * * Lempel-Ziv compression based on 'compress'. GIF modifications by * David Rowley (mgardi@watdcsu.waterloo.edu) * ***************************************************************************/ /* * General DEFINEs */ #define GIFBITS 12 #define HSIZE 5003 /* 80% occupancy */ #ifdef NO_UCHAR typedef char char_type; #else /*NO_UCHAR*/ typedef unsigned char char_type; #endif /*NO_UCHAR*/ /* * * GIF Image compression - modified 'compress' * * Based on: compress.c - File compression ala IEEE Computer, June 1984. * * By Authors: Spencer W. Thomas (decvax!harpo!utah-cs!utah-gr!thomas) * Jim McKie (decvax!mcvax!jim) * Steve Davies (decvax!vax135!petsd!peora!srd) * Ken Turkowski (decvax!decwrl!turtlevax!ken) * James A. Woods (decvax!ihnp4!ames!jaw) * Joe Orost (decvax!vax135!petsd!joe) * */ #include #define ARGVAL() (*++(*argv) || (--argc && *++argv)) static int n_bits; /* number of bits/code */ static int maxbits = GIFBITS; /* user settable max # bits/code */ static code_int maxcode; /* maximum code, given n_bits */ static code_int maxmaxcode = (code_int)1 << GIFBITS; /* should NEVER generate this code */ #ifdef COMPATIBLE /* But wrong! */ # define MAXCODE(n_bits) ((code_int) 1 << (n_bits) - 1) #else /*COMPATIBLE*/ # define MAXCODE(n_bits) (((code_int) 1 << (n_bits)) - 1) #endif /*COMPATIBLE*/ static count_int htab [HSIZE]; static unsigned short codetab [HSIZE]; #define HashTabOf(i) htab[i] #define CodeTabOf(i) codetab[i] static code_int hsize = HSIZE; /* for dynamic table sizing */ /* * To save much memory, we overlay the table used by compress() with those * used by decompress(). The tab_prefix table is the same size and type * as the codetab. The tab_suffix table needs 2**GIFBITS characters. We * get this from the beginning of htab. The output stack uses the rest * of htab, and contains characters. There is plenty of room for any * possible stack (stack used to be 8000 characters). */ #define tab_prefixof(i) CodeTabOf(i) #define tab_suffixof(i) ((char_type*)(htab))[i] #define de_stack ((char_type*)&tab_suffixof((code_int)1< 0 ) goto probe; nomatch: output ( (code_int) ent ); ++out_count; ent = c; #ifdef SIGNED_COMPARE_SLOW if ( (unsigned) free_ent < (unsigned) maxmaxcode) { #else /*SIGNED_COMPARE_SLOW*/ if ( free_ent < maxmaxcode ) { /* } */ #endif /*SIGNED_COMPARE_SLOW*/ CodeTabOf (i) = free_ent++; /* code -> hashtable */ HashTabOf (i) = fcode; } else cl_block(); } /* * Put out the final code. */ output( (code_int)ent ); ++out_count; output( (code_int) EOFCode ); } /***************************************************************** * TAG( output ) * * Output the given code. * Inputs: * code: A n_bits-bit integer. If == -1, then EOF. This assumes * that n_bits =< (long)wordsize - 1. * Outputs: * Outputs code to the file. * Assumptions: * Chars are 8 bits long. * Algorithm: * Maintain a GIFBITS character long buffer (so that 8 codes will * fit in it exactly). Use the VAX insv instruction to insert each * code in turn. When the buffer fills up empty it and start over. */ static unsigned long cur_accum = 0; static int cur_bits = 0; static unsigned long masks[] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF }; static void output(code_int code) { cur_accum &= masks[ cur_bits ]; if( cur_bits > 0 ) cur_accum |= ((long)code << cur_bits); else cur_accum = code; cur_bits += n_bits; while( cur_bits >= 8 ) { char_out( (unsigned int)(cur_accum & 0xff) ); cur_accum >>= 8; cur_bits -= 8; } /* * If the next entry is going to be too big for the code size, * then increase it, if possible. */ if ( free_ent > maxcode || clear_flg ) { if( clear_flg ) { maxcode = MAXCODE (n_bits = g_init_bits); clear_flg = 0; } else { ++n_bits; if ( n_bits == maxbits ) maxcode = maxmaxcode; else maxcode = MAXCODE(n_bits); } } if( code == EOFCode ) { /* * At EOF, write the rest of the buffer. */ while( cur_bits > 0 ) { char_out( (unsigned int)(cur_accum & 0xff) ); cur_accum >>= 8; cur_bits -= 8; } flush_char(); if (!g_outptr->dataGood) return; } } /* * Clear out the hash table */ static void cl_block (void) /* table clear for block compress */ { cl_hash ( (count_int) hsize ); free_ent = ClearCode + 2; clear_flg = 1; output( (code_int)ClearCode ); } static void cl_hash(register count_int hsize) /* reset code table */ { register count_int *htab_p = htab+hsize; register long i; register long m1 = -1; i = hsize - 16; do { /* might use Sys V memset(3) here */ *(htab_p-16) = m1; *(htab_p-15) = m1; *(htab_p-14) = m1; *(htab_p-13) = m1; *(htab_p-12) = m1; *(htab_p-11) = m1; *(htab_p-10) = m1; *(htab_p-9) = m1; *(htab_p-8) = m1; *(htab_p-7) = m1; *(htab_p-6) = m1; *(htab_p-5) = m1; *(htab_p-4) = m1; *(htab_p-3) = m1; *(htab_p-2) = m1; *(htab_p-1) = m1; htab_p -= 16; } while ((i -= 16) >= 0); for ( i += 16; i > 0; --i ) *--htab_p = m1; } /****************************************************************************** * * GIF Specific routines * ******************************************************************************/ /* * Number of characters so far in this 'packet' */ static int a_count; /* * Set up the 'byte output' routine */ static void char_init(void) { a_count = 0; } /* * Define the storage for the packet accumulator */ static char accum[ 256 ]; /* * Add a character to the end of the current packet, and if it is 254 * characters, flush the packet to disk. */ static void char_out(int c) { accum[ a_count++ ] = c; if( a_count >= 254 ) flush_char(); } /* this is the remnant of the original file-writing code */ #if 0 /* * Flush the packet to disk, and reset the accumulator */ static void flush_char(void) { if( a_count > 0 ) { fputc( a_count, g_outfile ); fwrite( accum, 1, a_count, g_outfile ); a_count = 0; } } #endif static void flush_char(void) { if( a_count > 0 ) { Putchar( a_count, g_outptr ); appendDynamic( g_outptr, accum, a_count ); a_count = 0; } } static void init_statics(void) { /* Some of these are properly initialized later. What I'm doing here is making sure code that depends on C's initialization of statics doesn't break when the code gets called more than once. */ Width = 0; Height = 0; curx = 0; cury = 0; CountDown = 0; Pass = 0; Interlace = 0; a_count = 0; cur_accum = 0; cur_bits = 0; g_init_bits = 0; g_outptr = NULL; ClearCode = 0; EOFCode = 0; free_ent = 0; clear_flg = 0; offset = 0; in_count = 1; out_count = 0; hsize = HSIZE; n_bits = 0; maxbits = GIFBITS; maxcode = 0; maxmaxcode = (code_int)1 << GIFBITS; } /* +-------------------------------------------------------------------+ */ /* | Copyright 1990, 1991, 1993, David Koblas. (koblas@netcom.com) | */ /* | Permission to use, copy, modify, and distribute this software | */ /* | and its documentation for any purpose and without fee is hereby | */ /* | granted, provided that the above copyright notice appear in all | */ /* | copies and that both that copyright notice and this permission | */ /* | notice appear in supporting documentation. This software is | */ /* | provided "as is" without express or implied warranty. | */ /* +-------------------------------------------------------------------+ */ #define MAXCOLORMAPSIZE 256 #define TRUE 1 #define FALSE 0 #define CM_RED 0 #define CM_GREEN 1 #define CM_BLUE 2 #define MAX_LWZ_BITS 12 #define INTERLACE 0x40 #define LOCALCOLORMAP 0x80 #define BitSet(byte, bit) (((byte) & (bit)) == (bit)) #define ReadOK(file,buffer,len) (fread(buffer, len, 1, file) != 0) #define LM_to_uint(a,b) (((b)<<8)|(a)) /* We may eventually want to use this information, but def it out for now */ #if 0 static struct { unsigned int Width; unsigned int Height; unsigned char ColorMap[3][MAXCOLORMAPSIZE]; unsigned int BitPixel; unsigned int ColorResolution; unsigned int Background; unsigned int AspectRatio; } GifScreen; #endif static struct { int transparent; int delayTime; int inputFlag; int disposal; } Gif89 = { -1, -1, -1, 0 }; static int ReadColorMap (FILE *fd, int number, unsigned char (*buffer)[256]); static int DoExtension (FILE *fd, int label, int *Transparent); static int GetDataBlock (FILE *fd, unsigned char *buf); static int GetCode (FILE *fd, int code_size, int flag); static int LWZReadByte (FILE *fd, int flag, int input_code_size); static void ReadImage (gdImagePtr im, FILE *fd, int len, int height, unsigned char (*cmap)[256], int interlace, int ignore); int ZeroDataBlock; gdImagePtr gdImageCreateFromGif(FILE *fd) { int imageNumber; int BitPixel; int ColorResolution; int Background; int AspectRatio; int Transparent = (-1); unsigned char buf[16]; unsigned char c; unsigned char ColorMap[3][MAXCOLORMAPSIZE]; unsigned char localColorMap[3][MAXCOLORMAPSIZE]; int imw, imh; int useGlobalColormap; int bitPixel; int imageCount = 0; char version[4]; gdImagePtr im = 0; ZeroDataBlock = FALSE; imageNumber = 1; if (! ReadOK(fd,buf,6)) { return 0; } if (strncmp((char *)buf,"GIF",3) != 0) { return 0; } strncpy(version, (char *)buf + 3, 3); version[3] = '\0'; if ((strcmp(version, "87a") != 0) && (strcmp(version, "89a") != 0)) { return 0; } if (! ReadOK(fd,buf,7)) { return 0; } BitPixel = 2<<(buf[4]&0x07); ColorResolution = (int) (((buf[4]&0x70)>>3)+1); Background = buf[5]; AspectRatio = buf[6]; if (BitSet(buf[4], LOCALCOLORMAP)) { /* Global Colormap */ if (ReadColorMap(fd, BitPixel, ColorMap)) { return 0; } } for (;;) { if (! ReadOK(fd,&c,1)) { return 0; } if (c == ';') { /* GIF terminator */ int i; if (imageCount < imageNumber) { return 0; } /* Terminator before any image was declared! */ if (!im) { return 0; } /* Check for open colors at the end, so we can reduce colorsTotal and ultimately BitsPerPixel */ for (i=((im->colorsTotal-1)); (i>=0); i--) { if (im->open[i]) { im->colorsTotal--; } else { break; } } return im; } if (c == '!') { /* Extension */ if (! ReadOK(fd,&c,1)) { return 0; } DoExtension(fd, c, &Transparent); continue; } if (c != ',') { /* Not a valid start character */ continue; } ++imageCount; if (! ReadOK(fd,buf,9)) { return 0; } useGlobalColormap = ! BitSet(buf[8], LOCALCOLORMAP); bitPixel = 1<<((buf[8]&0x07)+1); imw = LM_to_uint(buf[4],buf[5]); imh = LM_to_uint(buf[6],buf[7]); if (!(im = gdImageCreate(imw, imh))) { return 0; } im->interlace = BitSet(buf[8], INTERLACE); if (! useGlobalColormap) { if (ReadColorMap(fd, bitPixel, localColorMap)) { return 0; } ReadImage(im, fd, imw, imh, localColorMap, BitSet(buf[8], INTERLACE), imageCount != imageNumber); } else { ReadImage(im, fd, imw, imh, ColorMap, BitSet(buf[8], INTERLACE), imageCount != imageNumber); } if (Transparent != (-1)) { gdImageColorTransparent(im, Transparent); } } } static int ReadColorMap(FILE *fd, int number, unsigned char (*buffer)[256]) { int i; unsigned char rgb[3]; for (i = 0; i < number; ++i) { if (! ReadOK(fd, rgb, sizeof(rgb))) { return TRUE; } buffer[CM_RED][i] = rgb[0] ; buffer[CM_GREEN][i] = rgb[1] ; buffer[CM_BLUE][i] = rgb[2] ; } return FALSE; } static int DoExtension(FILE *fd, int label, int *Transparent) { static unsigned char buf[256]; switch (label) { case 0xf9: /* Graphic Control Extension */ (void) GetDataBlock(fd, (unsigned char*) buf); Gif89.disposal = (buf[0] >> 2) & 0x7; Gif89.inputFlag = (buf[0] >> 1) & 0x1; Gif89.delayTime = LM_to_uint(buf[1],buf[2]); if ((buf[0] & 0x1) != 0) *Transparent = buf[3]; while (GetDataBlock(fd, (unsigned char*) buf) != 0) ; return FALSE; default: break; } while (GetDataBlock(fd, (unsigned char*) buf) != 0) ; return FALSE; } static int GetDataBlock(FILE *fd, unsigned char *buf) { unsigned char count; if (! ReadOK(fd,&count,1)) { return -1; } ZeroDataBlock = count == 0; if ((count != 0) && (! ReadOK(fd, buf, count))) { return -1; } return count; } static int GetCode(FILE *fd, int code_size, int flag) { static unsigned char buf[280]; static int curbit, lastbit, done, last_byte; int i, j, ret; unsigned char count; if (flag) { curbit = 0; lastbit = 0; done = FALSE; return 0; } if ( (curbit+code_size) >= lastbit) { if (done) { if (curbit >= lastbit) { /* Oh well */ } return -1; } buf[0] = buf[last_byte-2]; buf[1] = buf[last_byte-1]; if ((count = GetDataBlock(fd, &buf[2])) == 0) done = TRUE; last_byte = 2 + count; curbit = (curbit - lastbit) + 16; lastbit = (2+count)*8 ; } ret = 0; for (i = curbit, j = 0; j < code_size; ++i, ++j) ret |= ((buf[ i / 8 ] & (1 << (i % 8))) != 0) << j; curbit += code_size; return ret; } static int LWZReadByte(FILE *fd, int flag, int input_code_size) { static int fresh = FALSE; int code, incode; static int code_size, set_code_size; static int max_code, max_code_size; static int firstcode, oldcode; static int clear_code, end_code; static int table[2][(1<< MAX_LWZ_BITS)]; static int stack[(1<<(MAX_LWZ_BITS))*2], *sp; register int i; if (flag) { set_code_size = input_code_size; code_size = set_code_size+1; clear_code = 1 << set_code_size ; end_code = clear_code + 1; max_code_size = 2*clear_code; max_code = clear_code+2; GetCode(fd, 0, TRUE); fresh = TRUE; for (i = 0; i < clear_code; ++i) { table[0][i] = 0; table[1][i] = i; } for (; i < (1< stack) return *--sp; while ((code = GetCode(fd, code_size, FALSE)) >= 0) { if (code == clear_code) { for (i = 0; i < clear_code; ++i) { table[0][i] = 0; table[1][i] = i; } for (; i < (1< 0) ; if (count != 0) return -2; } incode = code; if (code >= max_code) { *sp++ = firstcode; code = oldcode; } while (code >= clear_code) { *sp++ = table[1][code]; if (code == table[0][code]) { /* Oh well */ } code = table[0][code]; } *sp++ = firstcode = table[1][code]; if ((code = max_code) <(1<= max_code_size) && (max_code_size < (1< stack) return *--sp; } return code; } static void ReadImage(gdImagePtr im, FILE *fd, int len, int height, unsigned char (*cmap)[256], int interlace, int ignore) { unsigned char c; int v; int xpos = 0, ypos = 0, pass = 0; int i; /* Stash the color map into the image */ for (i=0; (ired[i] = cmap[CM_RED][i]; im->green[i] = cmap[CM_GREEN][i]; im->blue[i] = cmap[CM_BLUE][i]; im->open[i] = 1; } /* Many (perhaps most) of these colors will remain marked open. */ im->colorsTotal = gdMaxColors; /* ** Initialize the Compression routines */ if (! ReadOK(fd,&c,1)) { return; } if (LWZReadByte(fd, TRUE, c) < 0) { return; } /* ** If this is an "uninteresting picture" ignore it. */ if (ignore) { while (LWZReadByte(fd, FALSE, c) >= 0) ; return; } while ((v = LWZReadByte(fd,FALSE,c)) >= 0 ) { /* This how we recognize which colors are actually used. */ if (im->open[v]) { im->open[v] = 0; } gdImageSetPixel(im, xpos, ypos, v); ++xpos; if (xpos == len) { xpos = 0; if (interlace) { switch (pass) { case 0: case 1: ypos += 8; break; case 2: ypos += 4; break; case 3: ypos += 2; break; } if (ypos >= height) { ++pass; switch (pass) { case 1: ypos = 4; break; case 2: ypos = 2; break; case 3: ypos = 1; break; default: goto fini; } } } else { ++ypos; } } if (ypos >= height) break; } fini: if (LWZReadByte(fd,FALSE,c)>=0) { /* Ignore extra */ } } void gdImageRectangle(gdImagePtr im, int x1, int y1, int x2, int y2, int color) { gdImageLine(im, x1, y1, x2, y1, color); gdImageLine(im, x1, y2, x2, y2, color); gdImageLine(im, x1, y1, x1, y2, color); gdImageLine(im, x2, y1, x2, y2, color); } void gdImageFilledRectangle(gdImagePtr im, int x1, int y1, int x2, int y2, int color) { int x, y; for (y=y1; (y<=y2); y++) { for (x=x1; (x<=x2); x++) { gdImageSetPixel(im, x, y, color); } } } void gdImageCopy(gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int w, int h) { int c; int x, y; int tox, toy; int i; int colorMap[gdMaxColors]; for (i=0; (ired[c], src->green[c], src->blue[c]); } if (nc == (-1)) { /* No, so try to allocate it */ nc = gdImageColorAllocate(dst, src->red[c], src->green[c], src->blue[c]); /* If we're out of colors, go for the closest color */ if (nc == (-1)) { nc = gdImageColorClosest(dst, src->red[c], src->green[c], src->blue[c]); } } colorMap[c] = nc; } gdImageSetPixel(dst, tox, toy, colorMap[c]); tox++; } toy++; } } void gdImageCopyResized(gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int dstW, int dstH, int srcW, int srcH) { int c; int x, y; int tox, toy; int ydest; int i; int colorMap[gdMaxColors]; /* Stretch vectors */ int *stx; int *sty; /* We only need to use floating point to determine the correct stretch vector for one line's worth. */ double accum; stx = (int *) malloc(sizeof(int) * srcW); sty = (int *) malloc(sizeof(int) * srcH); accum = 0; for (i=0; (i < srcW); i++) { int got; accum += (double)dstW/(double)srcW; got = floor(accum); stx[i] = got; accum -= got; } accum = 0; for (i=0; (i < srcH); i++) { int got; accum += (double)dstH/(double)srcH; got = floor(accum); sty[i] = got; accum -= got; } for (i=0; (ired[c], src->green[c], src->blue[c]); } if (nc == (-1)) { /* No, so try to allocate it */ nc = gdImageColorAllocate(dst, src->red[c], src->green[c], src->blue[c]); /* If we're out of colors, go for the closest color */ if (nc == (-1)) { nc = gdImageColorClosest(dst, src->red[c], src->green[c], src->blue[c]); } } colorMap[c] = nc; } for (i=0; (i < stx[x - srcX]); i++) { gdImageSetPixel(dst, tox, toy, colorMap[c]); tox++; } } toy++; } } free(stx); free(sty); } int gdGetWord(int *result, FILE *in) { int r; r = getc(in); if (r == EOF) { return 0; } *result = r << 8; r = getc(in); if (r == EOF) { return 0; } *result += r; return 1; } void gdPutWord(int w, dynamicPtr *dp) { Putchar((unsigned char)(w >> 8), dp); Putchar((unsigned char)(w & 0xFF), dp); } int gdGetByte(int *result, FILE *in) { int r; r = getc(in); if (r == EOF) { return 0; } *result = r; return 1; } gdImagePtr gdImageCreateFromGd(FILE *in) { int sx, sy; int x, y; int i; gdImagePtr im; if (!gdGetWord(&sx, in)) { goto fail1; } if (!gdGetWord(&sy, in)) { goto fail1; } im = gdImageCreate(sx, sy); if (!gdGetByte(&im->colorsTotal, in)) { goto fail2; } if (!gdGetWord(&im->transparent, in)) { goto fail2; } if (im->transparent == 257) { im->transparent = (-1); } for (i=0; (ired[i], in)) { goto fail2; } if (!gdGetByte(&im->green[i], in)) { goto fail2; } if (!gdGetByte(&im->blue[i], in)) { goto fail2; } } for (y=0; (ypixels[x][y] = ch; } } return im; fail2: gdImageDestroy(im); fail1: return 0; } void* gdImageGdPtr(gdImagePtr im, int* size) { int x, y; int i; int trans; dynamicPtr* dp; void* data; *size = 0; /* create the dynamic pointer */ dp = (dynamicPtr*) malloc(sizeof(dynamicPtr)); if (dp == NULL) { return NULL; } if (!allocDynamic(dp,1024)) return NULL; gdPutWord(im->sx, dp); gdPutWord(im->sy, dp); Putchar((unsigned char)im->colorsTotal, dp); trans = im->transparent; if (trans == (-1)) { trans = 257; } gdPutWord(trans, dp); for (i=0; (ired[i], dp); Putchar((unsigned char)im->green[i], dp); Putchar((unsigned char)im->blue[i], dp); } for (y=0; (y < im->sy); y++) { for (x=0; (x < im->sx); x++) { Putchar((unsigned char)im->pixels[x][y], dp); } } /* clean up the data block and return it */ if (dp->dataGood) { trimDynamic(dp); *size = dp->logicalSize; data = dp->data; } else { data = NULL; } free(dp); return data; } void gdImageGd(gdImagePtr im, FILE *out) { void* data; int size; data = gdImageGdPtr(im,&size); if (data != NULL) fwrite(data,1,size,out); free(data); } #ifdef USE_SFIO #undef fgets #define fgets(s,n,f) myfgets(s,n,f) char * myfgets(char *s, int n, FILE *fd) { char *p = s; while ((n > 0) && !PerlIO_eof(fd)) { *s = PerlIO_getc(fd); if ((*s == '\n') || (*s == '\0')) break; s++; } if (s != p) return(p); else return NULL; } #endif gdImagePtr gdImageCreateFromXbm(FILE *fd) { gdImagePtr im; int bit; int w, h; int bytes; int ch; int i, x, y; char *sp; char s[161]; if (!fgets(s, 160, fd)) { return 0; } sp = &s[0]; /* Skip #define */ sp = strchr(sp, ' '); if (!sp) { return 0; } /* Skip width label */ sp++; sp = strchr(sp, ' '); if (!sp) { return 0; } /* Get width */ w = atoi(sp + 1); if (!w) { return 0; } if (!fgets(s, 160, fd)) { return 0; } sp = s; /* Skip #define */ sp = strchr(sp, ' '); if (!sp) { return 0; } /* Skip height label */ sp++; sp = strchr(sp, ' '); if (!sp) { return 0; } /* Get height */ h = atoi(sp + 1); if (!h) { return 0; } /* Skip declaration line */ if (!fgets(s, 160, fd)) { return 0; } bytes = (w * h / 8) + 1; im = gdImageCreate(w, h); gdImageColorAllocate(im, 255, 255, 255); gdImageColorAllocate(im, 0, 0, 0); x = 0; y = 0; for (i=0; (i < bytes); i++) { char h[3]; int b; /* Skip spaces, commas, CRs, 0x */ while(1) { ch = getc(fd); if (ch == EOF) { goto fail; } if (ch == 'x') { break; } } /* Get hex value */ ch = getc(fd); if (ch == EOF) { goto fail; } h[0] = ch; ch = getc(fd); if (ch == EOF) { goto fail; } h[1] = ch; h[2] = '\0'; sscanf(h, "%x", &b); for (bit = 1; (bit <= 128); (bit = bit << 1)) { gdImageSetPixel(im, x++, y, (b & bit) ? 1 : 0); if (x == im->sx) { x = 0; y++; if (y == im->sy) { return im; } /* Fix 8/8/95 */ break; } } } /* Shouldn't happen */ fprintf(stderr, "Error: bug in gdImageCreateFromXbm!\n"); return 0; fail: gdImageDestroy(im); return 0; } void gdImagePolygon(gdImagePtr im, gdPointPtr p, int n, int c) { if (!n) { return; } gdImageLine(im, p->x, p->y, p[n-1].x, p[n-1].y, c); gdImageOpenPolygon(im, p, n, c); } void gdImageOpenPolygon(gdImagePtr im, gdPointPtr p, int n, int c) { int i; int lx, ly; if (!n) { return; } lx = p->x; ly = p->y; for (i=1; (i < n); i++) { p++; gdImageLine(im, lx, ly, p->x, p->y, c); lx = p->x; ly = p->y; } } int gdCompareInt(const void *a, const void *b); void gdImageFilledPolygon(gdImagePtr im, gdPointPtr p, int n, int c) { int i; int y; int y1, y2; int ints; if (!n) { return; } if (!im->polyAllocated) { im->polyInts = (int *) malloc(sizeof(int) * n); im->polyAllocated = n; } if (im->polyAllocated < n) { while (im->polyAllocated < n) { im->polyAllocated *= 2; } im->polyInts = (int *) realloc(im->polyInts, sizeof(int) * im->polyAllocated); } y1 = p[0].y; y2 = p[0].y; for (i=1; (i < n); i++) { if (p[i].y > y2) { y2 = p[i].y; } else if (p[i].y < y1) { y1 = p[i].y; } } for (y=y1; (y <= y2); y++) { int dirLast = 0; ints = 0; if (p[0].y == y) { i = n; while (dirLast == 0 && i--) { if (p[i].y > y) dirLast = 1; else if (p[i].y < y) dirLast = -1; } } for (i=0; (i < n); i++) { int x1, x2; int y1, y2; int dir; int i2 = i+1; if (i2 == n) i2 = 0; y1 = p[i].y; y2 = p[i2].y; if (y1 < y2) { x1 = p[i].x; x2 = p[i2].x; dir = -1; } else if (y1 > y2) { y2 = p[i].y; y1 = p[i2].y; x2 = p[i].x; x1 = p[i2].x; dir = 1; } else { /* Horizontal; just draw it */ gdImageLine(im, p[i].x, y1, p[i2].x, y1, c); continue; } if ((y >= y1) && (y <= y2)) { int inter = (y-y1) * (x2-x1) / (y2-y1) + x1; /* Only count intersections once except at maxima and minima. */ if ((y == p[i].y) && (dir == dirLast)) continue; im->polyInts[ints++] = inter; dirLast = dir; } } qsort(im->polyInts, ints, sizeof(int), gdCompareInt); for (i=0; (i < (ints-1)); i+=2) { gdImageLine(im, im->polyInts[i], y, im->polyInts[i+1], y, c); } } } int gdCompareInt(const void *a, const void *b) { return (*(const int *)a) - (*(const int *)b); } void gdImageSetStyle(gdImagePtr im, int *style, int noOfPixels) { if (im->style) { free(im->style); } im->style = (int *) malloc(sizeof(int) * noOfPixels); memcpy(im->style, style, sizeof(int) * noOfPixels); im->styleLength = noOfPixels; im->stylePos = 0; } void gdImageSetBrush(gdImagePtr im, gdImagePtr brush) { int i; im->brush = brush; for (i=0; (i < gdImageColorsTotal(brush)); i++) { int index; index = gdImageColorExact(im, gdImageRed(brush, i), gdImageGreen(brush, i), gdImageBlue(brush, i)); if (index == (-1)) { index = gdImageColorAllocate(im, gdImageRed(brush, i), gdImageGreen(brush, i), gdImageBlue(brush, i)); if (index == (-1)) { index = gdImageColorClosest(im, gdImageRed(brush, i), gdImageGreen(brush, i), gdImageBlue(brush, i)); } } im->brushColorMap[i] = index; } } void gdImageSetTile(gdImagePtr im, gdImagePtr tile) { int i; im->tile = tile; for (i=0; (i < gdImageColorsTotal(tile)); i++) { int index; index = gdImageColorExact(im, gdImageRed(tile, i), gdImageGreen(tile, i), gdImageBlue(tile, i)); if (index == (-1)) { index = gdImageColorAllocate(im, gdImageRed(tile, i), gdImageGreen(tile, i), gdImageBlue(tile, i)); if (index == (-1)) { index = gdImageColorClosest(im, gdImageRed(tile, i), gdImageGreen(tile, i), gdImageBlue(tile, i)); } } im->tileColorMap[i] = index; } } void gdImageInterlace(gdImagePtr im, int interlaceArg) { im->interlace = interlaceArg; } /* ********************************************************************* * * InitDynamic - Return a dynamically resizable void* * * ********************************************************************* */ static int allocDynamic (dynamicPtr* dp,int initialSize) { dp->logicalSize = 0; dp->dataGood = FALSE; dp->data = malloc(initialSize); if (dp->data !=NULL) { dp->realSize = initialSize; dp->dataGood = TRUE; return TRUE; } else { dp->realSize = 0; return FALSE; } } /* append bytes to the end of a dynamic pointer */ static int appendDynamic (dynamicPtr* dp, const void* src, int size) { int bytesNeeded; char* tmp; if (!dp->dataGood) return FALSE; bytesNeeded = dp->logicalSize + size; if (bytesNeeded > dp->realSize) { if (!reallocDynamic(dp,dp->realSize*2)) { dp->dataGood = FALSE; return FALSE; } } /* if we get here, we can be sure that we have enough bytes to copy safely */ tmp = (char*)dp->data; memcpy((void*)(tmp+dp->logicalSize),src,size); dp->logicalSize += size; return TRUE; } /* grow (or shrink) dynamic pointer */ static int reallocDynamic (dynamicPtr* dp, int required) { void* newPtr; /* First try realloc(). If that doesn't work, make a new memory block and copy. */ if ((newPtr = realloc(dp->data,required))) { dp->realSize = required; dp->data = newPtr; return TRUE; } /* create a new pointer */ newPtr = malloc(required); if (!newPtr) { dp->dataGood = FALSE; return FALSE; } /* copy the old data into it */ memcpy(newPtr,dp->data,dp->logicalSize); free(dp->data); dp->data = newPtr; dp->realSize = required; return TRUE; } /* trim pointer so that its real and logical sizes match */ static int trimDynamic (dynamicPtr* dp) { return reallocDynamic(dp,dp->logicalSize); } /* dispose of the dynamic pointer */ static void freeDynamic (dynamicPtr* dp) { if (dp->data != NULL) { free(dp->data); dp->data = NULL; } dp->realSize=0; dp->logicalSize=0; }