File:  [parser3project] / parser3 / src / classes / gd / Attic / gif.C
Revision 1.4: download - view: text, annotated - select for diffs - revision graph
Thu Apr 12 14:07:34 2001 UTC (25 years, 3 months ago) by paf
Branches: MAIN
CVS tags: HEAD
image:line/fill/rectangle/bar/replace/polygon/polybar

/** @file
	Parser: image manipulations impl1.

	Author: Alexander Petrosyan <paf@design.ru> (http://design.ru/paf)

	$Id: gif.C,v 1.4 2001/04/12 14:07:34 paf Exp $
*/

#include <string.h>
#include <stdlib.h>
#include <math.h>

#include "gif.h"

//static void BrushApply(int x, int y);
//static void TileApply(int x, int y);

void gdImage::Create(int asx, int asy) {
	sx = asx;
	sy = asy;

	int i;
	pixels = (unsigned char **) malloc(sizeof(unsigned char *) * sx);
	polyInts = 0;
	polyAllocated = 0;
	styleWidth = 1;
	for (i=0; (i<asx); i++)
		pixels[i] = (unsigned char *) calloc(asy);
	colorsTotal = 0;
	transparent = (-1);
	interlace = 0;
}

int gdImage::ColorClosest(int r, int g, int b)
{
	int i;
	long rd, gd, bd;
	int ct = (-1);
	long mindist = 0;
	for (i=0; (i<(colorsTotal)); i++) {
		long dist;
		if (open[i]) {
			continue;
		}
		rd = (red[i] - r);	
		gd = (green[i] - g);
		bd = (blue[i] - b);
		dist = rd * rd + gd * gd + bd * bd;
		if ((i == 0) || (dist < mindist)) {
			mindist = dist;	
			ct = i;
		}
	}
	return ct;
}

int gdImage::ColorExact(int r, int g, int b)
{
	int i;
	for (i=0; (i<(colorsTotal)); i++) {
		if (open[i]) {
			continue;
		}
		if ((red[i] == r) && 
			(green[i] == g) &&
			(blue[i] == b)) {
			return i;
		}
	}
	return -1;
}

int gdImage::ColorAllocate(int r, int g, int b)
{
	int i;
	int ct = (-1);
	for (i=0; (i<(colorsTotal)); i++) {
		if (open[i]) {
			ct = i;
			break;
		}
	}	
	if (ct == (-1)) {
		ct = colorsTotal;
		if (ct == gdMaxColors) {
			return -1;
		}
		colorsTotal++;
	}
	red[ct] = r;
	green[ct] = g;
	blue[ct] = b;
	open[ct] = 0;
	return ct;
}

int gdImage::ColorRGB(int r, int g, int b){
    int idx=ColorExact(r,g,b);
    return idx<0 ? ColorAllocate(r,g,b) : idx;
}

int gdImage::Color(unsigned int rgb){
    unsigned int b=rgb, g=b>>8, r=g>>8;
    return ColorRGB(r & 0xFF,g & 0xFF,b & 0xFF);
}

void gdImage::ColorDeallocate(int color)
{
	/* Mark it open. */
	open[color] = 1;
}

void gdImage::SetColorTransparent(int color)
{
	transparent = color;
}


void gdImage::SetPixel(int x, int y, int color)
{
//paf	int p;

	switch (styleWidth){
	    case 1: {
		DoSetPixel(x, y,color);
		return;
	    }
	    case 2: {
		DoSetPixel(x, y-1,color);
		DoSetPixel(x-1, y,color);
		DoSetPixel(x, y,color);
		DoSetPixel(x+1, y,color);
		DoSetPixel(x, y+1,color);
		return;
	    }
	    default:{
		int i,j;
		for (i=-1;i<=1;i++) DoSetPixel(x+i, y-2,color);
		for (j=-1;j<=1;j++) for (i=-2;i<=2;i++) DoSetPixel(x+i, y+j,color);
		for (i=-1;i<=1;i++) DoSetPixel(x+i, y+2,color);
		return;
	    }
	}
}

int gdImage::GetPixel(int x, int y)
{
	return BoundsSafe(x, y) ? pixels[x][y]:0;
}

/* Bresenham as presented in Foley & Van Dam */

