Annotation of ntddk/src/video/displays/vga/fillpath.c, revision 1.1.1.1

1.1       root        1: /******************************Module*Header*******************************\
                      2: * Module Name: fillpath.c
                      3: *
                      4: * DrvFillPath
                      5: *
                      6: * Copyright (c) 1992 Microsoft Corporation
                      7: \**************************************************************************/
                      8: 
                      9: // LATER identify convex polygons and special-case?
                     10: // LATER identify vertical edges and special-case?
                     11: // LATER move pointed-to variables into automatics in search loops
                     12: 
                     13: #include "driver.h"
                     14: #include "bitblt.h"
                     15: 
                     16: #define MAX_PATH_RECTS  200 // maximum number of rects we'll fill per call to
                     17:                             //  the fill code
                     18: 
                     19: // Describe a single non-horizontal edge of a path to fill.
                     20: typedef struct _EDGE {
                     21:     PVOID pNext;
                     22:     INT iScansLeft;
                     23:     INT X;
                     24:     INT Y;
                     25:     INT iErrorTerm;
                     26:     INT iErrorAdjustUp;
                     27:     INT iErrorAdjustDown;
                     28:     INT iXWhole;
                     29:     INT iXDirection;
                     30:     INT iWindingDirection;
                     31: } EDGE, *PEDGE;
                     32: 
                     33: //MIX translation table. Translates a mix 1-16, into an old style Rop 0-255.
                     34: extern BYTE gaMix[];
                     35: 
                     36: VOID AdvanceAETEdges(EDGE *pAETHead);
                     37: VOID XSortAETEdges(EDGE *pAETHead);
                     38: VOID MoveNewEdges(EDGE *pGETHead, EDGE *pAETHead, INT iCurrentY);
                     39: EDGE * AddEdgeToGET(EDGE *pGETHead, EDGE *pFreeEdge, POINTFIX *ppfxEdgeStart,
                     40:         POINTFIX *ppfxEdgeEnd);
                     41: VOID ConstructGET(EDGE *pGETHead, EDGE *pFreeEdges, PATHOBJ *ppo,
                     42:         PATHDATA *pd, BOOL bMore);
                     43: 
                     44: /******************************Public*Routine******************************\
                     45: * DrvFillPath
                     46: *
                     47: * Fill the specified path with the specified brush and ROP.
                     48: *
                     49: \**************************************************************************/
                     50: 
                     51: BOOL DrvFillPath
                     52: (
                     53:     SURFOBJ  *pso,
                     54:     PATHOBJ  *ppo,
                     55:     CLIPOBJ  *pco,
                     56:     BRUSHOBJ *pbo,
                     57:     POINTL   *pptlBrush,
                     58:     MIX       mix,
                     59:     FLONG    flOptions
                     60: )
                     61: {
                     62:     ULONG iSolidColor;  // Solid color for solid brushes
                     63:     PDEVSURF pdsurf;
                     64:     BRUSHINST *pbri;    // Pointer to a brush instance
                     65:     BYTE jClipping;     // clipping type
                     66:     EDGE *pCurrentEdge;
                     67:     EDGE AETHead;       // dummy head/tail node & sentinel for Active Edge Table
                     68:     EDGE *pAETHead;     // pointer to AETHead
                     69:     EDGE GETHead;       // dummy head/tail node & sentinel for Global Edge Table
                     70:     EDGE *pGETHead;     // pointer to GETHead
                     71:     EDGE *pFreeEdges;   // pointer to memory free for use to store edges
                     72:     ULONG ulNumRects;   // # of rectangles to draw currently in rectangle list
                     73:     RECTL *prclRects;   // pointer to start of rectangle draw list
                     74:     INT iCurrentY;      // scan line for which we're currently scanning out the
                     75:                         //  fill
                     76:     BOOL     bMore;
                     77:     PATHDATA pd;
                     78: 
                     79:     VOID (*pfnFill)(PDEVSURF,ULONG,PRECTL,MIX, BRUSHINST *,PPOINTL);
                     80: 
                     81:     // The drawing surface
                     82:     pdsurf = (PDEVSURF) pso->dhsurf;
                     83: 
                     84:     // Is there clipping? We don't handle that
                     85:     // LATER handle rectangle clipping
                     86: 
                     87:     // Set up the clipping type
                     88:     if (pco == (CLIPOBJ *) NULL) {
                     89:         // No CLIPOBJ provided, so we don't have to worry about clipping
                     90:         jClipping = DC_TRIVIAL;
                     91:     } else {
                     92:         // Use the CLIPOBJ-provided clipping
                     93:         jClipping = pco->iDComplexity;
                     94:     }
                     95: 
                     96:     if (jClipping != DC_TRIVIAL) {
                     97:         return(FALSE);  // there is clipping; let GDI fill the path
                     98:     }
                     99: 
                    100:     // There's nothing to do if there's only one point
                    101:     // LATER is this needed?
                    102:     if (ppo->cCurves <= 1) {
                    103:         return(TRUE);
                    104:     }
                    105: 
                    106:     // Do we have enough memory for all the edges?
                    107:     // LATER does cCurves include closure?
                    108:     if (ppo->cCurves > ((pdsurf->ulTempBufferSize -
                    109:             (MAX_PATH_RECTS * (ULONG)sizeof(RECTL))) / sizeof(EDGE))) {
                    110:         return(FALSE);  // too many edges; let GDI fill the path
                    111:     }
                    112: 
                    113:     // See if we can handle this pattern and ROP
                    114: 
                    115:     // We don't handle ROP4s
                    116:     if ((mix & 0xFF) != ((mix >> 8) & 0xFF)) {
                    117:         return(FALSE);  // it's a ROP4; let GDI fill the path
                    118:     }
                    119: 
                    120: 
                    121:     // See if we can use the solid brush accelerators, or have to draw a
                    122:     // pattern
                    123:     mix &= 0xFF;
                    124:     switch (mix) {
                    125:         case 0:
                    126:             return(FALSE);  // LATER should this ever happen?
                    127: 
                    128:         case R2_MASKNOTPEN:
                    129:         case R2_NOTCOPYPEN:
                    130:         case R2_XORPEN:
                    131:         case R2_MASKPEN:
                    132:         case R2_NOTXORPEN:
                    133:         case R2_MERGENOTPEN:
                    134:         case R2_COPYPEN:
                    135:         case R2_MERGEPEN:
                    136:         case R2_NOTMERGEPEN:
                    137:         case R2_MASKPENNOT:
                    138:         case R2_NOTMASKPEN:
                    139:         case R2_MERGEPENNOT:
                    140: 
                    141:             if (pbo->iSolidColor != 0xffffffff) {
                    142: 
                    143:                 // Solid fill
                    144:                 iSolidColor = pbo->iSolidColor;
                    145:                 pbri = (BRUSHINST *)NULL;   // marks fill as solid
                    146: 
                    147:             } else {
                    148: 
                    149:                 // We'll use our special case pattern code
                    150:                 if (pbo->pvRbrush == (PVOID)NULL)
                    151:                 {
                    152:                     pbri = (BRUSHINST *)BRUSHOBJ_pvGetRbrush(pbo);
                    153: 
                    154:                     if (pbri == (BRUSHINST *)NULL)
                    155:                         return(FALSE);  // couldn't realize the brush; let GDI
                    156:                                         //  fill the path
                    157:                 }
                    158:                 else
                    159:                 {
                    160:                     pbri = (BRUSHINST *)pbo->pvRbrush;
                    161:                 }
                    162: 
                    163:                 // Handle color and mono patterns differently
                    164:                 if (pbri->usStyle == BRI_MONO_PATTERN) {
                    165:                     pfnFill = vMonoPatBlt;
                    166:                 } else {
                    167:                     pfnFill = vClrPatBlt;
                    168:                 }
                    169: 
                    170:                 // We only support non-8 wide brushes with R2_COPYPEN
                    171:                 // LATER do we need to check for 16 wide for R2_COPYPEN?
                    172: 
                    173:                 if ((mix != R2_COPYPEN) && (pbri->RealWidth != 8)) {
                    174:                     return(FALSE);  // not R2_COPYPEN and not 8 wide; let GDI
                    175:                                     //  fill the path
                    176:                 }
                    177: 
                    178:                 break;
                    179:             }
                    180: 
                    181:         // Rops that are implicit solid colors
                    182: 
                    183:         case R2_NOT:
                    184:         case R2_WHITE:
                    185:         case R2_BLACK:
                    186: 
                    187:             // Brush color parameter doesn't matter for these rops
                    188:             // LATER then why do we set it?
                    189:             iSolidColor = pbo->iSolidColor;
                    190:             pbri = (BRUSHINST *)NULL;   // marks fill as solid
                    191:             break;
                    192: 
                    193:         case R2_NOP:
                    194:             return TRUE;
                    195:     }
                    196: 
                    197:     // set up working storage in the temporary buffer
                    198: 
                    199:     prclRects = pdsurf->pvBankBufferPlane0; // storage for list of rectangles
                    200:                                             //  to draw
                    201: 
                    202:     // enumerate path here first time  to  check  for  special
                    203:     // cases (rectangles,  single pixel and monotone polygons)
                    204: 
                    205:     // it is too difficult to  determine  interaction  between
                    206:     // multiple paths,   if there is more than one,  skip this
                    207: 
                    208:     if (! (bMore = PATHOBJ_bEnum(ppo, &pd))) { 
                    209: 
                    210:         // if the count is less than three than it is at best a
                    211:         // line which means nothing gets drawn so get  out  now
                    212: 
                    213:         if (pd.count < 3) return(TRUE);
                    214: 
                    215:         // if the count is four, check to see if the polygon is
                    216:         // really a rectangle since we can really speed that up
                    217: 
                    218:         if (pd.count == 4) {
                    219:             prclRects = prclRects;
                    220:  
                    221:             // we have to start somewhere so assume that most
                    222:             // applications specify the top left point  first
                    223: 
                    224:             // we want to check that the first two points are
                    225:             // either vertically or horizontally aligned.  if
                    226:             // they are then we check that the last point [3]
                    227:             // is either horizontally or  vertically  aligned,
                    228:             // and finally that the 3rd point [2] is  aligned
                    229:             // with both the second point and the last  point
                    230: 
                    231:             // start by taking floor of the points  to  deter-
                    232:             // mine if the GIQ points are in the  same  pixel
                    233: 
                    234: #define FIX_SHIFT 4L
                    235: #define FIX_MASK (- (1L << FIX_SHIFT))
                    236: 
                    237:             prclRects->top   = pd.pptfx[0].y + 15 & FIX_MASK;
                    238:             prclRects->left  = pd.pptfx[0].x + 15 & FIX_MASK;
                    239:             prclRects->right = pd.pptfx[1].x + 15 & FIX_MASK;
                    240: 
                    241:             if (prclRects->left ^ prclRects->right) {
                    242:                 if (prclRects->top  ^ (pd.pptfx[1].y + 15 & FIX_MASK))
                    243:                     goto not_rectangle;
                    244:                 
                    245:                 if (prclRects->left ^ (pd.pptfx[3].x + 15 & FIX_MASK))
                    246:                     goto not_rectangle;
                    247:                 
                    248:                 if (prclRects->right ^ (pd.pptfx[2].x + 15 & FIX_MASK))
                    249:                     goto not_rectangle;
                    250:                 
                    251:                 prclRects->bottom = pd.pptfx[2].y + 15 & FIX_MASK;
                    252:                 if (prclRects->bottom ^ (pd.pptfx[3].y + 15 & FIX_MASK))
                    253:                     goto not_rectangle;
                    254: 
                    255:             } else {
                    256:                 if (prclRects->top ^ (pd.pptfx[3].y + 15 & FIX_MASK))
                    257:                     goto not_rectangle;
                    258:                 
                    259:                 prclRects->bottom = pd.pptfx[1].y + 15 & FIX_MASK;
                    260:                 if (prclRects->bottom ^ (pd.pptfx[2].y + 15 & FIX_MASK))
                    261:                     goto not_rectangle;
                    262:                 
                    263:                 prclRects->right = pd.pptfx[2].x + 15 & FIX_MASK;
                    264:                 if (prclRects->right ^ (pd.pptfx[3].x + 15 & FIX_MASK))
                    265:                     goto not_rectangle;
                    266:             }
                    267:             
                    268:             // if the left is greater than the right then
                    269:             // swap them so the blt code doesn't wig  out
                    270: 
                    271:             if (prclRects->left > prclRects->right) {
                    272:                 FIX temp;
                    273:             
                    274:                 temp = prclRects->left;
                    275:                 prclRects->left = prclRects->right;
                    276:                 prclRects->right = temp;
                    277: 
                    278:             } else {
                    279: 
                    280:                 // if left == right there's nothing to draw
                    281:                 
                    282:                 if (prclRects->left == prclRects->right) {
                    283:                     return(TRUE);
                    284:                 }
                    285:             }
                    286:             
                    287:             // shift the values to get pixel coordinates
                    288: 
                    289:             prclRects->left  = prclRects->left  >> FIX_SHIFT;
                    290:             prclRects->right = prclRects->right >> FIX_SHIFT;
                    291: 
                    292:             if (prclRects->top > prclRects->bottom) {
                    293:                 FIX temp;
                    294:                 
                    295:                 temp = prclRects->top;
                    296:                 prclRects->top = prclRects->bottom;
                    297:                 prclRects->bottom = temp;
                    298: 
                    299:             } else {
                    300:                 if (prclRects->top == prclRects->bottom) {
                    301:                     return(TRUE);
                    302:                 }
                    303:             }
                    304: 
                    305:             // shift the values to get pixel coordinates
                    306: 
                    307:             prclRects->top    = prclRects->top    >> FIX_SHIFT;
                    308:             prclRects->bottom = prclRects->bottom >> FIX_SHIFT;
                    309:             
                    310:             // if we get here then the polygon is a rectangle,
                    311:             // set count  to  1  and  goto  bottom to draw it
                    312: 
                    313:             ulNumRects = 1;
                    314:             goto draw_remaining_rectangles;
                    315:         }
                    316:     }
                    317: 
                    318:     //  if this is not one of the special cases then we find
                    319:     //  ourselves here and we scan convert by building edges
                    320: 
                    321: not_rectangle:
                    322:     pFreeEdges = (EDGE *) (((BYTE *) prclRects) +
                    323:             (MAX_PATH_RECTS * sizeof(RECTL))); // storage for list of edges
                    324:                                                //  between which to fill
                    325: 
                    326:     // Initialize an empty list of rectangles to fill
                    327:     ulNumRects = 0;
                    328: 
                    329:     // Enumerate the path edges and build a Global Edge Table (GET) from them
                    330:     // in YX-sorted order.
                    331:     pGETHead = &GETHead;
                    332:     ConstructGET(pGETHead, pFreeEdges, ppo, &pd, bMore);
                    333: 
                    334:     // Create an empty AET with the head node also a tail sentinel
                    335:     pAETHead = &AETHead;
                    336:     AETHead.pNext = pAETHead;  // mark that the AET is empty
                    337:     AETHead.X = 0x7FFFFFFF;    // this is greater than any valid X value, so
                    338:                                //  searches will always terminate
                    339: 
                    340:     // Top scan of polygon is the top of the first edge we come to
                    341:     iCurrentY = ((EDGE *)GETHead.pNext)->Y;
                    342: 
                    343:     // Loop through all the scans in the polygon, adding edges from the GET to
                    344:     // the Active Edge Table (AET) as we come to their starts, and scanning out
                    345:     // the AET at each scan into a rectangle list. Each time it fills up, the
                    346:     // rectangle list is passed to the filling routine, and then once again at
                    347:     // the end if any rectangles remain undrawn. We continue so long as there
                    348:     // are edges to be scanned out
                    349:     while (1) {
                    350: 
                    351:         // Advance the edges in the AET one scan, discarding any that have
                    352:         // reached the end (if there are any edges in the AET)
                    353:         if (AETHead.pNext != pAETHead) {
                    354:             AdvanceAETEdges(pAETHead);
                    355:         }
                    356: 
                    357:         // If the AET is empty, done if the GET is empty, else jump ahead to
                    358:         // the next edge in the GET; if the AET isn't empty, re-sort the AET
                    359:         if (AETHead.pNext == pAETHead) {
                    360:             if (GETHead.pNext == pGETHead) {
                    361:                 // Done if there are no edges in either the AET or the GET
                    362:                 break;
                    363:             }
                    364:             // There are no edges in the AET, so jump ahead to the next edge in
                    365:             // the GET
                    366:             iCurrentY = ((EDGE *)GETHead.pNext)->Y;
                    367:         } else {
                    368:             // Re-sort the edges in the AET by X coordinate, if there are at
                    369:             // least two edges in the AET (there could be one edge if the
                    370:             // balancing edge hasn't yet been added from the GET)
                    371:             if (((EDGE *)AETHead.pNext)->pNext != pAETHead) {
                    372:                 XSortAETEdges(pAETHead);
                    373:             }
                    374:         }
                    375: 
                    376:         // Move any new edges that start on this scan from the GET to the AET;
                    377:         // bother calling only if there's at least one edge to add
                    378:         if (((EDGE *)GETHead.pNext)->Y == iCurrentY) {
                    379:             MoveNewEdges(pGETHead, pAETHead, iCurrentY);
                    380:         }
                    381: 
                    382:         // Scan the AET into rectangles to fill (there's always at least one
                    383:         // edge pair in the AET)
                    384:         pCurrentEdge = AETHead.pNext;   // point to the first edge
                    385:         do {
                    386: 
                    387:             INT iLeftEdge;
                    388: 
                    389:             // The left edge of any given edge pair is easy to find; it's just
                    390:             // wherever we happen to be currently
                    391:             iLeftEdge = pCurrentEdge->X;
                    392: 
                    393:             // Find the matching right edge according to the current fill rule
                    394:             if ((flOptions & FP_WINDINGMODE) != 0) {
                    395: 
                    396:                 INT iWindingCount;
                    397: 
                    398:                 // Do winding fill; scan across until we've found equal numbers
                    399:                 // of up and down edges
                    400:                 iWindingCount = pCurrentEdge->iWindingDirection;
                    401:                 do {
                    402:                     pCurrentEdge = pCurrentEdge->pNext;
                    403:                     iWindingCount += pCurrentEdge->iWindingDirection;
                    404:                 } while (iWindingCount != 0);
                    405:             } else {
                    406:                 // Odd-even fill; the next edge is the matching right edge
                    407:                 pCurrentEdge = pCurrentEdge->pNext;
                    408:             }
                    409: 
                    410:             // See if the resulting span encompasses at least one pixel, and
                    411:             // add it to the list of rectangles to draw if so
                    412:             if (iLeftEdge < pCurrentEdge->X) {
                    413: 
                    414:                 // We've got an edge pair to add to the list to be filled; see
                    415:                 // if there's room for one more rectangle
                    416:                 if (ulNumRects >= MAX_PATH_RECTS) {
                    417:                     // No more room; draw the rectangles in the list and reset
                    418:                     // it to empty
                    419:                     if (pbri == (BRUSHINST *)NULL)
                    420:                     {
                    421:                         vTrgBlt(pdsurf, ulNumRects, prclRects, mix,
                    422:                                 iSolidColor);
                    423:                     }
                    424:                     else
                    425:                     {
                    426:                         (*pfnFill)(pdsurf, ulNumRects, prclRects, mix, pbri,
                    427:                                    pptlBrush);
                    428:                     }
                    429: 
                    430:                     // Reset the list to empty
                    431:                     ulNumRects = 0;
                    432:                 }
                    433: 
                    434:                 // Add the rectangle representing the current edge pair
                    435:                 // LATER coalesce rectangles
                    436:                 prclRects[ulNumRects].top = iCurrentY;
                    437:                 prclRects[ulNumRects].bottom = iCurrentY+1;
                    438:                 prclRects[ulNumRects].left = iLeftEdge;
                    439:                 prclRects[ulNumRects].right = pCurrentEdge->X;
                    440:                 ulNumRects++;
                    441:             }
                    442:         } while ((pCurrentEdge = pCurrentEdge->pNext) != pAETHead);
                    443: 
                    444:         iCurrentY++;    // next scan
                    445:     }
                    446: 
                    447: /* draw the remaining rectangles,  if there are any */
                    448: 
                    449: draw_remaining_rectangles:
                    450: 
                    451:     if (ulNumRects > 0) {
                    452:         if (pbri == (BRUSHINST *)NULL)
                    453:         {
                    454:             vTrgBlt(pdsurf, ulNumRects, prclRects, mix, iSolidColor);
                    455:         }
                    456:         else
                    457:         {
                    458:             (*pfnFill)(pdsurf, ulNumRects, prclRects, mix, pbri, pptlBrush);
                    459:         }
                    460:     }
                    461: 
                    462:     return(TRUE);   // done successfully
                    463: }
                    464: 
                    465: // Advance the edges in the AET to the next scan, dropping any for which we've
                    466: // done all scans. Assumes there is at least one edge in the AET.
                    467: VOID AdvanceAETEdges(EDGE *pAETHead)
                    468: {
                    469:     EDGE *pLastEdge, *pCurrentEdge;
                    470: 
                    471:     pLastEdge = pAETHead;
                    472:     pCurrentEdge = pLastEdge->pNext;
                    473:     do {
                    474: 
                    475:         // Count down this edge's remaining scans
                    476:         if (--pCurrentEdge->iScansLeft == 0) {
                    477:             // We've done all scans for this edge; drop this edge from the AET
                    478:             pLastEdge->pNext = pCurrentEdge->pNext;
                    479:         } else {
                    480:             // Advance the edge's X coordinate for a 1-scan Y advance
                    481:             // Advance by the minimum amount
                    482:             pCurrentEdge->X += pCurrentEdge->iXWhole;
                    483:             // Advance the error term and see if we got one extra pixel this
                    484:             // time
                    485:             pCurrentEdge->iErrorTerm += pCurrentEdge->iErrorAdjustUp;
                    486:             if (pCurrentEdge->iErrorTerm >= 0) {
                    487:                 // The error term turned over, so adjust the error term and
                    488:                 // advance the extra pixel
                    489:                 pCurrentEdge->iErrorTerm -= pCurrentEdge->iErrorAdjustDown;
                    490:                 pCurrentEdge->X += pCurrentEdge->iXDirection;
                    491:             }
                    492: 
                    493:             pLastEdge = pCurrentEdge;
                    494:         }
                    495:     } while ((pCurrentEdge = pLastEdge->pNext) != pAETHead);
                    496: }
                    497: 
                    498: // X-sort the AET, because the edges may have moved around relative to
                    499: // one another when we advanced them. We'll use a multipass bubble
                    500: // sort, which is actually okay for this application because edges
                    501: // rarely move relative to one another, so we usually do just one pass.
                    502: // Also, this makes it easy to keep just a singly-linked list. Assumes there
                    503: // are at least two edges in the AET.
                    504: VOID XSortAETEdges(EDGE *pAETHead)
                    505: {
                    506:     BOOL bEdgesSwapped;
                    507:     EDGE *pLastEdge, *pCurrentEdge, *pNextEdge;
                    508: 
                    509:     do {
                    510: 
                    511:         bEdgesSwapped = FALSE;
                    512:         pLastEdge = pAETHead;
                    513:         pCurrentEdge = pLastEdge->pNext;
                    514:         pNextEdge = pCurrentEdge->pNext;
                    515: 
                    516:         do {
                    517:             if (pNextEdge->X < pCurrentEdge->X) {
                    518: 
                    519:                 // Next edge is to the left of the current edge; swap them
                    520:                 pLastEdge->pNext = pNextEdge;
                    521:                 pCurrentEdge->pNext = pNextEdge->pNext;
                    522:                 pNextEdge->pNext = pCurrentEdge;
                    523:                 bEdgesSwapped = TRUE;
                    524:                 pCurrentEdge = pNextEdge;   // continue sorting before the edge
                    525:                                             //  we just swapped; it might move
                    526:                                             //  farther yet
                    527:             }
                    528:             pLastEdge = pCurrentEdge;
                    529:             pCurrentEdge = pLastEdge->pNext;
                    530:         } while ((pNextEdge = pCurrentEdge->pNext) != pAETHead);
                    531:     } while (bEdgesSwapped);
                    532: }
                    533: 
                    534: // Moves all edges that start on the current scan from the GET to the AET in
                    535: // X-sorted order. Parameters are pointer to head of GET and pointer to dummy
                    536: // edge at head of AET, plus current scan line. Assumes there's at least one
                    537: // edge to be moved.
                    538: VOID MoveNewEdges(EDGE *pGETHead, EDGE *pAETHead, INT iCurrentY)
                    539: {
                    540:     EDGE *pCurrentEdge = pAETHead;
                    541:     EDGE *pGETNext = pGETHead->pNext;
                    542: 
                    543:     do {
                    544: 
                    545:         // Scan through the AET until the X-sorted insertion point for this
                    546:         // edge is found. We can continue from where the last search left
                    547:         // off because the edges in the GET are in X sorted order, as is
                    548:         // the AET. The search always terminates because the AET sentinel
                    549:         // is greater than any valid X
                    550:         while (pGETNext->X > ((EDGE *)pCurrentEdge->pNext)->X) {
                    551:             pCurrentEdge = pCurrentEdge->pNext;
                    552:         }
                    553: 
                    554:         // We've found the insertion point; add the GET edge to the AET, and
                    555:         // remove it from the GET
                    556:         pGETHead->pNext = pGETNext->pNext;
                    557:         pGETNext->pNext = pCurrentEdge->pNext;
                    558:         pCurrentEdge->pNext = pGETNext;
                    559:         pCurrentEdge = pGETNext;    // continue insertion search for the next
                    560:                                     //  GET edge after the edge we just added
                    561:         pGETNext = pGETHead->pNext;
                    562: 
                    563:     } while (pGETNext->Y == iCurrentY);
                    564: }
                    565: 
                    566: 
                    567: 
                    568: 
                    569: 
                    570: // Build the Global Edge Table from the path. There must be enough memory in
                    571: // the free edge area to hold all edges. The GET is constructed in Y-X order,
                    572: // and has a head/tail/sentinel node at pGETHead.
                    573: 
                    574: VOID ConstructGET(
                    575:    EDGE     *pGETHead,
                    576:    EDGE     *pFreeEdges,
                    577:    PATHOBJ  *ppo,
                    578:    PATHDATA *ppd,
                    579:    BOOL      bMore)
                    580: {
                    581:     POINTFIX pfxPathStart;    // point that started the current subpath
                    582:     POINTFIX pfxPathPrevious; // point before the current point in a subpath;
                    583:                               //  starts the current edge
                    584: 
                    585:     // Create an empty GET with the head node also a tail sentinel
                    586: 
                    587:     pGETHead->pNext = pGETHead; // mark that the GET is empty
                    588:     pGETHead->Y = 0x7FFFFFFF;   // this is greater than any valid Y value, so
                    589:                                 //  searches will always terminate
                    590: 
                    591:     // PATHOBJ_vEnumStart is implicitly  performed  by  engine
                    592:     // already and first path  is  enumerated  by  the  caller
                    593: 
                    594: next_subpath:
                    595: 
                    596:     // Make sure the PATHDATA is not empty (is this necessary)
                    597: 
                    598:     if (ppd->count != 0) {
                    599: 
                    600:         // If first point starts a subpath, remember it as such
                    601:         // and go on to the next point,   so we can get an edge
                    602: 
                    603:         if (ppd->flags & PD_BEGINSUBPATH) {
                    604: 
                    605:             // the first point starts the subpath;   remember it
                    606: 
                    607:             pfxPathStart    = *ppd->pptfx; // the subpath starts here
                    608:             pfxPathPrevious = *ppd->pptfx; // this points starts next edge
                    609:             ppd->pptfx++;                  // advance to the next point
                    610:             ppd->count--;                  // count off this point
                    611:         }
                    612: 
                    613:        // add edges in PATHDATA to GET,  in Y-X  sorted  order
                    614: 
                    615:         while (ppd->count--) {
                    616:             pFreeEdges = 
                    617:                 AddEdgeToGET(pGETHead, pFreeEdges,&pfxPathPrevious,ppd->pptfx);
                    618:             pfxPathPrevious = *ppd->pptfx; // current point becomes previous
                    619:             ppd->pptfx++;                  // advance to the next point
                    620:         }
                    621: 
                    622:      // If last point ends the subpath, insert the edge that
                    623:      // connects to first point   (is this built in already)
                    624: 
                    625:         if (ppd->flags & PD_ENDSUBPATH) {
                    626:             pFreeEdges = AddEdgeToGET
                    627:                 (pGETHead, pFreeEdges, &pfxPathPrevious, &pfxPathStart);
                    628:         }
                    629:     }
                    630:    
                    631:     // the initial loop conditions preclude a do, while or for
                    632: 
                    633:     if (bMore) {
                    634:         bMore = PATHOBJ_bEnum(ppo, ppd);
                    635:         goto next_subpath;
                    636:     }
                    637: }
                    638: 
                    639: // Adds the edge described by the two passed-in points to the Global Edge
                    640: // Table, if the edge spans at least one pixel vertically.
                    641: EDGE * AddEdgeToGET(EDGE *pGETHead, EDGE *pFreeEdge,
                    642:         POINTFIX *ppfxEdgeStart, POINTFIX *ppfxEdgeEnd)
                    643: {
                    644:     INT iYStart, iYEnd, iXStart, iXEnd, iYHeight, iXWidth;
                    645: 
                    646:     // Set the winding-rule direction of the edge, and put the endpoints in
                    647:     // top-to-bottom order
                    648:     iYHeight = ppfxEdgeEnd->y - ppfxEdgeStart->y;
                    649:     if (iYHeight >= 0) {
                    650:         iXStart = ppfxEdgeStart->x;
                    651:         iYStart = ppfxEdgeStart->y;
                    652:         iXEnd = ppfxEdgeEnd->x;
                    653:         iYEnd = ppfxEdgeEnd->y;
                    654:         pFreeEdge->iWindingDirection = 1;
                    655:     } else {
                    656:         iYHeight = -iYHeight;
                    657:         iXEnd = ppfxEdgeStart->x;
                    658:         iYEnd = ppfxEdgeStart->y;
                    659:         iXStart = ppfxEdgeEnd->x;
                    660:         iYStart = ppfxEdgeEnd->y;
                    661:         pFreeEdge->iWindingDirection = -1;
                    662:     }
                    663: 
                    664:     // First pixel scan line (non-fractional GIQ Y coordinate) edge intersects.
                    665:     // Dividing by 16 with a shift is okay because Y is always positive
                    666:     pFreeEdge->Y = (iYStart + 15) >> 4;
                    667: 
                    668:     // Calculate the number of pixels spanned by this edge
                    669:     pFreeEdge->iScansLeft = ((iYEnd + 15) >> 4) - pFreeEdge->Y;
                    670:     if (pFreeEdge->iScansLeft <= 0) {
                    671:         return(pFreeEdge);  // no pixels at all are spanned, so we can ignore
                    672:                             //  this edge
                    673:     }
                    674: 
                    675:     // Set the error term and adjustment factors, all in GIQ coordinates for
                    676:     // now
                    677:     iXWidth = iXEnd - iXStart;
                    678:     if (iXWidth >= 0) {
                    679:         // Left to right, so we change X as soon as we move at all
                    680:         pFreeEdge->iXDirection = 1;
                    681:         pFreeEdge->iErrorTerm = -1;
                    682:     } else {
                    683:         // Right to left, so we don't change X until we've moved a full GIQ
                    684:         // coordinate
                    685:         iXWidth = -iXWidth;
                    686:         pFreeEdge->iXDirection = -1;
                    687:         pFreeEdge->iErrorTerm = -iYHeight;
                    688:     }
                    689: 
                    690:     if (iXWidth >= iYHeight) {
                    691:         // Calculate base run length (minimum distance advanced in X for a 1-
                    692:         // scan advance in Y)
                    693:         pFreeEdge->iXWhole = iXWidth / iYHeight;
                    694:         // Add sign back into base run length if going right to left
                    695:         if (pFreeEdge->iXDirection == -1) {
                    696:             pFreeEdge->iXWhole = -pFreeEdge->iXWhole;
                    697:         }
                    698:         pFreeEdge->iErrorAdjustUp = iXWidth % iYHeight;
                    699:     } else {
                    700:         // Base run length is 0, because line is closer to vertical than
                    701:         // horizontal
                    702:         pFreeEdge->iXWhole = 0;
                    703:         pFreeEdge->iErrorAdjustUp = iXWidth;
                    704:     }
                    705:     pFreeEdge->iErrorAdjustDown = iYHeight;
                    706: 
                    707:     // If the edge doesn't start on a pixel scan (that is, it starts at a
                    708:     // fractional GIQ coordinate), advance it to the first pixel scan it
                    709:     // intersects
                    710:     // LATER might be faster to use multiplication and division to jump ahead,
                    711:     // rather than looping
                    712:     while ((iYStart & 0x0F) != 0) {
                    713:         // Starts at a fractional GIQ coordinate, not exactly on a pixel scan
                    714: 
                    715:         // Advance the edge's GIQ X coordinate for a 1-GIQ-pixel Y advance
                    716:         // Advance by the minimum amount
                    717:         iXStart += pFreeEdge->iXWhole;
                    718:         // Advance the error term and see if we got one extra pixel this time
                    719:         pFreeEdge->iErrorTerm += pFreeEdge->iErrorAdjustUp;
                    720:         if (pFreeEdge->iErrorTerm >= 0) {
                    721:             // The error term turned over, so adjust the error term and
                    722:             // advance the extra pixel
                    723:             pFreeEdge->iErrorTerm -= pFreeEdge->iErrorAdjustDown;
                    724:             iXStart += pFreeEdge->iXDirection;
                    725:         }
                    726:         iYStart++;  // advance to the next GIQ Y coordinate
                    727:     }
                    728: 
                    729:     // Turn the calculations into pixel rather than GIQ calculations
                    730: 
                    731:     // Move the X coordinate to the nearest pixel, and adjust the error term
                    732:     // accordingly
                    733:     // Dividing by 16 with a shift is okay because X is always positive
                    734:     pFreeEdge->X = (iXStart + 15) >> 4; // convert from GIQ to pixel coordinates
                    735: 
                    736:     // LATER adjust only if needed (if prestepped above)?
                    737:     if (pFreeEdge->iXDirection == 1) {
                    738:         // Left to right
                    739:         pFreeEdge->iErrorTerm -= pFreeEdge->iErrorAdjustDown *
                    740:                 (((iXStart + 15) & ~0x0F) - iXStart);
                    741:     } else {
                    742:         // Right to left
                    743:         pFreeEdge->iErrorTerm -= pFreeEdge->iErrorAdjustDown *
                    744:                 ((iXStart - 1) & 0x0F);
                    745:     }
                    746: 
                    747:     // Scale the error adjusts up by 16 times, to move 16 GIQ pixels at a time.
                    748:     // Shifts work to do the multiplying because these values are always
                    749:     // non-negative
                    750:     pFreeEdge->iErrorAdjustUp <<= 4;
                    751:     pFreeEdge->iErrorAdjustDown <<= 4;
                    752: 
                    753:     // Insert the edge into the GET in YX-sorted order. The search always ends
                    754:     // because the GET has a sentinel with a greater-than-possible Y value
                    755:     while ((pFreeEdge->Y > ((EDGE *)pGETHead->pNext)->Y) ||
                    756:             ((pFreeEdge->Y == ((EDGE *)pGETHead->pNext)->Y) &&
                    757:             (pFreeEdge->X > ((EDGE *)pGETHead->pNext)->X))) {
                    758:         pGETHead = pGETHead->pNext;
                    759:     }
                    760: 
                    761:     pFreeEdge->pNext = pGETHead->pNext; // link the edge into the GET
                    762:     pGETHead->pNext = pFreeEdge;
                    763: 
                    764:     return(++pFreeEdge);    // point to the next edge storage location for next
                    765:                             //  time
                    766: }
                    767: 

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.