Annotation of ntddk/src/video/displays/vga256/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-1993 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: 
                     15: #define MAX_PATH_RECTS  200 // maximum number of rects we'll fill per call to
                     16:                             //  the fill code
                     17: 
                     18: // Describe a single non-horizontal edge of a path to fill.
                     19: typedef struct _EDGE {
                     20:     PVOID pNext;
                     21:     INT iScansLeft;
                     22:     INT X;
                     23:     INT Y;
                     24:     INT iErrorTerm;
                     25:     INT iErrorAdjustUp;
                     26:     INT iErrorAdjustDown;
                     27:     INT iXWhole;
                     28:     INT iXDirection;
                     29:     INT iWindingDirection;
                     30: } EDGE, *PEDGE;
                     31: 
                     32: //MIX translation table. Translates a mix 1-16, into an old style Rop 0-255.
                     33: extern BYTE gaMix[];
                     34: 
                     35: VOID AdvanceAETEdges(EDGE *pAETHead);
                     36: VOID XSortAETEdges(EDGE *pAETHead);
                     37: VOID MoveNewEdges(EDGE *pGETHead, EDGE *pAETHead, INT iCurrentY);
                     38: EDGE * AddEdgeToGET(EDGE *pGETHead, EDGE *pFreeEdge, POINTFIX *ppfxEdgeStart,
                     39:         POINTFIX *ppfxEdgeEnd);
                     40: VOID ConstructGET(EDGE *pGETHead, EDGE *pFreeEdges, PATHOBJ *ppo,
                     41:         PATHDATA *pd, BOOL bMore);
                     42: 
                     43: /******************************Public*Routine******************************\
                     44: * DrvFillPath
                     45: *
                     46: * Fill the specified path with the specified brush and ROP.
                     47: *
                     48: \**************************************************************************/
                     49: 
                     50: BOOL DrvFillPath
                     51: (
                     52:     SURFOBJ  *pso,
                     53:     PATHOBJ  *ppo,
                     54:     CLIPOBJ  *pco,
                     55:     BRUSHOBJ *pbo,
                     56:     POINTL   *pptlBrush,
                     57:     MIX       mix,
                     58:     FLONG    flOptions
                     59: )
                     60: {
                     61:     PPDEV ppdev;
                     62:     BYTE jClipping;     // clipping type
                     63:     EDGE *pCurrentEdge;
                     64:     EDGE AETHead;       // dummy head/tail node & sentinel for Active Edge Table
                     65:     EDGE *pAETHead;     // pointer to AETHead
                     66:     EDGE GETHead;       // dummy head/tail node & sentinel for Global Edge Table
                     67:     EDGE *pGETHead;     // pointer to GETHead
                     68:     EDGE *pFreeEdges;   // pointer to memory free for use to store edges
                     69:     ULONG ulNumRects;   // # of rectangles to draw currently in rectangle list
                     70:     RECTL *prclRects;   // pointer to start of rectangle draw list
                     71:     INT iCurrentY;      // scan line for which we're currently scanning out the
                     72:                         //  fill
                     73: 
                     74:     RBRUSH_COLOR rbc;    // Realized brush or solid color
                     75:     PFNFILL      pfnFill;// Points to appropriate fill routine
                     76: 
                     77:    BOOL     bMore;
                     78:    PATHDATA pd;
                     79: 
                     80:     // The drawing surface
                     81:     ppdev = (PPDEV) pso->dhsurf;
                     82: 
                     83:     // Our rectangle fill routines work only in planar mode:
                     84:     if (!(ppdev->fl & DRIVER_PLANAR_CAPABLE))
                     85:         return(FALSE);
                     86: 
                     87:     // Is there clipping? We don't handle that
                     88:     // LATER handle rectangle clipping
                     89: 
                     90:     // Set up the clipping type
                     91:     if (pco == (CLIPOBJ *) NULL) {
                     92:         // No CLIPOBJ provided, so we don't have to worry about clipping
                     93:         jClipping = DC_TRIVIAL;
                     94:     } else {
                     95:         // Use the CLIPOBJ-provided clipping
                     96:         jClipping = pco->iDComplexity;
                     97:     }
                     98: 
                     99:     if (jClipping != DC_TRIVIAL) {
                    100:         return(FALSE);  // there is clipping; let GDI fill the path
                    101:     }
                    102: 
                    103:     // There's nothing to do if there's only one point
                    104:     // LATER is this needed?
                    105:     if (ppo->cCurves <= 1) {
                    106:         return(TRUE);
                    107:     }
                    108: 
                    109:     // Do we have enough memory for all the edges?
                    110:     // LATER does cCurves include closure?
                    111:     if (ppo->cCurves > ((TMP_BUFFER_SIZE -
                    112:             (MAX_PATH_RECTS * (ULONG)sizeof(RECTL))) / sizeof(EDGE))) {
                    113:         return(FALSE);  // too many edges; let GDI fill the path
                    114:     }
                    115: 
                    116:     // See if we can handle this pattern and ROP
                    117: 
                    118:     // We don't handle ROP4s
                    119:     // LATER how could we possibly get a ROP4?
                    120:     if ((mix & 0xFF) != ((mix >> 8) & 0xFF)) {
                    121:         return(FALSE);  // it's a ROP4; let GDI fill the path
                    122:     }
                    123: 
                    124: 
                    125:     // See if we can use the solid brush accelerators, or have to draw a
                    126:     // pattern
                    127:     mix &= 0xFF;
                    128:     switch (mix) {
                    129:         case 0:
                    130:             return(FALSE);  // LATER should this ever happen?
                    131: 
                    132:         case R2_MASKNOTPEN:
                    133:         case R2_NOTCOPYPEN:
                    134:         case R2_XORPEN:
                    135:         case R2_MASKPEN:
                    136:         case R2_NOTXORPEN:
                    137:         case R2_MERGENOTPEN:
                    138:         case R2_COPYPEN:
                    139:         case R2_MERGEPEN:
                    140:         case R2_NOTMERGEPEN:
                    141:         case R2_MASKPENNOT:
                    142:         case R2_NOTMASKPEN:
                    143:         case R2_MERGEPENNOT:
                    144: 
                    145:             // vTrgBlt can only handle solid color fills
                    146: 
                    147:             if (pbo->iSolidColor != 0xffffffff)
                    148:             {
                    149:                 rbc.iSolidColor = pbo->iSolidColor;
                    150:                 pfnFill = vTrgBlt;
                    151:             }
                    152:             else
                    153:             {
                    154:                 rbc.prb = (RBRUSH*) pbo->pvRbrush;
                    155:                 if (rbc.prb == NULL)
                    156:                 {
                    157:                     rbc.prb = (RBRUSH*) BRUSHOBJ_pvGetRbrush(pbo);
                    158:                     if (rbc.prb == NULL)
                    159:                     {
                    160:                     // If we haven't realized the brush, punt the call:
                    161: 
                    162:                         return(FALSE);
                    163:                     }
                    164:                 }
                    165:                 if (!(rbc.prb->fl & RBRUSH_BLACKWHITE) &&
                    166:                     ((mix & 0xff) != R2_COPYPEN))
                    167:                 {
                    168:                 // Only black/white brushes can handle ROPs other
                    169:                 // than COPYPEN:
                    170: 
                    171:                     return(FALSE);
                    172:                 }
                    173: 
                    174:                 if (rbc.prb->fl & RBRUSH_NCOLOR)
                    175:                     pfnFill = vColorPat;
                    176:                 else
                    177:                     pfnFill = vMonoPat;
                    178:             }
                    179: 
                    180:             break;
                    181: 
                    182:         // Rops that are implicit solid colors
                    183: 
                    184:         case R2_NOT:
                    185:         case R2_WHITE:
                    186:         case R2_BLACK:
                    187: 
                    188:             // Brush color parameter doesn't matter for these rops
                    189: 
                    190:             pfnFill = vTrgBlt;
                    191:             break;
                    192: 
                    193:         case R2_NOP:
                    194:             return TRUE;
                    195:     }
                    196: 
                    197: /* set up working storage in the temporary buffer */
                    198: 
                    199:    prclRects = ppdev->pvTmp;                // 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:       RECTL *rectangle;
                    210:       INT i = pd.count;
                    211: 
                    212:    /* if the count is less than three than it is at best a
                    213:       line which means nothing gets drawn so get  out  now */
                    214: 
                    215:       if (i < 3) return(TRUE);
                    216: 
                    217:    /* if the count is four, check to see if the polygon is
                    218:       really a rectangle since we can really speed that up */
                    219: 
                    220:       if (i == 4) {
                    221:          rectangle = prclRects;
                    222: 
                    223:       /* we have to start somewhere so assume that most
                    224:          applications specify the top left point  first
                    225: 
                    226:          we want to check that the first two points are
                    227:          either vertically or horizontally aligned.  if
                    228:          they are then we check that the last point [3]
                    229:          is either horizontally or  vertically  aligned,
                    230:          and finally that the 3rd point [2] is  aligned
                    231:          with both the first point and the  last  point */
                    232: 
                    233: #define FIX_SHIFT 4L
                    234: #define FIX_MASK (- (1 << FIX_SHIFT))
                    235: 
                    236:          rectangle->top   = pd.pptfx[0].y - 1 & FIX_MASK;
                    237:          rectangle->left  = pd.pptfx[0].x - 1 & FIX_MASK;
                    238:          rectangle->right = pd.pptfx[1].x - 1 & FIX_MASK;
                    239: 
                    240:          if (rectangle->left ^ rectangle->right) {
                    241:             if (rectangle->top  ^ (pd.pptfx[1].y - 1 & FIX_MASK))
                    242:                goto not_rectangle;
                    243: 
                    244:             if (rectangle->left ^ (pd.pptfx[3].x - 1 & FIX_MASK))
                    245:                goto not_rectangle;
                    246: 
                    247:             if (rectangle->right ^ (pd.pptfx[2].x - 1 & FIX_MASK))
                    248:                goto not_rectangle;
                    249: 
                    250:             rectangle->bottom = pd.pptfx[2].y - 1 & FIX_MASK;
                    251:             if (rectangle->bottom ^ (pd.pptfx[3].y - 1 & FIX_MASK))
                    252:                goto not_rectangle;
                    253:          }
                    254:          else {
                    255:             if (rectangle->top ^ (pd.pptfx[3].y - 1 & FIX_MASK))
                    256:                goto not_rectangle;
                    257: 
                    258:             rectangle->bottom = pd.pptfx[1].y - 1 & FIX_MASK;
                    259:             if (rectangle->bottom ^ (pd.pptfx[2].y - 1 & FIX_MASK))
                    260:                goto not_rectangle;
                    261: 
                    262:             rectangle->right = pd.pptfx[2].x - 1 & FIX_MASK;
                    263:             if (rectangle->right ^ (pd.pptfx[3].x - 1 & FIX_MASK))
                    264:                 goto not_rectangle;
                    265:          }
                    266: 
                    267:       /* if the left is greater than the right then
                    268:          swap them so the blt code doesn't wig  out */
                    269: 
                    270:          if (rectangle->left > rectangle->right) {
                    271:             FIX temp;
                    272: 
                    273:             temp = rectangle->left;
                    274:             rectangle->left = rectangle->right;
                    275:             rectangle->right = temp;
                    276:          }
                    277:          else {
                    278: 
                    279:          /* if left == right there's nothing to draw */
                    280: 
                    281:             if (rectangle->left == rectangle->right) {
                    282:                return(TRUE);
                    283:             }
                    284:          }
                    285: 
                    286:       /* shift the values to get pixel coordinates */
                    287: 
                    288:          rectangle->left  = (rectangle->left  >> FIX_SHIFT) + 1;
                    289:          rectangle->right = (rectangle->right >> FIX_SHIFT) + 1;
                    290: 
                    291:          if (rectangle->top > rectangle->bottom) {
                    292:             FIX temp;
                    293: 
                    294:             temp = rectangle->top;
                    295:             rectangle->top = rectangle->bottom;
                    296:             rectangle->bottom = temp;
                    297:          }
                    298:          else {
                    299:             if (rectangle->top == rectangle->bottom) {
                    300:                return(TRUE);
                    301:             }
                    302:          }
                    303: 
                    304:       /* shift the values to get pixel coordinates */
                    305: 
                    306:          rectangle->top    = (rectangle->top    >> FIX_SHIFT) + 1;
                    307:          rectangle->bottom = (rectangle->bottom >> FIX_SHIFT) + 1;
                    308: 
                    309:       /* if we get here then the polygon is a rectangle,
                    310:          set count to 1 and  goto  bottom  to  draw  it */
                    311: 
                    312:          ulNumRects = 1;
                    313:          goto draw_remaining_rectangles;
                    314:       }
                    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: 
                    420:                     (*pfnFill)(ppdev, ulNumRects, prclRects, mix, rbc,
                    421:                                pptlBrush);
                    422: 
                    423:                     // Reset the list to empty
                    424:                     ulNumRects = 0;
                    425:                 }
                    426: 
                    427:                 // Add the rectangle representing the current edge pair
                    428:                 // LATER coalesce rectangles
                    429:                 prclRects[ulNumRects].top = iCurrentY;
                    430:                 prclRects[ulNumRects].bottom = iCurrentY+1;
                    431:                 prclRects[ulNumRects].left = iLeftEdge;
                    432:                 prclRects[ulNumRects].right = pCurrentEdge->X;
                    433:                 ulNumRects++;
                    434:             }
                    435:         } while ((pCurrentEdge = pCurrentEdge->pNext) != pAETHead);
                    436: 
                    437:         iCurrentY++;    // next scan
                    438:     }
                    439: 
                    440: /* draw the remaining rectangles,  if there are any */
                    441: 
                    442: draw_remaining_rectangles:
                    443: 
                    444:     if (ulNumRects > 0) {
                    445:         (*pfnFill)(ppdev, ulNumRects, prclRects, mix, rbc, pptlBrush);
                    446:     }
                    447: 
                    448:     return(TRUE);   // done successfully
                    449: }
                    450: 
                    451: // Advance the edges in the AET to the next scan, dropping any for which we've
                    452: // done all scans. Assumes there is at least one edge in the AET.
                    453: VOID AdvanceAETEdges(EDGE *pAETHead)
                    454: {
                    455:     EDGE *pLastEdge, *pCurrentEdge;
                    456: 
                    457:     pLastEdge = pAETHead;
                    458:     pCurrentEdge = pLastEdge->pNext;
                    459:     do {
                    460: 
                    461:         // Count down this edge's remaining scans
                    462:         if (--pCurrentEdge->iScansLeft == 0) {
                    463:             // We've done all scans for this edge; drop this edge from the AET
                    464:             pLastEdge->pNext = pCurrentEdge->pNext;
                    465:         } else {
                    466:             // Advance the edge's X coordinate for a 1-scan Y advance
                    467:             // Advance by the minimum amount
                    468:             pCurrentEdge->X += pCurrentEdge->iXWhole;
                    469:             // Advance the error term and see if we got one extra pixel this
                    470:             // time
                    471:             pCurrentEdge->iErrorTerm += pCurrentEdge->iErrorAdjustUp;
                    472:             if (pCurrentEdge->iErrorTerm >= 0) {
                    473:                 // The error term turned over, so adjust the error term and
                    474:                 // advance the extra pixel
                    475:                 pCurrentEdge->iErrorTerm -= pCurrentEdge->iErrorAdjustDown;
                    476:                 pCurrentEdge->X += pCurrentEdge->iXDirection;
                    477:             }
                    478: 
                    479:             pLastEdge = pCurrentEdge;
                    480:         }
                    481:     } while ((pCurrentEdge = pLastEdge->pNext) != pAETHead);
                    482: }
                    483: 
                    484: // X-sort the AET, because the edges may have moved around relative to
                    485: // one another when we advanced them. We'll use a multipass bubble
                    486: // sort, which is actually okay for this application because edges
                    487: // rarely move relative to one another, so we usually do just one pass.
                    488: // Also, this makes it easy to keep just a singly-linked list. Assumes there
                    489: // are at least two edges in the AET.
                    490: VOID XSortAETEdges(EDGE *pAETHead)
                    491: {
                    492:     BOOL bEdgesSwapped;
                    493:     EDGE *pLastEdge, *pCurrentEdge, *pNextEdge;
                    494: 
                    495:     do {
                    496: 
                    497:         bEdgesSwapped = FALSE;
                    498:         pLastEdge = pAETHead;
                    499:         pCurrentEdge = pLastEdge->pNext;
                    500:         pNextEdge = pCurrentEdge->pNext;
                    501: 
                    502:         do {
                    503:             if (pNextEdge->X < pCurrentEdge->X) {
                    504: 
                    505:                 // Next edge is to the left of the current edge; swap them
                    506:                 pLastEdge->pNext = pNextEdge;
                    507:                 pCurrentEdge->pNext = pNextEdge->pNext;
                    508:                 pNextEdge->pNext = pCurrentEdge;
                    509:                 bEdgesSwapped = TRUE;
                    510:                 pCurrentEdge = pNextEdge;   // continue sorting before the edge
                    511:                                             //  we just swapped; it might move
                    512:                                             //  farther yet
                    513:             }
                    514:             pLastEdge = pCurrentEdge;
                    515:             pCurrentEdge = pLastEdge->pNext;
                    516:         } while ((pNextEdge = pCurrentEdge->pNext) != pAETHead);
                    517:     } while (bEdgesSwapped);
                    518: }
                    519: 
                    520: // Moves all edges that start on the current scan from the GET to the AET in
                    521: // X-sorted order. Parameters are pointer to head of GET and pointer to dummy
                    522: // edge at head of AET, plus current scan line. Assumes there's at least one
                    523: // edge to be moved.
                    524: VOID MoveNewEdges(EDGE *pGETHead, EDGE *pAETHead, INT iCurrentY)
                    525: {
                    526:     EDGE *pCurrentEdge = pAETHead;
                    527:     EDGE *pGETNext = pGETHead->pNext;
                    528: 
                    529:     do {
                    530: 
                    531:         // Scan through the AET until the X-sorted insertion point for this
                    532:         // edge is found. We can continue from where the last search left
                    533:         // off because the edges in the GET are in X sorted order, as is
                    534:         // the AET. The search always terminates because the AET sentinel
                    535:         // is greater than any valid X
                    536:         while (pGETNext->X > ((EDGE *)pCurrentEdge->pNext)->X) {
                    537:             pCurrentEdge = pCurrentEdge->pNext;
                    538:         }
                    539: 
                    540:         // We've found the insertion point; add the GET edge to the AET, and
                    541:         // remove it from the GET
                    542:         pGETHead->pNext = pGETNext->pNext;
                    543:         pGETNext->pNext = pCurrentEdge->pNext;
                    544:         pCurrentEdge->pNext = pGETNext;
                    545:         pCurrentEdge = pGETNext;    // continue insertion search for the next
                    546:                                     //  GET edge after the edge we just added
                    547:         pGETNext = pGETHead->pNext;
                    548: 
                    549:     } while (pGETNext->Y == iCurrentY);
                    550: }
                    551: 
                    552: 
                    553: 
                    554: 
                    555: 
                    556: // Build the Global Edge Table from the path. There must be enough memory in
                    557: // the free edge area to hold all edges. The GET is constructed in Y-X order,
                    558: // and has a head/tail/sentinel node at pGETHead.
                    559: 
                    560: VOID ConstructGET(
                    561:    EDGE     *pGETHead,
                    562:    EDGE     *pFreeEdges,
                    563:    PATHOBJ  *ppo,
                    564:    PATHDATA *pd,
                    565:    BOOL      bMore)
                    566: {
                    567:    POINTFIX pfxPathStart;    // point that started the current subpath
                    568:    POINTFIX pfxPathPrevious; // point before the current point in a subpath;
                    569:                               //  starts the current edge
                    570: 
                    571: /* Create an empty GET with the head node also a tail sentinel */
                    572: 
                    573:    pGETHead->pNext = pGETHead; // mark that the GET is empty
                    574:    pGETHead->Y = 0x7FFFFFFF;   // this is greater than any valid Y value, so
                    575:                                 //  searches will always terminate
                    576: 
                    577: /* PATHOBJ_vEnumStart is implicitly  performed  by  engine
                    578:    already and first path  is  enumerated  by  the  caller */
                    579: 
                    580: next_subpath:
                    581: 
                    582: /* Make sure the PATHDATA is not empty (is this necessary) */
                    583: 
                    584:    if (pd->count != 0) {
                    585: 
                    586:    /* If first point starts a subpath, remember it as such
                    587:       and go on to the next point,   so we can get an edge */
                    588: 
                    589:       if (pd->flags & PD_BEGINSUBPATH) {
                    590: 
                    591:       /* the first point starts the subpath;   remember it */
                    592: 
                    593:          pfxPathStart    = *pd->pptfx; /* the subpath starts here          */
                    594:          pfxPathPrevious = *pd->pptfx; /* this points starts the next edge */
                    595:          pd->pptfx++;                  /* advance to the next point        */
                    596:          pd->count--;                  /* count off this point             */
                    597:       }
                    598: 
                    599: 
                    600:    /* add edges in PATHDATA to GET,  in Y-X  sorted  order */
                    601: 
                    602:       while (pd->count--) {
                    603:          pFreeEdges =
                    604:             AddEdgeToGET(pGETHead, pFreeEdges, &pfxPathPrevious, pd->pptfx);
                    605:          pfxPathPrevious = *pd->pptfx; /* current point becomes previous   */
                    606:          pd->pptfx++;                  /* advance to the next point        */
                    607:       }
                    608: 
                    609: 
                    610:    /* If last point ends the subpath, insert the edge that
                    611:       connects to firs t point  (is this built in already) */
                    612: 
                    613:       if (pd->flags & PD_ENDSUBPATH) {
                    614:          pFreeEdges = AddEdgeToGET
                    615:             (pGETHead, pFreeEdges, &pfxPathPrevious, &pfxPathStart);
                    616:       }
                    617:    }
                    618: 
                    619: /* the initial loop conditions preclude a do, while or for */
                    620: 
                    621:    if (bMore) {
                    622:        bMore = PATHOBJ_bEnum(ppo, pd);
                    623:        goto next_subpath;
                    624:    }
                    625: }
                    626: 
                    627: // Adds the edge described by the two passed-in points to the Global Edge
                    628: // Table, if the edge spans at least one pixel vertically.
                    629: EDGE * AddEdgeToGET(EDGE *pGETHead, EDGE *pFreeEdge,
                    630:         POINTFIX *ppfxEdgeStart, POINTFIX *ppfxEdgeEnd)
                    631: {
                    632:     INT iYStart, iYEnd, iXStart, iXEnd, iYHeight, iXWidth;
                    633: 
                    634:     // Set the winding-rule direction of the edge, and put the endpoints in
                    635:     // top-to-bottom order
                    636:     iYHeight = ppfxEdgeEnd->y - ppfxEdgeStart->y;
                    637:     if (iYHeight >= 0) {
                    638:         iXStart = ppfxEdgeStart->x;
                    639:         iYStart = ppfxEdgeStart->y;
                    640:         iXEnd = ppfxEdgeEnd->x;
                    641:         iYEnd = ppfxEdgeEnd->y;
                    642:         pFreeEdge->iWindingDirection = 1;
                    643:     } else {
                    644:         iYHeight = -iYHeight;
                    645:         iXEnd = ppfxEdgeStart->x;
                    646:         iYEnd = ppfxEdgeStart->y;
                    647:         iXStart = ppfxEdgeEnd->x;
                    648:         iYStart = ppfxEdgeEnd->y;
                    649:         pFreeEdge->iWindingDirection = -1;
                    650:     }
                    651: 
                    652:     // First pixel scan line (non-fractional GIQ Y coordinate) edge intersects.
                    653:     // Dividing by 16 with a shift is okay because Y is always positive
                    654:     pFreeEdge->Y = (iYStart + 15) >> 4;
                    655: 
                    656:     // Calculate the number of pixels spanned by this edge
                    657:     pFreeEdge->iScansLeft = ((iYEnd + 15) >> 4) - pFreeEdge->Y;
                    658:     if (pFreeEdge->iScansLeft <= 0) {
                    659:         return(pFreeEdge);  // no pixels at all are spanned, so we can ignore
                    660:                             //  this edge
                    661:     }
                    662: 
                    663:     // Set the error term and adjustment factors, all in GIQ coordinates for
                    664:     // now
                    665:     iXWidth = iXEnd - iXStart;
                    666:     if (iXWidth >= 0) {
                    667:         // Left to right, so we change X as soon as we move at all
                    668:         pFreeEdge->iXDirection = 1;
                    669:         pFreeEdge->iErrorTerm = -1;
                    670:     } else {
                    671:         // Right to left, so we don't change X until we've moved a full GIQ
                    672:         // coordinate
                    673:         iXWidth = -iXWidth;
                    674:         pFreeEdge->iXDirection = -1;
                    675:         pFreeEdge->iErrorTerm = -iYHeight;
                    676:     }
                    677: 
                    678:     if (iXWidth >= iYHeight) {
                    679:         // Calculate base run length (minimum distance advanced in X for a 1-
                    680:         // scan advance in Y)
                    681:         pFreeEdge->iXWhole = iXWidth / iYHeight;
                    682:         // Add sign back into base run length if going right to left
                    683:         if (pFreeEdge->iXDirection == -1) {
                    684:             pFreeEdge->iXWhole = -pFreeEdge->iXWhole;
                    685:         }
                    686:         pFreeEdge->iErrorAdjustUp = iXWidth % iYHeight;
                    687:     } else {
                    688:         // Base run length is 0, because line is closer to vertical than
                    689:         // horizontal
                    690:         pFreeEdge->iXWhole = 0;
                    691:         pFreeEdge->iErrorAdjustUp = iXWidth;
                    692:     }
                    693:     pFreeEdge->iErrorAdjustDown = iYHeight;
                    694: 
                    695:     // If the edge doesn't start on a pixel scan (that is, it starts at a
                    696:     // fractional GIQ coordinate), advance it to the first pixel scan it
                    697:     // intersects
                    698:     // LATER might be faster to use multiplication and division to jump ahead,
                    699:     // rather than looping
                    700:     while ((iYStart & 0x0F) != 0) {
                    701:         // Starts at a fractional GIQ coordinate, not exactly on a pixel scan
                    702: 
                    703:         // Advance the edge's GIQ X coordinate for a 1-GIQ-pixel Y advance
                    704:         // Advance by the minimum amount
                    705:         iXStart += pFreeEdge->iXWhole;
                    706:         // Advance the error term and see if we got one extra pixel this time
                    707:         pFreeEdge->iErrorTerm += pFreeEdge->iErrorAdjustUp;
                    708:         if (pFreeEdge->iErrorTerm >= 0) {
                    709:             // The error term turned over, so adjust the error term and
                    710:             // advance the extra pixel
                    711:             pFreeEdge->iErrorTerm -= pFreeEdge->iErrorAdjustDown;
                    712:             iXStart += pFreeEdge->iXDirection;
                    713:         }
                    714:         iYStart++;  // advance to the next GIQ Y coordinate
                    715:     }
                    716: 
                    717:     // Turn the calculations into pixel rather than GIQ calculations
                    718: 
                    719:     // Move the X coordinate to the nearest pixel, and adjust the error term
                    720:     // accordingly
                    721:     // Dividing by 16 with a shift is okay because X is always positive
                    722:     pFreeEdge->X = (iXStart + 15) >> 4; // convert from GIQ to pixel coordinates
                    723: 
                    724:     // LATER adjust only if needed (if prestepped above)?
                    725:     if (pFreeEdge->iXDirection == 1) {
                    726:         // Left to right
                    727:         pFreeEdge->iErrorTerm -= pFreeEdge->iErrorAdjustDown *
                    728:                 (((iXStart + 15) & ~0x0F) - iXStart);
                    729:     } else {
                    730:         // Right to left
                    731:         pFreeEdge->iErrorTerm -= pFreeEdge->iErrorAdjustDown *
                    732:                 ((iXStart - 1) & 0x0F);
                    733:     }
                    734: 
                    735:     // Scale the error adjusts up by 16 times, to move 16 GIQ pixels at a time.
                    736:     // Shifts work to do the multiplying because these values are always
                    737:     // non-negative
                    738:     pFreeEdge->iErrorAdjustUp <<= 4;
                    739:     pFreeEdge->iErrorAdjustDown <<= 4;
                    740: 
                    741:     // Insert the edge into the GET in YX-sorted order. The search always ends
                    742:     // because the GET has a sentinel with a greater-than-possible Y value
                    743:     while ((pFreeEdge->Y > ((EDGE *)pGETHead->pNext)->Y) ||
                    744:             ((pFreeEdge->Y == ((EDGE *)pGETHead->pNext)->Y) &&
                    745:             (pFreeEdge->X > ((EDGE *)pGETHead->pNext)->X))) {
                    746:         pGETHead = pGETHead->pNext;
                    747:     }
                    748: 
                    749:     pFreeEdge->pNext = pGETHead->pNext; // link the edge into the GET
                    750:     pGETHead->pNext = pFreeEdge;
                    751: 
                    752:     return(++pFreeEdge);    // point to the next edge storage location for next
                    753:                             //  time
                    754: }
                    755: 

unix.superglobalmegacorp.com

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