我想实现双缓冲,消除画图闪烁,可是老有问题。(50分)

  • 主题发起人 主题发起人 lizhao
  • 开始时间 开始时间
L

lizhao

Unregistered / Unconfirmed
GUEST, unregistred user!
我在绘图时屏幕闪得太历害,我想用双缓冲,可是由于我对win api不熟,
老是不对,并且还将我的NT搞死。
下面是我 paint过程 的源程序的部分内容。

BufferBitmap:=TBitmap.Create;
BufferBitmap.LoadFromFile('c:/windows/forest.bmp');
//如果不加上面的这句就根本显示不了,
//如果加上的话,就相当于把这个bitmap当底图了,可是仍旧闪烁。
BufferBitmap.Width:=width;
BufferBitmap.Height:=height;

//这里是往BufferBitmap.canvas上绘图的操作。

mdc:=CreateCompatibleDc(Canvas.Handle);
SelectObject(mdc,Bufferbitmap.handle);
bitblt(Canvas.Handle,0,0,width,height,mdc,0,0,SRCCOPY );
//上面一句是将Bitmap.canvas往我要的绘图区的Canvas上copy.
DeleteDc(mdc);
bufferbitmap.FreeImage;
 
我没有在Delphi中用过双缓冲,可能说的不对
你这样写,实际上不是双缓冲,
你的程序可以看成,从外部读了一个图片,然后先画在Buffer上,在画在Canvas上
双缓冲的本质是用户看不见绘图过程,在你的程序钟,虽然使用了API进行位拷贝,
可是用户还是可以看见绘图过程的,闪烁也就难免
加上频繁使用create,free速度肯定慢!
我想应该这么写
1.定义两个Image:Iamge1,Iamge2
一个标记位Flag,表明当前显示页True :Iamge1 false:Iamge2
2.切换时
a:判断当前页(假设为Image1)
b:图像读入Image2中(此时Image2.visible:=false)(你的程序放这里)
c:Image2.visible:=true;
Flag:= Not(flag);
Image1.visible:=false;
3大功告成

 
你把背景预先读入,保存在tbitmap里,每次paint没必要释放
 
是这样的,我不一定要用背景图,只是不用背景的话,(即上面的
oadfromfile这句话)我自己的图就不能显示。
其实我的意思就是往我自己的容器的Canvas上画图,如果直接画的话就会闪烁,
因为我有一个循环在里面一个一个图元的画,所以我就考虑先定义一个bufferbitmap,将要画的图形先画到Bufferbitmap的canvas上,
然后,用bitblt将Bufferbitmap的Canvas Copy 到我自己的容器的Canvas上。

to mayday:
你说我画图的过程用户看得到,不知错在哪句,
从实际的运行来看也是这样的,我就是不想让用户见到画图的过程,
否则就会有闪烁。
我对Api不熟悉,请指教。

 
关于动态建立tbitmap,并向他的canvas上画图,只要这样:
Bitmap.Width := Width;
Bitmap.Height := Height;
Bitmap.PixelFormat := pf24bit;
就可以了
至于向canvas上画此bitmap,可以:
Canvas.Draw(0,0,Bitmap);
 
我的语文水平差差?
你在往屏幕上进行大面积的像素移动,这实际上就是一个绘制过程
加上你反复的Create ;Free 所以中间过程时间就长当然有闪烁乐!
我的意思是BitBlt虽然是WinAPI但是这些还是让系统处理比较好一些
一般我用两个Frame通过改变可见性来进行操作。
例子已经贴出来了:)


 
to tqz:
你说的这种方法我已用过,不仅有闪烁,而且屏幕不再刷新,即使把Canvas上的某个图元移开后,在初始处还有痕迹。

to Mayday:
我想我是明白你的意思了,我的语文也很差,高考语文-1分(千真万确),
你的意思是不是建立两个Bufferbitmap,通过一个开关确定哪个BufferBitmap
是Visible,然后就先将图元往不可见的BufferBitmap上画,画好后交换两个
BufferBitmap的可见性。
可是我现在的问题是这样的我,我作的是一个CAD的作图的软件,所有的图元
都是画在我自己做的一个容器上的Canvas上的,我不可能在程序里做两个容器
来控制哪个是Visible的。所以我一直想用一个BufferBitmap来先将图画好,
然后将其CanvasCopy 到我的容器的Canvas上。没想到Copy的过程还是一个
重画的过程。呜呜。。。


 
这个问题我自己解决了就按照我最先的思路,屏幕不再闪烁了,可是由于我在
每次paint的时候,都先往一个Bitmap的Canvas上画,然后再copy 到我要
的Canvas上,可能是我要开一块内存给Bitmap,导致程序运行很慢,尤其是
我在Canvas上拖动某个图元时,其实我也知道,不用对整个屏幕重画,只需对
有变化的部分重画,可是我作不到,希望各位帮我。
就算最后问题没有解决我也会把分数分给给大家的。
 
其实双缓冲的作用只是将复杂的画图操作放在一块不可见的后台BufferBitMap中
进行,待全部画图操作完成之后,再将这块BufferBitMap一次性画到前台。
使用双缓冲,似乎无法避免增加内存的开销(即后台BufferBitMap).
如果你在程序中可以确定要重画的区域,那就可以开一块和该区域相同大小的后台
BufferBitMap, 以减少内存的开销。
另外,双缓冲技术一般都是使用内存位图来实现的,即使用TBitMap类,而不是使
用TImage或其他的图象处理类。
 
容器闪烁的原因并不是光用双缓冲就能解决的, 因为容器是继承自TWinControl类的, 它的刷新
是windows控制的, 不是delphi, 刷新时, windows先发送WM_ERASEBKGND消息通知清除要
刷新区域的底色(因为你改变的是容器的背景, 所以这个区域就是整个client), 然后发
WM_PAINT消息通知重画, 引起闪烁的罪魁其实就是WM_ERASEBKGND, 所以只要你截掉
WM_ERASEBKGND消息, 什么都不做就返回就成了(千万别inherited原来的消息处理程序)
 
另外, 如果要实现TWinControl类的透明, 则需要用setwindowlong修改parent的style属
性,不然得到的背景不正确(要么是直接透明到form下面的桌面上去了, 要么得到一个灰色的rect,
取决于你是否截掉WM_ERASEBKGND),
在透明TWinControl控件移动时要调用ReCreateWnd, 不然背景不会跟着改变
 
to Another_eYes:
我的确是将Wm_EraseBkgrd 消息截获了。在前面忘记提了。
procedure WMEraseBkgnd(var Message: TMessage); message WM_ERASEBKGND;
 
多人接受答案了。
 

Similar threads

S
回复
0
查看
3K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
2K
SUNSTONE的Delphi笔记
S
I
回复
0
查看
782
import
I
I
回复
0
查看
621
import
I
I
回复
0
查看
625
import
I
后退
顶部