如何更快地使屏幕无效?

时间:2011-04-17 07:23:49

标签: c# drawing invalidation

在我目前的项目(生命游戏)中,我需要重绘屏幕,因为大约有200个物体在移动。我可以想到两种方法,但没有任何线索会更快:

我可以:
1)为整个屏幕调用Invalidate(),并在Paint处理程序中有以下内容:

void Paint(object sender, PaintEventArgs e)
{
   foreach(Cell c in ListOfCells)
          {
             e.DrawImage(c,c.x,c.y);
          }
}

2)或者我可以使每个单元格的屏幕的每个部分无效:

public void MyInvalidate()
{
  foreach(Cell c in ListOfCells)
              {
                 Invalidate(c.X,c.Y,c.Width,c.Height);
              }


}

并拥有与上面相同的处理程序

2 个答案:

答案 0 :(得分:4)

现在,第一条规则始终是“不要过早优化”。您需要确保需要优化清洁代码。

现在,在生命游戏中,你最初看的是一个非常空的屏幕。然而,随着比赛的进行,越来越多的细胞将被填满,最终将成为整个棋盘。现在,GOL规则规定大多数细胞将从一个周期变为另一个周期。

您还需要通过“使区域无效”来理解其含义。在Windows中,无效区域“加起来”以形成“更新区域”,以便WM_PAINT消息可以告诉程序要绘制屏幕的哪个部分。在Paint事件处理程序中,使用RectVisible来确定是否要刷新单元格。

换句话说,做方法1的“成本”(假设电路板尺寸为n):

(n x n) x (redraw cell + update cell on screen)

请注意,我假设您在绘制单元格之前记得要清除屏幕,否则在上一个循环中过去“活着”的单元格将保留在屏幕上。因此,这假设您要在整个屏幕上绘制活动单元格或空白单元格。

做方法2的“成本”:

(v) x (redraw cell + update cell on screen) + (n x n) x (RectVisible call)

其中v =更改的单元格数量(请参阅下面的警告说明)。

因此,如果出现以下情况,方法1比方法2快:

(n x n) x redraw < v x redraw + (n x n) x RectVisible
n2 x (redraw - RectVisible) < v x redraw
v > ((redraw - RectVisible) / redraw) x n2
v > (1 - RectVisible/redraw) x n2

换句话说,更改的单元格数量必须大于1减去(RectVisible cost / redraw cost)的比率。现在,与在屏幕上绘制位图相比,RectVisible通常非常快,特别是如果您的单元格具有高分辨率。因此,Rectvisible / redraw通常是一个非常小的数字,只有当有> 99%的电路板同时改变时,方法1才比方法2更快,这是不可能的。

换句话说,您会发现方法2通常会产生更高的性能,因为如果要在剪切区域之外(即不在更新区域内)绘制图像,DrawImage通常会跳过整个操作。

CAVEAT - 您的代码,因为它不起作用

但是,方法2中的代码不起作用。你必须记住在最后一个循环中“清空”曾经有“活”细胞的“死”细胞。换句话说,您使“已更改”的单元格无效,而不仅仅是那些“活着”的单元格。对于多个周期“活动”的单元格需要无效。因此,MyInvalidate方法中的逻辑存在缺陷。

答案 1 :(得分:0)

如果您坚持使用与第一个相同的Paint事件处理程序,第二个代码将没有任何好处,因为基本上您将多次重绘所有单元格(因为对于每个单元格,您将重绘所有其他细胞也是如此)。要解决此问题,您可以检查e.ClipRectangle,只重绘该矩形内的单元格。

但是,如果您屏幕上唯一的东西是单元格(因此您没有大量其他显示元素,例如UI控件),那么第一种方法是您将获得的最佳方法(即只是Invalidate()整个屏幕)。仅当您使屏幕上很多未更改的区域无效时,才会出现性能损失。