home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
AmigActive 13
/
AACD13.ISO
/
AACD
/
Resources
/
System
/
BoingBag1
/
Contributions
/
Workbench
/
RexxArpLib3p6
/
src
/
areapolydraw.c
< prev
next >
Wrap
C/C++ Source or Header
|
1998-06-21
|
18KB
|
1,006 lines
/** areapolydraw.c
*
* This file contains the functions Alloc/FreeAreaInfo() and Alloc/FreeTmpRas()
* to initialize and free TmpRas and AreaInfo structures, as well as the
* function AreaPolyDraw(), and AddVertex() and KillVertex().
*
* W.G.J. Langeveld, 14 July 1988
*
* AUTHOR/DATE: W.G.J. Langeveld, July 1988.
* ============
*
* CURRENT VERSION:
*
* This version has been converted to SAS C 6.5 format. It has been modified
* for modern definition sequences for ANSI compilation. This no longer works
* with OS versions prior to 2.04.
*
* AUTHOR/DATE: Joanne Dow, jdow@bix.com, June 1998.
* ============
*
**/
#include <functions.h>
#include "ralprotos.h"
#include <exec/types.h>
#include <exec/exec.h>
#include <intuition/intuition.h>
#include <graphics/gfxmacros.h>
#include <devices/timer.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "areapolydraw.h"
#include "areapatterns.h"
static int AllocAreaInfo(struct RastPort *, int);
static int FreeAreaInfo(struct RastPort *);
static int FindAreaSize(struct vtx *, int *, int *, int *, int *);
static int setdither(unsigned int *, int);
static int bm(int *, int);
static struct vtx *AreaPolyClip(struct vtx *, int, int, int, int);
static struct vtx *SHClip(struct vtx *, int, int);
static struct vtx *addvtx(struct vtx *, struct vtx *);
static int inside(struct vtx *, int, int);
static struct vtx *addintersect(struct vtx *, struct vtx *,
struct vtx *, int, int);
extern struct IntuitionBase *IntuitionBase;
extern struct GfxBase *GfxBase;
#define AISIZE ((long) sizeof(struct AreaInfo))
#define TRSIZE ((long) sizeof(struct TmpRas))
#define min(a, b) ( ((a) > (b)) ? (b) : (a) )
static int bm(), setdither(), FindAreaSize();
static struct vtx *AreaPolyClip();
#define BD_LEFT 1
#define BD_RIGHT 2
#define BD_TOP 3
#define BD_BOTTOM 4
/**
*
* NAME
*
* AllocAreaInfo()
* ===============
*
* SYNOPSIS
*
* AllocAreaInfo(rp, nverts)
* FreeAreaInfo(rp)
*
* FUNCTION
*
* Function to allocate and initialize and free an AreaInfo structure
* for Area functions.
*
* INPUTS
*
* struct RastPort *rp Rastport to be initialized
* int nverts Number of vertices allowed
*
* RESULT
*
* The fields in Rastport rp are initialized.
*
* ADDITIONAL CONSIDERATIONS
*
* BUGS
*
* None known.
*
* AUTHOR
*
* W.G.J. Langeveld, 8 November1988
*
**/
static int AllocAreaInfo( struct RastPort *rp, int nverts )
{
struct AreaInfo *ai = NULL;
UBYTE *aidata = NULL;
if (ai = rp->AreaInfo)
{
if (ai->MaxCount >= nverts)
return(1);
}
ai = (struct AreaInfo *) AllocMem(AISIZE, MEMF_CLEAR);
if (ai == NULL)
goto cleanup;
aidata = (UBYTE *) AllocMem((long) nverts * 5L, MEMF_CLEAR);
if (aidata == NULL)
goto cleanup;
InitArea(ai, (WORD *) aidata, (long) nverts);
rp->AreaInfo = ai;
return(2);
cleanup :
if (aidata)
FreeMem(aidata, (long) nverts * 5L);
if (ai)
FreeMem(ai, AISIZE);
return(0);
}
static int FreeAreaInfo( struct RastPort *rp )
{
struct AreaInfo *ai;
UBYTE *aidata;
if (ai = rp->AreaInfo)
{
if (aidata = (UBYTE *) ai->VctrTbl)
{
FreeMem(aidata, (long) ai->MaxCount * 5L);
}
FreeMem(ai, AISIZE);
rp->AreaInfo = NULL;
}
return(0);
}
/**
*
* NAME
*
* AllocTmpRas()
* =============
*
* SYNOPSIS
*
* AllocTmpRas(rp, width, height)
*
* FUNCTION
*
* Function to allocate and initialize and free a TmpRas structure
* for Area functions.
*
* INPUTS
*
* struct RastPort *rp Rastport to be initialized
* int width, height Size of TmpRas needed
*
* RESULT
*
* The fields in Rastport rp are initialized.
*
* ADDITIONAL CONSIDERATIONS
*
* BUGS
*
* None known.
*
* AUTHOR
*
* W.G.J. Langeveld, 24 July 1988
*
**/
long AllocTmpRas( struct RastPort *rp, int width, int height)
{
struct TmpRas *tr = NULL;
UBYTE *trdata = NULL;
if (tr = rp->TmpRas)
{
if (tr->Size >= (long) RASSIZE((long) width, (long) height))
return(1);
}
tr = (struct TmpRas *) AllocMem (TRSIZE, MEMF_CLEAR);
if (tr == NULL)
goto cleanup;
trdata = (UBYTE *) AllocRaster((long) width, (long) height);
if (trdata == NULL)
goto cleanup;
InitTmpRas(tr, trdata, (long) RASSIZE((long) width, (long) height) );
rp->TmpRas = tr;
return(2);
cleanup :
if (trdata)
FreeRaster(trdata, (long) width, (long) height);
if (tr)
FreeMem(tr, TRSIZE);
return(0);
}
long FreeTmpRas( struct RastPort *rp )
{
struct TmpRas *tr;
if (tr = rp->TmpRas)
{
FreeMem(tr->RasPtr, tr->Size);
FreeMem(tr, TRSIZE);
rp->TmpRas = NULL;
}
return(0);
}
/**
*
* NAME
*
* AreaPolyDraw()
* ==============
*
* SYNOPSIS
*
* AreaPolyDraw(rp, vertex, ppen, xoff, yoff)
*
* FUNCTION
*
* Draws a filled n-vertex polygon according to the outline in linked
* list of vertices. Performs pre-clipping, allows various textures and
* 4-pixel dithering patterns.
*
* INPUTS
*
* w Pointer to a window to receive the imagery.
* vertex Ptr to linked list of vtx structures
* ppen Pattern pen. 0->15: use APen 0 through 15.
* If texture flag set, use one of 16 textures.
* If dither flag is set use one of 125 dithers.
* If outline flag is set, use outline.
* xoff X offset for polygon
* yoff Y offset for polygon
*
* RESULT
*
* Zero on failure, non-zero otherwise.
*
* ADDITIONAL CONSIDERATIONS
*
* This routine allocates its own AreaInfo and TmpRas structures, and
* frees them as well. If the RastPort already had these, they are saved
* and restored at the end.
* The routine preclips all AreaMoves and AreaDraws so they won't
* require a TmpRas larger than a single full bitplane the size of
* the window. Window borders are taken into account.
*
* BUGS
*
* None known.
*
* AUTHOR
*
* W.G.J. Langeveld, 24 July 1988
* revised: November 1989.
*
**/
AreaPolyDraw( struct Window *w, struct vtx *v, int ppen, int xoff, int yoff )
{
register int n;
register struct vtx *tmp;
register int pow, clipped = 0;
int resai = 0, restr = 0, res = 0, notmoved, type;
struct RastPort *rp;
struct RastPort tmprp;
int minx, maxx, miny, maxy;
int clipxmin, clipymin, clipxmax, clipymax;
unsigned int colpat[64];
if (w == NULL)
return(0);
rp = w->RPort;
if (v == NULL)
return(0);
if (ppen & APD_NOFILL)
return(0);
/*
* See if we need clipping
*/
clipxmin = w->BorderLeft;
clipymin = w->BorderTop;
clipxmax = w->Width - w->BorderRight - 1;
clipymax = w->Height - w->BorderBottom - 1;
n = FindAreaSize(v, &minx, &miny, &maxx, &maxy);
/*
* If so, clip the vertexlist, compute new size and set flag.
*/
if ((minx < clipxmin) || (miny < clipymin) ||
(maxx > clipxmax) || (maxy > clipymax) )
{
v = AreaPolyClip(v, clipxmin, clipymin, clipxmax, clipymax);
n = FindAreaSize(v, &minx, &miny, &maxx, &maxy);
clipped = 1;
}
/*
* Check for empty set
*/
if (v == NULL)
return(0);
if (n == 0)
goto cleanup;
/*
* Make a copy of the rastport
*/
movmem(rp, &tmprp, sizeof(struct RastPort));
/*
* Add an outline if requested
*/
if (ppen & APD_OUTLINE)
{
SetOPen(&tmprp, (long) tmprp.FgPen)
}
else
{
BNDRYOFF(&tmprp);
}
/*
* Set APen
*/
if ((ppen & 0xFF) < 16)
SetAPen(&tmprp, (long) (ppen & 0xFF));
/*
* If object still too large, just draw it, don't bother filling
*/
if ((minx < clipxmin) || (miny < clipymin) ||
(maxx > clipxmax) || (maxy > clipymax) )
{
tmp = v;
notmoved = 1;
while (tmp)
{
type = tmp->type;
if (notmoved && (type == AREADRAW))
type = AREAMOVE;
switch (type)
{
case AREAMOVE :
Move(&tmprp, (long) (tmp->x + xoff), (long) (tmp->y + yoff));
notmoved = 0;
break;
case AREADRAW :
Draw(&tmprp, (long) (tmp->x + xoff), (long) (tmp->y + yoff) );
break;
case AREACIRCLE :
case AREAELLIPSE :
DrawEllipse(&tmprp, (long) (tmp->x + xoff), (long) (tmp->y + yoff),
(long) tmp->a , (long) tmp->b );
break;
}
tmp = tmp->next;
}
res = 1;
}
else
{
/*
* Allocate new AreaInfo/TmpRas (resxx = 2) or use the ones we have (resxx = 1).
*/
resai = AllocAreaInfo(&tmprp, n + 10);
restr = AllocTmpRas(&tmprp, maxx - minx + 32, maxy - miny + 2);
/*
* AreaMove, AreaDraw, AreaCircle, AreaEllipse.
*/
if (resai && restr)
{
tmp = v;
notmoved = 1;
while (tmp)
{
type = tmp->type;
if (notmoved && (type == AREADRAW))
type = AREAMOVE;
switch (type)
{
case AREAMOVE :
AreaMove(&tmprp, (long) (tmp->x + xoff),
(long) (tmp->y + yoff));
notmoved = 0;
break;
case AREADRAW :
AreaDraw(&tmprp, (long) (tmp->x + xoff),
(long) (tmp->y + yoff) );
break;
case AREACIRCLE :
case AREAELLIPSE :
AreaEllipse(&tmprp, (long) (tmp->x + xoff),
(long) (tmp->y + yoff),
(long) tmp->a ,
(long) tmp->b );
break;
}
tmp = tmp->next;
}
/*
* Set up an areafill pattern
*/
setmem(colpat, 128, 0);
/*
* Copy either a texture...
*/
if (ppen & APD_TEXTURE)
{
pow = arpt[ppen & 0xF].power;
movmem(arpt[ppen & 0xF].data, colpat,
(pow < 0) ? (1 << (1 - pow)) * 3 : 1 << (pow + 1));
SetAfPt(&tmprp, (unsigned short *) colpat, pow); // Stop a complaint
if (pow < 0)
SetAPen(&tmprp, 255L);
else
SetAPen(&tmprp, 1L);
SetDrMd(&tmprp, JAM2);
}
/*
* ...or a dithered color
*/
else
if (ppen & APD_DITHER)
{
setdither(colpat, ppen & 0xFF);
SetAfPt(&tmprp, (unsigned short *) colpat, -1);
SetAPen(&tmprp, 255L);
SetDrMd(&tmprp, JAM2);
}
/*
* Do the blit. Make sure the blit finishes before deallocating.
*/
AreaEnd(&tmprp);
WaitBlit();
res = 1;
}
/*
* Free allocated resources.
*/
if (resai == 2)
FreeAreaInfo(&tmprp);
if (restr == 2)
FreeTmpRas(&tmprp);
}
/*
* Deallocate our local copy of the list
*/
cleanup:
if (clipped)
KillVertex(v);
return(res);
}
/**
*
* Find number of vertices and horizontal and vertical extent.
*
**/
static int FindAreaSize(struct vtx *v, int *minx, int *miny, int *maxx, int *maxy )
{
int n = 0, t;
*minx = *miny = 20000;
*maxx = *maxy = 0;
while (v)
{
switch (v->type)
{
case AREAMOVE :
n++;
case AREADRAW :
n++;
if ((t = v->x) < *minx)
*minx = t;
if (t > *maxx)
*maxx = t;
if ((t = v->y) < *miny)
*miny = t;
if (t > *maxy)
*maxy = t;
break;
case AREACIRCLE :
v->b = v->a;
case AREAELLIPSE :
n += 2;
if ((t = v->x - v->a) < *minx)
*minx = t;
if ((t = v->x + v->a) > *maxx)
*maxx = t;
if ((t = v->y - v->b) < *miny)
*miny = t;
if ((t = v->y + v->b) > *maxy)
*maxy = t;
break;
}
v = v->next;
}
return(n);
}
/**
*
* Allocate a new vtx structure, and add to list.
*
**/
struct vtx *AddVertex( struct vtx *v )
{
struct vtx *t, *m;
t = v; // Stop compiler squawk placing it here.
if (v)
{
// t = v;
while (t->next)
t = t->next;
}
m = (struct vtx *) AllocMem((long) sizeof(struct vtx), MEMF_CLEAR);
if (v)
t->next = m;
return(m);
}
/**
*
* Delete vtx structure list.
*
**/
struct vtx *KillVertex( struct vtx *v )
{
struct vtx *t;
while (v)
{
t = v->next;
FreeMem(v, (long) sizeof(struct vtx));
v = t;
}
return(NULL);
}
/**
*
* This function calculates a dithering pattern for color n,
* where n is a number between 0 and 124 (inclusive) according to
*
* n = r * 25 + g * 5 + b,
*
* with r, g, and b between 0 and 4 (inclusive).
*
**/
static unsigned int bits[16][2] =
{
{0x0000, 0x0000
}
,
{
0xaaaa, 0x0000
}
,
{
0x5555, 0x0000
}
,
{
0xffff, 0x0000
}
,
{
0x0000, 0xaaaa
}
,
{
0xaaaa, 0xaaaa
}
,
{
0x5555, 0xaaaa
}
,
{
0xffff, 0xaaaa
}
,
{
0x0000, 0x5555
}
,
{
0xaaaa, 0x5555
}
,
{
0x5555, 0x5555
}
,
{
0xffff, 0x5555
}
,
{
0x0000, 0xffff
}
,
{
0xaaaa, 0xffff
}
,
{
0x5555, 0xffff
}
,
{
0xffff, 0xffff
}
};
static int setdither( unsigned int ptdata[], int n )
{
int r, g, b;
int l, m, nblack, nwhite, nmagenta, ncyan, nyellow;
static int indices[4];
static int map[4] =
{
0, 3, 1, 2
};
/*
* Argument tests
*/
if (ptdata == 0L)
return;
if (n < 0)
return;
n = (n & 0x7f);
if (n > 124)
n = 124;
/*
* Extract r, g, and b
*/
r = n / 25;
b = n - r * 25;
g = b / 5;
b = b - g * 5;
/*
* Initialize number of black pixels in a square
*/
nblack = 4;
/*
* Number of white pixels...
*/
if (nwhite = min(min(r, g), b))
{
r -= nwhite;
g -= nwhite;
b -= nwhite;
nblack -= nwhite;
}
/*
* ...and cyan, magenta or yellow pixels
*/
ncyan = min(g, b);
nmagenta = min(r, b);
nyellow = min(r, g);
if (ncyan)
{
g -= ncyan;
b -= ncyan;
nblack -= ncyan;
}
else
if (nmagenta)
{
r -= nmagenta;
b -= nmagenta;
nblack -= nmagenta;
}
else
if (nyellow)
{
r -= nyellow;
g -= nyellow;
nblack -= nyellow;
}
/*
* ...and red green and blue pixels
*/
if (r)
nblack -= r;
else
if (g)
nblack -= g;
else
if (b)
nblack -= b;
/*
* Set up the color indices for the four pixels
*/
m = 0;
for (l = 0; l < nblack; l++, m++)
indices[map[m]] = 0;
for (l = 0; l < nwhite; l++, m++)
indices[map[m]] = 1;
for (l = 0; l < ncyan; l++, m++)
indices[map[m]] = 5;
for (l = 0; l < nmagenta; l++, m++)
indices[map[m]] = 6;
for (l = 0; l < nyellow; l++, m++)
indices[map[m]] = 7;
for (l = 0; l < r; l++, m++)
indices[map[m]] = 2;
for (l = 0; l < g; l++, m++)
indices[map[m]] = 3;
for (l = 0; l < b; l++, m++)
indices[map[m]] = 4;
/*
* Convert to bitmaps and return in ptdata.
*/
movmem(bits[bm(indices, 0)], &ptdata[0], 4);
movmem(bits[bm(indices, 1)], &ptdata[2], 4);
movmem(bits[bm(indices, 2)], &ptdata[4], 4);
return;
}
/**
*
* Extract bitmap n from quadruple s
*
**/
static int bm( int s[], int n )
{
int index = 0;
int mask = 1 << n;
index = (( ((s[3] & mask) << 3) +
((s[2] & mask) << 2) +
((s[1] & mask) << 1) +
((s[0] & mask) ) ) >> n) & 0xf ;
return(index);
}
/**
*
* Clip a vertex list against a rectangle
*
**/
static struct vtx *AreaPolyClip( struct vtx *vi, int x1, int y1, int x2, int y2 )
{
struct vtx *vo = NULL;
vo = SHClip(vi, BD_LEFT, x1);
/*
* Be careful not to deallocate the input list!
*/
vi = vo;
vo = SHClip(vi, BD_RIGHT, x2);
KillVertex(vi);
vi = vo;
vo = SHClip(vi, BD_TOP, y1);
KillVertex(vi);
vi = vo;
vo = SHClip(vi, BD_BOTTOM, y2);
KillVertex(vi);
return(vo);
}
/**
*
* Polygon clipping against a single boundary
* Sutherland-Hodgman, Foley and Van dam page 454
* Modified to do just rectangular clips.
*
**/
static struct vtx *SHClip( struct vtx *vi, int bordertype, int bordervalue )
{
struct vtx *p, *s, *vo, *t, *u;
int first = 1;
/*
* Find last vertex s
*/
s = NULL;
t = vi;
while (t)
{
if (t->type == AREADRAW)
s = t;
t = t->next;
}
/*
* If there is no last vertex, there is nothing to do
*/
if (s == NULL)
return(NULL);
/*
* Set up output vertex array
*/
vo = AddVertex(NULL);
if (vo == NULL)
return(NULL);
/*
* Loop over the list
*/
while (vi)
{
/*
* Skip non-vertices.
*/
if ((vi->type == AREAMOVE) || (vi->type == AREADRAW))
{
p = vi;
/*
* Now s and p correspond to the vertices in Fig. 11.21 in
* Foley and Van Dam
*/
if (inside(p, bordertype, bordervalue))
{
if (inside(s, bordertype, bordervalue))
{
addvtx(vo, p);
}
else
{
t = addintersect(vo, s, p, bordertype, bordervalue);
u = addvtx(vo, p);
/*
* Tricky: (only the first time!) if the first point is inside, but the
* previous point (which is the last point in the list) is outside,
* we want to reverse the functions of the two added points. The new
* intersect (t) becomes a "move", whereas the point at hand becomes a
* "draw" (if they were "draw" and "move" before). Reason: the intersect
* gets added *first*, even though it is the leftover of the *last* point.
*/
if (first && t && u)
{
if ((s->type == AREADRAW) && (p->type == AREAMOVE))
{
t->type = AREAMOVE;
u->type = AREADRAW;
}
}
}
}
else
{
if (inside(s, bordertype, bordervalue))
{
addintersect(vo, s, p, bordertype, bordervalue);
}
}
first = 0;
/*
* Advance to next pair of vertices
*/
s = p;
}
else
if (vi->type)
{
/*
* If this is something other than AREADRAW or MOVE, but not NULL,
* copy it straight into the output.
*/
addvtx(vo, vi);
}
vi = vi->next;
}
return(vo);
}
/**
*
* Add a vertex p to list vo.
*
**/
static struct vtx *addvtx( struct vtx *vo, struct vtx *p )
{
struct vtx *t;
if (t = AddVertex(vo))
{
t->type = p->type;
t->x = p->x;
t->y = p->y;
t->a = p->a;
t->b = p->b;
}
return(t);
}
/**
*
* Determine whether a vertex is inside or outside the border
*
**/
static int inside( struct vtx *s, int bordertype, int bordervalue )
{
if (s)
{
switch (bordertype)
{
case BD_LEFT :
return(s->x >= bordervalue);
break;
case BD_RIGHT :
return(s->x < bordervalue);
break;
case BD_TOP :
return(s->y >= bordervalue);
break;
case BD_BOTTOM :
return(s->y < bordervalue);
break;
}
}
return(0);
}
/**
*
* Add the intersect of vectors s and p with the border to list vo
* Routine assumes that one vector is inside, the other outside (no
* particular order).
*
**/
static struct vtx *addintersect( struct vtx *vo, struct vtx *s, struct vtx *p, int bordertype, int bordervalue )
{
struct vtx *v;
v = AddVertex(vo);
if (s && p && v)
{
v->type = p->type;
switch (bordertype)
{
case BD_LEFT :
case BD_RIGHT :
v->x = bordervalue;
v->y = s->y + (int) (((long) (bordervalue - s->x) * (p->y - s->y)) /
(p->x - s->x));
break;
case BD_TOP :
case BD_BOTTOM :
v->y = bordervalue;
v->x = s->x + (int) (((long) (bordervalue - s->y) * (p->x - s->x)) /
(p->y - s->y));
break;
}
}
return(v);
}