怎样使用内存双缓冲技术消除屏幕闪烁?

下面是我写风车的代码,修改一下代码,消除屏幕闪烁。#include<Windows.h>#include<tchar.h>#include<math.h>longWINA... 下面是我写风车的代码,修改一下代码,消除屏幕闪烁。
#include <Windows.h>
#include <tchar.h>
#include <math.h>

long WINAPI WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
UINT SCREEN_WIDTH = 800;
UINT SCREEN_HEIGHT = 600;

int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
TCHAR className[] = _T("serenesunny");
TCHAR windowName[] = _T("fengche");
WNDCLASS wndcls;
wndcls.cbClsExtra = 0;
wndcls.cbWndExtra = 0;
wndcls.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndcls.hCursor = LoadCursor(hInstance, IDC_ARROW);
wndcls.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
wndcls.hInstance = hInstance;
wndcls.lpfnWndProc = WndProc;
wndcls.lpszClassName = className;
wndcls.lpszMenuName = 0;
wndcls.style = CS_HREDRAW | CS_VREDRAW;

RegisterClass(&wndcls);

HWND hWnd = CreateWindow(className, windowName, WS_OVERLAPPEDWINDOW, 0,
0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0, hInstance, 0);

ShowWindow(hWnd, SW_SHOWNORMAL);
UpdateWindow(hWnd);

MSG msg;
while (GetMessage(&msg, hWnd, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}

return msg.wParam;
}

#define Pi 3.1415926
#define N 4
UINT radius = 200;
UINT centerx = SCREEN_WIDTH / 2;
UINT centery = SCREEN_HEIGHT / 2;
UINT rx = centerx;
UINT ry = centery- radius;
UINT rp = radius / 2;
UINT p1x, p1y, p2x, p2y, centerpx, centerpy;
float theta = 2 * Pi / (float)N;
float thetaR = Pi / (float)2;
float thx = 2 * Pi / (float)360;
int ti = 0;
UINT index = 0;

