效率,还是效率 .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();
通过以上方法,基本可以解决闪烁问题。
其实这样的方法,不但可以处理一个元素在动,几个元素同时在动也可以解决的,画面中的小鸟和人都是同时在动的。