圆角矩形不准确

时间:2011-05-19 14:49:31

标签: vb.net winforms rounded-corners

我使用GDI +绘制圆角矩形的每个示例代码都是这样的(从BobPowell.net解除并略微修改):

  Private Sub Panel1_Paint(ByVal sender As Object, ByVal e As PaintEventArgs) Handles Panel1.Paint
    e.Graphics.Clear(SystemColors.Window)
    e.Graphics.SmoothingMode = SmoothingMode.None

    Call DrawRoundRect(e.Graphics, Pens.Red, 10, 10, 48, 24, 6)
  End Sub

  Public Sub DrawRoundRect(ByVal g As Graphics, ByVal p As Pen, ByVal x As Single, ByVal y As Single, ByVal width As Single, ByVal height As Single, ByVal radius As Single)
    Using gp As New GraphicsPath()
      gp.StartFigure()
      gp.AddArc(x + width - radius, y, radius * 2, radius * 2, 270, 90)
      gp.AddArc(x + width - radius, y + height - radius, radius * 2, radius * 2, 0, 90)
      gp.AddArc(x, y + height - radius, radius * 2, radius * 2, 90, 90)
      gp.AddArc(x, y, radius * 2, radius * 2, 180, 90)
      gp.CloseFigure()
      g.DrawPath(p, gp)
    End Using
  End Sub

这会产生一个圆角矩形,只有左上角是准确的。

AntiAliasing必须关闭,因为它正在通过远程桌面连接,我不能依赖它可用。此外,我正在寻找一个清晰的圆角矩形。

enter image description here

我已经尝试调整其他角落的大小并更改笔对齐,但似乎没有任何东西可以生成简单,精确的圆角矩形。

有没有办法在旧的winforms中绘制比这更好的圆角矩形?

4 个答案:

答案 0 :(得分:3)

1)将源图像的大小调整为其原始大小的二进制倍数。通常情况下,我会重新采样到比原始宽度和高度高4倍(或8或16)的宽度和高度。

2)执行我所有的GDI +绘图操作(当然,考虑到我的坐标需要乘以4倍)。没有必要使用任何花哨的抗锯齿。

3)将图像重新采样回原始尺寸。缩小图像会产生很好的平滑效果,并最大限度地减少线条,曲线等中的任何舍入误差。

private Bitmap GenerateButton(int overSampling) {

    int overSampling = 8;
    int width=(48 + 10 + 10 + 6) * overSampling;
    int height=(24 + 10 + 10 + 6) * overSampling;

    // Draw the button with the rounded corners, but do
    // so at 8 times the normal size.
    Bitmap bitmap=new Bitmap(width,height);
    using (Graphics g = Graphics.FromImage(bitmap)) {
        g.Clear(Color.White);
        g.SmoothingMode = SmoothingMode.None;
        DrawRoundRect(overSampling, g, new Pen(Color.Red, overSampling), 10, 10, 48, 24, 6);
    }

    // Shrink the image down to its intended size
    Bitmap shrunkVersion=new Bitmap(bitmap.Width / overSampling, bitmap.Height / overSampling);
    using (Graphics g = Graphics.FromImage(shrunkVersion)) {
        // Use hi-quality resampling for a nice, smooth image.
        g.InterpolationMode = InterpolationMode.HighQualityBicubic;
        g.DrawImage(bitmap, 0, 0, shrunkVersion.Width, shrunkVersion.Height);
    }

    return shrunkVersion;
}

private void DrawRoundRect(int overSampling, Graphics g, Pen p, float x, float y, float width, float height, float radius)
{
    using (GraphicsPath gp = new GraphicsPath())
    {
        gp.StartFigure();
        gp.AddArc((x + width - radius) * overSampling, y * overSampling, (radius * 2) * overSampling, (radius * 2) * overSampling, 270, 90);
        gp.AddArc((x + width - radius) * overSampling, (y + height - radius) * overSampling, (radius * 2) * overSampling, (radius * 2) * overSampling, 0, 90);
        gp.AddArc(x * overSampling, (y + height - radius) * overSampling, radius * 2 * overSampling, radius * 2 * overSampling, 90, 90);
        gp.AddArc(x * overSampling, y * overSampling, radius * 2 * overSampling, radius * 2 * overSampling, 180, 90);
        gp.CloseFigure();
        g.DrawPath(p, gp);
    }
}

没有过采样:

Without Smoothing

进行8次过采样:

With Smoothing

答案 1 :(得分:2)

我找到了最好的解决方案,只是老派的Windows API:

Private Sub DrawRoundRect(ByVal g As Graphics, ByVal r As Rectangle)
  Dim hDC As IntPtr = g.GetHdc
  Dim hPen As IntPtr = CreatePen(PS_SOLID, 0, ColorTranslator.ToWin32(Color.Red))
  Dim hOldPen As IntPtr = SelectObject(hDC, hPen)
  SelectObject(hDC, GetStockObject(NULL_BRUSH))
  RoundRect(hDC, r.Left, r.Top, r.Right - 1, r.Bottom - 1, 12, 12)
  SelectObject(hDC, hOldPen)
  DeleteObject(hPen)
  g.ReleaseHdc(hDC)
End Sub

这会生成我一直在寻找的对称圆角矩形:

enter image description here

答案 2 :(得分:1)

因为在这里没有人回答你这是我过去使用的一个技巧。它运行得相当好,并且看起来比使用AddArc()的经典实现更好。

它使用圆和剪裁来达到你想要的效果。当使用宽度大于1px的笔时,它可能会显示轻微的伪影,但除此之外它还能正常工作。

