Chapter 6. First Contact: DirectDraw
1.Using the Interfaces Together
-
create an IDirectDraw7 interface directly with DirectDrawCreateEx(). Using this interface, set both the cooperation level and video mode.
-
Using the IDirectDrawSurface7 interface, create at least a primary surface to draw on. Based on the color depth of the surface and the video mode itself, a palette will be needed if the video mode is 8 bits per pixel or less.
-
Create a palette using the IDirectDrawPalette interface, initialize it with RGB triples, and attach it to the surface of interest.
-
If the DirectDraw application is going to be windowed, or if you're going to render bitmaps that could potentially go out of bounds of the visible DirectDraw surface, at least create a single clipper and size it to the extents of the visible window.
-
Draw on the primary surface.
2.Error Handling with DirectDraw
FAILED() //Tests for failure.
SUCCEEDED() // Tests for success.
Return Code | Description |
---|---|
DD_OK | Total Success. |
DDERR_DIRECTDRAWALREADYCREATED | DirectDraw object has already been created. |
DDERR_GENERIC | DirectDraw has no idea what's wrong. |
DDERR_INVALIDDIRECTDRAWGUID | The device GUID is unknown. |
DDERR_INVALIDPARAMS | Something is wrong with the parameters you sent. |
DDERR_NODIRECTDRAWHW | There isn't any hardware. |
DDERR_OUTOFMEMORY | Take a wild guess? |
3.Creating a DirectDraw Object
HRESULT WINAPI DirectDrawCreateEx(
GUID FAR *lpGUID, // the GUID of the driver, NULL for active display
LPVOID *lplpDD, // receiver of the interface
REFIID iid, // the interface ID of the interface you are requesting
IUnknown FAR *pUnkOuter // advanced COM, NULL
);
LPDIRECTDRAW7 lpdd; // version 7.0
// create version 7.0 DirectDraw object interface
DirectDrawCreateEx(NULL, (void **)&lpdd, IID_IDirectDraw7, NULL);
IDirectDraw7:: SetCooperativeLevel()
HRESULT SetCooperativeLevel(HWND hWnd, DWORD dwFlags);
Value | Description |
---|---|
DDSCL_ALLOWMODEX | Allows the use of Mode X (320x200,240,400) display modes. Can be used only if the DDSCL_EXCLUSIVE and DDSCL_FULLSCREEN flags are present. |
DDSCL_ALLOWREBOOT | Allows Ctrl+Alt+Del to be detected while in exclusive (full-screen) mode. |
DDSCL_EXCLUSIVE | Requests the exclusive level. This flag must be used with the DDSCL_FULLSCREEN flag. |
DDSCL_FPUSETUP | Indicates that the calling application is likely to keep the FPU set up for optimal Direct3D performance (single precision and exceptions disabled) so Direct3D does not need to explicitly set the FPU each time. For more information, look up "DirectDraw Cooperative Levels and FPU Precision" in the DirectX SDK. |
DDSCL_FULLSCREEN | Indicates full-screen mode will be used. GDI from other appli cations will not be able to draw on the screen. This flag must be used with the DDSCL_EXCLUSIVE flag. |
DDSCL_MULTITHREADED | Requests multithread-safe DirectDraw behavior. Don't worry about this for now. |
DDSCL_NORMAL | Indicates that the application will function as a regular Windows application. This flag cannot be used with the DDSCL_ALLOWMODEX, DDSCL_EXCLUSIVE, or DDSCL_FULLSCREEN flags. |
DDSCL_NOWINDOWCHANGES | Indicates that DirectDraw is not allowed to minimize or restore the application window on activation. |
5.Getting into the Mode of Things
IDirectDraw7::SetDisplayMode().
HRESULT SetDisplayMode(DWORD dwWidth, // width of mode in pixels
DWORD dwHeight, // height if mode in pixels
DWORD dwBPP, // bits per pixel, 8,16,24, etc.
DWORD dwRefreshRate, // desired refresh, 0 for default
DWORD dwFlags); // extra flags (advanced) 0 for default
6.The Subtleties of Color
- Create one or more palette data structures as arrays of 256 PALETTENTRY's.
- Create a DirectDraw palette interface IDirectDrawPalette object from the DirectDraw object itself. In many cases, this will be directly mapped to the hardware VGA palette registers.
- Attach the palette object to a drawing surface, such as the primary surface, so all data rendered to it is displayed in the appropriate colors.
- Optional) If you desire, you can change the palette entries or the entire palette. You will need to take this step if you sent a NULL palette during step 2 and opted to omit step 1. Basically, what I'm trying to say is that when you create a palette interface, you can send it a palette of color also. But if you don't, you can always do it later. Therefore, step 2 can be step 1 if you remember to fill up the palette entries at a later time.
You must set the peFlags field to PC_NOCOLLAPSE. This is necessary because you don't want Win32/DirectX optimizing your palette for you.
/
PALETTEENTRY palette[256]; // palette storage
// fill em up with color!
for (int color=1; color < 255; color++)
{
// fill with random RGB values
palette[color].peRed = rand()%256;
palette[color].peGreen = rand()%256;
palette[color].peBlue = rand()%256;
// set flags field to PC_NOCOLLAPSE
palette[color].peFlags = PC_NOCOLLAPSE;
} // end for color
// now fill in entry 0 and 255 with black and white
palette[0].peRed = 0;
palette[0].peGreen = 0;
palette[0].peBlue = 0;
palette[0].peFlags = PC_NOCOLLAPSE;
palette[255].peRed = 255;
palette[255].peGreen = 255;
palette[255].peBlue = 255;
palette[255].peFlags = PC_NOCOLLAPSE;
/
HRESULT CreatePalette(DWORD dwFlags, // control flags
LPPALETTEENTRY lpColorTable, // palette data or NULL
LPDIRECTDRAWPALETTE FAR *lplpDDPalette, // received palette interface
IUnknown FAR *pUnkOuter); // advanced, make NULL
/
LPDIRECTDRAWPALETTE lpddpal = NULL; // palette interface
if (FAILED(lpdd->CreatePalette(DDPCAPS_8BIT | DDPCAPS_ALLOW256 | DDPCAPS_INITIALIZE,
palette, &lpddpal, NULL)))
{
// error
} // end if
/
7.Building a Display Surface
A primary surface directly corresponds to the actual video memory being rasterized by the video card and is visible at all times.
从表面:a.平滑动画效果:后备缓冲(总是合著表面有相同的BPP和尺寸)、页面切换。
b.保存位图和游戏中表示对象的动画:只有使用DirectDraw显示表面才能调用使用于位图数据的加速硬件。
To create any surface, you must follow these steps:
a.Fill out a DDSURFACEDESC2 data structure that describes the surface you want to create.
b.Call IDirectDraw7::CreateSurface() to create the surface.
typedef struct _DDSURFACEDESC2
{
DWORD dwSize; // size of this structure
DWORD dwFlags; // indicate fields you'll be filling or which fields to retrieve
// DDSD_CAPS
DWORD dwHeight; // height of surface in pixels
DWORD dwWidth; // width of surface in pixels
union
{
LONG lPitch; // memory pitch per row,每行的字节数,步幅(stride),内存宽度
DWORD dwLinearSize; // size of the buffer in bytes
} DUMMYUNIONNAMEN(1);
DWORD dwBackBufferCount; // number of back buffers chained
union
{
DWORD dwMipMapCount; // number of mip-map levels
DWORD dwRefreshRate; // refresh rate
} DUMMYUNIONNAMEN(2);
DWORD dwAlphaBitDepth; // number of alpha bits
DWORD dwReserved; // reserved
LPVOID lpSurface; // pointer to surface memory
DDCOLORKEY ddckCKDestOverlay; // dest overlay color key
DDCOLORKEY ddckCKDestBlt; // destination color key
DDCOLORKEY ddckCKSrcOverlay; // source overlay color key
DDCOLORKEY ddckCKSrcBlt; // source color key
DDPIXELFORMAT ddpfPixelFormat; // pixel format of surface
DDSCAPS2 ddsCaps; // surface capabilities
DWORD dwTextureStage; // used to bind a texture
// to specific stage of D3D
} DDSURFACEDESC2, FAR* LPDDSURFACEDESC2;
typedef struct _DDSCAPS2
{
DWORD dwCaps; // Surface capabilities
DWORD dwCaps2; // More surface capabilities
DWORD dwCaps3; // future expansion
DWORD dwCaps4; // future expansion
} DDSCAPS2, FAR* LPDDSCAPS2;
Value | Description |
---|---|
DDSCAPS_BACKBUFFER | Indicates that this surface is the back buffer of a surface flipping structure. |
DDSCAPS_COMPLEX | Indicates that a complex surface is being described. A complex surface is a surface with a primary surface and one or more back buffers to create a flipping chain. |
DDSCAPS_FLIP | Indicates that this surface is a part of a surface flipping structure. When this capability is passed to the CreateSurface() method, a front buffer and one or more back buffers are created. |
DDSCAPS_LOCALVIDMEM | Indicates that this surface exists in true, local video memory rather than non-local video memory. If this flag is specified, DDSCAPS_VIDEOMEMORY must be specified as well. |
DDSCAPS_MODEX | Indicates that this surface is a 320x200 or 320x240 Mode X surface. |
DDSCAPS_NONLOCALVIDMEM | Indicates that this surface exists in non-local video memory rather than true, local video memory. If this flag is specified, DDSCAPS_VIDEOMEMORY flag must be specified as well. |
DDSCAPS_OFFSCREENPLAIN | Indicates that this surface is an offscreen surface that is not a special surface such as an overlay, texture, z-buffer, front-buffer, back-buffer, or alpha surface. Usually used for sprites. |
DDSCAPS_OWNDC | Indicates that this surface will have a device context association for a long period. |
DDSCAPS_PRIMARYSURFACE | Indicates that this surface is the primary surface. It represents what is visible to the user at the moment. |
DDSCAPS_STANDARDVGAMODE | Indicates that this surface is a standard VGA mode surface, and not a Mode X surface. This flag cannot be used in combination with the DDSCAPS_MODEX flag. |
DDSCAPS_SYSTEMMEMORY | Indicates that this surface memory was allocated in system memory. |
DDSCAPS_VIDEOMEMORY | Indicates that this surface exists in display memory. |
/
LPDIRECTDRAWSURFACE7 lpddsprimary = NULL;
DDSURFACEDESC2 ddsd; // the DirectDraw surface description
// MS recommends clearing out the structure
memset(&ddsd,0,sizeof(ddsd)); // could use ZeroMemory()
// now fill in size of structure
ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_CAPS;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
// now create the primary surface
if (FAILED(lpdd->CreateSurface(&ddsd, &lpddsprimary, NULL)))
{
// error
} // end if
/
8.Attaching the Palette
HRESULT SetPalette(LPDIRECTDRAWPALETTE lpDDPalette);
if (FAILED(lpddsprimary->SetPalette(lpddpal)))
{
// error
} // end if
9.Plotting Pixels
All DirectDraw video modes and surfaces are linear.
// assume this points to VRAM or the surface memory
UCHAR *video_buffer8;
video_buffer8[x + y*memory_pitchB] = pixel_color_8;
// assume this points to VRAM or the surface memory
USHORT *video_buffer16;
video_buffer16[x + y*(memory_pitchB >> 1)] = pixel_color_16;
// this builds a 16 bit color value in 5.5.5 format (1-bit alpha mode)
#define _RGB16BIT555(r,g,b) ((b & 31) + ((g & 31) << 5) + ((r & 31) << 10))
// this builds a 16 bit color value in 5.6.5 format (green dominate mode)
#define _RGB16BIT565(r,g,b) ((b & 31) + ((g & 63) << 5) + ((r & 31) << 11))
要访问任何显示表面(主、从)时,必须对内存加锁和解锁:
HRESULT Lock(LPRECT lpDestRect, // destination RECT to lock, NULL to full windows
LPDDSURFACEDESC2 lpDDSurfaceDesc, // address of struct to receive info
DWORD dwFlags, // request flags
HANDLE hEvent); // advanced, make NULL
DDLOCK_READONLY | Indicates that the surface being locked will be read-only. |
DDLOCK_SURFACEMEMORYPTR | Indicates that a valid memory pointer to the top of the specified RECT should be returned. If no rectangle is specified, a pointer to the top of the surface is returned. This is the default. |
DDLOCK_WAIT | If a lock cannot be obtained because a blit operation is in progress, the method retries until a lock is obtained or another error occurs, such as DDERR_SURFACEBUSY. |
DDLOCK_WRITEONLY | Indicates that the surface being locked will be write-enabled. |
HRESULT Unlock(LPRECT lpRect);
/
inline void Plot8(int x, int y, // position of pixel
UCHAR color, // color index of pixel
UCHAR *buffer, // pointer to surface memory
int mempitch) // memory pitch per line
{
// this function plots a single pixel
buffer[x+y*mempitch] = color;
} // end Plot8
Plot8(100,20,26, (UCHAR *)ddsd.lpSurface,(int)ddsd.lPitch);
/
Similarly, here's a 16-bit 5.6.5 RGB mode plot function:
inline void Plot16(int x, int y, // position of pixel
UCHAR red,
UCHAR green,
UCHAR, blue // RGB color of pixel
USHORT *buffer, // pointer to surface memory
int mempitch) // memory pitch bytes per line
{
// this function plots a single pixel
buffer[x+y*(mempitch>>1)] = __RGB16BIT565(red,green,blue);
} // end Plot16
Plot16(300,100,10,14,30,(USHORT *)ddsd.lpSurface,(int)ddsd.lPitch);
/
10.清理资源
// first kill the palette
if (lpddpal)
{
lpddpal->Release();
lpddpal = NULL;
} // end if
// now the primary surface
if (lpddsprimary)
lpddsprimary->Release();
// and finally the directdraw object itself
if (lpdd)
{
lpdd->Release();
lpdd = NULL;
} // end if