我有一个WinForm
申请'Bouncing Balls',我需要画球
在bitmap
上显示此表单上的位图。
我有plusButton
添加新球,我将每个新球保存在列表中。
现在,Form_Paint
方法告诉每个球画自己,它运作正常
直到有很多球并且所有应用变得非常慢..
这是我的代码:
表单代码的paint方法:
private void Form1_Paint(object sender, PaintEventArgs e)
{
ballsArray.drawImage(bmp,e, ClientRectangle);
}
注意: ballsArray
来自AllBalls
类型,这是一个包装球方法的类,在他的内部我正在创建一个保持每个方法的列表球。在bmp
方法加载表单时创建Form_Load()
。
ballsArray
代码的drawImage:
public void drawImage(Bitmap bmp,PaintEventArgs e, Rectangle r)
{
foreach (Ball b in allBalls)
{
b.drawImage(bmp,e, r);
}
}
Ball代码drawImage
:
public void drawImage(Bitmap bmp, PaintEventArgs e, Rectangle r)
{
using (Graphics g = Graphics.FromImage(bmp))
{
e.Graphics.FillEllipse(brush, ballLocation);
g.DrawImage(bmp, 0, 0);
}
}
注意: ballLocation
是一个矩形,代表每个球的位置
运动的步骤..
那么我做错了什么?是什么导致应用程序缓慢?
我有一个约束来绘制位图上的所有内容并将其呈现在表单上。 我也传递了我在表单加载时创建的位图,因为我需要在其上绘制每个位图。
答案 0 :(得分:3)
快速实现这一目标的一些基本技巧:
不要双重缓冲自己,特别是不要双重缓冲两次。通过将表单的DoubleBuffer属性设置为true,您获得的双缓冲优于您自己做的大多数双缓冲。缓冲区经过高度优化,可以高效地处理视频适配器的设置。所以完全删除你的bmp
变量并绘制到你从Paint事件处理程序参数获得的e.Graphics。
您没有使用传递的r
参数。可能意图支持削减隐形球。你要传递的是e.ClipRectangle,你可以跳过完全在这个矩形之外的绘画球。虽然这是一个优化,但是当您使用Aero主题并且您确实获得不一致的重绘率时,它不是通常有用的,因此您可能想要跳过那个。
在绘制球时,为什么同时使用Graphics.FillEllipse和Graphics.DrawImage并不是很清楚。图像应该与圆重叠,因此只需删除FillEllipse。
非常关注存储球图形的Bitmap对象。首先要确保的是,它是使用图像的确切大小绘制的,因此不必重新缩放。重新缩放非常昂贵。虽然您在DrawImage()调用中没有任何重新缩放,但如果位图的分辨率与视频适配器的分辨率不同,您仍会得到它。下一步将解决这个问题
球位图的像素格式非常重要。您需要一个允许将位图直接复制到视频内存而无需任何格式转换的内容。在任何现代机器上,该格式为PixelFormat.Format32bppPArgb。差异是巨大的,它比其他任何一个快十倍。您不会从添加的图像资源中获取此格式,您必须在程序启动时创建该位图。检查this answer以获取所需的代码。
遵循这些准则时,您应该能够以至少15倍的速度渲染。如果这仍然足够你需要转向DirectX,它具有无与伦比的优势,能够将球图形存储在视频内存中,这样你就不会从主内存到视频内存获得昂贵的blt。
答案 1 :(得分:0)
DrawImage
上的{p> Paint
(或MouseMove
上的问题)设计非常糟糕。
Graphics.DrawImage操作费用昂贵,而且对于屏幕而言,这是非常昂贵的。 为了改善用户体验(缓慢),您应该在MouseDown / MouseUp事件上进行绘制。
此外,首先在drawImage
方法中绘制MemoryBuffer,然后在准备最终图像后,在UI上绘制一次。这种技术称为double buffering。
Don't Flicker! Double Buffer! - CodeProject
此外,您还可以查看BitBlit Native API,以便快速将颜色/图像传输到屏幕。
简约c#示例是here
答案 2 :(得分:0)
在表单上启用双缓冲(DoubleBuffered = true
)。