1 /************************************************************************
3 Copyright 1987, 1998 The Open Group
5 Permission to use, copy, modify, distribute, and sell this software and its
6 documentation for any purpose is hereby granted without fee, provided that
7 the above copyright notice appear in all copies and that both that
8 copyright notice and this permission notice appear in supporting
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 Except as contained in this notice, the name of The Open Group shall not be
22 used in advertising or otherwise to promote the sale, use or other dealings
23 in this Software without prior written authorization from The Open Group.
26 Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
30 Permission to use, copy, modify, and distribute this software and its
31 documentation for any purpose and without fee is hereby granted,
32 provided that the above copyright notice appear in all copies and that
33 both that copyright notice and this permission notice appear in
34 supporting documentation, and that the name of Digital not be
35 used in advertising or publicity pertaining to distribution of the
36 software without specific, written prior permission.
38 DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
39 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
40 DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
41 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
42 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
43 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
46 ************************************************************************/
48 #define LARGE_COORDINATE 1000000
49 #define SMALL_COORDINATE -LARGE_COORDINATE
56 #include <X11/Xregion.h>
62 * Insert the given edge into the edge table.
63 * First we must find the correct bucket in the
64 * Edge table, then find the right slot in the
65 * bucket. Finally, we can insert it.
73 ScanLineListBlock **SLLBlock,
76 register EdgeTableEntry *start, *prev;
77 register ScanLineList *pSLL, *pPrevSLL;
78 ScanLineListBlock *tmpSLLBlock;
81 * find the right bucket to put the edge into
83 pPrevSLL = &ET->scanlines;
84 pSLL = pPrevSLL->next;
85 while (pSLL && (pSLL->scanline < scanline))
92 * reassign pSLL (pointer to ScanLineList) if necessary
94 if ((!pSLL) || (pSLL->scanline > scanline))
96 if (*iSLLBlock > SLLSPERBLOCK-1)
99 (ScanLineListBlock *)Xmalloc(sizeof(ScanLineListBlock));
100 (*SLLBlock)->next = tmpSLLBlock;
101 tmpSLLBlock->next = (ScanLineListBlock *)NULL;
102 *SLLBlock = tmpSLLBlock;
105 pSLL = &((*SLLBlock)->SLLs[(*iSLLBlock)++]);
107 pSLL->next = pPrevSLL->next;
108 pSLL->edgelist = (EdgeTableEntry *)NULL;
109 pPrevSLL->next = pSLL;
111 pSLL->scanline = scanline;
114 * now insert the edge in the right bucket
116 prev = (EdgeTableEntry *)NULL;
117 start = pSLL->edgelist;
118 while (start && (start->bres.minor_axis < ETE->bres.minor_axis))
128 pSLL->edgelist = ETE;
134 * This routine creates the edge table for
135 * scan converting polygons.
136 * The Edge Table (ET) looks like:
140 * | ymax | ScanLineLists
141 * |scanline|-->------------>-------------->...
142 * -------- |scanline| |scanline|
143 * |edgelist| |edgelist|
144 * --------- ---------
148 * list of ETEs list of ETEs
150 * where ETE is an EdgeTableEntry data structure,
151 * and there is one ScanLineList per scanline at
152 * which an edge is initially entered.
159 register XPoint *pts,
162 register EdgeTableEntry *pETEs,
163 ScanLineListBlock *pSLLBlock)
165 register XPoint *top, *bottom;
166 register XPoint *PrevPt, *CurrPt;
170 if (count < 2) return;
173 * initialize the Active Edge Table
175 AET->next = (EdgeTableEntry *)NULL;
176 AET->back = (EdgeTableEntry *)NULL;
177 AET->nextWETE = (EdgeTableEntry *)NULL;
178 AET->bres.minor_axis = SMALL_COORDINATE;
181 * initialize the Edge Table.
183 ET->scanlines.next = (ScanLineList *)NULL;
184 ET->ymax = SMALL_COORDINATE;
185 ET->ymin = LARGE_COORDINATE;
186 pSLLBlock->next = (ScanLineListBlock *)NULL;
188 PrevPt = &pts[count-1];
191 * for each vertex in the array of points.
192 * In this loop we are dealing with two vertices at
193 * a time -- these make up one edge of the polygon.
200 * find out which point is above and which is below.
202 if (PrevPt->y > CurrPt->y)
204 bottom = PrevPt, top = CurrPt;
205 pETEs->ClockWise = 0;
209 bottom = CurrPt, top = PrevPt;
210 pETEs->ClockWise = 1;
214 * don't add horizontal edges to the Edge table.
216 if (bottom->y != top->y)
218 pETEs->ymax = bottom->y-1; /* -1 so we don't get last scanline */
221 * initialize integer edge algorithm
223 dy = bottom->y - top->y;
224 BRESINITPGONSTRUCT(dy, top->x, bottom->x, pETEs->bres);
226 InsertEdgeInET(ET, pETEs, top->y, &pSLLBlock, &iSLLBlock);
228 if (PrevPt->y > ET->ymax)
229 ET->ymax = PrevPt->y;
230 if (PrevPt->y < ET->ymin)
231 ET->ymin = PrevPt->y;
242 * This routine moves EdgeTableEntries from the
243 * EdgeTable into the Active Edge Table,
244 * leaving them sorted by smaller x coordinate.
250 register EdgeTableEntry *AET,
251 register EdgeTableEntry *ETEs)
253 register EdgeTableEntry *pPrevAET;
254 register EdgeTableEntry *tmp;
260 while (AET && (AET->bres.minor_axis < ETEs->bres.minor_axis))
269 ETEs->back = pPrevAET;
270 pPrevAET->next = ETEs;
280 * This routine links the AET by the
281 * nextWETE (winding EdgeTableEntry) link for
282 * use by the winding number rule. The final
283 * Active Edge Table (AET) might look something
287 * ---------- --------- ---------
288 * |ymax | |ymax | |ymax |
289 * | ... | |... | |... |
290 * |next |->|next |->|next |->...
291 * |nextWETE| |nextWETE| |nextWETE|
292 * --------- --------- ^--------
294 * V-------------------> V---> ...
299 register EdgeTableEntry *AET)
301 register EdgeTableEntry *pWETE;
302 register int inside = 1;
303 register int isInside = 0;
305 AET->nextWETE = (EdgeTableEntry *)NULL;
315 if ((!inside && !isInside) ||
316 ( inside && isInside))
318 pWETE->nextWETE = AET;
324 pWETE->nextWETE = (EdgeTableEntry *)NULL;
330 * Just a simple insertion sort using
331 * pointers and back pointers to sort the Active
338 register EdgeTableEntry *AET)
340 register EdgeTableEntry *pETEchase;
341 register EdgeTableEntry *pETEinsert;
342 register EdgeTableEntry *pETEchaseBackTMP;
343 register int changed = 0;
350 while (pETEchase->back->bres.minor_axis > AET->bres.minor_axis)
351 pETEchase = pETEchase->back;
354 if (pETEchase != pETEinsert)
356 pETEchaseBackTMP = pETEchase->back;
357 pETEinsert->back->next = AET;
359 AET->back = pETEinsert->back;
360 pETEinsert->next = pETEchase;
361 pETEchase->back->next = pETEinsert;
362 pETEchase->back = pETEinsert;
363 pETEinsert->back = pETEchaseBackTMP;
375 register ScanLineListBlock *pSLLBlock)
377 register ScanLineListBlock *tmpSLLBlock;
381 tmpSLLBlock = pSLLBlock->next;
382 Xfree((char *)pSLLBlock);
383 pSLLBlock = tmpSLLBlock;
388 * Create an array of rectangles from a list of points.
389 * If indeed these things (POINTS, RECTS) are the same,
390 * then this proc is still needed, because it allocates
391 * storage for the array, which was allocated on the
392 * stack by the calling procedure.
395 static int PtsToRegion(
396 register int numFullPtBlocks,
397 register int iCurPtBlock,
398 POINTBLOCK *FirstPtBlock,
402 register XPoint *pts;
403 register POINTBLOCK *CurPtBlock;
405 register BOX *extents;
406 register int numRects;
407 BOX *prevRects = reg->rects;
409 extents = ®->extents;
411 numRects = ((numFullPtBlocks * NUMPTSTOBUFFER) + iCurPtBlock) >> 1;
413 if (!(reg->rects = (BOX *)Xrealloc((char *)reg->rects,
414 (unsigned) (sizeof(BOX) * numRects)))) {
419 reg->size = numRects;
420 CurPtBlock = FirstPtBlock;
421 rects = reg->rects - 1;
423 extents->x1 = MAXSHORT, extents->x2 = MINSHORT;
425 for ( ; numFullPtBlocks >= 0; numFullPtBlocks--) {
426 /* the loop uses 2 points per iteration */
427 i = NUMPTSTOBUFFER >> 1;
428 if (!numFullPtBlocks)
429 i = iCurPtBlock >> 1;
430 for (pts = CurPtBlock->pts; i--; pts += 2) {
431 if (pts->x == pts[1].x)
433 if (numRects && pts->x == rects->x1 && pts->y == rects->y2 &&
434 pts[1].x == rects->x2 &&
435 (numRects == 1 || rects[-1].y1 != rects->y1) &&
436 (i && pts[2].y > pts[1].y)) {
437 rects->y2 = pts[1].y + 1;
442 rects->x1 = pts->x; rects->y1 = pts->y;
443 rects->x2 = pts[1].x; rects->y2 = pts[1].y + 1;
444 if (rects->x1 < extents->x1)
445 extents->x1 = rects->x1;
446 if (rects->x2 > extents->x2)
447 extents->x2 = rects->x2;
449 CurPtBlock = CurPtBlock->next;
453 extents->y1 = reg->rects->y1;
454 extents->y2 = rects->y2;
461 reg->numRects = numRects;
469 * Scan converts a polygon by returning a run-length
470 * encoding of the resultant bitmap -- the run-length
471 * encoding is in the form of an array of rectangles.
475 XPoint *Pts, /* the pts */
476 int Count, /* number of pts */
477 int rule) /* winding rule */
480 register EdgeTableEntry *pAET; /* Active Edge Table */
481 register int y; /* current scanline */
482 register int iPts = 0; /* number of pts in buffer */
483 register EdgeTableEntry *pWETE; /* Winding Edge Table Entry*/
484 register ScanLineList *pSLL; /* current scanLineList */
485 register XPoint *pts; /* output buffer */
486 EdgeTableEntry *pPrevAET; /* ptr to previous AET */
487 EdgeTable ET; /* header node for ET */
488 EdgeTableEntry AET; /* header node for AET */
489 EdgeTableEntry *pETEs; /* EdgeTableEntries pool */
490 ScanLineListBlock SLLBlock; /* header for scanlinelist */
492 POINTBLOCK FirstPtBlock, *curPtBlock; /* PtBlock buffers */
493 POINTBLOCK *tmpPtBlock;
494 int numFullPtBlocks = 0;
496 if (! (region = XCreateRegion())) return (Region) NULL;
498 /* special case a rectangle */
501 ((Count == 5) && (pts[4].x == pts[0].x) && (pts[4].y == pts[0].y))) &&
502 (((pts[0].y == pts[1].y) &&
503 (pts[1].x == pts[2].x) &&
504 (pts[2].y == pts[3].y) &&
505 (pts[3].x == pts[0].x)) ||
506 ((pts[0].x == pts[1].x) &&
507 (pts[1].y == pts[2].y) &&
508 (pts[2].x == pts[3].x) &&
509 (pts[3].y == pts[0].y)))) {
510 region->extents.x1 = min(pts[0].x, pts[2].x);
511 region->extents.y1 = min(pts[0].y, pts[2].y);
512 region->extents.x2 = max(pts[0].x, pts[2].x);
513 region->extents.y2 = max(pts[0].y, pts[2].y);
514 if ((region->extents.x1 != region->extents.x2) &&
515 (region->extents.y1 != region->extents.y2)) {
516 region->numRects = 1;
517 *(region->rects) = region->extents;
522 if (Count < 2) return region;
524 if (! (pETEs = (EdgeTableEntry *)
525 Xmalloc((unsigned) (sizeof(EdgeTableEntry) * Count)))) {
526 XDestroyRegion(region);
527 return (Region) NULL;
530 pts = FirstPtBlock.pts;
531 CreateETandAET(Count, Pts, &ET, &AET, pETEs, &SLLBlock);
532 pSLL = ET.scanlines.next;
533 curPtBlock = &FirstPtBlock;
535 if (rule == EvenOddRule) {
539 for (y = ET.ymin; y < ET.ymax; y++) {
541 * Add a new edge to the active edge table when we
542 * get to the next edge.
544 if (pSLL != NULL && y == pSLL->scanline) {
545 loadAET(&AET, pSLL->edgelist);
552 * for each active edge
555 pts->x = pAET->bres.minor_axis, pts->y = y;
559 * send out the buffer
561 if (iPts == NUMPTSTOBUFFER) {
562 tmpPtBlock = (POINTBLOCK *)Xmalloc(sizeof(POINTBLOCK));
563 curPtBlock->next = tmpPtBlock;
564 curPtBlock = tmpPtBlock;
565 pts = curPtBlock->pts;
569 EVALUATEEDGEEVENODD(pAET, pPrevAET, y);
571 (void) InsertionSort(&AET);
578 for (y = ET.ymin; y < ET.ymax; y++) {
580 * Add a new edge to the active edge table when we
581 * get to the next edge.
583 if (pSLL != NULL && y == pSLL->scanline) {
584 loadAET(&AET, pSLL->edgelist);
593 * for each active edge
597 * add to the buffer only those edges that
598 * are in the Winding active edge table.
601 pts->x = pAET->bres.minor_axis, pts->y = y;
605 * send out the buffer
607 if (iPts == NUMPTSTOBUFFER) {
608 tmpPtBlock = (POINTBLOCK *)Xmalloc(sizeof(POINTBLOCK));
609 curPtBlock->next = tmpPtBlock;
610 curPtBlock = tmpPtBlock;
611 pts = curPtBlock->pts;
612 numFullPtBlocks++; iPts = 0;
614 pWETE = pWETE->nextWETE;
616 EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET);
620 * recompute the winding active edge table if
621 * we just resorted or have exited an edge.
623 if (InsertionSort(&AET) || fixWAET) {
629 FreeStorage(SLLBlock.next);
630 (void) PtsToRegion(numFullPtBlocks, iPts, &FirstPtBlock, region);
631 for (curPtBlock = FirstPtBlock.next; --numFullPtBlocks >= 0;) {
632 tmpPtBlock = curPtBlock->next;
633 Xfree((char *)curPtBlock);
634 curPtBlock = tmpPtBlock;
636 Xfree((char *)pETEs);