void gdImage::Line(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;
		}
		SetPixel(x, y, color);
		if (((y2 - y1) * ydirflag) > 0) {
			while (x < xend) {
				x++;
				if (d <0) {
					d+=incr1;
				} else {
					y++;
					d+=incr2;
				}
				SetPixel(x, y, color);
			}
		} else {
			while (x < xend) {
				x++;
				if (d <0) {
					d+=incr1;
				} else {
					y--;
					d+=incr2;
				}
				SetPixel(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;
		}
		SetPixel(x, y, color);
		if (((x2 - x1) * xdirflag) > 0) {
			while (y < yend) {
				y++;
				if (d <0) {
					d+=incr1;
				} else {
					x++;
					d+=incr2;
				}
				SetPixel(x, y, color);
			}
		} else {
			while (y < yend) {
				y++;
				if (d <0) {
					d+=incr1;
				} else {
					x--;
					d+=incr2;
				}
				SetPixel(x, y, color);
			}
		}
	}
}

/* As above, plus dashing */

#define dashedSet \
	{ \
		dashStep++; \
		if (dashStep == gdDashSize) { \
			dashStep = 0; \
			on = !on; \
		} \
		if (on) { \
			SetPixel(x, y, color); \
		} \
	}

void gdImage::DashedLine(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;
			}
		}
	}
}

/* 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 gdImage::FillToBorder(int x, int y, int border, int color)
{
	if(!BoundsSafe(x, y)) //PAF
		return;

	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 (GetPixel(i, y) == border) {
			break;
		}
		SetPixel(i, y, color);
		leftLimit = i;
	}
	if (leftLimit == (-1)) {
		return;
	}
	/* Seek right */
	rightLimit = x;
	for (i = (x+1); (i < sx); i++) {	
		if (GetPixel(i, y) == border) {
			break;
		}
		SetPixel(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 = GetPixel(i, y-1);
			if (lastBorder) {
				if ((c != border) && (c != color)) {	
					FillToBorder(i, y-1, 
						border, color);		
					lastBorder = 0;
				}
			} else if ((c == border) || (c == color)) {
				lastBorder = 1;
			}
		}
	}
	/* Below */
	if (y < ((sy) - 1)) {
		lastBorder = 1;
		for (i = leftLimit; (i <= rightLimit); i++) {
			int c;
			c = GetPixel(i, y+1);
			if (lastBorder) {
				if ((c != border) && (c != color)) {	
					FillToBorder(i, y+1, 
						border, color);		
					lastBorder = 0;
				}
			} else if ((c == border) || (c == color)) {
				lastBorder = 1;
			}
		}
	}
}

void gdImage::Fill(int x, int y, int color)
{
	if(!BoundsSafe(x, y)) //PAF
		return;

	int lastBorder;
	int old;
	int leftLimit, rightLimit;
	int i;
	old = GetPixel(x, y);
	if (old == color) {
		/* Nothing to be done */
		return;
	}
	/* Seek left */
	leftLimit = (-1);
	for (i = x; (i >= 0); i--) {
		if (GetPixel(i, y) != old) {
			break;
		}
		SetPixel(i, y, color);
		leftLimit = i;
	}
	if (leftLimit == (-1)) {
		return;
	}
	/* Seek right */
	rightLimit = x;
	for (i = (x+1); (i < sx); i++) {	
		if (GetPixel(i, y) != old) {
			break;
		}
		SetPixel(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 = GetPixel(i, y-1);
			if (lastBorder) {
				if (c == old) {	
					Fill(i, y-1, color);		
					lastBorder = 0;
				}
			} else if (c != old) {
				lastBorder = 1;
			}
		}
	}
	/* Below */
	if (y < ((sy) - 1)) {
		lastBorder = 1;
		for (i = leftLimit; (i <= rightLimit); i++) {
			int c;
			c = GetPixel(i, y+1);
			if (lastBorder) {
				if (c == old) {
					Fill(i, y+1, color);		
					lastBorder = 0;
				}
			} else if (c != old) {
				lastBorder = 1;
			}
		}
	}
}
	
void gdImage::Rectangle(int x1, int y1, int x2, int y2, int color)
{
	Line(x1, y1, x2, y1, color);		
	Line(x1, y2, x2, y2, color);		
	Line(x1, y1, x1, y2, color);
	Line(x2, y1, x2, y2, color);
}

void gdImage::FilledRectangle(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++)
			SetPixel(x, y, color);
}

