Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
SSW_8_11.doc
Скачиваний:
1
Добавлен:
01.05.2025
Размер:
5 Мб
Скачать
☆

The Basics of DirectDraw

To use DirectDraw, you first create an instance of the DirectDraw object, which represents the display adapter on the computer. You then use the interface methods to manipulate the object. In addition, you need to create one or more instances of a DirectDrawSurface object to be able to display your game on a graphics surface. To demonstrate this, the DDEX1 sample included with this SDK performs the following a number of steps, which will be considered below.

Step 1: Creating a DirectDraw Object. To create an instance of a DirectDraw object, your application should use the DirectDrawCreate function as shown in the doInit function of the DDEX1 program. DirectDrawCreate contains three parameters. The first parameter takes a globally unique identifier (GUID) that represents the display device. The GUID, in most cases, is set to NULL, which means DirectDraw uses the default display driver for the system. The second parameter contains the address of a pointer that identifies the location of the DirectDraw object if it is created. The third parameter is always set to NULL and is included for future expansion.

ddrval = DirectDrawCreate(NULL, &lpDD, NULL);

if(ddrval == DD_OK)

{

// lpDD is a valid DirectDraw object.

}

else

{

// The DirectDraw object could not be created.

}

Step 2: Determining the Application's Behavior. Before you can change the resolution of your display, you must at a minimum specify the DDSCL_EXCLUSIVE and DDSCL_FULLSCREEN flags in the dwFlags parameter of the IDirectDraw::SetCooperativeLevel method. This gives your application complete control over the display device, and no other application will be able to share it. In addition, the DDSCL_FULLSCREEN flag sets the application in exclusive (full-screen) mode. Your application covers the entire desktop, and only your application can write to the screen. The desktop is still available, however. (For an example of how to see the desktop in an application running in exclusive mode, start the application and press ALT + TAB.)

The following example demonstrates the use of IDirectDraw::SetCooperativeLevel:

HRESULT ddrval;

LPDIRECTDRAW lpDD; // Already created by DirectDrawCreate

ddrval = lpDD->SetCooperativeLevel(hwnd, DDSCL_EXCLUSIVE |

DDSCL_FULLSCREEN);

if(ddrval == DD_OK)

{ // Exclusive mode was successful.

}

else

{ // Exclusive mode was not successful.

// The application can still run, however.

}

If IDirectDraw::SetCooperativeLevel does not return DD_OK, you can still run your application. The application will not be in exclusive mode, however, and it might not be capable of the performance your application requires. In this case, you might want to display a message that allows the user to decide whether or not to continue.

One requirement for using IDirectDraw::SetCooperativeLevel is that you must pass a handle to a window (HWND) to allow Windows to determine if your application terminates abnormally. For example, if a general protection (GP) fault occurs and GDI is flipped to the back buffer, the user will not be able to get the Windows screen back. To prevent this from occurring, DirectDraw provides a process running in the background that traps messages that are sent to that window. DirectDraw uses these messages to determine when the application terminates. This feature imposes some restrictions, however. First, you have to specify the window handle that is retrieving messages for your application—that is, if you create another window, you must ensure that you specify the window that is active. Otherwise, you might experience problems, including unpredictable behavior from GDI, or no response when you press ALT+TAB.

Step 3: Changing the Display Mode. After you have set the application's behavior, you can use the IDirectDraw::SetDisplayMode method to change the resolution of the display. The following example shows how to set the display mode to 640x480x8 bpp:

HRESULT ddrval;

LPDIRECTDRAW lpDD; // Already created

ddrval = lpDD->SetDisplayMode(640, 480, 8);

if(ddrval == DD_OK)

{ // The display mode changed successfully.

}

else

{ // The display mode cannot be changed.

// The mode is either not supported or

// another application has exclusive mode.

}

When you set the display mode, you should ensure that if the user's hardware cannot support higher resolutions, your application reverts to a standard mode that is supported by a majority of display adapters. For example, your application could be designed to run on all systems that support 6404808 as a standard backup resolution. (IDirectDraw::SetDisplayMode returns a DDERR_INVALIDMODE error value if the display adapter could not be set to the desired resolution. Therefore, you should use the IDirectDraw::EnumDisplayModes method to determine the capabilities of the user's display adapter before trying to set the display mode.)

Step 4: Creating Flipping Surfaces. After you have set the display mode, you must create the surfaces on which to place your application. Because the DDEX1 example is using the IDirectDraw::SetCooperativeLevel method to set the mode to exclusive (full-screen) mode, you can create surfaces that flip between the surfaces. If you were using IDirectDraw::SetCooperativeLevel to set the mode to DDSCL_NORMAL, you could create only surfaces that blit between the surfaces. Creating flipping surfaces requires the steps: Defining the surface requirements and Creating the surfaces.

Defining the Surface Requirements. The first step in creating flipping surfaces is to define the surface requirements in a DDSURFACEDESC structure. The following example shows the structure definitions and flags needed to create a flipping surface.

// Create the primary surface with one back buffer.

ddsd.dwSize = sizeof(ddsd);

ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;

ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP | DDSCAPS_COMPLEX;

ddsd.dwBackBufferCount = 1;

