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
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);
|
|
}
|
|
|