void gdImage::Copy(gdImage& dst, 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; (i<gdMaxColors); i++) {
		colorMap[i] = (-1);
	}
	toy = dstY;
	for (y=srcY; (y < (srcY + h)); y++) {
		tox = dstX;
		for (x=srcX; (x < (srcX + w)); x++) {
			int nc;
			c = GetPixel(x, y);
			/* Added 7/24/95: support transparent copies */
			if (GetTransparent() == c) {
				tox++;
				continue;
			}
			/* Have we established a mapping for this color? */
			if (colorMap[c] == (-1)) {
				/* If it's the same image, mapping is trivial */
				if (&dst == this) {
					nc = c;
				} else { 
					/* First look for an exact match */
					nc = dst.ColorExact(
						red[c], green[c],
						blue[c]);
				}	
				if (nc == (-1)) {
					/* No, so try to allocate it */
					nc = dst.ColorAllocate(
						red[c], green[c],
						blue[c]);
					/* If we're out of colors, go for the
						closest color */
					if (nc == (-1)) {
						nc = dst.ColorClosest(
							red[c], green[c],
							blue[c]);
					}
				}
				colorMap[c] = nc;
			}
			dst.SetPixel(tox, toy, colorMap[c]);
			tox++;
		}
		toy++;
	}
}			

static 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;
}

static void gdPutWord(int w, FILE *out)
{
	putc((unsigned char)(w >> 8), out);
	putc((unsigned char)(w & 0xFF), out);
}

static int gdGetByte(int *result, FILE *in)
{
	int r;
	r = getc(in);
	if (r == EOF) {
		return 0;
	}
	*result = r;
	return 1;
}

void gdImage::Polygon(Point *p, int n, int c)
{
	int i;
	int lx, ly;
	if (!n) {
		return;
	}
	lx = p->x;
	ly = p->y;
	Line(lx, ly, p[n-1].x, p[n-1].y, c);
	for (i=1; (i < n); i++) {
		p++;
		Line(lx, ly, p->x, p->y, c);
		lx = p->x;
		ly = p->y;
	}
}	
	
int gdCompareInt(const void *a, const void *b);
	
