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