SetDisplayMode为设置视频模式的函数
HRESULT SetDisplayMode(DWORD dwWidth, // 屏幕宽 DWORD dwHeight, // 屏幕高 DWORD dwBPP, // 每个像素的位数, 8,16,24, 等. DWORD dwRefreshRate, // 刷新频率,0为默认 DWORD dwFlags); // 标记字,一般为 0 即可
视频模式 |
|||
Width |
Height |
BPP |
Mode X |
320 |
200 |
8 |
* |
320 |
240 |
8 |
* |
320 |
400 |
8 |
* |
512 |
512 |
8,16,24,32 |
|
640 |
480 |
8,16,24,32 |
|
800 |
600 |
8,16,24,32 |
|
1024 |
768 |
8,16,24,32 |
|
1280 |
1024 |
8,16,24,32 |
|
现在用色彩深度多数为8、16、24、32
当选择8位的色彩深度的时候,我们需要一个调色板。
先了解调色板数据结构:
typedef struct tagPALETTEENTRY{ BYTE peRed; // red component 8-bits BYTE peGreen; // green component 8-bits BYTE peBlue; // blue component 8-bits BYTE peFlags; // 把这个标记设置为 PC_NOCOLLAPSE } PALETTEENTRY;
要创建一个调色板只要定义下面这样的数组。
PALETTEENTRY palette[256];
然后填充这个数组,注意的是把peFlags设置为PC_NOCOLLAPSE,因为我们不想Win32/DirectX优化我们的调色板。
PALETTEENTRY palette[ 256 ]; // 调色板 // 初始化调色板数组 for ( int color = 1 ; color < 255 ; color ++ ) { // 随机填充RGB值 palette[color].peRed = rand() % 256 ; palette[color].peGreen = rand() % 256 ; palette[color].peBlue = rand() % 256 ; palette[color].peFlags = PC_NOCOLLAPSE; // 不优化调色板 } // 把0项填充为黑色 palette[ 0 ].peRed = 0 ; palette[ 0 ].peGreen = 0 ; palette[ 0 ].peBlue = 0 ; palette[ 0 ].peFlags = PC_NOCOLLAPSE; // 把255项填充为白色 palette[ 255 ].peRed = 255 ; palette[ 255 ].peGreen = 255 ; palette[ 255 ].peBlue = 255 ; palette[ 255 ].peFlags = PC_NOCOLLAPSE;
HRESULT CreatePalette( DWORD dwFlags, // 控制标记,见下面 LPPALETTEENTRY lpColorTable, // 调色板数据指针,见上面 LPDIRECTDRAWPALETTE FAR * lplpDDPalette, // 返回的调色板接口指针 IUnknown FAR * pUnkOuter); // 传NULL即可
CreatePalette()控制标记 |
|
值 |
描述 |
DDPCAPS_1BIT |
1位色彩。色彩表包含2项 |
DDPCAPS_2BIT |
2位色彩。色彩表包含4项 |
DDPCAPS_4BIT |
4位色彩。色彩表包含16项 |
DDPCAPS_8BIT |
8位色彩。色彩表包含256项.最常用 |
DDPCAPS_8BITENTRIES |
用于一个称为索引调色板的高级我改,适用于1、2和4位调色板,在此我们只要设置为否 |
DDPCAPS_ALPHA |
表示想关的PALETTEENTRY结构peFlags成员将被翻译成一个控制透明的8位alpha值。用这个标记创建 的调色板只配置能到一个用DDSCAPS_TEXTURE性能标记创建的D3D纹理表面 |
DDPCAPS_ALLOW256 |
表示这个调色板可以定义所有256个项。正常情况下,0项和255项分别相应地接收黑和白,并且在某些系统如NT上你在任何情况下都不能写这些项。然而,大多数情况下你不需要这个标记,因为为0项通常都是黑并且大多数调色板在255项为白的时候也能工作。 |
DDPCAPS_INITIALIZE |
用lpDDColorArray传递的色彩数组中的色彩初始化调色板。使得调色板数据能被传递并下载到硬件调色板 |
DDPCAPS_PRIMARYSURFACE |
这个调色板配置到主显示表面。改变调色板的色表会立即影响显示,除非DDPSETPAL_VSYNC是特定的值并受去拷的 |
DDPCAPS_VSYNC |
强制调色板更新并只能在垂直的空白期执行。最小色彩异常和闪烁。不过还示获完全支持。 |
上面搞那么多控制标记,但一般是下面组合。
DDPCAPS_8BIT | DDPCAPS_ALLOW256 | DDPCAPS_INITIALIZE
如果不考虑设置色彩项0和255可以忽略DDPCAPS_ALLOW256
如果CreatePalette()调用中没有传递调色板,可以忽略DDPCAPS_INITIALIZE
// 创建调色板对象 if (FAILED(lpdd -> CreatePalette(DDPCAPS_8BIT | DDPCAPS_ALLOW256 | DDPCAPS_INITIALIZE, palette, & lpddpal, NULL))) { return ( 0 ); }
有两种显示表面:主显示表面 和 从显示表面
主显示表面:直接对应于被视频卡光栅化的实际显存,且任何时候都是可见的。在任何DDraw程序里我们只能有一个主显示表面,它直接指向图像并常驻VRAM。
从显示表面:它可以是任意大小,可以驻留在VRAM或是系统内存,可以有任意多个。
创建一个表面只要再个步骤:
1.填充一个DDSURFACEDESC2数据结构,来描述创建的显示表面。
2.调用IDirectDraw7::CreateSurface()来创建显示表面。
/* * DDSURFACEDESC2 */ typedef struct _DDSURFACEDESC2 { DWORD dwSize; // size of the DDSURFACEDESC structure DWORD dwFlags; // determines what fields are valid DWORD dwHeight; // height of surface to be created DWORD dwWidth; // width of input surface union { LONG lPitch; // distance to start of next line (return value only) DWORD dwLinearSize; // Formless late-allocated optimized surface size } DUMMYUNIONNAMEN( 1 ); union { DWORD dwBackBufferCount; // number of back buffers requested DWORD dwDepth; // the depth if this is a volume texture } DUMMYUNIONNAMEN( 5 ); union { DWORD dwMipMapCount; // number of mip-map levels requestde // dwZBufferBitDepth removed, use ddpfPixelFormat one instead DWORD dwRefreshRate; // refresh rate (used when display mode is described) DWORD dwSrcVBHandle; // The source used in VB::Optimize } DUMMYUNIONNAMEN( 2 ); DWORD dwAlphaBitDepth; // depth of alpha buffer requested DWORD dwReserved; // reserved LPVOID lpSurface; // pointer to the associated surface memory union { DDCOLORKEY ddckCKDestOverlay; // color key for destination overlay use DWORD dwEmptyFaceColor; // Physical color for empty cubemap faces } DUMMYUNIONNAMEN( 3 ); DDCOLORKEY ddckCKDestBlt; // color key for destination blt use DDCOLORKEY ddckCKSrcOverlay; // color key for source overlay use DDCOLORKEY ddckCKSrcBlt; // color key for source blt use union { DDPIXELFORMAT ddpfPixelFormat; // pixel format description of the surface DWORD dwFVF; // vertex format description of vertex buffers } DUMMYUNIONNAMEN( 4 ); DDSCAPS2 ddsCaps; // direct draw surface capabilities DWORD dwTextureStage; // stage in multitexture cascade } DDSURFACEDESC2;
这个结构值成员比较多。我们只要了解粗体部分即可。
dwSize:本结构自身的大小。
dwFlags:指示DDraw会把有次数拨款哪个域中或从哪个域查询数据。见下表。
DDSURFACEDESC2结构中dwFlags域的可选标志 |
|
Value |
Description |
DDSD_ALPHABITDEPTH |
表明dwAlphaBitDepth 成员有效. |
DDSD_BACKBUFFERCOUNT |
表明dwBackBufferCount 成员有效. |
DDSD_CAPS |
表明ddsCaps 成员有效. |
DDSD_CKDESTBLT |
表明ddckCKDestBlt 成员有效. |
DDSD_CKDESTOVERLAY |
表明ddckCKDestOverlay 成员有效. |
DDSD_CKSRCBLT |
表明ddckCKSrcBlt 成员有效. |
DDSD_CKSRCOVERLAY |
表明ddckCKSrcOverlay 成员有效. |
DDSD_HEIGHT |
表明dwHeight 成员有效. |
DDSD_LINEARSIZE |
表明dwLinearSize 成员有效. |
DDSD_LPSURFACE |
表明lpSurface 成员有效. |
DDSD_MIPMAPCOUNT |
表明dwMipMapCount 成员有效. |
DDSD_PITCH |
表明lPitch 成员有效. |
DDSD_PIXELFORMAT |
表明ddpfPixelFormat 成员有效. |
DDSD_REFRESHRATE |
表明dwRefreshRate 成员有效. |
DDSD_TEXTURESTAGE |
表明dwTextureStage 成员有效. |
DDSD_WIDTH |
表明dwWidth 成员有效. |
dwHeight: 显示表面以像素计的宽度。
dwWidth:显示表面以像素计的高度。
lPitch:水平内存间距。它是该显式模式中每行上的字节数。注意:视VRAM的布局而定,lPitch可以是是任何值。因此当你倾向于逐行方总一个DDraw显示表面内存时,必须用lPitch来移到下一行,而不是用每像素字节数乘宽度。
dwBackBufferCount:后备缓冲或连锁于主显示表面的从属离屏切换缓冲的数目。
lpSurface:用于获取指向所创建的显示表面所驻留的实际内存的指针。
ddckCKDestBlt:目标色彩键。
ddckCKSrcBlt:源色彩键。
ddsCaps:返回所请求的显示表面的一些未在别处定义的属性。它也是一个结构体
typedef struct _DDSCAPS2 { DWORD dwCaps; // 见下表 DWORD dwCaps2; // 给3D内容伤脑筋 DWORD dwCaps3; // 保留值,不使用 DWORD dwCaps4; // 保留值,不使用 } DDSCAPS2, FAR* LPDDSCAPS2;
DDraw显示表面的功能控制设置 |
|
值 |
描述 |
DDSCAPS_BACKBUFFER |
表示该显示表面的是一个平面翻转结构的后备缓冲 |
DDSCAPS_COMPLEX |
表示正在描述一个复杂的显示表面,该表面拥有一个主表面缓冲和一或多个后备缓冲以生成翻转链 |
DDSCAPS_FLIP |
表示该显示表面是一个平台翻转结构的一部分。当这个功能标记传递给CreateSurface方法时,将会创建 一个前端缓冲和一或多个后备缓冲 |
DDSCAPS_LOCALVIDMEM |
表示该显示表面存在于真下的本地显存,而不是非本地显存中。如果指定该标记,DDSCAPS_VIDEOMEMORY也要指定。 |
DDSCAPS_MODEX |
表示该显示表面是一个320*320或320*240ModelX显示平面 |
DDSCAPS_NONLOCALVIDMEM |
表示该显示表面存在于非本地显存而非真正的本地显存中,如果批定了该标志DDSCAPS_VIDEOMEMORY不能要指定。 |
DDSCAPS_OFFSCREENPLAIN |
表示该显示表面将一个离屏表面,不是一个特殊的表面,如覆盖、纹理、z-缓冲、前端缓冲、后端缓冲或是alpha缓冲平面。通常用于图元精灵(Sprite) |
DDSCAPS_OWNDC |
表示该显示表面uqf会长期有一个设备上下文关联 |
DDSCAPS_PRIMARYSURFACE |
表示该显示表面是主显示表面,代表其时用户可见的内容 |
DDSCAPS_STANDARDVGAMODE |
表示该表面是一个标准的VGA平面,而且不是一个ModelX平面些标记不能与DDSCAPS_MODEX标记同时使用 |
DDSCAPS_SYSTEMMEMORY |
表示该显示表面内存从系统内存分配 |
DDSCAPS_VIDEOMEMORY |
表示该显示表面存在于显存中 |
// 初始化ddsd memset( & ddsd, 0 , sizeof (ddsd)); ddsd.dwSize = sizeof (ddsd); ddsd.dwFlags = DDSD_CAPS; ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; // 创建主显示表面标志 // 创建主显示表面 if (FAILED(lpdd -> CreateSurface( & ddsd, & lpddsprimary, NULL))) { return ( 0 ); }
// 把调色板和主显示表面关联起来 if (FAILED(lpddsprimary -> SetPalette(lpddpal))) { return ( 0 ); }
要访问任何显示表面—主显示表面、从显示表面等。必须 对内存加锁和解锁。没有任何保证 说VRAM会留在同样的地方。它可能是虚拟的,但对它上锁,它就会在锁住期间保持同样的地址空间以便控制。HRESULT Lock( LPRECT lpDestRect, // 要上锁的Rect,整个表面上锁传 NULLLPDDSURFACEDESC2 lpDDSurfaceDesc, // 传递一个空的LPDDSURFACEDESC2DWORD dwFlags, // 标志见下表HANDLE hEvent); // 传 NULL
Lock()方法的控制标记 |
|
值 |
描述 |
DDLOCK_READONLY |
表示被锁的显示表面的是只读的 |
DDLOCK_SURFACEMEMORYPTR |
表示将要返回一个指向特定RECT顶部的有效的内存指针,如果没有指定矩形,将会返回一个指向显示表面顶部的指针 |
DDLOCK_WAIT |
如果由于正在进行一个块传输操作而不能获得一个锁,该 方法会重试直到得到锁或是有另外的错误发生,如DDERR_SURFACEBUSY |
DDLOCK_WRITEONLY |
表示被锁的显示表面是可写的 |
// 把显示表面锁住 if (FAILED(lpddsprimary -> Lock( NULL, // 想要上锁的区域的RECT & ddsd, // DDSURFACEDESC2的地址,给空即可 DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT, // 告诉Lock想做什么 NULL))) // 事件,高为NULL { return ( 0 ); }
使用IDirectDrawSurface7::Unlock()方法解锁
声明如下:
HRESULT Unlock(LPRECT lpRect); // lpRect为在lock函数中第一个参数的Rect
下面是完整例子
完整例子源代码下载
// ------------------------------------------------------------------------- // 文件名 : 6_3.cpp // 创建者 : 方煜宽 // 邮箱 : [email protected] // 创建时间 : 2010-12-5 23:54 // 功能描述 : 创建主显示表面、 // 创建调色板、 // 关联显示表面和调色板、 // 锁住显示表面、绘制、解锁 // ------------------------------------------------------------------------- #define INITGUID #include < windows.h > #include < ddraw.h > LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); HWND main_window_handle = NULL; // 全局的windows窗口句柄 LPDIRECTDRAW7 lpdd = NULL; // ddraw 接口指针 DDSURFACEDESC2 ddsd; // ddraw 显示表面 描述结构 LPDIRECTDRAWSURFACE7 lpddsprimary = NULL; // ddraw 主显示表面 LPDIRECTDRAWSURFACE7 lpddsback = NULL; // ddraw 从显示表面 LPDIRECTDRAWPALETTE lpddpal = NULL; // 调色板接口指针 PALETTEENTRY palette[ 256 ]; // 调色板 PALETTEENTRY save_palette[ 256 ]; // 调色板 #define SCREEN_WIDTH 640 // 屏幕宽 #define SCREEN_HEIGHT 480 // 屏幕高 #define SCREEN_BPP 8 // 深度 #define KEYDOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0) #define KEYUP(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1)int Game_Init( void * parms = NULL, int num_parms = 0 ) { // 创建组件并返回IDirectDraw7接口指针 if (FAILED(DirectDrawCreateEx(NULL, ( void ** ) & lpdd, IID_IDirectDraw7, NULL))) return 0 ; // 设置协作级别 lpdd -> SetCooperativeLevel(main_window_handle, DDSCL_FULLSCREEN | DDSCL_ALLOWMODEX | DDSCL_EXCLUSIVE | DDSCL_ALLOWREBOOT); // 设置模式 if (FAILED(lpdd -> SetDisplayMode(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, 0 , 0 ))) { return 0 ; } // 初始化 ddsd memset( & ddsd, 0 , sizeof (ddsd)); ddsd.dwSize = sizeof (ddsd); ddsd.dwFlags = DDSD_CAPS; ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; // 创建主显示表面标志 // 创建主显示表面 if (FAILED(lpdd -> CreateSurface( & ddsd, & lpddsprimary, NULL))) { return 0 ; } // 初始化 调色板 数组 for ( int color = 1 ; color < 255 ; color ++ ) { // 随机填充RGB值 palette[color].peRed = rand() % 256 ; palette[color].peGreen = rand() % 256 ; palette[color].peBlue = rand() % 256 ; palette[color].peFlags = PC_NOCOLLAPSE; // 不优化调色板 } // 把0项填充为黑色 palette[ 0 ].peRed = 0 ; palette[ 0 ].peGreen = 0 ; palette[ 0 ].peBlue = 0 ; palette[ 0 ].peFlags = PC_NOCOLLAPSE; // 把255项填充为白色 palette[ 255 ].peRed = 255 ; palette[ 255 ].peGreen = 255 ; palette[ 255 ].peBlue = 255 ; palette[ 255 ].peFlags = PC_NOCOLLAPSE; // 创建调色板对象 if (FAILED(lpdd -> CreatePalette(DDPCAPS_8BIT | DDPCAPS_ALLOW256 | DDPCAPS_INITIALIZE, palette, & lpddpal, NULL))) { return 0 ; } // 把 调色板 和 主显示表面 关联起来 if (FAILED(lpddsprimary -> SetPalette(lpddpal))) { return 0 ; } return 1 ; }int Game_Shutdown( void * parms = NULL, int num_parms = 0 ) { // first the palette if (lpddpal) { lpddpal -> Release(); lpddpal = NULL; } // now the primary surface if (lpddsprimary) { lpddsprimary -> Release(); lpddsprimary = NULL; } // now blow away the IDirectDraw4 interface if (lpdd) { lpdd -> Release(); lpdd = NULL; } return 1 ; }
int Game_Main( void * parms = NULL, int num_parms = 0 ) { if (KEYDOWN(VK_ESCAPE)) SendMessage(main_window_handle, WM_CLOSE, 0 , 0 ); memset( & ddsd, 0 , sizeof (ddsd)); ddsd.dwSize = sizeof (ddsd); // 把 显示表面 锁住 if (FAILED(lpddsprimary -> Lock( NULL, // 想要上锁的区域的RECT & ddsd, // DDSURFACEDESC2的地址,给空即可 DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT, // 告诉Lock想做什么 NULL))) // 事件,高为NULL { return ( 0 ); } // now ddsd.lPitch is valid and so is ddsd.lpSurface // make a couple aliases to make code cleaner, so we don't // have to cast int mempitch = ( int )ddsd.lPitch; UCHAR * video_buffer = (UCHAR * )ddsd.lpSurface; // 随机 颜色 绘制到随机 像素点 for ( int index = 0 ; index < 1000 ; index ++ ) { // select random position and color for 640x480x8 UCHAR color = rand() % 256 ; int x = rand() % 640 ; int y = rand() % 480 ; video_buffer[x + y * mempitch] = color; } // 解锁显示表面(需要将原本 lock中使用的RECT传递给Unlock,如果事个显示表面传NULL if (FAILED(lpddsprimary -> Unlock(NULL))) return ( 0 ); Sleep( 30 ); return ( 1 ); }
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { HWND hwnd; MSG msg; TCHAR lpszClassName[] = TEXT( " kuan " ); WNDCLASS wc; wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc; wc.cbClsExtra = 0 ; wc.cbWndExtra = 0 ; wc.hInstance = hInstance; wc.hIcon = ::LoadIcon(NULL,IDI_APPLICATION); wc.hCursor = ::LoadCursor(NULL,IDC_ARROW); wc.hbrBackground = (HBRUSH)::GetStockObject(BLACK_BRUSH); wc.lpszMenuName = NULL; wc.lpszClassName = lpszClassName; RegisterClass( & wc); hwnd = CreateWindow(lpszClassName, TEXT( " fangyukuan " ), WS_POPUP | WS_VISIBLE, 0 , 0 ,SCREEN_WIDTH,SCREEN_HEIGHT, NULL, NULL, hInstance, NULL); main_window_handle = hwnd; Game_Init(); while (TRUE) { if (::PeekMessage( & msg, NULL, 0 , 0 , PM_REMOVE)) { if (msg.message == WM_QUIT) break ; ::TranslateMessage( & msg); ::DispatchMessage( & msg); } Game_Main(); } Game_Shutdown(); return msg.wParam; } LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_LBUTTONDOWN: { ::MessageBeep( 0 ); } break ; case WM_DESTROY: ::PostQuitMessage( 0 ); break ; default : return ::DefWindowProc(hwnd,message,wParam,lParam); } return 0 ; }
运行效果如下:
方煜宽 2011.05.21
转载请保留下面链接
http://www.cnblogs.com/fangyukuan/archive/2011/05/21/2052728.html