/*************************************************************************\ * Module Name: Lines.c * * Contains most of the required GDI line support. Supports drawing * lines in short 'strips' when clipping is complex or coordinates * are too large to be drawn by the line hardware. * * Copyright (c) 1990-1995 Microsoft Corporation \**************************************************************************/ #include "precomp.h" /////////////////////////////////////////////////////////////////////// // We have to be careful of arithmetic overflow in a number of places. // Fortunately, the compiler is guaranteed to natively support 64-bit // signed LONGLONGs and 64-bit unsigned DWORDLONGs. // // UUInt32x32To64(a, b) is a macro defined in 'winnt.h' that multiplies // two 32-bit ULONGs to produce a 64-bit DWORDLONG result. // // UInt64By32To32 is our own macro to divide a 64-bit DWORDLONG by // a 32-bit ULONG to produce a 32-bit ULONG result. // // UInt64Mod32To32 is our own macro to modulus a 64-bit DWORDLONG by // a 32-bit ULONG to produce a 32-bit ULONG result. // // 64 bit divides are usually very expensive. Since it's very rare // that we'll get lines where the upper 32 bits of the 64 bit result // are used, we can almost always use 32-bit ULONG divides. We still // must correctly handle the larger cases: #define UInt64Div32To32(a, b) \ ((((DWORDLONG)(a)) > ULONG_MAX) ? \ (ULONG)((DWORDLONG)(a) / (ULONG)(b)) : \ (ULONG)((ULONG)(a) / (ULONG)(b))) #define UInt64Mod32To32(a, b) \ ((((DWORDLONG)(a)) > ULONG_MAX) ? \ (ULONG)((DWORDLONG)(a) % (ULONG)(b)) : \ (ULONG)((ULONG)(a) % (ULONG)(b))) #define SWAPL(x,y,t) {t = x; x = y; y = t;} FLONG gaflRound[] = { FL_H_ROUND_DOWN | FL_V_ROUND_DOWN, // no flips FL_H_ROUND_DOWN | FL_V_ROUND_DOWN, // FL_FLIP_D FL_H_ROUND_DOWN, // FL_FLIP_V FL_V_ROUND_DOWN, // FL_FLIP_V | FL_FLIP_D FL_V_ROUND_DOWN, // FL_FLIP_SLOPE_ONE 0xbaadf00d, // FL_FLIP_SLOPE_ONE | FL_FLIP_D FL_H_ROUND_DOWN, // FL_FLIP_SLOPE_ONE | FL_FLIP_V 0xbaadf00d // FL_FLIP_SLOPE_ONE | FL_FLIP_V | FL_FLIP_D }; /******************************Public*Routine******************************\ * BOOL bLines(ppdev, pptfxFirst, pptfxBuf, cptfx, pls, * prclClip, apfn[], flStart) * * Computes the DDA for the line and gets ready to draw it. Puts the * pixel data into an array of strips, and calls a strip routine to * do the actual drawing. * \**************************************************************************/ BOOL bLines( PDEV* ppdev, POINTFIX* pptfxFirst, // Start of first line POINTFIX* pptfxBuf, // Pointer to buffer of all remaining lines RUN* prun, // Pointer to runs if doing complex clipping ULONG cptfx, // Number of points in pptfxBuf or number of runs // in prun LINESTATE* pls, // Colour and style info RECTL* prclClip, // Pointer to clip rectangle if doing simple clipping PFNSTRIP* apfn, // Array of strip functions FLONG flStart, // Flags for each line, which is a combination of: // FL_SIMPLE_CLIP // FL_COMPLEX_CLIP // FL_STYLED // FL_LAST_PEL_INCLUSIVE // - Should be set only for all integer lines, // and can't be used with FL_COMPLEX_CLIP ULONG ulHwMix) { ULONG M0; ULONG dM; ULONG N0; ULONG dN; ULONG dN_Original; FLONG fl; LONG x; LONG y; LONGLONG llBeta; LONGLONG llGamma; LONGLONG dl; LONGLONG ll; ULONG ulDelta; ULONG x0; ULONG y0; ULONG x1; ULONG cStylePels; // Major length of line in pixels for styling ULONG xStart; POINTL ptlStart; STRIP strip; PFNSTRIP pfn; LONG cPels; LONG* plStrip; LONG* plStripEnd; LONG cStripsInNextRun; POINTFIX* pptfxBufEnd = pptfxBuf + cptfx; // Last point in path record STYLEPOS spThis; // Style pos for this line BYTE* pjBase; pjBase = ppdev->pjBase; do { /***********************************************************************\ * Start the DDA calculations. * \***********************************************************************/ M0 = (LONG) pptfxFirst->x; dM = (LONG) pptfxBuf->x; N0 = (LONG) pptfxFirst->y; dN = (LONG) pptfxBuf->y; fl = flStart; // Check for non-clipped, non-styled integer endpoint lines if ((fl & (FL_CLIP | FL_STYLED)) == 0) { // Special-case integer end-point lines: if (((M0 | dM | N0 | dN) & (F - 1)) == 0) { LONG x0; LONG y0; LONG x1; LONG y1; x0 = M0 >> FLOG2; x1 = dM >> FLOG2; y0 = N0 >> FLOG2; y1 = dN >> FLOG2; // Unfortunately, we can only use the Weitek's point- // to-point capability for perfectly horizontal and // vertical lines, because for other lines the tie- // breaker rule comes into play, and the Weitek has // exactly the wrong tie-breaker convention. if (y0 == y1) { // Horizontal integer line. Do last-pel exclusion: if (x0 < x1) x1--; else if (x0 > x1) x1++; else goto Next_Line; // Zero-pel line CP_METALINE(ppdev, pjBase, x0, y0); CP_METALINE(ppdev, pjBase, x1, y1); CP_START_QUAD_WAIT(ppdev, pjBase); goto Next_Line; } else if (x0 == x1) { // Vertical integer line. Do last-pel exclusion: if (y0 < y1) y1--; else y1++; CP_METALINE(ppdev, pjBase, x0, y0); CP_METALINE(ppdev, pjBase, x1, y1); CP_START_QUAD_WAIT(ppdev, pjBase); goto Next_Line; } } } if ((LONG) M0 > (LONG) dM) { // Ensure that we run left-to-right: register ULONG ulTmp; SWAPL(M0, dM, ulTmp); SWAPL(N0, dN, ulTmp); fl |= FL_FLIP_H; } // Compute the delta dx. The DDI says we can never have a valid delta // with a magnitued more than 2^31 - 1, but GDI never actually checks // its transforms. So we have to check for this case to avoid overflow: dM -= M0; if ((LONG) dM < 0) { goto Next_Line; } if ((LONG) dN < (LONG) N0) { // Line runs from bottom to top, so flip across y = 0: N0 = -(LONG) N0; dN = -(LONG) dN; fl |= FL_FLIP_V; } dN -= N0; if ((LONG) dN < 0) { goto Next_Line; } // We now have a line running left-to-right, top-to-bottom from (M0, N0) // to (M0 + dM, N0 + dN): if (dN >= dM) { if (dN == dM) { // Have to special case slopes of one: fl |= FL_FLIP_SLOPE_ONE; } else { // Since line has slope greater than 1, flip across x = y: register ULONG ulTmp; SWAPL(dM, dN, ulTmp); SWAPL(M0, N0, ulTmp); fl |= FL_FLIP_D; } } fl |= gaflRound[(fl & FL_ROUND_MASK) >> FL_ROUND_SHIFT]; x = LFLOOR((LONG) M0); y = LFLOOR((LONG) N0); M0 = FXFRAC(M0); N0 = FXFRAC(N0); // Calculate the remainder term [ dM * (N0 + F/2) - M0 * dN ]: llGamma = UInt32x32To64(dM, N0 + F/2) - UInt32x32To64(M0, dN); if (fl & FL_V_ROUND_DOWN) // Adjust so y = 1/2 rounds down { llGamma--; } llGamma >>= FLOG2; llBeta = ~llGamma; /***********************************************************************\ * Figure out which pixels are at the ends of the line. * \***********************************************************************/ // The toughest part of GIQ is determining the start and end pels. // // Our approach here is to calculate x0 and x1 (the inclusive start // and end columns of the line respectively, relative to our normalized // origin). Then x1 - x0 + 1 is the number of pels in the line. The // start point is easily calculated by plugging x0 into our line equation // (which takes care of whether y = 1/2 rounds up or down in value) // getting y0, and then undoing the normalizing flips to get back // into device space. // // We look at the fractional parts of the coordinates of the start and // end points, and call them (M0, N0) and (M1, N1) respectively, where // 0 <= M0, N0, M1, N1 < 16. We plot (M0, N0) on the following grid // to determine x0: // // +-----------------------> +x // | // | 0 1 // | 0123456789abcdef // | // | 0 ........?xxxxxxx // | 1 ..........xxxxxx // | 2 ...........xxxxx // | 3 ............xxxx // | 4 .............xxx // | 5 ..............xx // | 6 ...............x // | 7 ................ // | 8 ................ // | 9 ......**........ // | a ........****...x // | b ............**** // | c .............xxx**** // | d ............xxxx **** // | e ...........xxxxx **** // | f ..........xxxxxx // | // | 2 3 // v // // +y // // This grid accounts for the appropriate rounding of GIQ and last-pel // exclusion. If (M0, N0) lands on an 'x', x0 = 2. If (M0, N0) lands // on a '.', x0 = 1. If (M0, N0) lands on a '?', x0 rounds up or down, // depending on what flips have been done to normalize the line. // // For the end point, if (M1, N1) lands on an 'x', x1 = // floor((M0 + dM) / 16) + 1. If (M1, N1) lands on a '.', x1 = // floor((M0 + dM)). If (M1, N1) lands on a '?', x1 rounds up or down, // depending on what flips have been done to normalize the line. // // Lines of exactly slope one require a special case for both the start // and end. For example, if the line ends such that (M1, N1) is (9, 1), // the line has gone exactly through (8, 0) -- which may be considered // to be part of 'x' because of rounding! So slopes of exactly slope // one going through (8, 0) must also be considered as belonging in 'x'. // // For lines that go left-to-right, we have the following grid: // // +-----------------------> +x // | // | 0 1 // | 0123456789abcdef // | // | 0 xxxxxxxx?....... // | 1 xxxxxxx......... // | 2 xxxxxx.......... // | 3 xxxxx........... // | 4 xxxx............ // | 5 xxx............. // | 6 xx.............. // | 7 x............... // | 8 x............... // | 9 x.....**........ // | a xx......****.... // | b xxx.........**** // | c xxxx............**** // | d xxxxx........... **** // | e xxxxxx.......... **** // | f xxxxxxx......... // | // | 2 3 // v // // +y // // This grid accounts for the appropriate rounding of GIQ and last-pel // exclusion. If (M0, N0) lands on an 'x', x0 = 0. If (M0, N0) lands // on a '.', x0 = 1. If (M0, N0) lands on a '?', x0 rounds up or down, // depending on what flips have been done to normalize the line. // // For the end point, if (M1, N1) lands on an 'x', x1 = // floor((M0 + dM) / 16) - 1. If (M1, N1) lands on a '.', x1 = // floor((M0 + dM)). If (M1, N1) lands on a '?', x1 rounds up or down, // depending on what flips have been done to normalize the line. // // Lines of exactly slope one must be handled similarly to the right-to- // left case. { // Calculate x0, x1 ULONG N1 = FXFRAC(N0 + dN); ULONG M1 = FXFRAC(M0 + dM); x1 = LFLOOR(M0 + dM); if (fl & FL_LAST_PEL_INCLUSIVE) { // It sure is easy to compute the first pel when lines have only // integer coordinates and are last-pel inclusive: x0 = 0; y0 = 0; // Last-pel inclusive lines that are exactly one pixel long // have a 'delta-x' and 'delta-y' equal to zero. The problem is // that our clip code assumes that 'delta-x' is always non-zero // (since it never happens with last-pel exclusive lines). As // an inelegant solution, we simply modify 'delta-x' in this // case -- because the line is exactly one pixel long, changing // the slope will obviously have no effect on rasterization. if (x1 == 0) { dM = 1; llGamma = 0; llBeta = ~llGamma; } } else { if (fl & FL_FLIP_H) { // --------------------------------------------------------------- // Line runs right-to-left: <---- // Compute x1: if (N1 == 0) { if (LROUND(M1, fl & FL_H_ROUND_DOWN)) { x1++; } } else if (abs((LONG) (N1 - F/2)) + M1 > F) { x1++; } if ((fl & (FL_FLIP_SLOPE_ONE | FL_H_ROUND_DOWN)) == (FL_FLIP_SLOPE_ONE)) { // Have to special-case diagonal lines going through our // the point exactly equidistant between two horizontal // pixels, if we're supposed to round x=1/2 down: if ((N1 > 0) && (M1 == N1 + 8)) x1++; // Don't you love special cases? Is this a rhetorical question? if ((N0 > 0) && (M0 == N0 + 8)) { x0 = 2; ulDelta = dN; goto right_to_left_compute_y0; } } // Compute x0: x0 = 1; ulDelta = 0; if (N0 == 0) { if (LROUND(M0, fl & FL_H_ROUND_DOWN)) { x0 = 2; ulDelta = dN; } } else if (abs((LONG) (N0 - F/2)) + M0 > F) { x0 = 2; ulDelta = dN; } // Compute y0: right_to_left_compute_y0: y0 = 0; ll = llGamma + (LONGLONG) ulDelta; if (ll >= (LONGLONG) (2 * dM - dN)) y0 = 2; else if (ll >= (LONGLONG) (dM - dN)) y0 = 1; } else { // --------------------------------------------------------------- // Line runs left-to-right: ----> // Compute x1: if (!(fl & FL_LAST_PEL_INCLUSIVE)) x1--; if (M1 > 0) { if (N1 == 0) { if (LROUND(M1, fl & FL_H_ROUND_DOWN)) x1++; } else if (abs((LONG) (N1 - F/2)) <= (LONG) M1) { x1++; } } if ((fl & (FL_FLIP_SLOPE_ONE | FL_H_ROUND_DOWN)) == (FL_FLIP_SLOPE_ONE | FL_H_ROUND_DOWN)) { // Have to special-case diagonal lines going through our // the point exactly equidistant between two horizontal // pixels, if we're supposed to round x=1/2 down: if ((M1 > 0) && (N1 == M1 + 8)) x1--; if ((M0 > 0) && (N0 == M0 + 8)) { x0 = 0; goto left_to_right_compute_y0; } } // Compute x0: x0 = 0; if (M0 > 0) { if (N0 == 0) { if (LROUND(M0, fl & FL_H_ROUND_DOWN)) x0 = 1; } else if (abs((LONG) (N0 - F/2)) <= (LONG) M0) { x0 = 1; } } // Compute y0: left_to_right_compute_y0: y0 = 0; if (llGamma >= (LONGLONG) (dM - (dN & (-(LONG) x0)))) { y0 = 1; } } } } cStylePels = x1 - x0 + 1; if ((LONG) cStylePels <= 0) goto Next_Line; xStart = x0; /***********************************************************************\ * Complex clipping. * \***********************************************************************/ if (fl & FL_COMPLEX_CLIP) { dN_Original = dN; Continue_Complex_Clipping: if (fl & FL_FLIP_H) { // Line runs right-to-left <----- x0 = xStart + cStylePels - prun->iStop - 1; x1 = xStart + cStylePels - prun->iStart - 1; } else { // Line runs left-to-right -----> x0 = xStart + prun->iStart; x1 = xStart + prun->iStop; } prun++; // Reset some variables we'll nuke a little later: dN = dN_Original; pls->spNext = pls->spComplex; // No overflow since large integer math is used. Both values // will be positive: dl = UInt32x32To64(x0, dN) + llGamma; // y0 = dl / dM: y0 = UInt64Div32To32(dl, dM); ASSERTDD((LONG) y0 >= 0, "y0 weird: Goofed up end pel calc?"); } /***********************************************************************\ * Simple rectangular clipping. * \***********************************************************************/ if (fl & FL_SIMPLE_CLIP) { ULONG y1; LONG xRight; LONG xLeft; LONG yBottom; LONG yTop; // Note that y0 and y1 are actually the lower and upper bounds, // respectively, of the y coordinates of the line (the line may // have actually shrunk due to first/last pel clipping). // // Also note that x0, y0 are not necessarily zero. RECTL* prcl = &prclClip[(fl & FL_RECTLCLIP_MASK) >> FL_RECTLCLIP_SHIFT]; // Normalize to the same point we've normalized for the DDA // calculations: xRight = prcl->right - x; xLeft = prcl->left - x; yBottom = prcl->bottom - y; yTop = prcl->top - y; if (yBottom <= (LONG) y0 || xRight <= (LONG) x0 || xLeft > (LONG) x1) { Totally_Clipped: if (fl & FL_STYLED) { pls->spNext += cStylePels; if (pls->spNext >= pls->spTotal2) pls->spNext %= pls->spTotal2; } goto Next_Line; } if ((LONG) x1 >= xRight) x1 = xRight - 1; // We have to know the correct y1, which we haven't bothered to // calculate up until now. This multiply and divide is quite // expensive; we could replace it with code similar to that which // we used for computing y0. // // The reason why we need the actual value, and not an upper // bounds guess like y1 = LFLOOR(dM) + 2 is that we have to be // careful when calculating x(y) that y0 <= y <= y1, otherwise // we can overflow on the divide (which, needless to say, is very // bad). dl = UInt32x32To64(x1, dN) + llGamma; // y1 = dl / dM: y1 = UInt64Div32To32(dl, dM); if (yTop > (LONG) y1) goto Totally_Clipped; if (yBottom <= (LONG) y1) { y1 = yBottom; dl = UInt32x32To64(y1, dM) + llBeta; // x1 = dl / dN: x1 = UInt64Div32To32(dl, dN); } // At this point, we've taken care of calculating the intercepts // with the right and bottom edges. Now we work on the left and // top edges: if (xLeft > (LONG) x0) { x0 = xLeft; dl = UInt32x32To64(x0, dN) + llGamma; // y0 = dl / dM; y0 = UInt64Div32To32(dl, dM); if (yBottom <= (LONG) y0) goto Totally_Clipped; } if (yTop > (LONG) y0) { y0 = yTop; dl = UInt32x32To64(y0, dM) + llBeta; // x0 = dl / dN + 1; x0 = UInt64Div32To32(dl, dN) + 1; if (xRight <= (LONG) x0) goto Totally_Clipped; } ASSERTDD(x0 <= x1, "Improper rectangle clip"); } /***********************************************************************\ * Done clipping. Unflip if necessary. * \***********************************************************************/ ptlStart.x = x + x0; ptlStart.y = y + y0; if (fl & FL_FLIP_D) { register LONG lTmp; SWAPL(ptlStart.x, ptlStart.y, lTmp); } if (fl & FL_FLIP_V) { ptlStart.y = -ptlStart.y; } cPels = x1 - x0 + 1; /***********************************************************************\ * Style calculations. * \***********************************************************************/ if (fl & FL_STYLED) { STYLEPOS sp; spThis = pls->spNext; pls->spNext += cStylePels; { if (pls->spNext >= pls->spTotal2) pls->spNext %= pls->spTotal2; if (fl & FL_FLIP_H) sp = pls->spNext - x0 + xStart; else sp = spThis + x0 - xStart; ASSERTDD(fl & FL_STYLED, "Oops"); // Normalize our target style position: if ((sp < 0) || (sp >= pls->spTotal2)) { sp %= pls->spTotal2; // The modulus of a negative number is not well-defined // in C -- if it's negative we'll adjust it so that it's // back in the range [0, spTotal2): if (sp < 0) sp += pls->spTotal2; } // Since we always draw the line left-to-right, but styling is // always done in the direction of the original line, we have // to figure out where we are in the style array for the left // edge of this line. if (fl & FL_FLIP_H) { // Line originally ran right-to-left: sp = -sp; if (sp < 0) sp += pls->spTotal2; pls->ulStyleMask = ~pls->ulStartMask; pls->pspStart = &pls->aspRtoL[0]; pls->pspEnd = &pls->aspRtoL[pls->cStyle - 1]; } else { // Line originally ran left-to-right: pls->ulStyleMask = pls->ulStartMask; pls->pspStart = &pls->aspLtoR[0]; pls->pspEnd = &pls->aspLtoR[pls->cStyle - 1]; } if (sp >= pls->spTotal) { sp -= pls->spTotal; if (pls->cStyle & 1) pls->ulStyleMask = ~pls->ulStyleMask; } pls->psp = pls->pspStart; while (sp >= *pls->psp) sp -= *pls->psp++; ASSERTDD(pls->psp <= pls->pspEnd, "Flew off into NeverNeverLand"); pls->spRemaining = *pls->psp - sp; if ((pls->psp - pls->pspStart) & 1) pls->ulStyleMask = ~pls->ulStyleMask; } } plStrip = &strip.alStrips[0]; plStripEnd = &strip.alStrips[STRIP_MAX]; // Is exclusive cStripsInNextRun = 0x7fffffff; strip.ptlStart = ptlStart; if (2 * dN > dM && !(fl & FL_STYLED)) { // Do a half flip! Remember that we may doing this on the // same line multiple times for complex clipping (meaning the // affected variables should be reset for every clip run): fl |= FL_FLIP_HALF; llBeta = llGamma - (LONGLONG) ((LONG) dM); dN = dM - dN; y0 = x0 - y0; // Note this may overflow, but that's okay } // Now, run the DDA starting at (ptlStart.x, ptlStart.y)! strip.flFlips = fl; pfn = apfn[(fl & FL_STRIP_MASK) >> FL_STRIP_SHIFT]; // Now calculate the DDA variables needed to figure out how many pixels // go in the very first strip: { register LONG i; register ULONG dI; register ULONG dR; ULONG r; if (dN == 0) i = 0x7fffffff; else { dl = UInt32x32To64(y0 + 1, dM) + llBeta; ASSERTDD(dl >= 0, "Oops!"); // i = (dl / dN) - x0 + 1; // r = (dl % dN); i = UInt64Div32To32(dl, dN); r = UInt64Mod32To32(dl, dN); i = i - x0 + 1; dI = dM / dN; dR = dM % dN; // 0 <= dR < dN ASSERTDD(dI > 0, "Weird dI"); } ASSERTDD(i > 0 && i <= 0x7fffffff, "Weird initial strip length"); ASSERTDD(cPels > 0, "Zero pel line"); /***********************************************************************\ * Run the DDA! * \***********************************************************************/ while(TRUE) { cPels -= i; if (cPels <= 0) break; *plStrip++ = i; if (plStrip == plStripEnd) { strip.cStrips = (LONG)(plStrip - &strip.alStrips[0]); (*pfn)(ppdev, &strip, pls); plStrip = &strip.alStrips[0]; } i = dI; r += dR; if (r >= dN) { r -= dN; i++; } } *plStrip++ = cPels + i; strip.cStrips = (ULONG)(plStrip - &strip.alStrips[0]); (*pfn)(ppdev, &strip, pls); } Next_Line: if (fl & FL_COMPLEX_CLIP) { cptfx--; if (cptfx != 0) goto Continue_Complex_Clipping; break; } else { pptfxFirst = pptfxBuf; pptfxBuf++; } } while (pptfxBuf < pptfxBufEnd); return(TRUE); } /******************************Public*Routine******************************\ * BOOL bIntegerUnclippedLines * * Draws lines using the Weitek's point-to-point capabilities. * Unfortunately, the Weitek has exactly the wrong rounding convention * for tie-breakers, and GDI is very picky about this. * * Consequently, we can only use the line hardware when we know there * will be no tie-breakers. Fortunately, this is pretty easy to detect, * and the odds are that 3 out of 4 lines will not have tie breakers. For * those cases where there are tie-breakers, we can still usually draw the * lines using the hardware, this time by doing a one-wide trapezoid. * Unfortunately, this works for only 6 of the 8 octants, so for the final * case we punt to our strips routine. * * Additional complications include the fact that lines have to be last-pel * exclusive, and that we try to optimize horizontal and vertical lines. * \**************************************************************************/ BOOL bIntegerUnclippedLines( PDEV* ppdev, POINTFIX* pptfxFirst, POINTFIX* pptfxBuf, RUN* prun, ULONG cptfx, LINESTATE* pls, RECTL* prclClip, PFNSTRIP* apfn, FLONG flStart, ULONG ulHwMix) { BYTE* pjBase; BOOL bClippingSet; ULONG ulLineMix; ULONG ulTrapezoidMix; LONG x0; LONG y0; LONG x1; LONG y1; LONG xLeft; LONG xRight; LONG yTop; LONG yBottom; LONG dx; LONG dy; LONG lOr; LONG lBit; LONG iShift; LONG xDir; LONG yDir; pjBase = ppdev->pjBase; bClippingSet = FALSE; if (P9000(ppdev)) { ulTrapezoidMix = ulHwMix; ulLineMix = ulTrapezoidMix | P9000_OVERSIZED; } else { ulTrapezoidMix = ulHwMix & 0xff; ulLineMix = ulTrapezoidMix | P9100_OVERSIZED; } while (TRUE) { x0 = pptfxFirst->x; y0 = pptfxFirst->y; x1 = pptfxBuf->x; y1 = pptfxBuf->y; // First, check to see if the line is has all-integer coordinates: if (((x0 | y0 | x1 | y1) & 0xf) != 0) { // Ack, this line has non-integer coordinates. The rest of the // lines in this batch likely have non-integer coordinates // as well, so punt the entire batch to our strips routine: if (bClippingSet) { CP_WAIT(ppdev, pjBase); CP_RASTER(ppdev, pjBase, ulLineMix); CP_ABS_WMIN(ppdev, pjBase, 0, 0); CP_ABS_WMAX(ppdev, pjBase, MAX_COORD, MAX_COORD); } return(bLines(ppdev, pptfxFirst, pptfxBuf, prun, cptfx, pls, prclClip, apfn, flStart, ulHwMix)); } else { x0 >>= 4; x1 >>= 4; y0 >>= 4; y1 >>= 4; if ((y0 == y1) && (!bClippingSet)) { // We special case horizontal lines: if (x0 < x1) x1--; else if (x0 > x1) x1++; else goto Next_Line; // Zero-length line CP_METALINE(ppdev, pjBase, x0, y0); CP_METALINE(ppdev, pjBase, x1, y0); CP_START_QUAD_WAIT(ppdev, pjBase); goto Next_Line; } else if (y0 < y1) { yTop = y0; yBottom = y1; } else { yBottom = y0; yTop = y1; } if ((x0 == x1) && (!bClippingSet)) { // We special case vertical lines: if (y0 < y1) y1--; else y1++; CP_METALINE(ppdev, pjBase, x0, y0); CP_METALINE(ppdev, pjBase, x0, y1); CP_START_QUAD_WAIT(ppdev, pjBase); goto Next_Line; } else if (x0 < x1) { xLeft = x0; xRight = x1; } else { xRight = x0; xLeft = x1; } dx = xRight - xLeft; dy = yBottom - yTop; if (dx >= dy) { if (dx == 0) goto Next_Line; // Get rid of zero-length line case // We have an x-major line. Adjust the clip box to // account for last-pel exclusion: if (x0 < x1) xRight--; else xLeft++; lOr = (dx | dy); lBit = 1; iShift = 1; while (!(lOr & lBit)) { lBit <<= 1; iShift++; } if (dx & lBit) { Output_Simple_Line: CP_METALINE(ppdev, pjBase, x0, y0); CP_METALINE(ppdev, pjBase, x1, y1); CP_WAIT(ppdev, pjBase); CP_RASTER(ppdev, pjBase, ulLineMix); CP_WMIN(ppdev, pjBase, xLeft, yTop); CP_WMAX(ppdev, pjBase, xRight, yBottom); CP_START_QUAD_WAIT(ppdev, pjBase); bClippingSet = TRUE; goto Next_Line; } else { if ((dx ^ dy) > 0) goto Punt_Line; // Ick, this x-major line has tie-breaker cases. xDir = 0; yDir = 1; dy >>= iShift; if (y0 > y1) { dy = -dy; yDir = -1; } y0 -= dy; y1 += dy; dx >>= iShift; if (x0 > x1) dx = -dx; x0 -= dx; x1 += dx; Output_Trapezoid_Line: CP_METAQUAD(ppdev, pjBase, x0, y0); CP_METAQUAD(ppdev, pjBase, x1 + xDir, y1 - yDir); CP_METAQUAD(ppdev, pjBase, x1, y1); CP_METAQUAD(ppdev, pjBase, x0 - xDir, y0 + yDir); CP_WAIT(ppdev, pjBase); CP_RASTER(ppdev, pjBase, ulTrapezoidMix); CP_WMIN(ppdev, pjBase, xLeft, yTop); CP_WMAX(ppdev, pjBase, xRight, yBottom); CP_START_QUAD_WAIT(ppdev, pjBase); bClippingSet = TRUE; goto Next_Line; } } else { // We have a y-major line. Adjust the clip box to // account for last-pel exclusion: if (y0 < y1) yBottom--; else yTop++; lOr = (dx | dy); lBit = 1; iShift = 1; while (!(lOr & lBit)) { lBit <<= 1; iShift++; } if (dy & lBit) { goto Output_Simple_Line; } else { // Ick, this y-major line has tie-breaker cases. yDir = 0; xDir = 1; dx >>= iShift; if (x0 > x1) { dx = -dx; xDir = -1; } x0 -= dx; x1 += dx; dy >>= iShift; if (y0 > y1) dy = -dy; y0 -= dy; y1 += dy; goto Output_Trapezoid_Line; } } } Punt_Line: if (bClippingSet) { CP_WAIT(ppdev, pjBase); CP_RASTER(ppdev, pjBase, ulLineMix); CP_ABS_WMIN(ppdev, pjBase, 0, 0); CP_ABS_WMAX(ppdev, pjBase, MAX_COORD, MAX_COORD); bClippingSet = FALSE; } bLines(ppdev, pptfxFirst, pptfxBuf, NULL, 1, pls, prclClip, apfn, flStart, ulHwMix); Next_Line: --cptfx; if (cptfx == 0) break; pptfxFirst = pptfxBuf; pptfxBuf++; } if (bClippingSet) { CP_WAIT(ppdev, pjBase); CP_RASTER(ppdev, pjBase, ulLineMix); // Might need for next batch CP_ABS_WMIN(ppdev, pjBase, 0, 0); CP_ABS_WMAX(ppdev, pjBase, MAX_COORD, MAX_COORD); } return(TRUE); } /******************************Public*Routine******************************\ * BOOL bIntegerClippedLines * * Draws lines using the hardware when there is a single clipping rectangle. * See 'bIntegerUnclippedLines' above for more details. * \**************************************************************************/ BOOL bIntegerClippedLines( PDEV* ppdev, POINTFIX* pptfxFirst, POINTFIX* pptfxBuf, RUN* prun, ULONG cptfx, LINESTATE* pls, RECTL* prclClip, PFNSTRIP* apfn, FLONG flStart, ULONG ulHwMix) { BYTE* pjBase; BOOL bClippingSet; ULONG ulLineMix; ULONG ulTrapezoidMix; LONG x0; LONG y0; LONG x1; LONG y1; LONG xLeft; LONG xRight; LONG yTop; LONG yBottom; LONG dx; LONG dy; LONG lOr; LONG lBit; LONG iShift; LONG xDir; LONG yDir; ASSERTDD(flStart & FL_SIMPLE_CLIP, "Expected only simple clipping"); pjBase = ppdev->pjBase; bClippingSet = FALSE; if (P9000(ppdev)) { ulTrapezoidMix = ulHwMix; ulLineMix = ulTrapezoidMix | P9000_OVERSIZED; } else { ulTrapezoidMix = ulHwMix & 0xff; ulLineMix = ulTrapezoidMix | P9100_OVERSIZED; } while (TRUE) { x0 = pptfxFirst->x; y0 = pptfxFirst->y; x1 = pptfxBuf->x; y1 = pptfxBuf->y; // First, check to see if the line is has all-integer coordinates: if (((x0 | y0 | x1 | y1) & 0xf) != 0) { // Ack, this line has non-integer coordinates. The rest of the // lines in this batch likely have non-integer coordinates // as well, so punt the entire batch to our strips routine: if (bClippingSet) { CP_WAIT(ppdev, pjBase); CP_RASTER(ppdev, pjBase, ulLineMix); CP_ABS_WMIN(ppdev, pjBase, 0, 0); CP_ABS_WMAX(ppdev, pjBase, MAX_COORD, MAX_COORD); } return(bLines(ppdev, pptfxFirst, pptfxBuf, prun, cptfx, pls, prclClip, apfn, flStart, ulHwMix)); } else { x0 >>= 4; x1 >>= 4; y0 >>= 4; y1 >>= 4; if (y0 < y1) { yTop = y0; yBottom = y1; } else { yBottom = y0; yTop = y1; } if (x0 < x1) { xLeft = x0; xRight = x1; } else { xRight = x0; xLeft = x1; } // Do a trivial rejection test, remembering that the bound box // we just computed is lower-right inclusive: if ((xLeft >= prclClip->right) || (yTop >= prclClip->bottom) || (xRight < prclClip->left) || (yBottom < prclClip->top)) { goto Next_Line; } else { dx = xRight - xLeft; dy = yBottom - yTop; if (dx >= dy) { if (dx == 0) goto Next_Line; // Get rid of zero-length line case // We have an x-major line. Adjust the clip box to // account for last-pel exclusion: if (x0 < x1) xRight--; else xLeft++; lOr = (dx | dy); lBit = 1; iShift = 1; while (!(lOr & lBit)) { lBit <<= 1; iShift++; } // The Weitek's clip registers are inclusive, and // are expected to be well-ordered: xLeft = max(xLeft, prclClip->left); yTop = max(yTop, prclClip->top); xRight = min(xRight, prclClip->right - 1); yBottom = min(yBottom, prclClip->bottom - 1); if ((xLeft <= xRight) && (yTop <= yBottom)) { if (dx & lBit) { Output_Simple_Line: CP_METALINE(ppdev, pjBase, x0, y0); CP_METALINE(ppdev, pjBase, x1, y1); CP_WAIT(ppdev, pjBase); CP_RASTER(ppdev, pjBase, ulLineMix); CP_WMIN(ppdev, pjBase, xLeft, yTop); CP_WMAX(ppdev, pjBase, xRight, yBottom); CP_START_QUAD_WAIT(ppdev, pjBase); bClippingSet = TRUE; goto Next_Line; } else { if ((dx ^ dy) > 0) goto Punt_Line; // Ick, this x-major line has tie-breaker cases. xDir = 0; yDir = 1; dy >>= iShift; if (y0 > y1) { dy = -dy; yDir = -1; } y0 -= dy; y1 += dy; dx >>= iShift; if (x0 > x1) dx = -dx; x0 -= dx; x1 += dx; Output_Trapezoid_Line: CP_METAQUAD(ppdev, pjBase, x0, y0); CP_METAQUAD(ppdev, pjBase, x1 + xDir, y1 - yDir); CP_METAQUAD(ppdev, pjBase, x1, y1); CP_METAQUAD(ppdev, pjBase, x0 - xDir, y0 + yDir); CP_WAIT(ppdev, pjBase); CP_RASTER(ppdev, pjBase, ulTrapezoidMix); CP_WMIN(ppdev, pjBase, xLeft, yTop); CP_WMAX(ppdev, pjBase, xRight, yBottom); CP_START_QUAD_WAIT(ppdev, pjBase); bClippingSet = TRUE; goto Next_Line; } } } else { // We have a y-major line. Adjust the clip box to // account for last-pel exclusion: if (y0 < y1) yBottom--; else yTop++; lOr = (dx | dy); lBit = 1; iShift = 1; while (!(lOr & lBit)) { lBit <<= 1; iShift++; } // The Weitek's clip registers are inclusive, and // are expected to be well-ordered: xLeft = max(xLeft, prclClip->left); yTop = max(yTop, prclClip->top); xRight = min(xRight, prclClip->right - 1); yBottom = min(yBottom, prclClip->bottom - 1); if ((xLeft <= xRight) && (yTop <= yBottom)) { if (dy & lBit) { goto Output_Simple_Line; } else { // Ick, this y-major line has tie-breaker cases. yDir = 0; xDir = 1; dx >>= iShift; if (x0 > x1) { dx = -dx; xDir = -1; } x0 -= dx; x1 += dx; dy >>= iShift; if (y0 > y1) dy = -dy; y0 -= dy; y1 += dy; goto Output_Trapezoid_Line; } } } } } Punt_Line: if (bClippingSet) { CP_WAIT(ppdev, pjBase); CP_RASTER(ppdev, pjBase, ulLineMix); CP_ABS_WMIN(ppdev, pjBase, 0, 0); CP_ABS_WMAX(ppdev, pjBase, MAX_COORD, MAX_COORD); bClippingSet = FALSE; } bLines(ppdev, pptfxFirst, pptfxBuf, NULL, 1, pls, prclClip, apfn, flStart, ulHwMix); Next_Line: --cptfx; if (cptfx == 0) break; pptfxFirst = pptfxBuf; pptfxBuf++; } if (bClippingSet) { CP_WAIT(ppdev, pjBase); CP_RASTER(ppdev, pjBase, ulLineMix); // Might need for next batch CP_ABS_WMIN(ppdev, pjBase, 0, 0); CP_ABS_WMAX(ppdev, pjBase, MAX_COORD, MAX_COORD); } return(TRUE); } /******************************Public*Routine******************************\ * BOOL bCacheCircle(ppdev, ppo, pco, pbo, bStroke, pla) * \**************************************************************************/ BOOL bCacheCircle( PDEV* ppdev, PATHOBJ* ppo, CLIPOBJ* pco, BRUSHOBJ* pbo, BOOL bStroke, // TRUE if stroke, FALSE if fill LINEATTRS* pla) // Used for strokes only { RECTFX rcfx; LONG xCircle; LONG yCircle; LONG xCached; LONG yCached; LONG cx; LONG cy; CIRCLEENTRY* pce; LONG i; BYTE* pjBase; RECTL rclDst; POINTL ptlSrc; ULONG ulHwMix; RECTL rclTmp; CLIPENUM ce; LONG c; LONG bMore; LONG iCircleCache; SURFOBJ* pso; CLIPOBJ co; BRUSHOBJ bo; if (!(ppdev->flStat & STAT_CIRCLE_CACHE)) return(FALSE); PATHOBJ_vGetBounds(ppo, &rcfx); // Normalize bounds to upper-left corner: xCircle = rcfx.xLeft & ~0xfL; yCircle = rcfx.yTop & ~0xfL; rcfx.xLeft -= xCircle; rcfx.xRight -= xCircle; rcfx.yTop -= yCircle; rcfx.yBottom -= yCircle; // Convert to pixel units: xCircle >>= 4; yCircle >>= 4; cx = (rcfx.xRight >> 4) + 2; cy = (rcfx.yBottom >> 4) + 2; if ((cx > CIRCLE_DIMENSION) || (cy > CIRCLE_DIMENSION)) { // This circle is too big to cache, so decline it: return(FALSE); } pjBase = ppdev->pjBase; pce = &ppdev->ace[0]; for (i = TOTAL_CIRCLE_COUNT; i != 0; i--) { if ((pce->bStroke == bStroke) && (pce->rcfxCircle.xLeft == rcfx.xLeft) && (pce->rcfxCircle.yTop == rcfx.yTop) && (pce->rcfxCircle.xRight == rcfx.xRight) && (pce->rcfxCircle.yBottom == rcfx.yBottom)) { Draw_It: // We got a hit! Colour-expand from our off-screen // cache to the screen: rclDst.left = xCircle; rclDst.right = xCircle + cx; rclDst.top = yCircle; rclDst.bottom = yCircle + cy; // 'ptlSrc' has to be in relative coordinates: ptlSrc.x = pce->xCached - ppdev->xOffset; ptlSrc.y = pce->yCached - ppdev->yOffset; CP_WAIT(ppdev, pjBase); if (P9000(ppdev)) { CP_FOREGROUND(ppdev, pjBase, pbo->iSolidColor); ulHwMix = 0xee22; } else { CP_COLOR0(ppdev, pjBase, pbo->iSolidColor); ulHwMix = 0xe2e2; } if ((pco == NULL) || (pco->iDComplexity == DC_TRIVIAL)) { ppdev->pfnCopyBlt(ppdev, 1, &rclDst, ulHwMix, &ptlSrc, &rclDst); } else if (pco->iDComplexity == DC_RECT) { if (bIntersect(&rclDst, &pco->rclBounds, &rclTmp)) ppdev->pfnCopyBlt(ppdev, 1, &rclTmp, ulHwMix, &ptlSrc, &rclDst); } else { CLIPOBJ_cEnumStart(pco, FALSE, CT_RECTANGLES, CD_ANY, 0); do { bMore = CLIPOBJ_bEnum(pco, sizeof(ce), (ULONG*) &ce); c = cIntersect(&rclDst, ce.arcl, ce.c); if (c != 0) ppdev->pfnCopyBlt(ppdev, c, ce.arcl, ulHwMix, &ptlSrc, &rclDst); } while (bMore); } return(TRUE); } } // Make an entry in our cache: iCircleCache = ppdev->iCircleCache; if (++iCircleCache >= TOTAL_CIRCLE_COUNT) iCircleCache = 0; ppdev->iCircleCache = iCircleCache; pce = &ppdev->ace[iCircleCache]; // We must place the circle in off-screen memory with the same dword // alignment as the one we've been asked to draw, because we're // going to have GDI draw there instead: xCached = pce->x + (xCircle & 3); yCached = pce->y; // Store all the relevant information about the circle: pce->xCached = xCached; pce->yCached = yCached; pce->bStroke = bStroke; pce->rcfxCircle.xLeft = rcfx.xLeft; pce->rcfxCircle.yTop = rcfx.yTop; pce->rcfxCircle.xRight = rcfx.xRight; pce->rcfxCircle.yBottom = rcfx.yBottom; // Fudge up some parameters for the GDI call: pso = ppdev->psoPunt; pso->pvScan0 = ppdev->pjScreen + ((yCached - yCircle) * ppdev->lDelta) + ((xCached - xCircle) * ppdev->cjPel); ASSERTDD((((ULONG_PTR) pso->pvScan0) & 0x3) == 0, "Surface must have dword alignment"); co.iDComplexity = DC_TRIVIAL; bo.iSolidColor = ppdev->ulWhite; // Erase old thing: CP_ABS_METARECT(ppdev, pjBase, xCached, yCached); CP_ABS_METARECT(ppdev, pjBase, xCached + CIRCLE_DIMENSION, yCached + CIRCLE_DIMENSION); CP_WAIT(ppdev, pjBase); CP_RASTER(ppdev, pjBase, 0); // Same on both P9000 and P9100 CP_START_QUAD(ppdev, pjBase); // Get GDI to draw the circle in our off-screen cache: if (bStroke) { EngStrokePath(pso, ppo, &co, NULL, &bo, NULL, pla, 0x0d0d); } else { EngFillPath(pso, ppo, &co, &bo, NULL, 0x0d0d, FP_ALTERNATEMODE); } goto Draw_It; } VOID (*gapfnStrip[])(PDEV*, STRIP*, LINESTATE*) = { vStripSolidHorizontal, vStripSolidVertical, vStripSolidDiagonalHorizontal, vStripSolidDiagonalVertical, vStripStyledHorizontal, vStripStyledVertical, vStripStyledVertical, // Diagonal goes here vStripStyledVertical, // Diagonal goes here }; // Style array for alternate style (alternates one pixel on, one pixel off): STYLEPOS gaspAlternateStyle[] = { 1 }; /******************************Public*Routine******************************\ * BOOL DrvStrokePath(pso, ppo, pco, pxo, pbo, pptlBrush, pla, mix) * * Strokes the path. * \**************************************************************************/ BOOL DrvStrokePath( SURFOBJ* pso, PATHOBJ* ppo, CLIPOBJ* pco, XFORMOBJ* pxo, BRUSHOBJ* pbo, POINTL* pptlBrush, LINEATTRS* pla, MIX mix) { STYLEPOS aspLtoR[STYLE_MAX_COUNT]; STYLEPOS aspRtoL[STYLE_MAX_COUNT]; LINESTATE ls; PFNSTRIP* apfn; PFNLINES pfnLines; FLONG fl; PDEV* ppdev; DSURF* pdsurf; OH* poh; RECTL arclClip[4]; // For rectangular clipping BYTE* pjBase; RECTL* prclClip; ULONG ulHwMix; ASSERTDD(((mix >> 8) & 0xff) == (mix & 0xff), "GDI gave us an improper mix"); // Pass the surface off to GDI if it's a device bitmap that we've // converted to a DIB: pdsurf = (DSURF*) pso->dhsurf; if (pdsurf->dt == DT_DIB) { return(EngStrokePath(pdsurf->pso, ppo, pco, pxo, pbo, pptlBrush, pla, mix)); } // We'll be drawing to the screen or an off-screen DFB; copy the surface's // offset now so that we won't need to refer to the DSURF again: poh = pdsurf->poh; ppdev = (PDEV*) pso->dhpdev; ppdev->xOffset = poh->x; ppdev->yOffset = poh->y; // Because we set GCAPS_BEZIERS, we have to watch out for Beziers: if (ppo->fl & PO_BEZIERS) { // We only try to cache solid-styled COPYPEN ellipses: if ((ppo->fl & PO_ELLIPSE) && (mix == 0x0d0d) && !(pla->fl & LA_ALTERNATE) && (pla->pstyle == NULL)) { if (bCacheCircle(ppdev, ppo, pco, pbo, TRUE, pla)) return(TRUE); } // Get GDI to break the Beziers into lines before calling us // again: return(FALSE); } pfnLines = bLines; if ((pla->pstyle == NULL) && !(pla->fl & LA_ALTERNATE)) { // We can accelerate solid lines: if (pco->iDComplexity == DC_TRIVIAL) { pfnLines = bIntegerUnclippedLines; } else if (pco->iDComplexity == DC_RECT) { RECTFX rcfxBounds; // We have to be sure that we don't overflow the hardware registers // for current position, line length, or DDA terms. We check // here to make sure that the current position and line length // values won't overflow: PATHOBJ_vGetBounds(ppo, &rcfxBounds); if (rcfxBounds.xLeft + (ppdev->xOffset * F) >= (MIN_INTEGER_BOUND * F) && rcfxBounds.xRight + (ppdev->xOffset * F) <= (MAX_INTEGER_BOUND * F) && rcfxBounds.yTop + (ppdev->yOffset * F) >= (MIN_INTEGER_BOUND * F) && rcfxBounds.yBottom + (ppdev->yOffset * F) <= (MAX_INTEGER_BOUND * F)) { pfnLines = bIntegerClippedLines; } } } pjBase = ppdev->pjBase; prclClip = NULL; fl = 0; // Look after styling initialization: if (pla->fl & LA_ALTERNATE) { ls.cStyle = 1; ls.spTotal = 1; ls.spTotal2 = 2; ls.spRemaining = 1; ls.aspRtoL = &gaspAlternateStyle[0]; ls.aspLtoR = &gaspAlternateStyle[0]; ls.spNext = HIWORD(pla->elStyleState.l); ls.xyDensity = 1; fl |= FL_STYLED; ls.ulStartMask = 0L; } else if (pla->pstyle != (FLOAT_LONG*) NULL) { PFLOAT_LONG pstyle; STYLEPOS* pspDown; STYLEPOS* pspUp; pstyle = &pla->pstyle[pla->cstyle]; ls.xyDensity = STYLE_DENSITY; ls.spTotal = 0; while (pstyle-- > pla->pstyle) { ls.spTotal += pstyle->l; } ls.spTotal *= STYLE_DENSITY; ls.spTotal2 = 2 * ls.spTotal; // Compute starting style position (this is guaranteed not to overflow): ls.spNext = HIWORD(pla->elStyleState.l) * STYLE_DENSITY + LOWORD(pla->elStyleState.l); fl |= FL_STYLED; ls.cStyle = pla->cstyle; ls.aspRtoL = aspRtoL; ls.aspLtoR = aspLtoR; if (pla->fl & LA_STARTGAP) ls.ulStartMask = 0xffffffffL; else ls.ulStartMask = 0L; pstyle = pla->pstyle; pspDown = &ls.aspRtoL[ls.cStyle - 1]; pspUp = &ls.aspLtoR[0]; while (pspDown >= &ls.aspRtoL[0]) { *pspDown = pstyle->l * STYLE_DENSITY; *pspUp = *pspDown; pspUp++; pspDown--; pstyle++; } } if (pco->iDComplexity == DC_RECT) { fl |= FL_SIMPLE_CLIP; arclClip[0] = pco->rclBounds; // FL_FLIP_D: arclClip[1].top = pco->rclBounds.left; arclClip[1].left = pco->rclBounds.top; arclClip[1].bottom = pco->rclBounds.right; arclClip[1].right = pco->rclBounds.bottom; // FL_FLIP_V: arclClip[2].top = -pco->rclBounds.bottom + 1; arclClip[2].left = pco->rclBounds.left; arclClip[2].bottom = -pco->rclBounds.top + 1; arclClip[2].right = pco->rclBounds.right; // FL_FLIP_V | FL_FLIP_D: arclClip[3].top = pco->rclBounds.left; arclClip[3].left = -pco->rclBounds.bottom + 1; arclClip[3].bottom = pco->rclBounds.right; arclClip[3].right = -pco->rclBounds.top + 1; prclClip = arclClip; } apfn = &gapfnStrip[4 * ((fl & FL_STYLE_MASK) >> FL_STYLE_SHIFT)]; // Get the device ready: ulHwMix = gaRop3FromMix[mix & 0xF]; ulHwMix = (ulHwMix << 8) | (ulHwMix); CP_WAIT(ppdev, pjBase); if (P9000(ppdev)) { CP_RASTER(ppdev, pjBase, P9000_OVERSIZED | ulHwMix); CP_BACKGROUND(ppdev, pjBase, pbo->iSolidColor); } else { CP_RASTER(ppdev, pjBase, P9100_OVERSIZED | (ulHwMix & 0xff)); CP_COLOR0(ppdev, pjBase, pbo->iSolidColor); } // Set up to enumerate the path: if (pco->iDComplexity != DC_COMPLEX) { PATHDATA pd; BOOL bMore; ULONG cptfx; POINTFIX ptfxStartFigure; POINTFIX ptfxLast; POINTFIX* pptfxFirst; POINTFIX* pptfxBuf; pd.flags = 0; do { bMore = PATHOBJ_bEnum(ppo, &pd); cptfx = pd.count; if (cptfx == 0) break; if (pd.flags & PD_BEGINSUBPATH) { ptfxStartFigure = *pd.pptfx; pptfxFirst = pd.pptfx; pptfxBuf = pd.pptfx + 1; cptfx--; } else { pptfxFirst = &ptfxLast; pptfxBuf = pd.pptfx; } if (pd.flags & PD_RESETSTYLE) ls.spNext = 0; if (cptfx > 0) { if (!pfnLines(ppdev, pptfxFirst, pptfxBuf, (RUN*) NULL, cptfx, &ls, prclClip, apfn, fl, ulHwMix)) { return(FALSE); } } ptfxLast = pd.pptfx[pd.count - 1]; if (pd.flags & PD_CLOSEFIGURE) { if (!pfnLines(ppdev, &ptfxLast, &ptfxStartFigure, (RUN*) NULL, 1, &ls, prclClip, apfn, fl, ulHwMix)) { return(FALSE); } } } while (bMore); if (fl & FL_STYLED) { // Save the style state: ULONG ulHigh; ULONG ulLow; // Masked styles don't normalize the style state. It's a good // thing to do, so let's do it now: if ((ULONG) ls.spNext >= (ULONG) ls.spTotal2) ls.spNext = (ULONG) ls.spNext % (ULONG) ls.spTotal2; ulHigh = ls.spNext / ls.xyDensity; ulLow = ls.spNext % ls.xyDensity; pla->elStyleState.l = MAKELONG(ulLow, ulHigh); } } else { // Local state for path enumeration: BOOL bMore; union { BYTE aj[offsetof(CLIPLINE, arun) + RUN_MAX * sizeof(RUN)]; CLIPLINE cl; } cl; fl |= FL_COMPLEX_CLIP; // We use the clip object when non-simple clipping is involved: PATHOBJ_vEnumStartClipLines(ppo, pco, pso, pla); do { bMore = PATHOBJ_bEnumClipLines(ppo, sizeof(cl), &cl.cl); if (cl.cl.c != 0) { if (fl & FL_STYLED) { ls.spComplex = HIWORD(cl.cl.lStyleState) * ls.xyDensity + LOWORD(cl.cl.lStyleState); } if (!pfnLines(ppdev, &cl.cl.ptfxA, &cl.cl.ptfxB, &cl.cl.arun[0], cl.cl.c, &ls, (RECTL*) NULL, apfn, fl, ulHwMix)) { return(FALSE); } } } while (bMore); } return(TRUE); }