我需要在给定的X,Y坐标处绘制小圆圈,但在窗口的面板上可能会高达6000个圆圈。它的速度非常慢,大约需要2-3秒钟才能产生5000圈。 我怎样才能更快地绘制它?
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
{
}
}
}
答案 0 :(得分:1)
正如我的评论中所述:您发布的代码有很多问题,其中之一也是缺乏速度的原因。
Winforms Graphics规则1:切勿使用control.CreateGraphics
!
也永远不要尝试缓存Graphics
对象!使用Bitmap bmp
绘制到Graphics g = Graphics.FromImage(bmp)
或使用Paint
参数绘制控件的e.Graphics
事件。
(唯一的例外是您实际上不想保持的图形,例如绘制橡皮筋矩形。 您可以通过执行最小化/最大化序列来测试图形的持久性。)
正确的方法是保留要绘制的事物的列表,并且只要列表更改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)
绘制到位图中,则可以期望达到相同的速度。
答案 1 :(得分:0)
按照下面的优化顺序进行操作,直到您对性能满意为止。
频繁更新用户界面后,调用Control.SuspendLayout和Control.ResumeLayout。
检查是否经常调用Control.Refresh,请使用Control.Invalidate代替Control.Refresh
仅绘制可见区域(Graphics.Clip)内的圆,然后绘制的圆要少得多。
在Windows API级别暂停绘画。看到这篇文章: How do I suspend painting for a control and its children?