Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2330 lines
74 KiB

/******************************Module*Header*******************************\
* Module Name: drawgdi.cxx
*
* Contains all the draw APIs for GDI.
*
* Created: 29-Oct-1990
* Author: J. Andrew Goossen [andrewgo]
*
* Copyright (c) 1990-1999 Microsoft Corporation
*
\**************************************************************************/
#include "precomp.hxx"
extern PPEN gpPenNull;
extern PBRUSH gpbrNull;
#include "flhack.hxx"
/******************************Public*Routine******************************\
* LONG lGetQuadrant(eptef)
*
* Returns the quadrant number (0 to 3) of the point.
*
* 22-Aug-1991 -by- J. Andrew Goossen [andrewgo]
* Wrote it.
\**************************************************************************/
LONG lGetQuadrant(EPOINTFL& eptef)
{
LONG lQuadrant = 0;
if (eptef.y.bIsNegative())
{
if (eptef.x.bIsNegative())
{
lQuadrant = 2;
} else {
lQuadrant = 3;
}
} else {
if ((eptef.x.bIsNegative()) || (eptef.x.bIsZero()))
{
lQuadrant = 1;
//
// check for case of exactly on -x axis
//
if (eptef.y.bIsZero()) {
lQuadrant = 2;
}
}
}
return(lQuadrant);
}
/******************************Public*Routine******************************\
* BOOL GreAngleArc (hdc,x,y,ulRadius,eStartAngle,eSweepAngle)
*
* Draws an arc. Angles are in degrees and are specified in IEEE floating
* point, and not necessarily our own internal representation.
*
* History:
* Sat 22-Jun-1991 00:34:22 -by- Charles Whitmer [chuckwh]
* Added ATTRCACHE support.
*
* 20-Nov-1990 -by- J. Andrew Goossen [andrewgo]
* Wrote it.
\**************************************************************************/
BOOL APIENTRY GreAngleArc
(
HDC hdc,
int x,
int y,
ULONG ulRadius,
FLOATL eStartAngle,
FLOATL eSweepAngle
)
{
LONG lStartQuad;
LONG lEndQuad;
LONG lSweptQuadrants;
EFLOAT efEndAngle;
// Lock the DC.
DCOBJ dco(hdc);
if (!dco.bValid() || dco.bStockBitmap())
{
SAVE_ERROR_CODE(ERROR_INVALID_HANDLE);
return(FALSE);
}
// sync user-mode attributes
SYNC_DRAWING_ATTRS(dco.pdc);
LONG lRadius = (LONG) ulRadius;
ERECTL ercl(x - lRadius, y - lRadius, x + lRadius, y + lRadius);
// Check for overflow of either ulRadius to lRadius conversion or that
// the circle defining the arc extends outside of world space:
if (lRadius < 0 || ercl.left > x || ercl.right < x
|| ercl.top > y || ercl.bottom < y)
{
SAVE_ERROR_CODE(ERROR_INVALID_PARAMETER);
return(FALSE);
}
EXFORMOBJ exo(dco, WORLD_TO_DEVICE);
// Get a path, and notify that we will update the current point:
PATHSTACKOBJ pso(dco, TRUE);
if (!pso.bValid())
{
return(FALSE);
}
// Make the rectangle well ordered in logical space:
ercl.vOrder();
// Convert IEEE floats to our internal representation:
EFLOAT efStartAngle;
EFLOAT efSweepAngle;
efStartAngle = eStartAngle;
efSweepAngle = eSweepAngle;
// efEndAngle must be more than efStartAngle for 'bPartialArc':
if (efSweepAngle.bIsNegative())
{
register LONG ll;
SWAPL(ercl.top, ercl.bottom, ll);
efSweepAngle.vNegate();
efStartAngle.vNegate();
}
// Assert: Now efSweepAngle >= 0
EBOX ebox(exo, ercl);
// A line is always drawn to the first point of the arc:
PARTIALARC paType = PARTIALARCTYPE_LINETO;
// The arc is swept multiple times if the sweep angle is more than
// 360 degrees. Because we approximate circles with Beziers,
// arcs of less than 90 degrees are more circular than arcs of 90
// degrees. Since the resulting curves are different, we can't do
// multiple sweeps by:
//
// bPartialArc(StartAngle, 360)
// bEllipse()
// bPartialArc(0, EndAngle)
//
// Since multiple sweeps will be rare, we don't bother making it
// too efficient.
EFLOAT efQuadrantsSwept = efSweepAngle;
efQuadrantsSwept *= FP_1DIV90;
efQuadrantsSwept.bEfToLTruncate(lSweptQuadrants);
// We arbitrarily limit this to sweeping eight circles (otherwise, if
// someone gave a really big sweep angle we would lock the system for
// a really long time):
LONG lCirclesSwept = lSweptQuadrants >> 2;
if (lCirclesSwept > 8)
lCirclesSwept = 8;
EPOINTFL eptefStart;
EPOINTFL eptefEnd;
EFLOAT efAngleSwept;
BOOL bAngleSweptIsZero;
efEndAngle = efStartAngle;
efEndAngle += efSweepAngle;
// ASSERT: efEndAngle >= efStartAngle, since efSweepAngle >= 0.
// If the difference between efEndAngle and efStartAngle is less than about 3 degrees,
// then the error in computation of eptefStart and eptefEnd using vCosSin will be enough
// so that the computation of the Bezier points in bPartialArc (which calls bPartialQuadrantArc)
// will be noticeably wrong.
// determine whether (efEndAngle - efStartAngle - 3.0 < 0.0)
efAngleSwept = efEndAngle;
efAngleSwept -= efStartAngle;
bAngleSweptIsZero = efAngleSwept.bIsZero();
efAngleSwept -= FP_3_0;
if (efAngleSwept.bIsNegative() && !bAngleSweptIsZero)
{
vCosSinPrecise(efStartAngle, &eptefStart.x, &eptefStart.y);
vCosSinPrecise(efEndAngle, &eptefEnd.x, &eptefEnd.y);
}
else
{
vCosSin(efStartAngle, &eptefStart.x, &eptefStart.y);
vCosSin(efEndAngle, &eptefEnd.x, &eptefEnd.y);
}
lStartQuad = lGetQuadrant(eptefStart);
if (efStartAngle > FP_3600_0 || efStartAngle < FP_M3600_0)
{
vArctan(eptefStart.x, eptefStart.y, efStartAngle, lStartQuad);
}
lEndQuad = lGetQuadrant(eptefEnd);
if (efEndAngle > FP_3600_0 || efEndAngle < FP_M3600_0)
{
vArctan(eptefEnd.x, eptefEnd.y, efEndAngle, lEndQuad);
// We have to re-count the number of swept quadrants:
lSweptQuadrants = (lEndQuad - lStartQuad) & 3;
if ((lSweptQuadrants == 0) && (efStartAngle > efEndAngle))
lSweptQuadrants = 3;
}
// Quadrants range from 0 to 3:
lEndQuad &= 3;
lStartQuad &= 3;
lSweptQuadrants &= 3;
for (LONG ll = 0; ll < lCirclesSwept; ll++)
{
if (!bPartialArc(paType, pso, ebox,
eptefStart, lStartQuad, efStartAngle,
eptefEnd, lEndQuad, efEndAngle,
lSweptQuadrants) ||
!bPartialArc(PARTIALARCTYPE_CONTINUE, pso, ebox,
eptefEnd, lEndQuad, efEndAngle,
eptefStart, lStartQuad, efStartAngle,
3 - lSweptQuadrants))
return(FALSE);
paType = PARTIALARCTYPE_CONTINUE;
}
if (!bPartialArc(paType, pso, ebox,
eptefStart, lStartQuad, efStartAngle,
eptefEnd, lEndQuad, efEndAngle,
lSweptQuadrants))
return(FALSE);
// Set the DC's current position in device space. It would be too much
// work to calculate the world space current position, so simply mark it
// as invalid:
dco.pdc->vInvalidatePtlCurrent();
dco.pdc->vValidatePtfxCurrent();
dco.ptfxCurrent() = pso.ptfxGetCurrent();
// If we're not in an active path bracket, stroke the temporary path
// we created:
return(dco.pdc->bActive() ||
pso.bStroke(dco, dco.plaRealize(exo), &exo));
}
/******************************Public*Routine******************************\
* BOOL NtGdiEllipse()
*
* Draws an ellipse in a counter-clockwise direction.
*
* History:
* 20-Nov-1990 -by- J. Andrew Goossen [andrewgo]
* Wrote it.
\**************************************************************************/
BOOL
APIENTRY
NtGdiEllipse(
HDC hdc,
int xLeft,
int yTop,
int xRight,
int yBottom
)
{
DCOBJ dco(hdc);
if (!dco.bValid() || dco.bStockBitmap())
{
SAVE_ERROR_CODE(ERROR_INVALID_HANDLE);
return(FALSE);
}
ERECTL ercl(xLeft, yTop, xRight, yBottom);
// Handle the PS_INSIDEFRAME pen attribute and lower-right exclusion
// by adjusting the box now. At the same time, get the transform
// type and order the rectangle:
EXFORMOBJ exo(dco, WORLD_TO_DEVICE);
LINEATTRS *pla = dco.plaRealize(exo);
// sync the client side cached brush
SYNC_DRAWING_ATTRS(dco.pdc);
// TRUE flag indicates that this is an ellipse, so adjust the bound box
// to make the fill nice:
EBOX ebox(dco, ercl, pla, TRUE);
if (ebox.bEmpty())
return(TRUE);
// Get a path and notify that we won't update the current position:
PATHSTACKOBJ pso(dco);
if (!pso.bValid())
{
SAVE_ERROR_CODE(ERROR_NOT_ENOUGH_MEMORY);
return(FALSE);
}
if (!bEllipse(pso, ebox))
{
return(FALSE);
}
// If the transform is simple and the path consists entirely of the
// ellipse we just added, we can set the flag indicating that the path
// consists of a single ellipse inscribed in the path's bounding
// rectangle. (This flag will get reset if anything is added to the
// path later.)
if (exo.bScale() && pso.cCurves == 5)
pso.fl |= PO_ELLIPSE;
if (dco.pdc->bActive())
return(TRUE);
BOOL bRet;
if (!ebox.bFillInsideFrame())
bRet = pso.bStrokeAndFill(dco, pla, &exo);
else
{
// Handle PS_INSIDEFRAME pen attribute for case when the pen is
// bigger than the bound box. We fill the result with the pen
// brush:
PBRUSH pbrOldFill = dco.pdc->pbrushFill();
dco.pdc->pbrushFill(dco.pdc->pbrushLine());
dco.pdc->flbrushAdd(DIRTY_FILL);
bRet = pso.bFill(dco);
dco.pdc->pbrushFill(pbrOldFill);
dco.pdc->flbrushAdd(DIRTY_FILL);
}
return(bRet);
}
/******************************Public*Routine******************************\
* BOOL NtGdiLineTo (hdc,x,y)
*
* Draws a line from the current position to the specified point.
*
* Current position is used.
*
* History:
* 29-Oct-1990 -by- J. Andrew Goossen [andrewgo]
* Wrote it.
\**************************************************************************/
BOOL APIENTRY NtGdiLineTo
(
HDC hdc,
int x,
int y
)
{
FIX x1;
FIX y1;
FIX x2;
FIX y2;
ERECTL rclBounds;
MIX mix;
BOOL bReturn = TRUE; // Assume we'll succeed
XDCOBJ dco(hdc);
if (dco.bValid())
{
if (!dco.bStockBitmap())
{
EXFORMOBJ exo(dco, WORLD_TO_DEVICE);
LINEATTRS* pla = dco.plaRealize(exo);
// Make sure that the line is solid and cosmetic, that no path is
// being accumulated in the DC, and that this is not an Info DC
// or something that has no surface:
SYNC_DRAWING_ATTRS(dco.pdc);
// Since we can send down only integer end-points to the driver
// with DrvLineTo, we go through this special case only if we're
// not in 'advanced mode' (non-advanced mode supports only integer
// transforms, for compatibility), or if the current transform
// does translation only:
if (!(pla->fl & (LA_STYLED | LA_GEOMETRIC | LA_ALTERNATE)) &&
!(dco.pdc->bActive()) &&
(exo.bTranslationsOnly() || (dco.pdc->iGraphicsMode() != GM_ADVANCED)))
{
// If it's a device managed surface, call DrvLineTo if it's
// hooked by the driver, otherwise call DrvStrokePath; if it's
// an engine managed surface, call DrvLineTo if it's hooked by
// the driver, otherwise call EngLineTo:
PDEVOBJ po(dco.hdev());
// Grab the devlock now so that we can safely get the window
// origin and look at the surface:
DEVLOCKOBJ dlo(dco);
if (dlo.bValid())
{
if (dco.bHasSurface())
{
// Don't call EngLineTo if the driver doesn't hook DrvLineTo
// but does hook DrvStrokePath.
SURFACE* pSurfDst = dco.pSurface();
PFN_DrvLineTo pfnDrvLineTo = NULL;
if (pSurfDst->flags() & HOOK_LINETO)
{
pfnDrvLineTo = PPFNDRV(po, LineTo);
}
else if ((pSurfDst->iType() == STYPE_BITMAP) &&
!(pSurfDst->flags() & HOOK_STROKEPATH))
{
pfnDrvLineTo = EngLineTo;
}
if (pfnDrvLineTo != NULL)
{
// We've satisfied all the conditions for DrvLineTo!
if (exo.bTranslationsOnly())
{
LONG xOffset = exo.fxDx() >> 4;
LONG yOffset = exo.fxDy() >> 4;
x2 = x + xOffset;
y2 = y + yOffset;
if (dco.pdc->bValidPtlCurrent())
{
x1 = dco.ptlCurrent().x + xOffset;
y1 = dco.ptlCurrent().y + yOffset;
}
else
{
x1 = dco.ptfxCurrent().x >> 4;
y1 = dco.ptfxCurrent().y >> 4;
}
}
else
{
ASSERTGDI(dco.pdc->iGraphicsMode() != GM_ADVANCED,
"Someone changed an 'if'");
POINTL aptl[2];
aptl[0].x = x;
aptl[0].y = y;
if (!dco.pdc->bValidPtfxCurrent())
{
aptl[1].x = dco.ptlCurrent().x;
aptl[1].y = dco.ptlCurrent().y;
exo.bXform(aptl, 2);
x1 = aptl[1].x;
y1 = aptl[1].y;
}
else
{
exo.bXform(aptl, 1);
x1 = dco.ptfxCurrent().x >> 4;
y1 = dco.ptfxCurrent().y >> 4;
}
x2 = aptl[0].x;
y2 = aptl[0].y;
}
// Validation to avoid FIX overflow errors.
// Input after transformation must be restricted to 27bits.
// This is apparently a spec issue.
if (!BLTOFXOK(x2)||
!BLTOFXOK(y2))
{
SAVE_ERROR_CODE(ERROR_INVALID_PARAMETER);
// The XDCOBJ dco is locked and the destructor does not
// automagically clean up the lock so we do it here.
dco.vUnlockFast();
return FALSE;
}
// Remember the new current position, in both logical
// space and device space, before applying the window offset:
dco.pdc->vCurrentPosition(x, y, x2 << 4, y2 << 4);
LONG xOrigin = dco.eptlOrigin().x;
LONG yOrigin = dco.eptlOrigin().y;
x1 += xOrigin;
x2 += xOrigin;
y1 += yOrigin;
y2 += yOrigin;
// parameter validation to avoid sticky overflow errors.
// Using BLTOFXOK here is really an overkill - we're trying to
// avoid the +1 below from wrapping and generating a non well-ordered
// bounds rectangle.
// Note: the above code adding xOrigin and yOrigin cannot generate
// non well-ordered rectangles because of the comparisons below during
// the computation of the bound box. This is true even if one of the
// corners wraps and an incorrect bound box is computed (The rect will
// be wrong and the line will be wrong too - but they'll match and the
// code won't crash with an access violation because a valid clip rect
// will be generated.)
if (!BLTOFXOK(x1)||
!BLTOFXOK(y1)||
!BLTOFXOK(x2)||
!BLTOFXOK(y2))
{
SAVE_ERROR_CODE(ERROR_INVALID_PARAMETER);
// We're exiting - undo the current position.
// This code should match the undo code just after the call to
// the driver below.
dco.pdc->vPtfxCurrentPosition(
(x1 - xOrigin) << 4, (y1 - yOrigin) << 4);
// The XDCOBJ dco is locked and the destructor does not
// automagically clean up the lock so we do it here.
dco.vUnlockFast();
return FALSE;
}
// Compute the bound box, remembering that it must be lower-right
// exclusive:
if (x1 <= x2)
{
rclBounds.left = x1;
rclBounds.right = x2 + 1;
}
else
{
rclBounds.left = x2;
rclBounds.right = x1 + 1;
}
if (y1 <= y2)
{
rclBounds.top = y1;
rclBounds.bottom = y2 + 1;
}
else
{
rclBounds.top = y2;
rclBounds.bottom = y1 + 1;
}
if (dco.fjAccum())
{
// Bounds are accumulated relative to the window origin.
// Fortunately, we don't often take this path:
ERECTL rclWindow;
rclWindow.left = rclBounds.left - xOrigin;
rclWindow.right = rclBounds.right - xOrigin;
rclWindow.top = rclBounds.top - yOrigin;
rclWindow.bottom = rclBounds.bottom - yOrigin;
dco.vAccumulate(rclWindow);
}
if (dco.pdc->pbrushLine() != gpPenNull)
{
ECLIPOBJ *pco = NULL;
// This is a pretty gnarly expression to save a return in here.
// Basically pco can be NULL if the rect is completely in the
// cached rect in the DC or if we set up a clip object that
// isn't empty.
if (((rclBounds.left >= dco.prclClip()->left) &&
(rclBounds.right <= dco.prclClip()->right) &&
(rclBounds.top >= dco.prclClip()->top) &&
(rclBounds.bottom <= dco.prclClip()->bottom)) ||
(pco = dco.pco(),
pco->vSetup(dco.prgnEffRao(),rclBounds,CLIP_NOFORCE),
(!pco->erclExclude().bEmpty())))
{
EBRUSHOBJ* pebo = dco.peboLine();
// We have to make sure that we have a solid pen
// for cosmetic lines. If the pen is dirty, we
// may be looking at an uninitialized field, but
// that's okay because we'd only be making the pen
// dirty again:
if (pebo->iSolidColor == (ULONG) -1)
{
dco.ulDirtyAdd(DIRTY_LINE);
}
if (dco.bDirtyBrush(DIRTY_LINE))
{
dco.vCleanBrush(DIRTY_LINE);
XEPALOBJ palDst(pSurfDst->ppal());
XEPALOBJ palDstDC(dco.ppal());
pebo->vInitBrush(dco.pdc,
dco.pdc->pbrushLine(),
palDstDC,
palDst,
pSurfDst,
FALSE);
}
// Exclude the pointer:
DEVEXCLUDEOBJ dxo(
dco,
(pco == NULL) ? &rclBounds : &pco->erclExclude(),
pco
);
// Update the target surface uniqueness:
INC_SURF_UNIQ(pSurfDst);
// No validation has been done on jROP2, so we must
// do it here:
mix = (((MIX) dco.pdc->jROP2() - 1) & 0xf) + 1;
mix |= (mix << 8);
if (!pfnDrvLineTo(pSurfDst->pSurfobj(),
pco,
pebo,
x1,
y1,
x2,
y2,
&rclBounds,
mix))
{
// The driver decided to punt. Make sure
// we undo the current position:
dco.pdc->vPtfxCurrentPosition(
(x1 - xOrigin) << 4, (y1 - yOrigin) << 4);
goto SlowWay;
}
}
else
{
// Completely clipped away, so return success.
}
}
else
{
// Null pens always succeed.
}
}
else
{
// LineTo isn't hooked:
goto SlowWay;
}
}
else
{
// It's an info DC, so we have no alternative...
goto SlowWay;
}
}
else
{
// If we can't grab the devlock, we may be full-screen:
bReturn = dco.bFullScreen();
}
dco.vUnlockFast();
return(bReturn);
}
SlowWay:
// We have to do it the slow way, by making a path:
EPOINTL eptl(x, y);
// Get a path, and notify that we will update the current point:
{
PATHSTACKOBJ pso(dco, TRUE);
if (!pso.bValid())
{
SAVE_ERROR_CODE(ERROR_NOT_ENOUGH_MEMORY);
}
else
{
if (pso.bPolyLineTo(&exo, &eptl, 1))
{
dco.pdc->vCurrentPosition(eptl, pso.ptfxGetCurrent());
bReturn = (dco.pdc->bActive() ||
pso.bStroke(dco, pla, &exo));
}
}
}
}
else
{
SAVE_ERROR_CODE(ERROR_INVALID_HANDLE);
bReturn = FALSE;
}
dco.vUnlockFast();
}
else
{
// We couldn't lock the DC.
bReturn = FALSE;
SAVE_ERROR_CODE(ERROR_INVALID_HANDLE);
}
return(bReturn);
}
/******************************Public*Routine******************************\
* BOOL GreMoveTo(hdc, x, y, pptl)
*
* Changes the current position. Optionally returns old current position.
*
* History:
* 29-Oct-1990 -by- J. Andrew Goossen [andrewgo]
* Wrote it.
\**************************************************************************/
BOOL APIENTRY GreMoveTo
(
HDC hdc,
int x,
int y,
LPPOINT pptl
)
{
XDCOBJ dco(hdc);
if (dco.bValid())
{
if (!dco.bStockBitmap())
{
if (pptl != (LPPOINT) NULL)
{
if (!dco.pdc->bValidPtlCurrent())
{
ASSERTGDI(dco.pdc->bValidPtfxCurrent(), "Both CPs invalid?");
EXFORMOBJ exoDtoW(dco, DEVICE_TO_WORLD);
if (!exoDtoW.bValid())
{
dco.vUnlockFast();
return(FALSE);
}
exoDtoW.bXform(&dco.ptfxCurrent(), &dco.ptlCurrent(), 1);
}
*((POINTL*) pptl) = dco.ptlCurrent();
}
// Don't bother computing the device-space current position; simply mark
// it invalid:
dco.ptlCurrent().x = x;
dco.ptlCurrent().y = y;
dco.pdc->vInvalidatePtfxCurrent();
dco.pdc->vValidatePtlCurrent();
if (!dco.pdc->bActive())
{
// If we're not in a path, we have to reset our style state:
LINEATTRS* pla = dco.plaRealized();
if (pla->fl & LA_GEOMETRIC)
pla->elStyleState.e = IEEE_0_0F;
else
pla->elStyleState.l = 0L;
}
dco.vUnlockFast();
return(TRUE);
}
else
{
dco.vUnlockFast();
}
}
SAVE_ERROR_CODE(ERROR_INVALID_HANDLE);
return(FALSE);
}
/******************************Public*Routine******************************\
* BOOL GrePolyBezier (hdc,pptl,cptl)
*
* Draw multiple Beziers. Current position is not used.
*
* History:
* 29-Oct-1990 -by- J. Andrew Goossen [andrewgo]
* Wrote it.
\**************************************************************************/
BOOL APIENTRY GrePolyBezier
(
HDC hdc,
LPPOINT pptl,
ULONG cptl
)
{
DCOBJ dco(hdc);
if (!dco.bValid() || dco.bStockBitmap())
{
SAVE_ERROR_CODE(ERROR_INVALID_HANDLE);
return(FALSE);
}
// Number of points must be 1 more than 3 times the number of curves:
if (cptl < 4 || cptl % 3 != 1)
{
SAVE_ERROR_CODE(ERROR_INVALID_PARAMETER);
return(FALSE);
}
// sync the client side cached brush
SYNC_DRAWING_ATTRS(dco.pdc);
EXFORMOBJ exo(dco, WORLD_TO_DEVICE);
// Get a path and notify that we won't update the current position:
PATHSTACKOBJ pso(dco);
if (!pso.bValid())
{
SAVE_ERROR_CODE(ERROR_NOT_ENOUGH_MEMORY);
return(FALSE);
}
if (!pso.bMoveTo(&exo, (PPOINTL) pptl) ||
!pso.bPolyBezierTo(&exo, ((PPOINTL) pptl) + 1, cptl - 1))
{
return(FALSE);
}
return(dco.pdc->bActive() ||
pso.bStroke(dco, dco.plaRealize(exo), &exo));
}
/******************************Public*Routine******************************\
* BOOL GrePolyBezierTo (hdc,pptl,cptl)
*
* Draws multiple Beziers. Current position is used and updated.
*
* History:
* 29-Oct-1990 -by- J. Andrew Goossen [andrewgo]
* Wrote it.
\**************************************************************************/
BOOL APIENTRY GrePolyBezierTo
(
HDC hdc,
LPPOINT pptl,
ULONG cptl
)
{
DCOBJ dco(hdc);
if (!dco.bValid() || dco.bStockBitmap())
{
SAVE_ERROR_CODE(ERROR_INVALID_HANDLE);
return(FALSE);
}
// Number of points must be 3 times the number of curves:
if (cptl < 3 || cptl % 3 != 0)
{
SAVE_ERROR_CODE(ERROR_INVALID_PARAMETER);
return(FALSE);
}
// sync the client side cached brush
SYNC_DRAWING_ATTRS(dco.pdc);
EXFORMOBJ exo(dco, WORLD_TO_DEVICE);
// Get a path, and notify that we will update the current point:
PATHSTACKOBJ pso(dco, TRUE);
if (!pso.bValid())
{
SAVE_ERROR_CODE(ERROR_NOT_ENOUGH_MEMORY);
return(FALSE);
}
if (!pso.bPolyBezierTo(&exo, (PPOINTL) pptl, cptl))
return(FALSE);
dco.pdc->vCurrentPosition(((POINTL*) pptl)[cptl - 1],
pso.ptfxGetCurrent());
return(dco.pdc->bActive() ||
pso.bStroke(dco, dco.plaRealize(exo), &exo));
}
/******************************Public*Routine******************************\
* BOOL GrePolyDraw(hdc,pptl,pfj,cptl)
*
* Draw a collection of lines and Bezier curves in a single call.
*
* History:
* 31-Jul-1991 -by- J. Andrew Goossen [andrewgo]
* Wrote it.
\**************************************************************************/
BOOL APIENTRY GrePolyDraw
(
HDC hdc,
LPPOINT pptl,
LPBYTE pfj,
ULONG cptl
)
{
// No point in validating pfj[] now because with shared memory
// window, client could trounce on pfj[] between now and when we
// get around to processing it.
DCOBJ dco(hdc);
if (!dco.bValid() || dco.bStockBitmap())
{
SAVE_ERROR_CODE(ERROR_INVALID_HANDLE);
return(FALSE);
}
SYNC_DRAWING_ATTRS(dco.pdc);
// Handle easy case:
if (cptl == 0)
return(TRUE);
EXFORMOBJ exo(dco, WORLD_TO_DEVICE);
// Get a path, and notify that we will update the current point:
PATHSTACKOBJ pso(dco, TRUE);
if (!pso.bValid())
{
SAVE_ERROR_CODE(ERROR_NOT_ENOUGH_MEMORY);
return(FALSE);
}
// Accumulate the path.
PBYTE pfjEnd = pfj + cptl;
PBYTE pfjStart;
SIZE_T cc;
BOOL bReturn = FALSE; // Fail by default
while (pfj < pfjEnd)
{
pfjStart = pfj;
switch(*pfj++)
{
case (PT_LINETO):
// Collect all the consecutive LineTo's
while (pfj < pfjEnd && *pfj == PT_LINETO)
pfj++;
if (pfj < pfjEnd && (*pfj & ~PT_CLOSEFIGURE) == PT_LINETO)
pfj++;
// Now fall through...
case (PT_LINETO | PT_CLOSEFIGURE):
cc = (SIZE_T)(pfj - pfjStart);
// Sundown, pfj will never exceed pfjEnd = pj + cptl where cptl is a
// ULONG, so it's safe to truncate here.
if (!pso.bPolyLineTo(&exo, (PPOINTL) pptl, (ULONG)cc))
return(bReturn);
pptl += cc;
if (*(pfj - 1) & PT_CLOSEFIGURE)
pso.bCloseFigure();
break;
case (PT_BEZIERTO):
// Collect all the consecutive BezierTo's (the first PT_BEZIERTO in
// a series should never have the PT_CLOSEFIGURE flag set)
while (pfj < pfjEnd && *pfj == PT_BEZIERTO)
pfj++;
if (pfj < pfjEnd && (*pfj & ~PT_CLOSEFIGURE) == PT_BEZIERTO)
pfj++;
// The number of BezierTo points must be a multiple of 3
cc = (SIZE_T)(pfj - pfjStart);
if (cc % 3 != 0)
{
SAVE_ERROR_CODE(ERROR_INVALID_PARAMETER);
return(FALSE);
}
//Sundown, safe to truncate here
if (!pso.bPolyBezierTo(&exo, (PPOINTL) pptl, (ULONG)cc))
return(bReturn);
pptl += cc;
if (*(pfj - 1) & PT_CLOSEFIGURE)
pso.bCloseFigure();
break;
case (PT_MOVETO):
if (!pso.bMoveTo(&exo, (PPOINTL) pptl))
return(bReturn);
pptl++;
break;
default:
// Abort without drawing anything:
SAVE_ERROR_CODE(ERROR_INVALID_PARAMETER);
return(FALSE);
}
}
dco.pdc->vCurrentPosition(*((POINTL*) pptl - 1),
pso.ptfxGetCurrent());
return(dco.pdc->bActive() ||
pso.bStroke(dco, dco.plaRealize(exo), &exo));
}
/******************************Public*Routine******************************\
* BOOL GrePolylineTo (hdc,pptl,cptl)
*
* Draw a polyline figure. Current position is used and updated.
*
* History:
* 29-Oct-1990 -by- J. Andrew Goossen [andrewgo]
* Wrote it.
\**************************************************************************/
BOOL APIENTRY GrePolylineTo
(
HDC hdc,
LPPOINT pptl,
ULONG cptl
)
{
// Lock the DC.
DCOBJ dco(hdc);
if (!dco.bValid() || dco.bStockBitmap())
{
SAVE_ERROR_CODE(ERROR_INVALID_HANDLE);
return(FALSE);
}
SYNC_DRAWING_ATTRS(dco.pdc);
// Return a trivial call.
if (cptl == 0)
return(TRUE);
// Locate the current transform.
EXFORMOBJ exo(dco, WORLD_TO_DEVICE);
// Get a path, and notify that we will update the current point:
PATHSTACKOBJ pso(dco, TRUE);
// Accumulate the lines.
if (!pso.bValid())
{
SAVE_ERROR_CODE(ERROR_NOT_ENOUGH_MEMORY);
return(FALSE);
}
if (!pso.bPolyLineTo(&exo,(PPOINTL) pptl, cptl))
return(FALSE);
dco.pdc->vCurrentPosition(((POINTL*) pptl)[cptl - 1],
pso.ptfxGetCurrent());
// Return now if we're in a path bracket, otherwise stroke the line:
return(dco.pdc->bActive() ||
pso.bStroke(dco, dco.plaRealize(exo), &exo));
}
/******************************Public*Routine******************************\
* BOOL GrePolyPolygonInternal(hdc,pptl,pcptl,ccptl,cMaxPoints)
*
* Creates multiple polygons. Current position is not used.
*
* History:
* 29-Oct-1990 -by- J. Andrew Goossen [andrewgo]
* Wrote it.
\**************************************************************************/
BOOL APIENTRY GrePolyPolygonInternal(
HDC hdc,
LPPOINT pptl,
LPINT pcptl,
int ccptl,
UINT cMaxPoints
)
{
BOOL bStatus = TRUE;
DCOBJ dco(hdc);
if (dco.bValid() && !dco.bStockBitmap())
{
//
// sync the client side cached brush
//
SYNC_DRAWING_ATTRS(dco.pdc);
//
// quick out on the trivial case
//
if (ccptl != 0)
{
EXFORMOBJ exo(dco, WORLD_TO_DEVICE);
//
// Get a path and notify that we won't update the current position:
//
PATHSTACKOBJ pso(dco);
if (pso.bValid())
{
bStatus = bPolyPolygon(pso,
exo,
(PPOINTL) pptl,
(PLONG) pcptl,
(LONG) ccptl,
cMaxPoints);
if (bStatus)
{
bStatus = (dco.pdc->bActive() ||
pso.bStrokeAndFill(dco, dco.plaRealize(exo), &exo));
}
}
else
{
SAVE_ERROR_CODE(ERROR_NOT_ENOUGH_MEMORY);
bStatus = FALSE;
}
}
}
else
{
SAVE_ERROR_CODE(ERROR_INVALID_HANDLE);
bStatus = FALSE;
}
return(bStatus);
}
/******************************Public*Routine******************************\
* BOOL bMakePathRecords
*
* Constructs an array of pathrecords from a given set of pre-transformed
* polypolyline points.
*
* NOTE: 'pcptl' and 'pptlSrc' are allowed to be user-mode pointers, so this
* routine may access violate -- in this case, the caller must provide
* 'try/excepts'!
*
* History:
* 24-Sep-1996 -by- J. Andrew Goossen [andrewgo]
* Wrote it.
\**************************************************************************/
BOOL bMakePathRecords
(
PATHRECORD* pprThis, // Destination buffer for pathrecords
ULONG* pcptl, // Pointer to count of polylines
// Note: Might be user-mode address!
LONG cptlRem, // Maximum number of points allowable
POINTL* pptlSrc, // Pointer to polypolyline points
// Note: Might be user-mode address!
ULONG cpr, // Count of resulting pathrecords
LONG xOffset, // x-offset in pre-transformed coordinates
LONG yOffset, // y-offset in pre-transformed coordinates
RECTFX* prcfxBoundBox, // Returns bounds of lines
PATHRECORD**pprLast // Returns pointer to last pathrecord
)
{
ULONG cptl;
LONG xBoundLeft;
LONG yBoundsTop;
LONG xBoundRight;
LONG yBoundsBottom;
BOOL bRet;
PATHRECORD* pprPrev;
bRet = TRUE; // Assume success
xBoundLeft = LONG_MAX;
yBoundsTop = LONG_MAX;
xBoundRight = LONG_MIN;
yBoundsBottom = LONG_MIN;
pprThis->pprprev = NULL;
while (TRUE)
{
// We have to check 'cptlRem' to be sure that the malevolent
// application hasn't modified the pcptl array -- we must make
// sure we don't add any more points than what we allocated
// (less is okay, though):
cptl = *pcptl++;
cptlRem -= cptl;
if ((cptlRem < 0) || (((LONG) cptl) < 2))
{
// We either ran out of buffer space, do not have enough
// points in the array, or a negative number of points were
// specified (keep in mind that cptlRem is a LONG, so cptl
// also needs to be treated as a LONG for the check).
bRet = FALSE;
break;
}
pprThis->count = cptl;
pprThis->flags = (PD_BEGINSUBPATH | PD_ENDSUBPATH);
pprPrev = pprThis;
// Copy all the points for this pathrecord, and at the same time
// add in the window offset and collect the bounds:
do {
LONG x;
LONG y;
x = pptlSrc->x;
if (x < xBoundLeft)
xBoundLeft = x;
if (x > xBoundRight)
xBoundRight = x;
pprThis->aptfx[0].x = x + xOffset;
y = pptlSrc->y;
if (y < yBoundsTop)
yBoundsTop = y;
if (y > yBoundsBottom)
yBoundsBottom = y;
pprThis->aptfx[0].y = y + yOffset;
// For efficiency, we advance 'pprThis' by the size of a point,
// rather than incuring more cycles to get a pointer directly
// to the points:
pprThis = (PATHRECORD*) ((BYTE*) pprThis + sizeof(POINTFIX));
pptlSrc++;
} while (--cptl != 0);
if (--cpr == 0)
break;
pprThis = (PATHRECORD*) (pprThis->aptfx);
pprThis->pprprev = pprPrev;
pprPrev->pprnext = pprThis;
}
if (bRet)
{
pprPrev->pprnext = NULL;
*pprLast = pprPrev;
// Watch for overflow when we added in 'xOffset' and 'yOffset':
prcfxBoundBox->xLeft = xOffset + xBoundLeft;
prcfxBoundBox->xRight = xOffset + xBoundRight;
if (xBoundLeft > xBoundRight)
bRet = FALSE;
prcfxBoundBox->yTop = yOffset + yBoundsTop;
prcfxBoundBox->yBottom = yOffset + yBoundsBottom;
if (yBoundsTop > yBoundsBottom)
bRet = FALSE;
// If 'cptlRem' isn't zero, someone modified the 'pcptl' array while we
// were looking at it!
if (cptlRem != 0)
bRet = FALSE;
}
return(bRet);
}
/******************************Public*Routine******************************\
* BOOL NtGdiFastPolyPolyline(hdc, pptl, pcptl, ccpl)
*
* Fast path for drawing solid, nominal width polylines. Will return FALSE
* if the fast-path can't be used.
*
* History:
* 29-Oct-1990 -by- J. Andrew Goossen [andrewgo]
* Wrote it.
\**************************************************************************/
extern "C" {
BOOL NtGdiFastPolyPolyline(HDC, CONST POINT*, ULONG*, ULONG);
};
#define POLYBUFFERSIZE 100
BOOL NtGdiFastPolyPolyline
(
HDC hdc,
CONST POINT *pptl, // Pointer to user-mode data
ULONG *pcptl, // Pointer to user-mode data
ULONG ccptl
)
{
ULONG cMaxPoints = 0;
BOOL bReturn = FALSE; // Assume we'll fail
DCOBJ dco(hdc);
if (dco.bValid() && !dco.bStockBitmap())
{
EXFORMOBJ exo(dco, WORLD_TO_DEVICE);
LINEATTRS* pla = dco.plaRealize(exo);
SYNC_DRAWING_ATTRS(dco.pdc);
if (ccptl != 0)
{
__try
{
ULONG i;
ULONG* pul;
ULONG c;
if (ccptl <= (MAXULONG/sizeof(ULONG)))
{
ProbeForRead(pcptl, ccptl * sizeof(ULONG), sizeof(BYTE));
c = 0;
i = ccptl;
pul = pcptl;
do {
// Don't add directly to 'cMaxPoints' here because we're in
// a try/except and the compiler can't register 'cMaxPoints'
// but can enregister 'c.
//
// Also note that we do not care if a bad app puts bogus
// data in here to cause cMaxPoints to overflow since
// code not only uses cMaxPoints to allocate memory, but
// also passes it to bXform and bMakePathRecords to
// ensure we do not access past the amount allocated.
// Furthermore, bMakePathRecords also checks for the
// overflow case as it processes each Polyline and
// will return failure if it detects the overflow.
c += *pul++;
} while (--i != 0);
if (c <= (MAXULONG/sizeof(POINT)))
{
ProbeForRead(pptl, c * sizeof(POINT), sizeof(BYTE));
// Only set cMaxPoints if no overflows or exceptions
// occur. Otherwise cMaxPoints is zero and test
// for cMaxPoints > 0 below will fail.
cMaxPoints = c;
}
}
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
WARNINGX(5);
}
// Watch out for overflow:
if ((cMaxPoints > 0) &&
(ccptl < 0x08000000) &&
(cMaxPoints < 0x08000000))
{
// We handle only solid cosmetic lines in this special case.
// We don't do styled lines so that we don't have to worry about
// updating the style state in the DC after the call is done, and
// we don't have to check for opaque styles (for which we would
// have to call the driver twice).
if (!dco.pdc->bActive() &&
((pla->fl & (LA_GEOMETRIC | LA_ALTERNATE)) == 0) &&
(pla->pstyle == NULL))
{
LONG xOffset;
LONG yOffset;
BOOL bIntegers;
MIX mix;
ULONG cjAlloc;
EPATHOBJ epo;
PATH path;
PATHRECORD* pprFirst;
POINTL* pptlTransform;
union {
PATHRECORD prStackBuffer;
BYTE ajStackBuffer[POLYBUFFERSIZE];
};
cjAlloc = ccptl * offsetof(PATHRECORD, aptfx)
+ cMaxPoints * sizeof(POINTFIX);
if (cjAlloc > POLYBUFFERSIZE)
{
pprFirst = (PATHRECORD*) PVALLOCTEMPBUFFER(cjAlloc);
if (pprFirst == NULL)
{
// bReturn is already FALSE
return(bReturn);
}
}
else
{
pprFirst = &prStackBuffer;
}
DEVLOCKOBJ dlo(dco);
if (dlo.bValid())
{
// Now that we have the devlock, we can safely add in
// the window offset:
xOffset = dco.eptlOrigin().x;
yOffset = dco.eptlOrigin().y;
bReturn = TRUE;
bIntegers = TRUE;
pptlTransform = (POINTL*) pptl;
// We don't have to call transform routines if we only
// have a translation. If we are not in compatible mode
// we also require integer translations.
if ((!exo.bTranslationsOnly()) ||
(dco.pdc->iGraphicsMode() != GM_COMPATIBLE &&
((exo.fxDx() | exo.fxDy()) & (FIX_ONE - 1)) != 0))
{
// The transform isn't trivial, so call out to transform
// all the points. Rather than allocate yet another
// buffer, we stick the transformed points at the end
// of our pathrecords buffer.
pptlTransform = (POINTL*) ((BYTE*) pprFirst
+ cjAlloc
- sizeof(POINTFIX) * cMaxPoints);
// Because we're dealing with user-mode buffers for
// 'pptl' and 'pcptl', we copy the points under the
// protection of a 'try / except'. 'bXform' is
// guaranteed to be recoverable:
__try
{
if (dco.pdc->iGraphicsMode() == GM_ADVANCED)
{
// In advanced mode, the transform can cause
// fractional coordinates, so we can't set the
// PO_ALL_INTEGERS flag:
bIntegers = FALSE;
// Since we will add DC offsets to the
// path records in bMakePathRecords,
// just convert to fixed and apply
// non-translation transformation
// elements here.
bReturn = exo.bXform(
(VECTORL*) pptl,
(VECTORFX*) pptlTransform,
cMaxPoints);
// Convert offsets to fixed point and add
// translation values
xOffset = LTOFX(xOffset) + exo.fxDx();
yOffset = LTOFX(yOffset) + exo.fxDy();
}
else
{
// In compatibility mode, the transform never
// causes fractional coordinates, so we can
// transform directly to integers and set the
// PO_ALL_INTEGERS flag:
bIntegers = TRUE;
bReturn = exo.bXform(
(POINTL*) pptl,
(POINTL*) pptlTransform,
cMaxPoints);
}
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
bReturn = FALSE;
}
}
else
{
// Add translation values to offsets applied in
// bMakePathRecords
xOffset += FXTOL(exo.fxDx());
yOffset += FXTOL(exo.fxDy());
}
// We special case integer polylines and actually
// record integer coordinates in the path instead
// of integers. At bEnum() time we will transform
// them to fixed coordinates if the driver doesn't
// expect to receive integers.
epo.fl = bIntegers ? PO_ALL_INTEGERS : 0;
// Because we're dealing with user-mode buffers for
// 'pptl' and 'pcptl', we copy the points under the
// protection of a 'try / except'. bMakePathRecord
// is guaranteed to be recoverable:
__try
{
bReturn &= bMakePathRecords(
pprFirst,
pcptl,
cMaxPoints,
pptlTransform,
ccptl,
xOffset,
yOffset,
&path.rcfxBoundBox,
&path.pprlast);
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
bReturn = FALSE;
}
// The bound-box was collected in integer coordinates
// but has to be fixed:
if (bIntegers)
{
path.rcfxBoundBox.xLeft <<= 4;
path.rcfxBoundBox.xRight <<= 4;
path.rcfxBoundBox.yTop <<= 4;
path.rcfxBoundBox.yBottom <<= 4;
}
if (!bReturn)
{
if (pprFirst != &prStackBuffer)
{
FREEALLOCTEMPBUFFER(pprFirst);
}
return(bReturn);
}
// Initialize all the remaining path fields:
path.pprfirst = pprFirst;
path.flags = 0;
path.pprEnum = NULL;
epo.cCurves = cMaxPoints - ccptl;
epo.ppath = &path;
ERECTL erclBoundBox(path.rcfxBoundBox);
// Make sure the bounds are lower-right exclusive:
erclBoundBox.bottom++;
erclBoundBox.right++;
if (dco.fjAccum())
{
ERECTL ercl;
// Bounds are accumulated relative to the window
// origin:
ercl.left = erclBoundBox.left - dco.eptlOrigin().x;
ercl.right = erclBoundBox.right - dco.eptlOrigin().x;
ercl.top = erclBoundBox.top - dco.eptlOrigin().y;
ercl.bottom = erclBoundBox.bottom - dco.eptlOrigin().y;
dco.vAccumulate(ercl);
}
if (dco.pdc->pbrushLine() != gpPenNull)
{
SURFACE* pSurfDst = dco.pSurface();
if (pSurfDst != NULL)
{
XEPALOBJ palDst(pSurfDst->ppal());
XEPALOBJ palDstDC(dco.ppal());
EBRUSHOBJ* pebo = dco.peboLine();
// We have to make sure that we have a solid pen
// for cosmetic lines. If the pen is dirty, we
// may be looking at an uninitialized field, but
// that's okay because we'd only be making the pen
// dirty again:
if (pebo->iSolidColor == (ULONG) -1)
{
dco.ulDirtyAdd(DIRTY_LINE);
}
if (dco.bDirtyBrush(DIRTY_LINE))
{
dco.vCleanBrush(DIRTY_LINE);
pebo->vInitBrush(dco.pdc,
dco.pdc->pbrushLine(),
palDstDC, palDst,
pSurfDst,
FALSE);
}
// No validation has been done on jROP2, so we must
// do it here:
mix = (((MIX) dco.pdc->jROP2() - 1) & 0xf) + 1;
mix |= (mix << 8);
ECLIPOBJ eco(dco.prgnEffRao(), erclBoundBox);
if (!eco.erclExclude().bEmpty())
{
PDEVOBJ pdo(pSurfDst->hdev());
// Exclude the pointer:
DEVEXCLUDEOBJ dxo(dco, &eco.erclExclude(), &eco);
// Update the target surface uniqueness:
INC_SURF_UNIQ(pSurfDst);
bReturn = (*PPFNGET(pdo, StrokePath, pSurfDst->flags()))
(
pSurfDst->pSurfobj(),
&epo,
&eco,
NULL,
pebo,
NULL,
pla,
mix
);
}
else
{
// Completely clipped away:
bReturn = TRUE;
}
}
else
{
// When there's no surface pointer, we're drawing to an
// INFO DC, or something:
bReturn = TRUE;
}
}
else
{
// Null pens will always succeed:
bReturn = TRUE;
}
}
else
{
// If we can't grab the devlock, it may be because we're
// in full-screen:
bReturn = dco.bFullScreen();
}
if (pprFirst != &prStackBuffer)
{
FREEALLOCTEMPBUFFER(pprFirst);
}
}
}
}
else
{
// Trivial case always succeeds:
bReturn = TRUE;
}
}
return(bReturn);
}
/******************************Public*Routine******************************\
* BOOL GrePolyPolylineInternal(hdc,pptl,pcptl,cptl,cMaxPoints)
*
* Slow way to draw multiple polylines. Current position is not used.
*
* History:
* 29-Oct-1990 -by- J. Andrew Goossen [andrewgo]
* Wrote it.
\**************************************************************************/
#define POLYBUFFERSIZE 100
BOOL APIENTRY GrePolyPolylineInternal
(
HDC hdc,
CONST POINT *pptl,
ULONG *pcptl,
ULONG ccptl,
UINT cMaxPoints
)
{
BOOL bReturn = FALSE; // Assume we'll fail
DCOBJ dco(hdc);
if (dco.bValid() && !dco.bStockBitmap())
{
EXFORMOBJ exo(dco, WORLD_TO_DEVICE);
LINEATTRS* pla = dco.plaRealize(exo);
SYNC_DRAWING_ATTRS(dco.pdc);
if (ccptl != 0)
{
// We'll do it the slow way. First, get a path and notify that
// we won't update the current position:
// Note: This instance of PATHSTACKOBJ takes up a bunch of stack
// space that we could share with prStackBuffer is stack
// space ever becomes tight.
PATHSTACKOBJ pso(dco);
if (!pso.bValid())
{
SAVE_ERROR_CODE(ERROR_NOT_ENOUGH_MEMORY);
return(FALSE);
}
LONG cPts;
ULONG* pcptlEnd = pcptl + ccptl;
do {
// We have to be careful to make a local copy of this
// polyline's point count (by copying it to cPts) to get
// the value out of the shared client/server memory window,
// where the app could trash the value at any time:
cPts = *pcptl;
cMaxPoints = (UINT) ((LONG) cMaxPoints - cPts);
// Fail if any polyline is less than 2 points or if we've
// passed our maximum number of points:
if ((LONG) cMaxPoints < 0 || cPts < 2)
{
SAVE_ERROR_CODE(ERROR_INVALID_PARAMETER);
return(FALSE);
}
if (!pso.bMoveTo(&exo, (PPOINTL) pptl) ||
!pso.bPolyLineTo(&exo, ((PPOINTL) pptl) + 1, cPts - 1))
return(FALSE);
pptl += cPts;
pcptl++;
} while (pcptl < pcptlEnd);
bReturn = (dco.pdc->bActive() ||
pso.bStroke(dco, dco.plaRealize(exo), &exo));
}
else
{
// Trivial case always succeeds:
bReturn = TRUE;
}
}
else
{
// We couldn't lock the DC. bReturn is already FALSE.
SAVE_ERROR_CODE(ERROR_INVALID_HANDLE);
}
return(bReturn);
}
/******************************Public*Routine******************************\
* BOOL NtGdiRectangle()
*
* Draws a rectangle. Current position is not used. The rectangle is
* drawn in a counter-clockwise direction.
*
* History:
* 29-Oct-1990 -by- J. Andrew Goossen [andrewgo]
* Wrote it.
\**************************************************************************/
BOOL
APIENTRY
NtGdiRectangle(
HDC hdc,
int xLeft,
int yTop,
int xRight,
int yBottom
)
{
BOOL bRet;
LINEATTRS* pla;
DCOBJ dco(hdc);
if (!dco.bValid() || dco.bStockBitmap())
{
SAVE_ERROR_CODE(ERROR_INVALID_HANDLE);
return(FALSE);
}
if (MIRRORED_DC(dco.pdc))
{
// If it a mirrored DC then shift the rect one pixel to the right
// This will give the effect of including the right edge of the rect and exclude the left edge.
--xLeft;
--xRight;
}
ERECTL ercl(xLeft, yTop, xRight, yBottom);
// Sync the client side cached brush
SYNC_DRAWING_ATTRS(dco.pdc);
EXFORMOBJ exoWorld(dco, WORLD_TO_DEVICE);
if (exoWorld.bScale() && !dco.pdc->bActive())
{
// We try to optimize the simple cases as much as possible. The first
// criteria is that there is no funky transform in effect, and the
// second is that we're not accumulating a path.
if (dco.pdc->pbrushLine() == gpPenNull)
{
// Unfortunately, we have to check here if we have a NULL brush
// as well:
if (dco.pdc->pbrushFill() == gpbrNull)
{
return(TRUE);
}
// When we don't have a pen, we do a simple PatBlt:
if (dco.pdc->iGraphicsMode() != GM_ADVANCED)
{
// Round to the nearest integer if not in advanced mode.
if (exoWorld.bTranslationsOnly())
{
LONG lOffset;
lOffset = FXTOLROUND(exoWorld.fxDx());
ercl.left += lOffset;
ercl.right += lOffset;
lOffset = FXTOLROUND(exoWorld.fxDy());
ercl.top += lOffset;
ercl.bottom += lOffset;
}
else
{
ASSERTGDI(exoWorld.bScale(), "Fast path can't do weird xforms");
ercl.left = FXTOLROUND(exoWorld.fxFastX(ercl.left));
ercl.right = FXTOLROUND(exoWorld.fxFastX(ercl.right));
ercl.top = FXTOLROUND(exoWorld.fxFastY(ercl.top));
ercl.bottom = FXTOLROUND(exoWorld.fxFastY(ercl.bottom));
}
ercl.vOrder();
// If we're not in advanced mode, figures are lower-right
// exclusive, so we have to adjust the rectangle:
ercl.right--;
ercl.bottom--;
}
else
{
if (exoWorld.bTranslationsOnly())
{
LONG lOffset;
lOffset = FXTOLCEILING(exoWorld.fxDx());
ercl.left += lOffset;
ercl.right += lOffset;
lOffset = FXTOLCEILING(exoWorld.fxDy());
ercl.top += lOffset;
ercl.bottom += lOffset;
}
else
{
ASSERTGDI(exoWorld.bScale(), "Fast path can't do weird xforms");
ercl.left = FXTOLCEILING(exoWorld.fxFastX(ercl.left));
ercl.right = FXTOLCEILING(exoWorld.fxFastX(ercl.right));
ercl.top = FXTOLCEILING(exoWorld.fxFastY(ercl.top));
ercl.bottom = FXTOLCEILING(exoWorld.fxFastY(ercl.bottom));
}
ercl.vOrder();
}
if (ercl.bWrapped())
return(TRUE);
return(GreRectBlt(dco, &ercl));
}
pla = dco.plaRealize(exoWorld);
if (!(pla->fl & LA_GEOMETRIC))
{
// We handle here the case where we draw the outline with a pen.
BYTE rpo[sizeof(RECTANGLEPATHOBJ)];
// NOTE: For compatibility with Win3.1, we round the points to
// integer coordinates. If we didn't do this, the rectangle outline
// would be rendered according to GIQ, and could have pixels
// 'missing' in the corners if the corners didn't end on integers.
if (dco.pdc->iGraphicsMode() != GM_ADVANCED)
{
if (exoWorld.bTranslationsOnly())
{
LONG lOffset;
lOffset = FXTOLROUND(exoWorld.fxDx());
ercl.left += lOffset;
ercl.right += lOffset;
lOffset = FXTOLROUND(exoWorld.fxDy());
ercl.top += lOffset;
ercl.bottom += lOffset;
}
else
{
ASSERTGDI(exoWorld.bScale(), "Fast path can't do weird xforms");
ercl.left = FXTOLROUND(exoWorld.fxFastX(ercl.left));
ercl.right = FXTOLROUND(exoWorld.fxFastX(ercl.right));
ercl.top = FXTOLROUND(exoWorld.fxFastY(ercl.top));
ercl.bottom = FXTOLROUND(exoWorld.fxFastY(ercl.bottom));
}
ercl.vOrder();
// If we're not in advanced mode, figures are lower-right
// exclusive, so we have to adjust the rectangle.
ercl.right--;
ercl.bottom--;
if ((ercl.left > ercl.right) || (ercl.top > ercl.bottom))
return(TRUE);
}
else
{
if (exoWorld.bTranslationsOnly())
{
LONG lOffset;
lOffset = FXTOLCEILING(exoWorld.fxDx());
ercl.left += lOffset;
ercl.right += lOffset;
lOffset = FXTOLCEILING(exoWorld.fxDy());
ercl.top += lOffset;
ercl.bottom += lOffset;
}
else
{
ASSERTGDI(exoWorld.bScale(), "Fast path can't do weird xforms");
ercl.left = FXTOLCEILING(exoWorld.fxFastX(ercl.left));
ercl.right = FXTOLCEILING(exoWorld.fxFastX(ercl.right));
ercl.top = FXTOLCEILING(exoWorld.fxFastY(ercl.top));
ercl.bottom = FXTOLCEILING(exoWorld.fxFastY(ercl.bottom));
}
ercl.vOrder();
}
((RECTANGLEPATHOBJ*) &rpo)->vInit(&ercl, dco.pdc->bClockwise());
// An important feature is to not draw the interior when we've
// got a NULL brush:
if (dco.pdc->pbrushFill() != gpbrNull)
{
// For compatibility, we also have to shrink the fill rectangle
// on the top and left sides when we don't have a NULL pen
// (this matters for some ROPs and styled pens):
ercl.left++;
ercl.top++;
if (!ercl.bWrapped() && !GreRectBlt(dco, &ercl))
return(FALSE);
}
return(((RECTANGLEPATHOBJ*) &rpo)->bStroke(dco, pla, NULL));
}
}
// Now cover the cases we haven't handled.
// We may have not realize the LINEATTRS yet, so make sure we do now (it
// will early-out if we have already realized it):
pla = dco.plaRealize(exoWorld);
EBOX ebox(dco, ercl, pla);
if (ebox.bEmpty())
return(TRUE);
// Get a path and add to it:
PATHSTACKOBJ pso(dco);
if (!pso.bValid())
{
SAVE_ERROR_CODE(ERROR_NOT_ENOUGH_MEMORY);
return(FALSE);
}
if (!pso.bMoveTo((EXFORMOBJ*) NULL, &ebox.aeptl[0]) ||
!pso.bPolyLineTo((EXFORMOBJ*) NULL, &ebox.aeptl[1], 3) ||
!pso.bCloseFigure())
{
return(FALSE);
}
if (dco.pdc->bActive())
return(TRUE);
if (!ebox.bFillInsideFrame())
{
// Rectangles created with old-style pens always have miter joins:
ULONG iSaveJoin = pla->iJoin;
if (((PPEN)dco.pdc->pbrushLine())->bIsOldStylePen())
{
pla->iJoin = JOIN_MITER;
}
bRet = pso.bStrokeAndFill(dco, pla, &exoWorld);
pla->iJoin = iSaveJoin;
}
else
{
// Handle PS_INSIDEFRAME pen attribute for case when the pen is
// bigger than the bound box. We fill the result with the pen
// brush:
PBRUSH pbrOldFill = dco.pdc->pbrushFill();
dco.pdc->pbrushFill(dco.pdc->pbrushLine());
dco.pdc->flbrushAdd(DIRTY_FILL);
bRet = pso.bFill(dco);
dco.pdc->pbrushFill(pbrOldFill);
dco.pdc->flbrushAdd(DIRTY_FILL);
}
return(bRet);
}
/******************************Public*Routine******************************\
* BOOL NtGdiRoundRect (hdc,x1,y1,x2,y2,x3,y3)
*
* Draws a rounded rectangle in a counter-clockwise direction.
*
* History:
* 20-Nov-1990 -by- J. Andrew Goossen [andrewgo]
* Wrote it.
\**************************************************************************/
BOOL
APIENTRY
NtGdiRoundRect(
HDC hdc,
int x1,
int y1,
int x2,
int y2,
int x3,
int y3
)
{
// If either axis of the ellipse is zero, then we'll be outputing
// a rectangle. We do this check here and not in 'bRoundRect'
// because Rectangle needs the DC (for checking for the fast rectangle
// condition).
// Note that for compatibility with Win3, this must be here! Zero-size
// ellipse roundrects created with old-style pens must have miter joins,
// and Rectangle will take care of that:
if (x3 == 0 || y3 == 0)
return(NtGdiRectangle(hdc,x1,y1,x2,y2));
DCOBJ dco(hdc);
if (!dco.bValid() || dco.bStockBitmap())
{
SAVE_ERROR_CODE(ERROR_INVALID_HANDLE);
return(FALSE);
}
// Sync the client side cached brush
SYNC_DRAWING_ATTRS(dco.pdc);
ERECTL ercl(x1, y1, x2, y2);
EXFORMOBJ exo(dco, WORLD_TO_DEVICE);
EBOX ebox(dco, ercl, dco.plaRealize(exo), TRUE);
if (ebox.bEmpty())
return(TRUE);
// Get a path and notify that we won't update the current position:
PATHSTACKOBJ pso(dco);
if (!pso.bValid())
{
SAVE_ERROR_CODE(ERROR_NOT_ENOUGH_MEMORY);
return(FALSE);
}
if (!bRoundRect(pso, ebox, x3, y3))
return(FALSE);
if (dco.pdc->bActive())
return(TRUE);
BOOL bRet;
if (!ebox.bFillInsideFrame())
bRet = pso.bStrokeAndFill(dco, dco.plaRealize(exo), &exo);
else
{
// Handle PS_INSIDEFRAME pen attribute for case when the pen is
// bigger than the bound box. We fill the result with the pen
// brush:
PBRUSH pbrOldFill = dco.pdc->pbrushFill();
dco.pdc->pbrushFill(dco.pdc->pbrushLine());
dco.pdc->flbrushAdd(DIRTY_FILL);
bRet = pso.bFill(dco);
dco.pdc->pbrushFill(pbrOldFill);
dco.pdc->flbrushAdd(DIRTY_FILL);
}
return(bRet);
}
/******************************Public*Routine******************************\
* bSyncBrushObj()
*
* This routine just makes sure that the kernel brush matches the user mode
* brush.
*
* History:
* 19-Jul-1995 -by- Eric Kutter [erick]
* Wrote it.
\**************************************************************************/
BOOL bSyncBrushObj(
PBRUSH pbrush)
{
BOOL bRet = TRUE;
if (pbrush)
{
PBRUSHATTR pBrushattr = pbrush->pBrushattr();
//
// if the brush handle is a cached solid brush,
// call GreSetSolidBrushInternal to change the color
//
if (pBrushattr->AttrFlags & ATTR_NEW_COLOR)
{
//
// set the new color for the cached brush
//
if (!GreSetSolidBrushLight(pbrush,pBrushattr->lbColor,pbrush->bIsPen()))
{
WARNING1("GreSyncbrush failed to setsolidbrushiternal\n");
bRet = FALSE;
}
else
{
pBrushattr->AttrFlags &= ~ATTR_NEW_COLOR;
}
}
}
return(bRet);
}