看到有些朋友关心窗口模式的实现,特根据个人实践整理代码出来. 窗口模式前表面的操作范围是整个桌面区域,GetWindowRect(hwnd,&rect);取得实际窗口范围,实际窗口范围包括了我们需要的绘制区和窗口控件,要取得窗口控件的大小,并修正rect. 裁剪只对Blt有效,对GDI装置无效,对表面锁定操作无效,因此所有绘图工作和表面锁定操作应该在后表面进行.
※预定义文档: 取出系统信息,实际客户区是窗口大小扣去窗体控件占用区 #define SafeRelease(lpx) if(lpx!=NULL){lpx->Release();lpx=NULL;} //释放Macro #define DD_Call(callcode) if(FAILED(callcode))return DD_FALSE //DX调用Macro
#define GSM_CAPTION GetSystemMetrics(SM_CYCAPTION) //标题栏 #define GSM_CXBORDER GetSystemMetrics(SM_CXFIXEDFRAME) //不可调边框 #define GSM_CYBORDER GetSystemMetrics(SM_CYFIXEDFRAME) #define GSM_CYMENU GetSystemMetrics(SM_CYMENU) //如果有菜单 #define MAXWIDTH 640 //游戏显示区大小 #define MAXHEIGHT 480
※WinMain: 不包含最大化和可调边框 hwnd=CreateWindow( __T(appname), __T(wndname), WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX|WS_OVERLAPPED, //注意这里 CW_USEDEFAULT, CW_USEDEFAULT, MAXWIDTH+(GSM_CXBORDER<<1), //注意这里 MAXHEIGHT+GSM_CAPTION+(GSM_CYBORDER<<1), //注意这里 GetDesktopWindow(), NULL, hinst, NULL );
※初始化: 主表面lpddsurmain/ddsdmain,次表面lpddsurback/ddsdback 次表面大小符合游戏区大小 DD_Call(DirectDrawCreateEx(NULL,(void**)&lpdd,IID_IDirectDraw7,NULL)); DD_Call(lpdd->SetCooperativeLevel(hwnd,DDSCL_NORMAL)); //注意这里 DD_Call(MainSurface(hwnd,&lpdd,&lpddsurmain,&ddsdmain)); DD_Call(BackSurface(MAXWIDTH,MAXHEIGHT,&lpdd,&lpddsurback,&ddsdback)); //注意这里
※主表面函数: HRESULT MainSurface(HWND hwnd,LPDIRECTDRAW7* lplpdd,LPDIRECTDRAWSURFACE7* lplpddsur,DDSURFACEDESC2* lpddsd) { memset(lpddsd,0,sizeof(DDSURFACEDESC2)); lpddsd->dwSize=sizeof(DDSURFACEDESC2); lpddsd->dwFlags=DDSD_CAPS; lpddsd->ddsCaps.dwCaps=DDSCAPS_PRIMARYSURFACE; DD_Call((*lplpdd)->CreateSurface(lpddsd,&(*lplpddsur),NULL)); LPDIRECTDRAWCLIPPER lpclip; DD_Call((*lplpdd)->CreateClipper(NULL,&lpclip,NULL)); DD_Call((*lplpddsur)->SetClipper(lpclip)); DD_Call(lpclip->SetHWnd(NULL,hwnd)); //注意这里 SafeRelease(lpclip); return DD_OK; } ※次表面函数: HRESULT BackSurface(UINT nWidth,UINT nHeight,LPDIRECTDRAW7* lplpdd,LPDIRECTDRAWSURFACE7* lplpddsur,DDSURFACEDESC2* lpddsd) { memset(lpddsd,0,sizeof(DDSURFACEDESC2)); lpddsd->dwSize=sizeof(DDSURFACEDESC2); lpddsd->dwFlags=DDSD_CAPS|DDSD_WIDTH|DDSD_HEIGHT; lpddsd->ddsCaps.dwCaps=DDSCAPS_OFFSCREENPLAIN|DDSCAPS_VIDEOMEMORY|DDSCAPS_LOCALVIDMEM; lpddsd->dwWidth=nWidth; lpddsd->dwHeight=nHeight; DD_Call((*lplpdd)->CreateSurface(lpddsd,lplpddsur,NULL)); return DD_OK; }
※游戏循环体:
RECT rect; //这个是主表面的区域 GetWindowRect(hwnd,&rect); //取得整个窗口区域 rect.left+=GSM_CXBORDER; //修正到主表面区域 rect.top+=GSM_CAPTION+GSM_CYBORDER; rect.right-=GSM_CXBORDER; rect.bottom-=GSM_CYBORDER; RECT rectback={0,0,ddsdback.dwWidth,ddsdback.dwHeight}; //这个是次表面的区域
........ //实际在次表面绘图操作
HDC hdc; DD_Call(lpddsurback->GetDC(&hdc)); TCHAR strbuf[55]; _stprintf(strbuf,"%s",__T("Just A Test!")); TextOut(hdc,0,0,strbuf,(int)_tcslen(__T("Just A Test!"))); DD_Call(lpddsurback->ReleaseDC(hdc));
DD_Call(lpddsurmain->Blt(&rect,lpddsurback,&rectback,DDBLT_WAIT,NULL)); 一切操作在次表面进行,完成后全部Blt到主表面.
|