1 /* $TOG: PolyReg.c /main/15 1998/02/06 17:47:08 kaleb $ */
2 /************************************************************************
4 Copyright 1987, 1998 The Open Group
8 The above copyright notice and this permission notice shall be included in
9 all copies or substantial portions of the Software.
11 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
12 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
13 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
14 OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
15 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
16 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
18 Except as contained in this notice, the name of The Open Group shall not be
19 used in advertising or otherwise to promote the sale, use or other dealings
20 in this Software without prior written authorization from The Open Group.
23 Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
27 Permission to use, copy, modify, and distribute this software and its
28 documentation for any purpose and without fee is hereby granted,
29 provided that the above copyright notice appear in all copies and that
30 both that copyright notice and this permission notice appear in
31 supporting documentation, and that the name of Digital not be
32 used in advertising or publicity pertaining to distribution of the
33 software without specific, written prior permission.
35 DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
36 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
37 DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
38 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
39 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
40 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
43 ************************************************************************/
44 /* $XFree86: xc/lib/X11/PolyReg.c,v 1.4 1998/10/03 08:41:21 dawes Exp $ */
46 #define LARGE_COORDINATE 1000000
47 #define SMALL_COORDINATE -LARGE_COORDINATE
49 #include <gdkregion.h>
50 #include "gdkregion-generic.h"
51 #include "gdkpoly-generic.h"
56 * Insert the given edge into the edge table.
57 * First we must find the correct bucket in the
58 * Edge table, then find the right slot in the
59 * bucket. Finally, we can insert it.
63 InsertEdgeInET(ET, ETE, scanline, SLLBlock, iSLLBlock)
67 ScanLineListBlock **SLLBlock;
70 EdgeTableEntry *start, *prev;
71 ScanLineList *pSLL, *pPrevSLL;
72 ScanLineListBlock *tmpSLLBlock;
75 * find the right bucket to put the edge into
77 pPrevSLL = &ET->scanlines;
78 pSLL = pPrevSLL->next;
79 while (pSLL && (pSLL->scanline < scanline))
86 * reassign pSLL (pointer to ScanLineList) if necessary
88 if ((!pSLL) || (pSLL->scanline > scanline))
90 if (*iSLLBlock > SLLSPERBLOCK-1)
93 (ScanLineListBlock *)g_malloc(sizeof(ScanLineListBlock));
94 (*SLLBlock)->next = tmpSLLBlock;
95 tmpSLLBlock->next = (ScanLineListBlock *)NULL;
96 *SLLBlock = tmpSLLBlock;
99 pSLL = &((*SLLBlock)->SLLs[(*iSLLBlock)++]);
101 pSLL->next = pPrevSLL->next;
102 pSLL->edgelist = (EdgeTableEntry *)NULL;
103 pPrevSLL->next = pSLL;
105 pSLL->scanline = scanline;
108 * now insert the edge in the right bucket
110 prev = (EdgeTableEntry *)NULL;
111 start = pSLL->edgelist;
112 while (start && (start->bres.minor_axis < ETE->bres.minor_axis))
122 pSLL->edgelist = ETE;
128 * This routine creates the edge table for
129 * scan converting polygons.
130 * The Edge Table (ET) looks like:
134 * | ymax | ScanLineLists
135 * |scanline|-->------------>-------------->...
136 * -------- |scanline| |scanline|
137 * |edgelist| |edgelist|
138 * --------- ---------
142 * list of ETEs list of ETEs
144 * where ETE is an EdgeTableEntry data structure,
145 * and there is one ScanLineList per scanline at
146 * which an edge is initially entered.
151 CreateETandAET(count, pts, ET, AET, pETEs, pSLLBlock)
156 EdgeTableEntry *pETEs;
157 ScanLineListBlock *pSLLBlock;
159 GdkPoint *top, *bottom;
160 GdkPoint *PrevPt, *CurrPt;
164 if (count < 2) return;
167 * initialize the Active Edge Table
169 AET->next = (EdgeTableEntry *)NULL;
170 AET->back = (EdgeTableEntry *)NULL;
171 AET->nextWETE = (EdgeTableEntry *)NULL;
172 AET->bres.minor_axis = SMALL_COORDINATE;
175 * initialize the Edge Table.
177 ET->scanlines.next = (ScanLineList *)NULL;
178 ET->ymax = SMALL_COORDINATE;
179 ET->ymin = LARGE_COORDINATE;
180 pSLLBlock->next = (ScanLineListBlock *)NULL;
182 PrevPt = &pts[count-1];
185 * for each vertex in the array of points.
186 * In this loop we are dealing with two vertices at
187 * a time -- these make up one edge of the polygon.
194 * find out which point is above and which is below.
196 if (PrevPt->y > CurrPt->y)
198 bottom = PrevPt, top = CurrPt;
199 pETEs->ClockWise = 0;
203 bottom = CurrPt, top = PrevPt;
204 pETEs->ClockWise = 1;
208 * don't add horizontal edges to the Edge table.
210 if (bottom->y != top->y)
212 pETEs->ymax = bottom->y-1; /* -1 so we don't get last scanline */
215 * initialize integer edge algorithm
217 dy = bottom->y - top->y;
218 BRESINITPGONSTRUCT(dy, top->x, bottom->x, pETEs->bres);
220 InsertEdgeInET(ET, pETEs, top->y, &pSLLBlock, &iSLLBlock);
222 if (PrevPt->y > ET->ymax)
223 ET->ymax = PrevPt->y;
224 if (PrevPt->y < ET->ymin)
225 ET->ymin = PrevPt->y;
236 * This routine moves EdgeTableEntries from the
237 * EdgeTable into the Active Edge Table,
238 * leaving them sorted by smaller x coordinate.
244 EdgeTableEntry *AET, *ETEs;
246 EdgeTableEntry *pPrevAET;
253 while (AET && (AET->bres.minor_axis < ETEs->bres.minor_axis))
262 ETEs->back = pPrevAET;
263 pPrevAET->next = ETEs;
273 * This routine links the AET by the
274 * nextWETE (winding EdgeTableEntry) link for
275 * use by the winding number rule. The final
276 * Active Edge Table (AET) might look something
280 * ---------- --------- ---------
281 * |ymax | |ymax | |ymax |
282 * | ... | |... | |... |
283 * |next |->|next |->|next |->...
284 * |nextWETE| |nextWETE| |nextWETE|
285 * --------- --------- ^--------
287 * V-------------------> V---> ...
294 EdgeTableEntry *pWETE;
298 AET->nextWETE = (EdgeTableEntry *)NULL;
308 if ((!inside && !isInside) ||
309 ( inside && isInside))
311 pWETE->nextWETE = AET;
317 pWETE->nextWETE = (EdgeTableEntry *)NULL;
323 * Just a simple insertion sort using
324 * pointers and back pointers to sort the Active
333 EdgeTableEntry *pETEchase;
334 EdgeTableEntry *pETEinsert;
335 EdgeTableEntry *pETEchaseBackTMP;
343 while (pETEchase->back->bres.minor_axis > AET->bres.minor_axis)
344 pETEchase = pETEchase->back;
347 if (pETEchase != pETEinsert)
349 pETEchaseBackTMP = pETEchase->back;
350 pETEinsert->back->next = AET;
352 AET->back = pETEinsert->back;
353 pETEinsert->next = pETEchase;
354 pETEchase->back->next = pETEinsert;
355 pETEchase->back = pETEinsert;
356 pETEinsert->back = pETEchaseBackTMP;
367 FreeStorage(pSLLBlock)
368 ScanLineListBlock *pSLLBlock;
370 ScanLineListBlock *tmpSLLBlock;
374 tmpSLLBlock = pSLLBlock->next;
376 pSLLBlock = tmpSLLBlock;
381 * Create an array of rectangles from a list of points.
382 * If indeed these things (POINTS, RECTS) are the same,
383 * then this proc is still needed, because it allocates
384 * storage for the array, which was allocated on the
385 * stack by the calling procedure.
388 static int PtsToRegion(numFullPtBlocks, iCurPtBlock, FirstPtBlock, reg)
389 int numFullPtBlocks, iCurPtBlock;
390 POINTBLOCK *FirstPtBlock;
395 POINTBLOCK *CurPtBlock;
397 GdkRegionBox *extents;
400 extents = ®->extents;
402 numRects = ((numFullPtBlocks * NUMPTSTOBUFFER) + iCurPtBlock) >> 1;
404 reg->rects = g_renew (GdkRegionBox, reg->rects, numRects);
406 reg->size = numRects;
407 CurPtBlock = FirstPtBlock;
408 rects = reg->rects - 1;
410 extents->x1 = G_MAXSHORT, extents->x2 = G_MINSHORT;
412 for ( ; numFullPtBlocks >= 0; numFullPtBlocks--) {
413 /* the loop uses 2 points per iteration */
414 i = NUMPTSTOBUFFER >> 1;
415 if (!numFullPtBlocks)
416 i = iCurPtBlock >> 1;
417 for (pts = CurPtBlock->pts; i--; pts += 2) {
418 if (pts->x == pts[1].x)
420 if (numRects && pts->x == rects->x1 && pts->y == rects->y2 &&
421 pts[1].x == rects->x2 &&
422 (numRects == 1 || rects[-1].y1 != rects->y1) &&
423 (i && pts[2].y > pts[1].y)) {
424 rects->y2 = pts[1].y + 1;
429 rects->x1 = pts->x; rects->y1 = pts->y;
430 rects->x2 = pts[1].x; rects->y2 = pts[1].y + 1;
431 if (rects->x1 < extents->x1)
432 extents->x1 = rects->x1;
433 if (rects->x2 > extents->x2)
434 extents->x2 = rects->x2;
436 CurPtBlock = CurPtBlock->next;
440 extents->y1 = reg->rects->y1;
441 extents->y2 = rects->y2;
448 reg->numRects = numRects;
456 * Scan converts a polygon by returning a run-length
457 * encoding of the resultant bitmap -- the run-length
458 * encoding is in the form of an array of rectangles.
461 gdk_region_polygon(GdkPoint *Pts, gint Count, GdkFillRule rule)
464 EdgeTableEntry *pAET; /* Active Edge Table */
465 int y; /* current scanline */
466 int iPts = 0; /* number of pts in buffer */
467 EdgeTableEntry *pWETE; /* Winding Edge Table Entry*/
468 ScanLineList *pSLL; /* current scanLineList */
469 GdkPoint *pts; /* output buffer */
470 EdgeTableEntry *pPrevAET; /* ptr to previous AET */
471 EdgeTable ET; /* header node for ET */
472 EdgeTableEntry AET; /* header node for AET */
473 EdgeTableEntry *pETEs; /* EdgeTableEntries pool */
474 ScanLineListBlock SLLBlock; /* header for scanlinelist */
476 POINTBLOCK FirstPtBlock, *curPtBlock; /* PtBlock buffers */
477 POINTBLOCK *tmpPtBlock;
478 int numFullPtBlocks = 0;
480 region = gdk_region_new ();
482 /* special case a rectangle */
485 ((Count == 5) && (pts[4].x == pts[0].x) && (pts[4].y == pts[0].y))) &&
486 (((pts[0].y == pts[1].y) &&
487 (pts[1].x == pts[2].x) &&
488 (pts[2].y == pts[3].y) &&
489 (pts[3].x == pts[0].x)) ||
490 ((pts[0].x == pts[1].x) &&
491 (pts[1].y == pts[2].y) &&
492 (pts[2].x == pts[3].x) &&
493 (pts[3].y == pts[0].y)))) {
494 region->extents.x1 = MIN(pts[0].x, pts[2].x);
495 region->extents.y1 = MIN(pts[0].y, pts[2].y);
496 region->extents.x2 = MAX(pts[0].x, pts[2].x);
497 region->extents.y2 = MAX(pts[0].y, pts[2].y);
498 if ((region->extents.x1 != region->extents.x2) &&
499 (region->extents.y1 != region->extents.y2)) {
500 region->numRects = 1;
501 *(region->rects) = region->extents;
506 pETEs = g_new (EdgeTableEntry, Count);
508 pts = FirstPtBlock.pts;
509 CreateETandAET(Count, Pts, &ET, &AET, pETEs, &SLLBlock);
510 pSLL = ET.scanlines.next;
511 curPtBlock = &FirstPtBlock;
513 if (rule == GDK_EVEN_ODD_RULE) {
517 for (y = ET.ymin; y < ET.ymax; y++) {
519 * Add a new edge to the active edge table when we
520 * get to the next edge.
522 if (pSLL != NULL && y == pSLL->scanline) {
523 loadAET(&AET, pSLL->edgelist);
530 * for each active edge
533 pts->x = pAET->bres.minor_axis, pts->y = y;
537 * send out the buffer
539 if (iPts == NUMPTSTOBUFFER) {
540 tmpPtBlock = (POINTBLOCK *)g_malloc(sizeof(POINTBLOCK));
541 curPtBlock->next = tmpPtBlock;
542 curPtBlock = tmpPtBlock;
543 pts = curPtBlock->pts;
547 EVALUATEEDGEEVENODD(pAET, pPrevAET, y);
549 (void) InsertionSort(&AET);
556 for (y = ET.ymin; y < ET.ymax; y++) {
558 * Add a new edge to the active edge table when we
559 * get to the next edge.
561 if (pSLL != NULL && y == pSLL->scanline) {
562 loadAET(&AET, pSLL->edgelist);
571 * for each active edge
575 * add to the buffer only those edges that
576 * are in the Winding active edge table.
579 pts->x = pAET->bres.minor_axis, pts->y = y;
583 * send out the buffer
585 if (iPts == NUMPTSTOBUFFER) {
586 tmpPtBlock = (POINTBLOCK *)g_malloc(sizeof(POINTBLOCK));
587 curPtBlock->next = tmpPtBlock;
588 curPtBlock = tmpPtBlock;
589 pts = curPtBlock->pts;
590 numFullPtBlocks++; iPts = 0;
592 pWETE = pWETE->nextWETE;
594 EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET);
598 * recompute the winding active edge table if
599 * we just resorted or have exited an edge.
601 if (InsertionSort(&AET) || fixWAET) {
607 FreeStorage(SLLBlock.next);
608 (void) PtsToRegion(numFullPtBlocks, iPts, &FirstPtBlock, region);
609 for (curPtBlock = FirstPtBlock.next; --numFullPtBlocks >= 0;) {
610 tmpPtBlock = curPtBlock->next;
612 curPtBlock = tmpPtBlock;