|
|
/******************************Module*Header*******************************\
* Module Name: pathflat.cxx * * Code to flatten paths * * Created: 3-Dec-1990 10:15:00 * Author: Paul Butzi [paulb] * * Copyright (c) 1990-1999 Microsoft Corporation \**************************************************************************/
#include "precomp.hxx"
#include "pathwide.hxx"
#define INLINE inline
INLINE BOOL bIntersect(RECTFX* prcfx1, RECTFX* prcfx2) { BOOL bRet = (prcfx1->yTop <= prcfx2->yBottom && prcfx1->yBottom >= prcfx2->yTop && prcfx1->xLeft <= prcfx2->xRight && prcfx1->xRight >= prcfx2->xLeft); return(bRet); }
INLINE VOID vBoundBox(POINTFIX* aptfx, RECTFX* prcfx) { if (aptfx[0].x >= aptfx[1].x) if (aptfx[2].x >= aptfx[3].x) { prcfx->xLeft = MIN(aptfx[1].x, aptfx[3].x); prcfx->xRight = MAX(aptfx[0].x, aptfx[2].x); } else { prcfx->xLeft = MIN(aptfx[1].x, aptfx[2].x); prcfx->xRight = MAX(aptfx[0].x, aptfx[3].x); } else if (aptfx[2].x <= aptfx[3].x) { prcfx->xLeft = MIN(aptfx[0].x, aptfx[2].x); prcfx->xRight = MAX(aptfx[1].x, aptfx[3].x); } else { prcfx->xLeft = MIN(aptfx[0].x, aptfx[3].x); prcfx->xRight = MAX(aptfx[1].x, aptfx[2].x); }
if (aptfx[0].y >= aptfx[1].y) if (aptfx[2].y >= aptfx[3].y) { prcfx->yTop = MIN(aptfx[1].y, aptfx[3].y); prcfx->yBottom = MAX(aptfx[0].y, aptfx[2].y); } else { prcfx->yTop = MIN(aptfx[1].y, aptfx[2].y); prcfx->yBottom = MAX(aptfx[0].y, aptfx[3].y); } else if (aptfx[2].y <= aptfx[3].y) { prcfx->yTop = MIN(aptfx[0].y, aptfx[2].y); prcfx->yBottom = MAX(aptfx[1].y, aptfx[3].y); } else { prcfx->yTop = MIN(aptfx[0].y, aptfx[3].y); prcfx->yBottom = MAX(aptfx[1].y, aptfx[2].y); } }
INLINE VOID HFDBASIS32::vInit(FIX p1, FIX p2, FIX p3, FIX p4) { // ASSERTGDI(((p1 | p2 | p3 | p4) & 0xffffc000) == 0, "Range too big");
#if (LTOFX(1) != 0x10)
#error "FIX format changed, update flattener routine"
#endif
// Change basis and convert from 28.4 to 18.14 format:
e0 = (p1 ) << 10; e1 = (p4 - p1 ) << 10; e2 = (3 * (p2 - p3 - p3 + p4)) << 11; e3 = (3 * (p1 - p2 - p2 + p3)) << 11; }
INLINE VOID HFDBASIS32::vLazyHalveStepSize(LONG cShift) { e2 = (e2 + e3) >> 1; e1 = (e1 - (e2 >> cShift)) >> 1; }
INLINE VOID HFDBASIS32::vSteadyState(LONG cShift) { // We now convert from 18.14 fixed format to 15.17:
e0 <<= 3; e1 <<= 3;
register LONG lShift = cShift - 3;
if (lShift < 0) { lShift = -lShift; e2 <<= lShift; e3 <<= lShift; } else { e2 >>= lShift; e3 >>= lShift; } }
INLINE VOID HFDBASIS32::vHalveStepSize() { e2 = (e2 + e3) >> 3; e1 = (e1 - e2) >> 1; e3 >>= 2; }
INLINE VOID HFDBASIS32::vDoubleStepSize() { e1 += e1 + e2; e3 <<= 2; e2 = (e2 << 3) - e3; }
INLINE VOID HFDBASIS32::vTakeStep() { e0 += e1; register LONG lTemp = e2; e1 += lTemp; e2 += lTemp - e3; e3 = lTemp; }
typedef struct _BEZIERCONTROLS { POINTFIX ptfx[4]; } BEZIERCONTROLS;
BOOL BEZIER32::bInit( POINTFIX* aptfxBez, // Pointer to 4 control points
RECTFX* prcfxClip) // Bound box of visible region (optional)
{ POINTFIX aptfx[4]; LONG cShift = 0; // Keeps track of 'lazy' shifts
cSteps = 1; // Number of steps to do before reach end of curve
vBoundBox(aptfxBez, &rcfxBound);
*((BEZIERCONTROLS*) aptfx) = *((BEZIERCONTROLS*) aptfxBez);
{ register FIX fxOr; register FIX fxOffset;
fxOffset = rcfxBound.xLeft; fxOr = (aptfx[0].x -= fxOffset); fxOr |= (aptfx[1].x -= fxOffset); fxOr |= (aptfx[2].x -= fxOffset); fxOr |= (aptfx[3].x -= fxOffset);
fxOffset = rcfxBound.yTop; fxOr |= (aptfx[0].y -= fxOffset); fxOr |= (aptfx[1].y -= fxOffset); fxOr |= (aptfx[2].y -= fxOffset); fxOr |= (aptfx[3].y -= fxOffset);
// This 32 bit cracker can only handle points in a 10 bit space:
if ((fxOr & 0xffffc000) != 0) return(FALSE); }
x.vInit(aptfx[0].x, aptfx[1].x, aptfx[2].x, aptfx[3].x); y.vInit(aptfx[0].y, aptfx[1].y, aptfx[2].y, aptfx[3].y);
if (prcfxClip == (RECTFX*) NULL || bIntersect(&rcfxBound, prcfxClip)) { while (TRUE) { register LONG lTestMagnitude = TEST_MAGNITUDE_INITIAL << cShift;
if (x.lError() <= lTestMagnitude && y.lError() <= lTestMagnitude) break;
cShift += 2; x.vLazyHalveStepSize(cShift); y.vLazyHalveStepSize(cShift); cSteps <<= 1; } }
x.vSteadyState(cShift); y.vSteadyState(cShift);
// Note that this handles the case where the initial error for
// the Bezier is already less than TEST_MAGNITUDE_NORMAL:
x.vTakeStep(); y.vTakeStep(); cSteps--;
return(TRUE); }
BOOL BEZIER32::bNext(POINTFIX* pptfx) { // Return current point:
pptfx->x = x.fxValue() + rcfxBound.xLeft; pptfx->y = y.fxValue() + rcfxBound.yTop;
// If cSteps == 0, that was the end point in the curve!
if (cSteps == 0) return(FALSE);
// Okay, we have to step:
if (MAX(x.lError(), y.lError()) > TEST_MAGNITUDE_NORMAL) { x.vHalveStepSize(); y.vHalveStepSize(); cSteps <<= 1; }
ASSERTGDI(MAX(x.lError(), y.lError()) <= TEST_MAGNITUDE_NORMAL, "Please tell AndrewGo he was wrong");
while (!(cSteps & 1) && x.lParentErrorDividedBy4() <= (TEST_MAGNITUDE_NORMAL >> 2) && y.lParentErrorDividedBy4() <= (TEST_MAGNITUDE_NORMAL >> 2)) { x.vDoubleStepSize(); y.vDoubleStepSize(); cSteps >>= 1; }
cSteps--; x.vTakeStep(); y.vTakeStep();
return(TRUE); }
///////////////////////////////////////////////////////////////////////////
// BEZIER64
//
// All math is done using 64 bit fixed numbers in a 36.28 format.
//
// All drawing is done in a 31 bit space, then a 31 bit window offset
// is applied. In the initial transform where we change to the HFD
// basis, e2 and e3 require the most bits precision: e2 = 6(p2 - 2p3 + p4).
// This requires an additional 4 bits precision -- hence we require 36 bits
// for the integer part, and the remaining 28 bits is given to the fraction.
//
// In rendering a Bezier, every 'subdivide' requires an extra 3 bits of
// fractional precision. In order to be reversible, we can allow no
// error to creep in. Since a FIX coordinate is 32 bits, and we
// require an additional 4 bits as mentioned above, that leaves us
// 28 bits fractional precision -- meaning we can do a maximum of
// 9 subdivides. Now, the maximum absolute error of a Bezier curve in 27
// bit integer space is 2^29 - 1. But 9 subdivides reduces the error by a
// guaranteed factor of 2^18, meaning we can crack down only to an error
// of 2^11 before we overflow, when in fact we want to crack error to less
// than 1.
//
// So what we do is HFD until we hit an error less than 2^11, reverse our
// basis transform to get the four control points of this smaller curve
// (rounding in the process to 32 bits), then invoke another copy of HFD
// on the reduced Bezier curve. We again have enough precision, but since
// its starting error is less than 2^11, we can reduce error to 2^-7 before
// overflowing! We'll start a low HFD after every step of the high HFD.
////////////////////////////////////////////////////////////////////////////
// The following is our 2^11 target error encoded as a 36.28 number
// (don't forget the additional 4 bits of fractional precision!) and
// the 6 times error multiplier:
LONGLONG geqErrorHigh = (LONGLONG)(6 * (1L << 15) >> (32 - FRACTION64)) << 32;
// The following is the default 2/3 error encoded as a 36.28 number,
// multiplied by 6, and leaving 4 bits for fraction:
LONGLONG geqErrorLow = (LONGLONG)4 << 32;
LONGLONG* gpeqErrorHigh = &geqErrorHigh; LONGLONG* gpeqErrorLow = &geqErrorLow;
INLINE FIX HFDBASIS64::fxValue() { // Convert from 36.28 and round:
LONGLONG eq = e0; eq += (1L << (FRACTION64 - 1)); eq >>= FRACTION64; return((FIX) (LONG) eq); }
INLINE VOID HFDBASIS64::vParentError(LONGLONG* peq) { *peq = MAX(ABS(e3 << 2), ABS((e2 << 3) - (e3 << 2))); }
INLINE VOID HFDBASIS64::vError(LONGLONG* peq) { *peq = MAX(ABS(e2), ABS(e3)); }
VOID HFDBASIS64::vInit(FIX p1, FIX p2, FIX p3, FIX p4) { LONGLONG eqTmp; LONGLONG eqP2 = (LONGLONG) p2; LONGLONG eqP3 = (LONGLONG) p3;
// e0 = p1
// e1 = p4 - p1
// e2 = 6(p2 - 2p3 + p4)
// e3 = 6(p1 - 2p2 + p3)
// Change basis:
e0 = p1; // e0 = p1
e1 = p4; e2 = eqP2; e2 -= eqP3; e2 -= eqP3; e2 += e1; // e2 = p2 - 2*p3 + p4
e3 = e0; e3 -= eqP2; e3 -= eqP2; e3 += eqP3; // e3 = p1 - 2*p2 + p3
e1 -= e0; // e1 = p4 - p1
// Convert to 36.28 format and multiply e2 and e3 by six:
e0 <<= FRACTION64; e1 <<= FRACTION64; eqTmp = e2; e2 += eqTmp; e2 += eqTmp; e2 <<= (FRACTION64 + 1); eqTmp = e3; e3 += eqTmp; e3 += eqTmp; e3 <<= (FRACTION64 + 1); }
VOID HFDBASIS64::vUntransform(FIX* afx) { // Declare some temps to hold our operations, since we can't modify e0..e3.
LONGLONG eqP0; LONGLONG eqP1; LONGLONG eqP2; LONGLONG eqP3;
// p0 = e0
// p1 = e0 + (6e1 - e2 - 2e3)/18
// p2 = e0 + (12e1 - 2e2 - e3)/18
// p3 = e0 + e1
eqP0 = e0;
// NOTE PERF: Convert this to a multiply by 6: [andrewgo]
eqP2 = e1; eqP2 += e1; eqP2 += e1; eqP1 = eqP2; eqP1 += eqP2; // 6e1
eqP1 -= e2; // 6e1 - e2
eqP2 = eqP1; eqP2 += eqP1; // 12e1 - 2e2
eqP2 -= e3; // 12e1 - 2e2 - e3
eqP1 -= e3; eqP1 -= e3; // 6e1 - e2 - 2e3
// NOTE PERF: May just want to approximate these divides! [andrewgo]
// Or can do a 64 bit divide by 32 bit to get 32 bits right here.
VDIV(eqP1, 18, 0); VDIV(eqP2, 18, 0); eqP1 += e0; eqP2 += e0;
eqP3 = e0; eqP3 += e1;
// Convert from 36.28 format with rounding:
eqP0 += (1L << (FRACTION64 - 1)); eqP0 >>= FRACTION64; afx[0] = (LONG) eqP0; eqP1 += (1L << (FRACTION64 - 1)); eqP1 >>= FRACTION64; afx[2] = (LONG) eqP1; eqP2 += (1L << (FRACTION64 - 1)); eqP2 >>= FRACTION64; afx[4] = (LONG) eqP2; eqP3 += (1L << (FRACTION64 - 1)); eqP3 >>= FRACTION64; afx[6] = (LONG) eqP3; }
VOID HFDBASIS64::vHalveStepSize() { // e2 = (e2 + e3) >> 3
// e1 = (e1 - e2) >> 1
// e3 >>= 2
e2 += e3; e2 >>= 3; e1 -= e2; e1 >>= 1; e3 >>= 2; }
VOID HFDBASIS64::vDoubleStepSize() { // e1 = 2e1 + e2
// e3 = 4e3;
// e2 = 8e2 - e3
e1 <<= 1; e1 += e2; e3 <<= 2; e2 <<= 3; e2 -= e3; }
VOID HFDBASIS64::vTakeStep() { e0 += e1; LONGLONG eqTmp = e2; e1 += e2; e2 += eqTmp; e2 -= e3; e3 = eqTmp; }
VOID BEZIER64::vInit( POINTFIX* aptfx, // Pointer to 4 control points
RECTFX* prcfxVis, // Pointer to bound box of visible area (may be NULL)
LONGLONG* peqError) // Fractional maximum error (32.32 format)
{ LONGLONG eqTmp;
cStepsHigh = 1; cStepsLow = 0;
xHigh.vInit(aptfx[0].x, aptfx[1].x, aptfx[2].x, aptfx[3].x); yHigh.vInit(aptfx[0].y, aptfx[1].y, aptfx[2].y, aptfx[3].y);
// Initialize error:
eqErrorLow = *peqError;
if (prcfxVis == (RECTFX*) NULL) prcfxClip = (RECTFX*) NULL; else { rcfxClip = *prcfxVis; prcfxClip = &rcfxClip; }
while (((xHigh.vError(&eqTmp), eqTmp) > *gpeqErrorHigh) || ((yHigh.vError(&eqTmp), eqTmp) > *gpeqErrorHigh)) { cStepsHigh <<= 1; xHigh.vHalveStepSize(); yHigh.vHalveStepSize(); } }
// Returns TRUE if there is another point after this one:
BOOL BEZIER64::bNext(POINTFIX* pptfx) { POINTFIX aptfx[4]; RECTFX rcfxBound; LONGLONG eqTmp;
if (cStepsLow == 0) { // Optimization that if the bound box of the control points doesn't
// intersect with the bound box of the visible area, render entire
// curve as a single line:
xHigh.vUntransform(&aptfx[0].x); yHigh.vUntransform(&aptfx[0].y);
xLow.vInit(aptfx[0].x, aptfx[1].x, aptfx[2].x, aptfx[3].x); yLow.vInit(aptfx[0].y, aptfx[1].y, aptfx[2].y, aptfx[3].y); cStepsLow = 1;
if (prcfxClip != (RECTFX*) NULL) vBoundBox(aptfx, &rcfxBound);
if (prcfxClip == (RECTFX*) NULL || bIntersect(&rcfxBound, prcfxClip)) { while (((xLow.vError(&eqTmp), eqTmp) > eqErrorLow) || ((yLow.vError(&eqTmp), eqTmp) > eqErrorLow)) { cStepsLow <<= 1; xLow.vHalveStepSize(); yLow.vHalveStepSize(); } }
// This 'if' handles the case where the initial error for the Bezier
// is already less than the target error:
if (--cStepsHigh != 0) { xHigh.vTakeStep(); yHigh.vTakeStep();
if (((xHigh.vError(&eqTmp), eqTmp) > *gpeqErrorHigh) || ((yHigh.vError(&eqTmp), eqTmp) > *gpeqErrorHigh)) { cStepsHigh <<= 1; xHigh.vHalveStepSize(); yHigh.vHalveStepSize(); }
while (!(cStepsHigh & 1) && ((xHigh.vParentError(&eqTmp), eqTmp) <= *gpeqErrorHigh) && ((yHigh.vParentError(&eqTmp), eqTmp) <= *gpeqErrorHigh)) { xHigh.vDoubleStepSize(); yHigh.vDoubleStepSize(); cStepsHigh >>= 1; } } }
xLow.vTakeStep(); yLow.vTakeStep();
pptfx->x = xLow.fxValue(); pptfx->y = yLow.fxValue();
cStepsLow--; if (cStepsLow == 0 && cStepsHigh == 0) return(FALSE);
if (((xLow.vError(&eqTmp), eqTmp) > eqErrorLow) || ((yLow.vError(&eqTmp), eqTmp) > eqErrorLow)) { cStepsLow <<= 1; xLow.vHalveStepSize(); yLow.vHalveStepSize(); }
while (!(cStepsLow & 1) && ((xLow.vParentError(&eqTmp), eqTmp) <= eqErrorLow) && ((yLow.vParentError(&eqTmp), eqTmp) <= eqErrorLow)) { xLow.vDoubleStepSize(); yLow.vDoubleStepSize(); cStepsLow >>= 1; }
return(TRUE); }
/******************************Public*Routine******************************\
* newpathrec (pppr,pcMax,cNeeded) * * * * Create a new pathrecord. * * * * History: * * Fri 19-Jun-1992 19:24:56 -by- Charles Whitmer [chuckwh] * * Added the quick out if we get enough points. * * * * 5-Dec-1990 -by- Paul Butzi [paulb] * * Wrote it. * \**************************************************************************/
BOOL EPATHOBJ::newpathrec(PATHRECORD **pppr,COUNT *pcMax,COUNT cNeeded) { PATHALLOC *ppa = ppath->ppachain;
*pcMax = 0;
if ( ppa != (PPATHALLOC) 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
ASSERT4GB((ULONGLONG)(end - start)); *pcMax = (ULONG)(end - start); } }
// Now we can decide if we need a new pathalloc
if ((*pcMax < cNeeded) && (*pcMax < PATHALLOCTHRESHOLD)) { // allocate a new pathalloc, link it into path
if ( (ppa = newpathalloc()) == (PPATHALLOC) NULL) return FALSE;
ppa->ppanext = ppath->ppachain; ppath->ppachain = ppa;
// adjust maxadd
// Sundown safe truncation
ASSERT4GB((ULONGLONG)(((char *)ppa + ppa->siztPathAlloc) - (char *)ppa->pprfreestart));
ULONG numbytes = (ULONG)(((char *)ppa + ppa->siztPathAlloc) - (char *)ppa->pprfreestart);
*pcMax = (numbytes - offsetof(PATHRECORD, aptfx))/sizeof(POINTFIX); }
// create new pathrec header
*pppr = ppa->pprfreestart;
return(TRUE); }
/******************************Public*Routine******************************\
* EPATHOBJ::bFlatten() * * Cruise over a path, translating all of the beziers into sequences of lines. * * History: * 5-Dec-1990 -by- Paul Butzi [paulb] * Wrote it. \**************************************************************************/
BOOL EPATHOBJ::bFlatten() { // stress failure in RFONTOBJ::bInsertMetricsPlusPath caused by invalid
// ppath. After Beta2, we will pick up the fix from Adobe.
if (!bValid()) { return (FALSE); }
// Run down the path, looking for records that contain beziers.
// Skip over the records containing lines.
for ( PATHRECORD *ppr = ppath->pprfirst; ppr != (PPATHREC) NULL; ppr = ppr->pprnext) { if (ppr->flags & PD_BEZIERS) { ppr = pprFlattenRec(ppr); if (ppr == (PPATHREC) NULL) return(FALSE); } }
fl &= ~PO_BEZIERS;
return(TRUE); }
/******************************Public*Routine******************************\
* EPATHOBJ::pprFlattenRec(ppr) * * Cruise over a path, translating all of the beziers into sequences of lines. * * History: * 5-Dec-1990 -by- Paul Butzi [paulb] * Wrote it. \**************************************************************************/
PPATHREC EPATHOBJ::pprFlattenRec(PATHRECORD *ppr) { // Create a new record
PATHRECORD *pprNew; COUNT maxadd;
if ( newpathrec(&pprNew,&maxadd,MAXLONG) != TRUE ) return (PPATHREC) NULL;
// Take record of Beziers out of path list, and put a new record
// in its place. Update 'pprNew->pprnext' when we exit.
pprNew->pprprev = ppr->pprprev; pprNew->count = 0; pprNew->flags = (ppr->flags & ~PD_BEZIERS);
if (pprNew->pprprev == (PPATHREC) NULL) ppath->pprfirst = pprNew; else pprNew->pprprev->pprnext = pprNew;
POINTFIX aptfxControl[4]; // If needed, temp buf for control points
PPOINTFIX pptfxControl; // Points to Bezier's 4 control points
PPOINTFIX pptfxNext; // Points to 2nd point of next Bezier
// Now, run down the beziers, flattening them into the record
// First, set up for the first bezier in record
if ((ppr->flags & PD_BEGINSUBPATH) == 0) { // Because we are not starting a new subpath we don't need to
// enter the first control point into the record of lines. Since
// all 4 control points for the first Bezier aren't contiguous in
// memory, copy the points to aptfxControl[].
aptfxControl[0] = ppr->pprprev->aptfx[ppr->pprprev->count - 1]; pptfxNext = ppr->aptfx;
for (LONG ii = 1; ii < 4; ii++) {
// If the Bezier's control points are spread across two
// pathrecords, then handle it.
if (pptfxNext >= &ppr->aptfx[ppr->count]) { ASSERTGDI(ppr->pprnext != NULL, "Lost the other control points"); ppr = ppr->pprnext; ASSERTGDI((ppr->flags & (PD_BEZIERS | PD_BEGINSUBPATH)) != 0, "Mucked up continuation"); pptfxNext = ppr->aptfx; } aptfxControl[ii] = *pptfxNext++; } pptfxControl = aptfxControl;
ASSERTGDI(PATHALLOCTHRESHOLD > 3, "Threshold too small."); ASSERTGDI(pptfxNext <= &ppr->aptfx[ppr->count], "Threshold too small"); } else { pptfxNext = ppr->aptfx + 4; pptfxControl = ppr->aptfx; pprNew->aptfx[pprNew->count++] = ppr->aptfx[0]; }
// Now run down the list of Beziers, flattening them out.
while (TRUE) { // We've removing a curve, so adjust the curve count appropriately:
cCurves--;
// Crack Bezier described by points pointed to by pptfxControl:
BEZIER bez; bez.vInit(pptfxControl);
do { if ( pprNew->count >= maxadd ) { // Since we're continuing this path record onto another,
// we have to adjust this record's flags to note that:
pprNew->flags &= ~(PD_ENDSUBPATH | PD_CLOSEFIGURE);
// Filled the record, get a new one. Insert it after one we
// just filled and adjust the pathalloc record.
ppath->ppachain->pprfreestart = NEXTPATHREC(pprNew);
PATHRECORD *pprNewNew; if (newpathrec(&pprNewNew,&maxadd,MAXLONG) != TRUE) return((PPATHREC) NULL);
pprNewNew->pprprev = pprNew; pprNew->pprnext = pprNewNew; pprNew = pprNewNew;
pprNew->count = 0; pprNew->flags = (ppr->flags & ~(PD_BEZIERS | PD_BEGINSUBPATH | PD_RESETSTYLE)); }
// Now that we've generated the next point, stash it into the
// new path record:
cCurves++;
} while (bez.bNext(&pprNew->aptfx[pprNew->count++]));
// Move on to the next bezier:
// Sundown safe truncation
COUNT cptfxRemaining = (COUNT)(&ppr->aptfx[ppr->count] - pptfxNext);
if (cptfxRemaining <= 0) break;
if (cptfxRemaining >= 3) { pptfxControl = pptfxNext - 1; pptfxNext += 3; } else {
// Handle case where the Bezier's control points are spread
// across two pathrecord's. Copy all the points to
// aptfxControl[].
pptfxNext--; for (INT ii = 0; ii < 4; ii++) { if (pptfxNext >= &ppr->aptfx[ppr->count]) { ASSERTGDI(ppr != NULL, "Lost the other control points."); ppr = ppr->pprnext; ASSERTGDI((ppr->flags & (PD_BEZIERS | PD_BEGINSUBPATH)) != 0, "Mucked up continuation."); pptfxNext = ppr->aptfx; } aptfxControl[ii] = *pptfxNext++; } pptfxControl = aptfxControl; } }
ASSERTGDI(pptfxNext == &ppr->aptfx[ppr->count], "Lost some points");
// Adjust the pathalloc record:
ppath->ppachain->pprfreestart = NEXTPATHREC(pprNew);
pprNew->pprnext = ppr->pprnext; if (pprNew->pprnext == (PPATHREC) NULL) ppath->pprlast = pprNew; else pprNew->pprnext->pprprev = pprNew;
return(pprNew); }
|