reactos/win32ss/gdi/ntgdi/fillshap.c
jimtabor 94b4b5c127 [NtGDI] Fix ExtSelectClipRgn Tests
Fix results from tests, add (last one) gdi batch support for
ExtSelectClipRgn. Left commented out test code in tree this time.
Pass Katayama Hirofumi MZ SelectClipRgn tests. After commit will plug in
the last batch. After 12 years.

See CORE-13817 and CORE-15906.
2019-05-09 12:33:21 -05:00

1152 lines
31 KiB
C

/*
* PROJECT: ReactOS win32 kernel mode subsystem
* LICENSE: GPL - See COPYING in the top level directory
* FILE: win32ss/gdi/ntgdi/fillshap.c
* PURPOSE: fillshap
* PROGRAMMER:
*/
#include <win32k.h>
#define NDEBUG
#include <debug.h>
#define Rsin(d) ((d) == 0.0 ? 0.0 : ((d) == 90.0 ? 1.0 : sin(d*M_PI/180.0)))
#define Rcos(d) ((d) == 0.0 ? 1.0 : ((d) == 90.0 ? 0.0 : cos(d*M_PI/180.0)))
BOOL FASTCALL
IntGdiPolygon(PDC dc,
PPOINT Points,
int Count)
{
SURFACE *psurf;
PBRUSH pbrLine, pbrFill;
BOOL ret = FALSE; // Default to failure
RECTL DestRect;
int CurrentPoint;
PDC_ATTR pdcattr;
POINTL BrushOrigin;
// int Left;
// int Top;
ASSERT(dc); // Caller's responsibility to pass a valid dc
if (!Points || Count < 2 )
{
EngSetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
/*
// Find start x, y
Left = Points[0].x;
Top = Points[0].y;
for (CurrentPoint = 1; CurrentPoint < Count; ++CurrentPoint) {
Left = min(Left, Points[CurrentPoint].x);
Top = min(Top, Points[CurrentPoint].y);
}
*/
pdcattr = dc->pdcattr;
/* Convert to screen coordinates */
IntLPtoDP(dc, Points, Count);
for (CurrentPoint = 0; CurrentPoint < Count; CurrentPoint++)
{
Points[CurrentPoint].x += dc->ptlDCOrig.x;
Points[CurrentPoint].y += dc->ptlDCOrig.y;
}
// No need to have path here.
{
DestRect.left = Points[0].x;
DestRect.right = Points[0].x;
DestRect.top = Points[0].y;
DestRect.bottom = Points[0].y;
for (CurrentPoint = 1; CurrentPoint < Count; ++CurrentPoint)
{
DestRect.left = min(DestRect.left, Points[CurrentPoint].x);
DestRect.right = max(DestRect.right, Points[CurrentPoint].x);
DestRect.top = min(DestRect.top, Points[CurrentPoint].y);
DestRect.bottom = max(DestRect.bottom, Points[CurrentPoint].y);
}
if (pdcattr->ulDirty_ & (DIRTY_FILL | DC_BRUSH_DIRTY))
DC_vUpdateFillBrush(dc);
if (pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY))
DC_vUpdateLineBrush(dc);
/* Special locking order to avoid lock-ups */
pbrFill = dc->dclevel.pbrFill;
pbrLine = dc->dclevel.pbrLine;
psurf = dc->dclevel.pSurface;
if (psurf == NULL)
{
/* Memory DC without a bitmap selected, nothing to do. */
return TRUE;
}
/* Now fill the polygon with the current fill brush. */
if (!(pbrFill->flAttrs & BR_IS_NULL))
{
BrushOrigin = *((PPOINTL)&pbrFill->ptOrigin);
BrushOrigin.x += dc->ptlDCOrig.x;
BrushOrigin.y += dc->ptlDCOrig.y;
ret = IntFillPolygon (dc,
psurf,
&dc->eboFill.BrushObject,
Points,
Count,
DestRect,
&BrushOrigin);
}
// Draw the Polygon Edges with the current pen ( if not a NULL pen )
if (!(pbrLine->flAttrs & BR_IS_NULL))
{
int i;
for (i = 0; i < Count-1; i++)
{
// DPRINT1("Polygon Making line from (%d,%d) to (%d,%d)\n",
// Points[0].x, Points[0].y,
// Points[1].x, Points[1].y );
ret = IntEngLineTo(&psurf->SurfObj,
(CLIPOBJ *)&dc->co,
&dc->eboLine.BrushObject,
Points[i].x, /* From */
Points[i].y,
Points[i+1].x, /* To */
Points[i+1].y,
&DestRect,
ROP2_TO_MIX(pdcattr->jROP2)); /* MIX */
if (!ret) break;
}
/* Close the polygon */
if (ret)
{
ret = IntEngLineTo(&psurf->SurfObj,
(CLIPOBJ *)&dc->co,
&dc->eboLine.BrushObject,
Points[Count-1].x, /* From */
Points[Count-1].y,
Points[0].x, /* To */
Points[0].y,
&DestRect,
ROP2_TO_MIX(pdcattr->jROP2)); /* MIX */
}
}
}
return ret;
}
BOOL FASTCALL
IntGdiPolyPolygon(DC *dc,
LPPOINT Points,
PULONG PolyCounts,
int Count)
{
if (PATH_IsPathOpen(dc->dclevel))
return PATH_PolyPolygon ( dc, Points, (PINT)PolyCounts, Count);
while (--Count >=0)
{
if (!IntGdiPolygon ( dc, Points, *PolyCounts ))
return FALSE;
Points+=*PolyCounts++;
}
return TRUE;
}
BOOL FASTCALL
IntPolygon(HDC hdc, POINT *Point, int Count)
{
BOOL bResult;
PDC pdc;
pdc = DC_LockDc(hdc);
if (pdc == NULL)
{
EngSetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
bResult = IntGdiPolygon(pdc, Point, Count);
DC_UnlockDc(pdc);
return bResult;
}
/******************************************************************************/
/*
* NtGdiEllipse
*
* Author
* Filip Navara
*
* Remarks
* This function uses optimized Bresenham's ellipse algorithm. It draws
* four lines of the ellipse in one pass.
*
*/
BOOL APIENTRY
NtGdiEllipse(
HDC hDC,
int Left,
int Top,
int Right,
int Bottom)
{
PDC dc;
PDC_ATTR pdcattr;
RECTL RectBounds;
PBRUSH pbrush;
BOOL ret = TRUE;
LONG PenWidth, PenOrigWidth;
LONG RadiusX, RadiusY, CenterX, CenterY;
PBRUSH pFillBrushObj;
BRUSH tmpFillBrushObj;
dc = DC_LockDc(hDC);
if (dc == NULL)
{
EngSetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
if (PATH_IsPathOpen(dc->dclevel))
{
ret = PATH_Ellipse(dc, Left, Top, Right, Bottom);
DC_UnlockDc(dc);
return ret;
}
////
//// Could this use PATH_CheckCorners ?
////
if ((Left == Right) || (Top == Bottom))
{
DC_UnlockDc(dc);
return TRUE;
}
if (Right < Left)
{
INT tmp = Right; Right = Left; Left = tmp;
}
if (Bottom < Top)
{
INT tmp = Bottom; Bottom = Top; Top = tmp;
}
////
pdcattr = dc->pdcattr;
if (pdcattr->ulDirty_ & (DIRTY_FILL | DC_BRUSH_DIRTY))
DC_vUpdateFillBrush(dc);
if (pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY))
DC_vUpdateLineBrush(dc);
pbrush = PEN_ShareLockPen(pdcattr->hpen);
if (!pbrush)
{
DPRINT1("Ellipse Fail 1\n");
DC_UnlockDc(dc);
EngSetLastError(ERROR_INTERNAL_ERROR);
return FALSE;
}
PenOrigWidth = PenWidth = pbrush->lWidth;
if (pbrush->ulPenStyle == PS_NULL) PenWidth = 0;
if (pbrush->ulPenStyle == PS_INSIDEFRAME)
{
if (2*PenWidth > (Right - Left)) PenWidth = (Right -Left + 1)/2;
if (2*PenWidth > (Bottom - Top)) PenWidth = (Bottom -Top + 1)/2;
Left += PenWidth / 2;
Right -= (PenWidth - 1) / 2;
Top += PenWidth / 2;
Bottom -= (PenWidth - 1) / 2;
}
if (!PenWidth) PenWidth = 1;
pbrush->lWidth = PenWidth;
RectBounds.left = Left;
RectBounds.right = Right;
RectBounds.top = Top;
RectBounds.bottom = Bottom;
IntLPtoDP(dc, (LPPOINT)&RectBounds, 2);
RectBounds.left += dc->ptlDCOrig.x;
RectBounds.right += dc->ptlDCOrig.x;
RectBounds.top += dc->ptlDCOrig.y;
RectBounds.bottom += dc->ptlDCOrig.y;
// Setup for dynamic width and height.
RadiusX = max((RectBounds.right - RectBounds.left) / 2, 2); // Needs room
RadiusY = max((RectBounds.bottom - RectBounds.top) / 2, 2);
CenterX = (RectBounds.right + RectBounds.left) / 2;
CenterY = (RectBounds.bottom + RectBounds.top) / 2;
DPRINT("Ellipse 1: Left: %d, Top: %d, Right: %d, Bottom: %d\n",
RectBounds.left,RectBounds.top,RectBounds.right,RectBounds.bottom);
DPRINT("Ellipse 2: XLeft: %d, YLeft: %d, Width: %d, Height: %d\n",
CenterX - RadiusX, CenterY + RadiusY, RadiusX*2, RadiusY*2);
pFillBrushObj = BRUSH_ShareLockBrush(pdcattr->hbrush);
if (NULL == pFillBrushObj)
{
DPRINT1("FillEllipse Fail\n");
EngSetLastError(ERROR_INTERNAL_ERROR);
ret = FALSE;
}
else
{
RtlCopyMemory(&tmpFillBrushObj, pFillBrushObj, sizeof(tmpFillBrushObj));
//tmpFillBrushObj.ptOrigin.x += RectBounds.left - Left;
//tmpFillBrushObj.ptOrigin.y += RectBounds.top - Top;
tmpFillBrushObj.ptOrigin.x += dc->ptlDCOrig.x;
tmpFillBrushObj.ptOrigin.y += dc->ptlDCOrig.y;
DC_vPrepareDCsForBlit(dc, &RectBounds, NULL, NULL);
ret = IntFillEllipse( dc,
CenterX - RadiusX,
CenterY - RadiusY,
RadiusX*2, // Width
RadiusY*2, // Height
&tmpFillBrushObj);
BRUSH_ShareUnlockBrush(pFillBrushObj);
if (ret)
{
ret = IntDrawEllipse( dc,
CenterX - RadiusX,
CenterY - RadiusY,
RadiusX*2, // Width
RadiusY*2, // Height
pbrush);
}
DC_vFinishBlit(dc, NULL);
}
pbrush->lWidth = PenOrigWidth;
PEN_ShareUnlockPen(pbrush);
DC_UnlockDc(dc);
DPRINT("Ellipse Exit.\n");
return ret;
}
#if 0
// When the fill mode is ALTERNATE, GDI fills the area between odd-numbered and
// even-numbered polygon sides on each scan line. That is, GDI fills the area between the
// first and second side, between the third and fourth side, and so on.
// WINDING Selects winding mode (fills any region with a nonzero winding value).
// When the fill mode is WINDING, GDI fills any region that has a nonzero winding value.
// This value is defined as the number of times a pen used to draw the polygon would go around the region.
// The direction of each edge of the polygon is important.
extern BOOL FillPolygon(PDC dc,
SURFOBJ *SurfObj,
PBRUSHOBJ BrushObj,
MIX RopMode,
CONST PPOINT Points,
int Count,
RECTL BoundRect);
#endif
ULONG_PTR
APIENTRY
NtGdiPolyPolyDraw( IN HDC hDC,
IN PPOINT UnsafePoints,
IN PULONG UnsafeCounts,
IN ULONG Count,
IN INT iFunc )
{
DC *dc;
PVOID pTemp;
LPPOINT SafePoints;
PULONG SafeCounts;
NTSTATUS Status = STATUS_SUCCESS;
BOOL Ret = TRUE;
ULONG nPoints = 0, nMaxPoints = 0, nInvalid = 0, i;
if (!UnsafePoints || !UnsafeCounts ||
Count == 0 || iFunc == 0 || iFunc > GdiPolyPolyRgn)
{
/* Windows doesn't set last error */
return FALSE;
}
_SEH2_TRY
{
ProbeForRead(UnsafePoints, Count * sizeof(POINT), 1);
ProbeForRead(UnsafeCounts, Count * sizeof(ULONG), 1);
/* Count points and validate poligons */
for (i = 0; i < Count; i++)
{
if (UnsafeCounts[i] < 2)
{
nInvalid++;
}
nPoints += UnsafeCounts[i];
nMaxPoints = max(nMaxPoints, UnsafeCounts[i]);
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
if (!NT_SUCCESS(Status))
{
/* Windows doesn't set last error */
return FALSE;
}
if (nPoints == 0 || nPoints < nMaxPoints)
{
/* If all polygon counts are zero, or we have overflow,
return without setting a last error code. */
return FALSE;
}
if (nInvalid != 0)
{
/* If at least one poly count is 0 or 1, fail */
EngSetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
/* Allocate one buffer for both counts and points */
pTemp = ExAllocatePoolWithTag(PagedPool,
Count * sizeof(ULONG) + nPoints * sizeof(POINT),
TAG_SHAPE);
if (!pTemp)
{
EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
return FALSE;
}
SafeCounts = pTemp;
SafePoints = (PVOID)(SafeCounts + Count);
_SEH2_TRY
{
/* Pointers already probed! */
RtlCopyMemory(SafeCounts, UnsafeCounts, Count * sizeof(ULONG));
RtlCopyMemory(SafePoints, UnsafePoints, nPoints * sizeof(POINT));
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
if (!NT_SUCCESS(Status))
{
ExFreePoolWithTag(pTemp, TAG_SHAPE);
return FALSE;
}
/* Special handling for GdiPolyPolyRgn */
if (iFunc == GdiPolyPolyRgn)
{
INT iMode = (INT)(UINT_PTR)hDC;
HRGN hrgn;
hrgn = GreCreatePolyPolygonRgn(SafePoints, SafeCounts, Count, iMode);
ExFreePoolWithTag(pTemp, TAG_SHAPE);
return (ULONG_PTR)hrgn;
}
dc = DC_LockDc(hDC);
if (!dc)
{
EngSetLastError(ERROR_INVALID_HANDLE);
ExFreePoolWithTag(pTemp, TAG_SHAPE);
return FALSE;
}
DC_vPrepareDCsForBlit(dc, NULL, NULL, NULL);
if (dc->pdcattr->ulDirty_ & (DIRTY_FILL | DC_BRUSH_DIRTY))
DC_vUpdateFillBrush(dc);
if (dc->pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY))
DC_vUpdateLineBrush(dc);
/* Perform the actual work */
switch (iFunc)
{
case GdiPolyPolygon:
Ret = IntGdiPolyPolygon(dc, SafePoints, SafeCounts, Count);
break;
case GdiPolyPolyLine:
Ret = IntGdiPolyPolyline(dc, SafePoints, SafeCounts, Count);
break;
case GdiPolyBezier:
Ret = IntGdiPolyBezier(dc, SafePoints, *SafeCounts);
break;
case GdiPolyLineTo:
Ret = IntGdiPolylineTo(dc, SafePoints, *SafeCounts);
break;
case GdiPolyBezierTo:
Ret = IntGdiPolyBezierTo(dc, SafePoints, *SafeCounts);
break;
default:
EngSetLastError(ERROR_INVALID_PARAMETER);
Ret = FALSE;
}
/* Cleanup and return */
DC_vFinishBlit(dc, NULL);
DC_UnlockDc(dc);
ExFreePoolWithTag(pTemp, TAG_SHAPE);
return (ULONG_PTR)Ret;
}
BOOL
FASTCALL
IntRectangle(PDC dc,
int LeftRect,
int TopRect,
int RightRect,
int BottomRect)
{
SURFACE *psurf = NULL;
PBRUSH pbrLine, pbrFill;
BOOL ret = FALSE; // Default to failure
RECTL DestRect;
MIX Mix;
PDC_ATTR pdcattr;
POINTL BrushOrigin;
ASSERT ( dc ); // Caller's responsibility to set this up
pdcattr = dc->pdcattr;
// Rectangle Path only.
if ( PATH_IsPathOpen(dc->dclevel) )
{
return PATH_Rectangle ( dc, LeftRect, TopRect, RightRect, BottomRect );
}
/* Make sure rectangle is not inverted */
DestRect.left = min(LeftRect, RightRect);
DestRect.right = max(LeftRect, RightRect);
DestRect.top = min(TopRect, BottomRect);
DestRect.bottom = max(TopRect, BottomRect);
IntLPtoDP(dc, (LPPOINT)&DestRect, 2);
DestRect.left += dc->ptlDCOrig.x;
DestRect.right += dc->ptlDCOrig.x;
DestRect.top += dc->ptlDCOrig.y;
DestRect.bottom += dc->ptlDCOrig.y;
if (dc->fs & (DC_ACCUM_APP|DC_ACCUM_WMGR))
{
IntUpdateBoundsRect(dc, &DestRect);
}
/* In GM_COMPATIBLE, don't include bottom and right edges */
if (pdcattr->iGraphicsMode == GM_COMPATIBLE)
{
DestRect.right--;
DestRect.bottom--;
}
DC_vPrepareDCsForBlit(dc, &DestRect, NULL, NULL);
if (pdcattr->ulDirty_ & (DIRTY_FILL | DC_BRUSH_DIRTY))
DC_vUpdateFillBrush(dc);
if (pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY))
DC_vUpdateLineBrush(dc);
pbrFill = dc->dclevel.pbrFill;
pbrLine = dc->dclevel.pbrLine;
if (!pbrLine)
{
ret = FALSE;
goto cleanup;
}
psurf = dc->dclevel.pSurface;
if (!psurf)
{
ret = TRUE;
goto cleanup;
}
if (pbrFill)
{
if (!(pbrFill->flAttrs & BR_IS_NULL))
{
BrushOrigin = *((PPOINTL)&pbrFill->ptOrigin);
BrushOrigin.x += dc->ptlDCOrig.x;
BrushOrigin.y += dc->ptlDCOrig.y;
ret = IntEngBitBlt(&psurf->SurfObj,
NULL,
NULL,
(CLIPOBJ *)&dc->co,
NULL,
&DestRect,
NULL,
NULL,
&dc->eboFill.BrushObject,
&BrushOrigin,
ROP4_FROM_INDEX(R3_OPINDEX_PATCOPY));
}
}
// Draw the rectangle with the current pen
ret = TRUE; // Change default to success
if (!(pbrLine->flAttrs & BR_IS_NULL))
{
Mix = ROP2_TO_MIX(pdcattr->jROP2);
ret = ret && IntEngLineTo(&psurf->SurfObj,
(CLIPOBJ *)&dc->co,
&dc->eboLine.BrushObject,
DestRect.left, DestRect.top, DestRect.right, DestRect.top,
&DestRect, // Bounding rectangle
Mix);
ret = ret && IntEngLineTo(&psurf->SurfObj,
(CLIPOBJ *)&dc->co,
&dc->eboLine.BrushObject,
DestRect.right, DestRect.top, DestRect.right, DestRect.bottom,
&DestRect, // Bounding rectangle
Mix);
ret = ret && IntEngLineTo(&psurf->SurfObj,
(CLIPOBJ *)&dc->co,
&dc->eboLine.BrushObject,
DestRect.right, DestRect.bottom, DestRect.left, DestRect.bottom,
&DestRect, // Bounding rectangle
Mix);
ret = ret && IntEngLineTo(&psurf->SurfObj,
(CLIPOBJ *)&dc->co,
&dc->eboLine.BrushObject,
DestRect.left, DestRect.bottom, DestRect.left, DestRect.top,
&DestRect, // Bounding rectangle
Mix);
}
cleanup:
DC_vFinishBlit(dc, NULL);
/* Move current position in DC?
MSDN: The current position is neither used nor updated by Rectangle. */
return ret;
}
BOOL
APIENTRY
NtGdiRectangle(HDC hDC,
int LeftRect,
int TopRect,
int RightRect,
int BottomRect)
{
DC *dc;
BOOL ret; // Default to failure
dc = DC_LockDc(hDC);
if (!dc)
{
EngSetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
/* Do we rotate or shear? */
if (!(dc->pdcattr->mxWorldToDevice.flAccel & XFORM_SCALE))
{
POINTL DestCoords[4];
ULONG PolyCounts = 4;
DestCoords[0].x = DestCoords[3].x = LeftRect;
DestCoords[0].y = DestCoords[1].y = TopRect;
DestCoords[1].x = DestCoords[2].x = RightRect;
DestCoords[2].y = DestCoords[3].y = BottomRect;
// Use IntGdiPolyPolygon so to support PATH.
ret = IntGdiPolyPolygon(dc, DestCoords, &PolyCounts, 1);
}
else
{
ret = IntRectangle(dc, LeftRect, TopRect, RightRect, BottomRect );
}
DC_UnlockDc(dc);
return ret;
}
BOOL
FASTCALL
IntRoundRect(
PDC dc,
int Left,
int Top,
int Right,
int Bottom,
int xCurveDiameter,
int yCurveDiameter)
{
PDC_ATTR pdcattr;
PBRUSH pbrLine, pbrFill;
RECTL RectBounds;
LONG PenWidth, PenOrigWidth;
BOOL ret = TRUE; // Default to success
BRUSH brushTemp;
ASSERT ( dc ); // Caller's responsibility to set this up
if ( PATH_IsPathOpen(dc->dclevel) )
return PATH_RoundRect ( dc, Left, Top, Right, Bottom,
xCurveDiameter, yCurveDiameter );
if ((Left == Right) || (Top == Bottom)) return TRUE;
xCurveDiameter = max(abs( xCurveDiameter ), 1);
yCurveDiameter = max(abs( yCurveDiameter ), 1);
if (Right < Left)
{
INT tmp = Right; Right = Left; Left = tmp;
}
if (Bottom < Top)
{
INT tmp = Bottom; Bottom = Top; Top = tmp;
}
pdcattr = dc->pdcattr;
if (pdcattr->ulDirty_ & (DIRTY_FILL | DC_BRUSH_DIRTY))
DC_vUpdateFillBrush(dc);
if (pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY))
DC_vUpdateLineBrush(dc);
pbrLine = PEN_ShareLockPen(pdcattr->hpen);
if (!pbrLine)
{
/* Nothing to do, as we don't have a bitmap */
EngSetLastError(ERROR_INTERNAL_ERROR);
return FALSE;
}
PenOrigWidth = PenWidth = pbrLine->lWidth;
if (pbrLine->ulPenStyle == PS_NULL) PenWidth = 0;
if (pbrLine->ulPenStyle == PS_INSIDEFRAME)
{
if (2*PenWidth > (Right - Left)) PenWidth = (Right -Left + 1)/2;
if (2*PenWidth > (Bottom - Top)) PenWidth = (Bottom -Top + 1)/2;
Left += PenWidth / 2;
Right -= (PenWidth - 1) / 2;
Top += PenWidth / 2;
Bottom -= (PenWidth - 1) / 2;
}
if (!PenWidth) PenWidth = 1;
pbrLine->lWidth = PenWidth;
RectBounds.left = Left;
RectBounds.top = Top;
RectBounds.right = Right;
RectBounds.bottom = Bottom;
IntLPtoDP(dc, (LPPOINT)&RectBounds, 2);
RectBounds.left += dc->ptlDCOrig.x;
RectBounds.top += dc->ptlDCOrig.y;
RectBounds.right += dc->ptlDCOrig.x;
RectBounds.bottom += dc->ptlDCOrig.y;
pbrFill = BRUSH_ShareLockBrush(pdcattr->hbrush);
if (!pbrFill)
{
DPRINT1("FillRound Fail\n");
EngSetLastError(ERROR_INTERNAL_ERROR);
ret = FALSE;
}
else
{
DC_vPrepareDCsForBlit(dc, &RectBounds, NULL, NULL);
RtlCopyMemory(&brushTemp, pbrFill, sizeof(brushTemp));
brushTemp.ptOrigin.x += RectBounds.left - Left;
brushTemp.ptOrigin.y += RectBounds.top - Top;
ret = IntFillRoundRect( dc,
RectBounds.left,
RectBounds.top,
RectBounds.right,
RectBounds.bottom,
xCurveDiameter,
yCurveDiameter,
&brushTemp);
BRUSH_ShareUnlockBrush(pbrFill);
if (ret)
{
ret = IntDrawRoundRect( dc,
RectBounds.left,
RectBounds.top,
RectBounds.right,
RectBounds.bottom,
xCurveDiameter,
yCurveDiameter,
pbrLine);
}
DC_vFinishBlit(dc, NULL);
}
pbrLine->lWidth = PenOrigWidth;
PEN_ShareUnlockPen(pbrLine);
return ret;
}
BOOL
APIENTRY
NtGdiRoundRect(
HDC hDC,
int LeftRect,
int TopRect,
int RightRect,
int BottomRect,
int Width,
int Height)
{
DC *dc = DC_LockDc(hDC);
BOOL ret = FALSE; /* Default to failure */
DPRINT("NtGdiRoundRect(0x%p,%i,%i,%i,%i,%i,%i)\n",hDC,LeftRect,TopRect,RightRect,BottomRect,Width,Height);
if ( !dc )
{
DPRINT1("NtGdiRoundRect() - hDC is invalid\n");
EngSetLastError(ERROR_INVALID_HANDLE);
}
else
{
ret = IntRoundRect ( dc, LeftRect, TopRect, RightRect, BottomRect, Width, Height );
DC_UnlockDc ( dc );
}
return ret;
}
BOOL
NTAPI
GreGradientFill(
HDC hdc,
PTRIVERTEX pVertex,
ULONG nVertex,
PVOID pMesh,
ULONG nMesh,
ULONG ulMode)
{
PDC pdc;
SURFACE *psurf;
EXLATEOBJ exlo;
RECTL rclExtent;
POINTL ptlDitherOrg;
ULONG i;
BOOL bRet;
/* Check parameters */
if (ulMode & GRADIENT_FILL_TRIANGLE)
{
PGRADIENT_TRIANGLE pTriangle = (PGRADIENT_TRIANGLE)pMesh;
for (i = 0; i < nMesh; i++, pTriangle++)
{
if (pTriangle->Vertex1 >= nVertex ||
pTriangle->Vertex2 >= nVertex ||
pTriangle->Vertex3 >= nVertex)
{
EngSetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
}
}
else
{
PGRADIENT_RECT pRect = (PGRADIENT_RECT)pMesh;
for (i = 0; i < nMesh; i++, pRect++)
{
if (pRect->UpperLeft >= nVertex || pRect->LowerRight >= nVertex)
{
EngSetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
}
}
/* Lock the output DC */
pdc = DC_LockDc(hdc);
if(!pdc)
{
EngSetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
if (!pdc->dclevel.pSurface)
{
/* Memory DC with no surface selected */
DC_UnlockDc(pdc);
return TRUE; // CHECKME
}
/* Calculate extent */
rclExtent.left = rclExtent.right = pVertex->x;
rclExtent.top = rclExtent.bottom = pVertex->y;
for (i = 0; i < nVertex; i++)
{
rclExtent.left = min(rclExtent.left, (pVertex + i)->x);
rclExtent.right = max(rclExtent.right, (pVertex + i)->x);
rclExtent.top = min(rclExtent.top, (pVertex + i)->y);
rclExtent.bottom = max(rclExtent.bottom, (pVertex + i)->y);
}
IntLPtoDP(pdc, (LPPOINT)&rclExtent, 2);
rclExtent.left += pdc->ptlDCOrig.x;
rclExtent.right += pdc->ptlDCOrig.x;
rclExtent.top += pdc->ptlDCOrig.y;
rclExtent.bottom += pdc->ptlDCOrig.y;
if (RECTL_bIsEmptyRect(&rclExtent))
{
DC_UnlockDc(pdc);
return TRUE;
}
ptlDitherOrg.x = ptlDitherOrg.y = 0;
IntLPtoDP(pdc, (LPPOINT)&ptlDitherOrg, 1);
ptlDitherOrg.x += pdc->ptlDCOrig.x;
ptlDitherOrg.y += pdc->ptlDCOrig.y;
if (pdc->fs & (DC_ACCUM_APP|DC_ACCUM_WMGR))
{
IntUpdateBoundsRect(pdc, &rclExtent);
}
DC_vPrepareDCsForBlit(pdc, &rclExtent, NULL, NULL);
psurf = pdc->dclevel.pSurface;
EXLATEOBJ_vInitialize(&exlo, &gpalRGB, psurf->ppal, 0, 0, 0);
bRet = IntEngGradientFill(&psurf->SurfObj,
(CLIPOBJ *)&pdc->co,
&exlo.xlo,
pVertex,
nVertex,
pMesh,
nMesh,
&rclExtent,
&ptlDitherOrg,
ulMode);
EXLATEOBJ_vCleanup(&exlo);
DC_vFinishBlit(pdc, NULL);
DC_UnlockDc(pdc);
return bRet;
}
BOOL
APIENTRY
NtGdiGradientFill(
HDC hdc,
PTRIVERTEX pVertex,
ULONG nVertex,
PVOID pMesh,
ULONG nMesh,
ULONG ulMode)
{
BOOL bRet;
PTRIVERTEX SafeVertex;
PVOID SafeMesh;
ULONG cbVertex, cbMesh;
/* Validate parameters */
if (!pVertex || !nVertex || !pMesh || !nMesh)
{
EngSetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
switch (ulMode)
{
case GRADIENT_FILL_RECT_H:
case GRADIENT_FILL_RECT_V:
cbMesh = nMesh * sizeof(GRADIENT_RECT);
break;
case GRADIENT_FILL_TRIANGLE:
cbMesh = nMesh * sizeof(GRADIENT_TRIANGLE);
break;
default:
EngSetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
cbVertex = nVertex * sizeof(TRIVERTEX) ;
if(cbVertex + cbMesh <= cbVertex)
{
/* Overflow */
return FALSE ;
}
/* Allocate a kernel mode buffer */
SafeVertex = ExAllocatePoolWithTag(PagedPool, cbVertex + cbMesh, TAG_SHAPE);
if(!SafeVertex)
{
EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
return FALSE;
}
SafeMesh = (PVOID)((ULONG_PTR)SafeVertex + cbVertex);
/* Copy the parameters to kernel mode */
_SEH2_TRY
{
ProbeForRead(pVertex, cbVertex, 1);
ProbeForRead(pMesh, cbMesh, 1);
RtlCopyMemory(SafeVertex, pVertex, cbVertex);
RtlCopyMemory(SafeMesh, pMesh, cbMesh);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
ExFreePoolWithTag(SafeVertex, TAG_SHAPE);
SetLastNtError(_SEH2_GetExceptionCode());
_SEH2_YIELD(return FALSE;)
}
_SEH2_END;
/* Call the internal function */
bRet = GreGradientFill(hdc, SafeVertex, nVertex, SafeMesh, nMesh, ulMode);
/* Cleanup and return result */
ExFreePoolWithTag(SafeVertex, TAG_SHAPE);
return bRet;
}
BOOL APIENTRY
NtGdiExtFloodFill(
HDC hDC,
INT XStart,
INT YStart,
COLORREF Color,
UINT FillType)
{
PDC dc;
#if 0
PDC_ATTR pdcattr;
#endif
SURFACE *psurf;
EXLATEOBJ exlo;
BOOL Ret = FALSE;
RECTL DestRect;
POINTL Pt;
ULONG ConvColor;
PREGION prgn;
dc = DC_LockDc(hDC);
if (!dc)
{
EngSetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
if (!dc->dclevel.pSurface)
{
Ret = TRUE;
goto cleanup;
}
#if 0
pdcattr = dc->pdcattr;
#endif
Pt.x = XStart;
Pt.y = YStart;
IntLPtoDP(dc, (LPPOINT)&Pt, 1);
DC_vPrepareDCsForBlit(dc, &DestRect, NULL, NULL);
psurf = dc->dclevel.pSurface;
prgn = dc->prgnRao ? dc->prgnRao : dc->prgnVis;
if (prgn)
{
Ret = REGION_PtInRegion(prgn, Pt.x, Pt.y);
if (Ret)
REGION_GetRgnBox(prgn, (LPRECT)&DestRect);
else
{
DC_vFinishBlit(dc, NULL);
goto cleanup;
}
}
else
{
RECTL_vSetRect(&DestRect, 0, 0, psurf->SurfObj.sizlBitmap.cx, psurf->SurfObj.sizlBitmap.cy);
}
if (dc->fs & (DC_ACCUM_APP|DC_ACCUM_WMGR))
{
IntUpdateBoundsRect(dc, &DestRect);
}
EXLATEOBJ_vInitialize(&exlo, &gpalRGB, psurf->ppal, 0, 0xffffff, 0);
/* Only solid fills supported for now
* How to support pattern brushes and non standard surfaces (not offering dib functions):
* Version a (most likely slow): call DrvPatBlt for every pixel
* Version b: create a flood mask and let MaskBlt blit a masked brush */
ConvColor = XLATEOBJ_iXlate(&exlo.xlo, Color);
Ret = DIB_XXBPP_FloodFillSolid(&psurf->SurfObj, &dc->eboFill.BrushObject, &DestRect, &Pt, ConvColor, FillType);
DC_vFinishBlit(dc, NULL);
EXLATEOBJ_vCleanup(&exlo);
cleanup:
DC_UnlockDc(dc);
return Ret;
}
/* EOF */