CSDN博客

img myling

24小时学DX之第三小时——双缓冲

发表于2004/9/14 22:23:00  1802人阅读

#include "stdafx.h"
#define INITGUID
#include

#define SafeRelease(x) if (x) { x->Release(); x=NULL; }
#define IMAGE_COUNT 6 //图片数目

?

char file_names[IMAGE_COUNT][256] = {??
?"E:/CJD/素材/pic/魔兽/1.bmp",
?"E:/CJD/素材/pic/魔兽/2.bmp",
?"E:/CJD/素材/pic/魔兽/3.bmp",
?"E:/CJD/素材/pic/魔兽/4.bmp",
?"E:/CJD/素材/pic/魔兽/5.bmp",
?"E:/CJD/素材/pic/魔兽/6.bmp"
};//图片列表


//函数声明
BOOL InitWindow( HINSTANCE hInstance, int nCmdShow );
LRESULT CALLBACK WinProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam );
int cur_image=0;//当前图片号

//变量说明
HWND hWnd; //窗口句柄

/*=============================================================================
LPDIRECTDRAW7和LPDIRECTDRAWSURFACE7类型(7是版本号)是在ddraw.h头文件里预定义的,
指向IDirectDraw7和IDirectDrawSurface7类型的长型指针(前面加的LP代表Long Point),
从后面用的是"->"而不是"."也可以看出这一点。

DD是DirectDraw的缩写,DDS是DirectDrawSurface的缩写,
因此习惯上我们把变量名起为lpDD和lpDDSXXX。


虽然VC.net自带的DirectX SDK是8.1版的,
但是由于Microsoft从DirectX 8.0起停止了对DirectDraw的更新,
所以DirectDraw目前的最新版本还是7.0。
===============================================================================*/

LPDIRECTDRAW7 lpDD=NULL;// DirectDraw对象的指针
LPDIRECTDRAWSURFACE7 lpDDSPrimary=NULL;// DirectDraw主页面的指针
LPDIRECTDRAWSURFACE7 lpDDSBuffer=NULL;// DirectDraw后台缓存页面的指针
LPDIRECTDRAWSURFACE7 lpSlides[IMAGE_COUNT];//图片存储空间

RECT rect;

?

void MakeRect (int left, int top, int right, int bottom)
{
?rect.bottom = bottom;
?rect.left = left;
?rect.right = right;
?rect.top = top;
}


LPDIRECTDRAWSURFACE7 bitmap_surface(LPCTSTR file_name)
{
?HDC hdc;
?HBITMAP bit;
?LPDIRECTDRAWSURFACE7 surf;
?//载入BMP
?bit=(HBITMAP) LoadImage(NULL,file_name,IMAGE_BITMAP,0,0,
??LR_DEFAULTSIZE|LR_LOADFROMFILE);
?if (!bit)//载入失败,返回NULL
??return NULL;
?//获得BMP尺寸
?BITMAP bitmap;
?GetObject(bit, sizeof(BITMAP), &bitmap );
?//保存BMP尺寸
?int surf_width = bitmap.bmWidth;
?int surf_height = bitmap.bmHeight;

?//创建页面
?HRESULT ddrval;
?DDSURFACEDESC2 ddsd;
?//清空DDSURFACEDESC2结构
?ZeroMemory(&ddsd,sizeof(ddsd));
?ddsd.dwSize = sizeof(DDSURFACEDESC2);
?//设置填充标示
?ddsd.dwFlags = DDSD_CAPS|DDSD_WIDTH|DDSD_HEIGHT;
?//离屏页面
?ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN|DDSCAPS_SYSTEMMEMORY;
?//指定宽高
?ddsd.dwWidth =1024;
?ddsd.dwHeight =768;
?//创建离屏页面
?ddrval=lpDD->CreateSurface(&ddsd,&surf,NULL);
?if (ddrval!=DD_OK) {
??//失败的话,释放BMP
??DeleteObject(bit);
??return NULL;
?}
?else //创建成功
?{
??//取得DC
??surf->GetDC(&hdc);
??//与DC取得一致
??HDC bit_dc=CreateCompatibleDC(hdc);
??//
??SelectObject(bit_dc,bit);
??//传送bmp
??BitBlt(hdc,0,0,surf_width,surf_height,bit_dc,0,0,SRCCOPY);
??//释放DC
??surf->ReleaseDC(hdc);
??DeleteDC(bit_dc);
?}
?//释放BMP
?DeleteObject(bit);
?//返回离屏页面指针
?return surf;
}


