/* grClip.c - * * ********************************************************************* * * Copyright (C) 1985, 1990 Regents of the University of California. * * * 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. The University of California * * * makes no representations about the suitability of this * * * software for any purpose. It is provided "as is" without * * * express or implied warranty. Export of this software outside * * * of the United States of America may require an export license. * * ********************************************************************* * * This file contains additional functions to manipulate a * color display. Included here are rectangle clipping and * drawing routines. */ #ifndef lint static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/graphics/grClip.c,v 1.4 2010/06/24 12:37:18 tim Exp $"; #endif /* not lint */ #include #include "utils/magic.h" #include "textio/textio.h" #include "utils/geometry.h" #include "tiles/tile.h" #include "utils/hash.h" #include "utils/styles.h" #include "database/database.h" #include "windows/windows.h" #include "graphics/graphics.h" #include "graphics/graphicsInt.h" #include "utils/malloc.h" /* Forward declaration: */ extern bool GrDisjoint(); extern void GrClipTriangle(); /* The following rectangle defines the size of the cross drawn for * zero-size rectangles. This must be all on one line to keep * lintpick happy! */ global Rect GrCrossRect = {{-GR_CROSSSIZE, -GR_CROSSSIZE}, {GR_CROSSSIZE, GR_CROSSSIZE}}; global int GrNumClipBoxes = 0; /* for benchmarking */ global int grCurDStyle; global unsigned char GrGridMultiple = 1; /* A rectangle that is one square of the grid */ static Rect *grGridRect; /* * ---------------------------------------------------------------------------- * GrSetStuff -- * * Set up current drawing style. * * Results: * None. * * Side effects: * Variables are changed. If anything is drawn, it will appear in the * specified style. * ---------------------------------------------------------------------------- */ /* Current state for rectangle and text drawing */ static int grCurWMask, grCurStipple; #ifndef MAGIC_WRAPPER static /* Used in grTkCommon.c, so don't make static */ #endif int grCurOutline, grCurFill, grCurColor; /* Has the device driver been informed of the above? We only inform it * when we actually need to draw something -- this makes it harmless (in terms * of graphics bandwidth) to call GrSetStuff extra times. */ bool grDriverInformed = TRUE; void GrSetStuff(style) int style; { grCurDStyle = style; grCurWMask = GrStyleTable[style].mask; grCurColor = GrStyleTable[style].color; grCurOutline = GrStyleTable[style].outline; grCurStipple = GrStyleTable[style].stipple; grCurFill = GrStyleTable[style].fill; grDriverInformed = FALSE; } /*--------------------------------------------------------------------------- * grInformDriver: * * Inform the driver about the last GrSetStuff call. * * Results: * None. * * Side Effects: * None. * *---------------------------------------------------------------------------- */ void grInformDriver() { /* Now let the device drivers know */ (*grSetWMandCPtr)(grCurWMask, grCurColor); (*grSetLineStylePtr)(grCurOutline); (*grSetStipplePtr)(grCurStipple); grDriverInformed = TRUE; } /* * ---------------------------------------------------------------------------- * grClipAgainst -- * * Clip a linked list of rectangles against a single rectangle. This * may result in the list getting longer or shorter. * * Results: * None. * * Side effects: * The original list may change. * ---------------------------------------------------------------------------- */ void grClipAgainst(startllr, clip) LinkedRect **startllr; /* A pointer to the pointer that heads * the list . */ Rect *clip; /* The rectangle to clip against */ { extern bool grClipAddFunc(); /* forward declaration */ LinkedRect **llr, *lr; for (llr = startllr; *llr != (LinkedRect *) NULL; /*nop*/ ) { if ( GEO_TOUCH(&(*llr)->r_r, clip) ) { lr = *llr; *llr = lr->r_next; /* this will modify the list that we are traversing! */ (void) GrDisjoint(&lr->r_r, clip, grClipAddFunc, (ClientData) &llr); freeMagic( (char *) lr); } else llr = &((*llr)->r_next); } } /* Add a box to our linked list, and advance * our pointer into the list */ bool grClipAddFunc(box, cd) Rect *box; ClientData cd; { LinkedRect ***lllr, *lr; lllr = (LinkedRect ***) cd; lr = (LinkedRect *) mallocMagic((unsigned) (sizeof (LinkedRect))); lr->r_r = *box; lr->r_next = **lllr; **lllr = lr; *lllr = &lr->r_next; return TRUE; } void grObsBox(r) Rect *r; { LinkedRect *ob; LinkedRect *ar; LinkedRect **areas; ar = (LinkedRect *) mallocMagic((unsigned) (sizeof (LinkedRect))); ar->r_r = *r; ar->r_next = NULL; areas = &ar; /* clip against obscuring areas */ for (ob = grCurObscure; ob != NULL; ob = ob->r_next) { if ( GEO_TOUCH(r, &(ob->r_r)) ) grClipAgainst(areas, &(ob->r_r)); } while (*areas != NULL) { LinkedRect *oldarea; if (grCurFill == GR_STGRID) (*grDrawGridPtr)(grGridRect, grCurOutline, &((*areas)->r_r)); else (*grFillRectPtr)(&((*areas)->r_r)); oldarea = *areas; *areas = (*areas)->r_next; freeMagic( (char *) oldarea ); } } /*--------------------------------------------------------- * grClipPoints: * This routine computes the 0, 1, or 2 intersection points * between a line and a box. * * Results: * FALSE if the line is completely outside of the box. * * Side Effects: *--------------------------------------------------------- */ bool grClipPoints(line, box, p1, p1OK, p2, p2OK) Rect *line; /* Actually a line from line->r_ll to * line->r_ur. It is assumed that r_ll is to * the left of r_ur, but we don't assume that * r_ll is below r_ur. */ Rect *box; /* A box to check intersections with */ Point *p1, *p2; /* To be filled in with 0, 1, or 2 points * that are on the border of the box as well as * on the line. */ bool *p1OK, *p2OK; /* Says if the point was filled in */ { int tmp, delx, dely; bool delyneg; int x1, x2, y1, y2; bool ok1, ok2; if (p1OK != NULL) *p1OK = FALSE; ok1 = FALSE; if (p2OK != NULL) *p2OK = FALSE; ok2 = FALSE; x1 = line->r_xbot; x2 = line->r_xtop; y1 = line->r_ybot; y2 = line->r_ytop; delx = x2-x1; dely = y2-y1; /* We have to be careful because of machine-dependent problems * with rounding during division by negative numbers. */ if (dely<0) { dely = -dely; delyneg = TRUE; } else delyneg = FALSE; /* we know that delx is nonnegative if this is a real (non-empty) line */ if (delx < 0) return FALSE; if (x1 < box->r_xbot) { if (delx == 0) return FALSE; tmp = (((box->r_xbot-x1)*dely) + (delx>>1))/delx; if (delyneg) y1 -= tmp; else y1 += tmp; x1 = box->r_xbot; } else if (x1 > box->r_xtop) return FALSE; if (x2 > box->r_xtop) { if (delx == 0) return FALSE; tmp = ((x2-box->r_xtop)*dely + (delx>>1))/delx; if (delyneg) y2 += tmp; else y2 -= tmp; x2 = box->r_xtop; } else if (x2 < box->r_xbot) return FALSE; if (y2 > y1) { if (y1 < box->r_ybot) { x1 += (((box->r_ybot-y1)*delx) + (dely>>1))/dely; y1 = box->r_ybot; } else if (y1 > box->r_ytop) return FALSE; if (y2 > box->r_ytop) { x2 -= (((y2 - box->r_ytop)*delx) + (dely>>1))/dely; y2 = box->r_ytop; } else if (y2 < box->r_ybot) return FALSE; } else { if (y1 > box->r_ytop) { if (dely == 0) return FALSE; x1 += (((y1-box->r_ytop)*delx) + (dely>>1))/dely; y1 = box->r_ytop; } else if (y1 < box->r_ybot) return FALSE; if (y2 < box->r_ybot) { if (dely == 0) return FALSE; x2 -= (((box->r_ybot-y2)*delx) + (dely>>1))/dely; y2 = box->r_ybot; } else if (y2 > box->r_ytop) return FALSE; } if ( (x1 == box->r_xbot) || (y1 == box->r_ybot) || (y1 == box->r_ytop) ) { if (p1 != NULL) { p1->p_x = x1; p1->p_y = y1; } if (p1OK != NULL) *p1OK = TRUE; ok1 = TRUE; } if ( (x2 == box->r_xtop) || (y2 == box->r_ybot) || (y2 == box->r_ytop) ) { if (p2 != NULL) { p2->p_x = x2; p2->p_y = y2; } if (p2OK != NULL) *p2OK = TRUE; ok2 = TRUE; } /* is part of the line in the box? */ return ok1 || ok2 || ((x1 >= box->r_xbot) && (x1 <= box->r_xtop) && (y1 >= box->r_ybot) && (y1 <= box->r_ytop)); } #define NEWAREA(lr,x1,y1,x2,y2) {LinkedRect *tmp; \ tmp = (LinkedRect *) mallocMagic((unsigned) (sizeof (LinkedRect))); \ tmp->r_r.r_xbot = x1; tmp->r_r.r_xtop = x2; \ tmp->r_r.r_ybot = y1; tmp->r_r.r_ytop = y2; tmp->r_next = lr; lr = tmp;} /*--------------------------------------------------------- * GrClipLine: * GrClipLine will draw a line on the screen in the current * style and clip stuff. * * Results: None. * * Side Effects: * The line is drawn in the current style. *--------------------------------------------------------- */ void GrClipLine(x1, y1, x2, y2) int x1, y1, x2, y2; { LinkedRect **ar; LinkedRect *ob; LinkedRect *areas; GR_CHECK_LOCK(); if (!grDriverInformed) grInformDriver(); /* we will pretend the the ll corner of a rectangle is the * left endpoint of a line, and the ur corner the right endpoint * of the line. */ areas = (LinkedRect *) mallocMagic((unsigned) (sizeof (LinkedRect))); areas->r_next = NULL; if (x1 < x2) { areas->r_r.r_xbot = x1; areas->r_r.r_ybot = y1; areas->r_r.r_xtop = x2; areas->r_r.r_ytop = y2; } else { areas->r_r.r_xtop = x1; areas->r_r.r_ytop = y1; areas->r_r.r_xbot = x2; areas->r_r.r_ybot = y2; } /* clip against the clip box */ for (ar = &areas; *ar != NULL; ) { Rect *l; Rect canonRect; l = &((*ar)->r_r); GeoCanonicalRect(l, &canonRect); if (!GEO_TOUCH(&canonRect, &grCurClip)) { /* line is totally outside of clip area */ goto deleteit; } else { /* is there some intersection with clip area? */ if (!grClipPoints(l, &grCurClip, &(l->r_ll), (bool *) NULL, &(l->r_ur), (bool*) NULL)) { /* no intersection */ goto deleteit; } /* clip against obscuring areas */ for (ob = grCurObscure; ob != NULL; ob = ob->r_next) { Point p1, p2; Rect c; bool ok1 = FALSE, ok2 = FALSE; c = ob->r_r; c.r_xbot--; c.r_ybot--; c.r_xtop++; c.r_ytop++; if (grClipPoints(l, &c, &p1, &ok1, &p2, &ok2) && !ok1 && !ok2) { /* Line is not completely outside of the box, * nor does it intersect it. * Therefore, line is completely obscured. */ goto deleteit; } if (ok1 && ( ((l->r_xbot == p1.p_x) && (l->r_ybot == p1.p_y)) || ((l->r_xtop == p1.p_x) && (l->r_ytop == p1.p_y)) ) ) { ok1 = FALSE; /* do not split or clip at an endpoint */ } if (ok2 && ( ((l->r_xbot == p2.p_x) && (l->r_ybot == p2.p_y)) || ((l->r_xtop == p2.p_x) && (l->r_ytop == p2.p_y)) ) ) { ok2 = FALSE; /* do not split or clip at an endpoint */ } if (ok1 ^ ok2) { /* one segment to deal with */ if (ok1) l->r_ur = p1; else l->r_ll = p2; } else if (ok1 && ok2) { /* clip both sides */ LinkedRect *new; new = (LinkedRect *) mallocMagic((unsigned) (sizeof (LinkedRect))); new->r_r.r_ur = l->r_ur; new->r_r.r_ll = p2; new->r_next = (*ar); l->r_ur = p1; (*ar) = new; } } } ar = &((*ar)->r_next); continue; deleteit: { LinkedRect *reclaim; reclaim = (*ar); *ar = reclaim->r_next; freeMagic( (char *) reclaim); } } /* for ar */ /* draw the lines */ while (areas != NULL) { LinkedRect *oldarea; (*grDrawLinePtr)(areas->r_r.r_xbot, areas->r_r.r_ybot, areas->r_r.r_xtop, areas->r_r.r_ytop); oldarea = areas; areas = areas->r_next; freeMagic( (char *) oldarea ); } } /* *--------------------------------------------------------- * grAddSegment: * Add a segment to a linked list of rectangles * * Results: * None. * * Side effects: * Allocates memory; appends LinkedRect structure to * list "segments". * * Notes: * INLINE this for speedup? *--------------------------------------------------------- */ void grAddSegment(llx, lly, urx, ury, segments) int llx, lly, urx, ury; LinkedRect **segments; { LinkedRect *curseg; curseg = (LinkedRect *)mallocMagic(sizeof(LinkedRect)); curseg->r_r.r_xbot = llx; curseg->r_r.r_ybot = lly; curseg->r_r.r_xtop = urx; curseg->r_r.r_ytop = ury; curseg->r_next = *segments; *segments = curseg; } /*--------------------------------------------------------- * GrBoxOutline: * For box outlines, works around the boundary of * the box according to the associated tile structure, * and returns a linked rect list defining all the * segments which need to be drawn. * * Results: * TRUE if tile is isolated (GrFastBox can be used). * Otherwise, result is FALSE. * * Side Effects: * May allocate memory for a linked rect structure, * with pointer returned in tilesegs. If non-NULL, * the calling function needs to free memory for the * linked rect structure. * * Implementation notes: * Standard tech files define most bordered styles as * contact types, and most if not all contacts are * required to be square. So we want the case of an * isolated tile to go fast, avoiding any calls to * allocate memory. This complicates the routine but * keeps the routine from slowing down the layout * rendering. *--------------------------------------------------------- */ bool GrBoxOutline(tile, tilesegs) Tile *tile; LinkedRect **tilesegs; { Rect rect; TileType ttype; LinkedRect *curseg; Tile *tpleft, *tpright, *tptop, *tpbot; int edgeTop, edgeBot, edgeRight, edgeLeft; int isolate = 0; bool sense; *tilesegs = NULL; TiToRect(tile, &rect); if (IsSplit(tile) && SplitSide(tile)) isolate |= 0x1; else { ttype = TiGetLeftType(tile); edgeBot = rect.r_ybot; sense = TRUE; for (tpleft = BL(tile); BOTTOM(tpleft) < rect.r_ytop; tpleft = RT(tpleft)) { if (TiGetRightType(tpleft) != ttype) { if (!sense) { edgeBot = BOTTOM(tpleft); if (TOP(tpleft) >= rect.r_ytop) grAddSegment(rect.r_xbot, edgeBot, rect.r_xbot, rect.r_ytop, tilesegs); sense = TRUE; } } else { if (sense) { edgeTop = BOTTOM(tpleft); if (edgeTop > edgeBot) grAddSegment(rect.r_xbot, edgeBot, rect.r_xbot, edgeTop, tilesegs); isolate |= 0x1; sense = FALSE; } } } } if (IsSplit(tile) && !SplitSide(tile)) isolate |= 0x2; else { ttype = TiGetRightType(tile); edgeTop = rect.r_ytop; sense = TRUE; for (tpright = TR(tile); TOP(tpright) > rect.r_ybot; tpright = LB(tpright)) { if (TiGetLeftType(tpright) != ttype) { if (!sense) { edgeTop = TOP(tpright); if (BOTTOM(tpright) <= rect.r_ybot) grAddSegment(rect.r_xtop, rect.r_ybot, rect.r_xtop, edgeTop, tilesegs); sense = TRUE; } } else { if (sense) { edgeBot = TOP(tpright); if (edgeBot < edgeTop) grAddSegment(rect.r_xtop, edgeBot, rect.r_xtop, edgeTop, tilesegs); isolate |= 0x2; sense = FALSE; } } } } if (IsSplit(tile) && (SplitSide(tile) == SplitDirection(tile))) isolate |= 0x4; else { ttype = TiGetBottomType(tile); edgeLeft = rect.r_xbot; sense = TRUE; for (tpbot = LB(tile); LEFT(tpbot) < rect.r_xtop; tpbot = TR(tpbot)) { if (TiGetTopType(tpbot) != ttype) { if (!sense) { edgeLeft = LEFT(tpbot); if (RIGHT(tpbot) >= rect.r_xtop) grAddSegment(edgeLeft, rect.r_ybot, rect.r_xtop, rect.r_ybot, tilesegs); sense = TRUE; } } else { if (sense) { edgeRight = LEFT(tpbot); if (edgeRight > edgeLeft) grAddSegment(edgeLeft, rect.r_ybot, edgeRight, rect.r_ybot, tilesegs); isolate |= 0x4; sense = FALSE; } } } } if (IsSplit(tile) && (SplitSide(tile) != SplitDirection(tile))) isolate |= 0x8; else { ttype = TiGetTopType(tile); edgeRight = rect.r_xtop; sense = TRUE; for (tptop = RT(tile); RIGHT(tptop) > rect.r_xbot; tptop = BL(tptop)) { if (TiGetBottomType(tptop) != ttype) { if (!sense) { edgeRight = RIGHT(tptop); if (LEFT(tptop) <= rect.r_xbot) grAddSegment(rect.r_xbot, rect.r_ytop, edgeRight, rect.r_ytop, tilesegs); sense = TRUE; } } else { if (sense) { edgeLeft = RIGHT(tptop); if (edgeLeft < edgeRight) grAddSegment(edgeLeft, rect.r_ytop, edgeRight, rect.r_ytop, tilesegs); isolate |= 0x8; sense = FALSE; } } } } if (isolate == 0) /* Common case */ return TRUE; else { /* Need to malloc segments for isolated sides */ if (!(isolate & 0x1)) /* Left */ grAddSegment(rect.r_xbot, rect.r_ybot, rect.r_xbot, rect.r_ytop, tilesegs); if (!(isolate & 0x2)) /* Right */ grAddSegment(rect.r_xtop, rect.r_ybot, rect.r_xtop, rect.r_ytop, tilesegs); if (!(isolate & 0x4)) /* Bottom */ grAddSegment(rect.r_xbot, rect.r_ybot, rect.r_xtop, rect.r_ybot, tilesegs); if (!(isolate & 0x8)) /* Top */ grAddSegment(rect.r_xbot, rect.r_ytop, rect.r_xtop, rect.r_ytop, tilesegs); return FALSE; } } /* Threshold for determining if outlines are too small to draw */ #define GR_THRESH 4 /*--------------------------------------------------------- * GrBox -- * * GrBox draws a rectangle on the screen in the style * set by the previous call to GrSetStuff. It will * be clipped against the rectangles passed to GrSetStuff. * * Unlike GrFastBox (see below), GrBox makes no assumptions * about how the outline will be drawn. If there is no * outline, GrFastBox is called. The tile border is checked, * and if the tile outline is simple, GrFastBox is called. * Otherwise, we do a fast fill a la GrFastBox but draw the * outline segment by segment. * * Results: None. * * Side Effects: * The rectangle is drawn in the style specified. * The rectangle may be clipped before being drawn. *--------------------------------------------------------- */ void GrBox(MagWindow *mw, Transform *trans, Tile *tile) { Rect r, r2, clipr; bool needClip, needObscure, simpleBox; LinkedRect *ob, *tilesegs, *segptr; Point polyp[5]; int np; r.r_xbot = LEFT(tile); r.r_ybot = BOTTOM(tile); r.r_xtop = RIGHT(tile); r.r_ytop = TOP(tile); GeoTransRect(trans, &r, &r2); if (IsSplit(tile)) WindSurfaceToScreenNoClip(mw, &r2, &r); else WindSurfaceToScreen(mw, &r2, &r); GR_CHECK_LOCK(); if (!grDriverInformed) grInformDriver(); GrNumClipBoxes++; if (!GEO_TOUCH(&r, &grCurClip)) return; /* Do a quick check to make the (very common) special case of * no clipping go fast. */ needClip = !GEO_SURROUND(&grCurClip, &r); needObscure = FALSE; for (ob = grCurObscure; ob != NULL; ob = ob->r_next) needObscure |= GEO_TOUCH(&r, &(ob->r_r)); /* Nonmanhattan tiles: */ /* Expects one of the two tile types to be masked out before */ /* this procedure is called. */ if (IsSplit(tile)) { /* Perform matrix transformations on split tiles */ TileType dinfo; Rect fullr; dinfo = DBTransformDiagonal(TiGetTypeExact(tile), trans); clipr = fullr = r; if (needClip) GeoClip(&clipr, &grCurClip); GrClipTriangle(&fullr, &clipr, needClip, dinfo, polyp, &np); if ((grCurFill == GR_STSOLID) || (grCurFill == GR_STSTIPPLE) || (grCurFill == GR_STGRID) ) { if (needObscure) grObsBox(&clipr); else if (grFillPolygonPtr) (void) (*grFillPolygonPtr)(polyp, np); } } else { /* do solid areas (same as GrFastBox) */ if ((grCurFill == GR_STSOLID) || (grCurFill == GR_STSTIPPLE)) { /* We have a filled area to deal with */ clipr = r; if (needClip) GeoClip(&clipr, &grCurClip); if (needObscure) grObsBox(&clipr); else (void) (*grFillRectPtr)(&clipr); } } /* return if outline is too small to be worth drawing */ if ((r.r_xtop - r.r_xbot < GR_THRESH) && (r.r_ytop - r.r_ybot < GR_THRESH) && (grCurFill != GR_STOUTLINE)) return; /* do diagonal lines for contacts */ if (grCurFill == GR_STCROSS) { Rect rnc; /* don't clip contact diagonals */ if (needClip || needObscure) { WindSurfaceToScreenNoClip(mw, &r2, &rnc); /* (*grDrawLinePtr)(rnc.r_xbot, rnc.r_ybot, rnc.r_xtop, rnc.r_ytop); (*grDrawLinePtr)(rnc.r_xbot, rnc.r_ytop, rnc.r_xtop, rnc.r_ybot); */ if (!IsSplit(tile)) { GrClipLine(rnc.r_xbot, rnc.r_ybot, rnc.r_xtop, rnc.r_ytop); GrClipLine(rnc.r_xbot, rnc.r_ytop, rnc.r_xtop, rnc.r_ybot); } } else { if (!IsSplit(tile)) { (*grDrawLinePtr)(r.r_xbot, r.r_ybot, r.r_xtop, r.r_ytop); (*grDrawLinePtr)(r.r_xbot, r.r_ytop, r.r_xtop, r.r_ybot); } } } /* draw outlines */ if (grCurOutline != 0) { if (GrBoxOutline(tile, &tilesegs)) { /* simple box (from GrFastBox)*/ if (needClip || needObscure) { GrClipLine(r.r_xbot, r.r_ytop, r.r_xtop, r.r_ytop); GrClipLine(r.r_xbot, r.r_ybot, r.r_xtop, r.r_ybot); GrClipLine(r.r_xbot, r.r_ybot, r.r_xbot, r.r_ytop); GrClipLine(r.r_xtop, r.r_ybot, r.r_xtop, r.r_ytop); } else { (*grDrawLinePtr)(r.r_xbot, r.r_ytop, r.r_xtop, r.r_ytop); (*grDrawLinePtr)(r.r_xbot, r.r_ybot, r.r_xtop, r.r_ybot); (*grDrawLinePtr)(r.r_xbot, r.r_ybot, r.r_xbot, r.r_ytop); (*grDrawLinePtr)(r.r_xtop, r.r_ybot, r.r_xtop, r.r_ytop); } } else { /* non-rectangular box; requires drawing segments */ for (segptr = tilesegs; segptr != NULL; segptr = segptr->r_next) { GeoTransRect(trans, &segptr->r_r, &r2); WindSurfaceToScreen(mw, &r2, &r); if (needClip || needObscure) GrClipLine(r.r_xbot, r.r_ybot, r.r_xtop, r.r_ytop); else (*grDrawLinePtr)(r.r_xbot, r.r_ybot, r.r_xtop, r.r_ytop); /* Free memory, if it was allocated for outline segments */ freeMagic(segptr); } /* For non-manhattan tiles, the manhattan parts of the */ /* boundary have already been drawn. The diagonal boundary */ /* is guaranteed to be continuous by definition, and it has */ /* already undergone clipping with GrClipTriangle. So just */ /* draw it, now. */ if (IsSplit(tile)) { int cp; for (cp = 0; cp < np - 1; cp++) { if ((polyp[cp].p_x != polyp[cp + 1].p_x) && (polyp[cp].p_y != polyp[cp + 1].p_y)) { (*grDrawLinePtr)(polyp[cp].p_x, polyp[cp].p_y, polyp[cp + 1].p_x, polyp[cp + 1].p_y); break; /* only 1 diagonal line to draw */ } } if (cp == (np - 1)) { if ((polyp[cp].p_x != polyp[0].p_x) && (polyp[cp].p_y != polyp[0].p_y)) (*grDrawLinePtr)(polyp[cp].p_x, polyp[cp].p_y, polyp[0].p_x, polyp[0].p_y); } } } } } /*--------------------------------------------------------- * GrDrawFastBox: * GrDrawFastBox will draw a rectangle on the screen in the * style set by the previous call to GrSetStuff. It will also * be clipped against the rectangles passed to GrSetStuff. * * If GrClipBox is called between GrDrawFastBox calls then GrSetStuff * must be called to set the parameters back. * * Usually this is called as GrFastBox, defined as GrDrawFastBox(p, 0). * The "scale" is only used to reduce the size of crosses drawn at * point positions, to prevent point labels from dominating a layout * in top-level views. * * Results: None. * * Side Effects: * The rectangle is drawn in the style specified. * The rectangle is clipped before being drawn. *--------------------------------------------------------- */ void GrDrawFastBox(prect, scale) Rect *prect; /* The rectangle to be drawn, given in * screen coordinates. */ int scale; /* If < 0, we reduce the cross size for * points according to the (negative) scale, * so point labels don't dominate a top-level * layout. */ { Rect *r; bool needClip, needObscure; LinkedRect *ob; GR_CHECK_LOCK(); if (!grDriverInformed) grInformDriver(); GrNumClipBoxes++; if (grCurFill == GR_STGRID) { r = &grCurClip; grGridRect = prect; } else { r = prect; if (!GEO_TOUCH(r, &grCurClip)) return; } /* Do a quick check to make the (very common) special case of * no clipping go fast. */ needClip = !GEO_SURROUND(&grCurClip, r); needObscure = FALSE; for (ob = grCurObscure; ob != NULL; ob = ob->r_next) needObscure |= GEO_TOUCH(r, &(ob->r_r)); /* do solid areas */ if ( (grCurFill == GR_STSOLID) || (grCurFill == GR_STSTIPPLE) || (grCurFill == GR_STGRID) ) { Rect clipr; /* We have a filled area to deal with */ clipr = *r; if (needClip) GeoClip(&clipr, &grCurClip); if (needObscure) grObsBox(&clipr); else { if (grCurFill == GR_STGRID) (*grDrawGridPtr)(grGridRect, grCurOutline, &clipr); else (void) (*grFillRectPtr)(&clipr); } } /* return if rectangle is too small to see */ if ((r->r_xtop - r->r_xbot < GR_THRESH) && (r->r_ytop - r->r_ybot < GR_THRESH) && (grCurFill != GR_STOUTLINE)) return; /* draw outlines */ if ( (grCurOutline != 0) && (grCurFill != GR_STGRID) ) { if ( (grCurFill == GR_STOUTLINE) && (r->r_xbot == r->r_xtop) && (r->r_ybot == r->r_ytop) ) { int crossSize = GR_CROSSSIZE; if (scale < 0) { crossSize += scale; if (crossSize < 0) goto endit; } /* turn the outline into a cross */ if (needClip || needObscure) goto clipit; else { bool crossClip, crossObscure; Rect crossBox; /* check the larger cross area for clipping */ crossBox.r_xbot = r->r_xbot - crossSize; crossBox.r_ybot = r->r_ybot - crossSize; crossBox.r_xtop = r->r_xtop + crossSize; crossBox.r_ytop = r->r_ytop + crossSize; crossClip = !GEO_SURROUND(&grCurClip, &crossBox); crossObscure = FALSE; for (ob = grCurObscure; ob != NULL; ob = ob->r_next) crossObscure |= GEO_TOUCH(&crossBox, &(ob->r_r)); if (crossClip || crossObscure) goto clipit; else goto noclipit; } clipit: GrClipLine(r->r_xbot, r->r_ybot - crossSize, r->r_xtop, r->r_ytop + crossSize - 1 + GrPixelCorrect); GrClipLine(r->r_xbot - crossSize, r->r_ybot, r->r_xtop + crossSize - 1 + GrPixelCorrect, r->r_ytop); goto endit; noclipit: (*grDrawLinePtr)(r->r_xbot, r->r_ybot - crossSize, r->r_xtop, r->r_ytop + crossSize - 1 + GrPixelCorrect); (*grDrawLinePtr)(r->r_xbot - crossSize, r->r_ybot, r->r_xtop + crossSize - 1 + GrPixelCorrect, r->r_ytop); endit: ; } else { if (needClip || needObscure) { GrClipLine(r->r_xbot, r->r_ytop, r->r_xtop, r->r_ytop); GrClipLine(r->r_xbot, r->r_ybot, r->r_xtop, r->r_ybot); GrClipLine(r->r_xbot, r->r_ybot, r->r_xbot, r->r_ytop); GrClipLine(r->r_xtop, r->r_ybot, r->r_xtop, r->r_ytop); } else { (*grDrawLinePtr)(r->r_xbot, r->r_ytop, r->r_xtop, r->r_ytop); (*grDrawLinePtr)(r->r_xbot, r->r_ybot, r->r_xtop, r->r_ybot); (*grDrawLinePtr)(r->r_xbot, r->r_ybot, r->r_xbot, r->r_ytop); (*grDrawLinePtr)(r->r_xtop, r->r_ybot, r->r_xtop, r->r_ytop); } } } /* do diagonal lines for contacts */ if (grCurFill == GR_STCROSS) { if (needClip || needObscure) { GrClipLine(r->r_xbot, r->r_ybot, r->r_xtop, r->r_ytop); GrClipLine(r->r_xbot, r->r_ytop, r->r_xtop, r->r_ybot); } else { (*grDrawLinePtr)(r->r_xbot, r->r_ybot, r->r_xtop, r->r_ytop); (*grDrawLinePtr)(r->r_xbot, r->r_ytop, r->r_xtop, r->r_ybot); } } } /*--------------------------------------------------------- * GrClipTriangle: * Returns an array of points representing a clipped * triangle. These are always arranged in counter- * clockwise order. * * Results: * None. * * Side effects: * Returns array of points and modifies int * np * to hold the number of points in the array. *--------------------------------------------------------- */ #define xround(d) (int)(((((d % height) << 1) >= height) ? 1 : 0) + (d / height)) #define yround(d) (int)(((((d % width) << 1) >= width) ? 1 : 0) + (d / width)) void GrClipTriangle(r, c, clipped, dinfo, points, np) Rect *r; /* Bounding box of triangle, in screen coords */ Rect *c; /* Clipping rectangle */ bool clipped; /* Boolean, if bounding box is clipped */ TileType dinfo; /* Split side and direction information */ Point *points; /* Point array (up to 5 points) to fill */ int *np; /* Number of points in the clipped polygon */ { if (!(dinfo & TT_SIDE)) { points[1].p_x = r->r_xbot; points[0].p_y = r->r_ytop; points[2].p_y = r->r_ybot; points[0].p_x = points[2].p_x = r->r_xtop; } else { points[1].p_x = r->r_xtop; points[0].p_y = r->r_ybot; points[2].p_y = r->r_ytop; points[0].p_x = points[2].p_x = r->r_xbot; } if (!(dinfo & TT_DIRECTION)) { points[1].p_y = points[0].p_y; points[2].p_x = points[1].p_x; } else { points[0].p_x = points[1].p_x; points[1].p_y = points[2].p_y; } *np = 3; /* Clip the triangle to the clipping rectangle. Result is */ /* a 3- to 5-sided polygon, or empty. */ if (clipped) { dlong delx, dely; dlong width = (dlong)(r->r_xtop - r->r_xbot); dlong height = (dlong)(r->r_ytop - r->r_ybot); switch(dinfo & (TT_DIAGONAL | TT_DIRECTION | TT_SIDE)) { case TT_DIAGONAL: /* nw */ if (c->r_ytop < r->r_ytop) /* clip top */ { delx = (dlong)(points[1].p_y - c->r_ytop) * width; points[1].p_y = c->r_ytop; points[0].p_y = c->r_ytop; points[0].p_x -= xround(delx); } if (c->r_xbot > r->r_xbot) /* clip left */ { dely = (dlong)(c->r_xbot - points[2].p_x) * height; points[1].p_x = c->r_xbot; points[2].p_x = c->r_xbot; points[2].p_y += yround(dely); } if (c->r_ybot > points[2].p_y) /* clip bottom */ { delx = (dlong)(c->r_ybot - points[2].p_y) * width; points[2].p_y = c->r_ybot; points[3].p_y = c->r_ybot; points[3].p_x = points[2].p_x + xround(delx); *np = 4; if (c->r_xtop < points[3].p_x) /* clip right, rectangle */ { points[3].p_x = c->r_xtop; points[0].p_x = c->r_xtop; } else if (c->r_xtop < points[0].p_x) /* clip right, pentagon */ { dely = (dlong)(points[0].p_x - c->r_xtop) * height; points[0].p_x = c->r_xtop; points[4].p_x = c->r_xtop; points[4].p_y = points[0].p_y - yround(dely); *np = 5; } } else if (c->r_xtop < points[0].p_x) /* clip right, quadrangle */ { dely = (dlong)(points[0].p_x - c->r_xtop) * height; points[0].p_x = c->r_xtop; points[3].p_x = c->r_xtop; points[3].p_y = points[0].p_y - yround(dely); *np = 4; } if (points[1].p_x > points[0].p_x || points[2].p_y > points[1].p_y) *np = 0; /* clipped out of existence */ break; case TT_DIAGONAL | TT_DIRECTION: /* sw */ if (c->r_xbot > r->r_xbot) /* clip LEFT */ { dely = (dlong)(c->r_xbot - points[1].p_x) * height; points[1].p_x = c->r_xbot; points[0].p_x = c->r_xbot; points[0].p_y -= yround(dely); } if (c->r_ybot > r->r_ybot) /* clip BOTTOM */ { delx = (dlong)(c->r_ybot - points[2].p_y) * width; points[1].p_y = c->r_ybot; points[2].p_y = c->r_ybot; points[2].p_x -= xround(delx); } if (c->r_xtop < points[2].p_x) /* clip RIGHT */ { dely = (dlong)(points[2].p_x - c->r_xtop) * height; points[2].p_x = c->r_xtop; points[3].p_x = c->r_xtop; points[3].p_y = points[1].p_y + yround(dely); *np = 4; if (c->r_ytop < points[3].p_y) /* clip TOP, rectangle */ { points[3].p_y = c->r_ytop; points[0].p_y = c->r_ytop; } else if (c->r_ytop < points[0].p_y) /* clip TOP, pentagon */ { delx = (dlong)(points[0].p_y - c->r_ytop) * width; points[0].p_y = c->r_ytop; points[4].p_y = c->r_ytop; points[4].p_x = points[0].p_x + xround(delx); *np = 5; } } else if (c->r_ytop < points[0].p_y) /* clip TOP, quadrangle */ { delx = (dlong)(points[0].p_y - c->r_ytop) * width; points[0].p_y = c->r_ytop; points[3].p_y = c->r_ytop; points[3].p_x = points[0].p_x + xround(delx); *np = 4; } if (points[1].p_y > points[0].p_y || points[2].p_x < points[1].p_x) *np = 0; /* clipped out of existence */ break; case TT_DIAGONAL | TT_SIDE: /* se */ /* order: (bottom, right), (top, left) */ if (c->r_ybot > r->r_ybot) /* clip BOTTOM */ { delx = (dlong)(c->r_ybot - points[1].p_y) * width; points[1].p_y = c->r_ybot; points[0].p_y = c->r_ybot; points[0].p_x += xround(delx); } if (c->r_xtop < r->r_xtop) /* clip RIGHT */ { dely = (dlong)(points[2].p_x - c->r_xtop) * height; points[1].p_x = c->r_xtop; points[2].p_x = c->r_xtop; points[2].p_y -= yround(dely); } if (c->r_ytop < points[2].p_y) /* clip TOP */ { delx = (dlong)(points[2].p_y - c->r_ytop) * width; points[2].p_y = c->r_ytop; points[3].p_y = c->r_ytop; points[3].p_x = points[2].p_x - xround(delx); *np = 4; if (c->r_xbot > points[3].p_x) /* clip LEFT, rectangle */ { points[3].p_x = c->r_xbot; points[0].p_x = c->r_xbot; } else if (c->r_xbot > points[0].p_x) /* clip LEFT, pentagon */ { dely = (dlong)(c->r_xbot - points[0].p_x) * height; points[0].p_x = c->r_xbot; points[4].p_x = c->r_xbot; points[4].p_y = points[0].p_y + yround(dely); *np = 5; } } else if (c->r_xbot > points[0].p_x) /* clip LEFT, triangle */ { dely = (dlong)(c->r_xbot - points[0].p_x) * height; points[0].p_x = c->r_xbot; points[3].p_x = c->r_xbot; points[3].p_y = points[0].p_y + yround(dely); *np = 4; } if (points[0].p_x > points[1].p_x || points[1].p_y > points[2].p_y) *np = 0; /* clipped out of existence */ break; case TT_DIAGONAL | TT_SIDE | TT_DIRECTION: /* ne */ /* order: (top, right), (bottom, left) */ if (c->r_xtop < r->r_xtop) /* clip RIGHT */ { dely = (dlong)(points[1].p_x - c->r_xtop) * height; points[1].p_x = c->r_xtop; points[0].p_x = c->r_xtop; points[0].p_y += yround(dely); } if (c->r_ytop < r->r_ytop) /* clip TOP */ { delx = (dlong)(points[2].p_y - c->r_ytop) * width; points[1].p_y = c->r_ytop; points[2].p_y = c->r_ytop; points[2].p_x += xround(delx); } if (c->r_xbot > points[2].p_x) /* clip LEFT */ { dely = (dlong)(c->r_xbot - points[2].p_x) * height; points[2].p_x = c->r_xbot; points[3].p_x = c->r_xbot; points[3].p_y = points[2].p_y - yround(dely); *np = 4; if (c->r_ybot >= points[3].p_y) /* clip BOTTOM, rectangle */ { points[3].p_y = c->r_ybot; points[0].p_y = c->r_ybot; } else if (c->r_ybot > points[0].p_y) /* clip BOTTOM, pentagon */ { delx = (dlong)(c->r_ybot - points[0].p_y) * width; points[0].p_y = c->r_ybot; points[4].p_y = c->r_ybot; points[4].p_x = points[0].p_x - xround(delx); *np = 5; } } else if (c->r_ybot > points[0].p_y) /* clip BOTTOM, quadrangle */ { delx = (dlong)(c->r_ybot - points[0].p_y) * width; points[0].p_y = c->r_ybot; points[3].p_y = c->r_ybot; points[3].p_x = points[0].p_x - xround(delx); *np = 4; } if (points[1].p_y < points[0].p_y || points[2].p_x > points[1].p_x) *np = 0; /* clipped out of existence */ break; } } } /*--------------------------------------------------------- * GrDrawTriangleEdge -- * Draws the diagonal line in a triangle clipped by * grCurClip. * * Results: * None. * * Side effects: * Draws nonmanhattan layout geometry. * *--------------------------------------------------------- */ void GrDrawTriangleEdge(r, dinfo) Rect *r; /* Bounding box of triangle, in screen coords */ TileType dinfo; { Point tpoints[5]; int tnum, i, j; GrClipTriangle(r, &grCurClip, TRUE, dinfo, tpoints, &tnum); for (i = 0; i < tnum; i++) { j = (i + 1) % tnum; if (tpoints[i].p_x != tpoints[j].p_x && tpoints[i].p_y != tpoints[j].p_y) { GrClipLine(tpoints[i].p_x, tpoints[i].p_y, tpoints[j].p_x, tpoints[j].p_y); break; } } } /*--------------------------------------------------------- * GrDiagonal: * GrDiagonal will draw a triangle on the screen in the * style set by the previous call to GrSetStuff. It will also * be clipped against the rectangles passed to GrSetStuff. * * If GrDiagonal is called between GrFastBox calls then GrSetStuff * must be called to set the parameters back. * * Results: * None. * * Side Effects: * The rectangle is drawn in the style specified. * The rectangle is clipped before being drawn. *--------------------------------------------------------- */ void GrDiagonal(prect, dinfo) Rect *prect; /* The rectangle to be drawn, given in * screen coordinates. */ TileType dinfo; /* split and direction information */ { Rect *r; bool needClip, needObscure; LinkedRect *ob; int cp, np; Rect clipr, fullr; Point polyp[5]; GR_CHECK_LOCK(); if (!grDriverInformed) grInformDriver(); GrNumClipBoxes++; if (grCurFill == GR_STGRID) { r = &grCurClip; grGridRect = prect; } else { r = prect; if (!GEO_TOUCH(r, &grCurClip)) return; } /* Do a quick check to make the (very common) special case of * no clipping go fast. */ needClip = !GEO_SURROUND(&grCurClip, r); needObscure = FALSE; for (ob = grCurObscure; ob != NULL; ob = ob->r_next) needObscure |= GEO_TOUCH(r, &(ob->r_r)); /* Generate points for the triangle and do clipping if necessary */ clipr = fullr = *r; if (needClip) GeoClip(&clipr, &grCurClip); GrClipTriangle(&fullr, &clipr, needClip, dinfo, polyp, &np); /* do solid areas */ if ( (grCurFill == GR_STSOLID) || (grCurFill == GR_STSTIPPLE) || (grCurFill == GR_STGRID) ) { if (needObscure) grObsBox(&clipr); else if (grFillPolygonPtr) (void) (*grFillPolygonPtr)(polyp, np); } /* return if rectangle is too small to see */ if ((r->r_xtop - r->r_xbot < GR_THRESH) && (r->r_ytop - r->r_ybot < GR_THRESH) && (grCurFill != GR_STOUTLINE)) return; /* draw outlines */ if ( (grCurOutline != 0) && (grCurFill != GR_STGRID) ) { /* TO DO: Check for boundary with shared tile type */ for (cp = 0; cp < np - 1; cp++) (*grDrawLinePtr)(polyp[cp].p_x, polyp[cp].p_y, polyp[cp + 1].p_x, polyp[cp + 1].p_y); (*grDrawLinePtr)(polyp[cp].p_x, polyp[cp].p_y, polyp[0].p_x, polyp[0].p_y); } } /*--------------------------------------------------------- * GrFillPolygon -- * This routine is simply a call to the locally-defined * grFillPolygonPtr routine. * *--------------------------------------------------------- */ void GrFillPolygon(polyp, np) Point *polyp; /* Array of points defining polygon */ int np; /* number of points in array polyp */ { if (grFillPolygonPtr != NULL) { (*grFillPolygonPtr)(polyp, np); } } /*--------------------------------------------------------- * GrClipBox: * GrClipBox will draw a rectangle on the screen in one * of several possible styles, except that the rectangle will * be clipped against a list of obscuring rectangles. * * Results: None. * * Side Effects: * The rectangle is drawn in the style specified. * The rectangle is clipped before being drawn. *--------------------------------------------------------- */ void GrClipBox(prect, style) Rect *prect; /* The rectangle to be drawn, given in * screen coordinates. */ int style; /* The style to be used in drawing it. */ { GrSetStuff(style); GrFastBox(prect); } /* * ---------------------------------------------------------------------------- * GrDisjoint -- * * Clip a rectanglular area against a clipping box, applying the * supplied procedure to each rectangular region in "area" which * falls outside "clipbox". This works in pixel space, where a * rectangle is contains its lower x- and y-coordinates AND ALSO * its upper coordinates. This means that if the clipping box * occupies a given pixel, the things being clipped must not occupy * that pixel. This procedure will NOT work in tile space. * * The procedure should be of the form: * bool func(box, cdarg) * Rect * box; * ClientData cdarg; * * Results: * Return TRUE unless the supplied function returns FALSE. * * Side effects: * The side effects of the invoked procedure. * ---------------------------------------------------------------------------- */ bool GrDisjoint(area, clipBox, func, cdarg) Rect * area; Rect * clipBox; bool (*func) (); ClientData cdarg; { Rect ok, rArea; bool result; #define NULLBOX(R) ((R.r_xbot>R.r_xtop)||(R.r_ybot>R.r_ytop)) ASSERT((area!=(Rect *) NULL), "GrDisjoint"); if((clipBox==(Rect *) NULL)||(!GEO_TOUCH(area, clipBox))) { /* Since there is no overlap, all of "area" may be processed. */ result= (*func)(area, cdarg); return(result); } /* Do the disjoint operation in four steps, one for each side * of clipBox. In each step, divide the area being clipped * into one piece that is DEFINITELY outside clipBox, and one * piece left to check some more. */ /* Top edge of clipBox: */ rArea = *area; result = TRUE; if (clipBox->r_ytop < rArea.r_ytop) { ok = rArea; ok.r_ybot = clipBox->r_ytop + 1; rArea.r_ytop = clipBox->r_ytop; if (!(*func)(&ok, cdarg)) result = FALSE; } /* Bottom edge of clipBox: */ if (clipBox->r_ybot > rArea.r_ybot) { ok = rArea; ok.r_ytop = clipBox->r_ybot - 1; rArea.r_ybot = clipBox->r_ybot; if (!(*func)(&ok, cdarg)) result = FALSE; } /* Right edge of clipBox: */ if (clipBox->r_xtop < rArea.r_xtop) { ok = rArea; ok.r_xbot = clipBox->r_xtop + 1; rArea.r_xtop = clipBox->r_xtop; if (!(*func)(&ok, cdarg)) result = FALSE; } /* Left edge of clipBox: */ if (clipBox->r_xbot > rArea.r_xbot) { ok = rArea; ok.r_xtop = clipBox->r_xbot - 1; rArea.r_xbot = clipBox->r_xbot; if (!(*func)(&ok, cdarg)) result = FALSE; } /* Just throw away what's left of the area being clipped, since * it overlaps the clipBox. */ return result; } /*GrDisjoint*/