再次记录双缓冲,是因为之前理解得不透彻。前段时间项目又得用到,而对BufferedGraphics不熟,又再鄙视自已无耻一次。再次记录,希望以后能随手用上撒。。
双缓冲其实没啥,就是因为刷屏造成了闪烁,而闪烁的原因有许多种,但比较普遍的一种即是直接画,这种入门级的错误最常见,因为我们经常在ONPAINT函数里就直接DRAW****了。
当然还有其他一些原因,比如系统绘屏不同步,屏幕图像构造复杂等。。。其实如果复杂了也很少用DRAWING了,还不如直接用DX。又再同情自已一下,以后试试自已直接调GDI+试试
双缓冲FW封装了两种很简单的,只要设置下属性就可以了撒的。。。
通过将 DoubleBuffered 属性设置为 true 或使用 SetStyle 方法可以为 Windows 窗体和所创作的 Windows 控件启用默认双缓冲。
代表简单功能明显:
case DoubleBufferMethod.BuiltInDoubleBuffer:
this.SetStyle(ControlStyles.UserPaint, true);
this.DoubleBuffered = true;
break;
case DoubleBufferMethod.BuiltInOptimizedDoubleBuffer:
this.SetStyle(
ControlStyles.OptimizedDoubleBuffer |
ControlStyles.AllPaintingInWmPaint, true);
break;
双缓冲使用内存缓冲区来解决由多重绘制操作造成的闪烁问题。当启用双缓冲时,所有绘制操作首先呈现到内存缓冲区,而不是屏幕上的绘图图面。所有绘制操作完成后,内存缓冲区直接复制到与其关联的绘图图面。因为在屏幕上只执行一个图形操作,所以消除了由复杂绘制操作造成的图像闪烁。ms-help://MS.MSDNQTR.v80.chs/MS.MSDN.v80/MS.VisualStudio.v80.chs/dv_fxmclignrl/html/4f6fef99-0972-436e-9d73-0167e4033f71.htm
还有一种,是我更喜欢用的。。。。
直接在内存里NEW 一个BITMAP出来,在上面直接画画,画完之后再往桌面的Graphics扔。。。
Bitmap BackBuffer;
Graphics BufferGraphics;
BackBuffer = new Bitmap(ClientRectangle.Width, ClientRectangle.Height);
BufferGraphics = Graphics.FromImage(BackBuffer);
//这时对Graphics 就使劲画吧。。。LunchGraphicTest(BufferGraphics);
// this draws the image from the buffer into the form area
// (note: DrawImageUnscaled is the fastest way)
ControlGraphics.DrawImageUnscaled(BackBuffer, 0, 0);
DrawImageUnscaled:在指定的位置使用图像的原始物理大小绘制指定的图像。
画完之后就把Graphics的这张BITMAP整副画到你要的区域里去。。。
最后一种是之前介绍过的,也是FX直接的,原理是一样的。。。改天查查源码,他是做了什么事。。。
BufferedGraphicsContext GraphicManager;
BufferedGraphics ManagedBackBuffer;
GraphicManager = BufferedGraphicsManager.Current;
GraphicManager.MaximumBuffer = new Size(this.Width + 1, this.Height + 1);
ManagedBackBuffer = GraphicManager.Allocate(this.CreateGraphics(), ClientRectangle);
Allocate创建图形缓冲区。
BufferedGraphicsContext 类提供用于创建和配置图形缓冲区的方法,负责单独分配和管理图形缓冲区。Allocate 方法可以创建 BufferedGraphics,它作为图形缓冲区的包装,也提供了可用于写入缓冲区和将其内容呈现到输出设备的方法。每个应用程序都有自己的默认 BufferedGraphicsContext 来管理此应用程序的所有默认双缓冲。提供调用 Current 可以检索对此实例的引用。
在这里随便你对这个ManagedBackBuffer.Graphics 怎么去画吧,画到爽为止,嘿嘿,其实本人觉得就是利用了系统帮我做好的分配内存等操作,使我们不用去创建一块BUFFER,进而提高了性能。不过性能也确实比BITMAP提高些
//LunchGraphicTest(ManagedBackBuffer.Graphics);
// paint the picture in from the back buffer into the form draw area
ManagedBackBuffer.Render(ControlGraphics);
就这样给画回去了。
我们可以看下源码, AllocBuffer主要的操作:
if (targetGraphics != null) {
IntPtr destDc = targetGraphics.GetHdc();
try {
surface = CreateBuffer(destDc, -targetLoc.X, -targetLoc.Y, targetRectangle.Width, targetRectangle.Height);
}
finally {
targetGraphics.ReleaseHdcInternal(destDc);
}
}
else {
surface = CreateBuffer(targetDC, -targetLoc.X, -targetLoc.Y, targetRectangle.Width, targetRectangle.Height);
}
this.buffer = new BufferedGraphics(surface, this, targetGraphics, targetDC, targetLoc, virtualSize);
其中CreateBuffer主要是用了CreateCompatibleDC在内存创建一份一样的DC,并返回句柄。然后就可以直接对内存操作了,再通过SelectObject把先前的DC换掉,再把图像完整的画进去,就这样返回一个BUFFER。
The CreateCompatibleDC function creates a memory device context (DC) compatible with the specified device.
The SelectObject function selects an object into the specified device context (DC). The new object replaces the previous object of the same type.
The TranslateTransform method updates this Graphics object's world transformation matrix with the product of itself and a translation matrix.
而BufferedGraphics对象里则封装了:
this.context = context;
this.bufferedGraphicsSurface = bufferedGraphicsSurface;
this.targetDC = targetDC;
this.targetGraphics = targetGraphics;
this.targetLoc = targetLoc;
this.virtualSize = virtualSize;等信息
当我们调用Render函数时,则是调用BitBlt API
SafeNativeMethods.BitBlt(refTargetDC, targetLoc.X, targetLoc.Y, virtualSize.Width, virtualSize.Height,new HandleRef(buffer.Graphics, sourceDC), 0, 0, rop);
The BitBlt function performs a bit-block transfer of the color data corresponding to a rectangle of pixels from the specified source device context into a destination device context.
关于此函数MSDN还有这么一段关于减少闪烁的:
When you animate a simple graphic, users can sometimes encounter flicker or other undesirable visual effects. One way to limit this problem is to use a "bitblt" process on the graphic. Bitblt is the "bit-block transfer" of the color data from an origin rectangle of pixels to a destination rectangle of pixels.
With Windows Forms, bitblt is accomplished using the CopyFromScreen method of the Graphics class. In the parameters of the method, you specify the source and destination (as points), the size of the area to be copied, and the graphics object used to draw the new shape.
In the example below, a shape is drawn on the form in its Paint event handler. Then, the CopyFromScreen method is used to duplicate the shape.
基本上就是这样了,系统用API函数,肯定比我们一个BITMAP类快得多,BITMAP类看了一下700多行撒。。。。晕。。。。好几层API,还有几个MSDN查也查不出啥的方法。。。以后弄懂了再补充。
给出一个构造函数
public Bitmap(String filename) {
IntSecurity.DemandReadFileIO(filename);
//GDI+ will read this file multiple times. Get the fully qualified path
//so if our app changes default directory we won't get an error
//
filename = Path.GetFullPath(filename);
IntPtr bitmap = IntPtr.Zero;
int status = SafeNativeMethods.Gdip.GdipCreateBitmapFromFile(filename, out bitmap);
if (status != SafeNativeMethods.Gdip.Ok)
throw SafeNativeMethods.Gdip.StatusException(status);
status = SafeNativeMethods.Gdip.GdipImageForceValidation(new HandleRef(null, bitmap));
if (status != SafeNativeMethods.Gdip.Ok) {
SafeNativeMethods.Gdip.GdipDisposeImage(new HandleRef(null, bitmap));
throw SafeNativeMethods.Gdip.StatusException(status);
}
SetNativeImage(bitmap); 这两个看不懂,有谁可以解说下作用吗?
EnsureSave(this, filename, null);
}