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