In this example, the dwSize member is set to the size of the DDSURFACEDESC structure. This is to prevent any DirectDraw method call you use from returning with an invalid member error. (The dwSize member was provided for future expansion of the DDSURFACEDESC structure.)

The dwFlags member determines which members in the DDSURFACEDESC structure will be filled with valid information. For the DDEX1 example, dwFlags is set to specify that you want to use the DDSCAPS structure (DDSD_CAPS) and that you want to create a back buffer (DDSD_BACKBUFFERCOUNT).

The dwCaps member in the example indicates the flags that will be used in the DDSCAPS structure. In this case, it specifies a primary surface (DDSCAPS_PRIMARYSURFACE), a flipping surface (DDSCAPS_FLIP), and a complex surface (DDSCAPS_COMPLEX).

Finally, the example specifies one back buffer. The back buffer is where the backgrounds and sprites will actually be written. The back buffer is then flipped to the primary surface. In the DDEX1 example, the number of back buffers is set to 1. You can, however, create as many back buffers as the amount of display memory allows. For more information on creating more than one back buffer, see Triple Buffering.

Surface memory can be either display memory or system memory. DirectDraw uses system memory if the application runs out of display memory (for example, if you specify more than one back buffer on a display adapter with only 1 MB of RAM). You can also specify whether to use only system memory or only display memory by setting the dwCaps member in the DDSCAPS structure to DDSCAPS_SYSTEMMEMORY or DDSCAPS_VIDEOMEMORY. (If you specify DDSCAPS_VIDEOMEMORY, but not enough memory is available to create the surface, IDirectDraw::CreateSurface returns with a DDERR_OUTOFVIDEOMEMORY error.)

Creating the Surfaces. After the DDSURFACEDESC structure is filled, you can use it and lpDD, the pointer to the DirectDraw object that was created by the DirectDrawCreate function, to call the IDirectDraw::CreateSurface method, as shown in the following example:

ddrval = lpDD->CreateSurface(&ddsd, &lpDDSPrimary, NULL);

if(ddrval == DD_OK)

{ // lpDDSPrimary points to the new surface.

}

else

{

// The surface was not created.s

return FALSE;

}

The lpDDSPrimary parameter will point to the primary surface returned by IDirectDraw::CreateSurface if the call succeeds. After the pointer to the primary surface is available, you can use the IDirectDrawSurface::GetAttachedSurface method to retrieve a pointer to the back buffer, as shown in the following example:

ddscaps.dwCaps = DDSCAPS_BACKBUFFER;

ddrval = lpDDSPrimary->GetAttachedSurface(&ddcaps, &lpDDSBack);

if(ddrval == DD_OK)

{ // lpDDSBack points to the back buffer.

}

else

{ return FALSE;

}

By supplying the address of the surface's primary surface and by setting the capabilities value with the DDSCAPS_BACKBUFFER flag, the lpDDSBack parameter will point to the back buffer if the IDirectDrawSurface::GetAttachedSurface call succeeds.

Step 5: Rendering to the Surfaces. After the primary surface and a back buffer have been created, the DDEX1 example renders some text on the primary surface and back buffer surface by using standard Windows GDI functions, as shown in the following example:

if (lpDDSPrimary->GetDC(&hdc) == DD_OK)

{

SetBkColor(hdc, RGB(0, 0, 255));

SetTextColor(hdc, RGB(255, 255, 0));

TextOut(hdc, 0, 0, szFrontMsg, lstrlen(szFrontMsg));

lpDDSPrimary->ReleaseDC(hdc);

}

if (lpDDSBack->GetDC(&hdc) == DD_OK)

{

SetBkColor(hdc, RGB(0, 0, 255));

SetTextColor(hdc, RGB(255, 255, 0));

TextOut(hdc, 0, 0, szBackMsg, lstrlen(szBackMsg));

lpDDSBack->ReleaseDC(hdc);

}

The example uses the IDirectDrawSurface::GetDC method to retrieve the handle to the device context, and it internally locks the surface. If you are not going to use Windows functions that require a handle to a device context, you could use the IDirectDrawSurface::Lock and IDirectDrawSurface::Unlock methods to lock and unlock the back buffer.

Locking the surface memory (whether the whole surface or part of a surface) ensures that your application and the system blitter cannot obtain access to the surface memory at the same time. This prevents errors from occurring while your application is writing to surface memory. In addition, your application cannot page flip until the surface memory is unlocked.

After the surface is locked, the example uses the standard SetBkColor Windows GDI function to set the background color, SetTextColor to select the color of the text to be placed on the background, and TextOut to print the text and background color on the surfaces.

After the text has been written to the buffer, the example uses the IDirectDrawSurface::ReleaseDC method to unlock the surface and release the handle. Whenever your application finishes writing to the back buffer, you must call either IDirectDrawSurface::ReleaseDC or IDirectDrawSurface::Unlock, depending on your application. Your application cannot flip the surface until the surface is unlocked.

Typically, you write to a back buffer, which you then flip to the primary surface to be displayed. In the case of DDEX1, there is a significant delay before the first flip, so DDEX1 writes to the primary buffer in the initialization function to prevent a delay before displaying the surface. As you will see in a subsequent step of this tutorial, the DDEX1 example writes only to the back buffer during WM_TIMER. An initialization function or title page may be the only place where you might want to write to the primary surface.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]