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