/******************************Module*Header**********************************\ * * ******************* * * GDI SAMPLE CODE * * ******************* * * Module Name: fastfill.c * * Draws fast solid-coloured, unclipped, non-complex rectangles. * * Copyright (c) 1994-1998 3Dlabs Inc. Ltd. All rights reserved. * Copyright (c) 1995-1999 Microsoft Corporation. All rights reserved. \*****************************************************************************/ #include "precomp.h" #include "gdi.h" //----------------------------------------------------------------------------- // // BOOL bFillPolygon() // // Draws a non-complex, unclipped polygon. 'Non-complex' is defined as // having only two edges that are monotonic increasing in 'y'. That is, // the polygon cannot have more than one disconnected segment on any given // scan. Note that the edges of the polygon can self-intersect, so hourglass // shapes are permissible. This restriction permits this routine to run two // simultaneous DDAs(Digital Differential Analyzer), and no sorting of the // edges is required. // // Note that NT's fill convention is different from that of Win 3.1 or 4.0. // With the additional complication of fractional end-points, our convention // is the same as in 'X-Windows'. // // This routine handles patterns only when the Permedia2 area stipple can be // used. The reason for this is that once the stipple initialization is // done, pattern fills appear to the programmer exactly the same as solid // fills (with the slight difference of an extra bit in the render command). // // We break each polygon down to a sequenze of screen aligned trapeziods, which // the Permedia2 can handle. // // Optimisation list follows .... // // This routine is in no way the ultimate convex polygon drawing routine // Some obvious things that would make it faster: // // 1) Write it in Assembler // // 2) Make the non-complex polygon detection faster. If I could have // modified memory before the start of after the end of the buffer, // I could have simplified the detection code. But since I expect // this buffer to come from GDI, I can't do that. Another thing // would be to have GDI give a flag on calls that are guaranteed // to be convex, such as 'Ellipses' and 'RoundRects'. Note that // the buffer would still have to be scanned to find the top-most // point. // // 3) Implement support for a single sub-path that spans multiple // path data records, so that we don't have to copy all the points // to a single buffer like we do in 'fillpath.c'. // // 4) Use 'ebp' and/or 'esp' as a general register in the inner loops // of the Asm loops, and also Pentium-optimize the code. It's safe // to use 'esp' on NT because it's guaranteed that no interrupts // will be taken in our thread context, and nobody else looks at the // stack pointer from our context. // // 5) When we get to a part of the polygon where both vertices are of // equal height, the algorithm essentially starts the polygon again. // Using the Permedia2 Continue message could speed things up in certain // cases. // // Returns TRUE if the polygon was drawn; FALSE if the polygon was complex. // // Note: the point data (POINTFX) GDI passed to us in 28.4 format. Permedia 2 // hardware uses 12.15 format. So most of the time, we need to do a // x = (x + 15) >> 4 to bring it back to normal interger format and then // convert it to 12.15 format when we set the register value // // Parameters: // ppdev-------Pointer to PDev // pSurfDst----Destination surface // lEdges------Number of edges, includes close figure edge // pptfxFirst--Pointer to the first point in the data buffer. There are total // "lEdges" points // iSolidColor-Solid color fill // ulRop4------ROP4 // pco---------Clip Object. // prb---------Realized brush // pptlBrush---Pattern alignment // //----------------------------------------------------------------------------- BOOL bFillPolygon(PDev* ppdev, Surf* pSurfDst, LONG lEdges, POINTFIX* pptfxFirst, ULONG ulSolidColor, ULONG ulRop4, CLIPOBJ* pco, RBrush* prb, POINTL* pptlBrush) { POINTFIX* pptfxLast; // Points to the last point in the polygon // array POINTFIX* pptfxTop; // Points to the top-most point in the polygon POINTFIX* pptfxScan; // Current edge pointer for finding pptfxTop POINTFIX* aPtrFixTop[2]; // DDA terms and stuff POINTFIX* aPtrFixNext[2]; // DDA terms and stuff BOOL bRC = FALSE; // Return code for this function BOOL bSingleColor; // Only one color pass BOOL bTrivialClip; // Trivial Clip or not ClipEnum* pClipRegion = (ClipEnum*)(ppdev->pvTmpBuffer); // Buffer for storing clipping region DWORD dwAsMode[2]; // The area stipple mode and the color for that // pass DWORD dwColorMode; // Current color mode DWORD dwColorReg; // Current color register mode DWORD dwLogicMode; // Current logic op mode DWORD dwReadMode; // Current register read mode DWORD dwRenderBits; // Current render bits LONG lCount; // Number of scan lines to render LONG alDX[2]; // LONG alDY[2]; LONG lNumOfPass; // Number of passes required to render LONG lScanEdges; // Number of edges scanned to find pptfxTop // (doesn't include the closefigure edge) LONG alDxDy[2]; RECTL* pClipList; // List of clip rects ULONG ulBgColor; // Background color ULONG ulBgLogicOp = ulRop3ToLogicop(ulRop4 >> 8); ULONG ulBrushColor = ulSolidColor; // Current fill color ULONG ulColor[2]; // On multiple color passes we need to know how // to set up ULONG ulFgColor; // Foreground color ULONG ulFgLogicOp = ulRop3ToLogicop(ulRop4 & 0xFF); ULONG ulOrX; // We do logic OR for all values to eliminate ULONG ulOrY; // complex polygons GFNPB pb; // Functional block for lower level function ULONG* pBuffer; PERMEDIA_DECL; pb.ppdev = ppdev; DBG_GDI((6, "bFillPolygon called, rop4 = %x, fg ulFgLogicOp =%d, bg = %d", ulRop4, ulFgLogicOp, ulBgLogicOp)); ASSERTDD(lEdges > 1, "Polygon with less than 2 edges"); // // See if the polygon is 'non-complex' // Assume for now that the first point in path is the top-most // pptfxScan = pptfxFirst; pptfxTop = pptfxFirst; pptfxLast = pptfxFirst + lEdges - 1; // // Initialize our logic OR op counters // ulOrX = pptfxScan->x; ulOrY = pptfxScan->y; // // 'pptfxScan' will always point to the first point in the current // edge, and 'lScanEdges' will be the number of edges remaining, including // the current one, but not counting close figure // lScanEdges = lEdges - 1; // // First phase: Velidate input point data to see if we can handle it or not // // Check if the 2nd edge point is lower than current edge point // // Note: the (0,0) is at the up-left corner in this coordinate system // So the bigger the Y value, the lower the point // if ( (pptfxScan + 1)->y > pptfxScan->y ) { // // The edge goes down, that is, the 2nd point is lower than the 1st // point. Collect all downs: that is, collect all the X and Y until // the edge goes up // do { ulOrY |= (++pptfxScan)->y; ulOrX |= pptfxScan->x; // // If no more edge left, we are done // if ( --lScanEdges == 0 ) { goto SetUpForFilling; } } while ( (pptfxScan + 1)->y >= pptfxScan->y ); // // From this point, the edge goes up, that is, the next point is higher // than current point // Collect all ups: Collect all the X and Y until the edge goes down // do { ulOrY |= (++pptfxScan)->y; ulOrX |= pptfxScan->x; // // If no more edge left, we are done // if ( --lScanEdges == 0 ) { goto SetUpForFillingCheck; } } while ( (pptfxScan + 1)->y <= pptfxScan->y ); // // Reset pptfxTop to the current point which is at top again compare // with the next point // Collect all downs: // pptfxTop = pptfxScan; do { // // If the next edge point is lower than the 1st point, stop // if ( (pptfxScan + 1)->y > pptfxFirst->y ) { break; } ulOrY |= (++pptfxScan)->y; ulOrX |= pptfxScan->x; // // If no more edge left, we are done // if ( --lScanEdges == 0 ) { goto SetUpForFilling; } } while ( (pptfxScan + 1)->y >= pptfxScan->y ); // // If we fallen here, it means we are given down-up-down polygon. // We can't handle it and return FALSE to let GDI do it. // DBG_GDI((7, "Reject: can't fill down-up-down polygon")); goto ReturnBack; }// if ( (pptfxScan + 1)->y>pptfxScan->y ), 2nd point is lower than 1st one else { // // The edge goes up, that is, the 2nd point is higher than the 1st // point. Collect all ups: that is, collect all the X and Y until // the edge goes down. // Note: we keeps changing the value of "pptfxTop" so that after // this "while" loop, "pptfxTop" points to the TOPEST point // do { ulOrY |= (++pptfxTop)->y; // We increment this now because we ulOrX |= pptfxTop->x; // want it to point to the very last // // If no more edge left, we are done // if ( --lScanEdges == 0 ) { goto SetUpForFilling; } } while ( (pptfxTop + 1)->y <= pptfxTop->y ); // // Form this point, the edge goes down, that is, the next point is // lower than current point. Collect all downs: that is, collect all // the X and Y until the edge goes up // Note: here we keep changing "pptfxScan" so that after this loop, // "pptfxScan" points to the current scan line, which also is the // lowest point // pptfxScan = pptfxTop; do { ulOrY |= (++pptfxScan)->y; ulOrX |= pptfxScan->x; // // If no more edge left, we are done // if ( --lScanEdges == 0 ) { goto SetUpForFilling; } } while ( (pptfxScan + 1)->y >= pptfxScan->y ); // // Up to this point, the edge is about to go up again. // Collect all ups: // do { // // If the edge going down again, just qute because we can't // fill up-down-up polygon // if ( (pptfxScan + 1)->y < pptfxFirst->y ) { break; } ulOrY |= (++pptfxScan)->y; ulOrX |= pptfxScan->x; // // If no more edge left, we are done // if ( --lScanEdges == 0 ) { goto SetUpForFilling; } } while ( (pptfxScan + 1)->y <= pptfxScan->y ); // // If we fallen here, it means we are given up-down-up polygon. // We can't handle it and return FALSE to let GDI do it. // DBG_GDI((7, "Reject: Can't fill up-down-up polygon")); goto ReturnBack; }// if (pptfxScan + 1)->y<=pptfxScan->y), 2nd point is higher than 1st one // // Phase 2: Now we have validated the input point and think we can fill it // SetUpForFillingCheck: // // We check to see if the end of the current edge is higher than the top // edge we've found so far. If yes, then let pptfxTop point to the end of // current edge which is the highest. // // if ( pptfxScan->y < pptfxTop->y ) { pptfxTop = pptfxScan; } SetUpForFilling: // // Can only use block fills for trivial clip so work it out here // bTrivialClip = (pco == NULL) || (pco->iDComplexity == DC_TRIVIAL); if ( (ulOrY & 0xffffc00f) || (ulOrX & 0xffff8000) ) { ULONG ulNeg; ULONG ulPosX; ULONG ulPosY; // // Fractional Y must be done as spans // if ( ulOrY & 0xf ) { bRC = bFillSpans(ppdev, pSurfDst, lEdges, pptfxFirst, pptfxTop, pptfxLast, ulSolidColor, ulRop4, pco, prb, pptlBrush); goto ReturnBack; } // // Run through all the vertices and check that none of them // have a negative component less than -256. // ulNeg = 0; ulPosX = 0; ulPosY = 0; for ( pptfxScan = pptfxFirst; pptfxScan <= pptfxLast; ++pptfxScan ) { if ( pptfxScan->x < 0 ) { ulNeg |= -pptfxScan->x; } else { ulPosX |= pptfxScan->x; } if ( pptfxScan->y < 0 ) { ulNeg |= -pptfxScan->y; } else { ulPosY |= pptfxScan->y; } } // // We don't want to handle any polygon with a negative vertex // at <= -256 in either coordinate. // if ( ulNeg & 0xfffff000 ) { DBG_GDI((1, "Coords out of range for fast fill")); goto ReturnBack; } if ( (ulPosX > 2047) || (ulPosY > 1023) ) { DBG_GDI((1, "Coords out of range for Permedia2 fast fill")); goto ReturnBack; } }// if ( (ulOrY & 0xffffc00f) || (ulOrX & 0xffff8000) ) // // Now we are ready to fill // InputBufferReserve(ppdev, 2, &pBuffer); pBuffer[0] = __Permedia2TagFBWindowBase; pBuffer[1] = pSurfDst->ulPixOffset; pBuffer += 2; InputBufferCommit(ppdev, pBuffer); DBG_GDI((7, "bFillPolygon: Polygon is renderable. Go ahead and render")); if ( ulFgLogicOp == K_LOGICOP_COPY ) { dwColorMode = __PERMEDIA_DISABLE; dwLogicMode = __PERMEDIA_CONSTANT_FB_WRITE; dwReadMode = PM_FBREADMODE_PARTIAL(pSurfDst->ulPackedPP) | PM_FBREADMODE_PACKEDDATA(__PERMEDIA_DISABLE); // // Check to see if it is a non-solid fill brush fill // if ( (ulBrushColor == 0xffffffff) ||(!bTrivialClip) ) { // // Non-solid brush, not too much we can do // dwRenderBits = __RENDER_TRAPEZOID_PRIMITIVE; dwColorReg = __Permedia2TagFBWriteData; }// Non-solid brush else { // // For solid brush, We can use fast fills, so load the fb block // color register. // dwColorReg = __Permedia2TagFBBlockColor; dwRenderBits = __RENDER_FAST_FILL_ENABLE | __RENDER_TRAPEZOID_PRIMITIVE; // // Setup color data based on current color mode we are in // if ( ppdev->cPelSize == 1 ) { // // We are in 16 bit packed mode. So the color data must be // repeated in both halves of the FBBlockColor register // ASSERTDD((ulSolidColor & 0xFFFF0000) == 0, "bFillPolygon: upper bits are not zero"); ulSolidColor |= (ulSolidColor << 16); } else if ( ppdev->cPelSize == 0 ) { // // We are in 8 bit packed mode. So the color data must be // repeated in all 4 bytes of the FBBlockColor register // ASSERTDD((ulSolidColor & 0xFFFFFF00) == 0, "bFillPolygon: upper bits are not zero"); ulSolidColor |= ulSolidColor << 8; ulSolidColor |= ulSolidColor << 16; } // // Ensure that the last access was a write before loading // BlockColor // InputBufferReserve(ppdev, 2, &pBuffer); pBuffer[0] = __Permedia2TagFBBlockColor; pBuffer[1] = ulSolidColor; pBuffer += 2; InputBufferCommit(ppdev, pBuffer); }// Solid brush case }// LOGICOP_COPY else { dwColorReg = __Permedia2TagConstantColor; dwColorMode = __COLOR_DDA_FLAT_SHADE; dwLogicMode = P2_ENABLED_LOGICALOP(ulFgLogicOp); dwReadMode = PM_FBREADMODE_PARTIAL(pSurfDst->ulPackedPP) | LogicopReadDest[ulFgLogicOp]; dwRenderBits = __RENDER_TRAPEZOID_PRIMITIVE; }// Non-COPY LogicOP // // Determine how many passes we need to draw all the clip rects // if ( bTrivialClip ) { // // Just draw, no clipping to perform. // pClipList = NULL; // Indicate no clip list lNumOfPass = 1; } else { if ( pco->iDComplexity == DC_RECT ) { // // For DC_RECT, we can do it in one pass // lNumOfPass = 1; pClipList = &pco->rclBounds; } else { // // It may be slow to render the entire polygon for each clip rect, // especially if the object is very complex. An arbitary limit of // up to CLIP_LIMIT regions will be rendered by this function. // Return false if more than CLIP_LIMIT regions. // lNumOfPass = CLIPOBJ_cEnumStart(pco, FALSE, CT_RECTANGLES, CD_ANY, CLIP_LIMIT); if ( lNumOfPass == -1 ) { goto ReturnBack; // More than CLIP_LIMIT. } // // Put the regions into our clip buffer // if ( (CLIPOBJ_bEnum(pco, sizeof(ClipEnum), (ULONG*)pClipRegion)) ||(pClipRegion->c != lNumOfPass) ) { DBG_GDI((7, "CLIPOBJ_bEnum inconsistency. %d = %d", pClipRegion->c, lNumOfPass)); } pClipList = &(pClipRegion->arcl[0]); }// Non-DC_RECT case // // For non-trivial clipping, we can use SCISSOR to implement it // InputBufferReserve(ppdev, 2, &pBuffer); pBuffer[0] = __Permedia2TagScissorMode; pBuffer[1] = SCREEN_SCISSOR_DEFAULT | USER_SCISSOR_ENABLE; pBuffer += 2; InputBufferCommit(ppdev, pBuffer); }// Non-trivial clipping bSingleColor = TRUE; if ( ulBrushColor != 0xFFFFFFFF ) { // // Solid brush case, just set the color register as the color // InputBufferReserve(ppdev, 2, &pBuffer); pBuffer[0] = dwColorReg; pBuffer[1] = ulSolidColor; pBuffer += 2; InputBufferCommit(ppdev, pBuffer); }// Solid brush case else { // // For non-solid brush, we need to realize brush first // BrushEntry* pbe; // // Turn on the area stipple. // dwRenderBits |= __RENDER_AREA_STIPPLE_ENABLE; // // If anything has changed with the brush we must re-realize it. If the // brush has been kicked out of the area stipple unit we must fully // realize it. If only the alignment has changed we can simply update // the alignment for the stipple. // pbe = prb->pbe; pb.prbrush = prb; pb.pptlBrush = pptlBrush; if ( (pbe == NULL) || (pbe->prbVerify != prb) ) { DBG_GDI((7, "full brush realize")); vPatRealize(&pb); } else if ( (prb->ptlBrushOrg.x != pptlBrush->x) ||(prb->ptlBrushOrg.y != pptlBrush->y) ) { DBG_GDI((7, "changing brush offset")); vMonoOffset(&pb); } ulFgColor = prb->ulForeColor; ulBgColor = prb->ulBackColor; if ( (ulBgLogicOp == K_LOGICOP_NOOP) ||((ulFgLogicOp == K_LOGICOP_XOR) && (ulBgColor == 0)) ) { // // Either we have a transparent bitmap or it can be assumed to be // transparent (XOR with bg=0) // InputBufferReserve(ppdev, 4, &pBuffer); pBuffer[0] = dwColorReg; pBuffer[1] = ulFgColor; pBuffer[2] = __Permedia2TagAreaStippleMode; pBuffer[3] = prb->areaStippleMode; pBuffer += 4; InputBufferCommit(ppdev, pBuffer); }// Transparent bitmap else if ( (ulFgLogicOp == K_LOGICOP_XOR) && (ulFgColor == 0) ) { // // We have a transparent foreground! (XOR with fg=0) // InputBufferReserve(ppdev, 4, &pBuffer); pBuffer[0] = dwColorReg; pBuffer[1] = ulBgColor; pBuffer[2] = __Permedia2TagAreaStippleMode; pBuffer[3] = prb->areaStippleMode | AREA_STIPPLE_INVERT_PAT; pBuffer += 4; InputBufferCommit(ppdev, pBuffer); }// Transparent foreground else { // // Not using a transparent pattern // bSingleColor = FALSE; ulColor[0] = ulFgColor; ulColor[1] = ulBgColor; dwAsMode[0] = prb->areaStippleMode; dwAsMode[1] = dwAsMode[0] | AREA_STIPPLE_INVERT_PAT; // // Double the number of passes, one for fg one for bg // lNumOfPass <<= 1; }// No transparent }// if ( ulBrushColor == 0xFFFFFFFF ), non-solid brush InputBufferReserve(ppdev, 6, &pBuffer); pBuffer[0] = __Permedia2TagColorDDAMode; pBuffer[1] = dwColorMode; pBuffer[2] = __Permedia2TagFBReadMode; pBuffer[3] = dwReadMode; pBuffer[4] = __Permedia2TagLogicalOpMode; pBuffer[5] = dwLogicMode; pBuffer += 6; InputBufferCommit(ppdev, pBuffer); DBG_GDI((7, "Rendering Polygon in %d passes. with %s", lNumOfPass, bSingleColor ? "Single Color" : "Two Color")); lNumOfPass--; while ( 1 ) { // // Per pass initialization // if ( bSingleColor ) { // // Need to set up clip rect each pass // if ( pClipList ) { InputBufferReserve(ppdev, 4, &pBuffer); pBuffer[0] = __Permedia2TagScissorMinXY; pBuffer[1] = ((pClipList->left)<< SCISSOR_XOFFSET) | ((pClipList->top)<< SCISSOR_YOFFSET); pBuffer[2] = __Permedia2TagScissorMaxXY; pBuffer[3] = ((pClipList->right)<< SCISSOR_XOFFSET) | ((pClipList->bottom)<< SCISSOR_YOFFSET); pBuffer += 4; InputBufferCommit(ppdev, pBuffer); pClipList++; } }// Single color else { // // Need to set up clip rect every other pass and change color and // inversion mode every pass // if ( (pClipList) && (lNumOfPass & 1) ) { InputBufferReserve(ppdev, 4, &pBuffer); pBuffer[0] = __Permedia2TagScissorMinXY; pBuffer[1] = ((pClipList->left)<< SCISSOR_XOFFSET) | ((pClipList->top)<< SCISSOR_YOFFSET); pBuffer[2] = __Permedia2TagScissorMaxXY; pBuffer[3] = ((pClipList->right)<< SCISSOR_XOFFSET) | ((pClipList->bottom)<< SCISSOR_YOFFSET); pBuffer += 4; InputBufferCommit(ppdev, pBuffer); pClipList++; } InputBufferReserve(ppdev, 4, &pBuffer); pBuffer[0] = dwColorReg; pBuffer[1] = ulColor[lNumOfPass & 1]; pBuffer[2] = __Permedia2TagAreaStippleMode; pBuffer[3] = dwAsMode[lNumOfPass & 1]; pBuffer += 4; InputBufferCommit(ppdev, pBuffer); }// Non-single color mode // // Initialize left and right points (current) to top point. // aPtrFixNext[LEFT] = pptfxTop; aPtrFixNext[RIGHT] = pptfxTop; while ( 1 ) { // // aPtrFixNext[] is always the valid point to draw from // do { aPtrFixTop[LEFT] = aPtrFixNext[LEFT]; aPtrFixNext[LEFT] = aPtrFixTop[LEFT] - 1; if ( aPtrFixNext[LEFT] < pptfxFirst ) { aPtrFixNext[LEFT] = pptfxLast; } // // Special case of flat based polygon, need to break now as // polygon is finished // if ( aPtrFixNext[LEFT] == aPtrFixNext[RIGHT] ) { goto FinishedPolygon; } DBG_GDI((7, "LEFT: aPtrFixTop %x aPtrFixNext %x", aPtrFixTop[LEFT], aPtrFixNext[LEFT])); DBG_GDI((7, "FIRST %x LAST %x", pptfxFirst, pptfxLast)); DBG_GDI((7, "X %x Y %x Next: X %x Y %x", aPtrFixTop[LEFT]->x, aPtrFixTop[LEFT]->y, aPtrFixNext[LEFT]->x, aPtrFixNext[LEFT]->y)); } while ( aPtrFixTop[LEFT]->y == aPtrFixNext[LEFT]->y ); do { aPtrFixTop[RIGHT] = aPtrFixNext[RIGHT]; aPtrFixNext[RIGHT] = aPtrFixTop[RIGHT] + 1; if ( aPtrFixNext[RIGHT] > pptfxLast ) { aPtrFixNext[RIGHT] = pptfxFirst; } DBG_GDI((7, "RIGHT: aPtrFixTop %x aPtrFixNext %x FIRST %x", aPtrFixTop[RIGHT], aPtrFixNext[RIGHT], pptfxFirst)); DBG_GDI((7, " LAST %x X %x Y %x Next: X %x Y %x", pptfxLast, aPtrFixTop[RIGHT]->x, aPtrFixTop[RIGHT]->y, aPtrFixNext[RIGHT]->x, aPtrFixNext[RIGHT]->y)); } while ( aPtrFixTop[RIGHT]->y == aPtrFixNext[RIGHT]->y ); // // Start up new rectangle. Whenever we get to this code, both // points should have equal y values, and need to be restarted. // Note: To get correct results, we need to add on nearly one to // each X coordinate. // DBG_GDI((7, "New: Top: x: %x y: %x x: %x y: %x", aPtrFixTop[LEFT]->x, aPtrFixTop[LEFT]->y, aPtrFixTop[RIGHT]->x, aPtrFixTop[RIGHT]->y)); DBG_GDI((7, " Next: x: %x y: %x x: %x y: %x", aPtrFixNext[LEFT]->x, aPtrFixNext[LEFT]->y, aPtrFixNext[RIGHT]->x, aPtrFixNext[RIGHT]->y)); InputBufferReserve(ppdev, 6, &pBuffer); pBuffer[0] = __Permedia2TagStartXDom; pBuffer[1] = FIXtoFIXED(aPtrFixTop[LEFT]->x) + NEARLY_ONE; pBuffer[2] = __Permedia2TagStartXSub; pBuffer[3] = FIXtoFIXED(aPtrFixTop[RIGHT]->x)+ NEARLY_ONE; pBuffer[4] = __Permedia2TagStartY; pBuffer[5] = FIXtoFIXED(aPtrFixTop[RIGHT]->y); pBuffer += 6; InputBufferCommit(ppdev, pBuffer); // // We have 2 15.4 coordinates. We need to divide them and change // them into a 15.16 coordinate. We know the y coordinate is not // fractional, so we do not loose precision by shifting right by 4 // alDX[LEFT] = (aPtrFixNext[LEFT]->x - aPtrFixTop[LEFT]->x) << 12; alDY[LEFT] = (aPtrFixNext[LEFT]->y - aPtrFixTop[LEFT]->y) >> 4; // // Need to ensure we round delta down. divide rounds towards zero // if ( alDX[LEFT] < 0 ) { alDX[LEFT] -= alDY[LEFT] - 1; } alDxDy[LEFT] = alDX[LEFT] / alDY[LEFT]; InputBufferReserve(ppdev, 8, &pBuffer); pBuffer[0] = __Permedia2TagdXDom; pBuffer[1] = alDxDy[LEFT]; alDX[RIGHT] = (aPtrFixNext[RIGHT]->x - aPtrFixTop[RIGHT]->x) << 12; alDY[RIGHT] = (aPtrFixNext[RIGHT]->y - aPtrFixTop[RIGHT]->y) >> 4; // // Need to ensure we round delta down. divide rounds towards zero // if ( alDX[RIGHT] < 0 ) { alDX[RIGHT] -= alDY[RIGHT] - 1; } alDxDy[RIGHT] = alDX[RIGHT] / alDY[RIGHT]; pBuffer[2] = __Permedia2TagdXSub; pBuffer[3] = alDxDy[RIGHT]; // // Work out number of scanlines to render // if ( aPtrFixNext[LEFT]->y < aPtrFixNext[RIGHT]->y ) { lCount = alDY[LEFT]; } else { lCount = alDY[RIGHT]; } pBuffer[4] = __Permedia2TagCount; pBuffer[5] = lCount; pBuffer[6] = __Permedia2TagRender; pBuffer[7] = dwRenderBits; pBuffer += 8; InputBufferCommit(ppdev, pBuffer); // // With lots of luck, top trapezoid should be drawn now! // Repeatedly draw more trapezoids until points are equal // If y values are equal, then we can start again from // scratch. // while ( (aPtrFixNext[LEFT] != aPtrFixNext[RIGHT]) &&(aPtrFixNext[LEFT]->y != aPtrFixNext[RIGHT]->y) ) { // // Some continues are required for next rectangle // if ( aPtrFixNext[LEFT]->y < aPtrFixNext[RIGHT]->y ) { // // We have reached aPtrFixNext[LEFT]. aPtrFixNext[RIGHT] // is still ok // do { aPtrFixTop[LEFT] = aPtrFixNext[LEFT]; aPtrFixNext[LEFT] = aPtrFixTop[LEFT] - 1; if ( aPtrFixNext[LEFT] < pptfxFirst ) { aPtrFixNext[LEFT] = pptfxLast; } } while ( aPtrFixTop[LEFT]->y == aPtrFixNext[LEFT]->y ); // // We have a new aPtrFixNext[LEFT] now. // DBG_GDI((7, "Dom: Top: x: %x y: %x", aPtrFixTop[LEFT]->x, aPtrFixTop[LEFT]->y)); DBG_GDI((7, "Next: x: %x y: %x x: %x y: %x", aPtrFixNext[LEFT]->x, aPtrFixNext[LEFT]->y, aPtrFixNext[RIGHT]->x, aPtrFixNext[RIGHT]->y)); alDX[LEFT] = (aPtrFixNext[LEFT]->x - aPtrFixTop[LEFT]->x) << 12; alDY[LEFT] = (aPtrFixNext[LEFT]->y - aPtrFixTop[LEFT]->y) >> 4; // // Need to ensure we round delta down. Divide rounds // towards zero // if ( alDX[LEFT] < 0 ) { alDX[LEFT] -= alDY[LEFT] - 1; } alDxDy[LEFT] = alDX[LEFT] / alDY[LEFT]; if ( aPtrFixNext[LEFT]->y < aPtrFixNext[RIGHT]->y ) { lCount = alDY[LEFT]; } else { lCount = (abs(aPtrFixNext[RIGHT]->y - aPtrFixTop[LEFT]->y)) >> 4; } InputBufferReserve(ppdev, 6, &pBuffer); pBuffer[0] = __Permedia2TagStartXDom; pBuffer[1] = FIXtoFIXED(aPtrFixTop[LEFT]->x) + NEARLY_ONE; pBuffer[2] = __Permedia2TagdXDom; pBuffer[3] = alDxDy[LEFT]; pBuffer[4] = __Permedia2TagContinueNewDom; pBuffer[5] = lCount; pBuffer += 6; InputBufferCommit(ppdev, pBuffer); }// if ( aPtrFixNext[LEFT]->y < aPtrFixNext[RIGHT]->y ) else { // // We have reached aPtrFixNext[RIGHT]. aPtrFixNext[LEFT] // is still ok // do { aPtrFixTop[RIGHT] = aPtrFixNext[RIGHT]; aPtrFixNext[RIGHT] = aPtrFixTop[RIGHT] + 1; if ( aPtrFixNext[RIGHT] > pptfxLast ) { aPtrFixNext[RIGHT] = pptfxFirst; } } while ( aPtrFixTop[RIGHT]->y == aPtrFixNext[RIGHT]->y ); // // We have a new aPtrFixNext[RIGHT] now. // DBG_GDI((7, "Sub: Top: x: %x y: %x", aPtrFixTop[RIGHT]->x, aPtrFixTop[RIGHT]->y)); DBG_GDI((7, "Next: x: %x y: %x x: %x y: %x", aPtrFixNext[LEFT]->x, aPtrFixNext[LEFT]->y, aPtrFixNext[RIGHT]->x, aPtrFixNext[RIGHT]->y)); alDX[RIGHT] = (aPtrFixNext[RIGHT]->x - aPtrFixTop[RIGHT]->x) << 12; alDY[RIGHT] = (aPtrFixNext[RIGHT]->y - aPtrFixTop[RIGHT]->y) >> 4; // // Need to ensure we round delta down. divide rounds // towards zero // if ( alDX[RIGHT] < 0 ) { alDX[RIGHT] -= alDY[RIGHT] - 1; } alDxDy[RIGHT] = alDX[RIGHT] / alDY[RIGHT]; if ( aPtrFixNext[RIGHT]->y < aPtrFixNext[LEFT]->y ) { lCount = alDY[RIGHT]; } else { lCount = (abs(aPtrFixNext[LEFT]->y - aPtrFixTop[RIGHT]->y)) >> 4; } InputBufferReserve(ppdev, 6, &pBuffer); pBuffer[0] = __Permedia2TagStartXSub; pBuffer[1] = FIXtoFIXED(aPtrFixTop[RIGHT]->x) + NEARLY_ONE; pBuffer[2] = __Permedia2TagdXSub; pBuffer[3] = alDxDy[RIGHT]; pBuffer[4] = __Permedia2TagContinueNewSub; pBuffer[5] = lCount; pBuffer += 6; InputBufferCommit(ppdev, pBuffer); }// if !( aPtrFixNext[LEFT]->y < aPtrFixNext[RIGHT]->y ) }// Loop through next trapezoids // // Repeatedly draw more trapezoids until points are equal // If y values are equal, then we can start again from // scratch. // if ( aPtrFixNext[LEFT] == aPtrFixNext[RIGHT] ) { break; } }// loop through all the trapezoids FinishedPolygon: if ( !lNumOfPass-- ) { break; } }// Loop through all the polygons if ( pClipList ) { // // Reset scissor mode to its default state. // InputBufferReserve(ppdev, 2, &pBuffer); pBuffer[0] = __Permedia2TagScissorMode; pBuffer[1] = SCREEN_SCISSOR_DEFAULT; pBuffer += 2; InputBufferCommit(ppdev, pBuffer); } DBG_GDI((6, "bFillPolygon: returning TRUE")); bRC = TRUE; ReturnBack: InputBufferReserve(ppdev, 12, &pBuffer); pBuffer[0] = __Permedia2TagColorDDAMode; pBuffer[1] = __PERMEDIA_DISABLE; pBuffer[2] = __Permedia2TagdY; pBuffer[3] = INTtoFIXED(1); pBuffer[4] = __Permedia2TagContinue; pBuffer[5] = 0; pBuffer[6] = __Permedia2TagContinueNewDom; pBuffer[7] = 0; pBuffer[8] = __Permedia2TagdXDom; pBuffer[9] = 0; pBuffer[10] = __Permedia2TagdXSub; pBuffer[11] = 0; pBuffer += 12; InputBufferCommit(ppdev, pBuffer); return bRC; }// bFillPolygon() //----------------------------------------------------------------------------- // // BOOL bFillSpan() // // This is the code to break the polygon into spans. // // Parameters: // ppdev-------Pointer to PDev // pSurfDst----Destination surface // lEdges------Number of edges, includes close figure edge // pptfxFirst--Pointer to the first point in the data buffer. There are total // "lEdges" points // pptfxTop----Pointer to the toppest point in the polygon array. // pptfxLast---Pointer to the last point in the polygon array. // iSolidColor-Solid color fill // ulRop4------ROP4 // pco---------Clip Object. // prb---------Realized brush // pptlBrush---Pattern alignment // //----------------------------------------------------------------------------- BOOL bFillSpans(PDev* ppdev, Surf* pSurfDst, LONG lEdges, POINTFIX* pptfxFirst, POINTFIX* pptfxTop, POINTFIX* pptfxLast, ULONG ulSolidColor, ULONG ulRop4, CLIPOBJ* pco, RBrush* prb, POINTL* pptlBrush) { GFNPB pb; // Parameter block POINTFIX* pptfxOld; // Start point in current edge EDGEDATA aEd[2]; // Left and right edge EDGEDATA aEdTmp[2]; // DDA terms and stuff EDGEDATA* pEdgeData; BOOL bTrivialClip; // Trivial Clip or not DWORD dwAsMode[2]; // The area stipple mode and the color for that // pass DWORD dwColorMode; // Current color mode DWORD dwColorReg; // Current color register mode DWORD dwContinueMsg = 0; // Current "Continue" register settings DWORD dwLogicMode; // Current logic op mode DWORD dwRenderBits; // Current render bits DWORD dwReadMode; // Current register read mode LONG lCurrentSpan; // Current Span LONG lDX; // Edge delta in FIX units in x direction LONG lDY; // Edge delta in FIX units in y direction LONG lNumColors; // Number of colors LONG lNumOfPass; // Number of passes required to render LONG lNumScan; // Number of scans in current trapezoid LONG lQuotient; // Quotient LONG lRemainder; // Remainder LONG lStartY; // y-position of start point in current edge LONG lTempNumScan; // Temporary variable for number of spans LONG lTmpLeftX; // Temporary variable LONG lTmpRightX; // Temporary variable ULONG ulBgColor; // Background color ULONG ulBgLogicOp = ulRop3ToLogicop(ulRop4 >> 8); ULONG ulBrushColor = ulSolidColor; ULONG ulColor[2]; // On multiple color passes we need to know how // to set up ULONG ulFgColor; // Foreground color ULONG ulFgLogicOp = ulRop3ToLogicop(ulRop4 & 0xFF); ULONG* pBuffer; PERMEDIA_DECL; bTrivialClip = (pco == NULL) || (pco->iDComplexity == DC_TRIVIAL); pb.ppdev = ppdev; // // This span code cannot handle a clip list yet! // if ( !bTrivialClip ) { return FALSE; } DBG_GDI((7, "Starting Spans Code")); // // Setup window base first // InputBufferReserve(ppdev, 2, &pBuffer); pBuffer[0] = __Permedia2TagFBWindowBase; pBuffer[1] = pSurfDst->ulPixOffset; pBuffer += 2; InputBufferCommit(ppdev, pBuffer); // // Some Initialization. First trapezoid starts from the topest point // which is pointed by "pptfxTop". // Here we convert it from 28.4 to normal interger // lCurrentSpan = (pptfxTop->y + 15) >> 4; // // Make sure we initialize the DDAs appropriately: // aEd[LEFT].lNumOfScanToGo = 0; // Number of scans to go for this left edge aEd[RIGHT].lNumOfScanToGo = 0; // Number of scans to go for this right edge // // For now, guess as to which is the left and which is the right edge // aEd[LEFT].lPtfxDelta = -(LONG)sizeof(POINTFIX); // Delta (in bytes) from aEd[RIGHT].lPtfxDelta = sizeof(POINTFIX); // pptfx to next point aEd[LEFT].pptfx = pptfxTop; // Points to start of aEd[RIGHT].pptfx = pptfxTop; // current edge DBG_GDI((7, "bFillPolygon: Polygon is renderable. Go render")); if ( ulFgLogicOp == K_LOGICOP_COPY ) { dwColorMode = __PERMEDIA_DISABLE; dwLogicMode = __PERMEDIA_CONSTANT_FB_WRITE; dwReadMode = PM_FBREADMODE_PARTIAL(pSurfDst->ulPackedPP) | PM_FBREADMODE_PACKEDDATA(__PERMEDIA_DISABLE); // // If block fills not available or using the area stipple for mono // pattern, then use constant color. // if ( ulBrushColor == 0xffffffff ) { dwColorReg = __Permedia2TagFBWriteData; dwRenderBits = __RENDER_TRAPEZOID_PRIMITIVE; } // Non-solid brush else { // // We can use fast fills, so load the fb block color register. // dwColorReg = __Permedia2TagFBBlockColor; dwRenderBits = __RENDER_FAST_FILL_ENABLE | __RENDER_TRAPEZOID_PRIMITIVE; // // Replicate colour for block fill colour. // if ( ppdev->cPelSize < 2 ) { ulSolidColor |= ulSolidColor << 16; if ( ppdev->cPelSize == 0 ) { ulSolidColor |= ulSolidColor << 8; } } // // Ensure that the last access was a write before loading // BlockColor // InputBufferReserve(ppdev, 2, &pBuffer); pBuffer[0] = __Permedia2TagFBBlockColor; pBuffer[1] = ulSolidColor; pBuffer += 2; InputBufferCommit(ppdev, pBuffer); }// Solid brush }// K_LOGICOP_COPY else { dwColorMode = __COLOR_DDA_FLAT_SHADE; dwLogicMode = P2_ENABLED_LOGICALOP(ulFgLogicOp); dwReadMode = PM_FBREADMODE_PARTIAL(pSurfDst->ulPackedPP) | LogicopReadDest[ulFgLogicOp]; dwColorReg = __Permedia2TagConstantColor; dwRenderBits = __RENDER_TRAPEZOID_PRIMITIVE; }// NON_COPY // // To get correct results, we need to add on nearly one to each X // coordinate. // if ( ulBrushColor != 0xFFFFFFFF ) { // // This is a solid brush // lNumColors = 1; if ( dwColorMode == __PERMEDIA_DISABLE ) { // // This is from LOGICOP_COPY mode according to the dwColorMode we // set above // // Note: ColorDDAMode is DISABLED at initialisation time so // there is no need to re-load it here. // InputBufferReserve(ppdev, 6, &pBuffer); pBuffer[0] = __Permedia2TagFBReadMode; pBuffer[1] = dwReadMode; pBuffer[2] = __Permedia2TagLogicalOpMode; pBuffer[3] = dwLogicMode; pBuffer[4] = dwColorReg; pBuffer[5] = ulSolidColor; pBuffer += 6; InputBufferCommit(ppdev, pBuffer); }// Disable color DDA, LOGIC_COPY else { // // This is from NON-COPY logicop mode according to the dwColorMode // we set above // InputBufferReserve(ppdev, 8, &pBuffer); pBuffer[0] = __Permedia2TagColorDDAMode; pBuffer[1] = dwColorMode; pBuffer[2] = __Permedia2TagFBReadMode; pBuffer[3] = dwReadMode; pBuffer[4] = __Permedia2TagLogicalOpMode; pBuffer[5] = dwLogicMode; pBuffer[6] = dwColorReg; pBuffer[7] = ulSolidColor; pBuffer += 8; InputBufferCommit(ppdev, pBuffer); }// Enable colorDDA, NON-COPY mode }// Solid brush case else { // // For non-solid brush case, we need to realize brush // BrushEntry* pbe; // // Turn on the area stipple. // dwRenderBits |= __RENDER_AREA_STIPPLE_ENABLE; // // If anything has changed with the brush we must re-realize it. If // the brush has been kicked out of the area stipple unit we must // fully realize it. If only the alignment has changed we can // simply update the alignment for the stipple. // DBG_GDI((7, "Brush found")); ASSERTDD(prb != NULL, "Caller should pass in prb for non-solid brush"); pbe = prb->pbe; pb.prbrush = prb; pb.pptlBrush = pptlBrush; if ( (pbe == NULL) || (pbe->prbVerify != prb) ) { DBG_GDI((7, "full brush realize")); vPatRealize(&pb); } else if ( (prb->ptlBrushOrg.x != pptlBrush->x) ||(prb->ptlBrushOrg.y != pptlBrush->y) ) { DBG_GDI((7, "changing brush offset")); vMonoOffset(&pb); } ulFgColor = prb->ulForeColor; ulBgColor = prb->ulBackColor; if ( dwColorMode == __PERMEDIA_DISABLE ) { // // ColorDDAMode is DISABLED at initialisation time so there is // no need to re-load it here. // InputBufferReserve(ppdev, 4, &pBuffer); pBuffer[0] = __Permedia2TagFBReadMode; pBuffer[1] = dwReadMode; pBuffer[2] = __Permedia2TagLogicalOpMode; pBuffer[3] = dwLogicMode; pBuffer += 4; InputBufferCommit(ppdev, pBuffer); } else { InputBufferReserve(ppdev, 6, &pBuffer); pBuffer[0] = __Permedia2TagColorDDAMode; pBuffer[1] = dwColorMode; pBuffer[2] = __Permedia2TagFBReadMode; pBuffer[3] = dwReadMode; pBuffer[4] = __Permedia2TagLogicalOpMode; pBuffer[5] = dwLogicMode; pBuffer += 6; InputBufferCommit(ppdev, pBuffer); } if ( (ulBgLogicOp == K_LOGICOP_NOOP) ||((ulFgLogicOp == K_LOGICOP_XOR) && (ulBgColor == 0)) ) { // // Either we have a transparent bitmap or it can be assumed to // be transparent (XOR with bg=0) // DBG_GDI((7, "transparant bg")); lNumColors = 1; InputBufferReserve(ppdev, 4, &pBuffer); pBuffer[0] = dwColorReg; pBuffer[1] = ulFgColor; pBuffer[2] = __Permedia2TagAreaStippleMode; pBuffer[3] = prb->areaStippleMode; pBuffer += 4; InputBufferCommit(ppdev, pBuffer); } else if ( (ulFgLogicOp == K_LOGICOP_XOR) && (ulFgColor == 0) ) { // // We have a transparent foreground! (XOR with fg=0) // DBG_GDI((7, "transparant fg")); lNumColors = 1; InputBufferReserve(ppdev, 4, &pBuffer); pBuffer[0] = dwColorReg; pBuffer[1] = ulBgColor; pBuffer[2] = __Permedia2TagAreaStippleMode; pBuffer[3] = prb->areaStippleMode |AREA_STIPPLE_INVERT_PAT; pBuffer += 4; InputBufferCommit(ppdev, pBuffer); } else { // // Not using a transparent pattern // DBG_GDI((7, "2 color")); lNumColors = 2; ulColor[0] = ulFgColor; ulColor[1] = ulBgColor; dwAsMode[0] = prb->areaStippleMode; dwAsMode[1] = dwAsMode[0] | AREA_STIPPLE_INVERT_PAT; } }// Non-solid brush case InputBufferReserve(ppdev, 2, &pBuffer); pBuffer[0] = __Permedia2TagCount; pBuffer[1] = 0; pBuffer += 2; InputBufferCommit(ppdev, pBuffer); // // dxDom, dXSub and dY are initialised to 0, 0, and 1, so we don't need // to re-load them here. // DBG_GDI((7, "Rendering Polygon. %d Colors", lNumColors)); NewTrapezoid: DBG_GDI((7, "New Trapezoid")); // // DDA initialization // Here we start with LEFT(1) edge and then RIGHT(0) edge // for ( int iEdge = 1; iEdge >= 0; --iEdge ) { pEdgeData = &aEd[iEdge]; if ( pEdgeData->lNumOfScanToGo == 0 ) { // // No more scan lines left to go. Need a new DDA // loop until we have some scan lines to go // do { lEdges--; if ( lEdges < 0 ) { // // This is the only return point for this // "BreakIntoSpans", that is, we return TRUE when there // is no more edge left. We are done. // DBG_GDI((7, "bFillPolygon: returning TRUE")); return TRUE; }// if no more edge left // // Find the next left edge, accounting for wrapping. Before // that, save the old edge in "pptfxOld" // pptfxOld = pEdgeData->pptfx; // // Get next point // pEdgeData->pptfx = (POINTFIX*)((BYTE*)pEdgeData->pptfx + pEdgeData->lPtfxDelta); // // Checking the end point cases // if ( pEdgeData->pptfx < pptfxFirst ) { pEdgeData->pptfx = pptfxLast; } else if ( pEdgeData->pptfx > pptfxLast ) { pEdgeData->pptfx = pptfxFirst; } // // Have to find the edge that spans lCurrentSpan. // Note: we need to convert it to normal interger first // pEdgeData->lNumOfScanToGo = ((pEdgeData->pptfx->y + 15) >> 4) - lCurrentSpan; // // With fractional coordinate end points, we may get edges // that don't cross any scans, in which case we try the // next one // } while ( pEdgeData->lNumOfScanToGo <= 0 ); // // 'pEdgeData->pptfx' now points to the end point of the edge // spanning the scan 'lCurrentSpan'. // Calculate dx(lDX) and dy(lDY) // lDY = pEdgeData->pptfx->y - pptfxOld->y; lDX = pEdgeData->pptfx->x - pptfxOld->x; ASSERTDD(lDY > 0, "Should be going down only"); // // Compute the DDA increment terms // if ( lDX < 0 ) { // // X is moving from right to left because it is negative // lDX = -lDX; if ( lDX < lDY ) // Can't be '<=' { pEdgeData->lXAdvance = -1; pEdgeData->lErrorUp = lDY - lDX; } else { QUOTIENT_REMAINDER(lDX, lDY, lQuotient, lRemainder); pEdgeData->lXAdvance = -lQuotient; // - lDX / lDY pEdgeData->lErrorUp = lRemainder; // lDX % lDY if ( pEdgeData->lErrorUp > 0 ) { pEdgeData->lXAdvance--; pEdgeData->lErrorUp = lDY - pEdgeData->lErrorUp; } } }// lDX is negative else { // // X is moving from left to right // if ( lDX < lDY ) // Can't be '<=' { pEdgeData->lXAdvance = 0; pEdgeData->lErrorUp = lDX; } else { QUOTIENT_REMAINDER(lDX, lDY, lQuotient, lRemainder); pEdgeData->lXAdvance = lQuotient; // lDX / lDY pEdgeData->lErrorUp = lRemainder; // lDX % lDY } } // lDX is positive pEdgeData->lErrorDown = lDY; // DDA limit // // Error is initially zero (add lDY -1 for the ceiling, but // subtract off lDY so that we can check the sign instead of // comparing to lDY) // pEdgeData->lError = -1; // // Current edge X starting point // pEdgeData->lCurrentXPos = pptfxOld->x; // // Current edge Y starting point // lStartY = pptfxOld->y; // // Check if the floating part of the Y coordinate is 0 // Note: lStartY is still in 28.4 format // if ( (lStartY & 15) != 0 ) { // // Advance to the next integer y coordinate // Note: here "pEdgeData->x += pEdgeData->lXAdvance" only // increase its fraction part // for ( int i = 16 - (lStartY & 15); i != 0; --i ) { pEdgeData->lCurrentXPos += pEdgeData->lXAdvance; pEdgeData->lError += pEdgeData->lErrorUp; if ( pEdgeData->lError >= 0 ) { pEdgeData->lError -= pEdgeData->lErrorDown; pEdgeData->lCurrentXPos++; } } }// Handle fraction part of the coordinate if ( (pEdgeData->lCurrentXPos & 15) != 0 ) { // // We'll want the ceiling in just a bit... // pEdgeData->lError -= pEdgeData->lErrorDown * (16 - (pEdgeData->lCurrentXPos & 15)); pEdgeData->lCurrentXPos += 15; } // // Chop off those fractional bits, convert to regular format // pEdgeData->lCurrentXPos = pEdgeData->lCurrentXPos >> 4; pEdgeData->lError >>= 4; // // Convert to Permedia2 format positions and deltas // Note: all the data in pEdgeData, aEd are in Permedia2 format now // pEdgeData->lCurrentXPos = INTtoFIXED(pEdgeData->lCurrentXPos) + NEARLY_ONE; pEdgeData->lXAdvance = INTtoFIXED(pEdgeData->lXAdvance); }// If there is no more scan line left }// Looping throught the LEFT and RIGHT edges // // Number of scans in this trap // Note: here aEd[LEFT].lNumOfScanToGo and aEd[RIGHT].lNumOfScanToGo are // already in normal interger mode since we have done: // pEdgeData->lNumOfScanToGo = ((pEdgeData->pptfx->y + 15) >> 4) // - lCurrentSpan; above // lNumScan = min(aEd[LEFT].lNumOfScanToGo, aEd[RIGHT].lNumOfScanToGo); aEd[LEFT].lNumOfScanToGo -= lNumScan; aEd[RIGHT].lNumOfScanToGo -= lNumScan; lCurrentSpan += lNumScan; // Top scan in next trap // // If the left and right edges are vertical, simply output as a rectangle // DBG_GDI((7, "Generate spans")); lNumOfPass = 0; while ( ++lNumOfPass <= lNumColors ) { DBG_GDI((7, "Pass %d lNumColors %d", lNumOfPass, lNumColors)); if ( lNumColors == 2 ) { // // Two colours, so we need to save and restore aEd values // and set the color and stipple mode. // InputBufferReserve(ppdev, 4, &pBuffer); if ( lNumOfPass == 1 ) { // // Pass 1, set color reg as foreground color // aEdTmp[LEFT] = aEd[LEFT]; aEdTmp[RIGHT] = aEd[RIGHT]; lTempNumScan = lNumScan; pBuffer[0] = dwColorReg; pBuffer[1] = ulColor[0]; pBuffer[2] = __Permedia2TagAreaStippleMode; pBuffer[3] = dwAsMode[0]; DBG_GDI((7, "Pass 1, Stipple set")); } else { // // Pass 2, set color reg as background color // aEd[LEFT] = aEdTmp[LEFT]; aEd[RIGHT] = aEdTmp[RIGHT]; lNumScan = lTempNumScan; pBuffer[0] = dwColorReg; pBuffer[1] = ulColor[1]; pBuffer[2] = __Permedia2TagAreaStippleMode; pBuffer[3] = dwAsMode[1]; DBG_GDI((7, "Pass 2, Stipple set, New trap started")); } pBuffer += 4; InputBufferCommit(ppdev, pBuffer); }// if (nColor == 2) InputBufferReserve(ppdev, 8, &pBuffer); // // Reset render position to the top of the trapezoid. // Note: here aEd[RIGHT].x etc. are alreadu in 12.15 mode since // we have done // "pEdgeData->x = INTtoFIXED(pEdgeData->x);" and // "pEdgeData->lXAdvance = INTtoFIXED(pEdgeData->lXAdvance);" above // pBuffer[0] = __Permedia2TagStartXDom; pBuffer[1] = aEd[RIGHT].lCurrentXPos; pBuffer[2] = __Permedia2TagStartXSub; pBuffer[3] = aEd[LEFT].lCurrentXPos; pBuffer[4] = __Permedia2TagStartY; pBuffer[5] = INTtoFIXED(lCurrentSpan - lNumScan); pBuffer[6] = __Permedia2TagRender; pBuffer[7] = dwRenderBits; pBuffer += 8; InputBufferCommit(ppdev, pBuffer); dwContinueMsg = __Permedia2TagContinue; if ( ((aEd[LEFT].lErrorUp | aEd[RIGHT].lErrorUp) == 0) &&((aEd[LEFT].lXAdvance| aEd[RIGHT].lXAdvance) == 0) &&(lNumScan > 1) ) { // // Vertical-edge special case // DBG_GDI((7, "Vertical Edge Special Case")); // // Tell the hardware that we have "lNumScan" scan lines // to fill // InputBufferReserve(ppdev, 2, &pBuffer); pBuffer[0] = dwContinueMsg; pBuffer[1] = lNumScan; pBuffer += 2; InputBufferCommit(ppdev, pBuffer); continue; } while ( TRUE ) { // // Run the DDAs // DBG_GDI((7, "Doing a span 0x%x to 0x%x, 0x%x scans left.Continue%s", aEd[LEFT].lCurrentXPos, aEd[RIGHT].lCurrentXPos, lNumScan, (dwContinueMsg == __Permedia2TagContinueNewDom) ? "NewDom": ((dwContinueMsg == __Permedia2TagContinue)? "":"NewSub"))); // // Tell the hardware that we have "1" scan lines to fill // InputBufferReserve(ppdev, 2, &pBuffer); pBuffer[0] = dwContinueMsg; pBuffer[1] = 1; pBuffer += 2; InputBufferCommit(ppdev, pBuffer); // // We have finished this trapezoid. Go get the next one // // Advance the right wall // lTmpRightX = aEd[RIGHT].lCurrentXPos; aEd[RIGHT].lCurrentXPos += aEd[RIGHT].lXAdvance; aEd[RIGHT].lError += aEd[RIGHT].lErrorUp; if ( aEd[RIGHT].lError >= 0 ) { aEd[RIGHT].lError -= aEd[RIGHT].lErrorDown; aEd[RIGHT].lCurrentXPos += INTtoFIXED(1); } // // Advance the left wall // lTmpLeftX = aEd[LEFT].lCurrentXPos; aEd[LEFT].lCurrentXPos += aEd[LEFT].lXAdvance; aEd[LEFT].lError += aEd[LEFT].lErrorUp; if ( aEd[LEFT].lError >= 0 ) { aEd[LEFT].lError -= aEd[LEFT].lErrorDown; aEd[LEFT].lCurrentXPos += INTtoFIXED(1); } if ( --lNumScan == 0 ) { break; } // // Setup the X registers if we have changed either end. // if ( lTmpRightX != aEd[RIGHT].lCurrentXPos ) { if ( lTmpLeftX != aEd[LEFT].lCurrentXPos ) { InputBufferReserve(ppdev, 6, &pBuffer); pBuffer[0] = __Permedia2TagStartXSub; pBuffer[1] = aEd[LEFT].lCurrentXPos; pBuffer[2] = __Permedia2TagContinueNewSub; pBuffer[3] = 0; pBuffer[4] = __Permedia2TagStartXDom; pBuffer[5] = aEd[RIGHT].lCurrentXPos; pBuffer += 6; InputBufferCommit(ppdev, pBuffer); } else { InputBufferReserve(ppdev, 2, &pBuffer); pBuffer[0] = __Permedia2TagStartXDom; pBuffer[1] = aEd[RIGHT].lCurrentXPos; pBuffer += 2; InputBufferCommit(ppdev, pBuffer); } dwContinueMsg = __Permedia2TagContinueNewDom; } else if ( lTmpLeftX != aEd[LEFT].lCurrentXPos ) { InputBufferReserve(ppdev, 2, &pBuffer); pBuffer[0] = __Permedia2TagStartXSub; pBuffer[1] = aEd[LEFT].lCurrentXPos; pBuffer += 2; InputBufferCommit(ppdev, pBuffer); dwContinueMsg = __Permedia2TagContinueNewSub; } }// while ( TRUE ) }// while ( ++lNumOfPass <= lNumColors ) DBG_GDI((7, "Generate spans done")); goto NewTrapezoid; }// bFillSpans()