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.
835 lines
23 KiB
835 lines
23 KiB
/******************************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);
|
|
}
|