|
|
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:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.