/******************************Module*Header***********************************\ * * ******************* * * GDI SAMPLE CODE * * ******************* * * Module Name: lines.c * * Contains the code for drawing short fractional endpoint lines and * longer lines with strips. * * Copyright (C) 1994-1998 3Dlabs Inc. Ltd. All rights reserved. * Copyright (C) 1995-1999 Microsoft Corporation. All rights reserved. ******************************************************************************/ #include "precomp.h" #define SWAPL(x,y,t) {t = x; x = y; y = t;} #define ABS(a) ((a) < 0 ? -(a) : (a)) 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 }; BOOL bIntegerLine(PDev*, ULONG, ULONG, ULONG, ULONG); BOOL bHardwareLine(PDev*, POINTFIX*, POINTFIX*); //------------------------------------------------------------------------------ // // 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. // // Doing Lines Right // ----------------- // // In NT, all lines are given to the device driver in fractional // coordinates, in a 28.4 fixed point format. The lower 4 bits are // fractional for sub-pixel positioning. // // Note that you CANNOT! just round the coordinates to integers // and pass the results to your favorite integer Bresenham routine!! // (Unless, of course, you have such a high resolution device that // nobody will notice -- not likely for a display device.) The // fractions give a more accurate rendering of the line -- this is // important for things like our Bezier curves, which would have 'kinks' // if the points in its polyline approximation were rounded to integers. // // Unfortunately, for fractional lines there is more setup work to do // a DDA than for integer lines. However, the main loop is exactly // the same (and can be done entirely with 32 bit math). // // If You've Got Hardware That Does Bresenham // ------------------------------------------ // // A lot of hardware limits DDA error terms to 'n' bits. With fractional // coordinates, 4 bits are given to the fractional part, letting // you draw in hardware only those lines that lie entirely in a 2^(n-4) // by 2^(n-4) pixel space. // // And you still have to correctly draw those lines with coordinates // outside that space! Remember that the screen is only a viewport // onto a 28.4 by 28.4 space -- if any part of the line is visible // you MUST render it precisely, regardless of where the end points lie. // So even if you do it in software, somewhere you'll have to have a // 32 bit DDA routine. // // Our Implementation // ------------------ // // We employ a run length slice algorithm: our DDA calculates the // number of pixels that are in each row (or 'strip') of pixels. // // We've separated the running of the DDA and the drawing of pixels: // we run the DDA for several iterations and store the results in // a 'strip' buffer (which are the lengths of consecutive pixel rows of // the line), then we crank up a 'strip drawer' that will draw all the // strips in the buffer. // // We also employ a 'half-flip' to reduce the number of strip // iterations we need to do in the DDA and strip drawing loops: when a // (normalized) line's slope is more than 1/2, we do a final flip // about the line y = (1/2)x. So now, instead of each strip being // consecutive horizontal or vertical pixel rows, each strip is composed // of those pixels aligned in 45 degree rows. So a line like (0, 0) to // (128, 128) would generate only one strip. // // We also always draw only left-to-right. // // Style lines may have arbitrary style patterns. We specially // optimize the default patterns (and call them 'masked' styles). // // The DDA Derivation // ------------------ // // Here is how I like to think of the DDA calculation. // // We employ Knuth's "diamond rule": rendering a one-pixel-wide line // can be thought of as dragging a one-pixel-wide by one-pixel-high // diamond along the true line. Pixel centers lie on the integer // coordinates, and so we light any pixel whose center gets covered // by the "drag" region (John D. Hobby, Journal of the Association // for Computing Machinery, Vol. 36, No. 2, April 1989, pp. 209-229). // // We must define which pixel gets lit when the true line falls // exactly half-way between two pixels. In this case, we follow // the rule: when two pels are equidistant, the upper or left pel // is illuminated, unless the slope is exactly one, in which case // the upper or right pel is illuminated. (So we make the edges // of the diamond exclusive, except for the top and left vertices, // which are inclusive, unless we have slope one.) // // This metric decides what pixels should be on any line BEFORE it is // flipped around for our calculation. Having a consistent metric // this way will let our lines blend nicely with our curves. The // metric also dictates that we will never have one pixel turned on // directly above another that's turned on. We will also never have // a gap; i.e., there will be exactly one pixel turned on for each // column between the start and end points. All that remains to be // done is to decide how many pixels should be turned on for each row. // // So lines we draw will consist of varying numbers of pixels on // successive rows, for example: // // ****** // ***** // ****** // ***** // // We'll call each set of pixels on a row a "strip". // // (Please remember that our coordinate space has the origin as the // upper left pixel on the screen; postive y is down and positive x // is right.) // // Device coordinates are specified as fixed point 28.4 numbers, // where the first 28 bits are the integer coordinate, and the last // 4 bits are the fraction. So coordinates may be thought of as // having the form (x, y) = (M/F, N/F) where F is the constant scaling // factor F = 2^4 = 16, and M and N are 32 bit integers. // // Consider the line from (M0/F, N0/F) to (M1/F, N1/F) which runs // left-to-right and whose slope is in the first octant, and let // dM = M1 - M0 and dN = N1 - N0. Then dM >= 0, dN >= 0 and dM >= dN. // // Since the slope of the line is less than 1, the edges of the // drag region are created by the top and bottom vertices of the // diamond. At any given pixel row y of the line, we light those // pixels whose centers are between the left and right edges. // // Let mL(n) denote the line representing the left edge of the drag // region. On pixel row j, the column of the first pixel to be // lit is // // iL(j) = ceiling( mL(j * F) / F) // // Since the line's slope is less than one: // // iL(j) = ceiling( mL([j + 1/2] F) / F ) // // Recall the formula for our line: // // n(m) = (dN / dM) (m - M0) + N0 // // m(n) = (dM / dN) (n - N0) + M0 // // Since the line's slope is less than one, the line representing // the left edge of the drag region is the original line offset // by 1/2 pixel in the y direction: // // mL(n) = (dM / dN) (n - F/2 - N0) + M0 // // From this we can figure out the column of the first pixel that // will be lit on row j, being careful of rounding (if the left // edge lands exactly on an integer point, the pixel at that // point is not lit because of our rounding convention): // // iL(j) = floor( mL(j F) / F ) + 1 // // = floor( ((dM / dN) (j F - F/2 - N0) + M0) / F ) + 1 // // = floor( F dM j - F/2 dM - N0 dM + dN M0) / F dN ) + 1 // // F dM j - [ dM (N0 + F/2) - dN M0 ] // = floor( ---------------------------------- ) + 1 // F dN // // dM j - [ dM (N0 + F/2) - dN M0 ] / F // = floor( ------------------------------------ ) + 1 (1) // dN // // = floor( (dM j + alpha) / dN ) + 1 // // where // // alpha = - [ dM (N0 + F/2) - dN M0 ] / F // // We use equation (1) to calculate the DDA: there are iL(j+1) - iL(j) // pixels in row j. Because we are always calculating iL(j) for // integer quantities of j, we note that the only fractional term // is constant, and so we can 'throw away' the fractional bits of // alpha: // // beta = floor( - [ dM (N0 + F/2) - dN M0 ] / F ) (2) // // so // // iL(j) = floor( (dM j + beta) / dN ) + 1 (3) // // for integers j. // // Note if iR(j) is the line's rightmost pixel on row j, that // iR(j) = iL(j + 1) - 1. // // Similarly, rewriting equation (1) as a function of column i, // we can determine, given column i, on which pixel row j is the line // lit: // // dN i + [ dM (N0 + F/2) - dN M0 ] / F // j(i) = ceiling( ------------------------------------ ) - 1 // dM // // Floors are easier to compute, so we can rewrite this: // // dN i + [ dM (N0 + F/2) - dN M0 ] / F + dM - 1/F // j(i) = floor( ----------------------------------------------- ) - 1 // dM // // dN i + [ dM (N0 + F/2) - dN M0 ] / F + dM - 1/F - dM // = floor( ---------------------------------------------------- ) // dM // // dN i + [ dM (N0 + F/2) - dN M0 - 1 ] / F // = floor( ---------------------------------------- ) // dM // // We can once again wave our hands and throw away the fractional bits // of the remainder term: // // j(i) = floor( (dN i + gamma) / dM ) (4) // // where // // gamma = floor( [ dM (N0 + F/2) - dN M0 - 1 ] / F ) (5) // // We now note that // // beta = -gamma - 1 = ~gamma (6) // // To draw the pixels of the line, we could evaluate (3) on every scan // line to determine where the strip starts. Of course, we don't want // to do that because that would involve a multiply and divide for every // scan. So we do everything incrementally. // // We would like to easily compute c , the number of pixels on scan j: // j // // c = iL(j + 1) - iL(j) // j // // = floor((dM (j + 1) + beta) / dN) - floor((dM j + beta) / dN) (7) // // This may be rewritten as // // c = floor(i + r / dN) - floor(i + r / dN) (8) // j j+1 j+1 j j // // where i , i are integers and r < dN, r < dN. // j j+1 j j+1 // // Rewriting (7) again: // // c = floor(i + r / dN + dM / dN) - floor(i + r / dN) // j j j j j // // // = floor((r + dM) / dN) - floor(r / dN) // j j // // This may be rewritten as // // c = dI + floor((r + dR) / dN) - floor(r / dN) // j j j // // where dI + dR / dN = dM / dN, dI is an integer and dR < dN. // // r is the remainder (or "error") term in the DDA loop: r / dN // j j // is the exact fraction of a pixel at which the strip ends. To go // on to the next scan and compute c we need to know r . // j+1 j+1 // // So in the main loop of the DDA: // // c = dI + floor((r + dR) / dN) and r = (r + dR) % dN // j j j+1 j // // and we know r < dN, r < dN, and dR < dN. // j j+1 // // We have derived the DDA only for lines in the first octant; to // handle other octants we do the common trick of flipping the line // to the first octant by first making the line left-to-right by // exchanging the end-points, then flipping about the lines y = 0 and // y = x, as necessary. We must record the transformation so we can // undo them later. // // We must also be careful of how the flips affect our rounding. If // to get the line to the first octant we flipped about x = 0, we now // have to be careful to round a y value of 1/2 up instead of down as // we would for a line originally in the first octant (recall that // "In the case where two pels are equidistant, the upper or left // pel is illuminated..."). // // To account for this rounding when running the DDA, we shift the line // (or not) in the y direction by the smallest amount possible. That // takes care of rounding for the DDA, but we still have to be careful // about the rounding when determining the first and last pixels to be // lit in the line. // // Determining The First And Last Pixels In The Line // ------------------------------------------------- // // Fractional coordinates also make it harder to determine which pixels // will be the first and last ones in the line. We've already taken // the fractional coordinates into account in calculating the DDA, but // the DDA cannot tell us which are the end pixels because it is quite // happy to calculate pixels on the line from minus infinity to positive // infinity. // // The diamond rule determines the start and end pixels. (Recall that // the sides are exclusive except for the left and top vertices.) // This convention can be thought of in another way: there are diamonds // around the pixels, and wherever the true line crosses a diamond, // that pel is illuminated. // // Consider a line where we've done the flips to the first octant, and the // floor of the start coordinates is the origin: // // +-----------------------> +x // | // | 0 1 // | 0123456789abcdef // | // | 0 00000000?1111111 // | 1 00000000 1111111 // | 2 0000000 111111 // | 3 000000 11111 // | 4 00000 ** 1111 // | 5 0000 ****1 // | 6 000 1*** // | 7 00 1 **** // | 8 ? *** // | 9 22 3 **** // | a 222 33 *** // | b 2222 333 **** // | c 22222 3333 ** // | d 222222 33333 // | e 2222222 333333 // | f 22222222 3333333 // | // | 2 3 // v // +y // // If the start of the line lands on the diamond around pixel 0 (shown by // the '0' region here), pixel 0 is the first pel in the line. The same // is true for the other pels. // // A little more work has to be done if the line starts in the // 'nether-land' between the diamonds (as illustrated by the '*' line): // the first pel lit is the first diamond crossed by the line (pixel 1 in // our example). This calculation is determined by the DDA or slope of // the line. // // If the line starts exactly half way between two adjacent pixels // (denoted here by the '?' spots), the first pixel is determined by our // round-down convention (and is dependent on the flips done to // normalize the line). // // Last Pel Exclusive // ------------------ // // To eliminate repeatedly lit pels between continuous connected lines, // we employ a last-pel exclusive convention: if the line ends exactly on // the diamond around a pel, that pel is not lit. (This eliminates the // checks we had in the old code to see if we were re-lighting pels.) // // The Half Flip // ------------- // // To make our run length algorithm more efficient, we employ a "half // flip". If after normalizing to the first octant, the slope is more // than 1/2, we subtract the y coordinate from the x coordinate. This // has the effect of reflecting the coordinates through the line of slope // 1/2. Note that the diagonal gets mapped into the x-axis after a half // flip. // // How Many Bits Do We Need, Anyway? // --------------------------------- // // Note that if the line is visible on your screen, you must light up // exactly the correct pixels, no matter where in the 28.4 x 28.4 device // space the end points of the line lie (meaning you must handle 32 bit // DDAs, you can certainly have optimized cases for lesser DDAs). // // We move the origin to (floor(M0 / F), floor(N0 / F)), so when we // calculate gamma from (5), we know that 0 <= M0, N0 < F. And we // are in the first octant, so dM >= dN. Then we know that gamma can // be in the range [(-1/2)dM, (3/2)dM]. The DDI guarantees us that // valid lines will have dM and dN values at most 31 bits (unsigned) // of significance. So gamma requires 33 bits of significance (we store // this as a 64 bit number for convenience). // // When running through the DDA loop, r + dR can have a value in the // j // range 0 <= r < 2 dN; thus the result must be a 32 bit unsigned value. // j // // Testing Lines // ------------- // // To be NT compliant, a display driver must exactly adhere to GIQ, // which means that for any given line, the driver must light exactly // the same pels as does GDI. This can be tested using the Guiman tool // provided elsewhere in the DDK, and 'ZTest', which draws random lines // on the screen and to a bitmap, and compares the results. // // If You've Got Line Hardware // --------------------------- // // If your hardware already adheres to GIQ, you're all set. Otherwise // you'll want to look at the S3 sample code and read the following: // // 1) You'll want to special case integer-only lines, since they require // less processing time and are more common (CAD programs will probably // only ever give integer lines). GDI does not provide a flag saying // that all lines in a path are integer lines; consequently, you will // have to explicitly check every line. // // 2) You are required to correctly draw any line in the 28.4 device // space that intersects the viewport. If you have less than 32 bits // of significance in the hardware for the Bresenham terms, extremely // long lines would overflow the hardware. For such (rare) cases, you // can fall back to strip-drawing code, of which there is a C version in // the S3's lines.cxx (or if your display is a frame buffer, fall back // to the engine). // // 3) If you can explicitly set the Bresenham terms in your hardware, you // can draw non-integer lines using the hardware. If your hardware has // 'n' bits of precision, you can draw GIQ lines that are up to 2^(n-5) // pels long (4 bits are required for the fractional part, and one bit is // used as a sign bit). Note that integer lines don't require the 4 // fractional bits, so if you special case them as in 1), you can do // integer lines that are up to 2^(n - 1) pels long. See the S3's // fastline.asm for an example. // //------------------------------------------------------------------------------ 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 { 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 - 1; // Last point in path record STYLEPOS spThis; // Style pos for this line LONG xmask = 0xffff800f; LONG ymask = 0xffffc00f; LONG xmask1 = 0xffff8000; LONG ymask1 = 0xffffc000; PERMEDIA_DECL; 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-complex-clipped, non-styled integer endpoint lines // Essentially, we allow rendering of any line which 'looks' like an // unclipped solid line. Initialization of hardware will cause the // correct results to appear // if ((fl & (FL_COMPLEX_CLIP | FL_STYLED)) == 0 ) { LONG orx = (LONG) (M0 | dM); LONG ory = (LONG) (N0 | dN); if (orx < 0) { // At least one point was negative. Compute using abs points. orx = ABS((LONG)M0) | ABS((LONG)dM); } if (ory < 0) { // At least one point was negative. Compute using abs points. ory = ABS((LONG)N0) | ABS((LONG)dN); } DBG_GDI((7, "Lines: Trying Fast Integer %x %x %x %x", M0, N0, dM, dN)); // Call fast integer line routines it integer coordinates if (((orx & xmask) == 0) && ((ory & ymask) == 0)) { if (bFastIntegerLine(ppdev, M0, N0, dM, dN)) { if ((fl & FL_READ)) { // If we have a logical op which requires reading from // the frame buffer, we cannot guarantee // ContinueNewLine's behaviour when overwriting pixels. // Also, avoid ContinueNewLine on an MX. pptfxFirst = pptfxBuf; pptfxBuf++; continue; } else { // This is an optimization to use continue new line // to draw any subequent integer lines. The loop is // essentially the same as the outer loop, however, we // dont need to check for a lot of things that we already // know. We need to be able to fall out to the standard // outer loop if we cant handle a line though. while (TRUE) { // Have we reached the end of the list of points. if (pptfxBuf == pptfxBufEnd) return(TRUE); pptfxFirst = pptfxBuf; pptfxBuf++; M0 = dM; N0 = dN; dM = (LONG) pptfxBuf->x; dN = (LONG) pptfxBuf->y; // We know M0 and N0 satisfy our criteria for a // continue new line. Therefore, we just have to // check the new coordinates orx = (LONG) dM; ory = (LONG) dN; if (orx < 0) { // At least one point was negative. // Recompute or using abs. orx = ABS((LONG)dM); } if (ory < 0) { // At least one point was negative. // Recompute or using abs. ory = ABS((LONG)dN); } // We need to call the routine to continue // the line now. If the line is not a fast integer // line, then we need to break out and try non // integer lines. In this case, or will still be // valid, because we know M0, N0 are integer coords // that Permedia2 can handle. DBG_GDI((7, "Lines: Trying %x %x %x %x", M0, N0, dM, dN)); if (((orx & xmask) != 0) || ((ory & ymask) != 0) || (!bFastIntegerContinueLine( ppdev, M0, N0, dM, dN))) // Either we cant draw the line or the strip // drawer failed. break; } } } } // Call fast non integer line routines. if (((orx & xmask1) == 0) && ((ory & ymask1) == 0)) { if (bFastLine(ppdev, M0, N0, dM, dN)) { // This line done, do next line. pptfxFirst = pptfxBuf; pptfxBuf++; continue; } } } DBG_GDI((7, "Lines: Slow Lines %x %x %x %x", M0, N0, dM, dN)); 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 magnitude 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) // We can skip any lines with delta > 2^31 - 1 { 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) // We can skip any lines with delta > 2^31 - 1 { 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]; // // Convert M0 and N0 from 28.4 format to normal interger // 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 = Int32x32To64(dM, N0 + FBITS/2) - Int32x32To64(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); // // Store normal integer in x1, not 28.4 format // x1 = LFLOOR(M0 + dM); 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 - FBITS/2)) + M1 > FBITS) { 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++; 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 - FBITS/2)) + M0 > FBITS) { 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: x1--; if (M1 > 0) { if (N1 == 0) { if (LROUND(M1, fl & FL_H_ROUND_DOWN)) x1++; } else if (ABS((LONG) (N1 - FBITS/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 - FBITS/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 = Int32x32To64(x0, dN) + llGamma; 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 = Int32x32To64(x1, dN) + llGamma; y1 = UInt64Div32To32(dl, dM); if (yTop > (LONG) y1) goto Totally_Clipped; if (yBottom <= (LONG) y1) { y1 = yBottom; dl = Int32x32To64(y1, dM) + llBeta; 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 = Int32x32To64(x0, dN) + llGamma; y0 = UInt64Div32To32(dl, dM); if (yBottom <= (LONG) y0) goto Totally_Clipped; } if (yTop > (LONG) y0) { y0 = yTop; dl = Int32x32To64(y0, dM) + llBeta; 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_ARBITRARYSTYLED, "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) && !(fl & FL_DONT_DO_HALF_FLIP)) { // 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 = Int32x32To64(y0 + 1, dM) + llBeta; ASSERTDD(dl >= 0, "Oops!"); 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 = (LONG)(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); }// bLines()