我希望它对你的项目来说足够好。

    private void DrawRoundedRectangle(Graphics g, Pen pen, Rectangle rect, int radius)
    {
        g.DrawLine(pen, rect.Left + radius, rect.Top, rect.Right - radius, rect.Top);
        g.DrawLine(pen, rect.Right, rect.Top+radius, rect.Right, rect.Bottom - radius);
        g.DrawLine(pen, rect.Left + radius, rect.Bottom, rect.Right - radius, rect.Bottom);
        g.DrawLine(pen, rect.Left, rect.Top + radius, rect.Left, rect.Bottom - radius);

        g.SetClip(new Rectangle(rect.Left, rect.Top, radius, radius));
        g.DrawEllipse(pen, rect.Left, rect.Top, radius * 2, radius * 2);
        g.ResetClip();

        g.SetClip(new Rectangle(rect.Right-radius, rect.Top, radius+1, radius+1));
        g.DrawEllipse(pen, rect.Right - radius * 2, rect.Top, radius * 2, radius * 2);
        g.ResetClip();

        g.SetClip(new Rectangle(rect.Right - radius, rect.Bottom-radius, radius+1, radius+1));
        g.DrawEllipse(pen, rect.Right - radius * 2, rect.Bottom - (radius * 2), radius * 2, radius * 2);
        g.ResetClip();

        g.SetClip(new Rectangle(rect.Left, rect.Bottom - radius, radius+1, radius+1));
        g.DrawEllipse(pen, rect.Left, rect.Bottom - (radius * 2), radius * 2, radius * 2);
        g.ResetClip();
    }

该方法的界面很简单,但如果您需要帮助,请发表评论。

编辑:其他应该工作的是将相同的弧绘制四次,但是使用TranslateTransform和TranslateScale进行翻转。那个 意味着弧在每个角落看起来都是相同的。

    private void DrawRoundedRectangle(Graphics g, Pen pen, Rectangle rect, int radius)
    {
        g.DrawLine(pen, rect.Left + radius, rect.Top, rect.Right - radius, rect.Top);
        g.DrawLine(pen, rect.Right-1, rect.Top+radius, rect.Right-1, rect.Bottom - radius);
        g.DrawLine(pen, rect.Left + radius, rect.Bottom-1, rect.Right - radius, rect.Bottom-1);
        g.DrawLine(pen, rect.Left, rect.Top + radius, rect.Left, rect.Bottom - radius);

        g.TranslateTransform(rect.Left, rect.Top);
        g.DrawArc(pen, 0, 0, radius * 2, radius * 2, 180, 90);
        g.ResetTransform();

        g.TranslateTransform(rect.Right, rect.Top);
        g.ScaleTransform(-1, 1);
        g.DrawArc(pen, 1, 0, radius * 2, radius * 2, 180, 90);
        g.ResetTransform();

        g.TranslateTransform(rect.Right, rect.Bottom);
        g.ScaleTransform(-1, -1);
        g.DrawArc(pen, 1, 1, radius * 2, radius * 2, 180, 90);
        g.ResetTransform();

        g.TranslateTransform(rect.Left, rect.Bottom);
        g.ScaleTransform(1, -1);
        g.DrawArc(pen, 0, 1, radius * 2, radius * 2, 180, 90);
        g.ResetTransform();
    }

这类似于绘制圆形的旧计算机图形方法,您可以在其中绘制四分之一圆,以避免舍入错误,例如GDI中的错误。

另一种方法是将第一个弧线绘制到图像上,然后将图像绘制四次,根据需要进行翻转。下面是第二种方法的变体,使用图像绘制弧线。

    private void DrawRoundedRectangle(Graphics g, Pen pen, Rectangle rect, int radius)
    {
        g.DrawLine(pen, rect.Left + radius, rect.Top, rect.Right - radius, rect.Top);
        g.DrawLine(pen, rect.Right - 1, rect.Top + radius, rect.Right - 1, rect.Bottom - radius);
        g.DrawLine(pen, rect.Left + radius, rect.Bottom - 1, rect.Right - radius, rect.Bottom - 1);
        g.DrawLine(pen, rect.Left, rect.Top + radius, rect.Left, rect.Bottom - radius);

        Bitmap arc = new Bitmap(radius, radius, g);
        Graphics.FromImage(arc).DrawArc(pen, 0, 0, radius * 2, radius * 2, 180, 90);

        g.TranslateTransform(rect.Left, rect.Top);
        g.DrawImage(arc, 0, 0);
        g.ResetTransform();

        g.TranslateTransform(rect.Right, rect.Top);
        g.ScaleTransform(-1, 1);
        g.DrawImage(arc, 0, 0);
        g.ResetTransform();

        g.TranslateTransform(rect.Right, rect.Bottom);
        g.ScaleTransform(-1, -1);
        g.DrawImage(arc, 0, 0);
        g.ResetTransform();

        g.TranslateTransform(rect.Left, rect.Bottom);
        g.ScaleTransform(1, -1);
        g.DrawImage(arc, 0, 0);
        g.ResetTransform();

        arc.Dispose();
    }

答案 3 :(得分:1)

有时,我使用“低技术”的方法来处理GDI +中的舍入错误

1)将源图像的大小调整为其原始大小的二进制倍数。通常情况下,我会重新采样到比原始宽度和高度高4倍(或8或16)的宽度和高度。

2)执行我所有的GDI +绘图操作(当然,考虑到我的坐标需要乘以4倍)。没有必要使用任何花哨的抗锯齿。

3)将图像重新采样回原始尺寸。缩小图像会产生很好的平滑效果,并最大限度地减少线条,曲线等中的任何舍入误差。