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