如何在 windows 表单上更快地画圆圈?
How to draw circles faster on windows form?
我需要在给定的 X、Y 坐标处绘制小圆圈,但在 window 的面板上最多可以绘制 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
{
}
}
}
按照下面的优化顺序进行操作,直到您对性能感到满意为止。
在频繁 UI 更新后调用 Control.SuspendLayout 和 Control.ResumeLayout。
检查您是否经常呼叫 Control.Refresh,使用 Control.Invalidate 而不是 Control.Refresh
只绘制可见区域内的圆圈(Graphics.Clip),这样绘制的圆圈就会少很多。
在 Windows API 级别暂停绘画。看到这个 post:
How do I suspend painting for a control and its children?
如我评论所述:您post的代码有很多问题,其中之一也是速度不够的原因..
Winforms 图形规则 #1:永远不要使用 control.CreateGraphics
!
也永远不要尝试缓存 Graphics
对象!使用 Graphics g = Graphics.FromImage(bmp)
绘制到 Bitmap bmp
中,或者使用 e.Graphics
参数在控件的 Paint
事件中绘制..
(唯一的例外是您实际上不想 坚持 的图形,例如绘制橡皮筋矩形。
您可以通过执行 minimize/maximize 序列来测试图形的持久性..)
正确的方法是保留一个要绘制的列表,每当该列表更改时 Invalidate
您绘制的控件。所有绘图都应该在 Paint
事件中,在那里使用 e.Graphics
!
这里经常讨论这个问题;但这里更有趣的是,与错误的 non-persistent 相比,正确的方法 非常快 。
让我们看看:
创建的图形对象的 50k 和 100k 圆的缺失时间分别为 5.4 秒(对比 0.18 秒)和 10.9 秒(对比 0.41 秒)。 (动画文件对他们来说太长了..)
所以 e.Graphics
可以 'draw' 圆圈快 ~30-100 倍。
怎么会? - 实际上 'correct' 方法只会 准备 double-buffered 控制的表面 内部 但只在显示一次 完成后 和 有时间这样做,而错误的方式将交付每个圈直接。这个优化是系统完成的,同时也限制了输出区域..
(PicturBox
默认为double-buffered;其他控件也可以这样,见)
这里是测试平台:
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)
..
绘制位图,您可以获得相同的速度
我需要在给定的 X、Y 坐标处绘制小圆圈,但在 window 的面板上最多可以绘制 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
{
}
}
}
按照下面的优化顺序进行操作,直到您对性能感到满意为止。
在频繁 UI 更新后调用 Control.SuspendLayout 和 Control.ResumeLayout。
检查您是否经常呼叫 Control.Refresh,使用 Control.Invalidate 而不是 Control.Refresh
只绘制可见区域内的圆圈(Graphics.Clip),这样绘制的圆圈就会少很多。
在 Windows API 级别暂停绘画。看到这个 post: How do I suspend painting for a control and its children?
如我评论所述:您post的代码有很多问题,其中之一也是速度不够的原因..
Winforms 图形规则 #1:永远不要使用 control.CreateGraphics
!
也永远不要尝试缓存 Graphics
对象!使用 Graphics g = Graphics.FromImage(bmp)
绘制到 Bitmap bmp
中,或者使用 e.Graphics
参数在控件的 Paint
事件中绘制..
(唯一的例外是您实际上不想 坚持 的图形,例如绘制橡皮筋矩形。 您可以通过执行 minimize/maximize 序列来测试图形的持久性..)
正确的方法是保留一个要绘制的列表,每当该列表更改时 Invalidate
您绘制的控件。所有绘图都应该在 Paint
事件中,在那里使用 e.Graphics
!
这里经常讨论这个问题;但这里更有趣的是,与错误的 non-persistent 相比,正确的方法 非常快 。
让我们看看:
创建的图形对象的 50k 和 100k 圆的缺失时间分别为 5.4 秒(对比 0.18 秒)和 10.9 秒(对比 0.41 秒)。 (动画文件对他们来说太长了..)
所以 e.Graphics
可以 'draw' 圆圈快 ~30-100 倍。
怎么会? - 实际上 'correct' 方法只会 准备 double-buffered 控制的表面 内部 但只在显示一次 完成后 和 有时间这样做,而错误的方式将交付每个圈直接。这个优化是系统完成的,同时也限制了输出区域..
(PicturBox
默认为double-buffered;其他控件也可以这样,见
这里是测试平台:
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)
..