【问题标题】:How to draw circles faster on windows form?如何在 Windows 窗体上更快地绘制圆圈?
【发布时间】:2019-09-28 23:07:45
【问题描述】:

我需要在给定的 X、Y 坐标处绘制小圆圈,但在窗口的面板上可能高达 6000 个圆圈。它非常慢,5000 圈大约需要 2 到 3 秒。 我怎样才能画得更快?

 private void drawBGA_Pins(BGAmap PinCordinates, double ExternalZoomFactor, double ExternalOffset_X, double ExternalOffset_Y)
        {
            Graphics g = this.imgBox.CreateGraphics();
            double zoomFactor = (Math.Min(Math.Abs((imgBox.Width) / PinCordinates.width), Math.Abs((imgBox.Height) / PinCordinates.height)))*92/100 * ExternalZoomFactor;
            //g.Clear(Color.Transparent); //you can choose another color for your background here.
            Pen pen = new Pen(Color.Yellow);

            foreach (var p in PinCordinates.pkgCordinates)
            {
                try
                {
                    g.DrawEllipse(pen, (float)(ExternalOffset_X + (p.X* zoomFactor)), (float)(ExternalOffset_Y + (p.Y* zoomFactor)), 10, 10);
                }
                catch
                {

                }
            }
        }

【问题讨论】:

  • 这应该不会超过几分之一秒。请在catch 中添加一些内容,以确保 isn't 被调用。异常很慢!不要在常规情况下抓住它们。 (而是测试避免!!) - 另外:您不应该使用control.CreateGraphics,因为这里已经解释了>10k 次!! - 此外,您正在泄漏 GDI 资源,如笔和 Graphics 对象(您不应该在第一个位置创建) - 仅在 Paint 事件中绘制或传递其 e.Graphics 对象! - 对于无闪烁绘图,必须对标记进行双重缓冲。默认情况下只有PictureBox
  • 不使用 control.CreateGraphics 的一个原因是持久性,但另一个原因是速度。在我的测试中,如果做得对,输出速度会提高约 100 倍!

标签: c# canvas drawellipse


【解决方案1】:

按照下面的优化顺序,直到您对性能感到满意。

  1. 在频繁更新 UI 后调用 Control.SuspendLayout 和 Control.ResumeLayout。

  2. 检查是否经常调用 Control.Refresh,请使用 Control.Invalidate 而不是 Control.Refresh

  3. 仅绘制可见区域 (Graphics.Clip) 内的圆圈,这样要绘制的圆圈就会少得多。

  4. 在 Windows API 级别暂停绘制。看到这个帖子: How do I suspend painting for a control and its children?

【讨论】:

【解决方案2】:

正如我在评论中指出的:您发布的代码有很多问题,其中之一也是速度不足的原因..

Winforms 图形规则 #1:永远不要使用control.CreateGraphics

也永远不要尝试缓存Graphics 对象!使用Graphics g = Graphics.FromImage(bmp) 或在控件的Paint 事件中使用e.Graphics 参数绘制到Bitmap bmp 中。

(唯一的例外是您实际上不想保留的图形,例如绘制橡皮筋矩形。 您可以通过最小化/最大化序列来测试图形的持久性..)

正确的方法是保留一个要绘制的东西的列表,并且每当该列表发生更改时Invalidate 您绘制的控件。所有绘图都应在Paint 事件中,使用e.Graphics 那里!

这里经常讨论这个问题;但这里更有趣的是,与错误的、非持久的方法相比,正确的方法会非常快

让我们看看:

创建图形对象的 50k 和 100k 圆缺少的时间是 5.4s(vs 0.18s)和 10.9s(vs 0.41s)。 (动画文件对他们来说太长了..)

所以e.Graphics 可以“绘制”圆圈快 30-100 倍。

怎么会? - 实际上,“正确”的方式只会准备双缓冲控件内部的表面,但只会将其推送到显示一次完成后并且有时间这样做,而错误的方式将每个传递直接圈。这种优化是由系统完成的以及限制输出区域..

PicturBox 默认情况下是双缓冲的;也可以进行其他控件,请参阅here

这里是测试平台:

int count = 5000;
List<PointF> pinCoordinates = new List<PointF>();
Random rnd = new Random(9);
float zoomFactor = 1.5f;

private void Button1_Click(object sender, EventArgs e)
{
    // init a list of points
    pinCoordinates.Clear();
    Size sz = pictureBox1.ClientSize;
    Cursor = Cursors.WaitCursor;
    for (int i = 0; i < count; i++)
    {
        pinCoordinates.Add(new PointF(rnd.Next(sz.Width), rnd.Next(sz.Height)));
    }

    // now draw in one way or the other:
    if (radioButton1.Checked)
    {
        Graphics g = pictureBox1.CreateGraphics();
        DateTime dt0 = DateTime.Now;
        foreach (var p in pinCordinates)  DoDraw(g, p);
        sayTime(dt0);
    }
    else
    {
        pictureBox1.Invalidate();
    }
    Cursor = Cursors.Default;
}

private void PictureBox1_Paint(object sender, PaintEventArgs e)
{
    DateTime dt0 = DateTime.Now;
    foreach (var p in pinCoordinates)  DoDraw(e.Graphics, p);
    sayTime(dt0);
}

void DoDraw(Graphics g, PointF p)
{
    using (Pen pen = new Pen(Color.FromArgb(rnd.Next(1234567890))))
        g.DrawEllipse(pen, p.X * zoomFactor, p.Y * zoomFactor, 10, 10);
}

void sayTime(DateTime dt)
{
    DateTime dt1 = DateTime.Now;
    label1.Text = (dt1 - dt).ToString("s\\.ffff");
}

最后说明:如果您使用Graphics g = Graphics.FromImage(bmp).. 绘制位图,您可以期待相同的速度。

【讨论】:

  • 太棒了,我能够通过以前的建议将时间缩短一半,但这打破了所有记录。 . .非常感谢
猜你喜欢
  • 1970-01-01
  • 2015-12-03
  • 1970-01-01
  • 1970-01-01
  • 2013-12-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多