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

unix.superglobalmegacorp.com

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