//------ Function to Draw a Slide ------//
void draw_slide()
{
?//确定我们取得了图片
?if (!lpSlides[cur_image]) {
??lpSlides[cur_image]=bitmap_surface(file_names[cur_image]);
??if (!lpSlides[cur_image])
???return;
?}
?//开始在屏幕上画
?//DrawImage();
?MakeRect (0,0,1024,768);//设定传送rect
?//传送图像
?/* Bltfast()就可以了。它的原形是:
?HRESULT BltFast(
?DWORD dwX,
?DWORD dwY,//图像将被传送到目标页面何处。
?LPDIRECTDRAWSURFACE lpDDSrcSurface,//LPDIRECTDRAWSURFACE lpDDSrcSurface,//
?LPRECT lpSrcRect,//一个 RECT (Rectangle,即矩形)结构的地址,指明源页面上将被传送的区域。
???????????????????? //如果该参数是 NULL, 整个源页面将被使用。
?DWORD dwTrans??? //指定传送类型。有如下几种:
????????????????? //DDBLTFAST_NOCOLORKEY
????? //指定进行一次普通的复制,不带透明成分。
????? //DDBLTFAST_SRCCOLORKEY?
????? //指定进行一次带透明色的图像传送,使用源页面的透明色。
????? //DDBLTFAST_WAIT?
????? //如果图像传送器正忙,不断重试直到图像传送器准备好并传送好时才返回。
????? //一般都使用这个参数。
?);
?*/
?lpDDSBuffer->BltFast(0,0,lpSlides[cur_image],&rect,DDBLTFAST_WAIT);
?/*
?? 注意到了没有?在这个例子中,我们将有3个页面,
?? 就是主页面,用来换页的后台缓存页面以及用来存放图片的离屏页面
?? 创建完主页面和后台缓存页面,双缓存就很容易实现了,
?? 主要的差别就是图像要传送到后台缓存页面中进行而不是在主页面,
?? 当一桢完成后,主页面调用Flip命令即可。
????? Flip命令引起主页面与后台缓存页面的交换,但是这个动作不是马上进行,
?? 而是等到下次刷新周期的空白期才发生:)

?? Flip函数原型如下:
?? HRESULT Flip(
?? LPDIRECTDRAWSURFACE lpDDSurface,
???????? //换页链中另一个页面的 IDirectDrawSurface7接口的地址,
???????? //代表换页操作的目标页面。这个页面必须是换页链中的一员。
???????? //该参数缺省值是 NULL, 在这种情况下,
???????? //DirectDraw 从换页链中按照前后隶属关系依次换页。

?? DWORD dwFlags//换页的标志选项,常用DDFLIP_WAIT,同BltFast中的DDBLTFAST_WAIT差不多。
?? );
????? 一般我们这样即可换页:
?? lpDDSPrimary->Flip(NULL,DDFLIP_WAIT);
?*/
?lpDDSPrimary->Flip(NULL,DDFLIP_WAIT);//换页
?// 换图片,准备画下一张,并判断图片索引,形成循环
?int next_slide=(cur_image>=IMAGE_COUNT-1) ? 0 : cur_image+1;
?if (!lpSlides[next_slide])
??lpSlides[next_slide]=bitmap_surface(file_names[next_slide]);
?int prev_slide=(cur_image<1) ? IMAGE_COUNT-1 : cur_image-1;
?if (!lpSlides[prev_slide])
??lpSlides[prev_slide]=bitmap_surface(file_names[prev_slide]);
}

