PolyReg.c revision 1ab64890
1/* $Xorg: PolyReg.c,v 1.5 2001/02/09 02:03:35 xorgcvs Exp $ */ 2/************************************************************************ 3 4Copyright 1987, 1998 The Open Group 5 6Permission to use, copy, modify, distribute, and sell this software and its 7documentation for any purpose is hereby granted without fee, provided that 8the above copyright notice appear in all copies and that both that 9copyright notice and this permission notice appear in supporting 10documentation. 11 12The above copyright notice and this permission notice shall be included in 13all copies or substantial portions of the Software. 14 15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 19AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 22Except as contained in this notice, the name of The Open Group shall not be 23used in advertising or otherwise to promote the sale, use or other dealings 24in this Software without prior written authorization from The Open Group. 25 26 27Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. 28 29 All Rights Reserved 30 31Permission to use, copy, modify, and distribute this software and its 32documentation for any purpose and without fee is hereby granted, 33provided that the above copyright notice appear in all copies and that 34both that copyright notice and this permission notice appear in 35supporting documentation, and that the name of Digital not be 36used in advertising or publicity pertaining to distribution of the 37software without specific, written prior permission. 38 39DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 40ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL 41DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 42ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 43WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 44ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 45SOFTWARE. 46 47************************************************************************/ 48/* $XFree86: xc/lib/X11/PolyReg.c,v 1.6 2001/12/14 19:54:03 dawes Exp $ */ 49 50#define LARGE_COORDINATE 1000000 51#define SMALL_COORDINATE -LARGE_COORDINATE 52 53#ifdef HAVE_CONFIG_H 54#include <config.h> 55#endif 56#include "Xlibint.h" 57#include "Xutil.h" 58#include <X11/Xregion.h> 59#include "poly.h" 60 61/* 62 * InsertEdgeInET 63 * 64 * Insert the given edge into the edge table. 65 * First we must find the correct bucket in the 66 * Edge table, then find the right slot in the 67 * bucket. Finally, we can insert it. 68 * 69 */ 70static void 71InsertEdgeInET( 72 EdgeTable *ET, 73 EdgeTableEntry *ETE, 74 int scanline, 75 ScanLineListBlock **SLLBlock, 76 int *iSLLBlock) 77{ 78 register EdgeTableEntry *start, *prev; 79 register ScanLineList *pSLL, *pPrevSLL; 80 ScanLineListBlock *tmpSLLBlock; 81 82 /* 83 * find the right bucket to put the edge into 84 */ 85 pPrevSLL = &ET->scanlines; 86 pSLL = pPrevSLL->next; 87 while (pSLL && (pSLL->scanline < scanline)) 88 { 89 pPrevSLL = pSLL; 90 pSLL = pSLL->next; 91 } 92 93 /* 94 * reassign pSLL (pointer to ScanLineList) if necessary 95 */ 96 if ((!pSLL) || (pSLL->scanline > scanline)) 97 { 98 if (*iSLLBlock > SLLSPERBLOCK-1) 99 { 100 tmpSLLBlock = 101 (ScanLineListBlock *)Xmalloc(sizeof(ScanLineListBlock)); 102 (*SLLBlock)->next = tmpSLLBlock; 103 tmpSLLBlock->next = (ScanLineListBlock *)NULL; 104 *SLLBlock = tmpSLLBlock; 105 *iSLLBlock = 0; 106 } 107 pSLL = &((*SLLBlock)->SLLs[(*iSLLBlock)++]); 108 109 pSLL->next = pPrevSLL->next; 110 pSLL->edgelist = (EdgeTableEntry *)NULL; 111 pPrevSLL->next = pSLL; 112 } 113 pSLL->scanline = scanline; 114 115 /* 116 * now insert the edge in the right bucket 117 */ 118 prev = (EdgeTableEntry *)NULL; 119 start = pSLL->edgelist; 120 while (start && (start->bres.minor_axis < ETE->bres.minor_axis)) 121 { 122 prev = start; 123 start = start->next; 124 } 125 ETE->next = start; 126 127 if (prev) 128 prev->next = ETE; 129 else 130 pSLL->edgelist = ETE; 131} 132 133/* 134 * CreateEdgeTable 135 * 136 * This routine creates the edge table for 137 * scan converting polygons. 138 * The Edge Table (ET) looks like: 139 * 140 * EdgeTable 141 * -------- 142 * | ymax | ScanLineLists 143 * |scanline|-->------------>-------------->... 144 * -------- |scanline| |scanline| 145 * |edgelist| |edgelist| 146 * --------- --------- 147 * | | 148 * | | 149 * V V 150 * list of ETEs list of ETEs 151 * 152 * where ETE is an EdgeTableEntry data structure, 153 * and there is one ScanLineList per scanline at 154 * which an edge is initially entered. 155 * 156 */ 157 158static void 159CreateETandAET( 160 register int count, 161 register XPoint *pts, 162 EdgeTable *ET, 163 EdgeTableEntry *AET, 164 register EdgeTableEntry *pETEs, 165 ScanLineListBlock *pSLLBlock) 166{ 167 register XPoint *top, *bottom; 168 register XPoint *PrevPt, *CurrPt; 169 int iSLLBlock = 0; 170 int dy; 171 172 if (count < 2) return; 173 174 /* 175 * initialize the Active Edge Table 176 */ 177 AET->next = (EdgeTableEntry *)NULL; 178 AET->back = (EdgeTableEntry *)NULL; 179 AET->nextWETE = (EdgeTableEntry *)NULL; 180 AET->bres.minor_axis = SMALL_COORDINATE; 181 182 /* 183 * initialize the Edge Table. 184 */ 185 ET->scanlines.next = (ScanLineList *)NULL; 186 ET->ymax = SMALL_COORDINATE; 187 ET->ymin = LARGE_COORDINATE; 188 pSLLBlock->next = (ScanLineListBlock *)NULL; 189 190 PrevPt = &pts[count-1]; 191 192 /* 193 * for each vertex in the array of points. 194 * In this loop we are dealing with two vertices at 195 * a time -- these make up one edge of the polygon. 196 */ 197 while (count--) 198 { 199 CurrPt = pts++; 200 201 /* 202 * find out which point is above and which is below. 203 */ 204 if (PrevPt->y > CurrPt->y) 205 { 206 bottom = PrevPt, top = CurrPt; 207 pETEs->ClockWise = 0; 208 } 209 else 210 { 211 bottom = CurrPt, top = PrevPt; 212 pETEs->ClockWise = 1; 213 } 214 215 /* 216 * don't add horizontal edges to the Edge table. 217 */ 218 if (bottom->y != top->y) 219 { 220 pETEs->ymax = bottom->y-1; /* -1 so we don't get last scanline */ 221 222 /* 223 * initialize integer edge algorithm 224 */ 225 dy = bottom->y - top->y; 226 BRESINITPGONSTRUCT(dy, top->x, bottom->x, pETEs->bres); 227 228 InsertEdgeInET(ET, pETEs, top->y, &pSLLBlock, &iSLLBlock); 229 230 if (PrevPt->y > ET->ymax) 231 ET->ymax = PrevPt->y; 232 if (PrevPt->y < ET->ymin) 233 ET->ymin = PrevPt->y; 234 pETEs++; 235 } 236 237 PrevPt = CurrPt; 238 } 239} 240 241/* 242 * loadAET 243 * 244 * This routine moves EdgeTableEntries from the 245 * EdgeTable into the Active Edge Table, 246 * leaving them sorted by smaller x coordinate. 247 * 248 */ 249 250static void 251loadAET( 252 register EdgeTableEntry *AET, 253 register EdgeTableEntry *ETEs) 254{ 255 register EdgeTableEntry *pPrevAET; 256 register EdgeTableEntry *tmp; 257 258 pPrevAET = AET; 259 AET = AET->next; 260 while (ETEs) 261 { 262 while (AET && (AET->bres.minor_axis < ETEs->bres.minor_axis)) 263 { 264 pPrevAET = AET; 265 AET = AET->next; 266 } 267 tmp = ETEs->next; 268 ETEs->next = AET; 269 if (AET) 270 AET->back = ETEs; 271 ETEs->back = pPrevAET; 272 pPrevAET->next = ETEs; 273 pPrevAET = ETEs; 274 275 ETEs = tmp; 276 } 277} 278 279/* 280 * computeWAET 281 * 282 * This routine links the AET by the 283 * nextWETE (winding EdgeTableEntry) link for 284 * use by the winding number rule. The final 285 * Active Edge Table (AET) might look something 286 * like: 287 * 288 * AET 289 * ---------- --------- --------- 290 * |ymax | |ymax | |ymax | 291 * | ... | |... | |... | 292 * |next |->|next |->|next |->... 293 * |nextWETE| |nextWETE| |nextWETE| 294 * --------- --------- ^-------- 295 * | | | 296 * V-------------------> V---> ... 297 * 298 */ 299static void 300computeWAET( 301 register EdgeTableEntry *AET) 302{ 303 register EdgeTableEntry *pWETE; 304 register int inside = 1; 305 register int isInside = 0; 306 307 AET->nextWETE = (EdgeTableEntry *)NULL; 308 pWETE = AET; 309 AET = AET->next; 310 while (AET) 311 { 312 if (AET->ClockWise) 313 isInside++; 314 else 315 isInside--; 316 317 if ((!inside && !isInside) || 318 ( inside && isInside)) 319 { 320 pWETE->nextWETE = AET; 321 pWETE = AET; 322 inside = !inside; 323 } 324 AET = AET->next; 325 } 326 pWETE->nextWETE = (EdgeTableEntry *)NULL; 327} 328 329/* 330 * InsertionSort 331 * 332 * Just a simple insertion sort using 333 * pointers and back pointers to sort the Active 334 * Edge Table. 335 * 336 */ 337 338static int 339InsertionSort( 340 register EdgeTableEntry *AET) 341{ 342 register EdgeTableEntry *pETEchase; 343 register EdgeTableEntry *pETEinsert; 344 register EdgeTableEntry *pETEchaseBackTMP; 345 register int changed = 0; 346 347 AET = AET->next; 348 while (AET) 349 { 350 pETEinsert = AET; 351 pETEchase = AET; 352 while (pETEchase->back->bres.minor_axis > AET->bres.minor_axis) 353 pETEchase = pETEchase->back; 354 355 AET = AET->next; 356 if (pETEchase != pETEinsert) 357 { 358 pETEchaseBackTMP = pETEchase->back; 359 pETEinsert->back->next = AET; 360 if (AET) 361 AET->back = pETEinsert->back; 362 pETEinsert->next = pETEchase; 363 pETEchase->back->next = pETEinsert; 364 pETEchase->back = pETEinsert; 365 pETEinsert->back = pETEchaseBackTMP; 366 changed = 1; 367 } 368 } 369 return(changed); 370} 371 372/* 373 * Clean up our act. 374 */ 375static void 376FreeStorage( 377 register ScanLineListBlock *pSLLBlock) 378{ 379 register ScanLineListBlock *tmpSLLBlock; 380 381 while (pSLLBlock) 382 { 383 tmpSLLBlock = pSLLBlock->next; 384 Xfree((char *)pSLLBlock); 385 pSLLBlock = tmpSLLBlock; 386 } 387} 388 389/* 390 * Create an array of rectangles from a list of points. 391 * If indeed these things (POINTS, RECTS) are the same, 392 * then this proc is still needed, because it allocates 393 * storage for the array, which was allocated on the 394 * stack by the calling procedure. 395 * 396 */ 397static int PtsToRegion( 398 register int numFullPtBlocks, 399 register int iCurPtBlock, 400 POINTBLOCK *FirstPtBlock, 401 REGION *reg) 402{ 403 register BOX *rects; 404 register XPoint *pts; 405 register POINTBLOCK *CurPtBlock; 406 register int i; 407 register BOX *extents; 408 register int numRects; 409 BOX *prevRects = reg->rects; 410 411 extents = ®->extents; 412 413 numRects = ((numFullPtBlocks * NUMPTSTOBUFFER) + iCurPtBlock) >> 1; 414 415 if (!(reg->rects = (BOX *)Xrealloc((char *)reg->rects, 416 (unsigned) (sizeof(BOX) * numRects)))) { 417 Xfree(prevRects); 418 return(0); 419 } 420 421 reg->size = numRects; 422 CurPtBlock = FirstPtBlock; 423 rects = reg->rects - 1; 424 numRects = 0; 425 extents->x1 = MAXSHORT, extents->x2 = MINSHORT; 426 427 for ( ; numFullPtBlocks >= 0; numFullPtBlocks--) { 428 /* the loop uses 2 points per iteration */ 429 i = NUMPTSTOBUFFER >> 1; 430 if (!numFullPtBlocks) 431 i = iCurPtBlock >> 1; 432 for (pts = CurPtBlock->pts; i--; pts += 2) { 433 if (pts->x == pts[1].x) 434 continue; 435 if (numRects && pts->x == rects->x1 && pts->y == rects->y2 && 436 pts[1].x == rects->x2 && 437 (numRects == 1 || rects[-1].y1 != rects->y1) && 438 (i && pts[2].y > pts[1].y)) { 439 rects->y2 = pts[1].y + 1; 440 continue; 441 } 442 numRects++; 443 rects++; 444 rects->x1 = pts->x; rects->y1 = pts->y; 445 rects->x2 = pts[1].x; rects->y2 = pts[1].y + 1; 446 if (rects->x1 < extents->x1) 447 extents->x1 = rects->x1; 448 if (rects->x2 > extents->x2) 449 extents->x2 = rects->x2; 450 } 451 CurPtBlock = CurPtBlock->next; 452 } 453 454 if (numRects) { 455 extents->y1 = reg->rects->y1; 456 extents->y2 = rects->y2; 457 } else { 458 extents->x1 = 0; 459 extents->y1 = 0; 460 extents->x2 = 0; 461 extents->y2 = 0; 462 } 463 reg->numRects = numRects; 464 465 return(TRUE); 466} 467 468/* 469 * polytoregion 470 * 471 * Scan converts a polygon by returning a run-length 472 * encoding of the resultant bitmap -- the run-length 473 * encoding is in the form of an array of rectangles. 474 */ 475Region 476XPolygonRegion( 477 XPoint *Pts, /* the pts */ 478 int Count, /* number of pts */ 479 int rule) /* winding rule */ 480{ 481 Region region; 482 register EdgeTableEntry *pAET; /* Active Edge Table */ 483 register int y; /* current scanline */ 484 register int iPts = 0; /* number of pts in buffer */ 485 register EdgeTableEntry *pWETE; /* Winding Edge Table Entry*/ 486 register ScanLineList *pSLL; /* current scanLineList */ 487 register XPoint *pts; /* output buffer */ 488 EdgeTableEntry *pPrevAET; /* ptr to previous AET */ 489 EdgeTable ET; /* header node for ET */ 490 EdgeTableEntry AET; /* header node for AET */ 491 EdgeTableEntry *pETEs; /* EdgeTableEntries pool */ 492 ScanLineListBlock SLLBlock; /* header for scanlinelist */ 493 int fixWAET = FALSE; 494 POINTBLOCK FirstPtBlock, *curPtBlock; /* PtBlock buffers */ 495 POINTBLOCK *tmpPtBlock; 496 int numFullPtBlocks = 0; 497 498 if (! (region = XCreateRegion())) return (Region) NULL; 499 500 /* special case a rectangle */ 501 pts = Pts; 502 if (((Count == 4) || 503 ((Count == 5) && (pts[4].x == pts[0].x) && (pts[4].y == pts[0].y))) && 504 (((pts[0].y == pts[1].y) && 505 (pts[1].x == pts[2].x) && 506 (pts[2].y == pts[3].y) && 507 (pts[3].x == pts[0].x)) || 508 ((pts[0].x == pts[1].x) && 509 (pts[1].y == pts[2].y) && 510 (pts[2].x == pts[3].x) && 511 (pts[3].y == pts[0].y)))) { 512 region->extents.x1 = min(pts[0].x, pts[2].x); 513 region->extents.y1 = min(pts[0].y, pts[2].y); 514 region->extents.x2 = max(pts[0].x, pts[2].x); 515 region->extents.y2 = max(pts[0].y, pts[2].y); 516 if ((region->extents.x1 != region->extents.x2) && 517 (region->extents.y1 != region->extents.y2)) { 518 region->numRects = 1; 519 *(region->rects) = region->extents; 520 } 521 return(region); 522 } 523 524 if (Count < 2) return region; 525 526 if (! (pETEs = (EdgeTableEntry *) 527 Xmalloc((unsigned) (sizeof(EdgeTableEntry) * Count)))) { 528 XDestroyRegion(region); 529 return (Region) NULL; 530 } 531 532 pts = FirstPtBlock.pts; 533 CreateETandAET(Count, Pts, &ET, &AET, pETEs, &SLLBlock); 534 pSLL = ET.scanlines.next; 535 curPtBlock = &FirstPtBlock; 536 537 if (rule == EvenOddRule) { 538 /* 539 * for each scanline 540 */ 541 for (y = ET.ymin; y < ET.ymax; y++) { 542 /* 543 * Add a new edge to the active edge table when we 544 * get to the next edge. 545 */ 546 if (pSLL != NULL && y == pSLL->scanline) { 547 loadAET(&AET, pSLL->edgelist); 548 pSLL = pSLL->next; 549 } 550 pPrevAET = &AET; 551 pAET = AET.next; 552 553 /* 554 * for each active edge 555 */ 556 while (pAET) { 557 pts->x = pAET->bres.minor_axis, pts->y = y; 558 pts++, iPts++; 559 560 /* 561 * send out the buffer 562 */ 563 if (iPts == NUMPTSTOBUFFER) { 564 tmpPtBlock = (POINTBLOCK *)Xmalloc(sizeof(POINTBLOCK)); 565 curPtBlock->next = tmpPtBlock; 566 curPtBlock = tmpPtBlock; 567 pts = curPtBlock->pts; 568 numFullPtBlocks++; 569 iPts = 0; 570 } 571 EVALUATEEDGEEVENODD(pAET, pPrevAET, y); 572 } 573 (void) InsertionSort(&AET); 574 } 575 } 576 else { 577 /* 578 * for each scanline 579 */ 580 for (y = ET.ymin; y < ET.ymax; y++) { 581 /* 582 * Add a new edge to the active edge table when we 583 * get to the next edge. 584 */ 585 if (pSLL != NULL && y == pSLL->scanline) { 586 loadAET(&AET, pSLL->edgelist); 587 computeWAET(&AET); 588 pSLL = pSLL->next; 589 } 590 pPrevAET = &AET; 591 pAET = AET.next; 592 pWETE = pAET; 593 594 /* 595 * for each active edge 596 */ 597 while (pAET) { 598 /* 599 * add to the buffer only those edges that 600 * are in the Winding active edge table. 601 */ 602 if (pWETE == pAET) { 603 pts->x = pAET->bres.minor_axis, pts->y = y; 604 pts++, iPts++; 605 606 /* 607 * send out the buffer 608 */ 609 if (iPts == NUMPTSTOBUFFER) { 610 tmpPtBlock = (POINTBLOCK *)Xmalloc(sizeof(POINTBLOCK)); 611 curPtBlock->next = tmpPtBlock; 612 curPtBlock = tmpPtBlock; 613 pts = curPtBlock->pts; 614 numFullPtBlocks++; iPts = 0; 615 } 616 pWETE = pWETE->nextWETE; 617 } 618 EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET); 619 } 620 621 /* 622 * recompute the winding active edge table if 623 * we just resorted or have exited an edge. 624 */ 625 if (InsertionSort(&AET) || fixWAET) { 626 computeWAET(&AET); 627 fixWAET = FALSE; 628 } 629 } 630 } 631 FreeStorage(SLLBlock.next); 632 (void) PtsToRegion(numFullPtBlocks, iPts, &FirstPtBlock, region); 633 for (curPtBlock = FirstPtBlock.next; --numFullPtBlocks >= 0;) { 634 tmpPtBlock = curPtBlock->next; 635 Xfree((char *)curPtBlock); 636 curPtBlock = tmpPtBlock; 637 } 638 Xfree((char *)pETEs); 639 return(region); 640} 641