You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1380 lines
50 KiB
1380 lines
50 KiB
/******************************************************************************\
|
|
*
|
|
* $Workfile: FILLPATH.C $
|
|
*
|
|
* Contains the DrvFillPath routine.
|
|
*
|
|
* Copyright (c) 1992-1995 Microsoft Corporation
|
|
* Copyright (c) 1996 Cirrus Logic, Inc.
|
|
*
|
|
* $Log: X:/log/laguna/nt35/displays/cl546x/FILLPATH.C $
|
|
*
|
|
* Rev 1.14 Mar 04 1998 15:24:00 frido
|
|
* Added new shadow macros.
|
|
*
|
|
* Rev 1.13 Nov 03 1997 15:26:36 frido
|
|
* Added REQUIRE macros.
|
|
*
|
|
* Rev 1.12 08 Apr 1997 12:24:28 einkauf
|
|
*
|
|
* add SYNC_W_3D to coordinate MCD/2D HW access
|
|
*
|
|
* Rev 1.11 21 Mar 1997 11:41:44 noelv
|
|
*
|
|
* Combined do_flag and sw_test_flag into point_switch
|
|
*
|
|
* Rev 1.10 17 Dec 1996 17:04:26 SueS
|
|
* Added test for writing to log file based on cursor at (0,0). Added
|
|
* more information to the log file.
|
|
*
|
|
* Rev 1.9 26 Nov 1996 10:46:10 noelv
|
|
* Changed DBG LEVEL.
|
|
*
|
|
* Rev 1.8 26 Nov 1996 10:24:10 SueS
|
|
* Changed WriteLogFile parameters for buffering.
|
|
*
|
|
* Rev 1.7 13 Nov 1996 15:58:52 SueS
|
|
* Changed WriteFile calls to WriteLogFile.
|
|
*
|
|
* Rev 1.6 06 Sep 1996 14:46:24 noelv
|
|
*
|
|
* Updated NULL driver code for 4.0
|
|
*
|
|
* Rev 1.5 20 Aug 1996 11:03:32 noelv
|
|
* Bugfix release from Frido 8-19-96
|
|
*
|
|
* Rev 1.2 17 Aug 1996 15:32:30 frido
|
|
* #1244 - Fixed brush rotation for off-screen bitmaps.
|
|
* Added new comment header.
|
|
* Cleaned up some code.
|
|
*
|
|
\******************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
|
|
BOOL CacheMono(PPDEV ppdev, PRBRUSH pRbrush);
|
|
BOOL Cache4BPP(PPDEV ppdev, PRBRUSH pRbrush);
|
|
BOOL CacheDither(PPDEV ppdev, PRBRUSH pRbrush);
|
|
BOOL CacheBrush(PPDEV ppdev, PRBRUSH pRbrush);
|
|
|
|
// We have to be careful of arithmetic overflow in a number of places.
|
|
// Fortunately, the compiler is guaranteed to natively support 64-bit signed
|
|
// LONGLONGs and 64-bit unsigned DWORDLONGs.
|
|
//
|
|
// Int32x32To64(a, b) is a macro defined in 'winnt.h' that multiplies two
|
|
// 32-bit LONGs to produce a 64-bit LONGLONG result. I use it because it is
|
|
// much faster than 64x64 multiplies.
|
|
|
|
#define UInt64Div32To32(a, b) \
|
|
((((DWORDLONG)(a)) > ULONG_MAX) ? \
|
|
(ULONG)((DWORDLONG)(a) / (ULONG)(b)) : \
|
|
(ULONG)((ULONG)(a) / (ULONG)(b)))
|
|
|
|
#define TAKING_ALLOC_STATS 0
|
|
|
|
#define NUM_BUFFER_POINTS 96 // Maximum number of points in a path for
|
|
// which we'll attempt to join all the path
|
|
// records so that the path may still be
|
|
// drawn by FastFill
|
|
|
|
#if TAKING_ALLOC_STATS
|
|
ULONG BufferHitInFillpath = 0;
|
|
ULONG BufferMissInFillpath = 0;
|
|
#endif
|
|
|
|
#if LOG_CALLS
|
|
VOID LogFillPath(ULONG acc, PPDEV ppdev, SURFOBJ* pso);
|
|
#else
|
|
#define LogFillPath(acc, ppdev, pso)
|
|
#endif
|
|
|
|
// Describe a single non-horizontal edge of a path to fill.
|
|
typedef struct _EDGE {
|
|
PVOID pNext;
|
|
INT iScansLeft;
|
|
INT X;
|
|
INT Y;
|
|
INT iErrorTerm;
|
|
INT iErrorAdjustUp;
|
|
INT iErrorAdjustDown;
|
|
INT iXWhole;
|
|
INT iXDirection;
|
|
INT iWindingDirection;
|
|
} EDGE, *PEDGE;
|
|
|
|
// Maximum number of rects we'll fill per call to the fill code.
|
|
#define MAX_PATH_RECTS 50
|
|
#define RECT_BYTES (MAX_PATH_RECTS * sizeof(RECTL))
|
|
#define EDGE_BYTES (TMP_BUFFER_SIZE - RECT_BYTES)
|
|
#define MAX_EDGES (EDGE_BYTES/sizeof(EDGE))
|
|
|
|
#define FILLPATH_DBG_LEVEL 1
|
|
|
|
// MIX translation table. Translates a mix 1-16, into an old style ROP 0-255.
|
|
extern BYTE gaMix[];
|
|
|
|
VOID AdvanceAETEdges(EDGE* pAETHead);
|
|
VOID XSortAETEdges(EDGE* pAETHead);
|
|
VOID MoveNewEdges(EDGE* pGETHead, EDGE* pAETHead, INT iCurrentY);
|
|
EDGE* AddEdgeToGET(EDGE* pGETHead, EDGE* pFreeEdge, POINTFIX* ppfxEdgeStart,
|
|
POINTFIX* ppfxEdgeEnd, RECTL* pClipRect);
|
|
BOOL ConstructGET(EDGE* pGETHead, EDGE* pFreeEdges, PATHOBJ* ppo,
|
|
PATHDATA* pd, BOOL bMore, RECTL* pClipRect);
|
|
VOID AdjustErrorTerm(INT* pErrorTerm, INT iErrorAdjustUp,
|
|
INT iErrorAdjustDown, INT yJump, INT* pXStart,
|
|
INT iXDirection);
|
|
|
|
extern BYTE Rop2ToRop3[];
|
|
|
|
BYTE gajRop[] =
|
|
{
|
|
0x00, 0xff, 0xb2, 0x4d, 0xd4, 0x2b, 0x66, 0x99,
|
|
0x90, 0x6f, 0x22, 0xdd, 0x44, 0xbb, 0xf6, 0x09,
|
|
0xe8, 0x17, 0x5a, 0xa5, 0x3c, 0xc3, 0x8e, 0x71,
|
|
0x78, 0x87, 0xca, 0x35, 0xac, 0x53, 0x1e, 0xe1,
|
|
0xa0, 0x5f, 0x12, 0xed, 0x74, 0x8b, 0xc6, 0x39,
|
|
0x30, 0xcf, 0x82, 0x7d, 0xe4, 0x1b, 0x56, 0xa9,
|
|
0x48, 0xb7, 0xfa, 0x05, 0x9c, 0x63, 0x2e, 0xd1,
|
|
0xd8, 0x27, 0x6a, 0x95, 0x0c, 0xf3, 0xbe, 0x41,
|
|
0xc0, 0x3f, 0x72, 0x8d, 0x14, 0xeb, 0xa6, 0x59,
|
|
0x50, 0xaf, 0xe2, 0x1d, 0x84, 0x7b, 0x36, 0xc9,
|
|
0x28, 0xd7, 0x9a, 0x65, 0xfc, 0x03, 0x4e, 0xb1,
|
|
0xb8, 0x47, 0x0a, 0xf5, 0x6c, 0x93, 0xde, 0x21,
|
|
0x60, 0x9f, 0xd2, 0x2d, 0xb4, 0x4b, 0x06, 0xf9,
|
|
0xf0, 0x0f, 0x42, 0xbd, 0x24, 0xdb, 0x96, 0x69,
|
|
0x88, 0x77, 0x3a, 0xc5, 0x5c, 0xa3, 0xee, 0x11,
|
|
0x18, 0xe7, 0xaa, 0x55, 0xcc, 0x33, 0x7e, 0x81,
|
|
0x80, 0x7f, 0x32, 0xcd, 0x54, 0xab, 0xe6, 0x19,
|
|
0x10, 0xef, 0xa2, 0x5d, 0xc4, 0x3b, 0x76, 0x89,
|
|
0x68, 0x97, 0xda, 0x25, 0xbc, 0x43, 0x0e, 0xf1,
|
|
0xf8, 0x07, 0x4a, 0xb5, 0x2c, 0xd3, 0x9e, 0x61,
|
|
0x20, 0xdf, 0x92, 0x6d, 0xf4, 0x0b, 0x46, 0xb9,
|
|
0xb0, 0x4f, 0x02, 0xfd, 0x64, 0x9b, 0xd6, 0x29,
|
|
0xc8, 0x37, 0x7a, 0x85, 0x1c, 0xe3, 0xae, 0x51,
|
|
0x58, 0xa7, 0xea, 0x15, 0x8c, 0x73, 0x3e, 0xc1,
|
|
0x40, 0xbf, 0xf2, 0x0d, 0x94, 0x6b, 0x26, 0xd9,
|
|
0xd0, 0x2f, 0x62, 0x9d, 0x04, 0xfb, 0xb6, 0x49,
|
|
0xa8, 0x57, 0x1a, 0xe5, 0x7c, 0x83, 0xce, 0x31,
|
|
0x38, 0xc7, 0x8a, 0x75, 0xec, 0x13, 0x5e, 0xa1,
|
|
0xe0, 0x1f, 0x52, 0xad, 0x34, 0xcb, 0x86, 0x79,
|
|
0x70, 0x8f, 0xc2, 0x3d, 0xa4, 0x5b, 0x16, 0xe9,
|
|
0x08, 0xf7, 0xba, 0x45, 0xdc, 0x23, 0x6e, 0x91,
|
|
0x98, 0x67, 0x2a, 0xd5, 0x4c, 0xb3, 0xfe, 0x01
|
|
};
|
|
|
|
|
|
/******************************Public*Routine******************************\
|
|
* DrvFillPath
|
|
*
|
|
* Fill the specified path with the specified brush and ROP. This routine
|
|
* detects single convex polygons, and will call to separate faster convex
|
|
* polygon code for those cases. This routine also detects polygons that
|
|
* are really rectangles, and handles those separately as well.
|
|
*
|
|
* Note: Multiple polygons in a path cannot be treated as being disjoint;
|
|
* the fill must consider all the points in the path. That is, if the
|
|
* path contains multiple polygons, you cannot simply draw one polygon
|
|
* after the other (unless they don't overlap).
|
|
*
|
|
* Note: This function is optional, but is recommended for good performance.
|
|
* To get GDI to call this function, not only do you have to
|
|
* HOOK_FILLPATH, you have to set GCAPS_ALTERNATEFILL and/or
|
|
* GCAPS_WINDINGFILL.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL DrvFillPath(
|
|
SURFOBJ* pso,
|
|
PATHOBJ* ppo,
|
|
CLIPOBJ* pco,
|
|
BRUSHOBJ* pbo,
|
|
POINTL* pptlBrush,
|
|
MIX mix,
|
|
FLONG flOptions)
|
|
{
|
|
BYTE jClipping; // clipping type
|
|
EDGE *pCurrentEdge;
|
|
EDGE AETHead; // dummy head/tail node & sentinel for Active Edge Table
|
|
EDGE *pAETHead; // pointer to AETHead
|
|
EDGE GETHead; // dummy head/tail node & sentinel for Global Edge Table
|
|
EDGE *pGETHead; // pointer to GETHead
|
|
EDGE *pFreeEdges; // pointer to memory free for use to store edges
|
|
ULONG ulNumRects; // # of rectangles to draw currently in rectangle list
|
|
RECTL *prclRects; // pointer to start of rectangle draw list
|
|
INT iCurrentY; // scan line for which we're currently scanning out the
|
|
// fill
|
|
|
|
ULONG uRop; // Hardware foreground mix value
|
|
ULONG uRopb; // Hardware background mix value
|
|
ULONG avec; // A-vector notation for ternary rop
|
|
ULONG iSolidColor; // Copy of pbo->iSolidColor
|
|
FNFILL *pfnFill; // Points to appropriate fill routine
|
|
BOOL bRealizeTransparent; // Need a transparent realization for Rop
|
|
|
|
BOOL bSolid;
|
|
BOOL bMore;
|
|
PATHDATA pd;
|
|
RECTL ClipRect;
|
|
PDEV *ppdev;
|
|
|
|
BOOL bRetVal=FALSE; // FALSE until proven TRUE
|
|
BOOL bMemAlloced=FALSE; // FALSE until proven TRUE
|
|
|
|
FLONG flFirstRecord;
|
|
POINTFIX* pptfxTmp;
|
|
ULONG cptfxTmp;
|
|
POINTFIX aptfxBuf[NUM_BUFFER_POINTS];
|
|
ULONG ulBltDef = 0x1000;
|
|
|
|
|
|
#if NULL_PATH
|
|
{
|
|
if (pointer_switch) return(TRUE);
|
|
}
|
|
#endif
|
|
|
|
DISPDBG((FILLPATH_DBG_LEVEL,"DrvFillPath\n"));
|
|
|
|
// Set up the clipping
|
|
if (pco == (CLIPOBJ *) NULL) {
|
|
// No CLIPOBJ provided, so we don't have to worry about clipping
|
|
jClipping = DC_TRIVIAL;
|
|
} else {
|
|
// Use the CLIPOBJ-provided clipping
|
|
jClipping = pco->iDComplexity;
|
|
}
|
|
|
|
if (jClipping != DC_TRIVIAL) {
|
|
if (jClipping != DC_RECT) {
|
|
DISPDBG((FILLPATH_DBG_LEVEL,"Complex Clipping Early Out\n"));
|
|
#if LOG_CALLS
|
|
ppdev = (PDEV*) pso->dhpdev;
|
|
LogFillPath(2, ppdev, NULL);
|
|
#endif
|
|
goto ReturnFalse; // there is complex clipping; let GDI fill the path
|
|
}
|
|
// Clip to the clip rectangle
|
|
ClipRect = pco->rclBounds;
|
|
} else {
|
|
// So the y-clipping code doesn't do any clipping
|
|
// /16 so we don't blow the values out when we scale up to GIQ
|
|
ClipRect.top = (LONG_MIN + 1) / 16; // +1 to avoid compiler problem
|
|
ClipRect.bottom = LONG_MAX / 16;
|
|
}
|
|
|
|
// There's nothing to do if there are only one or two points
|
|
if (ppo->cCurves <= 2) {
|
|
DISPDBG((FILLPATH_DBG_LEVEL,"Nothing to do out\n"));
|
|
#if LOG_CALLS
|
|
ppdev = (PDEV*) pso->dhpdev;
|
|
LogFillPath(0, ppdev, pso);
|
|
#endif
|
|
goto ReturnTrue;
|
|
}
|
|
|
|
// Pass the surface off to GDI if it's a device bitmap that we've
|
|
// converted to a DIB:
|
|
// This is where to put device bit maps
|
|
ppdev = (PDEV*) pso->dhpdev;
|
|
|
|
SYNC_W_3D(ppdev);
|
|
|
|
if (pso->iType == STYPE_DEVBITMAP)
|
|
{
|
|
PDSURF pdsurf = (PDSURF) pso->dhsurf;
|
|
|
|
if ( pdsurf->pso && !bCreateScreenFromDib(ppdev, pdsurf) )
|
|
{
|
|
LogFillPath(4, ppdev, NULL);
|
|
return(EngFillPath(pdsurf->pso, ppo, pco, pbo, pptlBrush, mix,
|
|
flOptions));
|
|
}
|
|
ppdev->ptlOffset = pdsurf->ptl;
|
|
}
|
|
else
|
|
{
|
|
ppdev->ptlOffset.x = ppdev->ptlOffset.y = 0;
|
|
}
|
|
|
|
pfnFill = vMmFillSolid;
|
|
uRop = Rop2ToRop3[mix & 0xF];
|
|
uRopb = Rop2ToRop3[(mix >> 8) & 0xF];
|
|
bSolid = ((pbo == NULL) || (pbo->iSolidColor != -1));
|
|
|
|
//
|
|
// Make it simple and punt this one until later
|
|
//
|
|
avec = gajRop[uRop];
|
|
if ((uRop != uRopb) && !bSolid)
|
|
{
|
|
DISPDBG((FILLPATH_DBG_LEVEL, "ROPs it Fore=%x Back=%x ROP3=%x\n", uRop, uRopb, ROP3MIX(uRop, uRopb)));
|
|
uRop = ROP3MIX(uRop, uRopb);
|
|
avec = gajRop[uRop];
|
|
if (avec & AVEC_NEED_SOURCE)
|
|
{
|
|
// Use the implicit mask in the brush object.
|
|
// Note pre-align mask (as if "anchored")
|
|
|
|
if (!bSetMask(ppdev, pbo, pptlBrush, &ulBltDef))
|
|
{
|
|
DISPDBG((FILLPATH_DBG_LEVEL, "Set Mask Failed"));
|
|
LogFillPath(5, ppdev, NULL);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
iSolidColor = 0; // Assume we won't need a pattern
|
|
bRealizeTransparent = FALSE;
|
|
if (avec & AVEC_NEED_PATTERN)
|
|
{
|
|
iSolidColor = pbo->iSolidColor;
|
|
if (pbo->iSolidColor == -1)
|
|
{
|
|
bRealizeTransparent = (uRop != uRopb);
|
|
if (pbo->pvRbrush == NULL)
|
|
{
|
|
pbo->pvRbrush = BRUSHOBJ_pvGetRbrush(pbo);
|
|
if (pbo->pvRbrush == NULL)
|
|
{
|
|
DISPDBG((FILLPATH_DBG_LEVEL,"Could Not Get Brush\n"));
|
|
LogFillPath(6, ppdev, NULL);
|
|
return(FALSE);
|
|
}
|
|
}
|
|
pfnFill = vMmFillPatFast;
|
|
}
|
|
else
|
|
ulBltDef |= (BD_OP2 * IS_SOLID); // Or in 0x0007
|
|
}
|
|
|
|
if (avec & AVEC_NEED_DEST)
|
|
ulBltDef |= (BD_OP0 * IS_VRAM); // Or in 0x0100
|
|
|
|
// Enumerate path here first time to check for special
|
|
// cases (rectangles and monotone polygons)
|
|
|
|
// It is too difficult to determine interaction between
|
|
// multiple paths, if there is more than one, skip this
|
|
|
|
bMore = PATHOBJ_bEnum(ppo, &pd);
|
|
|
|
if (jClipping == DC_TRIVIAL)
|
|
{
|
|
// Try going through the fast non-complex fill code. We'll have
|
|
// to realize the brush first if we're going to handle a pattern:
|
|
|
|
if (iSolidColor == -1)
|
|
{
|
|
#ifdef S3
|
|
#if !FASTFILL_PATTERNS
|
|
goto SkipFastFill;
|
|
#else
|
|
// We handle patterns in 'pfnFastFill' only if we can use the S3
|
|
// hardware patterns.
|
|
if (!(ppdev->flCaps & CAPS_HW_PATTERNS))
|
|
goto SkipFastFill;
|
|
|
|
// Note: prb->pbe will be NULL and prb->ptlBrushOrg.x will be -1 the
|
|
// first time an RBRUSH is used. So we have to check the
|
|
// alignment *before* dereferencing prb->pbe...
|
|
|
|
if ((rbc.prb->ptlBrushOrg.x != pptlBrush->x + ppdev->xOffset) ||
|
|
(rbc.prb->ptlBrushOrg.y != pptlBrush->y + ppdev->yOffset) ||
|
|
(rbc.prb->apbe[IBOARD(ppdev)]->prbVerify != rbc.prb) ||
|
|
(rbc.prb->bTransparent != bRealizeTransparent))
|
|
{
|
|
vMmFastPatRealize(ppdev, pbo, pptlBrush,
|
|
bRealizeTransparent);
|
|
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
// Realize the brush
|
|
if (!SetBrush(ppdev, &ulBltDef, pbo, pptlBrush))
|
|
{
|
|
DISPDBG((FILLPATH_DBG_LEVEL,"Could Not Set Brush\n"));
|
|
LogFillPath(6, ppdev, NULL);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (bMore)
|
|
{
|
|
// FastFill only knows how to take a single contiguous buffer
|
|
// of points. Unfortunately, GDI sometimes hands us paths
|
|
// that are split over multiple path data records. Convex
|
|
// figures such as Ellipses, Pies and RoundRects are almost
|
|
// always given in multiple records. Since probably 90% of
|
|
// multiple record paths could still be done by FastFill, for
|
|
// those cases we simply copy the points into a contiguous
|
|
// buffer...
|
|
|
|
// First make sure that the entire path would fit in the
|
|
// temporary buffer, and make sure the path isn't comprised
|
|
// of more than one subpath:
|
|
|
|
if ((ppo->cCurves >= NUM_BUFFER_POINTS) ||
|
|
(pd.flags & PD_ENDSUBPATH))
|
|
goto SkipFastFill;
|
|
|
|
pptfxTmp = &aptfxBuf[0];
|
|
|
|
RtlCopyMemory(pptfxTmp, pd.pptfx, sizeof(POINTFIX) * pd.count);
|
|
|
|
pptfxTmp += pd.count;
|
|
cptfxTmp = pd.count;
|
|
flFirstRecord = pd.flags; // Remember PD_BEGINSUBPATH flag
|
|
|
|
do {
|
|
bMore = PATHOBJ_bEnum(ppo, &pd);
|
|
|
|
RtlCopyMemory(pptfxTmp, pd.pptfx, sizeof(POINTFIX) * pd.count);
|
|
cptfxTmp += pd.count;
|
|
pptfxTmp += pd.count;
|
|
} while (!(pd.flags & PD_ENDSUBPATH));
|
|
|
|
// Fake up the path data record:
|
|
|
|
pd.pptfx = &aptfxBuf[0];
|
|
pd.count = cptfxTmp;
|
|
pd.flags |= flFirstRecord;
|
|
|
|
// If there's more than one subpath, we can't call FastFill:
|
|
|
|
if (bMore)
|
|
goto SkipFastFill;
|
|
}
|
|
|
|
ppdev->uBLTDEF = ulBltDef;
|
|
if (bMmFastFill(ppdev, pd.count, pd.pptfx, uRop,
|
|
uRopb, iSolidColor, pbo))
|
|
{
|
|
LogFillPath(0, ppdev, pso);
|
|
return(TRUE);
|
|
}
|
|
}
|
|
|
|
SkipFastFill:
|
|
|
|
// Set up working storage in the temporary buffer
|
|
|
|
prclRects = (RECTL*) ppdev->pvTmpBuffer; // storage for list of rectangles to draw
|
|
|
|
if (!bMore) {
|
|
|
|
RECTL *rectangle;
|
|
INT cPoints = pd.count;
|
|
|
|
// The count can't be less than three, because we got all the edges
|
|
// in this subpath, and above we checked that there were at least
|
|
// three edges
|
|
|
|
// If the count is four, check to see if the polygon is really a
|
|
// rectangle since we can really speed that up. We'll also check for
|
|
// five with the first and last points the same, because under Win 3.1,
|
|
// it was required to close polygons
|
|
|
|
if ((cPoints == 4) ||
|
|
((cPoints == 5) &&
|
|
(pd.pptfx[0].x == pd.pptfx[4].x) &&
|
|
(pd.pptfx[0].y == pd.pptfx[4].y))) {
|
|
|
|
rectangle = prclRects;
|
|
|
|
/* we have to start somewhere so assume that most
|
|
applications specify the top left point first
|
|
|
|
we want to check that the first two points are
|
|
either vertically or horizontally aligned. if
|
|
they are then we check that the last point [3]
|
|
is either horizontally or vertically aligned,
|
|
and finally that the 3rd point [2] is aligned
|
|
with both the first point and the last point */
|
|
|
|
#define FIX_SHIFT 4L
|
|
#define FIX_MASK (- (1 << FIX_SHIFT))
|
|
|
|
rectangle->top = pd.pptfx[0].y - 1 & FIX_MASK;
|
|
rectangle->left = pd.pptfx[0].x - 1 & FIX_MASK;
|
|
rectangle->right = pd.pptfx[1].x - 1 & FIX_MASK;
|
|
|
|
if (rectangle->left ^ rectangle->right) {
|
|
if (rectangle->top ^ (pd.pptfx[1].y - 1 & FIX_MASK))
|
|
goto not_rectangle;
|
|
|
|
if (rectangle->left ^ (pd.pptfx[3].x - 1 & FIX_MASK))
|
|
goto not_rectangle;
|
|
|
|
if (rectangle->right ^ (pd.pptfx[2].x - 1 & FIX_MASK))
|
|
goto not_rectangle;
|
|
|
|
rectangle->bottom = pd.pptfx[2].y - 1 & FIX_MASK;
|
|
if (rectangle->bottom ^ (pd.pptfx[3].y - 1 & FIX_MASK))
|
|
goto not_rectangle;
|
|
}
|
|
else {
|
|
if (rectangle->top ^ (pd.pptfx[3].y - 1 & FIX_MASK))
|
|
goto not_rectangle;
|
|
|
|
rectangle->bottom = pd.pptfx[1].y - 1 & FIX_MASK;
|
|
if (rectangle->bottom ^ (pd.pptfx[2].y - 1 & FIX_MASK))
|
|
goto not_rectangle;
|
|
|
|
rectangle->right = pd.pptfx[2].x - 1 & FIX_MASK;
|
|
if (rectangle->right ^ (pd.pptfx[3].x - 1 & FIX_MASK))
|
|
goto not_rectangle;
|
|
}
|
|
|
|
/* if the left is greater than the right then
|
|
swap them so the blt code doesn't wig out */
|
|
|
|
if (rectangle->left > rectangle->right) {
|
|
FIX temp;
|
|
|
|
temp = rectangle->left;
|
|
rectangle->left = rectangle->right;
|
|
rectangle->right = temp;
|
|
}
|
|
else {
|
|
|
|
/* if left == right there's nothing to draw */
|
|
|
|
if (rectangle->left == rectangle->right)
|
|
{
|
|
LogFillPath(0, ppdev, pso);
|
|
goto ReturnTrue;
|
|
}
|
|
}
|
|
|
|
/* shift the values to get pixel coordinates */
|
|
|
|
rectangle->left = (rectangle->left >> FIX_SHIFT) + 1;
|
|
rectangle->right = (rectangle->right >> FIX_SHIFT) + 1;
|
|
|
|
if (rectangle->top > rectangle->bottom) {
|
|
FIX temp;
|
|
|
|
temp = rectangle->top;
|
|
rectangle->top = rectangle->bottom;
|
|
rectangle->bottom = temp;
|
|
}
|
|
else {
|
|
if (rectangle->top == rectangle->bottom)
|
|
{
|
|
LogFillPath(0, ppdev, pso);
|
|
goto ReturnTrue;
|
|
}
|
|
}
|
|
|
|
/* shift the values to get pixel coordinates */
|
|
|
|
rectangle->top = (rectangle->top >> FIX_SHIFT) + 1;
|
|
rectangle->bottom = (rectangle->bottom >> FIX_SHIFT) + 1;
|
|
|
|
// Finally, check for clipping
|
|
if (jClipping == DC_RECT) {
|
|
// Clip to the clip rectangle
|
|
if (!bIntersect(rectangle, &ClipRect, rectangle))
|
|
{
|
|
// Totally clipped, nothing to do
|
|
LogFillPath(0, ppdev, pso);
|
|
goto ReturnTrue;
|
|
}
|
|
}
|
|
|
|
/* if we get here then the polygon is a rectangle,
|
|
set count to 1 and goto bottom to draw it */
|
|
|
|
ulNumRects = 1;
|
|
goto draw_remaining_rectangles;
|
|
}
|
|
|
|
not_rectangle:
|
|
|
|
;
|
|
|
|
}
|
|
|
|
// Do we have enough memory for all the edges?
|
|
// LATER does cCurves include closure?
|
|
if (ppo->cCurves > MAX_EDGES) {
|
|
#if TAKING_ALLOC_STATS
|
|
BufferMissInFillpath++;
|
|
#endif
|
|
//
|
|
// try to allocate enough memory
|
|
//
|
|
#ifdef WINNT_VER40
|
|
pFreeEdges = (EDGE *) MEM_ALLOC(0, (ppo->cCurves * sizeof(EDGE)), ALLOC_TAG);
|
|
#else
|
|
pFreeEdges = (EDGE *) MEM_ALLOC(LMEM_FIXED, (ppo->cCurves * sizeof(EDGE)));
|
|
#endif
|
|
|
|
if (pFreeEdges == NULL)
|
|
{
|
|
LogFillPath(1, ppdev, NULL);
|
|
goto ReturnFalse; // too many edges; let GDI fill the path
|
|
}
|
|
else
|
|
{
|
|
bMemAlloced = TRUE;
|
|
}
|
|
}
|
|
else {
|
|
#if TAKING_ALLOC_STATS
|
|
BufferHitInFillpath++;
|
|
#endif
|
|
pFreeEdges = (EDGE*) ((BYTE*) ppdev->pvTmpBuffer + RECT_BYTES);
|
|
// use our handy temporary buffer (it's big enough)
|
|
}
|
|
|
|
// Initialize an empty list of rectangles to fill
|
|
ulNumRects = 0;
|
|
|
|
// Enumerate the path edges and build a Global Edge Table (GET) from them
|
|
// in YX-sorted order.
|
|
pGETHead = &GETHead;
|
|
if (!ConstructGET(pGETHead, pFreeEdges, ppo, &pd, bMore, &ClipRect))
|
|
{
|
|
LogFillPath(7, ppdev, NULL);
|
|
goto ReturnFalse; // outside GDI's 2**27 range
|
|
}
|
|
|
|
// Create an empty AET with the head node also a tail sentinel
|
|
pAETHead = &AETHead;
|
|
AETHead.pNext = pAETHead; // mark that the AET is empty
|
|
AETHead.X = 0x7FFFFFFF; // this is greater than any valid X value, so
|
|
// searches will always terminate
|
|
|
|
// Top scan of polygon is the top of the first edge we come to
|
|
iCurrentY = ((EDGE *)GETHead.pNext)->Y;
|
|
|
|
// Loop through all the scans in the polygon, adding edges from the GET to
|
|
// the Active Edge Table (AET) as we come to their starts, and scanning out
|
|
// the AET at each scan into a rectangle list. Each time it fills up, the
|
|
// rectangle list is passed to the filling routine, and then once again at
|
|
// the end if any rectangles remain undrawn. We continue so long as there
|
|
// are edges to be scanned out
|
|
while (1) {
|
|
|
|
// Advance the edges in the AET one scan, discarding any that have
|
|
// reached the end (if there are any edges in the AET)
|
|
if (AETHead.pNext != pAETHead) {
|
|
AdvanceAETEdges(pAETHead);
|
|
}
|
|
|
|
// If the AET is empty, done if the GET is empty, else jump ahead to
|
|
// the next edge in the GET; if the AET isn't empty, re-sort the AET
|
|
if (AETHead.pNext == pAETHead) {
|
|
if (GETHead.pNext == pGETHead) {
|
|
// Done if there are no edges in either the AET or the GET
|
|
break;
|
|
}
|
|
// There are no edges in the AET, so jump ahead to the next edge in
|
|
// the GET
|
|
iCurrentY = ((EDGE *)GETHead.pNext)->Y;
|
|
} else {
|
|
// Re-sort the edges in the AET by X coordinate, if there are at
|
|
// least two edges in the AET (there could be one edge if the
|
|
// balancing edge hasn't yet been added from the GET)
|
|
if (((EDGE *)AETHead.pNext)->pNext != pAETHead) {
|
|
XSortAETEdges(pAETHead);
|
|
}
|
|
}
|
|
|
|
// Move any new edges that start on this scan from the GET to the AET;
|
|
// bother calling only if there's at least one edge to add
|
|
if (((EDGE *)GETHead.pNext)->Y == iCurrentY) {
|
|
MoveNewEdges(pGETHead, pAETHead, iCurrentY);
|
|
}
|
|
|
|
// Scan the AET into rectangles to fill (there's always at least one
|
|
// edge pair in the AET)
|
|
pCurrentEdge = AETHead.pNext; // point to the first edge
|
|
do {
|
|
|
|
INT iLeftEdge;
|
|
|
|
// The left edge of any given edge pair is easy to find; it's just
|
|
// wherever we happen to be currently
|
|
iLeftEdge = pCurrentEdge->X;
|
|
|
|
// Find the matching right edge according to the current fill rule
|
|
if ((flOptions & FP_WINDINGMODE) != 0) {
|
|
|
|
INT iWindingCount;
|
|
|
|
// Do winding fill; scan across until we've found equal numbers
|
|
// of up and down edges
|
|
iWindingCount = pCurrentEdge->iWindingDirection;
|
|
do {
|
|
pCurrentEdge = pCurrentEdge->pNext;
|
|
iWindingCount += pCurrentEdge->iWindingDirection;
|
|
} while (iWindingCount != 0);
|
|
} else {
|
|
// Odd-even fill; the next edge is the matching right edge
|
|
pCurrentEdge = pCurrentEdge->pNext;
|
|
}
|
|
|
|
// See if the resulting span encompasses at least one pixel, and
|
|
// add it to the list of rectangles to draw if so
|
|
if (iLeftEdge < pCurrentEdge->X) {
|
|
|
|
// We've got an edge pair to add to the list to be filled; see
|
|
// if there's room for one more rectangle
|
|
if (ulNumRects >= MAX_PATH_RECTS) {
|
|
// No more room; draw the rectangles in the list and reset
|
|
// it to empty
|
|
|
|
ppdev->uBLTDEF = ulBltDef;
|
|
(*pfnFill)(ppdev, ulNumRects, prclRects, uRop,
|
|
uRopb, pbo, pptlBrush);
|
|
|
|
// Reset the list to empty
|
|
ulNumRects = 0;
|
|
}
|
|
|
|
// Add the rectangle representing the current edge pair
|
|
if (jClipping == DC_RECT) {
|
|
// Clipped
|
|
// Clip to left
|
|
prclRects[ulNumRects].left = max(iLeftEdge, ClipRect.left);
|
|
// Clip to right
|
|
prclRects[ulNumRects].right =
|
|
min(pCurrentEdge->X, ClipRect.right);
|
|
// Draw only if not fully clipped
|
|
if (prclRects[ulNumRects].left <
|
|
prclRects[ulNumRects].right) {
|
|
prclRects[ulNumRects].top = iCurrentY;
|
|
prclRects[ulNumRects].bottom = iCurrentY+1;
|
|
ulNumRects++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Unclipped
|
|
prclRects[ulNumRects].top = iCurrentY;
|
|
prclRects[ulNumRects].bottom = iCurrentY+1;
|
|
prclRects[ulNumRects].left = iLeftEdge;
|
|
prclRects[ulNumRects].right = pCurrentEdge->X;
|
|
ulNumRects++;
|
|
}
|
|
}
|
|
} while ((pCurrentEdge = pCurrentEdge->pNext) != pAETHead);
|
|
|
|
iCurrentY++; // next scan
|
|
}
|
|
|
|
/* draw the remaining rectangles, if there are any */
|
|
|
|
draw_remaining_rectangles:
|
|
|
|
if (ulNumRects > 0) {
|
|
ppdev->uBLTDEF = ulBltDef;
|
|
(*pfnFill)(ppdev, ulNumRects, prclRects, uRop, uRopb,
|
|
pbo, pptlBrush);
|
|
}
|
|
|
|
LogFillPath(0, ppdev, pso);
|
|
|
|
ReturnTrue:
|
|
bRetVal = TRUE; // done successfully
|
|
|
|
ReturnFalse:
|
|
|
|
// bRetVal is originally false. If you jumped to ReturnFalse from somewhere,
|
|
// then it will remain false, and be returned.
|
|
|
|
if (bMemAlloced)
|
|
{
|
|
//
|
|
// we did allocate memory, so release it
|
|
//
|
|
MEMORY_FREE (pFreeEdges);
|
|
}
|
|
|
|
return(bRetVal);
|
|
}
|
|
|
|
// Advance the edges in the AET to the next scan, dropping any for which we've
|
|
// done all scans. Assumes there is at least one edge in the AET.
|
|
VOID AdvanceAETEdges(EDGE *pAETHead)
|
|
{
|
|
EDGE *pLastEdge, *pCurrentEdge;
|
|
|
|
pLastEdge = pAETHead;
|
|
pCurrentEdge = pLastEdge->pNext;
|
|
do {
|
|
|
|
// Count down this edge's remaining scans
|
|
if (--pCurrentEdge->iScansLeft == 0) {
|
|
// We've done all scans for this edge; drop this edge from the AET
|
|
pLastEdge->pNext = pCurrentEdge->pNext;
|
|
} else {
|
|
// Advance the edge's X coordinate for a 1-scan Y advance
|
|
// Advance by the minimum amount
|
|
pCurrentEdge->X += pCurrentEdge->iXWhole;
|
|
// Advance the error term and see if we got one extra pixel this
|
|
// time
|
|
pCurrentEdge->iErrorTerm += pCurrentEdge->iErrorAdjustUp;
|
|
if (pCurrentEdge->iErrorTerm >= 0) {
|
|
// The error term turned over, so adjust the error term and
|
|
// advance the extra pixel
|
|
pCurrentEdge->iErrorTerm -= pCurrentEdge->iErrorAdjustDown;
|
|
pCurrentEdge->X += pCurrentEdge->iXDirection;
|
|
}
|
|
|
|
pLastEdge = pCurrentEdge;
|
|
}
|
|
} while ((pCurrentEdge = pLastEdge->pNext) != pAETHead);
|
|
}
|
|
|
|
// X-sort the AET, because the edges may have moved around relative to
|
|
// one another when we advanced them. We'll use a multipass bubble
|
|
// sort, which is actually okay for this application because edges
|
|
// rarely move relative to one another, so we usually do just one pass.
|
|
// Also, this makes it easy to keep just a singly-linked list. Assumes there
|
|
// are at least two edges in the AET.
|
|
VOID XSortAETEdges(EDGE *pAETHead)
|
|
{
|
|
BOOL bEdgesSwapped;
|
|
EDGE *pLastEdge, *pCurrentEdge, *pNextEdge;
|
|
|
|
do {
|
|
|
|
bEdgesSwapped = FALSE;
|
|
pLastEdge = pAETHead;
|
|
pCurrentEdge = pLastEdge->pNext;
|
|
pNextEdge = pCurrentEdge->pNext;
|
|
|
|
do {
|
|
if (pNextEdge->X < pCurrentEdge->X) {
|
|
|
|
// Next edge is to the left of the current edge; swap them
|
|
pLastEdge->pNext = pNextEdge;
|
|
pCurrentEdge->pNext = pNextEdge->pNext;
|
|
pNextEdge->pNext = pCurrentEdge;
|
|
bEdgesSwapped = TRUE;
|
|
pCurrentEdge = pNextEdge; // continue sorting before the edge
|
|
// we just swapped; it might move
|
|
// farther yet
|
|
}
|
|
pLastEdge = pCurrentEdge;
|
|
pCurrentEdge = pLastEdge->pNext;
|
|
} while ((pNextEdge = pCurrentEdge->pNext) != pAETHead);
|
|
} while (bEdgesSwapped);
|
|
}
|
|
|
|
// Moves all edges that start on the current scan from the GET to the AET in
|
|
// X-sorted order. Parameters are pointer to head of GET and pointer to dummy
|
|
// edge at head of AET, plus current scan line. Assumes there's at least one
|
|
// edge to be moved.
|
|
VOID MoveNewEdges(EDGE *pGETHead, EDGE *pAETHead, INT iCurrentY)
|
|
{
|
|
EDGE *pCurrentEdge = pAETHead;
|
|
EDGE *pGETNext = pGETHead->pNext;
|
|
|
|
do {
|
|
|
|
// Scan through the AET until the X-sorted insertion point for this
|
|
// edge is found. We can continue from where the last search left
|
|
// off because the edges in the GET are in X sorted order, as is
|
|
// the AET. The search always terminates because the AET sentinel
|
|
// is greater than any valid X
|
|
while (pGETNext->X > ((EDGE *)pCurrentEdge->pNext)->X) {
|
|
pCurrentEdge = pCurrentEdge->pNext;
|
|
}
|
|
|
|
// We've found the insertion point; add the GET edge to the AET, and
|
|
// remove it from the GET
|
|
pGETHead->pNext = pGETNext->pNext;
|
|
pGETNext->pNext = pCurrentEdge->pNext;
|
|
pCurrentEdge->pNext = pGETNext;
|
|
pCurrentEdge = pGETNext; // continue insertion search for the next
|
|
// GET edge after the edge we just added
|
|
pGETNext = pGETHead->pNext;
|
|
|
|
} while (pGETNext->Y == iCurrentY);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Build the Global Edge Table from the path. There must be enough memory in
|
|
// the free edge area to hold all edges. The GET is constructed in Y-X order,
|
|
// and has a head/tail/sentinel node at pGETHead.
|
|
|
|
BOOL ConstructGET(
|
|
EDGE *pGETHead,
|
|
EDGE *pFreeEdges,
|
|
PATHOBJ *ppo,
|
|
PATHDATA *pd,
|
|
BOOL bMore,
|
|
RECTL *pClipRect)
|
|
{
|
|
POINTFIX pfxPathStart; // point that started the current subpath
|
|
POINTFIX pfxPathPrevious; // point before the current point in a subpath;
|
|
// starts the current edge
|
|
|
|
/* Create an empty GET with the head node also a tail sentinel */
|
|
|
|
pGETHead->pNext = pGETHead; // mark that the GET is empty
|
|
pGETHead->Y = 0x7FFFFFFF; // this is greater than any valid Y value, so
|
|
// searches will always terminate
|
|
|
|
/* PATHOBJ_vEnumStart is implicitly performed by engine
|
|
already and first path is enumerated by the caller */
|
|
|
|
next_subpath:
|
|
|
|
/* Make sure the PATHDATA is not empty (is this necessary) */
|
|
|
|
if (pd->count != 0) {
|
|
|
|
/* If first point starts a subpath, remember it as such
|
|
and go on to the next point, so we can get an edge */
|
|
|
|
if (pd->flags & PD_BEGINSUBPATH) {
|
|
|
|
/* the first point starts the subpath; remember it */
|
|
|
|
|
|
pfxPathStart = *pd->pptfx; /* the subpath starts here */
|
|
pfxPathPrevious = *pd->pptfx; /* this points starts the next edge */
|
|
pd->pptfx++; /* advance to the next point */
|
|
pd->count--; /* count off this point */
|
|
}
|
|
|
|
|
|
/* add edges in PATHDATA to GET, in Y-X sorted order */
|
|
|
|
while (pd->count--) {
|
|
if ((pFreeEdges =
|
|
AddEdgeToGET(pGETHead, pFreeEdges, &pfxPathPrevious, pd->pptfx,
|
|
pClipRect)) == NULL) {
|
|
goto ReturnFalse;
|
|
}
|
|
pfxPathPrevious = *pd->pptfx; /* current point becomes previous */
|
|
pd->pptfx++; /* advance to the next point */
|
|
}
|
|
|
|
|
|
/* If last point ends the subpath, insert the edge that
|
|
connects to first point (is this built in already?) */
|
|
|
|
if (pd->flags & PD_ENDSUBPATH) {
|
|
if ((pFreeEdges = AddEdgeToGET(pGETHead, pFreeEdges, &pfxPathPrevious,
|
|
&pfxPathStart, pClipRect)) == NULL) {
|
|
goto ReturnFalse;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* the initial loop conditions preclude a do, while or for */
|
|
|
|
if (bMore) {
|
|
bMore = PATHOBJ_bEnum(ppo, pd);
|
|
goto next_subpath;
|
|
}
|
|
|
|
return(TRUE); // done successfully
|
|
|
|
ReturnFalse:
|
|
return(FALSE); // failed
|
|
}
|
|
|
|
// Adds the edge described by the two passed-in points to the Global Edge
|
|
// Table, if the edge spans at least one pixel vertically.
|
|
EDGE * AddEdgeToGET(EDGE *pGETHead, EDGE *pFreeEdge,
|
|
POINTFIX *ppfxEdgeStart, POINTFIX *ppfxEdgeEnd, RECTL *pClipRect)
|
|
{
|
|
INT iYStart, iYEnd, iXStart, iXEnd, iYHeight, iXWidth;
|
|
INT yJump, yTop;
|
|
|
|
// Set the winding-rule direction of the edge, and put the endpoints in
|
|
// top-to-bottom order
|
|
iYHeight = ppfxEdgeEnd->y - ppfxEdgeStart->y;
|
|
if (iYHeight == 0) {
|
|
return(pFreeEdge); // zero height; ignore this edge
|
|
} else if (iYHeight >= 0) {
|
|
iXStart = ppfxEdgeStart->x;
|
|
iYStart = ppfxEdgeStart->y;
|
|
iXEnd = ppfxEdgeEnd->x;
|
|
iYEnd = ppfxEdgeEnd->y;
|
|
pFreeEdge->iWindingDirection = 1;
|
|
} else {
|
|
iYHeight = -iYHeight;
|
|
iXEnd = ppfxEdgeStart->x;
|
|
iYEnd = ppfxEdgeStart->y;
|
|
iXStart = ppfxEdgeEnd->x;
|
|
iYStart = ppfxEdgeEnd->y;
|
|
pFreeEdge->iWindingDirection = -1;
|
|
}
|
|
|
|
if (iYHeight & 0x80000000) {
|
|
return(NULL); // too large; outside 2**27 GDI range
|
|
}
|
|
|
|
// Set the error term and adjustment factors, all in GIQ coordinates for
|
|
// now
|
|
iXWidth = iXEnd - iXStart;
|
|
if (iXWidth >= 0) {
|
|
// Left to right, so we change X as soon as we move at all
|
|
pFreeEdge->iXDirection = 1;
|
|
pFreeEdge->iErrorTerm = -1;
|
|
} else {
|
|
// Right to left, so we don't change X until we've moved a full GIQ
|
|
// coordinate
|
|
iXWidth = -iXWidth;
|
|
pFreeEdge->iXDirection = -1;
|
|
pFreeEdge->iErrorTerm = -iYHeight;
|
|
}
|
|
|
|
if (iXWidth & 0x80000000) {
|
|
return(NULL); // too large; outside 2**27 GDI range
|
|
}
|
|
|
|
if (iXWidth >= iYHeight) {
|
|
// Calculate base run length (minimum distance advanced in X for a 1-
|
|
// scan advance in Y)
|
|
pFreeEdge->iXWhole = iXWidth / iYHeight;
|
|
// Add sign back into base run length if going right to left
|
|
if (pFreeEdge->iXDirection == -1) {
|
|
pFreeEdge->iXWhole = -pFreeEdge->iXWhole;
|
|
}
|
|
pFreeEdge->iErrorAdjustUp = iXWidth % iYHeight;
|
|
} else {
|
|
// Base run length is 0, because line is closer to vertical than
|
|
// horizontal
|
|
pFreeEdge->iXWhole = 0;
|
|
pFreeEdge->iErrorAdjustUp = iXWidth;
|
|
}
|
|
pFreeEdge->iErrorAdjustDown = iYHeight;
|
|
|
|
// Calculate the number of pixels spanned by this edge, accounting for
|
|
// clipping
|
|
|
|
// Top true pixel scan in GIQ coordinates
|
|
// Shifting to divide and multiply by 16 is okay because the clip rect
|
|
// always contains positive numbers
|
|
yTop = max(pClipRect->top << 4, (iYStart + 15) & ~0x0F);
|
|
pFreeEdge->Y = yTop >> 4; // initial scan line on which to fill edge
|
|
|
|
// Calculate # of scans to actually fill, accounting for clipping
|
|
if ((pFreeEdge->iScansLeft = min(pClipRect->bottom, ((iYEnd + 15) >> 4))
|
|
- pFreeEdge->Y) <= 0) {
|
|
|
|
return(pFreeEdge); // no pixels at all are spanned, so we can
|
|
// ignore this edge
|
|
}
|
|
|
|
// If the edge doesn't start on a pixel scan (that is, it starts at a
|
|
// fractional GIQ coordinate), advance it to the first pixel scan it
|
|
// intersects. Ditto if there's top clipping. Also clip to the bottom if
|
|
// needed
|
|
|
|
if (iYStart != yTop) {
|
|
// Jump ahead by the Y distance in GIQ coordinates to the first pixel
|
|
// to draw
|
|
yJump = yTop - iYStart;
|
|
|
|
// Advance x the minimum amount for the number of scans traversed
|
|
iXStart += pFreeEdge->iXWhole * yJump;
|
|
|
|
AdjustErrorTerm(&pFreeEdge->iErrorTerm, pFreeEdge->iErrorAdjustUp,
|
|
pFreeEdge->iErrorAdjustDown, yJump, &iXStart,
|
|
pFreeEdge->iXDirection);
|
|
}
|
|
// Turn the calculations into pixel rather than GIQ calculations
|
|
|
|
// Move the X coordinate to the nearest pixel, and adjust the error term
|
|
// accordingly
|
|
// Dividing by 16 with a shift is okay because X is always positive
|
|
pFreeEdge->X = (iXStart + 15) >> 4; // convert from GIQ to pixel coordinates
|
|
|
|
// LATER adjust only if needed (if prestepped above)?
|
|
if (pFreeEdge->iXDirection == 1) {
|
|
// Left to right
|
|
pFreeEdge->iErrorTerm -= pFreeEdge->iErrorAdjustDown *
|
|
(((iXStart + 15) & ~0x0F) - iXStart);
|
|
} else {
|
|
// Right to left
|
|
pFreeEdge->iErrorTerm -= pFreeEdge->iErrorAdjustDown *
|
|
((iXStart - 1) & 0x0F);
|
|
}
|
|
|
|
// Scale the error term down 16 times to switch from GIQ to pixels.
|
|
// Shifts work to do the multiplying because these values are always
|
|
// non-negative
|
|
pFreeEdge->iErrorTerm >>= 4;
|
|
|
|
// Insert the edge into the GET in YX-sorted order. The search always ends
|
|
// because the GET has a sentinel with a greater-than-possible Y value
|
|
while ((pFreeEdge->Y > ((EDGE *)pGETHead->pNext)->Y) ||
|
|
((pFreeEdge->Y == ((EDGE *)pGETHead->pNext)->Y) &&
|
|
(pFreeEdge->X > ((EDGE *)pGETHead->pNext)->X))) {
|
|
pGETHead = pGETHead->pNext;
|
|
}
|
|
|
|
pFreeEdge->pNext = pGETHead->pNext; // link the edge into the GET
|
|
pGETHead->pNext = pFreeEdge;
|
|
|
|
return(++pFreeEdge); // point to the next edge storage location for next
|
|
// time
|
|
}
|
|
|
|
// Adjust the error term for a skip ahead in y. This is in ASM because there's
|
|
// a multiply/divide that may involve a larger than 32-bit value.
|
|
|
|
void AdjustErrorTerm(INT *pErrorTerm, INT iErrorAdjustUp, INT iErrorAdjustDown,
|
|
INT yJump, INT *pXStart, INT iXDirection)
|
|
{
|
|
#if defined(_X86_) || defined(i386)
|
|
// Adjust the error term up by the number of y coordinates we'll skip
|
|
//*pErrorTerm += iErrorAdjustUp * yJump;
|
|
_asm mov ebx,pErrorTerm
|
|
_asm mov eax,iErrorAdjustUp
|
|
_asm mul yJump
|
|
_asm add eax,[ebx]
|
|
_asm adc edx,-1 // the error term starts out negative
|
|
|
|
// See if the error term turned over even once while skipping
|
|
//if (*pErrorTerm >= 0) {
|
|
_asm js short NoErrorTurnover
|
|
|
|
// # of times we'll turn over the error term and step an extra x
|
|
// coordinate while skipping
|
|
// NumAdjustDowns = (*pErrorTerm / iErrorAdjustDown) + 1;
|
|
_asm div iErrorAdjustDown
|
|
_asm inc eax
|
|
// Note that EDX is the remainder; (EDX - iErrorAdjustDown) is where
|
|
// the error term ends up ultimately
|
|
|
|
// Advance x appropriately for the # of times the error term
|
|
// turned over
|
|
// if (iXDirection == 1) {
|
|
// *pXStart += NumAdjustDowns;
|
|
// } else {
|
|
// *pXStart -= NumAdjustDowns;
|
|
// }
|
|
_asm mov ecx,pXStart
|
|
_asm cmp iXDirection,1
|
|
_asm jz short GoingRight
|
|
_asm neg eax
|
|
GoingRight:
|
|
_asm add [ecx],eax
|
|
|
|
// Adjust the error term down to its proper post-skip value
|
|
// *pErrorTerm -= iErrorAdjustDown * NumAdjustDowns;
|
|
_asm sub edx,iErrorAdjustDown
|
|
_asm mov eax,edx // put into EAX for storing to pErrorTerm next
|
|
// }
|
|
NoErrorTurnover:
|
|
_asm mov [ebx],eax
|
|
#else
|
|
LONGLONG llErrorTerm;
|
|
INT NumAdjustDowns;
|
|
|
|
llErrorTerm = *pErrorTerm;
|
|
|
|
// Adjust the error term up by the number of y coordinates we'll skip
|
|
llErrorTerm += Int32x32To64(iErrorAdjustUp,yJump);
|
|
|
|
// See if the error term turned over even once while skipping
|
|
if (llErrorTerm >= 0) {
|
|
// # of times we'll turn over the error term and step an extra x
|
|
// coordinate while skipping
|
|
NumAdjustDowns = (UInt64Div32To32(llErrorTerm,iErrorAdjustDown)) + 1;
|
|
|
|
// Advance x appropriately for the # of times the error term
|
|
// turned over
|
|
if (iXDirection == 1) {
|
|
*pXStart += NumAdjustDowns;
|
|
} else {
|
|
*pXStart -= NumAdjustDowns;
|
|
}
|
|
|
|
// Adjust the error term down to its proper post-skip value
|
|
llErrorTerm -= iErrorAdjustDown * NumAdjustDowns;
|
|
}
|
|
|
|
*pErrorTerm = (INT) llErrorTerm;
|
|
#endif
|
|
}
|
|
|
|
//--------------------------------------------------------------------------//
|
|
// //
|
|
// bSetMask() //
|
|
// Used by DrvFillPath //
|
|
// to setup the chip to use the current mask. //
|
|
// We don't set the BLTDEF register directly here. We set a local copy, //
|
|
// which the calling routine will further modify befor writing it to //
|
|
// the chip. //
|
|
// //
|
|
//--------------------------------------------------------------------------//
|
|
BOOL bSetMask(
|
|
PPDEV ppdev,
|
|
BRUSHOBJ *pbo,
|
|
POINTL *pptlBrush,
|
|
ULONG *bltdef)
|
|
{
|
|
PRBRUSH pRbrush = 0;
|
|
USHORT patoff_x, patoff_y;
|
|
|
|
DISPDBG((FILLPATH_DBG_LEVEL, "bSetMask - Entry\n"));
|
|
|
|
// Guard against a solid brush (pen) in case the caller didn't
|
|
if ((pbo ==NULL) || (pbo->iSolidColor != -1))
|
|
{
|
|
RIP("bSetMask - solid mask!\n");
|
|
*bltdef |= BD_OP1_IS_SRAM_MONO;
|
|
REQUIRE(4);
|
|
LL_FGCOLOR(0xFFFFFFFF, 2); // totally
|
|
LL_BGCOLOR(0xFFFFFFFF, 2); // foreground
|
|
return (TRUE);
|
|
}
|
|
else if (pbo->pvRbrush != NULL)
|
|
{
|
|
pRbrush = pbo->pvRbrush;
|
|
}
|
|
else
|
|
{
|
|
pRbrush = BRUSHOBJ_pvGetRbrush(pbo);
|
|
// Fail if we do not handle the brush.
|
|
|
|
if (pRbrush == NULL)
|
|
{
|
|
DISPDBG((FILLPATH_DBG_LEVEL, "pRbrush is NULL\n"));
|
|
return (FALSE);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set pattern offset.
|
|
// NT specifies patttern offset as which pixel on the screen to align
|
|
// with pattern(0,0). Laguna specifies pattern offset as which pixel
|
|
// of the pattern to align with screen(0,0). Only the lowest three
|
|
// bits are significant, so we can ignore any overflow when converting.
|
|
// Also, even though PATOFF is a reg_16, we can't do byte wide writes
|
|
// to it. We have to write both PATOFF.pt.X and PATOFF.pt.Y in a single
|
|
// 16 bit write.
|
|
//
|
|
#if 1 //#1244
|
|
patoff_x = (USHORT)(-(pptlBrush->x + ppdev->ptlOffset.x) & 7);
|
|
patoff_y = (USHORT)(-(pptlBrush->y + ppdev->ptlOffset.y) & 7);
|
|
#else
|
|
patoff_x = 8 - (BYTE)(pptlBrush->x & 0x07);
|
|
patoff_y = 8 - (BYTE)(pptlBrush->y & 0x07);
|
|
#endif
|
|
REQUIRE(1);
|
|
LL16 (grPATOFF.w, ((patoff_y << 8) | patoff_x ));
|
|
|
|
//
|
|
// What kind of brush is it?
|
|
//
|
|
if (pRbrush->iType == BRUSH_MONO) // Monochrome brush.
|
|
{
|
|
DISPDBG((FILLPATH_DBG_LEVEL, "bSetMask: Using monochrome brush.\n"));
|
|
#define mb ((MC_ENTRY*)(((BYTE*)ppdev->Mtable) + pRbrush->cache_slot))
|
|
if (mb->iUniq != pRbrush->iUniq)
|
|
{
|
|
CacheMono(ppdev, pRbrush);
|
|
}
|
|
|
|
// Load the fg and bg color registers.
|
|
REQUIRE(6);
|
|
LL_FGCOLOR(0xFFFFFFFF, 0);
|
|
LL_BGCOLOR(0x00000000, 0);
|
|
|
|
LL32(grOP2_opMRDRAM, pRbrush->cache_xy);
|
|
*bltdef |= 0x00D0;
|
|
return(TRUE);
|
|
}
|
|
else if (pRbrush->iType == BRUSH_4BPP) // 4-bpp brush.
|
|
{
|
|
DISPDBG((FILLPATH_DBG_LEVEL, "bSetMask: Using 4-bpp brush.\n"));
|
|
#define xb ((XC_ENTRY*)(((BYTE*)ppdev->Xtable) + pRbrush->cache_slot))
|
|
if (xb->iUniq != pRbrush->iUniq)
|
|
{
|
|
Cache4BPP(ppdev, pRbrush);
|
|
}
|
|
REQUIRE(2);
|
|
LL32(grOP2_opMRDRAM, pRbrush->cache_xy);
|
|
*bltdef |= 0x0090;
|
|
return(TRUE);
|
|
}
|
|
else if (pRbrush->iType == BRUSH_DITHER) // Dither brush.
|
|
{
|
|
DISPDBG((FILLPATH_DBG_LEVEL, "bSetMask: Using dither brush.\n"));
|
|
#define db ((DC_ENTRY*)(((BYTE*)ppdev->Dtable) + pRbrush->cache_slot))
|
|
if (db->ulColor != pRbrush->iUniq)
|
|
{
|
|
CacheDither(ppdev, pRbrush);
|
|
}
|
|
REQUIRE(2);
|
|
LL32(grOP2_opMRDRAM, pRbrush->cache_xy);
|
|
*bltdef |= 0x0090;
|
|
return(TRUE);
|
|
}
|
|
else // Color brush.
|
|
{
|
|
DISPDBG((FILLPATH_DBG_LEVEL, "bSetMask: Using color brush.\n"));
|
|
#define cb ((BC_ENTRY*)(((BYTE*)ppdev->Ctable) + pRbrush->cache_slot))
|
|
if (cb->brushID != pRbrush)
|
|
{
|
|
CacheBrush(ppdev, pRbrush);
|
|
}
|
|
REQUIRE(2);
|
|
LL32(grOP2_opMRDRAM, pRbrush->cache_xy);
|
|
*bltdef |= 0x0090;
|
|
return(TRUE);
|
|
}
|
|
|
|
DISPDBG((FILLPATH_DBG_LEVEL, "SetMask Ret False\n"));
|
|
return FALSE;
|
|
}
|
|
|
|
#if LOG_CALLS
|
|
|
|
extern long lg_i;
|
|
extern char lg_buf[256];
|
|
|
|
void LogFillPath(
|
|
ULONG acc,
|
|
PPDEV ppdev,
|
|
SURFOBJ *pso
|
|
)
|
|
{
|
|
|
|
#if ENABLE_LOG_SWITCH
|
|
if (pointer_switch == 0) return;
|
|
#endif
|
|
|
|
lg_i = sprintf(lg_buf,"DrvFillPath: ");
|
|
WriteLogFile(ppdev->pmfile, lg_buf, lg_i, ppdev->TxtBuff, &ppdev->TxtBuffIndex);
|
|
|
|
// Did we realize it? If not, why?
|
|
switch (acc)
|
|
{
|
|
case 0: lg_i = sprintf(lg_buf,"(ACCL) Id=%p", pso); break;
|
|
case 1: lg_i = sprintf(lg_buf,"(Punted - Too many edges) "); break;
|
|
case 2: lg_i = sprintf(lg_buf,"(Punted - Complex clipping) "); break;
|
|
case 3: lg_i = sprintf(lg_buf,"(Punted - S3) "); break;
|
|
case 4: lg_i = sprintf(lg_buf,"(Punted - DevBmp on host) "); break;
|
|
case 5: lg_i = sprintf(lg_buf,"(Punted - Failed mask) "); break;
|
|
case 6: lg_i = sprintf(lg_buf,"(Punted - Failed brush) "); break;
|
|
case 7: lg_i = sprintf(lg_buf,"(Punted - Edge table failed) "); break;
|
|
default: lg_i = sprintf(lg_buf,"(STATUS UNKNOWN) "); break;
|
|
}
|
|
WriteLogFile(ppdev->pmfile, lg_buf, lg_i, ppdev->TxtBuff, &ppdev->TxtBuffIndex);
|
|
|
|
lg_i = sprintf(lg_buf,"\r\n");
|
|
WriteLogFile(ppdev->pmfile, lg_buf, lg_i, ppdev->TxtBuff, &ppdev->TxtBuffIndex);
|
|
|
|
}
|
|
#endif
|