3

我编写了一些GraphicsPath基于自定义结构创建圆角矩形的代码BorderRadius(它允许我定义矩形的左上角、右上角、左下角和右下角半径)和初始值Rectangle本身:

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(右侧),以便您可以清楚地看到问题:

矩形

我想知道的是:为什么绘制矩形上的所有弧都相等,而填充矩形上的所有弧都是奇数?

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

编辑:是否可以在不使用 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;
    }
}
4

4 回答 4

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));
于 2014-02-17T13:25:13.747 回答
1

我建议从每个弧的末尾到下一个弧的开头显式添加一条线。

您也可以尝试使用 Flatten 方法用线条近似路径中的所有曲线。这应该消除任何歧义。

您从 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 以不同的方式解释这些点。一些端点被视为控制点,反之亦然。

于 2013-10-04T04:44:56.883 回答
1

该行为的真正原因在FillRectangle 和 DrawRectangle 的像素行为中进行了解释。

它与默认像素舍入以及具有整数坐标的 FillRectangle/FillPath 最终在像素中间绘制并被舍入(根据 Graphics.PixelOffsetMode)有关。

另一方面,DrawRectangle/DrawPath 使用 1px 的笔绘制,在像素边界上得到完美的圆角。

根据您的使用情况,解决方案可能是将 FillRectangle/FillPath 的矩形膨胀/缩小 0.5 像素。

于 2017-12-06T13:06:04.003 回答
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();
于 2013-10-04T12:41:08.883 回答