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