DirectDraw 创建表面

创建表面

设置模式

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

//    创建者      :  方煜宽

//    邮箱        :  fangyukuan@gmail.com

//    创建时间    :  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;

}

 

运行效果如下:

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值