//************************************************************
//函数:InitDDraw()
//功能:DX初始化函数
//************************************************************
BOOL InitDDraw()
{
?DDSURFACEDESC2 ddsd; // DirectDraw的页面描述
? /*
??? 在一切工作之前,记得加入两个lib文件
??? 在菜单Project-Setting下的Link页的Object/Library Modules栏里加入
??? "ddraw.lib"和"dxguid.lib" ,后者可以不加入,而用#define INITGUID代替
??? PS:最好选择setting for All Configurations
??? 这样会在 debug 和 release 下都有效
? */
? /*
??? 如果要使用DirectDraw,必须创建一个DirectDraw对象,它是DirectDraw接口的核心。
??? 用DirectDrawCreateEx( )函数可以创建DirectDraw对象,
??? DirectDrawCreateEx( )函数是在ddraw.h中定义的,它的原型如下:

?????? HRESULT WINAPI DirectDrawCreateEx(
? ?????? GUID FAR *lpGUID, //指向DirectDraw接口的GUID的指针,NULL表示采用默认(当前)
? ?????? LPVOID *lplpDD,?? //用来接受初始化的DirectDraw对象的地址
? ?????? REFIID iid,?????? //IID_IDirectDraw7,当前版本
? ?????? IUnknown FAR *pUnkOuter? //NULL? 保留
??? );
?????? 所有的DirectDraw函数的返回值都是HRESULT类型,它是一个32位的值。
??? 函数调用成功用 "DD_OK"表示,
??? 所有的错误值标志开头都为"DDERR",如:
??? DDERR_DIRECTDRAWALREADYCREATED
??? DDERR_OUTOFMEMORY
? */
? //这里使用了 if (xxx!=DD_OK) 的方法进行错误检测,这是最常用的方法
???? if (DirectDrawCreateEx(NULL,(LPVOID*)&lpDD, IID_IDirectDraw7, NULL)!= DD_OK)
??return FALSE; //创建DirectDraw对象
? /*
? DirectDrawCreate函数调用成功后,lpDD已经指向了一个DirectDraw对象,
? 它是整个DirectDraw接口的最高层领导,以后的步骤都是在它的控制之下。
? 我们用IDirectDraw7::SetCooperativeLevel( )来设置DirectDraw程序对系统的控制级。
? 它的原型如下:
? HRESULT SetCooperativeLevel (HWND hWnd, DWORD dwFlags )
? 第一个参数是窗口句柄,我们给它hWnd,使DirectDraw对象与主窗口联系上。
???? 第二个参数是控制级标志。这里使用DDSCL_EXCLUSIVE|DDSCL_FULLSCREEN,
? 表示我们期望DirectDraw以独占和全屏方式工作。
????
? 控制级描述了DirectDraw是怎样与显示设备及系统作用的。
? DirectDraw控制级一般被用来决定应用程序是运行于全屏模式(必须与独占模式同时使用),
? 还是运行于窗口模式。但DirectDraw的控制级还可设置如下两项:
???? (1)允许按Ctrl+Alt+Del重新启动(仅用于独占模式,为DDSCL_ALLOWREBOOT)。
???? (2)不允许对DirectDraw应用程序最小化或还原 (DDSCL_NOWINDOWCHANGES)。
????
? 普通的控制级(DDSCL_NORMAL)表明我们的DirectDraw应用程序将以窗口的形式运行。
? 在这种控制级下,我们将不能改变显示器分辨率,或进行换页操作(这是一个重要的操作)。
? 除此之外,我们也不能够调用那些会对显存产生激烈反应的函数,如Lock()。
???? 当应用程序为全屏并且独占的控制级时,我们就可以充分的利用硬件资源了。
? 此时其它应用程序仍可创建页面、使用DirectDraw或GDI的函数,只是无法改变显示模式。
? */
???? if (lpDD->SetCooperativeLevel(hWnd,DDSCL_FULLSCREEN|DDSCL_EXCLUSIVE|
?? DDSCL_ALLOWREBOOT)!= DD_OK)
??????? return FALSE; //设置DirectDraw控制级
? /*
? 下一步我们使用IDirectDraw7::SetDisplayMode( )来设置显示模式,其原形为:
? HRESULT SetDisplayMode(
? DWORD dwWidth,
? DWORD dwHeight, //dwWidth and dwHeight用来设置显示模式的宽度和高度。
? DWORD dwBPP, //dwBPP用来设置显示模式的颜色位数。
? DWORD dwRefreshRate, //dwRefreshRate设置屏幕的刷新率,0为使用默认值。
? DWORD dwFlags //dwFlags现在唯一有效的值是DDSDM_STANDARDVGAMODE,
??????????????? //此值是如果在320×240×8模式下给VGA Mode 13用的,
?????? //我们通常把它设为0即可
?????
? );
? PS:用IDirectDraw7接口的EnumDisplayModes()函数
???? 可以取得当前可用的模式列表。具体用法请查阅DX的帮助
? */
???? if (lpDD->SetDisplayMode(1024,768,32,85,0)!= DD_OK)
??????? return FALSE; //设置显示模式

??? /*
?? 创建一个页面之前,首先需要填充一个DDSURFACEDESC2结构,
?? 它是DirectDraw Surface Description的缩写,意思是DirectDraw的页面描述。
?? 它的结构非常庞大,这里只能作一个最简单的介绍。
?? 要注意的是在填充此结构前一定要将其清空!
?*/

?//开始创建主页面,先清空页面描述
?ZeroMemory(&ddsd, sizeof(ddsd));

?//填充页面描述
??? ddsd.dwSize = sizeof(ddsd);//给dwSize页面描述的大小
?ddsd.dwFlags = DDSD_CAPS|DDSD_BACKBUFFERCOUNT;//有后台缓存
??? ddsd.ddsCaps.dwCaps =DDSCAPS_PRIMARYSURFACE|DDSCAPS_FLIP|
??DDSCAPS_COMPLEX;//主页面,有后台缓存,有换页链
?ddsd.dwBackBufferCount = 1;//后台缓存数为1
??? /*
?CreateSurface( )函数的第一个参数是被填充了页面信息的DDSURFACEDESC2结构的地址,
?此处为&ddsd;
?第二个参数是接收主页面指针的地址,此处为&lpDDSPrimary;
?第三个参数现在必须为NULL,为该函数所保留。
??? 如果函数调用成功,lpDDSPrimary将成为一个合法的主页面对象。
?由于在前面已经设置了该程序的工作模式为独占和全屏,
?所以,此时主页面所代表的实际上是我们的整个显示屏幕。
?在主页面上所绘制的图形将立即反映到我们的显示屏幕上。
?*/
???? if (lpDD->CreateSurface(&ddsd,&lpDDSPrimary, NULL) != DD_OK )
????? ?return FALSE; //创建主页面

??? ddsd.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER; //后台缓存页面
??? /*
?在创建主页面的时候,附加页面(这里是后台缓存页面)也同时创建了,
?但是他们没有被你调用的CreateSurface()所返回
?你要通过GetAttachedSurface()函数来取得该接口,
?*/
??? if (lpDDSPrimary->GetAttachedSurface(&ddsd.ddsCaps,&lpDDSBuffer)!=DD_OK)
??return FALSE; //创建后台缓存页面
?lpSlides[0]=bitmap_surface(file_names[0]);
?if (!lpSlides[0])
??return FALSE;
?//画图
?draw_slide();
?return TRUE;
}


