Files
Arquivotheca.SunOS-4.1.4/usr.lib/libpixrect/pr/pr_polygon2.c
seta75D ff309bfe1c Init
2021-10-11 18:37:13 -03:00

496 lines
14 KiB
C

#ifndef lint
static char sccsid [] = "@(#)pr_polygon2.c 1.1 94/10/31 SMI";
#endif
/*
* Copyright 1986 by Sun Microsystems, Inc.
*/
/*
* pr_polygon_2 (dpr, dx, dy, nbnds, npts, vlist, op, spr, sx, sy)
* Scan convert a 2-D polygon. Polygon may have multiple boundaries (holes)
* and may be filled with a texture.
*/
#include <sys/types.h>
#include <pixrect/pixrect.h>
#include <pixrect/gp1var.h>
/* Plan: change vector code to draw downwards and eliminate this option. */
#define VECSDRAWTORIGHT
#define swap(a,b,tmp) ((tmp) = (a), (a) = (b), (b) = (tmp))
struct edge { /* polygon edge data structure */
short ymn, ymx; /* lower, upper y of edge */
short xmn, dx; /* x start, delta x for edge */
short errx, erry; /* error x and y for edge. */
short error; /* breshenham err accumulator. */
struct edge *nxt;
};
static Pixrect *mpr; /* memory pixrect for band */
pr_polygon_2 (dpr, dx, dy, nbnds, npts, vlist, op, spr, sx, sy)
struct pixrect *dpr; /* destination pixrect */
int dx, dy; /* dest pixrect offset position */
int nbnds; /* number of closed boundaries */
int npts []; /* number of points in each boundary */
struct pr_pos *vlist; /* list of points in all boundaries */
int op; /* pixrect op to use in painting */
struct pixrect *spr; /* source pixrect */
int sx, sy; /* source pixrect offset position */
{
/*
* Fill a polygon which may contain holes. The polygon is defined
* by nbnds boundaries. Each boundary is a list of 2-D points,
* where npts[i] is the number of points in the ith boundary. This
* routine joins the last point and the first point to close each
* boundary. See Foley and Van Dam "Fundamentals Of Interactive
* Computer Graphics" pg 459 for algorithm description. Pixels on
* the boundary are painted if at the left edge but not painted if
* at the right edge. Thus polygons fit together with no overlap
* of pixels and no gaps between their edges.
*
* Note: Very thin polygons and zero width bridges will disappear,
* since we are not doing any antialiasing.
*
* Polygons are scanned downwards. Edge stepping code is designed to
* match code used in vector drawing. Unlike vector code, it is not
* necessary for special bresenham error balancing if edge is clipped
* since clipping occurs in pr_rop which paints scanlines.
*
* Octants are the four octants below the horizontal and are numbered
* clockwise.
*
* We no longer clip to the bounds of a source pixrect.
* Patterns (non-null source pixrects) are replicated in the X direction
* until they are wide enough that a rop can apply entire scanlines
* to the destination pixrect.
*/
struct edge aet[1]; /* active edge table */
char *malloc();
int gp1_rop();
int cg6_rop();
struct pixrect *mem_create();
struct edge *et; /* edge table. */
int soffset, spr_height; /* needed only for textured polygons (spr!=0) */
/* The scanline loop moves sy & uses the above, but that doesn't matter. */
/* check for illegal size spr */
if (spr && (spr->pr_size.x <= 0 || spr->pr_size.y <= 0))
return PIX_ERR;
/*
* Use the LEGO if applicable. If LEGO routine fails, soldier on in software.
* If LEGO succeeds, return 0.
*/
if (dpr->pr_ops->pro_rop == cg6_rop) {
if (!cg6_polygon_2 (dpr, dx, dy, nbnds, npts, vlist, op, spr, sx, sy))
return 0;
}else
/*
* Use the GP if applicable. If GP routine fails, soldier on in software.
* If GP succeeds, return 0.
*/
if (dpr->pr_ops->pro_rop == gp1_rop &&
gp1_d (dpr)->fbtype == FBTYPE_SUN2COLOR &&
!gp1_polygon_2 (dpr, dx, dy, nbnds, npts, vlist, op, spr, sx, sy))
return 0;
{register int i, x1, x2;
register struct edge *pet;
int n;
struct pr_pos *vp; /* vertex pointer. */
/*
* Set up the edge table.
* Also glean overall minimums and maximums for textured polygons.
*/
x1 = 1; /* Use register for total number of edges (+ first is dummy) */
for (i = 0; i < nbnds; i++) /* alloc edge table */
x1 += npts [i];
et = (struct edge *) malloc ((unsigned) x1 * sizeof (*et));
if (!et) {
return PIX_ERR;
}
pet = et;
pet->nxt = &et [1]; /* dummy 1st edge points to nxt */
x1 = x2 = dx + vlist->x;
for (n = 0; n < nbnds; n++) { /* for each boundary of polygon */
int startx = dx + vlist->x;
if (startx < x1)
x1 = startx;
else if (startx > x2)
x2 = startx;
vp = vlist; /* save vertex list start */
if (npts [n] < 3) {
free ((char *) et);
return PIX_ERR;
}
for (i = 1; i < npts [n]; i++) { /* put boundary in edge tbl. */
int endx = dx + (vlist+1)->x;
if(endx < x1) x1 = endx;
else if(endx > x2) x2 = endx;
if (vlist->y != (vlist+1)->y) { /* skip horiz edges */
pet++; /* put edge in edge table */
pet->xmn = dx + vlist->x; /* start x */
pet->dx = endx; /* end x */
pet->ymn = dy + vlist->y; /* start y */
pet->ymx = dy + (vlist+1)->y; /* end y */
pet->nxt = pet + 1;
}
vlist++;
}
/* Add edge from last boundary point to first boundary point */
if (vlist->y != vp->y) { /* Skip horiz edges */
pet++; /* Wrap around to first point */
pet->xmn = dx + vlist->x; /* start x */
pet->ymn = dy + vlist->y; /* start y */
pet->dx = vp->x + dx; /* end x */
pet->ymx = vp->y + dy; /* end y */
pet->nxt = pet+1;
}
vlist++;
}
pet->nxt = 0;
/* x1 & x2 are min and max X coords of vertices on dpr */
/*
* For textured polygons, need a "band" as high and deep as spr
* and as wide as the portion of the polygon within dpr.
*/
if (spr) {
int width_needed, minx, maxx;
/*
* Determine needed size for spr so each scanline can merely rop
*/
/* Using pixrect bounds even if PIX_DONTCLIP. Slower without it. */
/* (WRONG!!! Should be fixed to obey DONTCLIP.) */
minx = (x1 < 0)? 0 : x1;
maxx = (x2 >= dpr->pr_size.x)? dpr-> pr_size.x-1 : x2;
/* x1 & x2 have values which are no longer needed */
if( (minx >= dpr->pr_size.x) || (maxx < 0) ) {
free ((char *) et); /* No portion of this figure is within dpr */
return 0; /* Need not observe PIX_DONTCLIP, faster not to */
}
width_needed = maxx - minx; /* width = last - first + 1 */
/* might want to subract 1 from maxx, as well */
spr_height = spr->pr_size.y;
/* spr big enough minx & maxx are within aligned source pixrect? */
if(spr->pr_size.x < width_needed)
goto spr_inadequate;
/* Move sx within source pixrect, if it isn't already */
x1 = sx; /* Two loops do a mod operation, fast if not needed */
/* (But really slow if it is needed!) */
x2 = spr->pr_size.x;
while (x1 >= x2)
x1 -= x2;
/* And this loop is correct for negative sx; % may not be */
while (x1 < 0)
x1 += x2;
sx = x1; /* save sx moved within source pixrect */
/* spr big enough minx & maxx are within aligned source pixrect? */
if( ((dx - x1) <= minx) && (maxx <= dx - x1 + x2) ) {
/* spr should be used as the band, mpr not needed */
soffset = dx - sx; /* dpr X coord - soffset = spr X coord */
} else {
spr_inadequate:
/*
* spr too small or not correctly aligned. Build mpr band.
*/
if (mpr && mpr->pr_depth == spr->pr_depth
&& mpr->pr_size.x >= width_needed
&& mpr->pr_size.y >= spr_height)
{ } /* mpr is already adequate */
else {
if (mpr)
(void) pr_destroy(mpr);
if( (mpr = mem_create(width_needed, spr_height,
spr->pr_depth)) == (struct pixrect *) 0) {
free ((char *) et);
return PIX_ERR;
}
}
/*
* Replicate spr into mpr in X,
* aligning mpr's 0 with dpr's minx using dx and sx.
*/
x1 = sx - dx + minx;
x2 = spr->pr_size.x;
while (x1 >= x2)
x1 -= x2;
while (x1 < 0)
x1 += x2;
/* x1 is the offset within spr that maps onto mpr x==0 */
(void) pr_rop(mpr, 0, 0, x2 - x1, spr_height,
PIX_SRC, spr, x1, 0);
if (x1) /* write the left portion of spr into mpr, if any */
(void) pr_rop(mpr, x2 - x1, 0, x1, spr_height, PIX_SRC, spr, 0, 0);
/* Keep aligned with both spr width and word boundry */
while (x2 < width_needed) { /* final rop is clipped by pr_rop */
(void) pr_rop(mpr, x2, 0, x2, spr_height, PIX_SRC, mpr, 0, 0);
/* move dest. by width & write 2 times width next time */
x2 <<= 1; /* Next time, write twice the width */
}
soffset = minx; /* dpr X coord - soffset = spr X coord */
spr = mpr;
} /* end of preparing spr band in mpr */
} /* end of preparing spr band for textured polygons */
} /* end of set up the edge table. */
{register struct edge *p1, *p2, *pet, *paet;
int cury;
int temp;
/*
* Initialize the edge values: max y, min y, min x and delta x.
*/
p1 = et[0].nxt;
while (p1) { /* For all edges y min must be lowest. */
if (p1->ymn > p1->ymx) {
swap (p1->xmn, p1->dx, temp);
swap (p1->ymn, p1->ymx, temp);
}
p1->erry = p1->ymx - p1->ymn;
p1->errx = p1->dx - p1->xmn; /* dx initially stores xmax */
if (0 <= p1->errx) { /* octants 0 and 1. */
p1->dx = p1->errx / p1->erry;
if (p1->erry < p1->errx) { /* octant 0. */
p1->error = -(p1->errx >> 1)
+ (p1->dx / 2) * p1->erry;
if (p1->error <= 0) {
p1->error += p1->erry;
p1->xmn++;
}
p1->error -= p1->errx;
p1->xmn += p1->dx / 2;
} else { /* octant 1. */
p1->error = -(p1->erry >> 1);
}
} else { /* octants 2 and 3. */
p1->dx = -((-p1->errx) / p1->erry);
if (p1->erry < -p1->errx) { /* octant 3. */
p1->error = -(-p1->errx >> 1)
+ ((-p1->dx) / 2) * p1->erry;
/* -(- necessary since >> is not symmetric. */
if (p1->error <= 0) {
p1->error += p1->erry;
p1->xmn--;
}
p1->error += p1->errx;
p1->xmn += p1->dx / 2 + 1;
#ifdef VECSDRAWTORIGHT
p1->error= p1->errx + p1->dx * p1->erry - p1->error;
if (p1->erry & 1) p1->error++;
#endif
} else { /* octant 2. */
p1->error = -(p1->erry >> 1);
#ifdef VECSDRAWTORIGHT
if (p1->error <= -p1->erry) {
p1->xmn--;
p1->error += p1->erry;
}
#endif
}
}
p1 = p1->nxt;
}
/*
* Sort edge table on y, and on x within a y bucket.
* Bubble sort for now.
*/
p1 = et[0].nxt; /* p1 points to the edge list */
if (!p1) { /* If edge list is empty, polygon disappears. */
free ((char *) et);
return 0;
}
do {
temp = 0;
pet = et;
p1 = pet->nxt;
while (p1->nxt) {
if (p1->ymn > p1->nxt->ymn) {
paet = p1->nxt; /* Swap to sort y */
p1->nxt = p1->nxt->nxt;
paet->nxt = pet->nxt;
pet->nxt = paet;
temp = 1;
} else if (p1->ymn == p1->nxt->ymn) { /* Sort on > x*/
if (p1->xmn > p1->nxt->xmn) {
paet = p1->nxt; /* Swap to sort x */
p1->nxt = p1->nxt->nxt;
paet->nxt = pet->nxt;
pet->nxt = paet;
temp = 1;
}
}
pet = pet->nxt;
p1 = pet->nxt;
}
} while (temp);
/*
* Ready to draw the segments
*/
pet = et [0].nxt;
paet = aet; /* paet points to Active Edge Table */
cury = pet->ymn; /* Set ypixel to 1st non-empty bucket */
paet->nxt = 0; /* Init aet to empty */
if (spr) { /* If old mpr is wider than needed... */
op &= ~PIX_DONTCLIP; /* must clip to avoid using mpr trash */
sy += cury - dy; /* Give sy starting point within spr */
while (sy >= spr_height)
sy -= spr_height;
while (sy < 0)
sy += spr_height;
}
do { /* Repeat til aet & et are empty */
/*
* Get current edges out of edge table (et) and put in active
* edge table (aet).
*/
p1 = paet;
while (pet && cury >= pet->ymn) {
while ( p1->nxt && (p1->nxt->xmn < pet->xmn))
p1 = p1->nxt;
p2 = p1->nxt;
p1->nxt = pet;
pet = pet->nxt;
p1->nxt->nxt = p2;
}
/*
* Paint the current (active) scanline segments. We are
* guaranteed that the edges come in pairs.
*/
p1 = paet->nxt;
/* Don't rop on scanlines outside clipping bound */
if( (cury >= 0) && (cury < dpr->pr_size.y) ) {
/* Need not observe PIX_DONTCLIP, and faster not to do so */
while (p1) {
p2 = p1->nxt;
/* old replrop fix: x1 = p1->xmn; if(x1 < 0)x1=0; */
/* width = last - first + 1 */
(void) pr_rop (dpr, p1->xmn, cury, p2->xmn - p1->xmn, 1,
op, spr, p1->xmn - soffset, sy);
p1 = p2->nxt;
}
}
cury++; /* Prepare for next scan line. */
if (++sy >= spr_height)
sy -= spr_height;
/*
* Remove active edges when cury >= ymax
*/
p1 = paet;
while (p1->nxt)
if (cury >= p1->nxt->ymx) {
p1->nxt = p1->nxt->nxt;
} else p1 = p1->nxt;
/*
* Update x values in Active Edge Table.
*/
p1 = paet;
while (p1->nxt) {
p1 = p1->nxt;
if (p1->errx >= 0) { /* octants 0 and 1. */
if (p1->erry < p1->errx) { /* octant 0. */
p1->xmn += p1->dx;
p1->error += p1->erry * p1->dx;
if (p1->error <= 0) {
p1->error += p1->erry -p1->errx;
p1->xmn++;
} else {
p1->error -= p1->errx;
}
} else { /* octant 1. */
p1->error += p1->errx;
if (p1->error > 0) {
p1->error -= p1->erry;
p1->xmn++;
}
}
} else { /* octants 2 and 3. */
if (p1->erry < -p1->errx) { /* Octant 3. */
p1->xmn += p1->dx;
#ifdef VECSDRAWTORIGHT
p1->error -= p1->errx;
if (p1->error > 0) {
p1->xmn--;
p1->error -= p1->erry;
}
p1->error += p1->erry * p1->dx;
#else
p1->error -= p1->erry * p1->dx;
if (p1->error <= 0) {
p1->error += p1->erry;
p1->xmn--;
}
p1->error += p1->errx;
#endif
} else { /* Octant 2. */
#ifdef VECSDRAWTORIGHT
p1->error += p1->errx;
if (p1->error <= -p1->erry) {
p1->error += p1->erry;
p1->xmn--;
}
#else
p1->error -= p1->errx;
if (p1->error > 0) {
p1->error -= p1->erry;
p1->xmn--;
}
#endif
}
}
}
/* Resort on > xmn because previous step
* may have crossed edges.
*/
p1 = paet;
if (p1->nxt) {
do {
p1 = paet;
p2 = p1->nxt;
temp = 0;
while (p2->nxt)
if (p2->xmn > p2->nxt->xmn) {
p1->nxt = p2->nxt;
p2->nxt = p2->nxt->nxt;
p1->nxt->nxt = p2;
p1 = p1->nxt;
temp = 1;
} else {
p1 = p1->nxt;
p2 = p1->nxt;
}
} while (temp);
}
} while (paet->nxt || pet);
}
free ((char *) et);
return 0;
} /* pr_polygon2 */