地址:
QUOTE
FreeMind
目 录
1 图形编程基本常识
2 DirectDraw的简单概念
3 DirectDraw的初始化代码
4 高彩下的画点函数
--------------------------------------------------------------------------------
本来是应该从256色模式下的编程谈起的,但现在大家写游戏都喜欢用高彩模式,那我就顺因大势所趋,直接讲讲高彩模式下的编程吧。
1、图形编程基本常识
如果你有DOS时代在13H模式下的编程经验,你就肯定知道,要想在屏幕上显示图象则必须要同显示缓冲区打交道。而且在具体开始编程时,还必须知道显示缓冲区的地址和表示格式。对于初学者来说,可以简单地把显示缓冲区看作一块内存,往这块内存里填数据就能在屏幕上的对应位置显示出该数据表示的像素来。
为了便于理解,就先拿DOS下最简单的13H模式来举个例子。在这个模式下,显示缓冲区的起始地址是固定的(a000:0000H),而且整个显示缓冲区地址是连续线性排列的,每个字节用来表示一个像素。比如你要在屏幕(以屏幕 左上角为坐标原点,像素为单位)坐标[2,3]处画一个颜色号为138的像素,已知屏幕宽度是320,用公式Addr=y*320+x可以计算出地址,得到Addr=3*320+2=962(03c2H),因此在a000:03c2H处写一个字节138(8aH)就能达到目的了。
2、DirectDraw的简单概念
在Windows系统下,由于是处于保护模式状态,显示缓冲区的起始地址并不是固定的,我们将通过DirectDraw来得到显示缓冲区的起始地址。为了更容易地讲清楚这个问题,我们先来侃侃DirectDraw的一些基本概念和结构。
微软是按COM来设计DirectX的,每个DirectDraw对象代表一个显示设备,它提供了一些子对象和相应的各种界面来完成具体功能。
DirectDrawSurface对象可以看成是一块线性的显存,每个表面(Surface)对象提供了对应起始地址的指针,我们的图像数据就是往表面上进行读写的。表面又分了几种,直接在屏幕上见到的那个是主表面(Primary Surface),还有不能直接看到的离屏表面(Off-screen Surface)及覆盖表面(Overlay Surface),如果显存不够,这种不能直接看到的Surface可以在内存中创建,不过由于不能利用硬件的加速能力,性能要稍微受些影响。
DirectDrawPalette对象是为256色准备的,对高彩模式编程时完全用不上,因此在这里就暂且不去管它了。
另外,由于窗口裁剪非常容易编程实现,那个用来对窗口进行裁剪时使用的DirectDrawClipper对象,在这里也不去多谈它了。知道了DirectDrawSurface对象以后,下面该说说DirectDraw的一般工作流程和双缓冲显示原理了。
创建一个DirectDraw对象
设置协作模式
设置显示模式
创建主表面和一个后台表面
往后台表面写入内容
通过Flip(页面翻转)交换主表面和后台表面,从而显示出写入后台表面的内容
在这个工作流程中,最后两步完成了显示一帧的工作,以每秒n次的频率反复进行。
这种创建两个表面,在隐藏的表面中先画好内容,再通过Flip显示出来的方法称为双缓冲。如果不这样做,直接画到可见的主表面上面,而省去了后台表面及Flip过程,你会发现屏幕上会有明显的闪烁,这就是为什么要采用双缓冲的原因。
这么简单说说,可能是无法把DirectDraw编程讲得很清楚的,还是来看看实际的DirectDraw的初始化代码吧。
***************************************
在原来的基础上,代码更改之后的:
#include "stdafx.h"
#include
#include
#include
#include
#pragma comment(lib, "strmiids.lib")
#pragma comment(lib, "ddraw.lib")
#define COPYRIGHT "Fan YiPeng"
#define HOMEPAGE ""
#define TITLE "EXAMPLE"
#define NAME "DDRAW EXAMPLE"
// -------------------------------------
#define RELEASE(x) if (x != NULL) { x -> Release(); x = NULL; }
// -------------------------------------
HWND hWndCopy;
BOOL bActive;
LPDIRECTDRAW lpDD = NULL; // DirectDraw object
LPDIRECTDRAWSURFACE lpDDSPrimary = NULL; // DirectDraw primary surface
LPDIRECTDRAWSURFACE lpDDSBack = NULL; // DirectDraw back surface
int ScreenW = 320, ScreenH = 240, BitDepth = 16; // 屏幕模式参数
//int ScreenW = GetSystemMetrics(SM_CXSCREEN), ScreenH = GetSystemMetrics(SM_CYSCREEN), BitDepth = 16; // 屏幕模式参数
BOOL HighColor555Flag; // 555模式标志
// 初始化DDraw
BOOL InitDDraw(void)
{
DDSURFACEDESC ddsd;
DDSCAPS ddscaps;
DDPIXELFORMAT ddpf;
HRESULT ddrval;
// 创建DirectDraw对象
ddrval = DirectDrawCreate(NULL, &lpDD, NULL);
if(ddrval != DD_OK)
{
return FALSE;
}
// 设置为独占模式
//ddrval = lpDD->SetCooperativeLevel(hWndCopy, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN);
ddrval = lpDD->SetCooperativeLevel(hWndCopy, DDSCL_NORMAL);
if(ddrval != DD_OK)
{
return FALSE;
}
// 设置显示模式
//ddrval = lpDD->SetDisplayMode(ScreenW, ScreenH, BitDepth);
//if(ddrval != DD_OK)
//{
//return FALSE;
//}
// 创建带一个back buffer的primary surface
ZeroMemory(&ddsd, sizeof(DDSURFACEDESC));
ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_CAPS;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
ddrval = lpDD->CreateSurface(&ddsd, &lpDDSPrimary, NULL);
if(ddrval != DD_OK)
{
// Create the primary surface failed
return FALSE;
}
ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
ddsd.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER;
ddsd.dwWidth = ScreenW;
ddsd.dwHeight = ScreenH;
ddrval = lpDD->CreateSurface(&ddsd, &lpDDSBack, NULL);
if(ddrval != DD_OK)
{
// Create the primary surface failed
return FALSE;
}
// 取得像素格式
ddpf.dwSize = sizeof(ddpf);
ddrval = lpDDSPrimary->GetPixelFormat(&ddpf);
if(ddrval != DD_OK)
{
return FALSE;
}
WORD GBitMask = (WORD)ddpf.dwGBitMask;
// 分析像素格式
if(GBitMask == 0x03E0)
{
HighColor555Flag = TRUE;
}
else if(GBitMask == 0x07E0)
{
HighColor555Flag = FALSE;
}
else
{
// The Video Card doesn't support 5:5:5 or 5:6:5 HighColor
// return FALSE;
}
return TRUE;
}
// 恢复丢失的DDraw对象
HRESULT RestoreDDraw(void)
{
HRESULT ddrval;
if(lpDDSPrimary)
{
ddrval = lpDDSPrimary->Restore();
if(ddrval==DD_OK)
{
if(lpDDSBack)
{
ddrval = lpDDSBack->Restore();
}
}
}
return ddrval;
}
// 释放DDraw对象
void ReleaseObjects(void)
{
RELEASE(lpDDSBack);
RELEASE(lpDDSPrimary);
RELEASE(lpDD);
}
// 产生初始化失败消息窗口
void InitFail(LPSTR msg)
{
ReleaseObjects();
MessageBox(hWndCopy, msg, "ERROR", MB_OK);
DestroyWindow(hWndCopy);
}
// 检查程序是否已经在运行
BOOL AlreadyRun(void)
{
HWND FirsthWnd, FirstChildhWnd;
if((FirsthWnd = FindWindow(NULL, TITLE)) != NULL)
{
FirstChildhWnd = GetLastActivePopup(FirsthWnd);
BringWindowToTop(FirsthWnd);
if(FirsthWnd != FirstChildhWnd)
{
BringWindowToTop(FirstChildhWnd);
}
ShowWindow(FirsthWnd, SW_SHOWNORMAL);
return TRUE;
}
return FALSE;
}
// Windows消息处理
long FAR PASCAL WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch(message)
{
case WM_ACTIVATEAPP:
bActive = wParam;
break;
case WM_CREATE:
break;
case WM_SETCURSOR:
SetCursor(NULL);
return TRUE;
case WM_KEYDOWN:
switch(wParam)
{
case VK_ESCAPE: // ESC键退出
DestroyWindow(hWnd);
return 0;
}
break;
case WM_DESTROY:
ReleaseObjects();
PostQuitMessage(0);
break;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
// 初始化
BOOL doInit(HINSTANCE hInstance, int nCmdShow)
{
HWND hwnd;
WNDCLASS wc;
// set up and register window class
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WindowProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH )GetStockObject(BLACK_BRUSH);
wc.lpszMenuName = NAME;
wc.lpszClassName = NAME;
RegisterClass(&wc);
// create a window
hwnd = CreateWindowEx(WS_EX_TOPMOST,
NAME,
TITLE,
WS_POPUP,
0,
0,
ScreenW,
ScreenH,
//GetSystemMetrics(SM_CXSCREEN),
//GetSystemMetrics(SM_CYSCREEN),
NULL,
NULL,
hInstance,
NULL);
if(!hwnd) return FALSE;
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
hWndCopy = hwnd;
if(!InitDDraw()) // Init DDraw Object
{
InitFail("Init DirectDraw Fail");
}
return TRUE;
}
void DrawMap()
{
DDSURFACEDESC ddsd;
LPDWORD lpSurface;
long Pitch;
LRESULT ddrval;
ddsd.dwSize = sizeof(ddsd);
// 锁定Back Surface,DDraw中的Surface必须先锁定,然后才能访问
while((ddrval=lpDDSBack->Lock(NULL, &ddsd, 0, NULL))==DDERR_WASSTILLDRAWING);
if(ddrval == DD_OK)
{
// 起始地址指针,注意其类型是指向WORD的指针
lpSurface = (LPDWORD)ddsd.lpSurface;
// 我们关心的Pitch,其值是字节(BYTE)数
Pitch = ddsd.lPitch;
// 作了以上处理以后,像素坐标到地址的换算公式为
// (LPWORD)addr = lpSurface + y*Pitch + x;
// 画满屏的黑点
// Pitch = ddsd.dwWidth*4;
memset(lpSurface, 0xFF000000, ddsd.dwHeight * Pitch);
//ZeroMemory(lpSurface, ddsd.dwHeight*ddsd.dwWidth);
// 在第10行画一条白线
{
int x, y;
y=10;
for(x=0; x {
*(lpSurface + y*Pitch + x)=0xFFFFFFFF; // 0x7FFF (555)
// 0xFFFF=11111 111111 11111 0x7FFF=0 11111 11111 11111
}
}
// 在[10,200]处画一个红点
*(lpSurface + 20*Pitch + 100)=0xFFFF0000; // 0x7C00 (555)
// 0xF800=11111 000000 00000 0x7FFF=0 11111 00000 00000
// 在[20,300]处画一个绿点
*(lpSurface + 30*Pitch + 200)=0xFF00FF00; // 0x03E0 (555)
// 0x07E0=00000 111111 00000 0x03E0=0 00000 11111 00000
// 在[30,400]处画一个蓝点
*(lpSurface + 40*Pitch + 100)=0xFF0000FF; // 0x001F (555)
// 0x001F=00000 000000 11111 0x001F=0 00000 00000 11111
// 对Back Surface解锁,Surface访问后必须要解锁
lpDDSBack->Unlock(NULL);
RECT rcWindow;
GetWindowRect(hWndCopy, &rcWindow);
lpDDSPrimary->Blt(&rcWindow, lpDDSBack, NULL, DDBLT_WAIT, NULL);
char szTitle[256];
sprintf(szTitle, _T("result = %d\n"), Pitch);
//MessageBox(hWndCopy, szTitle, NULL, 0);
}
}
// WinMain函数
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
MSG msg;
HRESULT ddrval;
if(!doInit(hInstance,nCmdShow))
return FALSE;
// main loop
while(TRUE)
{
if(PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
{
// 消息队列处理
if (!GetMessage(&msg, NULL, 0, 0)) return msg.wParam;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
if(bActive)
{
// 以后在这里加入实际的图形代码
// ...
DrawMap();
// Flip surface(页面翻转)
while(TRUE)
{
ddrval = lpDDSPrimary->Flip(NULL, 0);
if(ddrval == DD_OK)
{
break;
}
if(ddrval == DDERR_SURFACELOST)
{
ddrval = RestoreDDraw();
if(ddrval != DD_OK)
{
break;
}
}
if(ddrval != DDERR_WASSTILLDRAWING)
{
break;
}
}
}
else
{
// 当程序未被激活时等待
WaitMessage();
}
}
}
}