void Cleanup(void)
{
?//释放接口
?for (int i=0;i??SafeRelease(lpSlides[i]);

?SafeRelease(lpDDSBuffer);
?SafeRelease(lpDDSPrimary);//删除主页面
?SafeRelease(lpDD);//删除ddraw对象
?//ps:与创建的顺序正好相反
}

//************************************************************
//函数:WinMain()
//功能:Windows程序入口函数。创建主窗口,处理消息循环
//************************************************************
int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
? if ( !InitWindow(hInstance, nCmdShow ) ) return FALSE; //创建主窗口
? //如果创建不成功则返回FALSE并同时退出程序
? MSG msg;
? //进入消息循环:
? for(;;)
? {
?if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
?{
??if ( msg.message==WM_QUIT) break;
??TranslateMessage(&msg);
??DispatchMessage(&msg);
?}
? }
?? return msg.wParam;
}

//************************************************************
//函数:InitWindow( )
//功能:创建窗口
//************************************************************

static BOOL InitWindow( HINSTANCE hInstance, int nCmdShow )
{
?//定义窗口风格:
?WNDCLASS wc;
?wc.style =NULL;//窗体类的风格
?wc.lpfnWndProc = (WNDPROC)WinProc; //窗口消息处理函数的指针
?wc.cbClsExtra = 0;//分配给窗口类结构之后的额外字节数
?wc.cbWndExtra = 0;//分配给窗口实例之后的额外字节数
?wc.hInstance = hInstance;//窗口所对应的应用程序的句柄
?wc.hIcon = NULL;//窗口的图标
?wc.hCursor = LoadCursor(NULL, IDC_ARROW); //窗口的鼠标
?wc.hbrBackground = CreateSolidBrush (RGB(0, 0, 0)); //黑色的背景
?wc.lpszMenuName = NULL;//MAKEINTRESOURCE(IDR_MENU);//窗口的菜单资源名称
?wc.lpszClassName = "My_Test";//窗口类的名称
?RegisterClass(&wc);//注册窗口

??? //按所给参数创造窗口
???? hWnd = CreateWindow("My_Test",//创建窗口所用的窗口类的名称
?? "24小时学DX_第2个小时",//窗口标题
?? WS_POPUP|WS_MAXIMIZE,//窗口风格,定义为普通型
?? 0,0,//窗口位置的x,y坐标
?? 1024,768,//窗口的宽度,高度
?? NULL,//父窗口句柄
?? NULL,//菜单句柄
?? hInstance,//应用程序句柄
?? NULL);
?if(!hWnd ) return FALSE;
?ShowWindow(hWnd,nCmdShow);//显示窗口
?UpdateWindow(hWnd);//刷新窗口
?InitDDraw();//初始化DX
?return TRUE;
}

//************************************************************
//函数:WinProc( )
//功能:处理窗口消息
//************************************************************

LRESULT CALLBACK WinProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
?switch(message)
?{
?case WM_COMMAND:
?? break;
?case WM_KEYDOWN://击键消息
??switch(wParam)
??{
??case VK_ESCAPE:
???PostMessage(hWnd, WM_CLOSE, 0, 0);//给窗口发送WM_CLOSE消息
???break;
??//左右被按下了,改变图片,重画
??case VK_LEFT:
???cur_image--;
???if (cur_image<0)
????cur_image=IMAGE_COUNT-1;
???draw_slide();
???break;
??case VK_RIGHT:
???cur_image++;
???if (cur_image>IMAGE_COUNT-1)
????cur_image=0;
???draw_slide();
???break;
??}
??break;
?case WM_DESTROY: //如果窗口被人释放…
??Cleanup();
???? PostQuitMessage(0); //给窗口发送WM_QUIT消息
??break;
??? default://调用缺省消息处理过程
??return DefWindowProc(hWnd, message, wParam, lParam);?
?}?
?return 0;
}

?

?

阅读全文
0 0

相关文章推荐

img
取 消
img