📄 path.c
字号:
*
* Should be called when a LineTo is performed on a DC that has an
* open path. This adds a PT_LINETO entry to the path (and possibly
* a PT_MOVETO entry, if this is the first LineTo in a stroke).
* Returns TRUE if successful, else FALSE.
*/
BOOL
FASTCALL
PATH_LineTo ( PDC dc, INT x, INT y )
{
POINT point, pointCurPos;
/* Check that path is open */
if ( dc->w.path.state != PATH_Open )
return FALSE;
/* Convert point to device coordinates */
point.x=x;
point.y=y;
CoordLPtoDP ( dc, &point );
/* Add a PT_MOVETO if necessary */
if ( dc->w.path.newStroke )
{
dc->w.path.newStroke = FALSE;
IntGetCurrentPositionEx ( dc, &pointCurPos );
CoordLPtoDP ( dc, &pointCurPos );
if ( !PATH_AddEntry(&dc->w.path, &pointCurPos, PT_MOVETO) )
return FALSE;
}
/* Add a PT_LINETO entry */
return PATH_AddEntry(&dc->w.path, &point, PT_LINETO);
}
/* PATH_Rectangle
*
* Should be called when a call to Rectangle is performed on a DC that has
* an open path. Returns TRUE if successful, else FALSE.
*/
BOOL
FASTCALL
PATH_Rectangle ( PDC dc, INT x1, INT y1, INT x2, INT y2 )
{
POINT corners[2], pointTemp;
INT temp;
/* Check that path is open */
if ( dc->w.path.state != PATH_Open )
return FALSE;
/* Convert points to device coordinates */
corners[0].x=x1;
corners[0].y=y1;
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 */
if ( corners[0].x > corners[1].x )
{
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 */
if ( IntGetGraphicsMode(dc) == GM_COMPATIBLE )
{
corners[1].x--;
corners[1].y--;
}
/* Close any previous figure */
IntGdiCloseFigure(dc);
/* Add four points to the path */
pointTemp.x=corners[1].x;
pointTemp.y=corners[0].y;
if ( !PATH_AddEntry(&dc->w.path, &pointTemp, PT_MOVETO) )
return FALSE;
if ( !PATH_AddEntry(&dc->w.path, corners, PT_LINETO) )
return FALSE;
pointTemp.x=corners[0].x;
pointTemp.y=corners[1].y;
if ( !PATH_AddEntry(&dc->w.path, &pointTemp, PT_LINETO) )
return FALSE;
if ( !PATH_AddEntry(&dc->w.path, corners+1, PT_LINETO) )
return FALSE;
/* Close the rectangle figure */
IntGdiCloseFigure(dc) ;
return TRUE;
}
/* PATH_RoundRect
*
* Should be called when a call to RoundRect is performed on a DC that has
* an open path. Returns TRUE if successful, else FALSE.
*
* FIXME: it adds the same entries to the path as windows does, but there
* is an error in the bezier drawing code so that there are small pixel-size
* gaps when the resulting path is drawn by StrokePath()
*/
FASTCALL BOOL PATH_RoundRect(DC *dc, INT x1, INT y1, INT x2, INT y2, INT ell_width, INT ell_height)
{
GdiPath *pPath = &dc->w.path;
POINT corners[2], pointTemp;
FLOAT_POINT ellCorners[2];
/* Check that path is open */
if(pPath->state!=PATH_Open)
return FALSE;
if(!PATH_CheckCorners(dc,corners,x1,y1,x2,y2))
return FALSE;
/* Add points to the roundrect path */
ellCorners[0].x = corners[1].x-ell_width;
ellCorners[0].y = corners[0].y;
ellCorners[1].x = corners[1].x;
ellCorners[1].y = corners[0].y+ell_height;
if(!PATH_DoArcPart(pPath, ellCorners, 0, -M_PI_2, TRUE))
return FALSE;
pointTemp.x = corners[0].x+ell_width/2;
pointTemp.y = corners[0].y;
if(!PATH_AddEntry(pPath, &pointTemp, PT_LINETO))
return FALSE;
ellCorners[0].x = corners[0].x;
ellCorners[1].x = corners[0].x+ell_width;
if(!PATH_DoArcPart(pPath, ellCorners, -M_PI_2, -M_PI, FALSE))
return FALSE;
pointTemp.x = corners[0].x;
pointTemp.y = corners[1].y-ell_height/2;
if(!PATH_AddEntry(pPath, &pointTemp, PT_LINETO))
return FALSE;
ellCorners[0].y = corners[1].y-ell_height;
ellCorners[1].y = corners[1].y;
if(!PATH_DoArcPart(pPath, ellCorners, M_PI, M_PI_2, FALSE))
return FALSE;
pointTemp.x = corners[1].x-ell_width/2;
pointTemp.y = corners[1].y;
if(!PATH_AddEntry(pPath, &pointTemp, PT_LINETO))
return FALSE;
ellCorners[0].x = corners[1].x-ell_width;
ellCorners[1].x = corners[1].x;
if(!PATH_DoArcPart(pPath, ellCorners, M_PI_2, 0, FALSE))
return FALSE;
IntGdiCloseFigure(dc);
return TRUE;
}
/* PATH_Ellipse
*
* Should be called when a call to Ellipse is performed on a DC that has
* an open path. This adds four Bezier splines representing the ellipse
* to the path. Returns TRUE if successful, else FALSE.
*/
BOOL
FASTCALL
PATH_Ellipse ( PDC dc, INT x1, INT y1, INT x2, INT y2 )
{
/* TODO: This should probably be revised to call PATH_AngleArc */
/* (once it exists) */
BOOL Ret = PATH_Arc ( dc, x1, y1, x2, y2, x1, (y1+y2)/2, x1, (y1+y2)/2, GdiTypeArc );
if (Ret) IntGdiCloseFigure(dc);
return Ret;
}
/* PATH_Arc
*
* Should be called when a call to Arc is performed on a DC that has
* an open path. This adds up to five Bezier splines representing the arc
* to the path. When 'lines' is 1, we add 1 extra line to get a chord,
* and when 'lines' is 2, we add 2 extra lines to get a pie.
* Returns TRUE if successful, else FALSE.
*/
BOOL
FASTCALL
PATH_Arc ( PDC dc, INT x1, INT y1, INT x2, INT y2,
INT xStart, INT yStart, INT xEnd, INT yEnd, INT lines)
{
double angleStart, angleEnd, angleStartQuadrant, angleEndQuadrant=0.0;
/* Initialize angleEndQuadrant to silence gcc's warning */
double x, y;
FLOAT_POINT corners[2], pointStart, pointEnd;
POINT centre;
BOOL start, end;
INT temp;
BOOL clockwise;
/* FIXME: This function should check for all possible error returns */
/* FIXME: Do we have to respect newStroke? */
ASSERT ( dc );
clockwise = ( dc->w.ArcDirection == AD_CLOCKWISE );
/* Check that path is open */
if ( dc->w.path.state != PATH_Open )
return FALSE;
/* FIXME: Do we have to close the current figure? */
/* Check for zero height / width */
/* FIXME: Only in GM_COMPATIBLE? */
if ( x1==x2 || y1==y2 )
return TRUE;
/* Convert points to device coordinates */
corners[0].x=(FLOAT)x1;
corners[0].y=(FLOAT)y1;
corners[1].x=(FLOAT)x2;
corners[1].y=(FLOAT)y2;
pointStart.x=(FLOAT)xStart;
pointStart.y=(FLOAT)yStart;
pointEnd.x=(FLOAT)xEnd;
pointEnd.y=(FLOAT)yEnd;
INTERNAL_LPTODP_FLOAT(dc, corners);
INTERNAL_LPTODP_FLOAT(dc, corners+1);
INTERNAL_LPTODP_FLOAT(dc, &pointStart);
INTERNAL_LPTODP_FLOAT(dc, &pointEnd);
/* Make sure first corner is top left and second corner is bottom right */
if ( corners[0].x > corners[1].x )
{
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;
}
/* Compute start and end angle */
PATH_NormalizePoint(corners, &pointStart, &x, &y);
angleStart=atan2(y, x);
PATH_NormalizePoint(corners, &pointEnd, &x, &y);
angleEnd=atan2(y, x);
/* Make sure the end angle is "on the right side" of the start angle */
if ( clockwise )
{
if ( angleEnd <= angleStart )
{
angleEnd+=2*M_PI;
ASSERT(angleEnd>=angleStart);
}
}
else
{
if(angleEnd>=angleStart)
{
angleEnd-=2*M_PI;
ASSERT(angleEnd<=angleStart);
}
}
/* In GM_COMPATIBLE, don't include bottom and right edges */
if ( IntGetGraphicsMode(dc) == GM_COMPATIBLE )
{
corners[1].x--;
corners[1].y--;
}
/* Add the arc to the path with one Bezier spline per quadrant that the
* arc spans */
start=TRUE;
end=FALSE;
do
{
/* Determine the start and end angles for this quadrant */
if(start)
{
angleStartQuadrant=angleStart;
if ( clockwise )
angleEndQuadrant=(floor(angleStart/M_PI_2)+1.0)*M_PI_2;
else
angleEndQuadrant=(ceil(angleStart/M_PI_2)-1.0)*M_PI_2;
}
else
{
angleStartQuadrant=angleEndQuadrant;
if ( clockwise )
angleEndQuadrant+=M_PI_2;
else
angleEndQuadrant-=M_PI_2;
}
/* Have we reached the last part of the arc? */
if ( (clockwise && angleEnd<angleEndQuadrant)
|| (!clockwise && angleEnd>angleEndQuadrant)
)
{
/* Adjust the end angle for this quadrant */
angleEndQuadrant = angleEnd;
end = TRUE;
}
/* Add the Bezier spline to the path */
PATH_DoArcPart ( &dc->w.path, corners, angleStartQuadrant, angleEndQuadrant, start );
start = FALSE;
} while(!end);
/* chord: close figure. pie: add line and close figure */
if(lines==GdiTypeChord) // 1
{
IntGdiCloseFigure(dc);
}
else if(lines==GdiTypePie) // 2
{
centre.x = (corners[0].x+corners[1].x)/2;
centre.y = (corners[0].y+corners[1].y)/2;
if(!PATH_AddEntry(&dc->w.path, ¢re, PT_LINETO | PT_CLOSEFIGURE))
return FALSE;
}
return TRUE;
}
BOOL
FASTCALL
PATH_PolyBezierTo ( PDC dc, const POINT *pts, DWORD cbPoints )
{
POINT pt;
ULONG i;
ASSERT ( dc );
ASSERT ( pts );
ASSERT ( cbPoints );
/* Check that path is open */
if ( dc->w.path.state != PATH_Open )
return FALSE;
/* Add a PT_MOVETO if necessary */
if ( dc->w.path.newStroke )
{
dc->w.path.newStroke=FALSE;
IntGetCurrentPositionEx ( dc, &pt );
CoordLPtoDP ( dc, &pt );
if ( !PATH_AddEntry(&dc->w.path, &pt, PT_MOVETO) )
return FALSE;
}
for(i = 0; i < cbPoints; i++)
{
pt = pts[i];
CoordLPtoDP ( dc, &pt );
PATH_AddEntry(&dc->w.path, &pt, PT_BEZIERTO);
}
return TRUE;
}
BOOL
FASTCALL
PATH_PolyBezier ( PDC dc, const POINT *pts, DWORD cbPoints )
{
POINT pt;
ULONG i;
ASSERT ( dc );
ASSERT ( pts );
ASSERT ( cbPoints );
/* Check that path is open */
if ( dc->w.path.state != PATH_Open )
return FALSE;
for ( i = 0; i < cbPoints; i++ )
{
pt = pts[i];
CoordLPtoDP ( dc, &pt );
PATH_AddEntry ( &dc->w.path, &pt, (i == 0) ? PT_MOVETO : PT_BEZIERTO );
}
return TRUE;
}
BOOL
FASTCALL
PATH_Polyline ( PDC dc, const POINT *pts, DWORD cbPoints )
{
POINT pt;
ULONG i;
ASSERT ( dc );
ASSERT ( pts );
ASSERT ( cbPoints );
/* Check that path is open */
if ( dc->w.path.state != PATH_Open )
return FALSE;
for ( i = 0; i < cbPoints; i++ )
{
pt = pts[i];
CoordLPtoDP ( dc, &pt );
PATH_AddEntry(&dc->w.path, &pt, (i == 0) ? PT_MOVETO : PT_LINETO);
}
return TRUE;
}
BOOL
FASTCALL
PATH_PolylineTo ( PDC dc, const POINT *pts, DWORD cbPoints )
{
POINT pt;
ULONG i;
ASSERT ( dc );
ASSERT ( pts );
ASSERT ( cbPoints );
/* Check that path is open */
if ( dc->w.path.state != PATH_Open )
return FALSE;
/* Add a PT_MOVETO if necessary */
if ( dc->w.path.newStroke )
{
dc->w.path.newStroke = FALSE;
IntGetCurrentPositionEx ( dc, &pt );
CoordLPtoDP ( dc, &pt );
if ( !PATH_AddEntry(&dc->w.path, &pt, PT_MOVETO) )
return FALSE;
}
for(i = 0; i < cbPoints; i++)
{
pt = pts[i];
CoordLPtoDP ( dc, &pt );
PATH_AddEntry(&dc->w.path, &pt, PT_LINETO);
}
return TRUE;
}
BOOL
FASTCALL
PATH_Polygon ( PDC dc, const POINT *pts, DWORD cbPoints )
{
POINT pt;
ULONG i;
ASSERT ( dc );
ASSERT ( pts );
/* Check that path is open */
if ( dc->w.path.state != PATH_Open )
return FALSE;
for(i = 0; i < cbPoints; i++)
{
pt = pts[i];
CoordLPtoDP ( dc, &pt );
PATH_AddEntry(&dc->w.path, &pt, (i == 0) ? PT_MOVETO :
((i == cbPoints-1) ? PT_LINETO | PT_CLOSEFIGURE :
PT_LINETO));
}
return TRUE;
}
BOOL
FASTCALL
PATH_PolyPolygon ( PDC dc, const POINT* pts, const INT* counts, UINT polygons )
{
POINT pt, startpt;
ULONG poly, point, i;
ASSERT ( dc );
ASSERT ( pts );
ASSERT ( counts );
ASSERT ( polygons );
/* Check that path is open */
if ( dc->w.path.state != PATH_Open );
return FALSE;
for(i = 0, poly = 0; poly < polygons; poly++)
{
for(point = 0; point < (ULONG) counts[poly]; point++, i++)
{
pt = pts[i];
CoordLPtoDP ( dc, &pt );
if(point == 0) startpt = pt;
PATH_AddEntry(&dc->w.path, &pt, (point == 0) ? PT_MOVETO : PT_LINETO);
}
/* win98 adds an extra line to close the figure for some reason */
PATH_AddEntry(&dc->w.path, &startpt, PT_LINETO | PT_CLOSEFIGURE);
}
return TRUE;
}
BOOL
FASTCALL
PATH_PolyPolyline ( PDC dc, const POINT* pts, const DWORD* counts, DWORD polylines )
{
POINT pt;
ULONG poly, point, i;
ASSERT ( dc );
ASSERT ( pts );
ASSERT ( counts );
ASSERT ( polylines );
/* Check that path is open */
if ( dc->w.path.state != PATH_Open )
return FALSE;
for(i = 0, poly = 0; poly < polylines; poly++)
{
for(point = 0; point < counts[poly]; point++, i++)
{
pt = pts[i];
CoordLPtoDP ( dc, &pt );
PATH_AddEntry(&dc->w.path, &pt, (point == 0) ? PT_MOVETO : PT_LINETO);
}
}
return TRUE;
}
/***********************************************************************
* Internal functions
*/
/* PATH_CheckCorners
*
* Helper function for PATH_RoundRect() and PATH_Rectangle()
*/
BOOL PATH_CheckCorners(DC *dc, POINT corners[], INT x1, INT y1, INT x2, INT y2)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -