2013-10-03 143 views
2

我已经写了一些代码,创建了一个圆角矩形GraphicsPath,基于自定义结构,BorderRadius(这让我定义左上角,右上角,左下角和右下角的半径矩形),初始Rectangle本身:奇怪绘制GraphicsPath中与Graphics.FillPath

public static GraphicsPath CreateRoundRectanglePath(BorderRadius radius, Rectangle rectangle) 
{ 
    GraphicsPath result = new GraphicsPath(); 

    if (radius.TopLeft > 0) 
    { 
     result.AddArc(rectangle.X, rectangle.Y, radius.TopLeft, radius.TopLeft, 180, 90); 
    } 
    else 
    { 
     result.AddLine(new System.Drawing.Point(rectangle.X, rectangle.Y), new System.Drawing.Point(rectangle.X, rectangle.Y)); 
    } 
    if (radius.TopRight > 0) 
    { 
     result.AddArc(rectangle.X + rectangle.Width - radius.TopRight, rectangle.Y, radius.TopRight, radius.TopRight, 270, 90); 
    } 
    else 
    { 
     result.AddLine(new System.Drawing.Point(rectangle.X + rectangle.Width, rectangle.Y), new System.Drawing.Point(rectangle.X + rectangle.Width, rectangle.Y)); 
    } 
    if (radius.BottomRight > 0) 
    { 
     result.AddArc(rectangle.X + rectangle.Width - radius.BottomRight, rectangle.Y + rectangle.Height - radius.BottomRight, radius.BottomRight, radius.BottomRight, 0, 90); 
    } 
    else 
    { 
     result.AddLine(new System.Drawing.Point(rectangle.X + rectangle.Width, rectangle.Y + rectangle.Height), new System.Drawing.Point(rectangle.X + rectangle.Width, rectangle.Y + rectangle.Height)); 
    } 
    if (radius.BottomLeft > 0) 
    { 
     result.AddArc(rectangle.X, rectangle.Y + rectangle.Height - radius.BottomLeft, radius.BottomLeft, radius.BottomLeft, 90, 90); 
    } 
    else 
    { 
     result.AddLine(new System.Drawing.Point(rectangle.X, rectangle.Y + rectangle.Height), new System.Drawing.Point(rectangle.X, rectangle.Y + rectangle.Height)); 
    } 

    return result; 
} 

现在,如果我用这个与FillPath和DrawPath以来,我注意到一些奇怪的结果:

GraphicsPath path = CreateRoundRectanglePath(new BorderRadius(8), new Rectangle(10, 10, 100, 100)); 
e.Graphics.DrawPath(new Pen(Color.Black, 1), path); 
e.Graphics.FillPath(new SolidBrush(Color.Black), path); 

我已经放大到每个结果Rectangle(右手边),这样你可以清楚地看到,问题:

rectangles

我想知道的是:为什么都上绘制的矩形弧的平等,以及所有弧上填满的矩形,奇数?

更好的是,它可以固定,以便填充的矩形绘制正确吗?

编辑:是否有可能在不使用FillPath的情况下填充GraphicsPath的内部?

编辑:按照意见....这里是BorderRadius结构

public struct BorderRadius 
{ 
    public Int32 TopLeft { get; set; } 
    public Int32 TopRight { get; set; } 
    public Int32 BottomLeft { get; set; } 
    public Int32 BottomRight { get; set; } 

    public BorderRadius(int all) : this() 
    { 
     this.TopLeft = this.TopRight = this.BottomLeft = this.BottomRight = all; 
    } 
} 
+0

你可以发布你的BorderRadius(),这样我们可以玩这个例子吗? –

+0

@Idle_Mind,查看更新 – series0ne

回答

3

我遇到了同样的问题并找到了解决方案。可能对你来说太迟了@seriesOne,但如果他们有这个问题,对其他人可能会有用。 基本上在使用填充方法时(以及在使用Graphics.SetClip将圆角矩形设置为剪切路径时),我们必须将右边和底边线移动一个像素。所以我想出了一个方法,它接受一个参数来修复这个矩形是否使用了填充。那就是:

