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