[WIN32K] Rewrite PATH_Ellipse & PATH_RoundRect using integer arithmetics

This commit is contained in:
Jérôme Gardou 2021-03-31 16:17:02 +02:00 committed by Jérôme Gardou
parent f9c068685f
commit 26b5596a11
2 changed files with 144 additions and 124 deletions

View file

@ -396,8 +396,8 @@ PATH_ScaleNormalizedPoint(
* Normalizes a point with respect to the box whose corners are passed in * Normalizes a point with respect to the box whose corners are passed in
* corners. The normalized coordinates are stored in *pX and *pY. * corners. The normalized coordinates are stored in *pX and *pY.
*/ */
static
VOID VOID
FASTCALL
PATH_NormalizePoint( PATH_NormalizePoint(
POINTL corners[], POINTL corners[],
const POINTL *pPoint, const POINTL *pPoint,
@ -428,47 +428,32 @@ PATH_NormalizePoint(
* *
* Helper function for PATH_RoundRect() and PATH_Rectangle() * Helper function for PATH_RoundRect() and PATH_Rectangle()
*/ */
static
BOOL BOOL
PATH_CheckCorners( PATH_CheckRect(
DC *dc, DC *dc,
POINT corners[], RECTL* rect,
INT x1, INT x1,
INT y1, INT y1,
INT x2, INT x2,
INT y2) INT y2)
{ {
INT temp;
PDC_ATTR pdcattr = dc->pdcattr; PDC_ATTR pdcattr = dc->pdcattr;
/* Convert points to device coordinates */ /* Convert points to device coordinates */
corners[0].x = x1; RECTL_vSetRect(rect, x1, y1, x2, y2);
corners[0].y = y1; IntLPtoDP(dc, (PPOINT)rect, 2);
corners[1].x = x2;
corners[1].y = y2;
IntLPtoDP(dc, corners, 2);
/* Make sure first corner is top left and second corner is bottom right */ /* Make sure first corner is top left and second corner is bottom right */
if (corners[0].x > corners[1].x) RECTL_vMakeWellOrdered(rect);
{
temp = corners[0].x;
corners[0].x = corners[1].x;
corners[1].x = temp;
}
if (corners[0].y > corners[1].y)
{
temp = corners[0].y;
corners[0].y = corners[1].y;
corners[1].y = temp;
}
/* In GM_COMPATIBLE, don't include bottom and right edges */ /* In GM_COMPATIBLE, don't include bottom and right edges */
if (pdcattr->iGraphicsMode == GM_COMPATIBLE) if (pdcattr->iGraphicsMode == GM_COMPATIBLE)
{ {
if (corners[0].x == corners[1].x) return FALSE; if (rect->left == rect->right) return FALSE;
if (corners[0].y == corners[1].y) return FALSE; if (rect->top == rect->bottom) return FALSE;
corners[1].x--; rect->right--;
corners[1].y--; rect->bottom--;
} }
return TRUE; return TRUE;
} }
@ -647,26 +632,25 @@ PATH_Rectangle(
INT y2) INT y2)
{ {
PPATH pPath; PPATH pPath;
POINT corners[2], points[4]; RECTL rect;
POINTL points[4];
BYTE *type; BYTE *type;
pPath = PATH_LockPath(dc->dclevel.hPath); pPath = PATH_LockPath(dc->dclevel.hPath);
if (!pPath) return FALSE; if (!pPath) return FALSE;
if (!PATH_CheckCorners(dc, corners, x1, y1, x2, y2)) if (!PATH_CheckRect(dc, &rect, x1, y1, x2, y2))
{ {
PATH_UnlockPath(pPath); PATH_UnlockPath(pPath);
return TRUE; return TRUE;
} }
points[0].x = corners[1].x; points[0].x = rect.right; points[0].y = rect.top;
points[0].y = corners[0].y; points[1].x = rect.left; points[1].y = rect.top;
points[1] = corners[0]; points[2].x = rect.left; points[2].y = rect.bottom;
points[2].x = corners[0].x; points[3].x = rect.right; points[3].y = rect.bottom;
points[2].y = corners[1].y;
points[3] = corners[1];
if (dc->dclevel.flPath & DCPATH_CLOCKWISE) reverse_points( points, 4 ); if (dc->dclevel.flPath & DCPATH_CLOCKWISE) reverse_points(points, 4 );
if (!(type = add_points( pPath, points, 4, PT_LINETO ))) if (!(type = add_points( pPath, points, 4, PT_LINETO )))
{ {
@ -688,7 +672,6 @@ PATH_Rectangle(
* *
*/ */
BOOL BOOL
FASTCALL
PATH_RoundRect( PATH_RoundRect(
DC *dc, DC *dc,
INT x1, INT x1,
@ -698,72 +681,85 @@ PATH_RoundRect(
INT ell_width, INT ell_width,
INT ell_height) INT ell_height)
{ {
const double factor = 0.55428475; /* 4 / 3 * (sqrt(2) - 1) */
PPATH pPath; PPATH pPath;
POINT corners[2], ellipse[2], points[16]; RECTL rect, ellipse;
POINT points[16];
BYTE *type; BYTE *type;
double width, height; INT xOffset, yOffset;
if (!ell_width || !ell_height) return PATH_Rectangle( dc, x1, y1, x2, y2 ); if (!ell_width || !ell_height) return PATH_Rectangle( dc, x1, y1, x2, y2 );
pPath = PATH_LockPath(dc->dclevel.hPath); pPath = PATH_LockPath(dc->dclevel.hPath);
if (!pPath) return FALSE; if (!pPath) return FALSE;
if (!PATH_CheckCorners(dc, corners, x1, y1, x2, y2)) if (!PATH_CheckRect(dc, &rect, x1, y1, x2, y2))
{ {
PATH_UnlockPath(pPath); PATH_UnlockPath(pPath);
return TRUE; return TRUE;
} }
ellipse[0].x = ellipse[0].y = 0; RECTL_vSetRect(&ellipse, 0, 0, ell_width, ell_height);
ellipse[1].x = ell_width; IntLPtoDP( dc, (PPOINT)&ellipse, 2 );
ellipse[1].y = ell_height; RECTL_vMakeWellOrdered(&ellipse);
IntLPtoDP( dc, &ellipse, 2 ); ell_width = min(RECTL_lGetWidth(&ellipse), RECTL_lGetWidth(&rect));
ell_width = min( abs( ellipse[1].x - ellipse[0].x ), corners[1].x - corners[0].x ); ell_height = min(RECTL_lGetHeight(&ellipse), RECTL_lGetHeight(&rect));
ell_height = min( abs( ellipse[1].y - ellipse[0].y ), corners[1].y - corners[0].y );
width = ell_width / 2.0; /*
height = ell_height / 2.0; * See here to understand what's happening
* https://stackoverflow.com/questions/1734745/how-to-create-circle-with-b%C3%A9zier-curves
*/
xOffset = EngMulDiv(ell_width, 44771525, 200000000); /* w * (1 - 0.5522847) / 2 */
yOffset = EngMulDiv(ell_height, 44771525, 200000000); /* h * (1 - 0.5522847) / 2 */
TRACE("xOffset %d, yOffset %d, Rect WxH: %dx%d.\n",
xOffset, yOffset, RECTL_lGetWidth(&rect), RECTL_lGetHeight(&rect));
/*
* Get half width & height.
* Do not use integer division, we need the rounding made by EngMulDiv.
*/
ell_width = EngMulDiv(ell_width, 1, 2);
ell_height = EngMulDiv(ell_height, 1, 2);
/* starting point */ /* starting point */
points[0].x = corners[1].x; points[0].x = rect.right;
points[0].y = corners[0].y + GDI_ROUND( height ); points[0].y = rect.top + ell_height;
/* first curve */ /* first curve */
points[1].x = corners[1].x; points[1].x = rect.right;
points[1].y = corners[0].y + GDI_ROUND( height * (1 - factor) ); points[1].y = rect.top + yOffset;
points[2].x = corners[1].x - GDI_ROUND( width * (1 - factor) ); points[2].x = rect.right - xOffset;
points[2].y = corners[0].y; points[2].y = rect.top;
points[3].x = corners[1].x - GDI_ROUND( width ); points[3].x = rect.right - ell_width;
points[3].y = corners[0].y; points[3].y = rect.top;
/* horizontal line */ /* horizontal line */
points[4].x = corners[0].x + GDI_ROUND( width ); points[4].x = rect.left + ell_width;
points[4].y = corners[0].y; points[4].y = rect.top;
/* second curve */ /* second curve */
points[5].x = corners[0].x + GDI_ROUND( width * (1 - factor) ); points[5].x = rect.left + xOffset;
points[5].y = corners[0].y; points[5].y = rect.top;
points[6].x = corners[0].x; points[6].x = rect.left;
points[6].y = corners[0].y + GDI_ROUND( height * (1 - factor) ); points[6].y = rect.top + yOffset;
points[7].x = corners[0].x; points[7].x = rect.left;
points[7].y = corners[0].y + GDI_ROUND( height ); points[7].y = rect.top + ell_height;
/* vertical line */ /* vertical line */
points[8].x = corners[0].x; points[8].x = rect.left;
points[8].y = corners[1].y - GDI_ROUND( height ); points[8].y = rect.bottom - ell_height;
/* third curve */ /* third curve */
points[9].x = corners[0].x; points[9].x = rect.left;
points[9].y = corners[1].y - GDI_ROUND( height * (1 - factor) ); points[9].y = rect.bottom - yOffset;
points[10].x = corners[0].x + GDI_ROUND( width * (1 - factor) ); points[10].x = rect.left + xOffset;
points[10].y = corners[1].y; points[10].y = rect.bottom;
points[11].x = corners[0].x + GDI_ROUND( width ); points[11].x = rect.left + ell_width;
points[11].y = corners[1].y; points[11].y = rect.bottom;
/* horizontal line */ /* horizontal line */
points[12].x = corners[1].x - GDI_ROUND( width ); points[12].x = rect.right - ell_width;
points[12].y = corners[1].y; points[12].y = rect.bottom;
/* fourth curve */ /* fourth curve */
points[13].x = corners[1].x - GDI_ROUND( width * (1 - factor) ); points[13].x = rect.right - xOffset;
points[13].y = corners[1].y; points[13].y = rect.bottom;
points[14].x = corners[1].x; points[14].x = rect.right;
points[14].y = corners[1].y - GDI_ROUND( height * (1 - factor) ); points[14].y = rect.bottom - yOffset;
points[15].x = corners[1].x; points[15].x = rect.right;
points[15].y = corners[1].y - GDI_ROUND( height ); points[15].y = rect.bottom - ell_height;
if (dc->dclevel.flPath & DCPATH_CLOCKWISE) reverse_points( points, 16 ); if (dc->dclevel.flPath & DCPATH_CLOCKWISE) reverse_points( points, 16 );
if (!(type = add_points( pPath, points, 16, PT_BEZIERTO ))) if (!(type = add_points( pPath, points, 16, PT_BEZIERTO )))
@ -783,7 +779,6 @@ PATH_RoundRect(
* *
*/ */
BOOL BOOL
FASTCALL
PATH_Ellipse( PATH_Ellipse(
PDC dc, PDC dc,
INT x1, INT x1,
@ -791,55 +786,81 @@ PATH_Ellipse(
INT x2, INT x2,
INT y2) INT y2)
{ {
const double factor = 0.55428475; /* 4 / 3 * (sqrt(2) - 1) */
PPATH pPath; PPATH pPath;
POINT corners[2], points[13]; POINT points[13];
RECTL rect;
BYTE *type; BYTE *type;
double width, height; LONG xRadius, yRadius, xOffset, yOffset;
POINT left, top, right, bottom;
pPath = PATH_LockPath(dc->dclevel.hPath); TRACE("PATH_Ellipse: %p -> (%d, %d) - (%d, %d)\n",
if (!pPath) return FALSE; dc, x1, y1, x2, y2);
if (!PATH_CheckCorners(dc, corners, x1, y1, x2, y2)) if (!PATH_CheckRect(dc, &rect, x1, y1, x2, y2))
{ {
PATH_UnlockPath(pPath);
return TRUE; return TRUE;
} }
width = (corners[1].x - corners[0].x) / 2.0; xRadius = RECTL_lGetWidth(&rect) / 2;
height = (corners[1].y - corners[0].y) / 2.0; yRadius = RECTL_lGetHeight(&rect) / 2;
/* starting point */ /* Get the four points which box our ellipse */
points[0].x = corners[1].x; left.x = rect.left; left.y = rect.top + yRadius;
points[0].y = corners[0].y + GDI_ROUND( height ); top.x = rect.left + xRadius; top.y = rect.top;
/* first curve */ right.x = rect.right; right.y = rect.bottom - yRadius;
points[1].x = corners[1].x; bottom.x = rect.right - xRadius; bottom.y = rect.bottom;
points[1].y = corners[0].y + GDI_ROUND( height * (1 - factor) );
points[2].x = corners[1].x - GDI_ROUND( width * (1 - factor) ); /*
points[2].y = corners[0].y; * See here to understand what's happening
points[3].x = corners[0].x + GDI_ROUND( width ); * https://stackoverflow.com/questions/1734745/how-to-create-circle-with-b%C3%A9zier-curves
points[3].y = corners[0].y; */
/* second curve */ xOffset = EngMulDiv(RECTL_lGetWidth(&rect), 55428475, 200000000); /* w * 0.55428475 / 2 */
points[4].x = corners[0].x + GDI_ROUND( width * (1 - factor) ); yOffset = EngMulDiv(RECTL_lGetHeight(&rect), 55428475, 200000000); /* h * 0.55428475 / 2 */
points[4].y = corners[0].y; TRACE("xOffset %d, yOffset %d, Rect WxH: %dx%d.\n",
points[5].x = corners[0].x; xOffset, yOffset, RECTL_lGetWidth(&rect), RECTL_lGetHeight(&rect));
points[5].y = corners[0].y + GDI_ROUND( height * (1 - factor) );
points[6].x = corners[0].x; pPath = PATH_LockPath(dc->dclevel.hPath);
points[6].y = corners[0].y + GDI_ROUND( height ); if (!pPath)
/* third curve */ return FALSE;
points[7].x = corners[0].x;
points[7].y = corners[1].y - GDI_ROUND( height * (1 - factor) ); /* Starting point: Right */
points[8].x = corners[0].x + GDI_ROUND( width * (1 - factor) ); points[0] = right;
points[8].y = corners[1].y;
points[9].x = corners[0].x + GDI_ROUND( width ); /* first curve - going up, left */
points[9].y = corners[1].y; points[1] = right;
/* fourth curve */ points[1].y -= yOffset;
points[10].x = corners[1].x - GDI_ROUND( width * (1 - factor) ); points[2] = top;
points[10].y = corners[1].y; points[2].x += xOffset;
points[11].x = corners[1].x;
points[11].y = corners[1].y - GDI_ROUND( height * (1 - factor) ); /* top */
points[12].x = corners[1].x; points[3] = top;
points[12].y = corners[1].y - GDI_ROUND( height );
/* second curve - going left, down*/
points[4] = top;
points[4].x -= xOffset;
points[5] = left;
points[5].y -= yOffset;
/* Left */
points[6] = left;
/* Third curve - going down, right */
points[7] = left;
points[7].y += yOffset;
points[8] = bottom;
points[8].x -= xOffset;
/* bottom */
points[9] = bottom;
/* Fourth curve - Going right, up */
points[10] = bottom;
points[10].x += xOffset;
points[11] = right;
points[11].y += yOffset;
/* Back to starting point */
points[12] = right;
if (dc->dclevel.flPath & DCPATH_CLOCKWISE) reverse_points( points, 13 ); if (dc->dclevel.flPath & DCPATH_CLOCKWISE) reverse_points( points, 13 );
if (!(type = add_points( pPath, points, 13, PT_BEZIERTO ))) if (!(type = add_points( pPath, points, 13, PT_BEZIERTO )))

View file

@ -74,7 +74,7 @@ typedef struct _EPATHOBJ
#define PATH_IsPathOpen(dclevel) ( ((dclevel).hPath) && ((dclevel).flPath & DCPATH_ACTIVE) ) #define PATH_IsPathOpen(dclevel) ( ((dclevel).hPath) && ((dclevel).flPath & DCPATH_ACTIVE) )
BOOL FASTCALL PATH_Arc (PDC dc, INT x1, INT y1, INT x2, INT y2, INT xStart, INT yStart, INT xEnd, INT yEnd, INT direction, INT lines); BOOL FASTCALL PATH_Arc (PDC dc, INT x1, INT y1, INT x2, INT y2, INT xStart, INT yStart, INT xEnd, INT yEnd, INT direction, INT lines);
BOOL FASTCALL PATH_Ellipse (PDC dc, INT x1, INT y1, INT x2, INT y2); BOOL PATH_Ellipse (PDC dc, INT x1, INT y1, INT x2, INT y2);
VOID FASTCALL PATH_EmptyPath (PPATH pPath); VOID FASTCALL PATH_EmptyPath (PPATH pPath);
BOOL FASTCALL PATH_LineTo (PDC dc, INT x, INT y); BOOL FASTCALL PATH_LineTo (PDC dc, INT x, INT y);
BOOL FASTCALL PATH_PolyBezier (PDC dc, const POINT *pts, DWORD cbPoints); BOOL FASTCALL PATH_PolyBezier (PDC dc, const POINT *pts, DWORD cbPoints);
@ -84,7 +84,7 @@ BOOL FASTCALL PATH_PolylineTo (PDC dc, const POINT *pts, DWORD cbPoints);
BOOL FASTCALL PATH_PolyPolygon ( PDC dc, const POINT* pts, const INT* counts, UINT polygons); BOOL FASTCALL PATH_PolyPolygon ( PDC dc, const POINT* pts, const INT* counts, UINT polygons);
BOOL FASTCALL PATH_PolyPolyline( PDC dc, const POINT* pts, const DWORD* counts, DWORD polylines); BOOL FASTCALL PATH_PolyPolyline( PDC dc, const POINT* pts, const DWORD* counts, DWORD polylines);
BOOL FASTCALL PATH_Rectangle (PDC dc, INT x1, INT y1, INT x2, INT y2); BOOL FASTCALL PATH_Rectangle (PDC dc, INT x1, INT y1, INT x2, INT y2);
BOOL FASTCALL PATH_RoundRect(DC *dc, INT x1, INT y1, INT x2, INT y2, INT ell_width, INT ell_height); BOOL PATH_RoundRect(DC *dc, INT x1, INT y1, INT x2, INT y2, INT ell_width, INT ell_height);
BOOL FASTCALL PATH_PathToRegion (PPATH pPath, INT nPolyFillMode, PREGION Rgn); BOOL FASTCALL PATH_PathToRegion (PPATH pPath, INT nPolyFillMode, PREGION Rgn);
BOOL FASTCALL PATH_ExtTextOut(PDC dc,INT x,INT y,UINT flags,const RECTL *lprc,LPCWSTR str,UINT count,const INT *dx); BOOL FASTCALL PATH_ExtTextOut(PDC dc,INT x,INT y,UINT flags,const RECTL *lprc,LPCWSTR str,UINT count,const INT *dx);
@ -96,7 +96,6 @@ PPATH FASTCALL PATH_FlattenPath (PPATH pPath);
BOOL FASTCALL PATH_ReserveEntries (PPATH pPath, INT numEntries); BOOL FASTCALL PATH_ReserveEntries (PPATH pPath, INT numEntries);
BOOL FASTCALL PATH_StrokePath(DC *dc, PPATH pPath); BOOL FASTCALL PATH_StrokePath(DC *dc, PPATH pPath);
BOOL PATH_CheckCorners(DC *dc, POINT corners[], INT x1, INT y1, INT x2, INT y2);
VOID FASTCALL IntGdiCloseFigure(PPATH pPath); VOID FASTCALL IntGdiCloseFigure(PPATH pPath);
BOOL FASTCALL PATH_Delete(HPATH hPath); BOOL FASTCALL PATH_Delete(HPATH hPath);