home *** CD-ROM | disk | FTP | other *** search
- /**
- ** SCANPOLY.C
- **
- ** Copyright (C) 1992, Csaba Biegl
- ** 820 Stirrup Dr, Nashville, TN, 37221
- ** csaba@vuse.vanderbilt.edu
- **
- ** This file is distributed under the terms listed in the document
- ** "copying.cb", available from the author at the address above.
- ** A copy of "copying.cb" should accompany this file; if not, a copy
- ** should be available from where this file was obtained. This file
- ** may not be distributed without a verbatim copy of "copying.cb".
- ** You should also have received a copy of the GNU General Public
- ** License along with this program (it is in the file "copying");
- ** if not, write to the Free Software Foundation, Inc., 675 Mass Ave,
- ** Cambridge, MA 02139, USA.
- **
- ** This program is distributed in the hope that it will be useful,
- ** but WITHOUT ANY WARRANTY; without even the implied warranty of
- ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ** GNU General Public License for more details.
- **/
-
- #include "grx.h"
- #include "libgrx.h"
- #include "clipping.h"
- #include "scale.h"
- #include "gmalloc.h"
-
- typedef enum {
- inactive,
- active,
- passed
- } edgestatus;
-
- typedef struct _scansegment_ {
- struct _scansegment_ *next;
- int X1,X2;
- } scansegment;
-
- typedef struct {
- scansegment seg;
- edgestatus status;
- int x1,x2,y1,y2;
- int slope,error;
- int dx,dy;
- } polyedge;
-
- void _GrScanPolygon(int n,int pt[][2],
- int is_XOR_color,
- _GrPixelDrawProc pixelproc,
- _GrLineDrawProc borderproc,
- _GrScanLineProc scanfillproc,
- void *fillarg)
- {
- polyedge *edgetable,*ep;
- scansegment *firstpt,*firstseg,*pp,*sp,*prev;
- int prevx,prevy;
- int minx,maxx,miny,maxy;
- int realedges;
- int X1,X2,Y;
- MOUSE_FLAG;
-
- #define add_scanpoint(seg,x) do { \
- X1 = (x); \
- prev = NULL; \
- for(pp = firstpt; pp != NULL; prev = pp,pp = pp->next) \
- if(pp->X1 >= X1) break; \
- (seg)->next = pp; \
- (seg)->X1 = X1; \
- *(prev ? &prev->next : &firstpt) = (seg); \
- } while(0)
-
- #define add_scansegment(seg,x1,x2) do { \
- char overlap = FALSE; \
- X1 = (x1); \
- X2 = (x2); \
- prev = NULL; \
- for(sp = firstseg; sp != NULL; prev = sp,sp = sp->next) { \
- if((sp->X1 <= X2) && (X1 <= sp->X2)) { \
- overlap = TRUE; \
- if(X1 < sp->X1) sp->X1 = X1; \
- if(X2 > sp->X2) { \
- prev = sp; \
- while((sp = sp->next) != NULL) { \
- if(sp->X1 > X2) break; \
- if(sp->X2 > X2) X2 = sp->X2; \
- } \
- prev->X2 = X2; \
- prev->next = sp; \
- } \
- break; \
- } \
- if(sp->X1 > X2) break; \
- } \
- if(!overlap) { \
- (seg)->next = sp; \
- (seg)->X1 = X1; \
- (seg)->X2 = X2; \
- *(prev ? &prev->next : &firstseg) = (seg); \
- } \
- } while(0)
-
- if((n > 1) && (pt[0][0] == pt[n-1][0]) && (pt[0][1] == pt[n-1][1])) n--;
- if(n <= 2) {
- if(n <= 0) return;
- minx = pt[0][0];
- miny = pt[0][1];
- if(n == 1) {
- MOUSE_BLOCK(CURC,minx,miny,minx,miny);
- (*pixelproc)(minx,miny,fillarg);
- }
- else {
- maxx = pt[1][0];
- maxy = pt[1][0];
- MOUSE_BLOCK(CURC,minx,miny,maxx,maxy);
- (*borderproc)(minx,miny,maxx,maxy,fillarg);
- }
- MOUSE_UNBLOCK();
- return;
- }
- edgetable = (polyedge *)_GrGetTempBuffer(sizeof(polyedge) * (n + 2));
- if(edgetable == NULL) return;
- /*
- * Build the edge table. Store only those edges which are in the valid
- * Y region. Clip them in Y if ncessary. Store them with the endpoints
- * ordered by Y in the edge table.
- */
- prevx = pt[0][0];
- prevy = pt[0][1];
- minx = miny = 32000;
- maxx = maxy = (-32000);
- realedges = 0;
- ep = edgetable;
- while(--n >= 0) {
- if(pt[n][1] >= prevy) {
- ep->x1 = prevx;
- ep->y1 = prevy;
- ep->x2 = prevx = pt[n][0];
- ep->y2 = prevy = pt[n][1];
- }
- else {
- ep->x2 = prevx;
- ep->y2 = prevy;
- ep->x1 = prevx = pt[n][0];
- ep->y1 = prevy = pt[n][1];
- }
- if(ep->y1 > _GrHiY) continue;
- if(ep->y2 < _GrLoY) continue;
- if(ep->y1 < _GrLoY) {
- SCALE(X1,(ep->x1 - ep->x2),(ep->y2 - _GrLoY),(ep->y2 - ep->y1));
- ep->x1 = ep->x2 + X1;
- ep->y1 = _GrLoY;
- }
- if(ep->y2 > _GrHiY) {
- SCALE(X2,(ep->x2 - ep->x1),(_GrHiY - ep->y1),(ep->y2 - ep->y1));
- ep->x2 = ep->x1 + X2;
- ep->y2 = _GrHiY;
- }
- if(ep->y1 < miny) miny = ep->y1;
- if(ep->y2 > maxy) maxy = ep->y2;
- if(ep->x2 > ep->x1) {
- if(ep->x1 < minx) minx = ep->x1;
- if(ep->x2 > maxx) maxx = ep->x2;
- }
- else {
- if(ep->x1 > maxx) maxx = ep->x1;
- if(ep->x2 < minx) minx = ep->x2;
- }
- ep->status = inactive;
- realedges++;
- ep++;
- }
- if(realedges == 0) return;
- if(minx > _GrHiX) return;
- if(maxx < _GrLoX) return;
- MOUSE_BLOCK(CURC,minx,miny,maxx,maxy);
- /*
- * Scan for every row between ymin and ymax.
- * Build a linked list of disjoint segments to fill. Rules:
- * (1) a horizontal edge in the row contributes a segment
- * (2) any other edge crossing the row contributes a point
- * (3) every segment between even and odd points is filled
- */
- for(Y = miny; Y <= maxy; Y++) {
- firstpt = NULL;
- firstseg = NULL;
- for(n = realedges,ep = edgetable; --n >= 0; ep++) {
- switch(ep->status) {
- case inactive:
- if(ep->y1 != Y) break;
- ep->dx = ep->x2 - ep->x1;
- ep->dy = ep->y2 - ep->y1;
- if(ep->dy == 0) {
- add_scansegment(&ep->seg,
- ((ep->dx > 0) ? ep->x1 : ep->x2),
- ((ep->dx > 0) ? ep->x2 : ep->x1)
- );
- ep->status = passed;
- break;
- }
- ep->slope = ep->dx / ep->dy;
- if(ep->slope && !is_XOR_color)
- (*borderproc)(ep->x1,ep->y1,ep->x2,ep->y2,fillarg);
- if((ep->dx %= ep->dy) < 0) { ep->slope--; ep->dx += ep->dy; }
- ep->error = ep->dy >> 1;
- ep->status = active;
- case active:
- if((ep->y2 == Y) && (Y != maxy)) { ep->status = passed; break; }
- add_scanpoint(&ep->seg,ep->x1);
- if((ep->error -= ep->dx) < 0) { ep->x1++; ep->error += ep->dy; }
- ep->x1 += ep->slope;
- break;
- default:
- break;
- }
- }
- while((pp = firstpt) != NULL) {
- firstpt = pp->next->next;
- add_scansegment(pp,pp->X1,pp->next->X1);
- }
- for(sp = firstseg; sp != NULL; sp = sp->next) {
- (*scanfillproc)(sp->X1,sp->X2,Y,fillarg);
- }
- }
- MOUSE_UNBLOCK();
- }
-