void gdImage::FilledPolygon(Point *p, int n, int c)
{
	int i;
	int y;
	int y1, y2;
	int ints;
	if (!n) {
		return;
	}
	if (!polyAllocated) {
		polyInts = (int *) malloc(sizeof(int) * n);
		polyAllocated = n;
	}		
	if (polyAllocated < n) {
		while (polyAllocated < n) {
			polyAllocated *= 2;
		}	
		polyInts = (int *) realloc(polyInts,
			sizeof(int) * polyAllocated);
	}
	y1 = p[0].y;
	y2 = p[0].y;
	for (i=1; (i < n); i++) {
		if (p[i].y < y1) {
			y1 = p[i].y;
		}
		if (p[i].y > y2) {
			y2 = p[i].y;
		}
	}
	for (y=y1; (y <= y2); y++) {
		int interLast = 0;
		int dirLast = 0;
		int interFirst = 1;
		ints = 0;
		for (i=0; (i <= n); i++) {
			int x1, x2;
			int y1, y2;
			int dir;
			int ind1, ind2;
			int lastInd1 = 0;
			if ((i == n) || (!i)) {
				ind1 = n-1;
				ind2 = 0;
			} else {
				ind1 = i-1;
				ind2 = i;
			}
			y1 = p[ind1].y;
			y2 = p[ind2].y;
			if (y1 < y2) {
				y1 = p[ind1].y;
				y2 = p[ind2].y;
				x1 = p[ind1].x;
				x2 = p[ind2].x;
				dir = -1;
			} else if (y1 > y2) {
				y2 = p[ind1].y;
				y1 = p[ind2].y;
				x2 = p[ind1].x;
				x1 = p[ind2].x;
				dir = 1;
			} else {
				/* Horizontal; just draw it */
				Line( 
					p[ind1].x, y1, 
					p[ind2].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. Also, 
					if two consecutive intersections are
					endpoints of the same horizontal line
					that is not at a maxima or minima,	
					discard the leftmost of the two. */
				if (!interFirst) {
					if ((p[ind1].y == p[lastInd1].y) &&
						(p[ind1].x != p[lastInd1].x)) {
						if (dir == dirLast) {
							if (inter > interLast) {
								/* Replace the old one */
								polyInts[ints] = inter;
							} else {
								/* Discard this one */
							}	
							continue;
						}
					}
					if (inter == interLast) {
						if (dir == dirLast) {
							continue;
						}
					}
				} 
				if (i > 0) {
					polyInts[ints++] = inter;
				}
				lastInd1 = i;
				dirLast = dir;
				interLast = inter;
				interFirst = 0;
			}
		}
		qsort(polyInts, ints, sizeof(int), gdCompareInt);
		for (i=0; (i < (ints-1)); i+=2)
			Line(polyInts[i], y, polyInts[i+1], y, c);
	}
}

//001005paf this used in drawing straight lines in gdImage::FilledPolygonReplaceColor
void gdImage::LineReplaceColor(int x1, int y1, int x2, int y2, int a, int b) {
	if(y1!=y2)
		return;

	for(int x=x1; x<=x2; x++) {
		unsigned char *pixel=&pixels[x][y1];
		if(*pixel==a)
			*pixel=b;
	}
}

void gdImage::FilledPolygonReplaceColor(Point *p, int n, int a, int b)
{
	int i;
	int y;
	int y1, y2;
	int ints;
	if (!n) {
		return;
	}
	if (!polyAllocated) {
		polyInts = (int *) malloc(sizeof(int) * n);
		polyAllocated = n;
	}		
	if (polyAllocated < n) {
		while (polyAllocated < n) {
			polyAllocated *= 2;
		}	
		polyInts = (int *) realloc(polyInts,
			sizeof(int) * polyAllocated);
	}
	y1 = p[0].y;
	y2 = p[0].y;
	for (i=1; (i < n); i++) {
		if (p[i].y < y1) {
			y1 = p[i].y;
		}
		if (p[i].y > y2) {
			y2 = p[i].y;
		}
	}
	for (y=y1; (y <= y2); y++) {
		int interLast = 0;
		int dirLast = 0;
		int interFirst = 1;
		ints = 0;
		for (i=0; (i <= n); i++) {
			int x1, x2;
			int y1, y2;
			int dir;
			int ind1, ind2;
			int lastInd1 = 0;
			if ((i == n) || (!i)) {
				ind1 = n-1;
				ind2 = 0;
			} else {
				ind1 = i-1;
				ind2 = i;
			}
			y1 = p[ind1].y;
			y2 = p[ind2].y;
			if (y1 < y2) {
				y1 = p[ind1].y;
				y2 = p[ind2].y;
				x1 = p[ind1].x;
				x2 = p[ind2].x;
				dir = -1;
			} else if (y1 > y2) {
				y2 = p[ind1].y;
				y1 = p[ind2].y;
				x2 = p[ind1].x;
				x1 = p[ind2].x;
				dir = 1;
			} else {
				/* Horizontal; just draw it */
				LineReplaceColor( 
					p[ind1].x, y1, 
					p[ind2].x, y1,
					a,b);
				continue;
			}
			if ((y >= y1) && (y <= y2)) {
				int inter = 
					(y-y1) * (x2-x1) / (y2-y1) + x1;
				/* Only count intersections once
					except at maxima and minima. Also, 
					if two consecutive intersections are
					endpoints of the same horizontal line
					that is not at a maxima or minima,	
					discard the leftmost of the two. */
				if (!interFirst) {
					if ((p[ind1].y == p[lastInd1].y) &&
						(p[ind1].x != p[lastInd1].x)) {
						if (dir == dirLast) {
							if (inter > interLast) {
								/* Replace the old one */
								polyInts[ints] = inter;
							} else {
								/* Discard this one */
							}	
							continue;
						}
					}
					if (inter == interLast) {
						if (dir == dirLast) {
							continue;
						}
					}
				} 
				if (i > 0) {
					polyInts[ints++] = inter;
				}
				lastInd1 = i;
				dirLast = dir;
				interLast = inter;
				interFirst = 0;
			}
		}
		qsort(polyInts, ints, sizeof(int), gdCompareInt);
		for (i=0; (i < (ints-1)); i+=2) {
			LineReplaceColor(polyInts[i], y,
				polyInts[i+1], y, a,b);
		}
	}
}

static int gdCompareInt(const void *a, const void *b)
{
	return (*(const int *)a) - (*(const int *)b);
}

void gdImage::SetInterlace(int interlaceArg)
{
	interlace = interlaceArg;
}

void gdImage::SetStyle(int width)
{
	styleWidth=width;
}


E-mail: