The article shows you a DirectDraw sample using Microsoft DirectX under Visual C++. DirectDraw is a component of Microsoft DirectX used for creating 2D graphics. The tutorial provides a step-by-step guide for setting up a DirectDraw application, creating surfaces, loading and manipulating images, and rendering graphics. By the going through this sample source code, you will have a functional DirectDraw application and a solid understanding of the core concepts involved in DirectX programming.

You can download the simple DirectDraw source code by using the following link.

  DirectDraw Sample Program (18.1 KiB, 11,208 hits)

Table of Contents

Setting up DirectX under Visual C++

Firstly, the directories must be set up so that Visual C/C++ can find the DirectX include files and libraries:

  1. Access the Tools/Options/Directories tabbed dialog.
  2. Select “library directories” from the drop-down list, and add the directory of the DX SDK libraries, e.g. “d:dxsdksdklib”
  3. Select “include directories” from the drop-down list, and add the directory of the DX SDK header files, e.g. “d:dxsdksdkinc”.
  4. If you are going to be using some of the DX utility headers used in the samples, then also add the samplesmisc directory, e.g. “d:dxsdksdksamplesmisc” to your includes path.

Note that the version of DirectX that normally ships with Visual C++ Compiler isn’t usually the latest, so to make sure that the compiler doesn’t find the older version located in its own directories, add the include and library paths for the SDK in front of the default include and library paths.

You must also for each application that uses DirectX explicitly add the required libraries to the project. Do this in Project/Settings (Alt+F7), under the “Link” tab for each configuration of your project. For DirectDraw, add ddraw.lib in the Object/Library modules box. You also need to add dxguid.lib here if your application uses any of the DirectX COM interface ID’s, eg IID_IDirectDraw7

The DirectDraw sample

The general outline of our sample DirectDraw application is as follows:

  1. Create a normal Windows window
  2. Set up our DirectX variables
  3. Initialize a DirectDraw object
  4. Set the “cooperative level” and display modes as necessary (explained later)
  5. Create front and back surfaces
  6. If in windowed mode, create and attach a clipper
  7. Render to the back buffer
  8. Perform the flipping. If in full-screen mode, just flip. If in windowed mode, you need to blit from the back surface to the primary surface each frame.
  9. Repeat from step 7 until we exit
  10. Clean up

Setting up

We are going to need a number of variables for our DirectDraw application. These can be global variables or class members, that’s up to you. The same goes for functions. Here are the variables we’re going to use:

All of these variables and functions I place in a seperate file, which can be called anything you want, although you should not use file names that already exist, such as “ddraw.h”. This compiler is likely to get confused about which one you want. I’ve used dd.h and dd.cpp in the sample.

Remember to ensure that these variables are initialized to NULL before we begin. If you were creating classes, you could do this in the constructor of the class.

Here is the general layout of my dd.h and dd.cpp files:

dd.h

The dd.h header file uses #ifndef and #define directives to include ddraw.h header file and other declarations.

dd.cpp

DirectDraw error checking

Before we begin, we should define a “clean” way of checking and debugging error codes from DirectX functions.

We create some functions to help us return and report error strings from HRESULT error codes.

A function that returns a string with the name of an HRESULT code:

A function that we can use in our code to help us check for errors. It checks if an HRESULT is a failure, and if it is, it prints a debugging message and returns true, otherwise it returns false.

Some lazy coders think that they can get away without doing much error checking. With DirectX, this is a very bad idea. You will have errors.

Initializing the DirectDraw system

After having created a Windows window (using MFC or plain Win32), we initialize the DirectDraw system, by creating an “IDirectDraw” object.

The DirectDrawCreate or DirectDrawCreateEx function calls can be used to create a DirectDraw object. You only create a single DirectDraw object for your application

Note that DirectDrawCreate will create an “old” DirectDraw that does not support the functions that “new” DirectDraw interfaces (such as an IDirectDraw7) does. Use DirectDrawCreateEx to create a DirectDraw interface that does. For our simple sample the above is sufficient.

Setting the screen mode

The remaining DirectDraw initialization (setting modes, creating surfaces and clippers) I place in a single function called CreateSurfaces.

The function SetCooperativeLevel is used to tell the system whether or not we want to use full-screen mode or windowed mode. In full-screen mode, we have to get exclusive access to the DirectDraw device, and then set the display mode. For windowed mode, we set the cooperative level to normal.

Creating surfaces

OK … now that we’ve got that bit of initialization out of the way, we need to create a flipping structure. No, I’m not cursing the structure .. “flipping” as in screen page-flipping :).

Anyway, we need to create one main surface that everyone will see, and a “back” surface. All drawing is done to the back surface. When we are finished drawing we need to make what we’ve drawn visible. In full-screen mode, we just need to call a routine called Flip, which will turn the current back surface into the primary surface and vice versa. In windowed mode, we don’t actually flip the surfaces – we copy the contents of the back buffer onto the primary buffer, which is what’s inside the window. In other words, we “blit” the back surface onto the primary surface.

Anyway, here is the bit of code to create the surfaces. Right now the code is ignoring full-screen mode and only catering for windowed mode, but that’ll change. Also, if there are errors in this code, consider them “exercises”.

Creating the Clipper

Now that we’ve created the surfaces, we need to create a clipper (if we’re running in windowed mode), and attach the clipper to the primary surface. This prevents DirectDraw from drawing outside the windows client area.

Putting it all together

Now that we have all these initialization routines, we need to actually call them, so the question is, where to call them?

In an MFC application, a logical place to do this is in the application’s InitInstance function:

In a plain Win32 application, you can do this in your WinMain function just before you enter the main message loop, but after you’ve created your window:

Restoring lost surfaces

As if all this initialization wasn’t enough, we also have to make sure our DirectDraw surfaces are not getting “lost”. The memory associated with DirectDraw surfaces can be released under certain circumstances, because it has to share resources with the Windows GDI. So each time we render, we first have to check if our surfaces have been lost and Restore them if they have. This is accomplished with the IsLost function.

The rendering loop

Now that we’ve got most of the general initialization out of the way, we need to set up a rendering loop. This is basically the main loop of the game, the so-called HeartBeat function. So we’re going to call it just that.

The HeartBeat function gets called during your applications idle-time processing, which is typically whenever the window has no more messages to process.

MFC: We can override the application’s OnIdle function and call our HeartBeat function from there. Use ClassWizard or the toolbar wizard to create a handler for “idle-time processing” for your main application class.

Win32: We can call the heartbeat function from inside the message loop, by using the function PeekMessage in our WinMain function to determine if we have any messages waiting:

There are alternate ways to decide when to call the HeartBeat function, for example you could use a timer. The method you use depends on the type of game you are making. If you are making a first-person 3D shooter, you probably want as high a frame rate as possible, so you might use the idle-time method. If you are making a 2D scrolling game, this might not be optimal, as you may want to control the frame rate.

The HeartBeat function

Now let’s look at the heartbeat function. The function checks for lost surfaces, then clears the back buffer with black, then draws a color square to the back buffer, and then flips the back buffer to the front.

The DDPutPixel function used here is already explained.

Flipping surfaces

Now let’s look at the function that performs the surface flipping.

A primary surface in windowed mode represents the entire Windows screen, so we have to first find out where on the screen our window is, and then translate by that offset in order to blit into the Window.

Note the Blt parameter DDBLT_WAIT. By default, if a surface is “busy” when you call Blt (for example if the GDI is accessing it) then DirectDraw will return an error, without performing the blit. Passing the DDBLT_WAIT option will instruct DirectDraw to wait until the surface becomes available and then perform the blit.

Cleaning up

When we’re done with DirectX objects, we have to “release” them, which is done by calling Release on them, for example:

Sample TODO

There are a few things the sample can’t do yet. For one thing, full-screen mode doesn’t work properly yet. It should also demonstrate how to handle switching between windowed and full-screen modes.