|
|
/******************************Module*Header**********************************\
* * ******************* * * GDI SAMPLE CODE * * ******************* * * Module Name: pxrxPoly.c * * Content: Draws polygons. * * Copyright (c) 1994-1999 3Dlabs Inc. Ltd. All rights reserved. * Copyright (c) 1995-2003 Microsoft Corporation. All rights reserved. \*****************************************************************************/
#define DBG_TRACK_CODE 0
#include "precomp.h"
#include "pxrx.h"
#define DO_SPANONLY_VERSION 0
#define RIGHT 0
#define LEFT 1
#define ABS(a) ((a) < 0 ? -(a) : (a))
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 lErrorDown; // DDA error adjustment
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
} EDGEDATA;
#define GetMoreFifoEntries( numberNeeded ) \
do { \ nSpaces -= numberNeeded; \ if( nSpaces <= 0 ) { \ do { \ nSpaces = 10 + numberNeeded; \ WAIT_FREE_PXRX_DMA_TAGS( nSpaces ); \ nSpaces -= numberNeeded; \ } while( nSpaces <= 0 ); \ } \ } while(0)
/*
#define SETUP_COLOUR_STUFF do { setupColourStuff(ppdev, glintInfo, \
&fgColor, &fgLogicOp, \ &bgColor, &bgLogicOp, \ prb, pptlBrush, \ &config2D, &renderMsg, &invalidatedFGBG); } while(0)
static void setupColourStuff( PDEV *ppdev, GlintDataRec *glintInfo, ULONG *fgColor_In, ULONG *fgLogicOp_In, ULONG *bgColor_In, ULONG *bgLogicOp_In, RBRUSH *prb, POINTL *pptlBrush, ULONG *config2D_In, ULONG *renderMsg_In, ULONG *invalidatedFGBG_In ) { ULONG fgColor = *fgColor_In, fgLogicOp = *fgLogicOp_In; ULONG bgColor = *bgColor_In, bgLogicOp = *bgLogicOp_In; ULONG config2D = *config2D_In, renderMsg = *renderMsg_In; ULONG invalidatedFGBG = *invalidatedFGBG_In; TEMP_MACRO_VARS; */
#define SETUP_COLOUR_STUFF \
do { \ SET_WRITE_BUFFERS; \ \ if( fgColor != 0xFFFFFFFF ) { \ WAIT_PXRX_DMA_TAGS( 4 ); \ /* Solid colour filled polygon */ \ if( (fgLogicOp == __GLINT_LOGICOP_COPY) && \ (ppdev->cPelSize != GLINTDEPTH8) && (ppdev->cPelSize != GLINTDEPTH32) ) { \ config2D |= __CONFIG2D_CONSTANTSRC; \ } else { \ config2D |= __CONFIG2D_LOGOP_FORE(fgLogicOp) | __CONFIG2D_CONSTANTSRC; \ renderMsg |= __RENDER_VARIABLE_SPANS; \ \ if( LogicopReadDest[fgLogicOp] ) { \ config2D |= __CONFIG2D_FBDESTREAD; \ SET_READ_BUFFERS; \ } \ } \ \ if( LogicOpReadSrc[fgLogicOp] ) \ LOAD_FOREGROUNDCOLOUR( fgColor ); \ \ DISPDBG((DBGLVL, "bGlintFastFillPolygon: solid fill, col = 0x%08x, logicOp = %d", fgColor, fgLogicOp)); \ } else { \ /* Brush filled polygon */ \ BRUSHENTRY *pbe; \ \ pbe = prb->apbe; \ \ if( prb->fl & RBRUSH_2COLOR ) { \ /* Monochrome brush */ \ config2D |= __CONFIG2D_CONSTANTSRC; \ renderMsg |= __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. */ \ if( (pbe == NULL) || (pbe->prbVerify != prb) ) { \ DISPDBG((DBGLVL, "full brush realise")); \ (*ppdev->pgfnPatRealize)(ppdev, prb, pptlBrush); \ } else if( (prb->ptlBrushOrg.x != pptlBrush->x) || \ (prb->ptlBrushOrg.y != pptlBrush->y) ) { \ DISPDBG((DBGLVL, "changing brush offset")); \ (*ppdev->pgfnMonoOffset)(ppdev, prb, pptlBrush); \ } \ \ fgColor = prb->ulForeColor; \ bgColor = prb->ulBackColor; \ \ if( ((bgLogicOp == __GLINT_LOGICOP_AND) && (bgColor == ppdev->ulWhite)) \ || ((bgLogicOp == __GLINT_LOGICOP_OR ) && (bgColor == 0)) \ || ((bgLogicOp == __GLINT_LOGICOP_XOR) && (bgColor == 0)) ) \ bgLogicOp = __GLINT_LOGICOP_NOOP; \ \ if( ((fgLogicOp != __GLINT_LOGICOP_COPY) || (bgLogicOp != __GLINT_LOGICOP_NOOP)) || \ (ppdev->cPelSize == GLINTDEPTH32) || (ppdev->cPelSize == GLINTDEPTH8) ) { \ config2D |= __CONFIG2D_OPAQUESPANS | __CONFIG2D_LOGOP_FORE(fgLogicOp) | __CONFIG2D_LOGOP_BACK(bgLogicOp); \ renderMsg |= __RENDER_VARIABLE_SPANS; \ } \ \ WAIT_PXRX_DMA_TAGS( 5 ); \ \ if( LogicopReadDest[fgLogicOp] || LogicopReadDest[bgLogicOp] ) { \ config2D |= __CONFIG2D_FBDESTREAD; \ SET_READ_BUFFERS; \ } \ \ if( LogicOpReadSrc[fgLogicOp] ) \ LOAD_FOREGROUNDCOLOUR( fgColor ); \ if( LogicOpReadSrc[bgLogicOp] ) \ LOAD_BACKGROUNDCOLOUR( bgColor ); \ \ DISPDBG((DBGLVL, "bGlintFastFillPolygon: mono pat fill, col = 0x%08x:0x%08x, logicOp = %d:%d", \ fgColor, bgColor, fgLogicOp, bgLogicOp)); \ } else { \ /* Colour brush */ \ POINTL brushOrg; \ \ brushOrg = *pptlBrush; \ if( (fgLogicOp == __GLINT_LOGICOP_COPY) && (ppdev->cPelSize != 0) ) \ brushOrg.x += (8 - (ppdev->xyOffsetDst & 0xFFFF)) & 7; \ \ if( (ppdev->PalLUTType != LUTCACHE_BRUSH) || (pbe == NULL) || (pbe->prbVerify != prb) ) { \ DISPDBG((DBGLVL, "realising brush")); \ (*ppdev->pgfnPatRealize)(ppdev, prb, &brushOrg); \ } else \ if( (prb->ptlBrushOrg.x != brushOrg.x) || (prb->ptlBrushOrg.y != brushOrg.y) || \ (prb->patternBase != ((glintInfo->lutMode >> 18) & 255)) ) { \ ULONG lutMode = glintInfo->lutMode; \ \ DISPDBG((DBGLVL, "resetting LUTMode")); \ \ prb->ptlBrushOrg.x = brushOrg.x; \ prb->ptlBrushOrg.y = brushOrg.y; \ \ DISPDBG((DBGLVL, "setting new LUT offset to %d, %d", (8 - prb->ptlBrushOrg.x) & 7, (8 - prb->ptlBrushOrg.y) & 7)); \ \ lutMode &= ~((7 << 8) | (7 << 12) | (7 << 15) | (255 << 18) | (1 << 26) | (1 << 27)); \ lutMode |= (1 << 8) | (1 << 27) | (prb->patternBase << 18) | \ (((8 - prb->ptlBrushOrg.x) & 7) << 12) | (((8 - prb->ptlBrushOrg.y) & 7) << 15); \ WAIT_PXRX_DMA_TAGS( 1 ); \ LOAD_LUTMODE( lutMode ); \ } else { \ /* we're cached already! */ \ DISPDBG((DBGLVL, "reusing LUT for brush @ %d, origin = (%d,%d)", prb->patternBase, prb->ptlBrushOrg.x, prb->ptlBrushOrg.y)); \ } \ \ WAIT_PXRX_DMA_TAGS( 4 ); \ if( (glintInfo->pxrxFlags & PXRX_FLAGS_DUAL_WRITING) || \ (glintInfo->pxrxFlags & PXRX_FLAGS_STEREO_WRITE) || \ (ppdev->cPelSize == GLINTDEPTH8) || (ppdev->cPelSize == GLINTDEPTH32) ) { \ config2D |= config2D_FillColourDual[fgLogicOp]; \ \ if( LogicopReadDest[fgLogicOp] ) \ SET_READ_BUFFERS; \ \ renderMsg |= __RENDER_VARIABLE_SPANS; \ } else { \ config2D |= config2D_FillColour[fgLogicOp]; \ \ if( fgLogicOp != __GLINT_LOGICOP_COPY ) \ renderMsg |= __RENDER_VARIABLE_SPANS; \ \ if( LogicopReadDest[fgLogicOp] ) { \ SET_READ_BUFFERS; \ } else if( fgLogicOp == __GLINT_LOGICOP_COPY ) { \ LOAD_FBWRITE_ADDR( 1, ppdev->DstPixelOrigin ); \ LOAD_FBWRITE_WIDTH( 1, ppdev->DstPixelDelta ); \ LOAD_FBWRITE_OFFSET( 1, ppdev->xyOffsetDst ); \ } \ } \ \ if( config2D & __CONFIG2D_LUTENABLE ) \ invalidatedFGBG = TRUE; \ \ DISPDBG((DBGLVL, "bGlintFastFillPolygon: colour pat fill, patBase = %d, logicOp = %d", prb->patternBase, fgLogicOp)); \ } \ } \ \ WAIT_PXRX_DMA_TAGS( 1 ); \ LOAD_CONFIG2D( config2D ); \ nSpaces = 0; \ } while( 0 )
/*
; *fgColor_In = fgColor; *fgLogicOp_In = fgLogicOp; *bgColor_In = bgColor; *bgLogicOp_In = bgLogicOp; *config2D_In = config2D; *renderMsg_In = renderMsg; *invalidatedFGBG_In = invalidatedFGBG; } */
/******************************Public*Routine******************************\
* BOOL bGlintFastFillPolygon * * 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 4.0. * 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. * * This routine handles patterns only when the glint 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 glint 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 GLINT Continue message could speed things up in certain * cases. * * Returns TRUE if the polygon was drawn; FALSE if the polygon was complex. * \**************************************************************************/
BOOL bGlintFastFillPolygon( PDEV *ppdev, LONG cEdges, // Includes close figure edge
POINTFIX *pptfxFirst, // Ptr to first point
ULONG fgColor, // Solid color fill
ULONG fgLogicOp, // Logical Operation to perform
ULONG bgLogicOp, // background logic op
CLIPOBJ *pco, // Clip Object.
RBRUSH *prb, POINTL *pptlBrush ) // Pattern alignment
{ 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
LONG cScanEdges; // Number of edges scanned to find pptfxTop
// (doesn't include the closefigure edge)
POINTFIX *pnt[2]; // DDA terms and stuff
POINTFIX *npnt[2]; // DDA terms and stuff
LONG dx[2], dy[2], gdx[2]; ULONG orx, ory; // all values ored, to eliminate complex polygons
LONG count; LONG nClips; // Number of clipping rectangles to render in
CLIPENUM *pClipRegion = (CLIPENUM *)(ppdev->pvTmpBuffer); RECTL *pClipList; // List of clip rects
LONG xOffFixed; ULONG bgColor; BOOL bTrivialClip, invalidatedFGBG = FALSE; BOOL invalidatedScissor = FALSE; ULONG config2D = __CONFIG2D_FBWRITE; ULONG renderMsg = __RENDER_TRAPEZOID_PRIMITIVE | __RENDER_FAST_FILL_ENABLE; LONG nSpaces; GLINT_DECL;
DISPDBG((DBGLVL, "bGlintFastFillPolygon: " "Checking polygon for renderability by glint")); ASSERTDD(cEdges > 1, "Polygon with less than 2 edges");
/////////////////////////////////////////////////////////////////
// See if the polygon is 'non-complex'
pptfxScan = pptfxFirst; pptfxTop = pptfxFirst; // Assume for now that the first
// point in path is the topmost
pptfxLast = pptfxFirst + cEdges - 1; orx = pptfxScan->x; ory = pptfxScan->y;
// '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 { ory |= (++pptfxScan)->y; orx |= pptfxScan->x; if( --cScanEdges == 0 ) { goto SetUpForFilling; } } while( (pptfxScan + 1)->y >= pptfxScan->y );
// Collect all ups:
do { ory |= (++pptfxScan)->y; orx |= pptfxScan->x; if( --cScanEdges == 0 ) { goto SetUpForFillingCheck; } } while( (pptfxScan + 1)->y <= pptfxScan->y );
// Collect all downs:
pptfxTop = pptfxScan;
do { if( (pptfxScan + 1)->y > pptfxFirst->y ) break;
ory |= (++pptfxScan)->y; orx |= pptfxScan->x; if( --cScanEdges == 0 ) { goto SetUpForFilling; } } while( (pptfxScan + 1)->y >= pptfxScan->y );
DISPDBG((DBGLVL, "Reject: GLINT can't fill down-up-down polygon")); return FALSE; } else { // Collect all ups:
do { ory |= (++pptfxTop)->y; // We increment this now because we
orx |= pptfxTop->x; // 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 { ory |= (++pptfxScan)->y; orx |= pptfxScan->x; if( --cScanEdges == 0 ) { goto SetUpForFilling; } } while( (pptfxScan + 1)->y >= pptfxScan->y );
// Collect all ups:
do { if( (pptfxScan + 1)->y < pptfxFirst->y ) { break; }
ory |= (++pptfxScan)->y; orx |= pptfxScan->x; if( --cScanEdges == 0 ) { goto SetUpForFilling; } } while( (pptfxScan + 1)->y <= pptfxScan->y );
DISPDBG((DBGLVL, "Reject: GLINT can't fill up-down-up polygon")); return FALSE; }
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->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( DO_SPANONLY_VERSION ) { goto BreakIntoSpans; }
if( (ory & 0xffffc00f) || (orx & 0xffff8000) ) { ULONG neg, posx, posy;
// fractional Y must be done as spans
if( ory & 0xf ) { goto BreakIntoSpans; }
// Run through all the vertices and check that none of them
// have a negative component less than -256.
neg = posx = posy = 0; for( pptfxScan = pptfxFirst; pptfxScan <= pptfxLast; pptfxScan++ ) { if( pptfxScan->x < 0 ) { neg |= -pptfxScan->x; } else { posx |= pptfxScan->x; } if( pptfxScan->y < 0 ) { neg |= -pptfxScan->y; } else { posy |= pptfxScan->y; } } // We don't want to handle any polygon with a negative vertex
// at <= -256 in either coordinate ???
if( neg & 0xfffff000 ) { DISPDBG((WRNLVL, "Coords out of range for fast fill")); return FALSE; } }
// The code can handle the polygon now. Lets go ahead and render it!
// Compiler gets its register allocation wrong. This forces it to redo them
GLINT_DECL_INIT;
DISPDBG((DBGLVL, "bGlintFastFillPolygon: " "Polygon is renderable. Go ahead and render"));
// Work out offset to add to each of the coords downloaded to GLINT
// To get correct results, we need to add on nearly one to each X
// coordinate.
// Also add on offsets to bitmap (This might be an off screen bitmap)
xOffFixed = INTtoFIXED(0) + NEARLY_ONE;
// determine how many passes we require to draw all the clip rects
if( bTrivialClip ) { // Just draw, no clipping to perform.
pClipList = NULL; // Indicate no clip list
nClips = 1; } else { if( pco->iDComplexity == DC_RECT ) { nClips = 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.
nClips = CLIPOBJ_cEnumStart(pco, FALSE, CT_RECTANGLES, CD_ANY, CLIP_LIMIT); if( nClips == -1 ) { return FALSE; // More than CLIP_LIMIT.
}
// Put the regions into our clip buffer
if( (CLIPOBJ_bEnum(pco, sizeof (CLIPENUM), (ULONG*)pClipRegion)) || (pClipRegion->c != nClips) ) { DISPDBG((DBGLVL, "bGlintFastFillPolygon: " "CLIPOBJ_bEnum inconsistency %d = %d", pClipRegion->c, nClips)); } pClipList = &(pClipRegion->arcl[0]); }
config2D |= __CONFIG2D_USERSCISSOR; }
SETUP_COLOUR_STUFF;
WAIT_PXRX_DMA_TAGS( 11 ); QUEUE_PXRX_DMA_TAG( __GlintTagdY, INTtoFIXED(1) );
DISPDBG((DBGLVL, "Rendering Polygon. %d clipping rectangles", nClips));
if( nClips && pClipList ) { invalidatedScissor = TRUE; }
// JME: check for 0 nClips //azn
if( nClips-- ) { while( 1 ) { // Need to set up clip rect each pass
if( pClipList ) { DISPDBG((DBGLVL, "Clip rect = (%d, %d -> %d, %d)", pClipList->left, pClipList->top, pClipList->right, pClipList->bottom)); QUEUE_PXRX_DMA_TAG( __GlintTagScissorMinXY, MAKEDWORD_XY(pClipList->left , pClipList->top ) ); QUEUE_PXRX_DMA_TAG( __GlintTagScissorMaxXY, MAKEDWORD_XY(pClipList->right, pClipList->bottom) ); pClipList++; }
// Initialize left and right points (current) to top point.
npnt[LEFT] = pptfxTop; npnt[RIGHT] = pptfxTop;
while( 1 ) { // npnt[] is always the valid point to draw from
do { pnt[LEFT] = npnt[LEFT]; npnt[LEFT] = pnt[LEFT] - 1;
if (npnt[LEFT] < pptfxFirst) { npnt[LEFT] = pptfxLast; }
// Special case of flat based polygon, need to break now
// as polygon is finished
if (npnt[LEFT] == npnt[RIGHT]) { goto FinishedPolygon; }
DISPDBG((DBGLVL, "LEFT: pnt %P npnt %P FIRST %P LAST %P", pnt[LEFT], npnt[LEFT], pptfxFirst, pptfxLast)); DISPDBG((DBGLVL, "x 0x%04X y 0x%04X Next: x 0x%04X y 0x%04X", pnt[LEFT]->x, pnt[LEFT]->y, npnt[LEFT]->x, npnt[LEFT]->y)); } while( pnt[LEFT]->y == npnt[LEFT]->y ); do { pnt[RIGHT] = npnt[RIGHT]; npnt[RIGHT] = pnt[RIGHT] + 1;
if (npnt[RIGHT] > pptfxLast) { npnt[RIGHT] = pptfxFirst; }
DISPDBG((DBGLVL, "RIGHT: pnt %P npnt %P FIRST %P LAST %P", pnt[RIGHT], npnt[RIGHT], pptfxFirst, pptfxLast)); DISPDBG((DBGLVL, "x 0x%04X y 0x%04X Next: x 0x%04X y 0x%04X", pnt[RIGHT]->x, pnt[RIGHT]->y, npnt[RIGHT]->x, npnt[RIGHT]->y)); } while( pnt[RIGHT]->y == npnt[RIGHT]->y ); // Start up new rectangle. Whenever we get to this code, both
// points should have equal y values, and need to be restarted.
DISPDBG((DBGLVL, "New: Top: (0x%04X, 0x%04X)->(0x%04X, 0x%04X)" " Next: (0x%04X, 0x%04X)->(0x%04X, 0x%04X)", pnt[LEFT]->x, pnt[LEFT]->y, pnt[RIGHT]->x, pnt[RIGHT]->y, npnt[LEFT]->x, npnt[LEFT]->y, npnt[RIGHT]->x, npnt[RIGHT]->y));
QUEUE_PXRX_DMA_TAG( __GlintTagStartXDom, FIXtoFIXED(pnt[LEFT]->x) + xOffFixed ); QUEUE_PXRX_DMA_TAG( __GlintTagStartXSub, FIXtoFIXED(pnt[RIGHT]->x) + xOffFixed); QUEUE_PXRX_DMA_TAG( __GlintTagStartY, FIXtoFIXED(pnt[RIGHT]->y) );
// 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
dx[LEFT] = (npnt[LEFT]->x - pnt[LEFT]->x) << 12; dy[LEFT] = (npnt[LEFT]->y - pnt[LEFT]->y) >> 4;
// Need to ensure we round delta down.
// divide rounds towards zero
if( dx[LEFT] < 0 ) { dx[LEFT] -= dy[LEFT] - 1; }
gdx[LEFT] = dx[LEFT] / dy[LEFT]; QUEUE_PXRX_DMA_TAG( __GlintTagdXDom, gdx[LEFT] );
dx[RIGHT] = (npnt[RIGHT]->x - pnt[RIGHT]->x) << 12; dy[RIGHT] = (npnt[RIGHT]->y - pnt[RIGHT]->y) >> 4;
// Need to ensure we round delta down.
// divide rounds towards zero
if( dx[RIGHT] < 0 ) { dx[RIGHT] -= dy[RIGHT] - 1; }
gdx[RIGHT] = dx[RIGHT] / dy[RIGHT]; QUEUE_PXRX_DMA_TAG( __GlintTagdXSub, gdx[RIGHT] );
// Work out number of scanlines to render
if (npnt[LEFT]->y < npnt[RIGHT]->y) { count = dy[LEFT]; } else { count = dy[RIGHT]; }
QUEUE_PXRX_DMA_TAG( __GlintTagCount, count ); QUEUE_PXRX_DMA_TAG( __GlintTagRender, renderMsg ); SEND_PXRX_DMA_BATCH;
// 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( (npnt[LEFT] != npnt[RIGHT]) && (npnt[LEFT]->y != npnt[RIGHT]->y) ) { // Some continues are required for next rectangle
if( npnt[LEFT]->y < npnt[RIGHT]->y ) { // We have reached npnt[LEFT]. npnt[RIGHT] is still ok
do { pnt[LEFT] = npnt[LEFT]; npnt[LEFT] = pnt[LEFT] - 1;
if (npnt[LEFT] < pptfxFirst) { npnt[LEFT] = pptfxLast; }
} while( pnt[LEFT]->y == npnt[LEFT]->y ); // We have a new npnt[LEFT] now.
DISPDBG((DBGLVL, "Dom: Top: x: %x y: %x " " Next: x: %x y: %x x: %x y: %x", pnt[LEFT]->x, pnt[LEFT]->y, npnt[LEFT]->x, npnt[LEFT]->y, npnt[RIGHT]->x, npnt[RIGHT]->y));
dx[LEFT] = (npnt[LEFT]->x - pnt[LEFT]->x) << 12; dy[LEFT] = (npnt[LEFT]->y - pnt[LEFT]->y) >> 4;
// Need to ensure we round delta down.
// divide rounds towards zero
if( dx[LEFT] < 0 ) { dx[LEFT] -= dy[LEFT] - 1; }
gdx[LEFT] = dx[LEFT] / dy[LEFT];
if( npnt[LEFT]->y < npnt[RIGHT]->y ) { count = dy[LEFT]; } else { count = (ABS(npnt[RIGHT]->y - pnt[LEFT]->y)) >> 4; }
WAIT_PXRX_DMA_TAGS( 3 ); QUEUE_PXRX_DMA_TAG( __GlintTagStartXDom, FIXtoFIXED(pnt[LEFT]->x) + xOffFixed ); QUEUE_PXRX_DMA_TAG( __GlintTagdXDom, gdx[LEFT] ); QUEUE_PXRX_DMA_TAG( __GlintTagContinueNewDom, count ); } else { // We have reached npnt[RIGHT]. npnt[LEFT] is still ok
do { pnt[RIGHT] = npnt[RIGHT]; npnt[RIGHT] = pnt[RIGHT] + 1;
if( npnt[RIGHT] > pptfxLast ) { npnt[RIGHT] = pptfxFirst; }
} while( pnt[RIGHT]->y == npnt[RIGHT]->y );
// We have a new npnt[RIGHT] now.
DISPDBG((DBGLVL, "Sub: Top: x: %x y: %x " " Next: x: %x y: %x x: %x y: %x", pnt[RIGHT]->x, pnt[RIGHT]->y, npnt[LEFT]->x, npnt[LEFT]->y, npnt[RIGHT]->x, npnt[RIGHT]->y)); dx[RIGHT] = (npnt[RIGHT]->x - pnt[RIGHT]->x) << 12; dy[RIGHT] = (npnt[RIGHT]->y - pnt[RIGHT]->y) >> 4;
// Need to ensure we round delta down.
// divide rounds towards zero
if( dx[RIGHT] < 0 ) { dx[RIGHT] -= dy[RIGHT] - 1; }
gdx[RIGHT] = dx[RIGHT] / dy[RIGHT];
if( npnt[RIGHT]->y < npnt[LEFT]->y ) { count = dy[RIGHT]; } else { count = (ABS(npnt[LEFT]->y - pnt[RIGHT]->y)) >> 4; }
WAIT_PXRX_DMA_TAGS( 3 ); QUEUE_PXRX_DMA_TAG( __GlintTagStartXSub, FIXtoFIXED(pnt[RIGHT]->x) + xOffFixed ); QUEUE_PXRX_DMA_TAG( __GlintTagdXSub, gdx[RIGHT] ); QUEUE_PXRX_DMA_TAG( __GlintTagContinueNewSub, count ); } }
// Repeatedly draw more trapezoids until points are equal
// If y values are equal, then we can start again from
// scratch.
if( npnt[LEFT] == npnt[RIGHT] ) { break; } WAIT_PXRX_DMA_TAGS( 7 ); // 7 entries needed to
// render first trapezoid
}
FinishedPolygon:
if( !nClips-- ) { break; }
// entries needed to clip and render first trapezoid
WAIT_PXRX_DMA_TAGS( 2 + 7 ); } // while
} // if
DISPDBG((DBGLVL, "bGlintFastFillPolygon: returning TRUE"));
// Need to send ContinueNewSub(0) to flush the framebuffer ??? //azn
if( invalidatedFGBG ) { WAIT_PXRX_DMA_DWORDS( 3 ); QUEUE_PXRX_DMA_INDEX2( __GlintTagForegroundColor, __GlintTagBackgroundColor ); QUEUE_PXRX_DMA_DWORD( glintInfo->foregroundColour ); QUEUE_PXRX_DMA_DWORD( glintInfo->backgroundColour ); }
if( (ppdev->cPelSize == GLINTDEPTH32) && invalidatedScissor ) { WAIT_PXRX_DMA_TAGS( 1 ); QUEUE_PXRX_DMA_TAG( __GlintTagScissorMaxXY, 0x7FFF7FFF ); }
SEND_PXRX_DMA_BATCH;
return TRUE;
/******************************************************************************/
// This is the code to break the polygon into spans.
BreakIntoSpans:
DISPDBG((DBGLVL, "Breaking into spans"));
{ LONG yTrapezoid; // Top scan for next trapezoid
LONG cyTrapezoid; // Number of scans in current trapezoid
POINTFIX* pptfxOld; // Start point in current edge
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 iEdge; LONG lQuotient; LONG lRemainder; LONG i; EDGEDATA aed[2];// DDA terms and stuff
EDGEDATA* ped; LONG tmpXl, tmpXr; DWORD continueMessage = 0;
// This span code cannot handle a clip list yet!
if( !bTrivialClip ) { return FALSE; }
DISPDBG((DBGLVL, "Starting Spans Code"));
/////////////////////////////////////////////////////////////////
// Some Initialization
yTrapezoid = (pptfxTop->y + 15) >> 4;
// Make sure we initialize the DDAs appropriately:
aed[LEFT].cy = 0; aed[RIGHT].cy = 0;
// For now, guess as to which is the left and which is the right edge:
aed[LEFT].dptfx = -(LONG) sizeof(POINTFIX); aed[RIGHT].dptfx = sizeof(POINTFIX); aed[LEFT].pptfx = pptfxTop; aed[RIGHT].pptfx = pptfxTop;
DISPDBG((DBGLVL, "bGlintFastFillPolygon: Polygon is renderable. " "Go ahead and render"));
// Work out offset to add to each of the coords downloaded to GLINT
// To get correct results, we need to add on nearly one to each X
// coordinate.
// Also add on offsets to bitmap (This might be an off screen bitmap)
xOffFixed = INTtoFIXED(0);
WAIT_PXRX_DMA_TAGS( 4 ); QUEUE_PXRX_DMA_TAG( __GlintTagCount, 0 ); QUEUE_PXRX_DMA_TAG( __GlintTagdXDom, 0 ); QUEUE_PXRX_DMA_TAG( __GlintTagdXSub, 0); QUEUE_PXRX_DMA_TAG( __GlintTagdY, INTtoFIXED(1));
DISPDBG((DBGLVL, "Rendering Polygon"));
nSpaces = 0; NewTrapezoid:
DISPDBG((DBGLVL, "New Trapezoid"));
/////////////////////////////////////////////////////////////////
// DDA initialization
for( iEdge = 1; iEdge >= 0; iEdge-- ) { ped = &aed[iEdge]; if( ped->cy == 0 ) { // Need a new DDA:
do { cEdges--; if( cEdges < 0 ) { DISPDBG((DBGLVL, "bGlintFastFillPolygon: " "returning TRUE")); return TRUE; }
// 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:
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->lErrorDown = 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->lErrorDown; ped->x++; } } }
if( (ped->x & 15) != 0 ) { ped->lError -= ped->lErrorDown * (16 - (ped->x & 15)); ped->x += 15; // We'll want the ceiling in just a bit...
}
// Chop off those fractional bits, convert to GLINT format
// and add in the bitmap offset:
ped->x = ped->x >> 4; ped->lError >>= 4;
// Convert to GLINT format positions and deltas
ped->x = INTtoFIXED(ped->x) + xOffFixed; ped->dx = INTtoFIXED(ped->dx); } }
cyTrapezoid = min(aed[LEFT].cy, aed[RIGHT].cy); // #scans in this trap
aed[LEFT].cy -= cyTrapezoid; aed[RIGHT].cy -= cyTrapezoid; yTrapezoid += cyTrapezoid; // Top scan in next trap
SETUP_COLOUR_STUFF;
// If the left and right edges are vertical, simply output as
// a rectangle:
DISPDBG((DBGLVL, "Generate spans for glint"));
do { GetMoreFifoEntries( 4 );
// Reset render position to the top of the trapezoid.
QUEUE_PXRX_DMA_TAG( __GlintTagStartXDom, aed[RIGHT].x ); QUEUE_PXRX_DMA_TAG( __GlintTagStartXSub, aed[LEFT].x ); QUEUE_PXRX_DMA_TAG( __GlintTagStartY, INTtoFIXED(yTrapezoid - cyTrapezoid) ); QUEUE_PXRX_DMA_TAG( __GlintTagRender, renderMsg ); SEND_PXRX_DMA_BATCH;
continueMessage = __GlintTagContinue;
if( ((aed[LEFT].lErrorUp | aed[RIGHT].lErrorUp) == 0) && ((aed[LEFT].dx | aed[RIGHT].dx) == 0) && (cyTrapezoid > 1) ) { //////////////////////////////////////////////////////////////
// Vertical-edge special case
DISPDBG((DBGLVL, "Vertical Edge Special Case"));
GetMoreFifoEntries( 1 ); QUEUE_PXRX_DMA_TAG( continueMessage, cyTrapezoid );
continue; }
while( TRUE ) { //////////////////////////////////////////////////////////////
// Run the DDAs
DISPDBG((DBGLVL, "Doing a span 0x%x to 0x%x, 0x%x scans left. " "Continue %s", aed[LEFT].x, aed[RIGHT].x, cyTrapezoid, (continueMessage == __GlintTagContinueNewDom) ? "NewDom" : ((continueMessage == __GlintTagContinue) ? "" : "NewSub")));
GetMoreFifoEntries( 1 ); QUEUE_PXRX_DMA_TAG( continueMessage, 1 );
// We have finished this trapezoid. Go get the next one!
// Advance the right wall:
tmpXr = aed[RIGHT].x; aed[RIGHT].x += aed[RIGHT].dx; aed[RIGHT].lError += aed[RIGHT].lErrorUp;
if( aed[RIGHT].lError >= 0 ) { aed[RIGHT].lError -= aed[RIGHT].lErrorDown; aed[RIGHT].x += INTtoFIXED(1); }
// Advance the left wall:
tmpXl = aed[LEFT].x; aed[LEFT].x += aed[LEFT].dx; aed[LEFT].lError += aed[LEFT].lErrorUp;
if( aed[LEFT].lError >= 0 ) { aed[LEFT].lError -= aed[LEFT].lErrorDown; aed[LEFT].x += INTtoFIXED(1); }
if( --cyTrapezoid == 0 ) { break; }
// Setup the GLINT X registers if we have changed either end.
if( tmpXr != aed[RIGHT].x ) { if( tmpXl != aed[LEFT].x ) { GetMoreFifoEntries( 3 );
QUEUE_PXRX_DMA_TAG( __GlintTagStartXSub, aed[LEFT].x ); QUEUE_PXRX_DMA_TAG( __GlintTagContinueNewSub, 0 ); } else { GetMoreFifoEntries( 1 ); } QUEUE_PXRX_DMA_TAG( __GlintTagStartXDom, aed[RIGHT].x ); continueMessage = __GlintTagContinueNewDom; } else if( tmpXl != aed[LEFT].x ) { GetMoreFifoEntries( 1 ); QUEUE_PXRX_DMA_TAG( __GlintTagStartXSub, aed[LEFT].x ); continueMessage = __GlintTagContinueNewSub; } } } while( 0 );
DISPDBG((DBGLVL, "Generate spans for glint done")); goto NewTrapezoid; }
if( invalidatedFGBG ) { WAIT_PXRX_DMA_DWORDS( 3 ); QUEUE_PXRX_DMA_INDEX2( __GlintTagForegroundColor, __GlintTagBackgroundColor ); QUEUE_PXRX_DMA_DWORD( glintInfo->foregroundColour ); QUEUE_PXRX_DMA_DWORD( glintInfo->backgroundColour ); }
SEND_PXRX_DMA_BATCH;
return TRUE; }
|