Leaked source code of windows server 2003
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.
 
 
 
 
 
 

2971 lines
90 KiB

/******************************Module*Header*******************************\
* Module Name: pathobj.cxx
*
* Non inline PATHOBJ methods
*
* Created: 28-Sep-1990 12:36:30
* Author: Paul Butzi [paulb]
*
* Copyright (c) 1990-1999 Microsoft Corporation
\**************************************************************************/
#include "precomp.hxx"
extern PBRUSH gpbrBackground;
extern PPEN gpPenNull;
extern PBRUSH gpbrNull;
extern LINEATTRS glaNominalGeometric;
// Default LINEATTRS for bSimpleStroke:
LA glaSimpleStroke =
{
0, // fl
0, // iJoin
0, // iEndCap
{1L}, // elWidth
IEEE_0_0F, // eMiterLimit
0, // cstyle
(LONG_FLOAT*) NULL, // pstyle
{0L} // elStyleState
};
#define XFORMNULL ((EXFORMOBJ *) NULL)
// The following declarations are required by the native c8 compiler.
HSEMAPHORE PATHALLOC::hsemFreelist; // Semaphore for freelist
PATHALLOC *PATHALLOC::freelist; // Free-list of pathallocs
COUNT PATHALLOC::cFree; // Count of free pathallocs
COUNT PATHALLOC::cAllocated; // Count of pathallocs allocated
/******************************Public*Routine******************************\
* XEPATHOBJ::XEPATHOBJ(hpath)
*
* Path user object constructor
*
* History:
* 28-Sep-1990 -by- Paul Butzi [paulb]
* Wrote it.
\**************************************************************************/
XEPATHOBJ::XEPATHOBJ(HPATH hPath)
{
ppath = (PPATH)HmgShareLock((HOBJ) hPath, PATH_TYPE);
if (ppath != (PATH*) NULL)
{
// Load up accelerator values:
cCurves = ppath->cCurves;
fl = ppath->fl;
}
return;
}
/******************************Public*Routine******************************\
* XEPATHOBJ::XEPATHOBJ(dco)
*
* Path user object constructor to get at the DC's path.
*
* History:
* 22-Mar-1992 -by- J. Andrew Goossen [andrewgo]
* Wrote it.
\**************************************************************************/
XEPATHOBJ::XEPATHOBJ(XDCOBJ& dco)
{
ASSERTGDI(dco.hpath() != HPATH_INVALID, "Invalid path");
// If a SaveDC was done, we may have to copy the path before mucking
// with it:
if (dco.pdc->bLazySave())
{
dco.pdc->vClearLazySave();
XEPATHOBJ epath(dco.hpath());
ASSERTGDI (epath.bValid(),"hpath invalid when bLazySave is set");
PATHMEMOBJ pmo;
if (pmo.bValid() && epath.bValid() && pmo.bClone(epath))
{
pmo.vKeepIt();
dco.pdc->hpath(pmo.hpath());
}
else
{
// Error case simply deletes the path if we managed to allocate
// one, and marks the DC path as invalid:
pmo.vDelete();
dco.pdc->hpath(HPATH_INVALID);
}
}
ppath = (PPATH)HmgShareLock((HOBJ) dco.hpath(), PATH_TYPE);
if (ppath != (PATH*) NULL)
{
// Load up accelerator values:
cCurves = ppath->cCurves;
fl = ppath->fl;
}
return;
}
/******************************Public*Routine******************************\
* EPATHFONTOBJ::vInit(ULONG)
*
* Initialize a chunk of memory to be a font pathobj
*
* History:
* 22-Mar-1992 -by- J. Andrew Goossen [andrewgo]
* Wrote it.
\**************************************************************************/
VOID EPATHFONTOBJ::vInit(ULONGSIZE_T size)
{
ppath = &path;
path.ppachain = &pa;
path.ptfxSubPathStart.x = 0;
path.ptfxSubPathStart.y = 0;
path.flags = PD_BEGINSUBPATH;
path.pprfirst = (PATHRECORD*) NULL;
path.pprlast = (PATHRECORD*) NULL;
path.rcfxBoundBox.xLeft = 0;
path.rcfxBoundBox.xRight = 0;
path.rcfxBoundBox.yTop = 0;
path.rcfxBoundBox.yBottom = 0;
path.flType = PATHTYPE_STACK;
pa.ppanext = (PATHALLOC*) NULL;
pa.pprfreestart = pa.apr;
pa.siztPathAlloc = size - offsetof(EPATHFONTOBJ,pa);
fl = 0;
cCurves = 0;
}
/******************************Public*Routine******************************\
* XEPATHOBJ::~XEPATHOBJ()
*
* Path user object destructor
*
* History:
* 1-Oct-1990 -by- Paul Butzi [paulb]
* Wrote it.
\**************************************************************************/
XEPATHOBJ::~XEPATHOBJ()
{
if (ppath != (PPATH) NULL)
{
// Since we're keeping the object, save the accelerator values
// away and unlock the object:
ppath->cCurves = cCurves;
ppath->fl = fl;
DEC_SHARE_REF_CNT(ppath);
}
return;
}
/******************************Public*Routine******************************\
* PATHMEMOBJ::PATHMEMOBJ()
*
* Create a new path object.
*
* Note: Using this constructor, the path will not inherit the current
* point from the DC!
*
* History:
* 1-Oct-1990 -by- Paul Butzi [paulb]
* Wrote it.
\**************************************************************************/
PATHMEMOBJ::PATHMEMOBJ()
{
PPATH ppathTemp;
ppath = ppathTemp = (PPATH)HmgAlloc(sizeof(PATH) ,PATH_TYPE, HMGR_ALLOC_ALT_LOCK);
if (ppathTemp != (PATH*) NULL)
{
// Private debug code to catch invalid hpath handle in DC
// 6/24/98 - davidx
ASSERTGDI(HmgObjtype(ppath->hGet()) == PATH_TYPE,
"Private debug breakpoint. Please contact ntgdi.");
//
// Since we 0 init in the Alloc we don't need to do most of this.
//
// pPathTemp->ppachain = (PATHALLOC*) NULL;
// pPathTemp->pprfirst = (PATHRECORD*) NULL;
// pPathTemp->pprlast = (PATHRECORD*) NULL;
// pPathTemp->rcfxBoundBox.xLeft = 0;
// pPathTemp->rcfxBoundBox.xRight = 0;
// pPathTemp->rcfxBoundBox.yTop = 0;
// pPathTemp->rcfxBoundBox.yBottom = 0;
// pPathTemp->ptfxSubPathStart.x = 0;
// pPathTemp->ptfxSubPathStart.y = 0;
// pPathTemp->flType = 0;
// pPathTemp->fl = 0;
// pPathTemp->cCurves = 0;
ppathTemp->flags = PD_BEGINSUBPATH | PD_ENDSUBPATH;
fl = 0;
cCurves = 0;
}
return;
}
/******************************Public*Routine******************************\
* PATHMEMOBJ::~PATHMEMOBJ()
*
* Release a path object unless made permanent.
*
* History:
* 1-Oct-1990 -by- Paul Butzi [paulb]
* Wrote it.
\**************************************************************************/
PATHMEMOBJ::~PATHMEMOBJ()
{
// ppath may have been made NULL by vDelete(), or by a failed PATH
// allocation in the constructor:
if (ppath != (PATH*) NULL)
{
if (!(ppath->flType & PATHTYPE_KEEPMEM))
{
// Free all the blocks in the path:
vFreeBlocks();
// Free the handle too:
HmgFree((HOBJ) ppath->hGet());
}
else
{
// Since we're keeping the object, save the accelerator values away
// and unlock the object:
ppath->cCurves = cCurves;
ppath->fl = fl;
DEC_SHARE_REF_CNT(ppath);
}
}
}
/******************************Public*Routine******************************\
* PATHSTACKOBJ::PATHSTACKOBJ()
*
* Create a new path object on the stack. The path can hold only a small
* number of points on the stack, and will overflow onto the heap if
* necessary.
*
* History:
* 22-Mar-1992 -by- J. Andrew Goossen [andrewgo]
* Wrote it.
\**************************************************************************/
PATHSTACKOBJ::PATHSTACKOBJ()
{
ppath = &path;
path.ppachain = &paBuf.pa;
path.ptfxSubPathStart.x = 0;
path.ptfxSubPathStart.y = 0;
path.flags = PD_BEGINSUBPATH;
path.pprfirst = (PATHRECORD*) NULL;
path.pprlast = (PATHRECORD*) NULL;
path.rcfxBoundBox.xLeft = 0;
path.rcfxBoundBox.xRight = 0;
path.rcfxBoundBox.yTop = 0;
path.rcfxBoundBox.yBottom = 0;
path.flType = PATHTYPE_STACK;
paBuf.pa.ppanext = (PATHALLOC*) NULL;
paBuf.pa.pprfreestart = paBuf.pa.apr;
paBuf.pa.siztPathAlloc = PATHSTACKALLOCSIZE;
cCurves = 0;
fl = 0;
}
/******************************Public*Routine******************************\
* PATHSTACKOBJ::PATHSTACKOBJ(dco, bUseCP)
*
* Create a new path object on the stack or locate an old one. If the DC
* is currently in a path bracket, we use the active path (which will NOT
* be on the path). Otherwise, we create a new path on the stack (it will
* overflow onto the heap if necessary). If we create a new path, we use
* some of the state from the DC to initialize the path, most notably the
* current position (ptfxSubPathStart).
*
* If bUseCP is set, the current position in the DC will be used to set the
* current position in the path. Calls that don't use the current position
* (i.e., immediately do a bMoveTo) shouldn't set bUseCP because it may
* require a transform call to set it (we need the value in device space).
*
* SaveDC's do lazy saves of paths. If a lazy save is pending, we have
* to copy the path and update the DC's path handle before we can modify
* it.
*
* History:
* 22-Mar-1992 -by- J. Andrew Goossen [andrewgo]
* Wrote it.
\**************************************************************************/
PATHSTACKOBJ::PATHSTACKOBJ(XDCOBJ& dco, BOOL bUseCP)
{
if (!dco.pdc->bActive())
{
cCurves = 0;
fl = 0;
// There's no active path in the DC, so we create a temporary
// path on the stack to hold our points:
ppath = &path;
path.ppachain = &paBuf.pa;
path.flags = PD_BEGINSUBPATH;
path.pprfirst = (PATHRECORD*) NULL;
path.pprlast = (PATHRECORD*) NULL;
path.rcfxBoundBox.xLeft = 0;
path.rcfxBoundBox.xRight = 0;
path.rcfxBoundBox.yTop = 0;
path.rcfxBoundBox.yBottom = 0;
path.flType = PATHTYPE_STACK;
paBuf.pa.ppanext = (PATHALLOC*) NULL;
paBuf.pa.pprfreestart = paBuf.pa.apr;
paBuf.pa.siztPathAlloc = PATHSTACKALLOCSIZE;
if (bUseCP)
{
if (!dco.pdc->bValidPtfxCurrent())
{
ASSERTGDI(dco.pdc->bValidPtlCurrent(), "Both CPs invalid?");
EXFORMOBJ exo(dco, WORLD_TO_DEVICE);
exo.bXformRound(&dco.ptlCurrent(), &dco.ptfxCurrent(), 1);
dco.pdc->vValidatePtfxCurrent();
}
path.ptfxSubPathStart = dco.ptfxCurrent();
// If we're not in a path, and a previous call to MoveToEx means
// that the style state should be reset, we do it now:
if (dco.ulDirty() & DIRTY_STYLESTATE)
{
dco.ulDirtySub(DIRTY_STYLESTATE);
LINEATTRS* pla = dco.plaRealized();
if (pla->fl & LA_GEOMETRIC)
pla->elStyleState.e = IEEE_0_0F;
else
pla->elStyleState.l = 0L;
}
}
}
else
{
// If a SaveDC was done, we may have to copy the path before we can
// muck with it:
if (dco.pdc->bLazySave())
{
dco.pdc->vClearLazySave();
XEPATHOBJ epath(dco.hpath());
ASSERTGDI (epath.bValid(),"hpath invalid when bLazySave is set");
PATHMEMOBJ pmo;
if (pmo.bValid() && epath.bValid() && pmo.bClone(epath))
{
pmo.vKeepIt();
dco.pdc->hpath(pmo.hpath());
}
else
{
pmo.vDelete();
dco.pdc->hpath(HPATH_INVALID);
}
}
// There is an active path bracket, so just add to it:
ppath = (PPATH)HmgShareLock((HOBJ) dco.hpath(), PATH_TYPE);
if (ppath == (PATH*) NULL)
return;
ASSERTGDI(ppath->flType & PATHTYPE_KEEPMEM, "Path not kept?");
// Load up accelerator values:
cCurves = ppath->cCurves;
fl = ppath->fl;
if (bUseCP)
{
if (!dco.pdc->bValidPtfxCurrent())
{
// If the device space current position has been invalidated
// (meaning that it's moved), always do a bMoveTo to the new
// device space point:
ASSERTGDI(dco.pdc->bValidPtlCurrent(), "Both CPs invalid?");
EXFORMOBJ exo(dco, WORLD_TO_DEVICE);
exo.bXformRound(&dco.ptlCurrent(), &dco.ptfxCurrent(), 1);
dco.pdc->vValidatePtfxCurrent();
bMoveTo(&dco.ptfxCurrent());
}
else
{
// See if what we think is the current position matches the DC's
// value (this code will be used when there is a path bracket
// and the path is being added to for the first time):
POINTFIX ptfx = ptfxGetCurrent();
if (dco.ptfxCurrent().x != ptfx.x ||
dco.ptfxCurrent().y != ptfx.y)
bMoveTo(&dco.ptfxCurrent());
}
}
}
}
/******************************Public*Routine******************************\
* PATHSTACKOBJ::~PATHSTACKOBJ()
*
* Deletes the path if it isn't the DC path.
*
* History:
* 22-Mar-1992 -by- J. Andrew Goossen [andrewgo]
* Wrote it.
\**************************************************************************/
PATHSTACKOBJ::~PATHSTACKOBJ()
{
// If we ran out of memory allocating blocks, we would have deleted the path,
// in which case ppath may be NULL:
if (ppath != (PATH*) NULL)
{
ASSERTGDI(ppath->flType == PATHTYPE_KEEPMEM ||
ppath->flType == PATHTYPE_STACK, "Unexpected path type");
if (ppath->flType & PATHTYPE_STACK)
{
// Path was only temporary; free any additional blocks if there
// are any:
if (ppath->ppachain != (PATHALLOC*) NULL)
vFreeBlocks();
}
else
{
// Path is a permanent path, so unlock object and save some
// accelerator values:
ppath->cCurves = cCurves;
ppath->fl = fl;
DEC_SHARE_REF_CNT(ppath);
}
}
}
/******************************Public*Routine******************************\
* RECTANGLEPATHOBJ::vInit(prcfx, bClockwise)
*
* Initializes the path with a single rectangle. Should probably only ever
* be called from Rectangle.
*
* History:
* 13-Dec-1992 -by- J. Andrew Goossen [andrewgo]
* Wrote it.
\**************************************************************************/
VOID RECTANGLEPATHOBJ::vInit(
RECTL* prcl, // Must be well-ordered
BOOL bClockwise) // Direction rectangle is to be drawn
{
prRect.pr.pprnext = (PATHRECORD*) NULL;
prRect.pr.pprprev = (PATHRECORD*) NULL;
prRect.pr.flags = (PD_BEGINSUBPATH | PD_ENDSUBPATH |
PD_RESETSTYLE | PD_CLOSEFIGURE);
prRect.pr.count = 4;
path.pprfirst = &prRect.pr;
path.pprlast = &prRect.pr;
path.flags = 0;
ppath = &path;
cCurves = 4;
fl = 0;
// Initialize PATHRECORD variables. When drawing in the counter-
// clockwise direction, we draw the vertices in the following order
// (this must match the EBOX constructor):
//
// 1 ___ 0
// | |
// |___|
// 2 3
path.rcfxBoundBox.xLeft = LTOFX(prcl->left);
prRect.pr.aptfx[1].x = path.rcfxBoundBox.xLeft;
prRect.pr.aptfx[2].x = path.rcfxBoundBox.xLeft;
path.rcfxBoundBox.xRight = LTOFX(prcl->right);
prRect.pr.aptfx[0].x = path.rcfxBoundBox.xRight;
prRect.pr.aptfx[3].x = path.rcfxBoundBox.xRight;
path.rcfxBoundBox.yTop = LTOFX(prcl->top);
path.rcfxBoundBox.yBottom = LTOFX(prcl->bottom);
if (!bClockwise)
{
prRect.pr.aptfx[0].y = path.rcfxBoundBox.yTop;
prRect.pr.aptfx[1].y = path.rcfxBoundBox.yTop;
prRect.pr.aptfx[2].y = path.rcfxBoundBox.yBottom;
prRect.pr.aptfx[3].y = path.rcfxBoundBox.yBottom;
}
else
{
prRect.pr.aptfx[2].y = path.rcfxBoundBox.yTop;
prRect.pr.aptfx[3].y = path.rcfxBoundBox.yTop;
prRect.pr.aptfx[0].y = path.rcfxBoundBox.yBottom;
prRect.pr.aptfx[1].y = path.rcfxBoundBox.yBottom;
}
}
/******************************Public*Routine******************************\
* EngCreatePath()
*
* DDI entry point to create a temporary path.
*
* History:
* 17-Feb-1992 -by- J. Andrew Goossen
* Wrote it.
\**************************************************************************/
PATHOBJ* EngCreatePath()
{
PATHMEMOBJ pmo;
if (!pmo.bValid())
return((PATHOBJ*) NULL);
EPATHOBJ* pepo = (EPATHOBJ*) PALLOCMEM(sizeof(EPATHOBJ), 'tapG');
if (pepo == (EPATHOBJ*) NULL)
return((PATHOBJ*) NULL);
pmo.vKeepIt();
pepo->vLock(pmo.hpath());
return(pepo);
}
/******************************Public*Routine******************************\
* EngDeletePath()
*
* DDI entry point to delete a path allocated by EngCreatePath().
*
* History:
* 17-Feb-1992 -by- J. Andrew Goossen
* Wrote it.
\**************************************************************************/
VOID EngDeletePath(PATHOBJ* ppo)
{
if (ppo != (PATHOBJ*) NULL)
{
((EPATHOBJ*) ppo)->vDelete();
VFREEMEM((PVOID) ppo);
}
else
WARNING("ERROR: EngDeletePath given NULL pointer");
}
/******************************Public*Routine******************************\
* EPATHOBJ::vFreeBlocks()
*
* Frees all the heap blocks in a path.
*
* History:
* 13-Sep-1991 -by- J. Andrew Goossen [andrewgo]
* Wrote it.
\**************************************************************************/
VOID EPATHOBJ::vFreeBlocks()
{
ASSERTGDI(ppath != (PATH*) NULL, "Trying to free freed path!");
// Free all the blocks in the path:
PATHALLOC* ppa = ppath->ppachain;
while (ppa != (PATHALLOC*) NULL)
{
PATHALLOC* ppasave = ppa->ppanext;
// Don't free blocks that aren't PATHALLOCSIZE in size because
// those were created on the stack:
if (ppa->siztPathAlloc == PATHALLOCSIZE)
{
ASSERTGDI(ppa->siztPathAlloc != PATHSTACKALLOCSIZE,
"PATHALLOCSIZE and PATHSTACKALLOCSIZE can't be the same!");
freepathalloc(ppa);
}
ppa = ppasave;
}
}
/******************************Public*Routine******************************\
* EPATHOBJ::vDelete()
*
* Delete the path and free the handle if there is one. It's polymorphic
* and can handle path type EPATHOBJ or PATHMEMOBJ.
*
* It can't handle PATHSTACKOBJ's or journal paths.
*
* History:
* 13-Sep-1991 -by- J. Andrew Goossen [andrewgo]
* Wrote it.
\**************************************************************************/
VOID EPATHOBJ::vDelete()
{
if (ppath != (PATH*) NULL)
{
ASSERTGDI(!(ppath->flags & PATH_JOURNAL), "Can't delete journal path");
// Free all the blocks in the path:
vFreeBlocks();
if (ppath->flType != PATHTYPE_STACK)
{
// Free the handle too (stack paths don't have handles):
HmgFree((HOBJ) ppath->hGet());
ppath = (PATH*) NULL; // Prevent destructors from doing anything
}
}
}
/******************************Public*Routine******************************\
* EPATHOBJ::bClone(epo)
*
* Copy the specified path.
*
* History:
* 22-Mar-1992 -by- J. Andrew Goossen [andrewgo]
* Wrote it.
\**************************************************************************/
BOOL EPATHOBJ::bClone(EPATHOBJ& epo)
{
// First, copy path information:
fl = epo.fl;
cCurves = epo.cCurves;
ppath->pprfirst = (PATHRECORD*) NULL;
ppath->pprlast = (PATHRECORD*) NULL;
ppath->rcfxBoundBox = epo.ppath->rcfxBoundBox;
ppath->ptfxSubPathStart = epo.ppath->ptfxSubPathStart;
ppath->flags = epo.ppath->flags;
// Copy rest of path on a PATHRECORD by PATHRECORD basis:
PATHRECORD* ppr = epo.ppath->pprfirst;
PATHRECORD* pprPrev = (PATHRECORD*) NULL;
PATHRECORD* pprNew;
while (ppr != (PATHRECORD*) NULL)
{
FLONG fl = ppr->flags;
POINTFIX* pptfx = ppr->aptfx;
COUNT cpt = ppr->count;
COUNT cptMax;
while (cpt > 0)
{
if (!newpathrec(&pprNew,&cptMax,cpt))
return(FALSE);
pprNew->flags = fl;
pprNew->pprprev = pprPrev;
pprNew->pprnext = (PATHRECORD*) NULL;
if (cpt <= cptMax)
pprNew->count = cpt;
else
{
// Have to copy this PATHRECORD into two separate PATHRECORDs,
// so adjust the count and clean up the flags:
// Adjust cptMax for Beziers.
if (fl & PD_BEZIERS)
{
if (fl & PD_BEGINSUBPATH)
cptMax -= (cptMax-1) % 3;
else
cptMax -= cptMax % 3;
}
pprNew->count = cptMax;
pprNew->flags &= ~(PD_ENDSUBPATH | PD_CLOSEFIGURE);
fl &= ~(PD_BEGINSUBPATH | PD_RESETSTYLE);
}
ppath->pprlast = pprNew;
if (pprPrev == (PATHRECORD*) NULL)
ppath->pprfirst = pprNew;
else
pprPrev->pprnext = pprNew;
RtlCopyMemory(pprNew->aptfx,
pptfx,
(SIZE_T) pprNew->count * sizeof(POINTFIX));
pptfx += pprNew->count;
cpt -= pprNew->count;
// Adjust the PATHALLOC record:
ppath->ppachain->pprfreestart = NEXTPATHREC(pprNew);
pprPrev = pprNew;
}
ppr = ppr->pprnext;
}
return(TRUE);
}
/******************************Public*Routine******************************\
* EPATHOBJ::cjSize(epo)
*
* Compute the size needed if a single PATHALLOC was to hold the entire
* path.
*
* History:
* 20-May-1992 -by- Paul Butzi
* Wrote it.
\**************************************************************************/
ULONGSIZE_T EPATHOBJ::cjSize()
{
ULONGSIZE_T cjRV = 0;
// Run down the path, adding up the sizes of the pathrecords.
PATHRECORD* ppr = ppath->pprfirst;
while (ppr != (PATHRECORD*) NULL)
{
cjRV += offsetof(PATHRECORD, aptfx)
+ (ULONGSIZE_T)ppr->count * sizeof(POINTFIX);
ppr = ppr->pprnext;
}
return(cjRV);
}
/******************************Member*Function*****************************\
* ULONG EPATHOBJ::cTotalCurves()
*
* Figure out the number of curves in the path for the path's cCurves
* field.
*
* History:
* 6-Apr-1992 -by- J. Andrew Goossen [andrewgo]
* Wrote it.
\**************************************************************************/
ULONG EPATHOBJ::cTotalCurves()
{
ULONG count = 0;
for (PATHRECORD *ppr = ppath->pprfirst;
ppr != (PPATHREC) NULL;
ppr = ppr->pprnext)
{
if (ppr->flags & PD_CLOSEFIGURE)
count++;
if (ppr->flags & PD_BEZIERS)
count += ppr->count / 3;
else
{
count += ppr->count;
if (ppr->flags & PD_BEGINSUBPATH)
count--;
}
}
return(count);
}
/******************************Member*Function*****************************\
* ULONG EPATHOBJ::cTotalPts()
*
* Figure out the number of control points in a path.
*
* History:
* 20-Feb-1991 -by- Wendy Wu [wendywu]
* Wrote it.
\**************************************************************************/
ULONG EPATHOBJ::cTotalPts()
{
ULONG count = 0;
for (PATHRECORD *ppr = ppath->pprfirst;
ppr != (PPATHREC) NULL;
ppr = ppr->pprnext)
{
count += ppr->count;
}
return(count);
}
/******************************Public*Routine******************************\
* EPATHOBJ::bAppend (ppoNew,pptfxDelta) *
* *
* Appends an offset version of the given path (ppoNew) to the target path. *
* *
* Wed 17-Jun-1992 00:21:57 -by- Charles Whitmer [chuckwh] *
* Wrote it. *
\**************************************************************************/
BOOL EPATHOBJ::bAppend(EPATHOBJ *ppoNew,POINTFIX *pptfxDelta)
{
PATHDATAL pd; // Needed to call growlastrec and createrec.
PATHRECORD *ppr; // For enumeration.
POINTFIX ptfx;
for
(
ppr = ppoNew->ppath->pprfirst;
ppr != (PPATHREC) NULL;
ppr = ppr->pprnext
)
{
pd.count = ppr->count;
pd.flags = ppr->flags & PD_BEZIERS;
pd.pptl = (POINTL *) &(ppr->aptfx[0]);
// Call bMoveTo if we are starting a new subpath.
if (ppr->flags & PD_BEGINSUBPATH)
{
ptfx.x = ppr->aptfx[0].x + pptfxDelta->x;
ptfx.y = ppr->aptfx[0].y + pptfxDelta->y;
bMoveTo(XFORMNULL,(POINTL *) &ptfx);
pd.count--;
pd.pptl++;
}
// Copy all the control points.
while (pd.count > 0)
{
if (!createrec(XFORMNULL,&pd,pptfxDelta))
return(FALSE);
}
// Set the CloseFigure flag to close it.
if (ppr->flags & PD_CLOSEFIGURE)
{
ppath->pprlast->flags |= PD_CLOSEFIGURE;
// Indicate that we are starting a new subpath:
ppath->flags |= PD_BEGINSUBPATH;
}
}
// Always reset the PO_ELLIPSE flag (the Ellipse call will set it
// appropriately) and turn on the PO_BEZIERS flag if we added Beziers:
fl &= ~PO_ELLIPSE;
if (ppoNew->fl & PO_BEZIERS)
fl |= PO_BEZIERS;
// Add in the count of curves.
cCurves += ppoNew->cCurves;
return(TRUE);
}
/******************************Public*Routine******************************\
* vOffsetPoints (pptfxDest,pptfxSrc,c,dx,dy) *
* *
* A simple routine that moves points while offsetting them. This is much *
* faster than calling the transform code! *
* *
* Wed 17-Jun-1992 00:54:32 -by- Charles Whitmer [chuckwh] *
* Wrote it. *
\**************************************************************************/
VOID vOffsetPoints
(
POINTFIX *pptfxDest,
POINTFIX *pptfxSrc,
UINT c,
LONG dx,
LONG dy
)
{
while (c--)
{
pptfxDest->x = pptfxSrc->x + dx;
pptfxDest->y = pptfxSrc->y + dy;
pptfxDest++;
pptfxSrc++;
}
}
/******************************Public*Routine******************************\
* EPATHOBJ::addpts()
*
* We add the specified points to the path. If possible, we tack the
* points onto the last record ('grow the last record') otherwise we
* create one or more records and stash the points in them.
*
* History:
* 1-Oct-1990 -by- Paul Butzi [paulb]
* Wrote it.
\**************************************************************************/
BOOL EPATHOBJ::addpoints(EXFORMOBJ *pxfo, PATHDATAL *ppd)
{
// No points always succeeds!
if (ppd->count == 0)
return(TRUE);
// Try to add to end of last record. Can't do it if:
//
// - beginsubpath flag set in ppath (must be new record)
//
// growlastrec may do nothing, in which case we just
// fall thru and add records.
if ((ppath->flags & PD_BEGINSUBPATH) == 0)
growlastrec(pxfo,ppd,(POINTFIX *) NULL);
// Now add new records until we're done. Note that if BEGINSUBPATH is
// set in the path, it means that we must add in the current position
// before the points in the ppd; this is handled by createrec.
while (ppd->count > 0)
{
if (!createrec(pxfo,ppd,(POINTFIX *) NULL)) // Adds a new pathalloc
return(FALSE); // when needed.
}
// Always reset the PO_ELLIPSE flag (the Ellipse call will set it
// appropriately) and turn on the PO_BEZIERS flag if we added Beziers:
fl &= ~PO_ELLIPSE;
if (ppd->flags & PD_BEZIERS)
fl |= PO_BEZIERS;
return(TRUE);
}
/******************************Public*Routine******************************\
* EPATHOBJ::growlastrec() *
* *
* growlastrec - add data to the already existing last record in path. *
* *
* Note: *
* If the PD_BEGINSUBPATH flag is set in the path header, then *
* it means that the current position has *not* been put in the path, *
* and it must be added to the path before we begin adding more *
* control points. Note that this cannot happen in this routine, *
* since we cannot both start a new path and extend an existing *
* record at the same time. *
* *
* History: *
* Wed 17-Jun-1992 00:15:06 -by- Charles Whitmer [chuckwh] *
* Added the pptfxDelta parameter. *
* *
* 1-Oct-1990 -by- Paul Butzi [paulb] *
* Wrote it. *
\**************************************************************************/
void EPATHOBJ::growlastrec(EXFORMOBJ *pxfo,PATHDATAL *ppd,POINTFIX *pptfxDelta)
{
PATHALLOC *ppa = ppath->ppachain;
PATHRECORD *ppr = ppath->pprlast;
// Check conditions for adding to last record
//
// 1. there must be a last record
// 2. there must be an existing pathalloc
// 3. the path data to be added must not start a new
// sub-path
// 4. the other flags must match (bezier, etc.)
if ( ppr == (PPATHREC) NULL )
return;
if ( ppa == (PATHALLOC*) NULL )
return;
if (ppd->flags !=
(ppr->flags & ~(PD_BEGINSUBPATH | PD_ENDSUBPATH)))
{
return;
}
// Figure out how much we can expand the current last record.
//
// NOTE: pointer casts are below are safe, since both the
// start and end of a pathalloc block are aligned on
// the most restrictive boundary. These alignment points
// are the *only* safe points to cast between types, since
// only there are we guaranteed to be safely aligned.
POINTFIX *oldend = &(ppr->aptfx[ppr->count]);
POINTFIX *newend = (POINTFIX *)((char *)ppa + ppa->siztPathAlloc);
ULONGSIZE_T maxadd = 0;
if ( newend > oldend )
{
//Sundown truncation
ASSERT4GB((ULONGLONG)(newend - oldend));
maxadd = (ULONG)(newend - oldend);
}
// Don't add more than we need!
if ( maxadd > ppd->count )
maxadd = (ULONGSIZE_T)ppd->count;
// We must add a multiple of 3 points for Beziers.
if (ppd->flags & PD_BEZIERS)
maxadd -= maxadd % 3;
if (maxadd == 0)
return;
// Copy the points:
if (pptfxDelta != (POINTFIX *) NULL)
{
vOffsetPoints(
&(ppr->aptfx[ppr->count]),
(POINTFIX *) ppd->pptl,
maxadd,
pptfxDelta->x,
pptfxDelta->y
);
}
else if (pxfo == XFORMNULL)
RtlCopyMemory(&(ppr->aptfx[ppr->count]),
ppd->pptl,
(SIZE_T) maxadd * sizeof(POINTFIX));
else
pxfo->bXformRound(ppd->pptl, &(ppr->aptfx[ppr->count]), maxadd);
ASSERTGDI((CHAR*) &ppr->aptfx[ppr->count + maxadd] <=
(CHAR*) ppa + ppa->siztPathAlloc,
"Overwrote bounds!");
ASSERTGDI((CHAR*) &(ppr->aptfx[ppr->count]) > (CHAR*) ppa, "Bad bound");
// Adjust the bounding box:
register POINTFIX *pptfx = &ppr->aptfx[ppr->count];
for ( register ULONG i = 0; i < maxadd; i += 1, pptfx += 1 )
{
((ERECTFX*) &ppath->rcfxBoundBox)->vInclude(*pptfx);
}
// adjust the path record
ppr->count += maxadd;
// adjust the pathalloc record
ppa->pprfreestart = NEXTPATHREC(ppr);
ASSERTGDI((CHAR*) ppa->pprfreestart <= (CHAR*) ppa + ppa->siztPathAlloc,
"Weird freestart");
// adjust the pathdata record
ppd->count -= maxadd;
ppd->pptl += maxadd;
}
/******************************Public*Routine******************************\
* EPATHOBJ::reinit() *
* Reinitialize an existing path so that it becomes an empty path. *
* This is useful in error cases when the path cannot be restored, *
* but also cannot be deleted (because the handle is in a DC). See *
* bug 177612 for details. *
* *
* History: *
* *
* 11-Aug-1998 -by- Ori Gershony [orig] *
* Wrote it. *
\**************************************************************************/
VOID EPATHOBJ::reinit()
{
if (ppath != (PATH *) NULL)
{
ASSERTGDI(!(ppath->flags & PATH_JOURNAL), "Can't delete journal path");
// Free all the blocks in the path:
vFreeBlocks();
// And reinitialize the path data
ppath->ppachain = (PATHALLOC*) NULL;
ppath->pprfirst = (PATHRECORD*) NULL;
ppath->pprlast = (PATHRECORD*) NULL;
ppath->rcfxBoundBox.xLeft = 0;
ppath->rcfxBoundBox.xRight = 0;
ppath->rcfxBoundBox.yTop = 0;
ppath->rcfxBoundBox.yBottom = 0;
ppath->ptfxSubPathStart.x = 0;
ppath->ptfxSubPathStart.y = 0;
ppath->flags = PD_BEGINSUBPATH | PD_ENDSUBPATH;
ppath->pprEnum = (PATHRECORD *) NULL;
// Don't clear flType - it's used by the destructor to determine
// how we allocated the memory and hence how to dispose of it correctly.
//ppath->flType = 0;
ppath->fl = 0;
ppath->cCurves = 0;
// don't need to initialize ppath->cle, because it will be initialized by vEnumPathStart
fl = 0;
cCurves = 0;
}
}
/******************************Public*Routine******************************\
* EPATHOBJ::createrec() *
* *
* Add a new path record to the path, taking control points and flags *
* from the pathdata struct passed. We try to fit things in the *
* last pathdata block if we can sensibly do it, but otherwise we *
* allocate a new pathdata and tack it on the end. Note that a pathdata *
* may not be filled completely if it isn't convenient. *
* *
* History: *
* Wed 17-Jun-1992 00:15:06 -by- Charles Whitmer [chuckwh] *
* Added the pptfxDelta parameter. *
* *
* 1-Oct-1990 -by- Paul Butzi [paulb] *
* Wrote it. *
\**************************************************************************/
BOOL EPATHOBJ::createrec(EXFORMOBJ *pxfo,PATHDATAL *ppd,POINTFIX *pptfxDelta)
{
PATHALLOC *ppa = ppath->ppachain;
ULONGSIZE_T maxadd = 0; // # of pts we can fit into zero space
if ( ppa != (PATHALLOC*) NULL )
{
// We have a current pathalloc, see how much will fit.
// Computation done into temps to avoid compiler assertion!
POINTFIX *start = &(ppa->pprfreestart->aptfx[0]);
POINTFIX *end = (POINTFIX *)((char *)ppa + ppa->siztPathAlloc);
if (end > start)
//Sundown safe truncation
maxadd =(ULONG)(end - start);
}
// Decide if we need to enter the current position before adding
// the points in the pathdata. cPoints gets the count of how many
// we add; either zero or one.
COUNT cPoints = (ppath->flags & PD_BEGINSUBPATH) ? 1 : 0;
// We must add a multiple of 3 points for Beziers.
if ((ppd->flags & PD_BEZIERS) && (maxadd > 0))
maxadd -= (maxadd - (ULONGSIZE_T)cPoints) % 3;
// At this point, 'maxadd' indicates how many points we could get
// into a record if we added it in the current pathalloc.
// Now we can decide if we need a new pathalloc
if ( (maxadd < (cPoints + ppd->count)) && (maxadd < PATHALLOCTHRESHOLD) )
{
// allocate a new pathalloc, link it into path
ppa = newpathalloc();
if (ppa == (PATHALLOC*) NULL)
{
// We're out of memory.
//
// At this point, our PATHALLOC chain is intact (so we can safely
// traverse the chain to free the blocks), but the rest of the path
// data can be corrupt (it's not worth trying to recover in this
// case), so we can't allow the path to be used anymore.
//
// But paths are persistent between API calls when there is an
// active path; we have to mark that the path is invalid, and not
// let any subsequent APIs try to do any operation on it other than
// to delete it.
SAVE_ERROR_CODE(ERROR_NOT_ENOUGH_MEMORY);
// We free all blocks and re-initialize the path. We don't want to delete
// it here because the handle might be stored in a DC (see bug 177612)
reinit();
return(FALSE);
}
ppa->ppanext = ppath->ppachain;
ppath->ppachain = ppa;
// adjust maxadd
ASSERTGDI((CHAR*) ppa + ppa->siztPathAlloc > (CHAR*) ppa->pprfreestart,
"Invalid pprfreeestart");
//Sundown truncation
ASSERT4GB((ULONGLONG)(((char *)ppa + ppa->siztPathAlloc) -
(char *)ppa->pprfreestart));
ULONGSIZE_T numbytes = (ULONG)(((char *)ppa + ppa->siztPathAlloc) -
(char *)ppa->pprfreestart);
maxadd = (ULONGSIZE_T)(numbytes - offsetof(PATHRECORD, aptfx)) /
sizeof(POINTFIX);
// We must add a multiple of 3 points for Beziers.
if (ppd->flags & PD_BEZIERS)
maxadd -= (maxadd - (ULONGSIZE_T)cPoints) % 3;
}
// Don't add more points than we need:
if ( maxadd > (ppd->count + cPoints) )
maxadd = (ULONGSIZE_T)(ppd->count + cPoints);
// Create new pathrec header:
PATHRECORD *ppr = ppa->pprfreestart;
ppr->flags = ppd->flags | PD_ENDSUBPATH;
ppr->count = maxadd;
ppr->pprnext = (PPATHREC)NULL;
ppr->pprprev = ppath->pprlast;
if ( cPoints == 0 )
{
// This new record is a continuation of the old one, so clear
// the previous record's end-subpath flag
if (ppath->pprlast != (PPATHREC) NULL)
ppath->pprlast->flags &= ~PD_ENDSUBPATH;
}
else
{
// This is a new sub-path, so add in the current point
*(ppr->aptfx) = ppath->ptfxSubPathStart;
maxadd -= 1;
ppr->flags |= (ppath->flags & (PD_BEGINSUBPATH | PD_RESETSTYLE));
ppath->flags &= ~(PD_BEGINSUBPATH | PD_RESETSTYLE);;
}
// Copy in the points:
if (pptfxDelta != (POINTFIX *) NULL)
{
vOffsetPoints(
&(ppr->aptfx[cPoints]),
(POINTFIX *) ppd->pptl,
maxadd,
pptfxDelta->x,
pptfxDelta->y
);
}
else if (pxfo == XFORMNULL)
RtlCopyMemory(&(ppr->aptfx[cPoints]),
ppd->pptl,
maxadd * sizeof(POINTFIX));
else
pxfo->bXformRound(ppd->pptl, &(ppr->aptfx[cPoints]), maxadd);
ASSERTGDI((CHAR*) &ppr->aptfx[cPoints + maxadd] <=
(CHAR*) ppa + ppa->siztPathAlloc,
"Overwrote bounds!");
// Adjust the bounding box:
register POINTFIX *pptfx = ppr->aptfx;
if ( ppath->pprlast == (PPATHREC) NULL )
{
// if path was empty, set the bound box to a single pt.
ppath->rcfxBoundBox.xLeft = ppath->rcfxBoundBox.xRight = pptfx->x;
ppath->rcfxBoundBox.yTop = ppath->rcfxBoundBox.yBottom = pptfx->y;
}
for ( register ULONG i = 0; i < maxadd + cPoints; i += 1, pptfx += 1 )
{
((ERECTFX*) &ppath->rcfxBoundBox)->vInclude(*pptfx);
}
// Now that we know we have succeeded, add the pathrec to the path chain
// Prior to doing this, we can back out it's as if we hadn't changed anything.
if (ppath->pprlast == (PPATHREC) NULL )
{
// first pathrec in path!
ppath->pprfirst = ppath->pprlast = ppr;
}
else
{
// add to pathrec chain
ppath->pprlast->pprnext = ppr;
ppath->pprlast = ppr;
}
// Adjust the pathalloc record:
ppa->pprfreestart = NEXTPATHREC(ppr);
ASSERTGDI((CHAR*) ppa->pprfreestart <= (CHAR*) ppa + ppa->siztPathAlloc,
"Weird freestart");
// Adjust a bunch of state:
ppd->count -= maxadd;
ppd->pptl += maxadd;
ppd->flags &= ~(PD_BEGINSUBPATH | PD_RESETSTYLE);
return(TRUE);
}
/******************************Public*Routine******************************\
* PATHOBJ_vGetBounds(ppo, prcfx)
*
* Gets the path bounds.
*
* History:
* 19-Aug-1991 -by- J. Andrew Goossen [andrewgo]
* Wrote it.
\**************************************************************************/
VOID PATHOBJ_vGetBounds(PATHOBJ* ppo, PRECTFX prcfx)
{
*prcfx = ((EPATHOBJ *) ppo)->rcfxBoundBox();
// Make the box lower-right exclusive if not empty (i.e., not {0,0,0,0}):
if (prcfx->yTop || prcfx->xLeft || prcfx->yBottom || prcfx->xRight)
{
prcfx->yBottom += 1;
prcfx->xRight += 1;
}
}
VOID PATHOBJ_vEnumStart(PATHOBJ* ppo)
{
((EPATHOBJ*) ppo)->vEnumStart();
}
/******************************Public*Routine******************************\
* EPATHOBJ::bEnum()
*
* Enumerate the path
*
* History:
* 19-Oct-1990 -by- Paul Butzi [paulb]
* Wrote it.
\**************************************************************************/
BOOL EPATHOBJ::bEnum(PATHDATA *ppd)
{
return(PATHOBJ_bEnum(this, ppd));
}
BOOL PATHOBJ_bEnum(PATHOBJ* ppo, PATHDATA* ppd)
{
PATH* ppath;
LONG i;
PATHRECORD* ppr;
POINTFIX* pptfx;
ppath = ((EPATHOBJ*) ppo)->ppath;
// Detect case where he didn't call bStartEnum()
if (ppath->pprEnum == NULL)
{
if (ppath->pprfirst == NULL)
{
// It's an empty path:
ppd->count = 0;
ppd->flags = 0;
ppd->pptfx = (PPOINTFIX) NULL;
return(FALSE);
}
else
{
// Start again at first record:
ppath->pprEnum = ppath->pprfirst;
}
}
ppr = ppath->pprEnum;
ppd->count = ppr->count;
ppd->flags = ppr->flags;
ppd->pptfx = ppr->aptfx;
ppath->pprEnum = ppr->pprnext;
// By setting PO_ENUM_AS_INTEGERS in the PATHOBJ before calling bEnum,
// the driver signals that it saw the PO_ALL_INTEGERS flag and wants
// the coordinates as integers instead of fixed point. That's great
// because if PO_ALL_INTEGERS was set, that means we recorded the path
// in integer coordinates.
if ((ppo->fl & (PO_ALL_INTEGERS | PO_ENUM_AS_INTEGERS)) == PO_ALL_INTEGERS)
{
// Uh oh, the driver didn't ask for integer coordinates. So we'll
// simply convert the path to fixed coordinates and remove the
// PO_ALL_INTEGERS flag:
ppo->fl &= ~PO_ALL_INTEGERS;
for (ppr = ppath->pprfirst; ppr != NULL; ppr = ppr->pprnext)
{
for (pptfx = ppr->aptfx, i = ppr->count; i != 0; pptfx++, i--)
{
pptfx->x <<= 4;
pptfx->y <<= 4;
}
}
}
if ((ppo->fl & (PO_ALL_INTEGERS | PO_ENUM_AS_INTEGERS)) == PO_ENUM_AS_INTEGERS)
{
// The driver set PO_ENUM_AS_INTEGERS when PO_ALL_INTEGERS wasn't
// set. Tsk, tsk.
RIP("Driver mustn't set PO_ENUM_AS_INTEGERS unless PO_ALL_INTEGERS was set by GDI");
}
return(ppath->pprEnum != (PPATHREC) NULL);
}
/******************************Public*Routine******************************\
* EPATHOBJ::vOffset()
*
* Enumerate the path
*
* History:
* 29-Oct-1990 -by- Paul Butzi [paulb]
* Wrote it.
\**************************************************************************/
VOID EPATHOBJ::vOffset(EPOINTL &eptl)
{
LONG xOffset = LTOFX(eptl.x);
LONG yOffset = LTOFX(eptl.y);
RECTFX *prcfxBoundBox = &ppath->rcfxBoundBox;
prcfxBoundBox->xLeft += xOffset;
prcfxBoundBox->xRight += xOffset;
prcfxBoundBox->yTop += yOffset;
prcfxBoundBox->yBottom += yOffset;
if (fl & PO_ALL_INTEGERS)
{
// If 'PO_ALL_INTEGERS' is set, the path has been recorded as integers
// instead of 28.4 fixed point. So convert the offsets back to integers
// again:
xOffset = FXTOL(xOffset);
yOffset = FXTOL(yOffset);
}
register PATHRECORD* ppr;
for (ppr = ppath->pprfirst; ppr != (PPATHREC) NULL; ppr = ppr->pprnext)
{
for (register EPOINTFIX *peptfx = (EPOINTFIX *)ppr->aptfx;
peptfx < (EPOINTFIX *)&ppr->aptfx[ppr->count];
peptfx += 1)
{
peptfx->x += xOffset;
peptfx->y += yOffset;
}
}
}
/******************************Public*Routine******************************\
* EPATHOBJ::bMoveTo()
*
* Set the current position in the path. Note that the following effects
* also occur:
* - last existing record in path is marked as ending the subpath
* - flag is set indicating that next path record will start a new
* subpath.
*
* History:
* 1-Oct-1990 -by- Paul Butzi [paulb]
* Wrote it.
\**************************************************************************/
BOOL EPATHOBJ::bMoveTo(EXFORMOBJ *pxfo, PPOINTL pptl)
{
// we can fail if the driver got a previous out of memory error
if (!bValid())
return(FALSE);
if (pxfo == XFORMNULL)
ppath->ptfxSubPathStart = *((PPOINTFIX) pptl);
else
{
pxfo->bXformRound(pptl, &ppath->ptfxSubPathStart, 1);
}
ppath->flags |= (PD_BEGINSUBPATH | PD_RESETSTYLE);
return(TRUE);
}
BOOL PATHOBJ_bMoveTo(PATHOBJ* ppo, POINTFIX ptfx)
{
return(((EPATHOBJ*) ppo)->bMoveTo(XFORMNULL, (PPOINTL) &ptfx));
}
/******************************Public*Routine******************************\
* EPATHOBJ::bCloseFigure()
*
* close the current subpath in the path.
*
* History:
* 8-Nov-1990 -by- Paul Butzi [paulb]
* Wrote it.
\**************************************************************************/
BOOL EPATHOBJ::bCloseFigure()
{
// we can fail if the driver got a previous out of memory error
if (!bValid())
return(FALSE);
if (ppath->pprlast != (PATHRECORD*) NULL)
{
if (!(ppath->pprlast->flags & PD_CLOSEFIGURE))
{
ppath->pprlast->flags |= PD_CLOSEFIGURE;
cCurves++;
}
}
// Indicate that we are starting a new subpath:
ppath->flags |= PD_BEGINSUBPATH;
// We don't have to update ptfxSubPathStart because the current position
// after a CloseFigure is set to the sub-path start point.
return(TRUE);
}
BOOL PATHOBJ_bCloseFigure(PATHOBJ* ppo)
{
return(((EPATHOBJ*) ppo)->bCloseFigure());
}
/******************************Public*Routine******************************\
* EPATHOBJ::vCloseAllFigures()
*
* Closes any open figures in the path. Used for FillPath and
* StrokeAndFillPath.
*
* History:
* 17-Sep-1991 -by- J. Andrew Goossen [andrewgo]
* Wrote it.
\**************************************************************************/
VOID EPATHOBJ::vCloseAllFigures()
{
PPATHREC ppr = ppath->pprfirst;
while (ppr != (PPATHREC) NULL)
{
if (ppr->flags & PD_ENDSUBPATH)
{
if (!(ppr->flags & PD_CLOSEFIGURE))
{
ppr->flags |= PD_CLOSEFIGURE;
cCurves++;
}
}
ppr = ppr->pprnext;
}
}
/******************************Public*Routine******************************\
* EPATHOBJ::bPolyLineTo()
*
* Draw lines from the current position in the path thru the specified
* points. Sets the current position to the last specified point.
*
* History:
* 1-Oct-1990 -by- Paul Butzi [paulb]
* Wrote it.
\**************************************************************************/
BOOL EPATHOBJ::bPolyLineTo(EXFORMOBJ *pxfo, PPOINTL pptl, ULONG cPts)
{
// we can fail if the driver got a previous out of memory error
if (!bValid())
return(FALSE);
PATHDATAL pd;
BOOL bRet;
pd.flags = 0;
pd.pptl = pptl;
pd.count = cPts;
bRet = addpoints(pxfo, &pd);
if (bRet)
cCurves += cPts;
return(bRet);
}
BOOL PATHOBJ_bPolyLineTo(PATHOBJ* ppo, PPOINTFIX pptfx, ULONG cptfx)
{
// NULL transform indicates that the points are already in device space:
return(((EPATHOBJ*) ppo)->bPolyLineTo(XFORMNULL,
(PPOINTL) pptfx,
cptfx));
}
/******************************Public*Routine******************************\
* EPATHOBJ::bPolyBezierTo()
*
* Draw curves from the current position in the path thru the specified
* points. Sets the current position to the last specified point.
*
* History:
* 1-Oct-1990 -by- Paul Butzi [paulb]
* Wrote it.
\**************************************************************************/
BOOL EPATHOBJ::bPolyBezierTo(EXFORMOBJ *pxfo, PPOINTL pptl, ULONG cPts)
{
// we can fail if the driver got a previous out of memory error
if (!bValid())
return(FALSE);
PATHDATAL pd;
BOOL bRet;
ASSERTGDI(cPts % 3 == 0, "Weird number of Bezier points");
pd.flags = PD_BEZIERS;
pd.pptl = pptl;
pd.count = cPts;
bRet = addpoints(pxfo, &pd);
if (bRet)
cCurves += cPts / 3;
return(bRet);
}
BOOL PATHOBJ_bPolyBezierTo(PATHOBJ* ppo, PPOINTFIX pptfx, ULONG cptfx)
{
// NULL transform indicates that the points are already in device space:
return(((EPATHOBJ*) ppo)->bPolyBezierTo(XFORMNULL,
(PPOINTL) pptfx,
cptfx));
}
BOOL EPATHOBJ::bTextOutSimpleFill(
XDCOBJ& dco,
RFONTOBJ& rfo,
PDEVOBJ* pdo,
SURFACE* pSurf,
CLIPOBJ* pco,
BRUSHOBJ* pbo,
POINTL* pptlBrushOrg,
MIX mix,
FLONG flOptions)
{
BOOL bSem = FALSE, bRet;
ULONG fl = 0, numLinks = 0;
BOOL aFaceLink[UMPD_MAX_FONTFACELINK], *pFaceLink = aFaceLink;
//
// Release rfont semaphores, otherwise holding rfont semaphores can
// disable APC queue while calling to the user mode.
//
//
// WINBUG #214225 tessiew 10-27-2000 Blackcomb: re-visit the RFONT.hsemCace acquiring/releasing issue
// Need to revisit the font semaphore problem in Blackcomb
// It seems that a thread doesn't need to hold the font caching semaphore
// during the whole GreExtTextOutWLocked call.
//
if (dco.bPrinter() && dco.bUMPD() && rfo.bValid())
{
bSem = UMPDReleaseRFONTSem(rfo, NULL, &fl, &numLinks, &pFaceLink);
}
bRet = bSimpleFill(dco.flGraphicsCaps(),
pdo,
pSurf,
pco,
pbo,
pptlBrushOrg,
mix,
flOptions);
if (bSem)
{
UMPDAcquireRFONTSem(rfo, NULL, fl, numLinks, pFaceLink);
if (pFaceLink && pFaceLink != aFaceLink)
{
VFREEMEM(pFaceLink);
}
}
return bRet;
}
BOOL EPATHOBJ::bTextOutSimpleStroke1(
XDCOBJ& dco,
RFONTOBJ& rfo,
PDEVOBJ* plo,
SURFACE* pSurface,
CLIPOBJ* pco,
BRUSHOBJ* pbo,
POINTL* pptlBrushOrg,
MIX mix)
{
BOOL bSem = FALSE, bRet;
ULONG fl = 0, numLinks = 0;
BOOL aFaceLink[UMPD_MAX_FONTFACELINK], *pFaceLink = aFaceLink;
//
// Release rfont semaphores, otherwise holding rfont semaphores can
// disable APC queue while calling to the user mode.
//
//
// WINBUG #214225 tessiew 10-27-2000 Blackcomb: re-visit the RFONT.hsemCace acquiring/releasing issue
// Need to revisit the font semaphore problem in Blackcomb
// It seems that a thread doesn't need to hold the font caching semaphore
// during the whole GreExtTextOutWLocked call.
//
if (dco.bPrinter() && dco.bUMPD() && rfo.bValid())
{
bSem = UMPDReleaseRFONTSem(rfo, NULL, &fl, &numLinks, &pFaceLink);
}
LINEATTRS laTmp = glaSimpleStroke.la;
bRet = bSimpleStroke(dco.flGraphicsCaps(),
plo,
pSurface,
pco,
(XFORMOBJ*) NULL,
pbo,
pptlBrushOrg,
&laTmp,
mix);
if (bSem)
{
UMPDAcquireRFONTSem(rfo, NULL, fl, numLinks, pFaceLink);
if (pFaceLink && pFaceLink != aFaceLink)
{
VFREEMEM(pFaceLink);
}
}
return bRet;
}
/******************************Member*Function*****************************\
* EPATHOBJ::bSimpleFill(flCaps, plo, pSurf, pco, pbo, pptlBrushOrg,
* mix, flOptions)
*
* Fill the path, accounting for smart devices.
*
* History:
* 7-Apr-1992 -by- J. Andrew Goossen [andrewgo]
* Wrote it.
\**************************************************************************/
BOOL EPATHOBJ::bSimpleFill(
FLONG flCaps, // For device graphics caps
PDEVOBJ* pdo,
SURFACE* pSurf,
CLIPOBJ* pco,
BRUSHOBJ* pbo,
POINTL* pptlBrushOrg,
MIX mix,
FLONG flOptions)
{
BOOL bRet;
#if DBG
ASSERTGDI(bAllClosed(), "A sub-path in the filled path wasn't closed.");
#endif
ASSERTGDI(cTotalCurves() == cCurves, "Messed up curve count somewhere.");
ASSERTGDI(mix & 0xff00, "Background mix uninitialized");
// The DDI restricts all paths to a 2^27 by 2^27 pixel space to allow
// device drivers to compute deltas using 32-bit integers without
// overflowing.
if (((rcfxBoundBox().xRight - rcfxBoundBox().xLeft) < 0) ||
((rcfxBoundBox().yBottom - rcfxBoundBox().yTop) < 0))
{
return(FALSE);
}
if (cCurves == 0)
{
return(TRUE);
}
if (pSurf->flags() & HOOK_FillPath)
{
if (((flOptions & WINDING) && (flCaps & GCAPS_WINDINGFILL)) ||
(!(flOptions & WINDING) && (flCaps & GCAPS_ALTERNATEFILL)))
{
if (bBeziers())
{
if (flCaps & GCAPS_BEZIERS)
{
// The driver says it can handle Beziers, so try giving it
// the path, Beziers and all:
ASSERTGDI(PPFNVALID(*pdo,FillPath),
"Driver hooked FillPath but didn't supply routine");
INC_SURF_UNIQ(pSurf);
bRet = (*PPFNDRV(*pdo,FillPath)) (
pSurf->pSurfobj(),
(PATHOBJ*) this,
pco,
pbo,
pptlBrushOrg,
mix,
flOptions);
if (bRet == TRUE)
return(TRUE);
else if (bRet == DDI_ERROR)
return(FALSE);
}
// If there's complex clipping, the driver might not want to
// render Beziers itself, so we'll flatten it and try again
// (or maybe the driver doesn't handle Beziers at all):
if (!bFlatten())
return(FALSE);
}
ASSERTGDI(PPFNVALID(*pdo,FillPath),
"Driver hooked FillPath but didn't supply routine");
INC_SURF_UNIQ(pSurf);
bRet = (*PPFNDRV(*pdo,FillPath)) (
pSurf->pSurfobj(),
(PATHOBJ*) this,
pco,
pbo,
pptlBrushOrg,
mix,
flOptions);
if (bRet == TRUE)
return(TRUE);
else if (bRet == DDI_ERROR)
return(FALSE);
}
}
INC_SURF_UNIQ(pSurf);
return(EngFillPath(pSurf->pSurfobj(),
(PATHOBJ*) this,
pco,
pbo,
pptlBrushOrg,
mix,
flOptions));
}
/******************************Member*Function*****************************\
* EPATHOBJ::bSimpleStroke(flCaps, pdo, pSurf, pco, pxo, pbo, pptlBrushOrg,
* pla, mix)
*
* Stroke the path, accounting for smart devices.
*
* History:
* 7-Apr-1992 -by- J. Andrew Goossen [andrewgo]
* Wrote it.
\**************************************************************************/
BOOL EPATHOBJ::bSimpleStroke(
FLONG flCaps, // For device graphics caps
PDEVOBJ* pdo,
SURFACE* pSurf,
CLIPOBJ* pco,
XFORMOBJ* pxo,
BRUSHOBJ* pbo,
POINTL* pptlBrushOrg,
LINEATTRS* pla,
MIX mix)
{
BOOL bRet;
// The DDI restricts all paths to a 2^27 by 2^27 pixel space to allow
// device drivers to compute deltas using 32-bit integers without
// overflowing.
if (((rcfxBoundBox().xRight - rcfxBoundBox().xLeft) < 0) ||
((rcfxBoundBox().yBottom - rcfxBoundBox().yTop) < 0))
{
return(FALSE);
}
if (cCurves == 0)
{
return(TRUE);
}
// the real thing
ASSERTGDI(cTotalCurves() == cCurves, "Messed up curve count somewhere.");
ASSERTGDI(mix & 0xff00, "Background mix uninitialized");
INC_SURF_UNIQ(pSurf);
if (pSurf->flags() & HOOK_StrokePath)
{
// Pass the path to the driver if it's not a wide line, or if the driver
// says it can take wide lines:
if (!(pla->fl & LA_GEOMETRIC) || (flCaps & GCAPS_GEOMETRICWIDE))
{
if (bBeziers())
{
if (flCaps & GCAPS_BEZIERS)
{
// The driver says it can handle Beziers, so try giving it
// the path, Beziers and all:
ASSERTGDI(PPFNVALID(*pdo,StrokePath),
"Driver hooked StrokePath but didn't supply routine");
bRet = (*PPFNDRV(*pdo,StrokePath)) (
pSurf->pSurfobj(),
(PATHOBJ*) this,
pco,
pxo,
pbo,
pptlBrushOrg,
pla,
mix);
if (bRet == TRUE)
return(TRUE);
else if (bRet == DDI_ERROR)
return(FALSE);
}
// If there's complex clipping, the driver might not want to
// render Beziers itself, so we'll flatten it and try again
// (or maybe the driver doesn't handle Beziers at all):
if (!bFlatten())
return(FALSE);
}
ASSERTGDI(PPFNVALID(*pdo,StrokePath),
"Driver hooked StrokePath but didn't supply routine");
bRet = (*PPFNDRV(*pdo,StrokePath)) (
pSurf->pSurfobj(),
(PATHOBJ*) this,
pco,
pxo,
pbo,
pptlBrushOrg,
pla,
mix);
if (bRet == TRUE)
return(TRUE);
else if (bRet == DDI_ERROR)
return(FALSE);
}
}
if (pla->fl & LA_GEOMETRIC)
{
//
// Handle wide lines, remembering that the widened bounds have
// already been computed:
//
if (!bWiden(pxo, pla))
return(FALSE);
return(bSimpleFill(flCaps,
pdo,
pSurf,
pco,
pbo,
pptlBrushOrg,
mix,
WINDING));
}
//
// Pass it off to the engine:
//
return(EngStrokePath(pSurf->pSurfobj(),
(PATHOBJ*) this,
pco,
pxo,
pbo,
pptlBrushOrg,
pla,
mix));
}
/******************************Member*Function*****************************\
* EPATHOBJ::bSimpleStrokeAndFill(flCaps, pdo, pSurf, pco, pxo, pboStroke,
* pla, pboFill, pptlBrushOrg, mix, flOptions)
*
* Stroke and fill the path, accounting for smart devices.
*
* History:
* 7-Apr-1992 -by- J. Andrew Goossen [andrewgo]
* Wrote it.
\**************************************************************************/
BOOL EPATHOBJ::bSimpleStrokeAndFill(
FLONG flCaps, // For device graphics caps
PDEVOBJ* pdo,
SURFACE* pSurf,
CLIPOBJ* pco,
XFORMOBJ* pxo,
BRUSHOBJ* pboStroke,
LINEATTRS* pla,
BRUSHOBJ* pboFill,
POINTL* pptlBrushOrg,
MIX mix,
FLONG flOptions)
{
BOOL bRet;
#if DBG
ASSERTGDI(bAllClosed(), "A sub-path in the filled path wasn't closed.");
#endif
ASSERTGDI(cTotalCurves() == cCurves, "Messed up curve count somewhere.");
ASSERTGDI(mix & 0xff00, "Background mix uninitialized");
// The DDI restricts all paths to a 2^27 by 2^27 pixel space to allow
// device drivers to compute deltas using 32-bit integers without
// overflowing.
if (((rcfxBoundBox().xRight - rcfxBoundBox().xLeft) < 0) ||
((rcfxBoundBox().yBottom - rcfxBoundBox().yTop) < 0))
{
return(FALSE);
}
if (cCurves == 0)
{
return(TRUE);
}
INC_SURF_UNIQ(pSurf);
if (pSurf->flags() & HOOK_StrokeAndFillPath)
{
// Pass the path to the driver if either it's not a wide line, or
// if the driver says it can take wide lines:
if (!(pla->fl & LA_GEOMETRIC) || (flCaps & GCAPS_GEOMETRICWIDE))
{
if (bBeziers())
{
if (flCaps & GCAPS_BEZIERS)
{
// The driver says it can handle Beziers, so try giving it
// the path, Beziers and all:
ASSERTGDI(PPFNVALID(*pdo,StrokeAndFillPath),
"Driver hooked StrokeAndFillPath but didn't supply routine");
bRet = (*PPFNDRV(*pdo,StrokeAndFillPath)) (
pSurf->pSurfobj(),
(PATHOBJ*) this,
pco,
pxo,
pboStroke,
pla,
pboFill,
pptlBrushOrg,
mix,
flOptions);
if (bRet == TRUE)
return(TRUE);
else if (bRet == DDI_ERROR)
return(FALSE);
}
// If there's complex clipping, the driver might not want to
// render Beziers itself, so we'll flatten it and try again
// (or maybe the driver doesn't handle Beziers at all):
if (!bFlatten())
return(FALSE);
}
ASSERTGDI(PPFNVALID(*pdo,StrokeAndFillPath),
"Driver hooked StrokeAndFillPath but didn't supply routine");
bRet = (*PPFNDRV(*pdo,StrokeAndFillPath)) (
pSurf->pSurfobj(),
(PATHOBJ*) this,
pco,
pxo,
pboStroke,
pla,
pboFill,
pptlBrushOrg,
mix,
flOptions);
if (bRet == TRUE)
return(TRUE);
else if (bRet == DDI_ERROR)
return(FALSE);
}
}
BOOL bDemote = FALSE;
// Can demote into separate Fill and Stroke calls if we're not doing a
// wide-line, or if we're doing a SRCCOPY and the display is raster, we
// can also demote (because in that case, we don't mind if we re-light
// pixels):
if (!(pla->fl & LA_GEOMETRIC))
bDemote = TRUE;
else if ((mix & 0xFF) == R2_COPYPEN)
{
PDEVOBJ po(pSurf->hdev());
if (po.ulTechnology() == DT_RASDISPLAY ||
po.ulTechnology() == DT_RASPRINTER)
bDemote = TRUE;
}
if (bDemote)
{
MIX mixFill, mixStroke;
mixFill = mixStroke = mix;
if (!((EBRUSHOBJ *)pboFill)->bIsMasking())
{
mixFill = (mix & 0xff) | ((mix & 0xff) << 8);
}
if (!((EBRUSHOBJ *)pboStroke)->bIsMasking())
{
mixStroke = (mix & 0xff) | ((mix & 0xff) << 8);
}
return(bSimpleFill(flCaps,
pdo,
pSurf,
pco,
pboFill,
pptlBrushOrg,
mixFill,
flOptions) &&
bSimpleStroke(flCaps,
pdo,
pSurf,
pco,
pxo,
pboStroke,
pptlBrushOrg,
pla,
mixStroke));
}
// Hand off to the engine simulation, which will nicely subtract the regions
// if necessary:
return(EngStrokeAndFillPath(pSurf->pSurfobj(),
(PATHOBJ*) this,
pco,
pxo,
pboStroke,
pla,
pboFill,
pptlBrushOrg,
mix,
flOptions));
}
/******************************Member*Function*****************************\
* EPATHOBJ::bStrokeAndOrFill(dco, pla, pexo, flType)
*
* Stroke and/or fills the path, depending on flType and the current pen
* and brush.
*
* Note that unless the only output operations you're doing is with the
* path, you probably want to do your own locking, pointer exclusion, etc.
* and call bSimpleStroke, bSimpleFill or bSimpleStrokeAndFill.
*
* History:
* 7-Apr-1992 -by- J. Andrew Goossen [andrewgo]
* Wrote it.
\**************************************************************************/
BOOL EPATHOBJ::bStrokeAndOrFill(
XDCOBJ& dco,
LINEATTRS* pla,
EXFORMOBJ* pexo,
FLONG flType)
{
ASSERTGDI(bValid(), "Invalid Path");
ASSERTGDI(dco.bValid(), "Invalid DC");
BOOL bRet = FALSE;
BOOL bOpaqueStyle = FALSE;
LONG lOldStyleState;
MIX mix;
BOOL bCanDither;
FLONG flOriginal;
// If there aren't any points (as may happen with consecutive calls to
// BeginPath and EndPath), we're all done -- don't let this get down to
// the driver, which won't expect it.
if (cCurves == 0)
{
return(TRUE);
}
flOriginal = flType;
// Simplify if NULL pen or NULL brush:
if (dco.pdc->pbrushLine() == gpPenNull)
{
flType &= ~PATH_STROKE;
}
if (dco.pdc->pbrushFill() == gpbrNull)
{
flType &= ~PATH_FILL;
}
// If doing a wide line, have to adjust bounds:
if (flType & PATH_STROKE)
{
if (pla->fl & LA_GEOMETRIC)
{
if (!bComputeWidenedBounds((XFORMOBJ*) pexo, pla))
{
SAVE_ERROR_CODE(ERROR_ARITHMETIC_OVERFLOW);
return(bRet);
}
// We don't support preserving the style state accross DrvStrokePaths
// in Product One for geometric lines, so make sure we always reset
// the style state value:
if (pla->pstyle != (PFLOAT_LONG) NULL)
pla->elStyleState.e = IEEE_0_0F;
}
}
// Compute bound box and make it lower-right exclusive:
ERECTL erclBoundsDevice(ppath->rcfxBoundBox);
erclBoundsDevice.bottom++;
erclBoundsDevice.right++;
if (dco.fjAccum())
dco.vAccumulate(erclBoundsDevice);
// If we're in FULLSCREEN mode, exit with success.
if (dco.bFullScreen()) // If in FULLSCREEN mode, exit.
return(TRUE);
// Lock Rao region, VisRgn. We would really like to do all the flattening,
// widening and converting to regions before we grab this, because they
// are time intensive and the DEVLOCK prevents any other screen updating.
// This might have to change if it becomes a performance issue.
DEVLOCKOBJ dlo(dco);
if (!dlo.bValid())
{
return(dco.bFullScreen());
}
if (dco.bDisplay() && !dco.bRedirection() && !UserScreenAccessCheck())
{
SAVE_ERROR_CODE(ERROR_ACCESS_DENIED);
return(FALSE);
}
ERECTL erclBoundsScreen = erclBoundsDevice;
erclBoundsScreen += dco.eptlOrigin();
vOffset(dco.eptlOrigin());
ECLIPOBJ eco(dco.prgnEffRao(), erclBoundsScreen);
// Might be able to do a quick-out:
if ((dco.dctp() == DCTYPE_INFO) || eco.erclExclude().bEmpty())
{
if (flType & PATH_STROKE)
{
if ((pla->pstyle != (PFLOAT_LONG) NULL && !(pla->fl & LA_GEOMETRIC))
|| pla->fl & LA_ALTERNATE)
{
vUpdateCosmeticStyleState(dco.pSurface(), pla);
}
}
bRet = TRUE;
return(bRet);
}
// Lock the destination surface and the ldev:
SURFACE *pSurfDest = dco.pSurface();
PDEVOBJ po(dco.hdev());
XEPALOBJ epalDest(pSurfDest->ppal());
XEPALOBJ epalDestDC(dco.ppal());
// Realize the brushes:
EBRUSHOBJ *peboPen = dco.peboLine();
EBRUSHOBJ *peboBrush = dco.peboFill();
EBRUSHOBJ *peboStroke = peboPen;
if (flType & PATH_STROKE)
{
ASSERTGDI(pla != (LINEATTRS*) NULL, "Invalid LineAttrs for stroke");
ASSERTGDI(!(pla->fl & LA_GEOMETRIC) || pexo != (EXFORMOBJ*) NULL,
"Invalid xform on geometric line");
if (pla->fl & LA_GEOMETRIC)
{
// The bCanDither flag actually means 'bCanDitherIfBrushSaysSo' --
// the brush has to be marked as ditherable AND bCanDither has to be
// set before the brush is dithered:
bCanDither = TRUE;
// Because of Win3.1 compatibility, when the PS_INSIDEFRAME pen
// is treated as a wideline, we can dither the brush. But the
// dither/can't dither decision depends on the transform,
// and the last time we used this brush we may have realized
// it as non-ditherable.
// PS_INSIDEFRAME pens are reasonably rare, and realizing a brush
// is relatively quick (particularly compared to the wide-line
// rendering time), so if the cached brush is a solid color, we
// simply mark the brush so that it will be re-realized (but only
// for PS_INSIDEFRAME pens):
// if the driver says dither (mainly for 8 color printer devices) we
// can dither even if it is not InsideFrame.
// Note: If the pen is dirty, we'll actually be looking at
// uninitialized fields! But that's okay, because we'd only be
// marking the pen dirty again:
if ((peboPen->iSolidColor != (ULONG) -1) &&
(peboPen->bIsInsideFrame() || po.bCapsForceDither()))
{
dco.ulDirty(dco.ulDirty() | DIRTY_LINE);
}
}
else
{
// Here we've got the opposite case. When the transform is such
// that a PS_INSIDEFRAME pen is treated as a cosmetic pen,
// the brush has to be solid colored:
bCanDither = FALSE;
if (peboPen->iSolidColor == (ULONG) -1)
{
dco.ulDirty(dco.ulDirty() | DIRTY_LINE);
}
}
if (dco.bDirtyBrush(DIRTY_LINE))
{
dco.vCleanBrush(DIRTY_LINE);
peboPen->vInitBrush(dco.pdc,
dco.pdc->pbrushLine(),
epalDestDC, epalDest,
pSurfDest,
bCanDither);
}
if (pla->pstyle != (PFLOAT_LONG) NULL &&
peboPen->bIsOldStylePen() &&
dco.pdc->jBkMode() == OPAQUE &&
!(pla->fl & LA_GEOMETRIC)) // Don't style wide Win3 pens
{
// If the background mode is OPAQUE, and we're styling with an
// old Win3-style pen, we have to note it. We do this styling in
// two passes. On the first pass, we use the opaque pen.
bOpaqueStyle = TRUE;
ASSERTGDI(peboPen->bIsDefaultStyle(), "Expect only default style");
// Change the sense of the first element in the style array:
pla->fl ^= LA_STARTGAP;
lOldStyleState = pla->elStyleState.l;
peboStroke = dco.peboBackground();
// Initialize the opaque pen:
// The opaquing brush must be solid colored. If the cached version
// isn't solid colored, mark the cached entry as invalid so that
// we'll re-realize. I don't expect this to ever happen (we merely
// implmement this for completeness), so it's hardly a performance
// hit.
if (!(dco.ulDirty() & DIRTY_BACKGROUND))
{
if (peboStroke->iSolidColor == (ULONG) -1)
{
dco.ulDirty(dco.ulDirty() | DIRTY_BACKGROUND);
}
}
if (dco.bDirtyBrush(DIRTY_BACKGROUND))
{
if((dco.flGraphicsCaps() & GCAPS_ARBRUSHOPAQUE) == 0)
{
// BUGFIX #27335 2-18-2000 bhouse
// We can clear the DIRTY_BACKGROUND bit only if
// we would have otherwise realized it without
// dithering enabled.
dco.vCleanBrush(DIRTY_BACKGROUND);
}
peboStroke->vInitBrush(
dco.pdc,
(PBRUSH)gpbrBackground,
epalDestDC, epalDest,
pSurfDest,
FALSE); // False means can't dither
}
}
mix = peboPen->mixBest(dco.pdc->jROP2(), dco.pdc->jBkMode());
}
if (flType & PATH_FILL)
{
if (dco.bDirtyBrush(DIRTY_FILL))
{
dco.vCleanBrush(DIRTY_FILL);
peboBrush->vInitBrush(dco.pdc,
dco.pdc->pbrushFill(),
epalDestDC, epalDest,
pSurfDest);
}
// For StrokeAndFill, we can pass down only a single 'mix' that applies
// to both brushes (normally, we don't want to pass down a transparent
// mix when the brush is not a hatch because the mix is what the display/
// printer driver cues off of to know if it has to do a transparent fill,
// which it typically does very slowly).
//
// If either brush is a hatched brush, and the BkMode is TRANSPARENT,
// then 'mix' must indicate a transparent mix. The way it works here
// is that if a transparent mix was already initialized for the pen, we
// don't recompute the mix:
if (!(flType & PATH_STROKE) ||
((mix >> 8) == (mix & 0xff)))
{
mix = peboBrush->mixBest(dco.pdc->jROP2(), dco.pdc->jBkMode());
}
}
// Reset the path for enumeration:
ppath->pprEnum = (PATHRECORD*) NULL;
// Exclude the pointer:
DEVEXCLUDEOBJ dxo(dco,&eco.erclExclude(),&eco);
if ((flType == 0) && (po.ulTechnology() != DT_RASDISPLAY))
{
// Microsoft Publisher and Adobe Persuasion draw colored pattern
// and gradient fills on Postscript by doing the following sequence:
//
// 1. Send BEGIN_PATH printer escape;
// 2. Draw a path using a hollow brush and a NULL pen;
// 3. Send END_PATH escape;
// 4. Send CLIP_TO_PATH.
//
// The problem is that we used to detect these path cases and
// optimize out the calls to the driver, so the clipping path
// would never get down to Postscript.
//
// To fix this, we now detect this case and subsitute R2_NOP for
// the mix instead, and in this manner the path will still get down
// to the driver. If the driver is not currently accumulating a
// path, the right thing will still be printed: that is, nothing.
flType = flOriginal;
mix = ((R2_NOP << 8) | R2_NOP);
pla = &glaSimpleStroke.la; // Give them something to look at
}
// Finally, make the necessary calls:
switch(flType)
{
case 0:
bRet = TRUE;
break;
case PATH_FILL:
bRet = bSimpleFill(dco.flGraphicsCaps(),
&po,
pSurfDest,
&eco,
peboBrush,
&dco.pdc->ptlFillOrigin(),
mix,
dco.pdc->jFillMode());
break;
case PATH_STROKE:
bRet = bSimpleStroke(dco.flGraphicsCaps(),
&po,
pSurfDest,
&eco,
(XFORMOBJ *) pexo,
peboStroke,
&dco.pdc->ptlFillOrigin(),
pla,
mix);
break;
case PATH_STROKE | PATH_FILL:
bRet = bSimpleStrokeAndFill(dco.flGraphicsCaps(),
&po,
pSurfDest,
&eco,
(XFORMOBJ *) pexo,
peboStroke,
pla,
peboBrush,
&dco.pdc->ptlFillOrigin(),
mix,
dco.pdc->jFillMode());
break;
default:
RIP("Woah Nellie!");
}
// Do a second pass to draw the dashes for opaque styled lines:
if (bOpaqueStyle)
{
pla->fl ^= LA_STARTGAP;
pla->elStyleState.l = lOldStyleState;
ppath->pprEnum = (PATHRECORD*) NULL;
bRet &= bSimpleStroke(dco.flGraphicsCaps(),
&po,
pSurfDest,
&eco,
(XFORMOBJ *) pexo,
peboPen,
&dco.pdc->ptlFillOrigin(),
pla,
mix);
}
return(bRet);
}
//
// Pathalloc structure
//
// We allocate the space for paths in blocks that are essentially
// independent of the chain used to order the pathdata records. Typically
// we will have several ( or many ) pathdata records packed into each
// pathalloc block. The pathalloc blocks for a path are all chained together
// using the ppanext pointers; the end of the chain is marked with a NULL
// pointer.
//
// Each pathalloc structure has two fields which describe the space available
// in the block; the 'start' field is the pointer to the start of the
// available space, and the 'end' field is the pointer to the address
// *following* the last valid address in the pathalloc. Thus, the space
// remaining in the pathalloc can be computed as end - start.
//
// Routines to allocate and free pathalloc structures
/******************************Public*Routine******************************\
* friend BOOL bInitPathAlloc()
*
* Initialize the freelist for pathallocs. Get a semaphore, set the
* freelist to empty.
*
* History:
* 1-Oct-1990 -by- Paul Butzi [paulb]
* Wrote it.
\**************************************************************************/
BOOL bInitPathAlloc()
{
if ((PATHALLOC::hsemFreelist = GreCreateSemaphore()) == NULL)
{
return(FALSE);
}
PATHALLOC::freelist = (PATHALLOC*) NULL;
PATHALLOC::cFree = 0;
PATHALLOC::cAllocated = 0;
return(TRUE);
}
/******************************Public*Routine******************************\
* friend void freepathalloc()
*
* Deallocate a pathalloc.
*
* History:
* 1-Oct-1990 -by- Paul Butzi [paulb]
* Wrote it.
\**************************************************************************/
VOID freepathalloc(PATHALLOC *ppa)
{
ASSERTGDI(ppa->siztPathAlloc == PATHALLOCSIZE, "Not a heap PATHALLOC");
SEMOBJ so(PATHALLOC::hsemFreelist);
if (PATHALLOC::cFree >= FREELIST_MAX)
{
// Free the memory if we've already got enough blocks on the freelist.
VFREEMEM(ppa);
PATHALLOC::cAllocated--;
}
else
{
// Keep around a couple of blocks on the freelist for fast access.
ppa->ppanext = PATHALLOC::freelist;
PATHALLOC::freelist = ppa;
PATHALLOC::cFree++;
}
}
/******************************Public*Routine******************************\
* friend PATHALLOC *newpathalloc()
*
* Allocate a new pathalloc, taking it off the freelist unless the
* freelist is empty. In that case, just allocate a new one
*
* 1-Oct-1990 -by- Paul Butzi [paulb]
* Wrote it.
\**************************************************************************/
PPATHALLOC newpathalloc()
{
SEMOBJ so(PATHALLOC::hsemFreelist);
register PPATHALLOC ppaNew = PATHALLOC::freelist;
if ( ppaNew != (PPATHALLOC) NULL )
{
PATHALLOC::freelist = ppaNew->ppanext;
PATHALLOC::cFree--;
}
else
{
ppaNew = (PPATHALLOC) PALLOCMEM(PATHALLOCSIZE, 'tapG');
if (ppaNew == (PPATHALLOC) NULL)
return((PPATHALLOC) NULL);
PATHALLOC::cAllocated++;
}
// Initialize the pathalloc structure:
ppaNew->pprfreestart = &(ppaNew->apr[0]);
ppaNew->ppanext = (PATHALLOC*) NULL;
ppaNew->siztPathAlloc = PATHALLOCSIZE;
return(ppaNew);
}
/******************************Public*Routine******************************\
* BOOL bSavePath(dco, lSave)
*
* Lazily save the DC's active or inactive path. We only actually copy
* the path when the user starts mucking around with it.
*
* History:
* 21-Mar-1992 -by- J. Andrew Goossen [andrewgo]
* Wrote it.
\**************************************************************************/
BOOL bSavePath(XDCOBJ& dco, LONG lSave)
{
DONTUSE(lSave);
// If there's a path in the DC (either active or inactive), make a note
// to save it when it's about to be modified:
if (dco.hpath() != HPATH_INVALID)
dco.pdc->vSetLazySave();
return(TRUE);
}
/******************************Public*Routine******************************\
* VOID vRestorePath(dco, lSave)
*
* Restore the active path.
*
* History:
* 21-Mar-1992 -by- J. Andrew Goossen [andrewgo]
* Wrote it.
\**************************************************************************/
VOID vRestorePath(XDCOBJ& dco, LONG lSave)
{
DONTUSE(lSave);
// If there's a path in the DC, and the bLazySave flag isn't set, it means
// that it's a new path created at this level, so we have to nuke it:
if (dco.hpath() != HPATH_INVALID && !dco.pdc->bLazySave())
{
XEPATHOBJ epath(dco.hpath());
ASSERTGDI(epath.bValid(), "Invalid DC path");
epath.vDelete();
dco.pdc->vDestroy();
}
}
/******************************Member*Function*****************************\
* BOOL EPATHOBJ::bAllClosed()
*
* If a fill is to be done on the path, every subpath must be have been
* explicitly marked as closed. This routine returns TRUE if all subpaths
* have be closed.
*
* Note: This is needed in checked builds only!
*
* History:
* 77-Nov-1993 -by- J. Andrew Goossen [andrewgo]
* Wrote it.
\**************************************************************************/
BOOL EPATHOBJ::bAllClosed()
{
ULONG count = 0;
for (PATHRECORD *ppr = ppath->pprfirst;
ppr != (PPATHREC) NULL;
ppr = ppr->pprnext)
{
if (ppr->flags & PD_ENDSUBPATH)
{
if (!(ppr->flags & PD_CLOSEFIGURE))
return(FALSE);
}
else
{
ASSERTGDI(!(ppr->flags & PD_CLOSEFIGURE),
"Shouldn't be a close figure when not end of subpath");
}
}
return(TRUE);
}
/******************************Public*Routine******************************\
* VOID EPATHOBJ::vPrint()
*
* Prints the path points, for debugging purposes.
*
* History:
* 21-Mar-1992 -by- J. Andrew Goossen [andrewgo]
* Wrote it.
\**************************************************************************/
VOID EPATHOBJ::vPrint()
{
DbgPrint("cCurves: %li fl: %lx\n", cCurves, fl);
PPATHREC ppr;
for (ppr = ppath->pprfirst; ppr != NULL; ppr = ppr->pprnext)
{
DbgPrint("\n%li: ", ppr->flags);
COUNT ii;
for (ii = 0; ii < ppr->count; ii++)
DbgPrint("(%li, %li) ", ppr->aptfx[ii].x, ppr->aptfx[ii].y);
}
DbgPrint("\n");
}
/******************************Public*Routine******************************\
* VOID EPATHOBJ::vPrint()
*
* Prints the path structure, for debugging purposes.
*
* History:
* 21-Mar-1992 -by- J. Andrew Goossen [andrewgo]
* Wrote it.
\**************************************************************************/
VOID EPATHOBJ::vDiag()
{
DbgPrint("flType: %lx\n", ppath->flType);
DbgPrint("Chain: %p PprFirst: %p PprLast: %p\n",
ppath->ppachain, ppath->pprfirst, ppath->pprlast);
for (PPATHALLOC ppa = ppath->ppachain; ppa != NULL; ppa = ppa->ppanext)
DbgPrint(" ppa: %p ppaNext: %p pprFreeStart: %p sizt: %li\n",
ppa, ppa->ppanext, ppa->pprfreestart, ppa->siztPathAlloc);
for (PPATHREC ppr = ppath->pprfirst; ppr != NULL; ppr = ppr->pprnext)
DbgPrint(" ppr: %p pprNext: %p pprPrev: %p count: %li flags: %li\n",
ppr, ppr->pprnext, ppr->pprprev, ppr->count, ppr->flags);
}
/******************************Public*Routine******************************\
* VOID vPathDebug()
*
* Prints the number of currently allocated PATHALLOCs, and the number of
* PATHALLOCs available on the free list, for debugging purposes.
*
* History:
* 21-Mar-1992 -by- J. Andrew Goossen [andrewgo]
* Wrote it.
\**************************************************************************/
VOID vPathDebug()
{
SEMOBJ so(PATHALLOC::hsemFreelist);
DbgPrint("F: %li A: %li\n", PATHALLOC::cFree, PATHALLOC::cAllocated);
}