long WINAPI WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
HDC MemDC;
HBRUSH hBrush;
HBITMAP hBmp;
HGDIOBJ hOldSel;
switch(message)
{
case WM_CREATE:

case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
hBrush = CreateSolidBrush(0x0000ff);
SelectObject(hdc, hBrush);;
for (index = 0; index < N; ++index)
{
centerpx = (UINT)(rp * sin((float)(index * theta) + thetaR + thx * ti));
centerpy = (UINT)(rp * cos((float)(index * theta) + thetaR + thx * ti));
p1x = centerx + centerpx - rp;
p1y = centery + centerpy - rp;
p2x = centerx + centerpx + rp;
p2y = centery + centerpy + rp;
rx = centerx + (UINT)(radius * sin((float)(index * theta) + thetaR + thx * ti));
ry = centery + (UINT)(radius * cos((float)(index * theta) + thetaR + thx * ti));
Pie(hdc, p1x, p1y, p2x, p2y, rx, ry, centerx, centery);
}
//清除资源
ti--;
ti = ti % 360;
Sleep(10);
InvalidateRect(hWnd,NULL,1); //重绘窗口区域.
EndPaint(hWnd, &ps);
break;
case WM_CLOSE:
DestroyWindow(hWnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
展开
 我来答
hostmanye
2011-01-25 · TA获得超过1057个赞
知道小有建树答主
回答量:511
采纳率:33%
帮助的人:198万
展开全部
CDC BufferDC;
CBitmap BufferImage;
BufferDC.CreateCompatibleDC(&dc);
BufferImage.CreateCompatibleBitmap(&dc,rcClient.Width(),rcClient.Height());
BufferDC.SelectObject(&BufferImage); //创建好的image就是新的背景图,你可以往上面继续画东西。
这是MFC的例子,你自己转到API吧,试试看吧。
feqgat
2011-01-26 · TA获得超过1002个赞
知道小有建树答主
回答量:1092
采纳率:100%
帮助的人:893万
展开全部
Java动画编程有多种实现方法,但它们实现的基本原理是一样的,即在屏幕上画出一系列的帧来造成运动的感觉。Java多线程技术是Java动画编程中普遍运用的技术,它在控制动画程序的流程和动画的显示效果方面起着重要的作用。Java动画编程中的动画闪烁和图像残缺不全等现象,是Java程序员经常遇到的问题。本文以作者应用实例程序为基础,阐述如何运用多线程、重载Update、双缓冲和图像跟踪等技巧来解决这类问题,以达到动画显示的最佳效果。
Java多线程技术
Java多线程技术简介
目前,线程(Thread)已经为许多操作系统和应用开发系统所采用。线程是程序的单个控制流,具有顺序程序的特点。但是,线程不是一个程序,它仅仅是程序的一个执行序列。线程具有很强的并发功能,在同一时刻可以有多个线程同时处于执行状态。线程是动态的,具有一定的生命周期,分别经历从创建、执行、阻塞、直到消亡的过程。Java语言对多线程编程的支持有两种实现方法:一种是直接继承Thread类,另一种是实现Runnable接口。Thread类提供了对线程的控制方法,如start(),stop(),run()、suspend()、resume()和sleep()等方法,它们可以对线程的状态进行控制。
动画线程的设计与实现
为了每秒中多次更新屏幕,必须创建一个线程来实现动画的循环,这个循环要跟踪当前帧并响应周期性的屏幕更新要求。许多Java初学者容易犯的一个错误是将动画循环放在paint()中,这样占据了主AWT线程,而主线程将负责所有的绘图和事件处理。因此,应该生成一个独立的动画线程来完成图像的显示和更新。例如,在一个Applet框架下,当Applet启动(Start)时,生成一个动画线程;在Applet停止(stop)时,终止该动画线程以释放它所占用的CPU资源。下列程序代码(简称“C1”代码)是该动画线程的具体实现:
public void start() {
if(animatorThread==null) {
animatorThread=new Thread(this);
//开始动画线程
animatorThread.start();
}
}
public void stop(){
//停止动画线程
animatorThread=null;
}
上面终止动画线程的时候,并不是调用该动画线程的stop()方法,而是设置该动画线程为null。因为如果直接调用线程的stop()方法会强制线程终止所有的执行工作,有时会带来不好的结果。设置该动画线程为null,则在run()方法中,由于不满足循环条件,线程会自然退出。这样,也进一步优化了该动画程序。
重载update()和双缓冲技术消除闪烁
在Java中,动画发生闪烁有两个原因:一个是由于在显示下一帧画面的时候,调用了repaint()方法;而repaint()方法被调用时,要清除整个背景,然后才调用paint()方法显示画面。这样,在清除背景和绘制图像的短暂时间间隔内被用户看见的就是闪烁。另一个是由于paint()方法要进行复杂的计算,绘制每一帧花费的时间太长,图像中的各个像素值不能同时得到,使得动画的生成频率低于显示器的刷新频率,从而造成闪烁。
下面两种方法可以明显地消除或减弱闪烁。
重载update()方法
当AWT接收到一个Applet的重绘请求时,它就调用Applet的update()方法。缺省情况下,update()方法清除Applet的背景,然后调用paint()方法。重载update()方法就可以将以前在paint()方法中的绘图代码包含在update()方法中,从而避免每次重绘时将整个区域清除。既然背景不再自动清除,Java程序员需要自己在update()中完成。
双缓冲技术
另一种消除帧之间闪烁的方法是使用双缓冲技术,它在许多动画Applet中被使用。主要原理是创建一幅后台图像,将每一帧画入图像,然后调用drawImage()方法将整个后台图像一次画到屏幕上去。这种方法的优点在于大部分绘制是离屏的。将离屏图像一次绘至屏幕上,比直接在屏幕上绘制要有效得多。在创建后台图像前,首先要通过调用createImage()方法生成合适的后台缓冲区,然后获得在缓冲区做图的环境(即Graphics类对象)。
下列实例程序代码(简称“C2”代码)就是这两种方法的结合使用,双缓冲技术在重载update()方法中实现。其中,offImage是Image类的对象,offGraphics是Graphics类的对象,这两个类对象是实现双缓冲技术的关键。相关代码如下:
public void paint(Graphics g){
update(g);
}
public void update(Graphics g){
Dimension d=getSize();
//如果后台图像不存在,就创建一个后台图像
if((offGraphics==null)||(d.width!=offDimension.width)
||(d.height!=offDimension.height)) {
offDimension=d;
offImage=createImage(d.width,d.height);
offGraphics=offImage.getGraphics();
}
//擦除上一帧
offGraphics.setColor(getBackground());
offGraphics.fillRect(0,0,d.width,d.height);
offGraphics.setColor(Color.black);
//将当前的帧输出到指定的image中
for(int i=0 ; i<10 ; i++){
offGraphics.drawImage(images[i],frameNumber*5%(d.width/2)
,i*d.height/10,this);
}
//输出指定的后台图像
g.drawImage(offImage,frameNumber*5%(d.width/2),0,this);
}
双缓冲技术可以使动画平滑,但有一个缺点,要分配一个后台图像的缓冲,如果图像相当大,这将占用很大一块内存。
图像跟踪与程序的逐步完善
图像跟踪
当动画线程刚刚启动的时候,由于没有全部载入图像,屏幕上显示的画面经常是残缺不全的。这时可以使用MediaTracker或ImageOberver类对象进行图像跟踪,待图像全部载入后,再调用drawImage()方法将图像输出到屏幕上去。DrawImage()方法的第四个参数正是ImageObserver类对象,所以可以用ImageObserver类对象进行图像跟踪。在实际应用Applet程序的init()方法中实现图像跟踪,相当于在动画线程的DrawImage()方法调用以前就画了一次图像,因为动画线程的初始化过程,即init()方法是先被调用的。下列代码(简称“C3”代码)展示了init()方法使用MediaTracker类对象来实现跟踪图像的载入,代码如下:
public void init(){
tracker=new MidiaTracker(this);
for(int i=1;i<=10;i++){
image[i-1]=getImage(getCodeBase(),"image"+i+".gif");
//用MediaTracker类对象的addImage()方法跟踪图像的载入
tracker.addImage(images[i-1],0);
}
......
}
程序的进一步完善
在“C2”代码的重载update()方法中加入下列if语句,从而对MediaTracker类对象的图像跟踪方法做出判断,if语句如下:
if(!tracker.checkAll()){
//如果图像还没有装载完毕,则仅清除背景,同时输出一个状态
g.clearRect(0,0,d.width,d.height);
g.drawString("Please wait...",0,d.height/2);
return;
}
在“C1”代码的stop()方法中加入两行代码,用以释放由双缓冲技术所占用的内存资源,这时stop()方法改为:
public void stop(){
//停止动画线程
animatorThread=null;
//释放用于双缓冲的内存资源
offGraphics=null;
offImage=null;
}
程序修改到此,还有一个小问题,就是动画线程启动后,第一幅图像有时仍有残留痕迹,而不是随着图像的更新而完全擦除掉。如果想解决此问题,只要将“C2”代码中最后的for()循环和g.drawImage()方法改为如下代码就可以了。
for(int i=0;i<10;i++){
offGraphics.drawImage(images[frameNumber%10],
,frameNumber*5%(d.width),i*d.height/10,this);
}
g.drawImage(offImage,0,0,this);
保持恒定的帧速度
为了使用户观看动画时没有闪烁感,至少需要达到每秒12帧的速度。更高的帧速度会产生更平滑的动画。通常,在动画显示的每两帧之间,调用线程的sleep()方法休眠一个固定的
已赞过 已踩过<
你对这个回答的评价是?
评论 收起
收起 1条折叠回答
推荐律师服务: 若未解决您的问题,请您详细描述您的问题,通过百度律临进行免费专业咨询

为你推荐:

下载百度知道APP,抢鲜体验
使用百度知道APP,立即抢鲜体验。你的手机镜头里或许有别人想知道的答案。
扫描二维码下载
×

类别

我们会通过消息、邮箱等方式尽快将举报结果通知您。

说明

0/200

提交
取消

辅 助

模 式