private static GraphicsPath CreateRoundedRectangle(Rectangle b, int r, bool fill = false) 
{ 
    var path = new GraphicsPath(); 
    var r2 = (int)r/2; 
    var fix = fill ? 1 : 0; 

    b.Location = new Point(b.X - 1, b.Y - 1); 
    if (!fill) 
     b.Size = new Size(b.Width - 1, b.Height - 1); 

    path.AddArc(b.Left, b.Top, r, r, 180, 90); 
    path.AddLine(b.Left + r2, b.Top, b.Right - r2 - fix, b.Top); 

    path.AddArc(b.Right - r - fix, b.Top, r, r, 270, 90); 
    path.AddLine(b.Right, b.Top + r2, b.Right, b.Bottom - r2); 

    path.AddArc(b.Right - r - fix, b.Bottom - r - fix, r, r, 0, 90); 
    path.AddLine(b.Right - r2, b.Bottom, b.Left + r2, b.Bottom); 

    path.AddArc(b.Left, b.Bottom - r - fix, r, r, 90, 90); 
    path.AddLine(b.Left, b.Bottom - r2, b.Left, b.Top + r2); 

    return path; 
} 

因此,这是你如何使用它:

g.DrawPath(new Pen(Color.Red), CreateRoundedRectangle(rect, 24, false)); 
g.FillPath(new SolidBrush(Color.Red), CreateRoundedRectangle(rect, 24, true)); 
+0

即使我有把我的手转向WPF,但仍然有兴趣看到这在Windows窗体中工作:-) – series0ne

1

我建议明确将来自每个圆弧到下一个的开始的结束线的一个实例。

您也可以尝试使用平展方法来用线逼近路径中的所有曲线。这应该消除任何含糊之处。

从FillPath获得的结果看起来类似于我在路径上的点被错误解释的问题,实质上导致二次方而不是三次贝塞尔样条曲线。

您可以使用GetPathData功能的路径上检查点:http://msdn.microsoft.com/en-us/library/ms535534%28v=vs.85%29.aspx

贝塞尔曲线(这GDI +用来近似圆弧)由4个点表示。第一个是终点,可以是任何类型。第二个和第三个是控制点,并具有PathPointBezier类型。最后一个是另一个终点,并且具有PathPointBezier类型。换句话说,当GDI +看到PathPointBezier时,它使用路径的当前位置和3个Bezier点绘制曲线。贝塞尔曲线可以串在一起,但贝塞尔点的数量应该总是可以被3整除。

你在做什么有点奇怪,因为你在不同的地方绘制曲线而没有明确的线条来加入它们。我猜想它会产生这样的模式:

PathPointStart - end point of first arc 
PathPointBezier - control point of first arc 
PathPointBezier - control point of first arc 
PathPointBezier - end point of first arc 
PathPointLine - end point of second arc 
PathPointBezier - control point of second arc 
PathPointBezier - control point of second arc 
PathPointBezier - end point of second arc 
PathPointLine - end point of third arc 
PathPointBezier - control point of third arc 
PathPointBezier - control point of third arc 
PathPointBezier - end point of third arc 

这看起来很合理。 GDI +应该绘制从每条曲线的最后一个端点到下一个端点的第一个端点。 DrawPath很清楚这一点,但我认为FillPath是以不同的方式解释这些点。有些终点被视为控制点,反之亦然。

+0

感谢您的提问,这是一个非常透彻的解释!你认为我需要重写我的整个CreateRoundRectanglePath方法,或者只是添加行吗? – series0ne

+0

我认为如果你添加线条,它有合理的工作机会,但只是一个有教养的猜测,点类型与你的问题有关。我不知道如果添加线条会发生什么情况,FillPath是如何处理这些点的,或者路径的实际外观如何。 –

+0

我尝试添加线条,并使用bezier曲线代替弧线,并尝试了Idle_Mind的答案......没有任何东西可以工作! :-( – series0ne

0

“是否可以在不使用FillPath的情况下填充GraphicsPath的内部?”

是...但我认为这更像是一个会客的伎俩(它可能不会像你期望的那样更复杂的形状)。您可以将图形剪辑到路径,然后只填充整个矩形:

 Rectangle rc = new Rectangle(10, 10, 100, 100); 
     GraphicsPath path = CreateRoundRectanglePath(new BorderRadius(8), rc); 
     e.Graphics.SetClip(path); 
     e.Graphics.FillRectangle(Brushes.Black, rc); 
     e.Graphics.ResetClip(); 
+0

非常好,谢谢你的回答...我会试试看! – series0ne

+0

不幸的是,这与FillPath有相同的效果:矩形奇怪,如图 – gigi

0

的真正原因行为在Pixel behaviour of FillRectangle and DrawRectangle解释。

它与默认的像素四舍五入以及具有整数坐标的FillRectangle/FillPath最终在一个像素的中间绘制并变为四舍五入(根据Graphics.PixelOffsetMode)的事实。

另一方面,DrawRectangle/DrawPath绘制一个1像素的笔,在像素边界上完美舍入。

根据您的使用情况,解决方案可以通过.5px对FillRectangle/FillPath的矩形进行充气/放气。