/******************************Module*Header*******************************\ * Module Name: fastfill.c * * Draws fast convex rectangles. * * Copyright (c) 1993-1996 Microsoft Corporation * Copyright (c) 1993-1996 Matrox Electronic Systems, Ltd. \**************************************************************************/ #include "precomp.h" #define RIGHT 0 #define LEFT 1 typedef struct _TRAPEZOIDDATA TRAPEZOIDDATA; // Handy forward declaration typedef VOID (FNTRAPEZOID)(TRAPEZOIDDATA*, LONG, LONG); // Prototype for trapezoid // drawing routines typedef struct _EDGEDATA { LONG x; // Current x position LONG dx; // # pixels to advance x on each scan LONG lError; // Current DDA error LONG lErrorUp; // DDA error increment on each scan LONG dN; // Signed delta-y in fixed point form (also known // as the DDA error adjustment, and used to be // called 'lErrorDown') LONG dM; // Signed delta-x in fixed point form POINTFIX* pptfx; // Points to start of current edge LONG dptfx; // Delta (in bytes) from pptfx to next point LONG cy; // Number of scans to go for this edge LONG bNew; // Set to TRUE when a new DDA must be started // for the edge. } EDGEDATA; /* ed, ped */ typedef struct _TRAPEZOIDDATA { FNTRAPEZOID* pfnTrap; // Pointer to appropriate trapezoid drawing routine, // or trapezoid clip routine FNTRAPEZOID* pfnTrapClip;// Pointer to appropriate trapezoid drawing routine // if doing clipping PDEV* ppdev; // Pointer to PDEV EDGEDATA aed[2]; // DDA information for both edges POINTL ptlBrush; // Brush alignment LONG yClipTop; // Top of clip rectangle LONG yClipBottom;// Bottom of clip rectangle // MGA specific stuff below here: ULONG ulMgaSgn; // Current sign register, MGA specific ULONG ulLinear; // Linear offset to brush in off-screen memory } TRAPEZOIDDATA; /* td, ptd */ /******************************Public*Routine******************************\ * VOID vClipTrapezoid * * Clips a trapezoid. * * NOTE: This routine assumes that the polygon's dimensions are small * enough that its QUOTIENT_REMAINDER calculations won't overflow. * This means that large polygons must never make it here. * \**************************************************************************/ VOID vClipTrapezoid( TRAPEZOIDDATA* ptd, LONG yTrapTop, LONG cyTrapezoid) { LONG yTrapBottom; LONG dN; LONG lNum; LONG xDelta; LONG lError; yTrapBottom = yTrapTop + cyTrapezoid; if (yTrapTop < ptd->yClipTop) { if ((ptd->aed[LEFT].bNew) && (yTrapBottom + ptd->aed[LEFT].cy > ptd->yClipTop)) { dN = ptd->aed[LEFT].dN; lNum = ptd->aed[LEFT].dM * (ptd->yClipTop - yTrapTop) + (ptd->aed[LEFT].lError + dN); if (lNum >= 0) { QUOTIENT_REMAINDER(lNum, dN, xDelta, lError); } else { lNum = -lNum; QUOTIENT_REMAINDER(lNum, dN, xDelta, lError); xDelta = -xDelta; if (lError != 0) { xDelta--; lError = dN - lError; } } ptd->aed[LEFT].x += xDelta; ptd->aed[LEFT].lError = lError - dN; } if ((ptd->aed[RIGHT].bNew) && (yTrapBottom + ptd->aed[RIGHT].cy > ptd->yClipTop)) { dN = ptd->aed[RIGHT].dN; lNum = ptd->aed[RIGHT].dM * (ptd->yClipTop - yTrapTop) + (ptd->aed[RIGHT].lError + dN); if (lNum >= 0) { QUOTIENT_REMAINDER(lNum, dN, xDelta, lError); } else { lNum = -lNum; QUOTIENT_REMAINDER(lNum, dN, xDelta, lError); xDelta = -xDelta; if (lError != 0) { xDelta--; lError = dN - lError; } } ptd->aed[RIGHT].x += xDelta; ptd->aed[RIGHT].lError = lError - dN; } } // If this trapezoid vertically intersects our clip rectangle, draw it: if ((yTrapBottom > ptd->yClipTop) && (yTrapTop < ptd->yClipBottom)) { if (yTrapTop <= ptd->yClipTop) { yTrapTop = ptd->yClipTop; // Have to let trapezoid drawer know that it has to load // its DDAs for very first trapezoid drawn: ptd->aed[RIGHT].bNew = TRUE; ptd->aed[LEFT].bNew = TRUE; } if (yTrapBottom >= ptd->yClipBottom) { yTrapBottom = ptd->yClipBottom; } ptd->pfnTrapClip(ptd, yTrapTop, yTrapBottom - yTrapTop); } } /******************************Public*Routine******************************\ * VOID vHardwareTrapezoid * * Uses the MGA's hardware trapezoid capability to draw solid or two-colour * pattern trapezoids. * \**************************************************************************/ VOID vHardwareTrapezoid( TRAPEZOIDDATA* ptd, LONG yTrapezoid, LONG cyTrapezoid) { PDEV* ppdev; LONG dM; LONG lError; BYTE* pjBase; ppdev = ptd->ppdev; pjBase = ppdev->pjBase; if (ptd->aed[LEFT].bNew) { dM = ptd->aed[LEFT].dM; if (dM >= 0) { ptd->ulMgaSgn &= ~sdxl_SUB; lError = -dM - ptd->aed[LEFT].lError - 1; dM = -dM; } else { ptd->ulMgaSgn |= sdxl_SUB; lError = dM + ptd->aed[LEFT].dN + ptd->aed[LEFT].lError; } CHECK_FIFO_SPACE(pjBase, 6); CP_WRITE(pjBase, DWG_AR2, dM); CP_WRITE(pjBase, DWG_AR1, lError); CP_WRITE(pjBase, DWG_AR0, ptd->aed[LEFT].dN); CP_WRITE(pjBase, DWG_FXLEFT, ptd->aed[LEFT].x + ppdev->xOffset); } if (ptd->aed[RIGHT].bNew) { dM = ptd->aed[RIGHT].dM; if (dM >= 0) { ptd->ulMgaSgn &= ~sdxr_DEC; lError = -dM - ptd->aed[RIGHT].lError - 1; dM = -dM; } else { ptd->ulMgaSgn |= sdxr_DEC; lError = dM + ptd->aed[RIGHT].dN + ptd->aed[RIGHT].lError; } CHECK_FIFO_SPACE(pjBase, 6); CP_WRITE(pjBase, DWG_AR5, dM); CP_WRITE(pjBase, DWG_AR4, lError); CP_WRITE(pjBase, DWG_AR6, ptd->aed[RIGHT].dN); CP_WRITE(pjBase, DWG_FXRIGHT, ptd->aed[RIGHT].x + ppdev->xOffset); } CP_WRITE(pjBase, DWG_SGN, ptd->ulMgaSgn); CP_START(pjBase, DWG_LEN, cyTrapezoid); } /******************************Public*Routine******************************\ * VOID vMilSoftwareTrapezoid * * Draws a trapezoid using a software DDA. * \**************************************************************************/ VOID vMilSoftwareTrapezoid( TRAPEZOIDDATA* ptd, LONG yTrapezoid, LONG cyTrapezoid) { PDEV* ppdev; LONG xOffset; LONG xBrush; ULONG ulOffset; ULONG ulLinear; ULONG ulScan; CHAR cFifo; LONG lLeftError; LONG xLeft; LONG lRightError; LONG xRight; ULONG ulAr0Adj; BYTE* pjBase; ppdev = ptd->ppdev; pjBase = ppdev->pjBase; xBrush = ptd->ptlBrush.x; ulOffset = ((yTrapezoid - ptd->ptlBrush.y) & 7) << PATTERN_PITCH_SHIFT; ulLinear = ptd->ulLinear; // For cjPelSize = 1, 2, 3, or 4, // ulAr0Adj = 2, 4, 0, or 6. if (ppdev->cjPelSize == 3) { ulAr0Adj = 0; } else { ulAr0Adj = (ppdev->cjPelSize + 2) & 0xfffffffe; } xOffset = ppdev->xOffset; yTrapezoid += ppdev->yOffset; // If the left and right edges are vertical, simply output as // a rectangle. if (((ptd->aed[LEFT].lErrorUp | ptd->aed[RIGHT].lErrorUp) == 0) && ((ptd->aed[LEFT].dx | ptd->aed[RIGHT].dx) == 0)) { xLeft = ptd->aed[LEFT].x + xOffset; xRight = ptd->aed[RIGHT].x + xOffset - 1; // Inclusive of edge if (xLeft <= xRight) { CHECK_FIFO_SPACE(pjBase, 4); ulScan = ulLinear + ulOffset + ((xLeft - xBrush) & 7); CP_WRITE(pjBase, DWG_AR3, ulScan); if (ulAr0Adj) { CP_WRITE(pjBase, DWG_AR0, ((ulScan & 0xfffffff8) | ((ulScan + ulAr0Adj) & 7))); } else { CP_WRITE(pjBase, DWG_AR0, (ulScan + 7)); } CP_WRITE(pjBase, DWG_FXBNDRY, (xRight << bfxright_SHIFT) | (xLeft & bfxleft_MASK)); CP_START(pjBase, DWG_YDSTLEN, (yTrapezoid << yval_SHIFT) | (cyTrapezoid & ylength_MASK)); } } else { cFifo = 0; lLeftError = ptd->aed[LEFT].lError; xLeft = ptd->aed[LEFT].x + xOffset; lRightError = ptd->aed[RIGHT].lError; xRight = ptd->aed[RIGHT].x + xOffset - 1; // Inclusive of edge while (TRUE) { ///////////////////////////////////////////////////////////////// // Run the DDAs if (xLeft <= xRight) { // We get a little tricky here and try to amortize the cost of // the read for checking the FIFO count on the MGA. Doing // so got us a 25% win on large triangles on a P90. cFifo -= 4; if (cFifo < 0) { do { cFifo = GET_FIFO_SPACE(pjBase) - 4; } while (cFifo < 0); } ulScan = ulLinear + ulOffset + ((xLeft - xBrush) & 7); CP_WRITE(pjBase, DWG_AR3, ulScan); if (ulAr0Adj) { CP_WRITE(pjBase, DWG_AR0, ((ulScan & 0xfffffff8) | ((ulScan + ulAr0Adj) & 7))); } else { CP_WRITE(pjBase, DWG_AR0, (ulScan + 7)); } CP_WRITE(pjBase, DWG_FXBNDRY, (xRight << bfxright_SHIFT) | (xLeft & bfxleft_MASK)); CP_START(pjBase, DWG_YDSTLEN, (yTrapezoid << yval_SHIFT) | (1 & ylength_MASK)); } ulOffset = (ulOffset + (1 << PATTERN_PITCH_SHIFT)) & (7 << PATTERN_PITCH_SHIFT); yTrapezoid++; // Advance the right wall. xRight += ptd->aed[RIGHT].dx; lRightError += ptd->aed[RIGHT].lErrorUp; if (lRightError >= 0) { lRightError -= ptd->aed[RIGHT].dN; xRight++; } // Advance the left wall. xLeft += ptd->aed[LEFT].dx; lLeftError += ptd->aed[LEFT].lErrorUp; if (lLeftError >= 0) { lLeftError -= ptd->aed[LEFT].dN; xLeft++; } cyTrapezoid--; if (cyTrapezoid == 0) break; } ptd->aed[LEFT].lError = lLeftError; ptd->aed[LEFT].x = xLeft - xOffset; ptd->aed[RIGHT].lError = lRightError; ptd->aed[RIGHT].x = xRight - xOffset + 1; } } /******************************Public*Routine******************************\ * VOID vMilTrapezoidSetup * * Initialize the hardware and some state for doing trapezoids. * \**************************************************************************/ VOID vMilTrapezoidSetup( PDEV* ppdev, ULONG rop4, ULONG iSolidColor, RBRUSH* prb, POINTL* pptlBrush, TRAPEZOIDDATA* ptd, LONG yStart, // First scan for drawing RECTL* prclClip) // NULL if no clipping { ULONG ulHwMix; ULONG ulDwg; LONG xOffset; LONG yOffset; BRUSHENTRY* pbe; BYTE* pjBase; pjBase = ppdev->pjBase; ptd->ppdev = ppdev; ptd->ulMgaSgn = 0; xOffset = ppdev->xOffset; yOffset = ppdev->yOffset; if ((prclClip != NULL) && (prclClip->top > yStart)) yStart = prclClip->top; if (iSolidColor != -1) { // Solid fill. ptd->pfnTrap = vHardwareTrapezoid; CHECK_FIFO_SPACE(pjBase, 3); if (rop4 == 0xf0f0) { CP_WRITE(pjBase, DWG_DWGCTL, (opcode_TRAP + atype_RPL + solid_SOLID + bop_SRCCOPY + transc_BG_OPAQUE)); } else { ulHwMix = (rop4 & 0x03) + ((rop4 & 0x30) >> 2); CP_WRITE(pjBase, DWG_DWGCTL, (opcode_TRAP + atype_RSTR + solid_SOLID + (ulHwMix << 16) + transc_BG_OPAQUE)); } CP_WRITE(pjBase, DWG_FCOL, COLOR_REPLICATE(ppdev, iSolidColor)); CP_WRITE(pjBase, DWG_YDST, yStart + yOffset); ppdev->HopeFlags = PATTERN_CACHE; } else { // Pattern fill. if (prb->fl & RBRUSH_2COLOR) { // Monochrome brush. ptd->pfnTrap = vHardwareTrapezoid; if ((rop4 & 0xff) == 0xf0) { ulDwg = opcode_TRAP + atype_RPL + bop_SRCCOPY; } else { ulHwMix = (rop4 & 0x03) + ((rop4 & 0x30) >> 2); ulDwg = opcode_TRAP + atype_RSTR + (ulHwMix << 16); } if (((rop4 >> 8) & 0xff) == (rop4 & 0xff)) { // Normal opaque mode. ulDwg |= transc_BG_OPAQUE; } else { // GDI guarantees us that if the foreground and background // ROPs are different, the background rop is LEAVEALONE. ulDwg |= transc_BG_TRANSP; } CHECK_FIFO_SPACE(pjBase, 9); CP_WRITE(pjBase, DWG_DWGCTL, ulDwg); CP_WRITE(pjBase, DWG_FCOL, COLOR_REPLICATE(ppdev, prb->ulColor[1])); CP_WRITE(pjBase, DWG_BCOL, COLOR_REPLICATE(ppdev, prb->ulColor[0])); CP_WRITE(pjBase, DWG_SRC0, prb->aulPattern[0]); CP_WRITE(pjBase, DWG_SRC1, prb->aulPattern[1]); CP_WRITE(pjBase, DWG_SRC2, prb->aulPattern[2]); CP_WRITE(pjBase, DWG_SRC3, prb->aulPattern[3]); CP_WRITE(pjBase, DWG_YDST, yStart + yOffset); CP_WRITE(pjBase, DWG_SHIFT, ((-(pptlBrush->y + ppdev->yOffset) & 7) << 4) | (-(pptlBrush->x + ppdev->xOffset) & 7)); } else { // Color brush. // We have to ensure that no other brush took our spot in // off-screen memory. pbe = prb->apbe[IBOARD(ppdev)]; if (pbe->prbVerify != prb) { // Download the brush into the cache. if (ppdev->cjPelSize != 3) { vMilPatRealize(ppdev, prb); } else { vMilPatRealize24bpp(ppdev, prb); } pbe = prb->apbe[IBOARD(ppdev)]; } ptd->pfnTrap = vMilSoftwareTrapezoid; ptd->ulLinear = pbe->ulLinear; ptd->ptlBrush = *pptlBrush; CHECK_FIFO_SPACE(pjBase, 2); if (rop4 == 0xf0f0) // PATCOPY { CP_WRITE(pjBase, DWG_DWGCTL, (opcode_BITBLT + atype_RPL + sgnzero_ZERO + shftzero_ZERO + bop_SRCCOPY + bltmod_BFCOL + pattern_ON + transc_BG_OPAQUE)); } else { ulHwMix = (rop4 & 0x03) + ((rop4 & 0x30) >> 2); CP_WRITE(pjBase, DWG_DWGCTL, (opcode_BITBLT + atype_RSTR + sgnzero_ZERO + shftzero_ZERO + (ulHwMix << 16) + bltmod_BFCOL + pattern_ON + transc_BG_OPAQUE)); } CP_WRITE(pjBase, DWG_AR5, PATTERN_PITCH); } ppdev->HopeFlags = 0; } if (prclClip != NULL) { ptd->pfnTrapClip = ptd->pfnTrap; ptd->pfnTrap = vClipTrapezoid; ptd->yClipTop = prclClip->top; ptd->yClipBottom = prclClip->bottom; CHECK_FIFO_SPACE(pjBase, 2); CP_WRITE(pjBase, DWG_CXLEFT, prclClip->left + xOffset); CP_WRITE(pjBase, DWG_CXRIGHT, prclClip->right + xOffset - 1); } } /******************************Public*Routine******************************\ * VOID vMgaSoftwareTrapezoid * * Draws a trapezoid using a software DDA. * \**************************************************************************/ VOID vMgaSoftwareTrapezoid( TRAPEZOIDDATA* ptd, LONG yTrapezoid, LONG cyTrapezoid) { PDEV* ppdev; BYTE* pjBase; LONG xOffset; LONG xBrush; ULONG ulOffset; ULONG ulLinear; ULONG ulScan; CHAR cFifo; LONG lLeftError; LONG xLeft; LONG lRightError; LONG xRight; ppdev = ptd->ppdev; pjBase = ppdev->pjBase; xBrush = ptd->ptlBrush.x; ulOffset = ((yTrapezoid - ptd->ptlBrush.y) & 7) << 5; ulLinear = ptd->ulLinear; xOffset = ppdev->xOffset; yTrapezoid += ppdev->yOffset; // If the left and right edges are vertical, simply output as // a rectangle: if (((ptd->aed[LEFT].lErrorUp | ptd->aed[RIGHT].lErrorUp) == 0) && ((ptd->aed[LEFT].dx | ptd->aed[RIGHT].dx) == 0)) { xLeft = ptd->aed[LEFT].x + xOffset; xRight = ptd->aed[RIGHT].x + xOffset - 1; // Inclusive of edge if (xLeft <= xRight) { CHECK_FIFO_SPACE(pjBase, 6); CP_WRITE(pjBase, DWG_FXLEFT, xLeft); // xOffset already added in CP_WRITE(pjBase, DWG_FXRIGHT, xRight); ulScan = ulLinear + ulOffset; CP_WRITE(pjBase, DWG_AR3, ulScan + ((xLeft - xBrush) & 7)); CP_WRITE(pjBase, DWG_AR0, ulScan + 15); CP_WRITE(pjBase, DWG_LEN, cyTrapezoid); CP_START(pjBase, DWG_YDST, yTrapezoid); } } else { cFifo = 0; lLeftError = ptd->aed[LEFT].lError; xLeft = ptd->aed[LEFT].x + xOffset; lRightError = ptd->aed[RIGHT].lError; xRight = ptd->aed[RIGHT].x + xOffset - 1; // Inclusive of edge while (TRUE) { ///////////////////////////////////////////////////////////////// // Run the DDAs if (xLeft <= xRight) { // We get a little tricky here and try to amortize the cost of // the read for checking the FIFO count on the MGA. Doing // so got us a 25% win on large triangles on a P90: cFifo -= 6; if (cFifo < 0) { do { cFifo = GET_FIFO_SPACE(pjBase) - 6; } while (cFifo < 0); } CP_WRITE(pjBase, DWG_FXLEFT, xLeft); CP_WRITE(pjBase, DWG_FXRIGHT, xRight); ulScan = ulLinear + ulOffset; CP_WRITE(pjBase, DWG_AR0, ulScan + 15); CP_WRITE(pjBase, DWG_AR3, ulScan + ((xLeft - xBrush) & 7)); CP_WRITE(pjBase, DWG_LEN, 1); CP_START(pjBase, DWG_YDST, yTrapezoid); } ulOffset = (ulOffset + (1 << 5)) & (7 << 5); yTrapezoid++; // Advance the right wall: xRight += ptd->aed[RIGHT].dx; lRightError += ptd->aed[RIGHT].lErrorUp; if (lRightError >= 0) { lRightError -= ptd->aed[RIGHT].dN; xRight++; } // Advance the left wall: xLeft += ptd->aed[LEFT].dx; lLeftError += ptd->aed[LEFT].lErrorUp; if (lLeftError >= 0) { lLeftError -= ptd->aed[LEFT].dN; xLeft++; } cyTrapezoid--; if (cyTrapezoid == 0) break; } ptd->aed[LEFT].lError = lLeftError; ptd->aed[LEFT].x = xLeft - xOffset; ptd->aed[RIGHT].lError = lRightError; ptd->aed[RIGHT].x = xRight - xOffset + 1; } } /******************************Public*Routine******************************\ * VOID vMgaTrapezoidSetup * * Initialize the hardware and some state for doing trapezoids. * \**************************************************************************/ VOID vMgaTrapezoidSetup( PDEV* ppdev, ULONG rop4, ULONG iSolidColor, RBRUSH* prb, POINTL* pptlBrush, TRAPEZOIDDATA* ptd, LONG yStart, // First scan for drawing RECTL* prclClip) // NULL if no clipping { BYTE* pjBase; ULONG ulHwMix; ULONG ulDwg; BRUSHENTRY* pbe; ptd->ppdev = ppdev; ptd->ulMgaSgn = 0; pjBase = ppdev->pjBase; if ((prclClip != NULL) && (prclClip->top > yStart)) yStart = prclClip->top; if (iSolidColor != -1) { ptd->pfnTrap = vHardwareTrapezoid; CHECK_FIFO_SPACE(pjBase, 7); if (rop4 == 0xf0f0) { CP_WRITE(pjBase, DWG_DWGCTL, opcode_TRAP + transc_BG_OPAQUE + blockm_ON + atype_RPL + bop_SRCCOPY); } else { ulHwMix = (rop4 & 0x03) + ((rop4 & 0x30) >> 2); CP_WRITE(pjBase, DWG_DWGCTL, opcode_TRAP + transc_BG_OPAQUE + blockm_OFF + atype_RSTR + (ulHwMix << 16)); } CP_WRITE(pjBase, DWG_FCOL, COLOR_REPLICATE(ppdev, iSolidColor)); CP_WRITE(pjBase, DWG_YDST, yStart + ppdev->yOffset); if (!(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; } else { if (prb->fl & RBRUSH_2COLOR) { ptd->pfnTrap = vHardwareTrapezoid; if ((rop4 & 0xff) == 0xf0) { ulDwg = opcode_TRAP + blockm_OFF + atype_RPL + bop_SRCCOPY; } else { ulHwMix = (rop4 & 0x03) + ((rop4 & 0x30) >> 2); ulDwg = opcode_TRAP + blockm_OFF + atype_RSTR + (ulHwMix << 16); } if (((rop4 >> 8) & 0xff) == (rop4 & 0xff)) { // Normal opaque mode: ulDwg |= transc_BG_OPAQUE; } else { // GDI guarantees us that if the foreground and background // ROPs are different, the background rop is LEAVEALONE: ulDwg |= transc_BG_TRANSP; } CHECK_FIFO_SPACE(pjBase, 9); CP_WRITE(pjBase, DWG_DWGCTL, ulDwg); CP_WRITE(pjBase, DWG_FCOL, COLOR_REPLICATE(ppdev, prb->ulColor[1])); CP_WRITE(pjBase, DWG_BCOL, COLOR_REPLICATE(ppdev, prb->ulColor[0])); CP_WRITE(pjBase, DWG_SRC0, prb->aulPattern[0]); CP_WRITE(pjBase, DWG_SRC1, prb->aulPattern[1]); CP_WRITE(pjBase, DWG_SRC2, prb->aulPattern[2]); CP_WRITE(pjBase, DWG_SRC3, prb->aulPattern[3]); CP_WRITE(pjBase, DWG_YDST, yStart + ppdev->yOffset); CP_WRITE(pjBase, DWG_SHIFT, ((-(pptlBrush->y + ppdev->yOffset) & 7) << 4) | (-(pptlBrush->x + ppdev->xOffset) & 7)); ppdev->HopeFlags = 0; } else { // We have to ensure that no other brush took our spot in off-screen // memory: ASSERTDD(ppdev->iBitmapFormat == BMF_8BPP, "Can only do 8bpp patterned fastfills"); if (prb->apbe[IBOARD(ppdev)]->prbVerify != prb) { vMgaPatRealize8bpp(ppdev, prb); } pjBase = ppdev->pjBase; pbe = prb->apbe[IBOARD(ppdev)]; ptd->pfnTrap = vMgaSoftwareTrapezoid; ptd->ulLinear = pbe->ulLinear; ptd->ptlBrush = *pptlBrush; CHECK_FIFO_SPACE(pjBase, 4); if (rop4 == 0xf0f0) // PATCOPY { CP_WRITE(pjBase, DWG_DWGCTL, (opcode_BITBLT + atype_RPL + blockm_OFF + trans_0 + bltmod_BFCOL + pattern_ON + transc_BG_OPAQUE + bop_SRCCOPY)); } else { RIP("Shouldn't allow ROPs for now, because of h/w bug!"); ulHwMix = (rop4 & 0x03) + ((rop4 & 0x30) >> 2); CP_WRITE(pjBase, DWG_DWGCTL, (opcode_BITBLT + atype_RSTR + blockm_OFF + trans_0 + bltmod_BFCOL + pattern_ON + transc_BG_OPAQUE + (ulHwMix << 16))); } if (!(GET_CACHE_FLAGS(ppdev, SIGN_CACHE))) { CP_WRITE(pjBase, DWG_SGN, 0); } ppdev->HopeFlags = SIGN_CACHE; CP_WRITE(pjBase, DWG_SHIFT, 0); CP_WRITE(pjBase, DWG_AR5, 32); } } if (prclClip != NULL) { ptd->pfnTrapClip = ptd->pfnTrap; ptd->pfnTrap = vClipTrapezoid; ptd->yClipTop = prclClip->top; ptd->yClipBottom = prclClip->bottom; CHECK_FIFO_SPACE(pjBase, 2); CP_WRITE(pjBase, DWG_CXLEFT, ppdev->xOffset + prclClip->left); CP_WRITE(pjBase, DWG_CXRIGHT, ppdev->xOffset + prclClip->right - 1); } } /******************************Public*Routine******************************\ * BOOL bFastFill * * 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, and no sorting of the edges is required. * * Note that NT's fill convention is different from that of Win 3.1 or Win95. * With the additional complication of fractional end-points, our convention * is the same as in 'X-Windows'. But a DDA is a DDA is a DDA, so once you * figure out how we compute the DDA terms for NT, you're golden. * * Returns TRUE if the polygon was drawn; FALSE if the polygon was complex. * \**************************************************************************/ BOOL bFastFill( PDEV* ppdev, LONG cEdges, // Includes close figure edge POINTFIX* pptfxFirst, ULONG rop4, ULONG iSolidColor, RBRUSH* prb, POINTL* pptlBrush, RECTL* prclClip) // NULL if no clipping { LONG yTrapezoid; // Top scan for next trapezoid LONG cyTrapezoid; // Number of scans in current trapezoid LONG yStart; // y-position of start point in current edge LONG dM; // Edge delta in FIX units in x direction LONG dN; // Edge delta in FIX units in y direction LONG i; LONG lCross; // Cross-product result POINTFIX* pptfxLast; // Points to the last point in the polygon array POINTFIX* pptfxTop; // Points to the top-most point in the polygon POINTFIX* pptfxOld; // Start point in current edge POINTFIX* pptfxScan; // Current edge pointer for finding pptfxTop LONG cScanEdges; // Number of edges scanned to find pptfxTop // (doesn't include the closefigure edge) LONG iEdge; LONG lQuotient; LONG lRemainder; BYTE* pjBase; TRAPEZOIDDATA td; // Edge data and stuff EDGEDATA* ped; // Points to current edge being processed ///////////////////////////////////////////////////////////////// // See if the polygon is convex pptfxScan = pptfxFirst; pptfxTop = pptfxFirst; // Assume for now that the first // point in path is the topmost pptfxLast = pptfxFirst + cEdges - 1; // Watch for close figure points, because we have the later restriction // that we won't allow coincident vertices: if ((pptfxLast->x == pptfxFirst->x) && (pptfxLast->y == pptfxFirst->y)) { pptfxLast--; cEdges--; } if (cEdges <= 2) goto ReturnTrue; // 'pptfxScan' will always point to the first point in the current // edge, and 'cScanEdges' will the number of edges remaining, including // the current one: cScanEdges = cEdges - 1; // The number of edges, not counting close figure if ((pptfxScan + 1)->y > pptfxScan->y) { // Collect all downs: do { if (--cScanEdges == 0) goto SetUpForFilling; pptfxScan++; } while ((pptfxScan + 1)->y >= pptfxScan->y); // Collect all ups: do { if (--cScanEdges == 0) goto SetUpForFillingCheck; pptfxScan++; } while ((pptfxScan + 1)->y <= pptfxScan->y); // Collect all downs: pptfxTop = pptfxScan; do { if ((pptfxScan + 1)->y > pptfxFirst->y) break; if (--cScanEdges == 0) goto SetUpForFilling; pptfxScan++; } while ((pptfxScan + 1)->y >= pptfxScan->y); goto ReturnFalse; } else { // Collect all ups: do { pptfxTop++; // We increment this now because we // want it to point to the very last // point if we early out in the next // statement... if (--cScanEdges == 0) goto SetUpForFilling; } while ((pptfxTop + 1)->y <= pptfxTop->y); // Collect all downs: pptfxScan = pptfxTop; do { if (--cScanEdges == 0) goto SetUpForFilling; pptfxScan++; } while ((pptfxScan + 1)->y >= pptfxScan->y); // Collect all ups: do { if ((pptfxScan + 1)->y < pptfxFirst->y) break; if (--cScanEdges == 0) goto SetUpForFilling; pptfxScan++; } while ((pptfxScan + 1)->y <= pptfxScan->y); goto ReturnFalse; } SetUpForFillingCheck: // We check to see if the end of the current edge is higher // than the top edge we've found so far: if ((pptfxScan + 1)->y < pptfxTop->y) pptfxTop = pptfxScan + 1; SetUpForFilling: pptfxScan = pptfxFirst; cScanEdges = cEdges - 2; // NOTE: For a bit of speed and simplicity, we will assume that // our cross product calculations will not overflow. A // consequence of this is that the caller MUST ensure that // the bounds of the polygon are small enough that there // will be no overflow. lCross = (((pptfxScan + 1)->x - (pptfxScan + 0)->x) * ((pptfxScan + 2)->y - (pptfxScan + 1)->y) - ((pptfxScan + 1)->y - (pptfxScan + 0)->y) * ((pptfxScan + 2)->x - (pptfxScan + 1)->x)); if (lCross == 0) { // We don't allow any colinear points into FastFill. We do this // here because we would need a non-zero cross product to determine // which direction the rest of the edges should go. We do this // later so that coincident vertices will never mess us up by // hiding a cross product sign change. goto ReturnFalse; } else if (lCross > 0) { // Make sure all cross products are positive: pptfxScan++; while (--cScanEdges != 0) { if (((pptfxScan + 1)->x - (pptfxScan + 0)->x) * ((pptfxScan + 2)->y - (pptfxScan + 1)->y) - ((pptfxScan + 1)->y - (pptfxScan + 0)->y) * ((pptfxScan + 2)->x - (pptfxScan + 1)->x) <= 0) { goto ReturnFalse; } pptfxScan++; } // Check the angles formed by the closefigure edge: if (((pptfxScan + 1)->x - (pptfxScan + 0)->x) * ((pptfxFirst )->y - (pptfxScan + 1)->y) - ((pptfxScan + 1)->y - (pptfxScan + 0)->y) * ((pptfxFirst )->x - (pptfxScan + 1)->x) <= 0) { goto ReturnFalse; } if (((pptfxFirst )->x - (pptfxScan + 1)->x) * ((pptfxFirst + 1)->y - (pptfxFirst )->y) - ((pptfxFirst )->y - (pptfxScan + 1)->y) * ((pptfxFirst + 1)->x - (pptfxFirst )->x) <= 0) { goto ReturnFalse; } // The figure has its points ordered in a clockwise direction: td.aed[LEFT].dptfx = -(LONG) sizeof(POINTFIX); td.aed[RIGHT].dptfx = sizeof(POINTFIX); } else { // Make sure all cross products are negative: pptfxScan++; while (--cScanEdges != 0) { if (((pptfxScan + 1)->x - (pptfxScan + 0)->x) * ((pptfxScan + 2)->y - (pptfxScan + 1)->y) - ((pptfxScan + 1)->y - (pptfxScan + 0)->y) * ((pptfxScan + 2)->x - (pptfxScan + 1)->x) >= 0) { goto ReturnFalse; } pptfxScan++; } // Check the angles formed by the closefigure edge: if (((pptfxScan + 1)->x - (pptfxScan + 0)->x) * ((pptfxFirst )->y - (pptfxScan + 1)->y) - ((pptfxScan + 1)->y - (pptfxScan + 0)->y) * ((pptfxFirst )->x - (pptfxScan + 1)->x) >= 0) { goto ReturnFalse; } if (((pptfxFirst )->x - (pptfxScan + 1)->x) * ((pptfxFirst + 1)->y - (pptfxFirst )->y) - ((pptfxFirst )->y - (pptfxScan + 1)->y) * ((pptfxFirst + 1)->x - (pptfxFirst )->x) >= 0) { goto ReturnFalse; } // The figure has its points ordered in a counter-clockwise direction: td.aed[LEFT].dptfx = sizeof(POINTFIX); td.aed[RIGHT].dptfx = -(LONG) sizeof(POINTFIX); } ///////////////////////////////////////////////////////////////// // Some Initialization td.aed[LEFT].pptfx = pptfxTop; td.aed[RIGHT].pptfx = pptfxTop; yTrapezoid = (pptfxTop->y + 15) >> 4; // Make sure we initialize the DDAs appropriately: td.aed[LEFT].cy = 0; td.aed[RIGHT].cy = 0; if (ppdev->ulBoardId == MGA_STORM) { vMilTrapezoidSetup(ppdev, rop4, iSolidColor, prb, pptlBrush, &td, yTrapezoid, prclClip); } else { vMgaTrapezoidSetup(ppdev, rop4, iSolidColor, prb, pptlBrush, &td, yTrapezoid, prclClip); } NewTrapezoid: ///////////////////////////////////////////////////////////////// // DDA initialization for (iEdge = 1; iEdge >= 0; iEdge--) { ped = &td.aed[iEdge]; ped->bNew = FALSE; if (ped->cy == 0) { // Our trapezoid drawing routine may want to be notified when // it will have to reset its DDA to start a new edge: ped->bNew = TRUE; // Need a new DDA: do { cEdges--; if (cEdges < 0) goto ResetClippingAndReturnTrue; // Find the next left edge, accounting for wrapping: pptfxOld = ped->pptfx; ped->pptfx = (POINTFIX*) ((BYTE*) ped->pptfx + ped->dptfx); if (ped->pptfx < pptfxFirst) ped->pptfx = pptfxLast; else if (ped->pptfx > pptfxLast) ped->pptfx = pptfxFirst; // Have to find the edge that spans yTrapezoid: ped->cy = ((ped->pptfx->y + 15) >> 4) - yTrapezoid; // With fractional coordinate end points, we may get edges // that don't cross any scans, in which case we try the // next one: } while (ped->cy <= 0); // 'pptfx' now points to the end point of the edge spanning // the scan 'yTrapezoid'. dN = ped->pptfx->y - pptfxOld->y; dM = ped->pptfx->x - pptfxOld->x; ASSERTDD(dN > 0, "Should be going down only"); // Compute the DDA increment terms: ped->dM = dM; // Not used for software trapezoid if (dM < 0) { dM = -dM; if (dM < dN) // Can't be '<=' { ped->dx = -1; ped->lErrorUp = dN - dM; } else { QUOTIENT_REMAINDER(dM, dN, lQuotient, lRemainder); ped->dx = -lQuotient; // - dM / dN ped->lErrorUp = lRemainder; // dM % dN if (ped->lErrorUp > 0) { ped->dx--; ped->lErrorUp = dN - ped->lErrorUp; } } } else { if (dM < dN) // Can't be '<=' { ped->dx = 0; ped->lErrorUp = dM; } else { QUOTIENT_REMAINDER(dM, dN, lQuotient, lRemainder); ped->dx = lQuotient; // dM / dN ped->lErrorUp = lRemainder; // dM % dN } } ped->dN = dN; // DDA limit ped->lError = -1; // Error is initially zero (add dN - 1 for // the ceiling, but subtract off dN so that // we can check the sign instead of comparing // to dN) ped->x = pptfxOld->x; yStart = pptfxOld->y; if ((yStart & 15) != 0) { // Advance to the next integer y coordinate for (i = 16 - (yStart & 15); i != 0; i--) { ped->x += ped->dx; ped->lError += ped->lErrorUp; if (ped->lError >= 0) { ped->lError -= ped->dN; ped->x++; } } } if ((ped->x & 15) != 0) { ped->lError -= ped->dN * (16 - (ped->x & 15)); ped->x += 15; // We'll want the ceiling in just a bit... } // Chop off those fractional bits: ped->x >>= 4; ped->lError >>= 4; } } cyTrapezoid = min(td.aed[LEFT].cy, td.aed[RIGHT].cy); // # of scans in this trap td.aed[LEFT].cy -= cyTrapezoid; td.aed[RIGHT].cy -= cyTrapezoid; td.pfnTrap(&td, yTrapezoid, cyTrapezoid); yTrapezoid += cyTrapezoid; goto NewTrapezoid; ResetClippingAndReturnTrue: if (prclClip != NULL) { pjBase = ppdev->pjBase; CHECK_FIFO_SPACE(pjBase, 2); CP_WRITE(pjBase, DWG_CXLEFT, 0); CP_WRITE(pjBase, DWG_CXRIGHT, ppdev->cxMemory - 1); } ReturnTrue: return(TRUE); ReturnFalse: return(FALSE); }