reactos/win32ss/gdi/ntgdi/fillshap.c
Timo Kreuzer 456e2192b7 [WIN32K] Check for empty output rect in GreGradientFill
Fixes a failed ASSERT.
CORE-14148 #resolve
2017-12-28 10:46:01 +01:00

1150 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;
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);
/// FIXME: what about prgnVIS? And what about REAL clipping?
psurf = dc->dclevel.pSurface;
if (dc->prgnRao)
{
Ret = REGION_PtInRegion(dc->prgnRao, Pt.x, Pt.y);
if (Ret)
REGION_GetRgnBox(dc->prgnRao, (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 */