【问题标题】:Using Bitmap in Bouncing Balls在弹跳球中使用位图
【发布时间】:2013-01-13 12:20:56
【问题描述】:

我有一个WinForm 应用程序'Bouncing Balls',我需要给球上色 在bitmap 上并在此表单上显示位图。

我有一个添加新球的plusButton,我将每个新球保存在一个列表中。

现在,Form_Paint 方法告诉每个球自己画,效果很好 直到有很多球并且所有应用程序变得非常缓慢..

这是我的代码:

表单代码的paint方法:

 private void Form1_Paint(object sender, PaintEventArgs e)
 {
     ballsArray.drawImage(bmp,e, ClientRectangle);
 }

注意: ballsArray 来自 AllBalls 类型,这是一个包装球方法的类,在他的 c'tor 中,我正在创建一个保存每个球的列表。 bmp,在表单加载时创建 - 在 Form_Load() 方法上。

ballsArray代码的drawImage:

 public void drawImage(Bitmap bmp,PaintEventArgs e, Rectangle r)
 {
     foreach (Ball b in allBalls)
     {
         b.drawImage(bmp,e, r);
     }
 }

鲍尔代码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 是一个矩形,表示每个球的位置 移动的步骤..

那么我做错了什么?是什么导致应用程序运行缓慢?

我必须在位图上绘制所有内容并将其呈现在表单上。 我还传递了加载表单时创建的位图,因为我需要在其上绘制每个位图。

【问题讨论】:

  • 通过将Form1_Paint 事件的Graphics 对象传递给drawImage 方法并直接在其上绘制椭圆,而不是为每个对象创建新的Graphics,您将获得更好的性能球。
  • @Rotem,好吧,你的意思是我应该将 Graphics g = Graphics.FromImage(bmp) 移动到 Form1_Paint 并发送这个对象drawImage 方法?
  • 不完全是。再次查看您的代码,我不确定您为什么要在位图上而不是直接在 Graphics 对象上绘画。
  • @Rotem 这是一个家庭任务,它的一个限制是使用位图来绘制所有的球..所以我所做的是上面的代码,但是当我开始的时候它变得很慢添加很多球..
  • 这对我来说毫无意义,你只是在自己绘制位图。

标签: c# .net winforms bitmap paintevent


【解决方案1】:

一些使这个速度更快的基本技术:

  • 不要对自己进行双重缓冲,尤其是不要双重缓冲两次。通过将表单的 DoubleBuffer 属性设置为 true 获得的双缓冲优于您自己进行的大多数双缓冲。缓冲区经过高度优化,可以有效地与您的视频适配器设置配合使用。因此,完全删除您的 bmp 变量并绘制到您从 Paint 事件处理程序参数中获得的 e.Graphics。

  • 您没有使用传递的r 参数。可能旨在支持剪切隐形球。您要传递的是 e.ClipRectangle,您可以跳过完全超出此矩形的画球。虽然这是一种优化,但当您使用 Aero 主题时,它通常不是很有用,而且您确实会得到不一致的重绘率,因此您可能想跳过该优化。

  • 目前还不清楚为什么在绘制球时同时使用 Graphics.FillEllipse 和 Graphics.DrawImage。图像应该与圆圈重叠,因此只需删除 FillEllipse。

  • 非常注意存储球图形的 Bitmap 对象。您要确保的第一件事是它是按照图像的确切大小绘制的,因此不必重新缩放。重新缩放非常昂贵。虽然您在 DrawImage() 调用中没有任何重新缩放,但如果位图的分辨率与视频适配器的分辨率不同,您仍然会得到它。下一步会解决这个问题

  • 球位图的像素格式非常很重要。您需要一个允许将位图直接复制到视频内存而无需任何格式转换的设备。在任何现代机器上,该格式都是 PixelFormat.Format32bppPArgb。差异是巨大的,它的绘制速度比其他任何一个都快十倍。您不会从您添加的图像资源中获得这种格式,您必须在程序启动时创建该位图。检查this answer 以获得所需的代码。

当您遵循这些准则时,您应该能够将渲染速度至少提高 15 倍。如果这仍然足够,那么您确实需要转向 DirectX,它具有无与伦比的优势,能够将球图形存储在显存中,因此您不会将昂贵的 blt 从主内存转移到显存。

【讨论】:

  • 你是说Format32bppPArgb 快​​,而Format32bppArgb 不是?
  • 是的,Argb 在我测试过的任何机器上都慢了十倍。
  • 表单有自己的位图是真的吗?!我的意思是当表单加载时,他自己画……
  • 当您将其 DoubleBuffered 属性设置为 true 时,您在 Paint 事件中获得的 e.Graphics 指的是位图,而不是屏幕。这个位图在Paint事件完成后被blit到屏幕上,从而达到双缓冲的效果。
【解决方案2】:

DrawImagePaint(或就此而言MouseMove)是非常糟糕的设计。

Graphics.DrawImage 是昂贵的操作,对于屏幕来说是额外的昂贵。 为了改善您的用户体验(缓慢),您应该在 MouseDown/MouseUp 事件上进行绘制。

此外,首先在您的drawImage 方法中绘制到 MemoryBuffer,并在准备好最终图像后,在 UI 上绘制一次。这种技术被称为double buffering

Don't Flicker! Double Buffer! - CodeProject

此外,您还可以查看 BitBlit Native API 以快速将颜色/图像传输到屏幕。

一个简约的 c# 示例是here

【讨论】:

  • 谢谢你,但我启用了双缓冲..它需要做你建议的同样的事情吗?
  • 您需要手动完成。e.Graphics.FillEllipse(brush, ballLocation); 是您应该关注的行。
【解决方案3】:

在您的表单上启用双缓冲 (DoubleBuffered = true)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-05-23
    • 2014-08-17
    • 2012-10-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多