代码改变世界

效率,还是效率 .NET Compact Framework下实现GDI双缓冲

2007-12-24 21:00  cppguy  阅读(1777)  评论(2编辑  收藏  举报

首先,为什么要实现双缓冲

      提高图形绘图效率,解决闪烁问题

先看桌面平台:

    在System.Drawing下有BufferedGraphics,可以实现图形的自定义双缓冲。它提供图形缓冲区的包装,以及可用于写入缓冲区和将其内容呈现到输出设备的方法。使用双缓冲的图形可以减少或消除重绘显示图面时产生的闪烁。使用双缓冲时,更新的图形首先被绘制到内存的缓冲区中,然后,此缓冲区的内容被迅速写入某些或所有显示的图面中。显示图形的重写相对简短,这通常可以减少或消除有时在更新图形时出现的闪烁

    使用双缓冲的最简单方法是使用 SetStyle 方法在控件上设置 OptimizedDoubleBuffer 控件样式标志。为某个控件设置了 OptimizedDoubleBuffer 标志以后,便可以通过一个默认的图形缓冲区对该控件的所有绘制进行重定向,而不需要任何附加代码。默认情况下,此标志设置为 true

在移动平台:

   没有像桌面平台下那样的BufferedGraphics的类,那我如果我们要实现GDI这样的双缓冲应该怎么办呢?

  首先看案例,我要在手机的屏幕上实现如图中小人的走动,实现的原理大家也都很清楚,就是几幅图片实现来回的切换,我们用一个二维数组来存储这些图片,上下左右各两幅






ManMoveImage[0, ] = new Bitmap(currentGame.BaseDirectory + @"Images\ManUp0.png");
ManMoveImage[0, 1] = new Bitmap(currentGame.BaseDirectory + @"Images\ManUp1.png");
ManMoveImage[1, 0] = new Bitmap(currentGame.BaseDirectory + @"Images\ManDown0.png");
ManMoveImage[1, 1] = new Bitmap(currentGame.BaseDirectory + @"Images\ManDown1.png");
ManMoveImage[2, 0] = new Bitmap(currentGame.BaseDirectory + @"Images\ManLeft0.png");
ManMoveImage[2, 1] = new Bitmap(currentGame.BaseDirectory + @"Images\ManLeft1.png");
ManMoveImage[3, 0] = new Bitmap(currentGame.BaseDirectory + @"Images\ManRight0.png");
ManMoveImage[3, 1] = new Bitmap(currentGame.BaseDirectory + @"Images\ManRight1.png");

当然实现走动最简单的方法就是,在窗体上不断的使用DrawImage(ManMoveImage[x,y],destrect,srcrect,GraphUtil.Pixel),当然前提是我要把上一次位置的画上的人的图片擦除掉,所以我们要保留一张背景图片,要对原来的位置进行局部的更新,但更新的方法,我们很容易也会直接用this.refresh();

  但是我们看看刚才做的一系列动作,这样是可以实现刷新的,但是一个简单的走动,我们要刷新两次Form,最直接的表现就是我们会看到两次闪屏,当然间隔很短,这对于用户体验是不能接受的。

  我在解决以上问题的时候用了以下的方法,当然可能不是最好的,也请高手能够指正。

     首先创建了两个Graphics,一个是为了修改Form,一个为了修改缓存图

       private Graphics offscreen;
        private Graphics formgtaphics;

      //存储背景图片的位图
       private Bitmap background;

 

      public GAME()//窗体叫GAME
        {
            InitializeComponent();
            man.Colliding += new CollideEventHandler(man_Colliding);
            furn = new Furnatures(this);
            background2 = new Bitmap(furn.BackGround);//缓存图
            offscreen = Graphics.FromImage(background);//从缓存图创建Graphics
            formgtaphics = this.CreateGraphics(); //从窗体创建Graphics
        }

看以下的功能函数

当人一走动时,我要把现在人所在位置上的图片擦掉,回复到没有人的时候

public void Restore()
       {
           currentGame.OffScreen.DrawImage(
               currentGame.BackBmp,
               left,
               top,
               new Rectangle(left, top, width, height),
               GraphicsUnit.Pixel);

       }

然后在缓存图中用新图在新位置上画上

public void Draw(Graphics gx, int x)
       {

           gx.DrawImage(
               ManMoveImage[x, MoveFlag[x]],
               new Rectangle(left, top, width, height),
               0,
               0,
               width,
               height,
               GraphicsUnit.Pixel,
               cellAttributes);

       }

最后在Form上,把变动的这些区域(可以叫做“脏区域”),用缓存图画上。

public void Refresh(Rectangle rcDirty)
        {
            formgtaphics.DrawImage(
                background,
                rcDirty.X,
                rcDirty.Y,
                rcDirty,
                GraphicsUnit.Pixel
                );
        }

当然也可以设这段矩形为无效区this.Invalidate(rcDirty);this.Update();

通过以上方法,基本可以解决闪烁问题。

其实这样的方法,不但可以处理一个元素在动,几个元素同时在动也可以解决的,画面中的小鸟和人都是同时在动的。