/*************************************************************************\ * 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-1996 Microsoft Corporation * Copyright (c) 1993-1996 Matrox Electronic Systems, Ltd. \**************************************************************************/ #include "precomp.h" // The MGA's hardware coordinates are limited to 16-bit signed values: #define MIN_INTEGER_BOUND (-32767) #define MAX_INTEGER_BOUND (32767) /////////////////////////////////////////////////////////////////////// // 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 }; ////////////////////////////////////////////////////////////////////////// // General defines for bHardwareLine BOOL bHardwareLine(PDEV*, POINTFIX*, POINTFIX*); #define HW_FLIP_D 0x0001L // Diagonal flip #define HW_FLIP_V 0x0002L // Vertical flip #define HW_FLIP_H 0x0004L // Horizontal flip #define HW_FLIP_SLOPE_ONE 0x0008L // Normalized line has exactly slope one #define HW_FLIP_MASK (HW_FLIP_D | HW_FLIP_V | HW_FLIP_H) #define HW_X_ROUND_DOWN 0x0100L // x = 1/2 rounds down in value #define HW_Y_ROUND_DOWN 0x0200L // y = 1/2 rounds down in value LONG gaiDir[] = { 0, 1, 7, 6, 3, 2, 4, 5 }; FLONG gaflHardwareRound[] = { HW_X_ROUND_DOWN | HW_Y_ROUND_DOWN, // | | | HW_X_ROUND_DOWN | HW_Y_ROUND_DOWN, // | | | FLIP_D HW_X_ROUND_DOWN, // | | FLIP_V | HW_Y_ROUND_DOWN, // | | FLIP_V | FLIP_D HW_Y_ROUND_DOWN, // | FLIP_H | | HW_X_ROUND_DOWN, // | FLIP_H | | FLIP_D 0, // | FLIP_H | FLIP_V | 0, // | FLIP_H | FLIP_V | FLIP_D HW_Y_ROUND_DOWN, // SLOPE_ONE | | | 0xffffffff, // SLOPE_ONE | | | FLIP_D HW_X_ROUND_DOWN, // SLOPE_ONE | | FLIP_V | 0xffffffff, // SLOPE_ONE | | FLIP_V | FLIP_D HW_Y_ROUND_DOWN, // SLOPE_ONE | FLIP_H | | 0xffffffff, // SLOPE_ONE | FLIP_H | | FLIP_D HW_X_ROUND_DOWN, // SLOPE_ONE | FLIP_H | FLIP_V | 0xffffffff // SLOPE_ONE | FLIP_H | FLIP_V | FLIP_D }; ////////////////////////////////////////////////////////////////////////// // MGA specific defines // The MGA's hardware can have 16 bits of significance for the error and // step terms: #define NUM_DDA_BITS 16 ULONG gaiSign[] = { sdydxl_MAJOR_X | sdy_ADD | sdxl_ADD, // | | | sdydxl_MAJOR_Y | sdy_ADD | sdxl_ADD, // | | |FLIP_D sdydxl_MAJOR_X | sdy_SUB | sdxl_ADD, // | |FLIP_V| sdydxl_MAJOR_Y | sdy_SUB | sdxl_ADD, // | |FLIP_V|FLIP_D sdydxl_MAJOR_X | sdy_ADD | sdxl_SUB, // |FLIP_H| | sdydxl_MAJOR_Y | sdy_ADD | sdxl_SUB, // |FLIP_H| |FLIP_D sdydxl_MAJOR_X | sdy_SUB | sdxl_SUB, // |FLIP_H|FLIP_V| sdydxl_MAJOR_Y | sdy_SUB | sdxl_SUB, // |FLIP_H|FLIP_V|FLIP_D sdydxl_MAJOR_X | sdy_ADD | sdxl_ADD, // SLOPE_ONE| | | 0xffffffff, // SLOPE_ONE| | |FLIP_D sdydxl_MAJOR_X | sdy_SUB | sdxl_ADD, // SLOPE_ONE| |FLIP_V| 0xffffffff, // SLOPE_ONE| |FLIP_V|FLIP_D sdydxl_MAJOR_X | sdy_ADD | sdxl_SUB, // SLOPE_ONE|FLIP_H| | 0xffffffff, // SLOPE_ONE|FLIP_H| |FLIP_D sdydxl_MAJOR_X | sdy_SUB | sdxl_SUB, // SLOPE_ONE|FLIP_H|FLIP_V| 0xffffffff // SLOPE_ONE|FLIP_H|FLIP_V|FLIP_D }; /******************************Public*Routine******************************\ * BOOL bLinesComplex(ppdev, pptfxFirst, pptfxBuf, prun, 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 NT 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. * * Styled 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 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 (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 * 'bHardwareLine' routine for an example. * \**************************************************************************/ BOOL bLinesComplex( 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 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) { // Integer end-point lines will probably account for 90% of // all lines drawn (curves rendered by GDI will be an // exception, and will have fractional GIQ coordinates). // // As such, we try to special-case integer lines as soon as // possible. // // Unfortunately, the MGA's point-to-point line capability has // a rounding convention for rendering tie-breaker pixels that // doesn't match that of GDI's, so we can't use it for // arbitrary integer lines, otherwise we would fail HCTs. But // since horizontal and vertical lines have no tie-breakers, // and probably account for 60% of all lines drawn, we special // case them here. (The 'bHardwareLine' special case a little // lower down will handle arbitrary direction lines.) // // Note: If your hardware can't handle GIQ lines ala // 'bHardwareLine,' but can handle the correct rounding // convention, I strongly recommend special-casing // integer lines of arbitrary slope here, too. // // How can you tell if you have the correct rounding convention? // Implement integer lines (they're easy), run Guiman and draw a // bunch of Polyline lines in random directions, and then press // the 'X' xor button -- if you see random pixels lit up, you've // got the wrong convention... if (((M0 | dM | N0 | dN) & (F - 1)) == 0) { // MGA specific code follows: LONG x0; LONG y0; LONG x1; LONG y1; if (N0 == dN) { // Horizontal integer line: y0 = (N0 >> FLOG2) + ppdev->yOffset; x0 = (M0 >> FLOG2) + ppdev->xOffset; x1 = (dM >> FLOG2) + ppdev->xOffset; CHECK_FIFO_SPACE(pjBase, 2); CP_WRITE(pjBase, DWG_XYSTRT, PACKXY(x0, y0)); CP_START(pjBase, DWG_XYEND, PACKXY(x1, y0)); goto Next_Line; } else if (M0 == dM) { // Vertical integer line: x0 = (M0 >> FLOG2) + ppdev->xOffset; y0 = (N0 >> FLOG2) + ppdev->yOffset; y1 = (dN >> FLOG2) + ppdev->yOffset; CHECK_FIFO_SPACE(pjBase, 2); CP_WRITE(pjBase, DWG_XYSTRT, PACKXY(x0, y0)); CP_START(pjBase, DWG_XYEND, PACKXY(x0, y1)); goto Next_Line; } } // Check for fractional endpoint lines that are small enough // to use the hardware DDA: if (bHardwareLine(ppdev, pptfxFirst, pptfxBuf)) 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 = (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); } /******************************Public*Routine******************************\ * VOID vLinesSimple * * Historically, NT has used funky 'GIQ' coordinates that have 4 bits of * fraction, and is very picky about which pixels are lit even for lines * whose coordinates are all integers. But as of NT 4.0 Service-Pack 1, * NT now informs us when all the coordinates in a path are integers (via * the PO_ALL_INTEGERS flag), and what's more it allows us to request that * the path coordinates before returned as integers instead of the funky * 28.4 (by setting the PO_ENUM_AS_INTEGERS flag). * * But the best part is that GDI now allows us to choose which pixel we'll * light for tie-breaker cases, when the error term is exactly 0.5 (the * lines must still be last-pixel exclusive, of course). * * So we can now use the hardware's auto-Bresenham-setup (point-to-point * lines) capability! And that's exactly what we do here... * * Unfortunately, this code is complicated by the funky format for the MGA's * auto-line mechanism -- we have to send down batches composed of a 1 dword * flag, followed by 32 vertices. The dword flag indicates which of the * following vertices should be considered the disjoint from the rest (i.e., * indicates PD_BEGINSUBPATH to the hardware). * \**************************************************************************/ // !!! These defines should be retrieved from winddi.h #define PO_ALL_INTEGERS 0x00000004 #define PO_ENUM_AS_INTEGERS 0x00000008 #define VECTOR_WRITE_ON(ppdev, pjBase, ulOpmodeOut) \ { \ ulOpmodeOut = CP_READ_REGISTER(pjBase + HST_OPMODE) & OPMODE_OTHER_INFO; \ CP_WRITE_DIRECT_BYTE(pjBase, HST_OPMODE, ulOpmodeOut | (dmamod_VECTOR_WRITE)); \ CP_MEMORY_BARRIER(); \ CP_WRITE_DIRECT_BYTE(pjBase, HST_OPMODE, ulOpmodeOut | (dmamod_VECTOR_WRITE | pseudodma_ON)); \ CP_EIEIO(); \ CP_READ_REGISTER(pjBase + HST_OPMODE); \ } #define VECTOR_WRITE_OFF(ppdev, pjBase, ulOpmodeIn) \ { \ CP_WRITE_DIRECT_BYTE(pjBase, HST_OPMODE, ulOpmodeIn | (pseudodma_OFF)); \ CP_EIEIO(); \ } // To use the MGA's pseudo-DMA vector transfer, we have to do everything // in batches of one dword 'tag' followed by 32 vertices: #define VECTOR_BATCH_SIZE 32 VOID vLinesSimple( PDEV* ppdev, PATHOBJ* ppo) { BYTE* pjBase; BYTE* pjDma; ULONG* pulDma; LONG xOffset; LONG yOffset; BOOL bMore; PATHDATA pd; LONG cLines; POINTFIX* pptfx; POINTFIX ptfxStartFigure; ULONG axyBuf[VECTOR_BATCH_SIZE]; ULONG* pxy; LONG cxy; ULONG ulTag; ULONG ulOpmode; // Notify GDI that we want the path enumerated as integers instead of // fixed coordinates. Note that we can only do this when GDI has set // the PO_ALL_INTEGERS flag: ppo->fl |= PO_ENUM_AS_INTEGERS; pjBase = ppdev->pjBase; pjDma = pjBase + DMAWND; xOffset = ppdev->xOffset; yOffset = ppdev->yOffset; pxy = axyBuf; cxy = VECTOR_BATCH_SIZE; CHECK_FIFO_SPACE(pjBase, FIFOSIZE); VECTOR_WRITE_ON(ppdev, pjBase, ulOpmode); do { bMore = PATHOBJ_bEnum(ppo, &pd); cLines = pd.count; pptfx = pd.pptfx; if (pd.flags & PD_BEGINSUBPATH) { cLines--; ptfxStartFigure.x = pptfx->x; ptfxStartFigure.y = pptfx->y; *pxy = PACKXY(pptfx->x + xOffset, pptfx->y + yOffset); pxy++; pptfx++; // Set the high bit to zero, and move all the other bits down: ulTag >>= 1; if (--cxy == 0) { // Flush the buffer! pxy = axyBuf; cxy = VECTOR_BATCH_SIZE; pulDma = (ULONG*) pjDma; CHECK_FIFO_SPACE(pjBase, 1); CP_WRITE_DMA(ppdev, pulDma, ulTag); pulDma++; CHECK_FIFO_SPACE(pjBase, FIFOSIZE); do { CP_WRITE_DMA(ppdev, pulDma, *pxy); pulDma++; pxy++; } while (--cxy != 0); pxy = axyBuf; cxy = VECTOR_BATCH_SIZE; } } PolylineSegments: while (cLines-- > 0) { *pxy = PACKXY(pptfx->x + xOffset, pptfx->y + yOffset); pxy++; pptfx++; // Set the high bit to one, and move all the other bits down: ulTag = 0x80000000 | (ulTag >> 1); if (--cxy == 0) { // Flush the buffer! pxy = axyBuf; cxy = VECTOR_BATCH_SIZE; pulDma = (ULONG*) pjDma; CHECK_FIFO_SPACE(pjBase, 1); CP_WRITE_DMA(ppdev, pulDma, ulTag); pulDma++; CHECK_FIFO_SPACE(pjBase, FIFOSIZE); do { CP_WRITE_DMA(ppdev, pulDma, *pxy); pulDma++; pxy++; } while (--cxy != 0); pxy = axyBuf; cxy = VECTOR_BATCH_SIZE; } } if (pd.flags & PD_CLOSEFIGURE) { pd.flags &= ~PD_CLOSEFIGURE; pptfx = &ptfxStartFigure; goto PolylineSegments; } } while (bMore); // Flush the buffer if there's anything left: if (cxy != VECTOR_BATCH_SIZE) { ulTag >>= cxy; pxy = axyBuf; cxy = VECTOR_BATCH_SIZE - cxy; pulDma = (ULONG*) pjDma; CHECK_FIFO_SPACE(pjBase, 1); CP_WRITE_DMA(ppdev, pulDma, ulTag); pulDma++; CHECK_FIFO_SPACE(pjBase, cxy); do { CP_WRITE_DMA(ppdev, pulDma, *pxy); pulDma++; pxy++; } while (--cxy != 0); } VECTOR_WRITE_OFF(ppdev, pjBase, ulOpmode); } /******************************Public*Routine******************************\ * BOOL bHardwareLine(ppdev, pptfxStart, pptfxEnd) * * This routine is useful for folks who have line drawing hardware where * they can explicitly set the Bresenham terms -- they can use this routine * to draw fractional coordinate GIQ lines with the hardware. * * Fractional coordinate lines require an extra 4 bits of precision in the * Bresenham terms. For example, if your hardware has 13 bits of precision * for the terms, you can only draw GIQ lines up to 255 pels long using this * routine. * * Input: * pptfxStart - Points to GIQ coordinate of start of line * pptfxEnd - Points to GIQ coordinate of end of line * NUM_DDA_BITS- The number of bits of precision your hardware can support. * * Output: * returns - TRUE if the line was drawn. * FALSE if the line is too long, and the strips code must be * used. * * DDALINE: * iDir - Direction of the line, as an octant numbered as follows: * * \ 5 | 6 / * \ | / * 4 \ | / 7 * \ / * -----+----- * /|\ * 3 / | \ 0 * / | \ * / 2 | 1 \ * * ptlStart - Start pixel of line. * cPels - # of pels in line. *NOTE* You must check if this is <= 0! * dMajor - Major axis delta. * dMinor - Minor axis delta. * lErrorTerm - Error term. * * What you do with the last 3 terms may be a little tricky. They are * actually the terms for the formula of the normalized line * * dMinor * x + (lErrorTerm + dMajor) * y(x) = floor( ---------------------------------- ) * dMajor * * where y(x) is the y coordinate of the pixel to be lit as a function of * the x-coordinate. * * Every time the line advances one in the major direction 'x', dMinor * gets added to the current error term. If the resulting value is >= 0, * we know we have to move one pixel in the minor direction 'y', and * dMajor must be subtracted from the current error term. * * If you're trying to figure out what this means for your hardware, you can * think of the DDALINE terms as having been computed equivalently as * follows: * * dMinor = 2 * (minor axis delta) * dMajor = 2 * (major axis delta) * lErrorTerm = - (major axis delta) - fixup * * That is, if your documentation tells you that for integer lines, a * register is supposed to be initialized with the value * '2 * (minor axis delta)', you'll actually use dMinor. * * Example: Setting up the 8514 * * AXSTPSIGN is supposed to be the axial step constant register, defined * as 2 * (minor axis delta). You set: * * AXSTPSIGN = dMinor * * DGSTPSIGN is supposed to be the diagonal step constant register, * defined as 2 * (minor axis delta) - 2 * (major axis delta). You set: * * DGSTPSIGN = dMinor - dMajor * * ERR_TERM is supposed to be the adjusted error term, defined as * 2 * (minor axis delta) - (major axis delta) - fixup. You set: * * ERR_TERM = lErrorTerm + dMinor * * Implementation: * * You'll want to special case integer lines before calling this routine * (since they're very common, take less time to the computation of line * terms, and can handle longer lines than this routine because 4 bits * aren't being given to the fraction). * * If a GIQ line is too long to be handled by this routine, you can just * use the slower strip routines for that line. Note that you cannot * just fail the call -- you must be able to accurately draw any line * in the 28.4 device space when it intersects the viewport. * * Testing: * * Use Guiman, or some other test that draws random fractional coordinate * lines and compares them to what GDI itself draws to a bitmap. * \**************************************************************************/ BOOL bHardwareLine( PDEV* ppdev, POINTFIX* pptfxStart, // Start of line POINTFIX* pptfxEnd) // End of line { FLONG fl; // Various flags ULONG M0; // Normalized fractional unit x start coordinate (0 <= M0 < F) ULONG N0; // Normalized fractional unit y start coordinate (0 <= N0 < F) ULONG M1; // Normalized fractional unit x end coordinate (0 <= M1 < F) ULONG N1; // Normalized fractional unit x end coordinate (0 <= N1 < F) ULONG dM; // Normalized fractional unit x-delta (0 <= dM) ULONG dN; // Normalized fractional unit y-delta (0 <= dN <= dM) LONG x; // Normalized x coordinate of origin LONG y; // Normalized y coordinate of origin LONG x0; // Normalized x offset from origin to start pixel (inclusive) LONG y0; // Normalized y offset from origin to start pixel (inclusive) LONG x1; // Normalized x offset from origin to end pixel (inclusive) LONG lGamma;// Bresenham error term at origin LONG cPels; // Number of pixels in line BYTE* pjBase; /***********************************************************************\ * Normalize line to the first octant. \***********************************************************************/ fl = 0; M0 = pptfxStart->x; dM = pptfxEnd->x; if ((LONG) dM < (LONG) M0) { // Line runs from right to left, so flip across x = 0: M0 = -(LONG) M0; dM = -(LONG) dM; fl |= HW_FLIP_H; } // Compute the delta. The DDI says we can never have a valid delta // with a magnitude more than 2^31 - 1, but the engine never actually // checks its transforms. To ensure that we'll never puke on our shoes, // we check for that case and simply refuse to draw the line: dM -= M0; if ((LONG) dM < 0) return(FALSE); N0 = pptfxStart->y; dN = pptfxEnd->y; if ((LONG) dN < (LONG) N0) { // Line runs from bottom to top, so flip across y = 0: N0 = -(LONG) N0; dN = -(LONG) dN; fl |= HW_FLIP_V; } // Compute another delta: dN -= N0; if ((LONG) dN < 0) return(FALSE); if (dN >= dM) { if (dN == dM) { // Have to special case slopes of one: fl |= HW_FLIP_SLOPE_ONE; } else { // Since line has slope greater than 1, flip across x = y: register ULONG ulTmp; ulTmp = dM; dM = dN; dN = ulTmp; ulTmp = M0; M0 = N0; N0 = ulTmp; fl |= HW_FLIP_D; } } // Figure out if we can do the line in hardware, given that we have a // limited number of bits of precision for the Bresenham terms. // // Remember that one bit has to be kept as a sign bit: if ((LONG) dM >= (1L << (NUM_DDA_BITS - 1))) return(FALSE); fl |= gaflHardwareRound[fl]; /***********************************************************************\ * Calculate the error term at pixel 0. \***********************************************************************/ x = LFLOOR((LONG) M0); y = LFLOOR((LONG) N0); M0 = FXFRAC(M0); N0 = FXFRAC(N0); // NOTE NOTE NOTE: If this routine were to handle any line in the 28.4 // space, it will overflow its math (the following part requires 36 bits // of precision)! But we get here for lines that the hardware can handle // (see the expression (dM >= (1L << (NUM_DDA_BITS - 1))) above?), so if // cBits is less than 28, we're safe. // // If you're going to use this routine to handle all lines in the 28.4 // device space, you will HAVE to make sure the math doesn't overflow, // otherwise you won't be NT compliant! (See 'bHardwareLine' for an example // how to do that. You don't have to worry about this if you simply // default to the strips code for long lines, because those routines // already do the math correctly.) // Calculate the remainder term [ dM * (N0 + F/2) - M0 * dN ]. Note // that M0 and N0 have at most 4 bits of significance (and if the // arguments are properly ordered, on a 486 each multiply would be no // more than 13 cycles): lGamma = (N0 + F/2) * dM - M0 * dN; if (fl & HW_Y_ROUND_DOWN) lGamma--; lGamma >>= FLOG2; /***********************************************************************\ * 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' // when an x value of 1/2 is supposed to round up in value. // Calculate x0, x1: N1 = FXFRAC(N0 + dN); M1 = FXFRAC(M0 + dM); x1 = LFLOOR(M0 + dM); // Line runs left-to-right: // Compute x1: x1--; if (M1 > 0) { if (N1 == 0) { if (LROUND(M1, fl & HW_X_ROUND_DOWN)) x1++; } else if (abs((LONG) (N1 - F/2)) <= (LONG) M1) { x1++; } } if ((fl & (HW_FLIP_SLOPE_ONE | HW_X_ROUND_DOWN)) == (HW_FLIP_SLOPE_ONE | HW_X_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 & HW_X_ROUND_DOWN)) x0 = 1; } else if (abs((LONG) (N0 - F/2)) <= (LONG) M0) { x0 = 1; } } left_to_right_compute_y0: /***********************************************************************\ * Calculate the start pixel. \***********************************************************************/ // We now compute y0 and adjust the error term. We know x0, and we know // the current formula for the pixels to be lit on the line: // // dN * x + lGamma // y(x) = floor( --------------- ) // dM // // The remainder of this expression is the new error term at (x0, y0). // Since x0 is going to be either 0 or 1, we don't actually have to do a // multiply or divide to compute y0. Finally, we subtract dM from the // new error term so that it is in the range [-dM, 0). y0 = 0; lGamma += (dN & (-x0)); lGamma -= dM; if (lGamma >= 0) { y0 = 1; lGamma -= dM; } // Undo our flips to get the start coordinate: x += x0; y += y0; if (fl & HW_FLIP_D) { register LONG lTmp; lTmp = x; x = y; y = lTmp; } if (fl & HW_FLIP_V) { y = -y; } if (fl & HW_FLIP_H) { x = -x; } /***********************************************************************\ * Return the Bresenham terms: \***********************************************************************/ // iDir = gaiDir[fl & HW_FLIP_MASK]; // ptlStart.x = x; // ptlStart.y = y; // cPels = x1 - x0 + 1; // NOTE: You'll have to check if cPels <= 0! // dMajor = dM; // dMinor = dN; // lErrorTerm = lGamma; /***********************************************************************\ * Draw the line. MGA specific code follows: \***********************************************************************/ cPels = x1 - x0 + 1; if (cPels > 0) { pjBase = ppdev->pjBase; CHECK_FIFO_SPACE(pjBase, 9); // Turn off MGA's autolines: CP_WRITE(pjBase, DWG_DWGCTL, ppdev->ulLineControl & ~opcode_AUTO); CP_WRITE(pjBase, DWG_LEN, cPels); CP_WRITE(pjBase, DWG_SGN, gaiSign[fl & HW_FLIP_MASK]); CP_WRITE(pjBase, DWG_AR0, dN); CP_WRITE(pjBase, DWG_AR1, dN + lGamma); CP_WRITE(pjBase, DWG_AR2, dN - dM); CP_WRITE(pjBase, DWG_XDST, x + ppdev->xOffset); CP_START(pjBase, DWG_YDST, y + ppdev->yOffset); // Turn MGA's autolines back on, because strip code and horizontal // and vertical line special cases expect it to be on: CP_WRITE(pjBase, DWG_DWGCTL, ppdev->ulLineControl); } return(TRUE); } /*******************************Public*Table*******************************\ * gapfnStrip * * Look-up table for DrvStrokePath to find which strip routines to call. * \**************************************************************************/ 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; FLONG fl; PDEV* ppdev; DSURF* pdsurf; OH* poh; RECTL arclClip[4]; // For rectangular clipping BYTE* pjBase; RECTL* prclClip; ULONG ulLinePattern; ULONG ulHwMix; PATHDATA pd; BOOL bMore; LONG cptfx; POINTFIX ptfxStartFigure; POINTFIX ptfxLast; POINTFIX* pptfxFirst; POINTFIX* pptfxBuf; RECTFX rcfxBounds; CLIPENUM ce; LONG i; 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; ////////////////////////////////////////////////////////////////////// // MGA specific initialization: pjBase = ppdev->pjBase; CHECK_FIFO_SPACE(pjBase, 6); if (mix == 0x0d0d) // R2_COPYPEN { ppdev->ulLineControl = (blockm_OFF + pattern_OFF + bltmod_BFCOL + transc_BG_TRANSP + atype_RPL + opcode_AUTOLINE_OPEN + bop_SRCCOPY + solid_SOLID); } else { ulHwMix = (mix & 0xff) - 1; ppdev->ulLineControl = (blockm_OFF + pattern_OFF + bltmod_BFCOL + transc_BG_TRANSP + atype_RSTR + opcode_AUTOLINE_OPEN + solid_SOLID + (ulHwMix << 16)); } CP_WRITE(pjBase, DWG_DWGCTL, ppdev->ulLineControl); CP_WRITE(pjBase, DWG_FCOL, COLOR_REPLICATE(ppdev, pbo->iSolidColor)); if ((ppdev->ulBoardId != MGA_STORM) && !(GET_CACHE_FLAGS(ppdev, PATTERN_CACHE))) { CP_WRITE(pjBase, DWG_SRC0, 0xFFFFFFFF); CP_WRITE(pjBase, DWG_SRC1, 0xFFFFFFFF); CP_WRITE(pjBase, DWG_SRC2, 0xFFFFFFFF); CP_WRITE(pjBase, DWG_SRC3, 0xFFFFFFFF); } ppdev->HopeFlags = PATTERN_CACHE; ////////////////////////////////////////////////////////////////////// // Special case solid integer lines: if ((ppo->fl & PO_ALL_INTEGERS) && !(pla->fl & LA_STYLED)) { // As of NT 4.0 SP2, the rasterization convention has been loosened // for paths that have the PO_ALL_INTEGERS flag set. For those paths, // you may do the 'tie-breaker' pixels anyway you like, which usually // means that you can use the point-to-point line drawing capabilities // of the hardware. // // HOWEVER: Your implementation must be invariant, regardless of clipping! // This means that if you choose to deviate from the standard NT // line convention, you MUST draw the same thing for DC_TRIVIAL, // DC_RECT, *and* DC_COMPLEX clipping. if (pco->iDComplexity == DC_TRIVIAL) { vLinesSimple(ppdev, ppo); return(TRUE); } else { // We have to make sure that the path coordinates would not // overflow our hardware precision! Note that the path's // bounds are still expressed as 28.4 coordinates even if // PO_ALL_INTEGERS or PO_ENUM_AS_INTEGERS is set: PATHOBJ_vGetBounds(ppo, &rcfxBounds); if ((rcfxBounds.xLeft >= 16 * MIN_INTEGER_BOUND) && (rcfxBounds.yTop >= 16 * MIN_INTEGER_BOUND) && (rcfxBounds.xRight <= 16 * MAX_INTEGER_BOUND) && (rcfxBounds.yBottom <= 16 * MAX_INTEGER_BOUND)) { // NOTE that if you deviate from the standard NT line // convention for PO_ALL_INTEGERS flags, you must support // all clipping types! if (pco->iDComplexity == DC_RECT) { vSetClipping(ppdev, &pco->rclBounds); vLinesSimple(ppdev, ppo); } else { CLIPOBJ_cEnumStart(pco, FALSE, CT_RECTANGLES, CD_RIGHTDOWN, 0); do { // Get a batch of region rectangles: bMore = CLIPOBJ_bEnum(pco, sizeof(ce), (VOID*) &ce); for (i = 0; i < ce.c; i++) { vSetClipping(ppdev, &ce.arcl[i]); vLinesSimple(ppdev, ppo); } } while (bMore); } vResetClipping(ppdev); return(TRUE); } } } prclClip = NULL; fl = 0; ulLinePattern = (ULONG) -1; // 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)]; ////////////////////////////////////////////////////////////////////// // Set up to enumerate the path: if (pco->iDComplexity != DC_COMPLEX) { 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 (!bLinesComplex(ppdev, pptfxFirst, pptfxBuf, (RUN*) NULL, cptfx, &ls, prclClip, apfn, fl)) return(FALSE); } ptfxLast = pd.pptfx[pd.count - 1]; if (pd.flags & PD_CLOSEFIGURE) { if (!bLinesComplex(ppdev, &ptfxLast, &ptfxStartFigure, (RUN*) NULL, 1, &ls, prclClip, apfn, fl)) 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 (!bLinesComplex(ppdev, &cl.cl.ptfxA, &cl.cl.ptfxB, &cl.cl.arun[0], cl.cl.c, &ls, (RECTL*) NULL, apfn, fl)) return(FALSE); } } while (bMore); } return(TRUE); }