reactos/win32ss/gdi/ntgdi/line.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

662 lines
17 KiB
C

/*
* PROJECT: ReactOS Win32k Subsystem
* LICENSE: GPL - See COPYING in the top level directory
* FILE: win32ss/gdi/ntgdi/line.c
* PURPOSE: Line functions
* PROGRAMMERS: ...
*/
#include <win32k.h>
#define NDEBUG
#include <debug.h>
// Some code from the WINE project source (www.winehq.com)
VOID FASTCALL
AddPenLinesBounds(PDC dc, int count, POINT *points)
{
DWORD join, endcap;
RECTL bounds, rect;
LONG lWidth;
PBRUSH pbrLine;
/* Get BRUSH from current pen. */
pbrLine = dc->dclevel.pbrLine;
ASSERT(pbrLine);
lWidth = pbrLine->lWidth;
// Setup bounds
bounds.left = bounds.top = INT_MAX;
bounds.right = bounds.bottom = INT_MIN;
if (((pbrLine->ulPenStyle & PS_TYPE_MASK) & PS_GEOMETRIC) || lWidth > 1)
{
/* Windows uses some heuristics to estimate the distance from the point that will be painted */
lWidth = lWidth + 2;
endcap = (PS_ENDCAP_MASK & pbrLine->ulPenStyle);
join = (PS_JOIN_MASK & pbrLine->ulPenStyle);
if (join == PS_JOIN_MITER)
{
lWidth *= 5;
if (endcap == PS_ENDCAP_SQUARE) lWidth = (lWidth * 3 + 1) / 2;
}
else
{
if (endcap == PS_ENDCAP_SQUARE) lWidth -= lWidth / 4;
else lWidth = (lWidth + 1) / 2;
}
}
while (count-- > 0)
{
rect.left = points->x - lWidth;
rect.top = points->y - lWidth;
rect.right = points->x + lWidth + 1;
rect.bottom = points->y + lWidth + 1;
RECTL_bUnionRect(&bounds, &bounds, &rect);
points++;
}
DPRINT("APLB dc %p l %d t %d\n",dc,rect.left,rect.top);
DPRINT(" r %d b %d\n",rect.right,rect.bottom);
{
RECTL rcRgn = dc->erclClip; // Use the clip box for now.
if (RECTL_bIntersectRect( &rcRgn, &rcRgn, &bounds ))
IntUpdateBoundsRect(dc, &rcRgn);
else
IntUpdateBoundsRect(dc, &bounds);
}
}
// Should use Fx in Point
//
BOOL FASTCALL
IntGdiMoveToEx(DC *dc,
int X,
int Y,
LPPOINT Point)
{
PDC_ATTR pdcattr = dc->pdcattr;
if ( Point )
{
if ( pdcattr->ulDirty_ & DIRTY_PTLCURRENT ) // Double hit!
{
Point->x = pdcattr->ptfxCurrent.x; // ret prev before change.
Point->y = pdcattr->ptfxCurrent.y;
IntDPtoLP ( dc, Point, 1); // Reconvert back.
}
else
{
Point->x = pdcattr->ptlCurrent.x;
Point->y = pdcattr->ptlCurrent.y;
}
}
pdcattr->ptlCurrent.x = X;
pdcattr->ptlCurrent.y = Y;
pdcattr->ptfxCurrent = pdcattr->ptlCurrent;
CoordLPtoDP(dc, &pdcattr->ptfxCurrent); // Update fx
pdcattr->ulDirty_ &= ~(DIRTY_PTLCURRENT|DIRTY_PTFXCURRENT|DIRTY_STYLESTATE);
return TRUE;
}
BOOL FASTCALL
GreMoveTo( HDC hdc,
INT x,
INT y,
LPPOINT pptOut)
{
BOOL Ret;
PDC dc;
if (!(dc = DC_LockDc(hdc)))
{
EngSetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
Ret = IntGdiMoveToEx(dc, x, y, pptOut);
DC_UnlockDc(dc);
return Ret;
}
// Should use Fx in pt
//
VOID FASTCALL
IntGetCurrentPositionEx(PDC dc, LPPOINT pt)
{
PDC_ATTR pdcattr = dc->pdcattr;
if ( pt )
{
if (pdcattr->ulDirty_ & DIRTY_PTFXCURRENT)
{
pdcattr->ptfxCurrent = pdcattr->ptlCurrent;
CoordLPtoDP(dc, &pdcattr->ptfxCurrent); // Update fx
pdcattr->ulDirty_ &= ~(DIRTY_PTFXCURRENT|DIRTY_STYLESTATE);
}
pt->x = pdcattr->ptlCurrent.x;
pt->y = pdcattr->ptlCurrent.y;
}
}
BOOL FASTCALL
IntGdiLineTo(DC *dc,
int XEnd,
int YEnd)
{
SURFACE *psurf;
BOOL Ret = TRUE;
PBRUSH pbrLine;
RECTL Bounds;
POINT Points[2];
PDC_ATTR pdcattr = dc->pdcattr;
ASSERT_DC_PREPARED(dc);
if (PATH_IsPathOpen(dc->dclevel))
{
Ret = PATH_LineTo(dc, XEnd, YEnd);
}
else
{
psurf = dc->dclevel.pSurface;
if (NULL == psurf)
{
EngSetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
Points[0].x = pdcattr->ptlCurrent.x;
Points[0].y = pdcattr->ptlCurrent.y;
Points[1].x = XEnd;
Points[1].y = YEnd;
IntLPtoDP(dc, Points, 2);
/* The DCOrg is in device coordinates */
Points[0].x += dc->ptlDCOrig.x;
Points[0].y += dc->ptlDCOrig.y;
Points[1].x += dc->ptlDCOrig.x;
Points[1].y += dc->ptlDCOrig.y;
Bounds.left = min(Points[0].x, Points[1].x);
Bounds.top = min(Points[0].y, Points[1].y);
Bounds.right = max(Points[0].x, Points[1].x);
Bounds.bottom = max(Points[0].y, Points[1].y);
/* Get BRUSH from current pen. */
pbrLine = dc->dclevel.pbrLine;
ASSERT(pbrLine);
if (dc->fs & (DC_ACCUM_APP|DC_ACCUM_WMGR))
{
DPRINT("Bounds dc %p l %d t %d\n",dc,Bounds.left,Bounds.top);
DPRINT(" r %d b %d\n",Bounds.right,Bounds.bottom);
AddPenLinesBounds(dc, 2, Points);
}
if (!(pbrLine->flAttrs & BR_IS_NULL))
{
Ret = IntEngLineTo(&psurf->SurfObj,
(CLIPOBJ *)&dc->co,
&dc->eboLine.BrushObject,
Points[0].x, Points[0].y,
Points[1].x, Points[1].y,
&Bounds,
ROP2_TO_MIX(pdcattr->jROP2));
}
}
if (Ret)
{
pdcattr->ptlCurrent.x = XEnd;
pdcattr->ptlCurrent.y = YEnd;
pdcattr->ptfxCurrent = pdcattr->ptlCurrent;
CoordLPtoDP(dc, &pdcattr->ptfxCurrent); // Update fx
pdcattr->ulDirty_ &= ~(DIRTY_PTLCURRENT|DIRTY_PTFXCURRENT|DIRTY_STYLESTATE);
}
return Ret;
}
BOOL FASTCALL
IntGdiPolyBezier(DC *dc,
LPPOINT pt,
DWORD Count)
{
BOOL ret = FALSE; // Default to FAILURE
if ( PATH_IsPathOpen(dc->dclevel) )
{
return PATH_PolyBezier ( dc, pt, Count );
}
/* We'll convert it into line segments and draw them using Polyline */
{
POINT *Pts;
INT nOut;
Pts = GDI_Bezier ( pt, Count, &nOut );
if ( Pts )
{
ret = IntGdiPolyline(dc, Pts, nOut);
ExFreePoolWithTag(Pts, TAG_BEZIER);
}
}
return ret;
}
BOOL FASTCALL
IntGdiPolyBezierTo(DC *dc,
LPPOINT pt,
DWORD Count)
{
BOOL ret = FALSE; // Default to failure
PDC_ATTR pdcattr = dc->pdcattr;
if ( PATH_IsPathOpen(dc->dclevel) )
ret = PATH_PolyBezierTo ( dc, pt, Count );
else /* We'll do it using PolyBezier */
{
POINT *npt;
npt = ExAllocatePoolWithTag(PagedPool,
sizeof(POINT) * (Count + 1),
TAG_BEZIER);
if ( npt )
{
npt[0].x = pdcattr->ptlCurrent.x;
npt[0].y = pdcattr->ptlCurrent.y;
memcpy(npt + 1, pt, sizeof(POINT) * Count);
ret = IntGdiPolyBezier(dc, npt, Count+1);
ExFreePoolWithTag(npt, TAG_BEZIER);
}
}
if ( ret )
{
pdcattr->ptlCurrent.x = pt[Count-1].x;
pdcattr->ptlCurrent.y = pt[Count-1].y;
pdcattr->ptfxCurrent = pdcattr->ptlCurrent;
CoordLPtoDP(dc, &pdcattr->ptfxCurrent); // Update fx
pdcattr->ulDirty_ &= ~(DIRTY_PTLCURRENT|DIRTY_PTFXCURRENT|DIRTY_STYLESTATE);
}
return ret;
}
BOOL FASTCALL
IntGdiPolyline(DC *dc,
LPPOINT pt,
int Count)
{
SURFACE *psurf;
BRUSH *pbrLine;
LPPOINT Points;
BOOL Ret = TRUE;
LONG i;
PDC_ATTR pdcattr = dc->pdcattr;
if (!dc->dclevel.pSurface)
{
return TRUE;
}
DC_vPrepareDCsForBlit(dc, NULL, NULL, NULL);
psurf = dc->dclevel.pSurface;
/* Get BRUSHOBJ from current pen. */
pbrLine = dc->dclevel.pbrLine;
ASSERT(pbrLine);
if (!(pbrLine->flAttrs & BR_IS_NULL))
{
Points = EngAllocMem(0, Count * sizeof(POINT), GDITAG_TEMP);
if (Points != NULL)
{
RtlCopyMemory(Points, pt, Count * sizeof(POINT));
IntLPtoDP(dc, Points, Count);
/* Offset the array of points by the DC origin */
for (i = 0; i < Count; i++)
{
Points[i].x += dc->ptlDCOrig.x;
Points[i].y += dc->ptlDCOrig.y;
}
if (dc->fs & (DC_ACCUM_APP|DC_ACCUM_WMGR))
{
AddPenLinesBounds(dc, Count, Points);
}
Ret = IntEngPolyline(&psurf->SurfObj,
(CLIPOBJ *)&dc->co,
&dc->eboLine.BrushObject,
Points,
Count,
ROP2_TO_MIX(pdcattr->jROP2));
EngFreeMem(Points);
}
else
{
Ret = FALSE;
}
}
DC_vFinishBlit(dc, NULL);
return Ret;
}
BOOL FASTCALL
IntGdiPolylineTo(DC *dc,
LPPOINT pt,
DWORD Count)
{
BOOL ret = FALSE; // Default to failure
PDC_ATTR pdcattr = dc->pdcattr;
if (PATH_IsPathOpen(dc->dclevel))
{
ret = PATH_PolylineTo(dc, pt, Count);
}
else /* Do it using Polyline */
{
POINT *pts = ExAllocatePoolWithTag(PagedPool,
sizeof(POINT) * (Count + 1),
TAG_SHAPE);
if ( pts )
{
pts[0].x = pdcattr->ptlCurrent.x;
pts[0].y = pdcattr->ptlCurrent.y;
memcpy( pts + 1, pt, sizeof(POINT) * Count);
ret = IntGdiPolyline(dc, pts, Count + 1);
ExFreePoolWithTag(pts, TAG_SHAPE);
}
}
if ( ret )
{
pdcattr->ptlCurrent.x = pt[Count-1].x;
pdcattr->ptlCurrent.y = pt[Count-1].y;
pdcattr->ptfxCurrent = pdcattr->ptlCurrent;
CoordLPtoDP(dc, &pdcattr->ptfxCurrent); // Update fx
pdcattr->ulDirty_ &= ~(DIRTY_PTLCURRENT|DIRTY_PTFXCURRENT|DIRTY_STYLESTATE);
}
return ret;
}
BOOL FASTCALL
IntGdiPolyPolyline(DC *dc,
LPPOINT pt,
PULONG PolyPoints,
DWORD Count)
{
ULONG i;
LPPOINT pts;
PULONG pc;
BOOL ret = FALSE; // Default to failure
pts = pt;
pc = PolyPoints;
if (PATH_IsPathOpen(dc->dclevel))
{
return PATH_PolyPolyline( dc, pt, PolyPoints, Count );
}
for (i = 0; i < Count; i++)
{
ret = IntGdiPolyline ( dc, pts, *pc );
if (ret == FALSE)
{
return ret;
}
pts+=*pc++;
}
return ret;
}
/******************************************************************************/
BOOL
APIENTRY
NtGdiLineTo(HDC hDC,
int XEnd,
int YEnd)
{
DC *dc;
BOOL Ret;
RECT rcLockRect ;
dc = DC_LockDc(hDC);
if (!dc)
{
EngSetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
rcLockRect.left = dc->pdcattr->ptlCurrent.x;
rcLockRect.top = dc->pdcattr->ptlCurrent.y;
rcLockRect.right = XEnd;
rcLockRect.bottom = YEnd;
IntLPtoDP(dc, &rcLockRect, 2);
/* The DCOrg is in device coordinates */
rcLockRect.left += dc->ptlDCOrig.x;
rcLockRect.top += dc->ptlDCOrig.y;
rcLockRect.right += dc->ptlDCOrig.x;
rcLockRect.bottom += dc->ptlDCOrig.y;
DC_vPrepareDCsForBlit(dc, &rcLockRect, NULL, NULL);
Ret = IntGdiLineTo(dc, XEnd, YEnd);
DC_vFinishBlit(dc, NULL);
DC_UnlockDc(dc);
return Ret;
}
// FIXME: This function is completely broken
BOOL
APIENTRY
NtGdiPolyDraw(
IN HDC hdc,
IN LPPOINT lppt,
IN LPBYTE lpbTypes,
IN ULONG cCount)
{
PDC dc;
PDC_ATTR pdcattr;
POINT bzr[4];
volatile PPOINT line_pts, line_pts_old, bzr_pts;
INT num_pts, num_bzr_pts, space, space_old, size;
ULONG i;
BOOL result = FALSE;
dc = DC_LockDc(hdc);
if (!dc) return FALSE;
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);
if (!cCount)
{
DC_UnlockDc(dc);
return TRUE;
}
line_pts = NULL;
line_pts_old = NULL;
bzr_pts = NULL;
_SEH2_TRY
{
ProbeArrayForRead(lppt, sizeof(POINT), cCount, sizeof(LONG));
ProbeArrayForRead(lpbTypes, sizeof(BYTE), cCount, sizeof(BYTE));
if (PATH_IsPathOpen(dc->dclevel))
{
result = PATH_PolyDraw(dc, (const POINT *)lppt, (const BYTE *)lpbTypes, cCount);
_SEH2_LEAVE;
}
/* Check for valid point types */
for (i = 0; i < cCount; i++)
{
switch (lpbTypes[i])
{
case PT_MOVETO:
case PT_LINETO | PT_CLOSEFIGURE:
case PT_LINETO:
break;
case PT_BEZIERTO:
if((i + 2 < cCount) && (lpbTypes[i + 1] == PT_BEZIERTO) &&
((lpbTypes[i + 2] & ~PT_CLOSEFIGURE) == PT_BEZIERTO))
{
i += 2;
break;
}
default:
_SEH2_LEAVE;
}
}
space = cCount + 300;
line_pts = ExAllocatePoolWithTag(PagedPool, space * sizeof(POINT), TAG_SHAPE);
if (line_pts == NULL)
{
result = FALSE;
_SEH2_LEAVE;
}
num_pts = 1;
line_pts[0].x = pdcattr->ptlCurrent.x;
line_pts[0].y = pdcattr->ptlCurrent.y;
for ( i = 0; i < cCount; i++ )
{
switch (lpbTypes[i])
{
case PT_MOVETO:
if (num_pts >= 2) IntGdiPolyline( dc, line_pts, num_pts );
num_pts = 0;
line_pts[num_pts++] = lppt[i];
break;
case PT_LINETO:
case (PT_LINETO | PT_CLOSEFIGURE):
line_pts[num_pts++] = lppt[i];
break;
case PT_BEZIERTO:
bzr[0].x = line_pts[num_pts - 1].x;
bzr[0].y = line_pts[num_pts - 1].y;
RtlCopyMemory( &bzr[1], &lppt[i], 3 * sizeof(POINT) );
if ((bzr_pts = GDI_Bezier( bzr, 4, &num_bzr_pts )))
{
size = num_pts + (cCount - i) + num_bzr_pts;
if (space < size)
{
space_old = space;
space = size * 2;
line_pts_old = line_pts;
line_pts = ExAllocatePoolWithTag(PagedPool, space * sizeof(POINT), TAG_SHAPE);
if (!line_pts) _SEH2_LEAVE;
RtlCopyMemory(line_pts, line_pts_old, space_old * sizeof(POINT));
ExFreePoolWithTag(line_pts_old, TAG_SHAPE);
line_pts_old = NULL;
}
RtlCopyMemory( &line_pts[num_pts], &bzr_pts[1], (num_bzr_pts - 1) * sizeof(POINT) );
num_pts += num_bzr_pts - 1;
ExFreePoolWithTag(bzr_pts, TAG_BEZIER);
bzr_pts = NULL;
}
i += 2;
break;
}
if (lpbTypes[i] & PT_CLOSEFIGURE) line_pts[num_pts++] = line_pts[0];
}
if (num_pts >= 2) IntGdiPolyline( dc, line_pts, num_pts );
IntGdiMoveToEx( dc, line_pts[num_pts - 1].x, line_pts[num_pts - 1].y, NULL );
result = TRUE;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
SetLastNtError(_SEH2_GetExceptionCode());
}
_SEH2_END;
if (line_pts != NULL)
{
ExFreePoolWithTag(line_pts, TAG_SHAPE);
}
if ((line_pts_old != NULL) && (line_pts_old != line_pts))
{
ExFreePoolWithTag(line_pts_old, TAG_SHAPE);
}
if (bzr_pts != NULL)
{
ExFreePoolWithTag(bzr_pts, TAG_BEZIER);
}
DC_UnlockDc(dc);
return result;
}
/*
* @implemented
*/
_Success_(return != FALSE)
BOOL
APIENTRY
NtGdiMoveTo(
IN HDC hdc,
IN INT x,
IN INT y,
OUT OPTIONAL LPPOINT pptOut)
{
PDC pdc;
BOOL Ret;
POINT Point;
pdc = DC_LockDc(hdc);
if (!pdc) return FALSE;
Ret = IntGdiMoveToEx(pdc, x, y, &Point);
if (Ret && pptOut)
{
_SEH2_TRY
{
ProbeForWrite(pptOut, sizeof(POINT), 1);
RtlCopyMemory(pptOut, &Point, sizeof(POINT));
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
SetLastNtError(_SEH2_GetExceptionCode());
Ret = FALSE; // CHECKME: is this correct?
}
_SEH2_END;
}
DC_UnlockDc(pdc);
return Ret;
}
/* EOF */