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
50 #include <gdkregion.h>
51 #include "gdkregion-generic.h"
52 #include "gdkpoly-generic.h"
58 * Insert the given edge into the edge table.
59 * First we must find the correct bucket in the
60 * Edge table, then find the right slot in the
61 * bucket. Finally, we can insert it.
65 InsertEdgeInET (EdgeTable *ET,
68 ScanLineListBlock **SLLBlock,
71 EdgeTableEntry *start, *prev;
72 ScanLineList *pSLL, *pPrevSLL;
73 ScanLineListBlock *tmpSLLBlock;
76 * find the right bucket to put the edge into
78 pPrevSLL = &ET->scanlines;
79 pSLL = pPrevSLL->next;
80 while (pSLL && (pSLL->scanline < scanline))
87 * reassign pSLL (pointer to ScanLineList) if necessary
89 if ((!pSLL) || (pSLL->scanline > scanline))
91 if (*iSLLBlock > SLLSPERBLOCK-1)
94 (ScanLineListBlock *)g_malloc(sizeof(ScanLineListBlock));
95 (*SLLBlock)->next = tmpSLLBlock;
96 tmpSLLBlock->next = (ScanLineListBlock *)NULL;
97 *SLLBlock = tmpSLLBlock;
100 pSLL = &((*SLLBlock)->SLLs[(*iSLLBlock)++]);
102 pSLL->next = pPrevSLL->next;
103 pSLL->edgelist = (EdgeTableEntry *)NULL;
104 pPrevSLL->next = pSLL;
106 pSLL->scanline = scanline;
109 * now insert the edge in the right bucket
111 prev = (EdgeTableEntry *)NULL;
112 start = pSLL->edgelist;
113 while (start && (start->bres.minor_axis < ETE->bres.minor_axis))
123 pSLL->edgelist = ETE;
129 * This routine creates the edge table for
130 * scan converting polygons.
131 * The Edge Table (ET) looks like:
135 * | ymax | ScanLineLists
136 * |scanline|-->------------>-------------->...
137 * -------- |scanline| |scanline|
138 * |edgelist| |edgelist|
139 * --------- ---------
143 * list of ETEs list of ETEs
145 * where ETE is an EdgeTableEntry data structure,
146 * and there is one ScanLineList per scanline at
147 * which an edge is initially entered.
152 CreateETandAET (int count,
156 EdgeTableEntry *pETEs,
157 ScanLineListBlock *pSLLBlock)
159 const GdkPoint *top, *bottom;
160 const 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.
243 loadAET(EdgeTableEntry *AET,
244 EdgeTableEntry *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---> ...
291 computeWAET (EdgeTableEntry *AET)
293 EdgeTableEntry *pWETE;
297 AET->nextWETE = (EdgeTableEntry *)NULL;
307 if ((!inside && !isInside) ||
308 ( inside && isInside))
310 pWETE->nextWETE = AET;
316 pWETE->nextWETE = (EdgeTableEntry *)NULL;
322 * Just a simple insertion sort using
323 * pointers and back pointers to sort the Active
329 InsertionSort (EdgeTableEntry *AET)
331 EdgeTableEntry *pETEchase;
332 EdgeTableEntry *pETEinsert;
333 EdgeTableEntry *pETEchaseBackTMP;
341 while (pETEchase->back->bres.minor_axis > AET->bres.minor_axis)
342 pETEchase = pETEchase->back;
345 if (pETEchase != pETEinsert)
347 pETEchaseBackTMP = pETEchase->back;
348 pETEinsert->back->next = AET;
350 AET->back = pETEinsert->back;
351 pETEinsert->next = pETEchase;
352 pETEchase->back->next = pETEinsert;
353 pETEchase->back = pETEinsert;
354 pETEinsert->back = pETEchaseBackTMP;
365 FreeStorage (ScanLineListBlock *pSLLBlock)
367 ScanLineListBlock *tmpSLLBlock;
371 tmpSLLBlock = pSLLBlock->next;
373 pSLLBlock = tmpSLLBlock;
378 * Create an array of rectangles from a list of points.
379 * If indeed these things (POINTS, RECTS) are the same,
380 * then this proc is still needed, because it allocates
381 * storage for the array, which was allocated on the
382 * stack by the calling procedure.
386 PtsToRegion (int numFullPtBlocks,
388 POINTBLOCK *FirstPtBlock,
393 POINTBLOCK *CurPtBlock;
395 GdkRegionBox *extents;
398 extents = ®->extents;
400 numRects = ((numFullPtBlocks * NUMPTSTOBUFFER) + iCurPtBlock) >> 1;
402 GROWREGION(reg, numRects);
404 CurPtBlock = FirstPtBlock;
405 rects = reg->rects - 1;
407 extents->x1 = G_MAXSHORT, extents->x2 = G_MINSHORT;
409 for ( ; numFullPtBlocks >= 0; numFullPtBlocks--) {
410 /* the loop uses 2 points per iteration */
411 i = NUMPTSTOBUFFER >> 1;
412 if (!numFullPtBlocks)
413 i = iCurPtBlock >> 1;
414 for (pts = CurPtBlock->pts; i--; pts += 2) {
415 if (pts->x == pts[1].x)
417 if (numRects && pts->x == rects->x1 && pts->y == rects->y2 &&
418 pts[1].x == rects->x2 &&
419 (numRects == 1 || rects[-1].y1 != rects->y1) &&
420 (i && pts[2].y > pts[1].y)) {
421 rects->y2 = pts[1].y + 1;
426 rects->x1 = pts->x; rects->y1 = pts->y;
427 rects->x2 = pts[1].x; rects->y2 = pts[1].y + 1;
428 if (rects->x1 < extents->x1)
429 extents->x1 = rects->x1;
430 if (rects->x2 > extents->x2)
431 extents->x2 = rects->x2;
433 CurPtBlock = CurPtBlock->next;
437 extents->y1 = reg->rects->y1;
438 extents->y2 = rects->y2;
445 reg->numRects = numRects;
451 * gdk_region_polygon:
452 * @points: an array of #GdkPoint structs
453 * @n_points: the number of elements in the @points array
454 * @fill_rule: specifies which pixels are included in the region when the
455 * polygon overlaps itself.
457 * Creates a new #GdkRegion using the polygon defined by a
460 * Returns: a new #GdkRegion based on the given polygon
463 gdk_region_polygon (const GdkPoint *points,
465 GdkFillRule fill_rule)
468 EdgeTableEntry *pAET; /* Active Edge Table */
469 int y; /* current scanline */
470 int iPts = 0; /* number of pts in buffer */
471 EdgeTableEntry *pWETE; /* Winding Edge Table Entry*/
472 ScanLineList *pSLL; /* current scanLineList */
473 GdkPoint *pts; /* output buffer */
474 EdgeTableEntry *pPrevAET; /* ptr to previous AET */
475 EdgeTable ET; /* header node for ET */
476 EdgeTableEntry AET; /* header node for AET */
477 EdgeTableEntry *pETEs; /* EdgeTableEntries pool */
478 ScanLineListBlock SLLBlock; /* header for scanlinelist */
480 POINTBLOCK FirstPtBlock, *curPtBlock; /* PtBlock buffers */
481 POINTBLOCK *tmpPtBlock;
482 int numFullPtBlocks = 0;
484 region = gdk_region_new ();
486 /* special case a rectangle */
487 if (((n_points == 4) ||
488 ((n_points == 5) && (points[4].x == points[0].x) && (points[4].y == points[0].y))) &&
489 (((points[0].y == points[1].y) &&
490 (points[1].x == points[2].x) &&
491 (points[2].y == points[3].y) &&
492 (points[3].x == points[0].x)) ||
493 ((points[0].x == points[1].x) &&
494 (points[1].y == points[2].y) &&
495 (points[2].x == points[3].x) &&
496 (points[3].y == points[0].y)))) {
497 region->extents.x1 = MIN(points[0].x, points[2].x);
498 region->extents.y1 = MIN(points[0].y, points[2].y);
499 region->extents.x2 = MAX(points[0].x, points[2].x);
500 region->extents.y2 = MAX(points[0].y, points[2].y);
501 if ((region->extents.x1 != region->extents.x2) &&
502 (region->extents.y1 != region->extents.y2)) {
503 region->numRects = 1;
504 *(region->rects) = region->extents;
509 pETEs = g_new (EdgeTableEntry, n_points);
511 pts = FirstPtBlock.pts;
512 CreateETandAET(n_points, points, &ET, &AET, pETEs, &SLLBlock);
513 pSLL = ET.scanlines.next;
514 curPtBlock = &FirstPtBlock;
516 if (fill_rule == GDK_EVEN_ODD_RULE) {
520 for (y = ET.ymin; y < ET.ymax; y++) {
522 * Add a new edge to the active edge table when we
523 * get to the next edge.
525 if (pSLL != NULL && y == pSLL->scanline) {
526 loadAET(&AET, pSLL->edgelist);
533 * for each active edge
536 pts->x = pAET->bres.minor_axis, pts->y = y;
540 * send out the buffer
542 if (iPts == NUMPTSTOBUFFER) {
543 tmpPtBlock = (POINTBLOCK *)g_malloc(sizeof(POINTBLOCK));
544 tmpPtBlock->next = NULL;
545 curPtBlock->next = tmpPtBlock;
546 curPtBlock = tmpPtBlock;
547 pts = curPtBlock->pts;
551 EVALUATEEDGEEVENODD(pAET, pPrevAET, y);
553 (void) InsertionSort(&AET);
560 for (y = ET.ymin; y < ET.ymax; y++) {
562 * Add a new edge to the active edge table when we
563 * get to the next edge.
565 if (pSLL != NULL && y == pSLL->scanline) {
566 loadAET(&AET, pSLL->edgelist);
575 * for each active edge
579 * add to the buffer only those edges that
580 * are in the Winding active edge table.
583 pts->x = pAET->bres.minor_axis, pts->y = y;
587 * send out the buffer
589 if (iPts == NUMPTSTOBUFFER) {
590 tmpPtBlock = (POINTBLOCK *)g_malloc(sizeof(POINTBLOCK));
591 tmpPtBlock->next = NULL;
592 curPtBlock->next = tmpPtBlock;
593 curPtBlock = tmpPtBlock;
594 pts = curPtBlock->pts;
595 numFullPtBlocks++; iPts = 0;
597 pWETE = pWETE->nextWETE;
599 EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET);
603 * recompute the winding active edge table if
604 * we just resorted or have exited an edge.
606 if (InsertionSort(&AET) || fixWAET) {
612 FreeStorage(SLLBlock.next);
613 (void) PtsToRegion(numFullPtBlocks, iPts, &FirstPtBlock, region);
614 for (curPtBlock = FirstPtBlock.next; --numFullPtBlocks >= 0;) {
615 tmpPtBlock = curPtBlock->next;
617 curPtBlock = tmpPtBlock;
623 #define __GDK_POLYREG_GENERIC_C__
624 #include "gdkaliasdef.c"