Annotation of ntddk/src/video/displays/vga/fillpath.c, revision 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.