DirectDraw for Visual Basic®


Introduction
 

This text is about the DirectDraw system for VB. DirectDraw is comprised of 10 classes. Each of these classes abstracts an important aspect for video / graphics programming. This text does not cover all of the classes, but is limited to the following:

These five classes form the foundation for using DirectDraw as a tool for games and animation. The rest of the classes will be covered in later DirectX-files™.

We will start this file with a small overview of each of the classes that will be covered in this text. An in-depth discussion of each class will be given as the DirectX-file™ series progresses. 

DirectDraw7

This is the main DirectDraw object. This object is used to create a few of the other objects, but also to provide generic system info on the video hardware capabilities and the DirectDraw driver version installed on the system. The DirectDraw7 object is used in the setup phase of a program to set certain system settings and properties and to get information about the available resources and possible configurations.

DirectDrawClipper

This object is used to automate clipping functionality. Giving the control of clipping to the DirectDrawClipper can mean the difference between a fast program and a really fast program. A clipper is basically a list of RECTS (rectangular structures) that determine the areas where the bit block transfer (The Blitter) can move pixels to (Blit to).

DirectDrawEnumModes

This object is used to store the possible display mode settings of the system. It is a very simple object and will be introduced in the first sample application.

DirectDrawPalette

The object encapsulates the palette used on the DirectDraw surfaces. It functionality and purpose is the same as the normal Win32 palette. Palettes in DirectDraw are still widely used and we will examine some pretty funky palette animation tricks.

DirectDrawSurface7

This is the most used and most important in the collection of objects that DirectDraw is. It holds function and methods for blitting, accessing, storing and using video memory either with assisted hardware or in pure software. The surface is such an essential object in DirectDraw that you must be careful that you completely understand it and how it is used. Through this object we can get direct access to the memory of the video card, which is something completely unique in the world of Win32 so please read the sections on the surface carefully.

Sub case 1: Setting up DirectDraw

Purpose

The purpose of this sub case is to demonstrate using the DirectDraw7 object. We will examine how to use this object to get information on the possible settings for the display mode supported by your hardware and also see how we can set these. Likewise will present the 'standard' way of creating the top DirectDraw objects.

 

Pay special attention to the details surrounding the setting of the display mode and cooperation level.

 

Getting into it

The project presented is the DXFile1_1 sample project. As stated above, this project has a twofold purpose – first we will obtain the possible display resolution configurations of the primary display card, and then try out these settings by changing the display mode.

The first step before doing anything else with DirectDraw is to declare and initialize the main DirectDraw and DirectX objects. These main objects are essential for any DirectDraw application. It is these we will use to create and initialize the rest of the DirectDraw objects. Since the DirectX7 object and the DirectDraw7 objects are being used in this context we must keep them active throughout the lifetime of the application, and only release them when we terminate the application. In this example we have declared the objects and given them global scope by placing them in the General Declarations section of the main form. The declarations are shown in Listing 1:

 

Option Explicit

Dim objDx As DirectX7
Dim objDraw7 As DirectDraw7
Dim objEnumModes As DirectDrawEnumModes

Listing 1 shows the declarations for the DXFile1_1 sample project

The DirectX7 object is the main DirectX object. It is from this object that we will create the DirectDraw object (this object is also used to create the other foundation objects of DirectX, such as DirectSound, Direct3D etc.). The DirectX7 object also contains some useful generic methods, which can be considered global methods for the DirectX libraries. These include, among others, functionality for setting an event callback function, getting the RECT of the main DirectDraw window, getting system color information (usable by both Direct3D and DirectDraw). In this sample we will only be concerned with the method for creating a DirectDraw7 object.

 

The DirectDraw7 object is in essence a representation of a video card installed on the system. Since both Windows98 and Windows2000 supports multiple video cards you can have multiple DirectDraw7 objects. This file will not cover multiple video card support.

Using the DirectDraw7 object you can instantiate the other related object that make up the DirectDraw object. You can also use it to get information on the available features of the video card / driver and the state of the video card and video memory. All objects created from the DirectDraw7 objects are destroyed and unusable if the DirectDraw7 object is destroyed. Therefore it is of utmost importance that you control  both the scope and the lifetime of this object. As we progress into the DirectX-file™ series we will study the methods and features of this object in considerably more detail. For now, just remember that it is this object that is used to create other DirectDraw related objects. When it gets released, so will all the other objects we created using it.

As you can see from Listing 1 we have declared a third object with Public scope. This object is a DirectDrawEnumModes object. This object will, when we create and initialize it, contain all the possible display settings of the video card. It is a simple object with only two methods, GetCount and GetItem. We will be covering these in just a short while.

Starting the Engine

The main object creation and initialization action takes place in the load event of our form. The first thing we have to do is to create a new DirectX object. This is done in much the same way as you create any new object in Visual Basic – using the New operator. If this creation fails you will get a run-time error indicating the failure. There is always the possibility that the user of your application has not installed the version of DirectX that your program requires. It is therefore essential that you trap any errors and warn the user if your program requires a later version than what has been installed. We will trap any errors in the declared ErrHandler label. 

After creating a valid DirectX7 object we can use it to create the DirectDraw7 object via the DirectDrawCreate  function.

In the sample application we simply create the DirectDraw7 object by passing an empty string. After having created the DirectDraw7 object we use the GetDisplayModesEnum function for getting the available display modes into the DirectDrawEnumModes object. This object will then contain all the possible display modes that we can use in our application. You should always check the display hardware to see if it supports a given resolution. Otherwise, if you try to set a resolution that the hardware does not support you will get a run-time error.

The DirectDrawEnumModes has two methods – GetCount and GetItem. The GetCount method returns the number of available display settings as a simple long. We'll use this function to get the upper bounds of the iterative that fills the list box. As you might have guessed we use the GetItem method to retrieve the settings available. This method takes two parameters, the first being the index to the item that should be received. This index must be in the range of 1 – GetCount(), if it is not a run-time error is generated. The second parameter is a DDSURFACEDESC2 structure. This structure is a descriptor for a given surface and is used it many contexts. In this application we use the lWidth, lHeight and the ddpfPixelFormat.lRGBBitCount members to retrieve the Height, Width and Color depth of the primary surface. In the initialization phase of the sample application we put these members in a list box for display. 

The code for initializing the application is shown in Listing 2.
 

Private Sub Form_Load()

On Error GoTo ErrHandler:

Dim ddsd As DDSURFACEDESC2
Dim I As Long, lgCount As Long

Set objDx = New DirectX7
''Create DirectDraw object
Set objDraw7 = objDx.DirectDrawCreate("")

''Get the modes in the enum modes object
Set objEnumModes = objDraw7.GetDisplayModesEnum(DDEDM_DEFAULT, ddsd)

''Put the data in the listbox
lgCount = objEnumModes.GetCount()

For I = 1 To lgCount

objEnumModes.GetItem I, ddsd

lstDisplayModes.AddItem CStr(ddsd.lWidth) & "x" & CStr(ddsd.lHeight) & "x" _ 
& CStr(ddsd.ddpfPixelFormat.lRGBBitCount)
Next I

ErrHandler:

Select Case Err.Number

Case 0 ''No Errors

Case Else
Err.Raise Err.Number, Err.Source, Err.Description, Err.HelpContext, Err.HelpContext

Call CleanUp

End Select

End Sub


 

Listing 2 shows the initialization for the DXFile1_1 sample project

One more thing that is worth mentioning is the Error handling in the initialization phase of the application. It cannot be stressed enough how important this is. You must catch any run-time errors that are thrown. If you do not, you run the risk of your application bombing. First and foremost, your application will stop executing before it really starts – and you are not likely to sell many games that way. It may also be possible that the error can be 'fixed'. By trapping and addressing the error, we can keep our program running. A future DirectX-file™ will deal exclusively with error handling techniques that you can employ. Just remember you must do it, even though it seems like redundant and boring code, otherwise you will surely pay the price. 
 

After initialization, the application is ready to run. You should be seeing a small window with a list box and two command buttons, something like Figure 1:

Try to select an item in the list box and click the ‘Set Selected Display mode’ button. Suddenly your display mode will change and the small window will be resized to fill the entire desktop. You have just done two things. First, you have changed the Display setting, and second, you have acquired the entire display under your control. If you move the window around a bit you can see other windows behind it (if you have any open), but you still have control of everything on the primary display. Don’t mistake your form for the primary display – they are two very distinct entities (as we will see later).

The code that changes and acquires the control over the display is in the SetNewDisplayMode function shown in Listing 3:
 

Private Function SetNewDisplayMode()
On Error GoTo ErrHandler

Dim ddsd As DDSURFACEDESC2
Dim I As Long

''Get the selected item
I = lstDisplayModes.ListIndex
If I = -1 Then
MsgBox "Please select item in Listbox", vbOKOnly, "Error"
Exit Function
End If

objEnumModes.GetItem (I + 1), ddsd

''Set cooperative level
objDraw7.SetCooperativeLevel Me.hWnd, DDSCL_FULLSCREEN Or DDSCL_ALLOWMODEX Or _ DDSCL_EXCLUSIVE

''Set the display mode
objDraw7.SetDisplayMode ddsd.lWidth, ddsd.lHeight, ddsd.ddpfPixelFormat.lRGBBitCount, 0, _ DDSDM_DEFAULT

''Draw the form
Me.Refresh

ErrHandler:

Select Case Err.Number

Case 0 ''No Errors

Case Else

Err.Raise Err.Number, Err.Source, Err.Description, Err.HelpContext, Err.HelpContext
Call CleanUp

End Select

End Function

 

Listing 3 shows the SetNewDisplayMode function in the DXFile1_1 sample project

The function first tries to identify the selected item from the List box. If no item has been selected, the user will be notified and the function will terminate; otherwise, the information for the selected item is stored in a DDSURFACEDESC2 structure. Before we can change the display setting of the primary display to match the selected information, we must announce how we want to cooperate with the DirectDraw system. This is done via the SetDisplayMode method of the DirectDraw7 object. The SetDisplayMode method is one of DirectDraw's initializing functions. This method tells the DirectDraw system how the application wants to interact with the rest of the system. At the basic level you have two choices on how your application can interact with the rest of the windows system: it can either take full and exclusive control of the display, or it can work as a normal window with no special rights. In this sample, we have taken full control of the display system and video card. This is the easiest approach since we do not have to worry about other running applications and their interaction with the system. Later, in this DirectX-file™ series, we will examine how to make DirectDraw applications that interact normally with Windows. 

Lastly, we call a refresh on the form to make it display properly. Play around with the system for a while and enjoy the ‘fun’. 

Afterthoughts

This sample is very simple and does not involve any special techniques or methods for accomplishing what we need. But it does serve as an example on how much of the interaction with the DirectDraw system is accomplished.

In the next sub case, we will examine some of DirectDraw's basic graphical abilities. 

DirectDraw for Visual Basic®

 

Sub Case 2: Getting some stuff on the screen

Purpose

The purpose of this sub case is to get some graphics onto the screen. We will delve into the art of loading simple bitmap data files (ordinary bitmap format) and get these into the video buffers. With these loaded and ready to go we will create the primary surface and show the graphics on the screen. Along the way we will also create some simple reusable types to assist us in our efforts.

Notice

Pay special attention to the details surrounding the loading of the graphics and also the section on reloading them.

Getting into it

The project presented is the DXFile1_2 sample project.

This simple project will simply show a single image on the primary screen. It sounds simple and easy – and it is. But be warned! In a real world scenario this section of any game or multimedia is critical – meaning that if it does not succeed your application can not run. So pay close attention as we intend to show you how to do this in a reliable and easily reusable way.

Surfaces

A major part of any DirectDraw program involves the creation and usage of surfaces. Surfaces are the memory buffers where all your beautiful graphics are placed, manipulated and blitted to and from. Basically a surface is just a chunk of linear memory residing either in the video memory chips on a video card, or in the ordinary system memory. The actual layout and format of the memory can vary depending on the color format specified when a surface is created. There is a lot to be said about surfaces, but in order to avoid confusing you, we will only address some of the most basic issues involving surfaces in this article.

Conceptually you can view a surface as a canvas for your graphics. Unlike a painter who usually works on one canvas at a time, we will frequently use multiple surfaces -often with different purposes and properties- in the creation of our multimedia applications.  In this DirectX-file™ we will discuss two kinds of surfaces – the Primary surface and a simple Off-Screen surface.

The primary surface is the surface that is shown, or rather drawn, onto the monitor. In a game this would be represented by the visible screen or play area. The off-screen surface is the simplest of the surfaces and basically provides a method for storing graphics or other application data which you might want to move to the primary surface during the course of your game.

The various surface properties and descriptors of a DDraw surface are contained in a special type called DDSURFACEDESC2 described in Listing 4:
 

Type DDSURFACEDESC2
     ddckCKDestBlt As DDCOLORKEY
     ddckCKDestOverlay As DDCOLORKEY
     ddckCKSrcBlt As DDCOLORKEY
     ddckCKSrcOverlay As DDCOLORKEY
     ddpfPixelFormat As DDPIXELFORMAT
     ddsCaps As DDSCAPS2
     lAlphaBitDepth As Long
     lBackBufferCount As Long
     lFlags As CONST_DDSURFACEDESCFLAGS
     lHeight As Long
     lLinearSize As Long
     lMipMapCount As Long
     lPitch As Long
     lRefreshRate As Long
     lTextureStage As Long
     lWidth As Long
     lZBufferBitDepth As Long
End Type

Listing 4 shows the DDSURFACEDESC structure

For a complete description on the various members of this type you can refer to the DirectDraw documentation in the DirectX SDK. The members used in this and later DirectX-files will be explained as they are used.

To fill the above listed type with valid data for a surface we will use the GetSurfaceDesc function that is a method in the DirectDrawSurface7 class. The GetSurfaceDesc is defined as shown in Listing 5:
 

GetSurfaceDesc(surface As DDSURFACEDESC2)

Listing 5 shows the GetSurfaceDesc function

This function is a very important function that we will use often to get certain properties for use in our programs. Take some time to familiarize yourself with it now and take a look at the documentation in the DirectX SDK.

So now we know, albeit on a very basic level, what a surface is (we will get into more details in later files) and we also know how to get information about any given surface. Let's examine how we actually create and use these surfaces.

A surface is created using the CreateSurface method on the DirectDraw7 object. The method is declared as shown in Listing 6. The one parameter the method takes is the surface description type – DDSURFACEDESC2. In this type you specify the kind of surface you want to create (a primary surface, an off-screen plain surface etc…) and also the various properties that a surface should have (number of colors, width, height etc…).
 

CreateSurface( dd As DDSURFACEDESC2) As DirectDrawSurface7

Listing 6 shows the CreateSurface function

So now that we know in theory how to create a surface, we might as well explore what we are actually doing with the surfaces and how we are going to use them.

We mentioned earlier that a surface, with the exception of the primary surface, can be used as a storage place for graphical or other related visual data. This data can be manipulated and then blitted onto the primary surface for display on the screen. If you have previously programmed using the Win32 GDI system you might see some similarities between a surface and a bitmapped memory DC (device context). In this assumption you are not all that wrong although there are some very distinct differences. First and foremost DirectX is a 'lower' layer than GDI, meaning that you have some specific hardware options in your toolbox when you want to do serious pixel manipulation. One of these options is directly accessing the memory that a surface has acquired – whether this memory is normal system memory or is video memory on the video card. This is not possible with the normal GDI system. Although DirectX might already sound as a paradise for you, be warned that having these capabilities comes at a cost – namely the cost of lost convenience that GDI provided. By directly manipulating the memory of a surface you lose all the automatic color conversion functionality that takes place transparently when using the Win32 GDI system.

What has all this got to do with the simple use of surfaces that we intend to explore in this DirectX-file? Well, before we can actually begin to use the surfaces we create we need to fill them up with our intended graphical data, and this is where the above mentioned issues become relevant. In the sample project we will create a full-screen application with 16-bit colors. This means that the surface will have 16 bit data for each of the pixels in the surface. This will give you a total of 65.536 possible color values for each pixel. So what do we do with our cool 32 bit graphics that we spent hours creating with various image programs and optimized palette handlers? We just can't display the graphics as is – since each pixel takes up 32 bits and we only have 16 bits per pixel on the surface. So we now have two choices:

1) Make up a color converting algorithm that will translate the 32-bit graphic into something usable by the 16-bit surface or 2) Go back to the good old GDI system and let it handle the color conversion.

Huh? So are you saying the GDI system is available to us if we are using DirectX? The short answer is yes. The longer response is that just because we can use it does not mean that we should use it. The problem with the GDI is that it still does a lot of stuff by using the most common denominator technique – i.e. the GDI does what it 'thinks' is the best, and not necessarily what is best for your application. Using the GDI also  means a loss in performance since it is not optimized and cannot use the specific hardware options that you can use via DirectX. The conclusion is that if the GDI does what you need at an acceptable level and if performance is not an issue then by all means use it. 

 

The issue of hiding complexity at the expense of functionality is one that Visual Basic programmers should be familiar with. Like VB the GDI performs certain tasks for you and this results in more 'general' code being run than if you created the code in something like assembly or C. There is always some tradeoff between ease of use and power. You'll need to carefully analyze your projects and become familiar with all your options to find the right balance. 

One of the areas where the GDI is useful is in populating your surfaces with data. This is usually done during startup or in between levels, where extreme speed is not essential, but a secure and stable result is. The GDI system is somewhat limited since is can only work with bitmaps but if that is what you are using then it can be used effectively here. 

In sample (DXFile1_2) for this sub-case we will use the GDI system to populate our off-screen surface and return to DirectDraw for faster blitting to the primary surface.

Enough talk some code please...

This sample will display an image using the DirectX blit function. For this we need two surfaces – a primary surface and an off-screen surface for holding our graphics.

The primary surface will be 640 pixels wide, 480 pixel high and 16 bit deep (16-bit color). The off-screen surface will have the dimensions of the bitmap we want to display.

All the surfaces are created and filled with data in the Load event of our form after we have initialized the DirectX and DirectDraw systems.

The code is displayed in Listing 7:
 

Private Sub Form_Load()

Dim ddsd2 As DDSURFACEDESC2

AppPath = App.Path
If Right$(AppPath, 1) <> "\" Then
AppPath = AppPath & "\"
End If

''Get reference to new directx object
Set objDx = New DirectX7

''Create directdraw object
Set objDraw7 = objDx.DirectDrawCreate("")

''Set initial cooperative level
objDraw7.SetCooperativeLevel Me.hWnd, DDSCL_FULLSCREEN Or DDSCL_EXCLUSIVE

''Set initial display mode
objDraw7.SetDisplayMode 640, 480, 16, 0, DDSDM_DEFAULT

''Set primary surface
ddsdPrim.lFlags = DDSD_CAPS
ddsdPrim.ddsCaps.lCaps = DDSCAPS_PRIMARYSURFACE
Set objPrimary = objDraw7.CreateSurface(ddsdPrim)

''Store the primary display properties
objPrimary.GetSurfaceDesc ddsdPrim

rPrimary.Right = ddsdPrim.lWidth
rPrimary.Bottom = ddsdPrim.lHeight

''Set the graphics for the surface
OffSurface.strFileName = AppPath & "directxfiles.bmp"
VBDXLoadOffScreenSurfaceFromType OffSurface, -1, objDraw7


''Draw it...
DrawIt

End Sub

Listing 7 shows the load event of DirectX-file sample DXFile1_2

The first thing we do is initialize the DirectX system and then set up the DirectDraw system, using code similar to that in Sample 1. We create a full-screen application with exclusive access to the primary display.

After this we initialize and create the primary surface. The primary surface is declared as a global entity for the project in the objPrimary variable. As you can see we just declare it as a standard DirectDrawSurface7 class. The description type for the primary surface is likewise declared as a global variable – ddsdPrim. We will use this description type both in the makings of our surface, but also to contain state for the surface (width and height).

The first step in creating the surface is to set the properties for the surface in the ddsdPrim type. First we set the ddsdPrim.lFlags member to the value of DDSD_CAPS. This will tell the DirectX system that we provide some information in the ddsdPrim.ddsCaps member type that should be evaluated. Failing to set this variable will give unpredictable results. The next thing we do is set the ddsdPrim.ddsCaps.lCaps member to the value of DDSCAPS_PRIMARYSURFACE. This marks the surface as a primary surface. That is actually all you need to do in order to create a primary surface. The next step is to call the CreateSurface function on the main DirectDraw object and passing the ddsdPrim type as a parameter. Remember to use the Set operator as we are creating an object.  If the CreateSurface function fails you will get a run-time error. We have purposely not included an error handler in order to keep the code simple, but you would need to do so in a real-world application.

The next thing we do on the primary surface is to store the various properties using the GetSurfaceDesc function. We do this now so we don't have to do it later when we might need the processor cycles for something else. Finally we copy the dimensions of the primary surface into a RECT structure, which we will use in the Blt function described below.

Now that we have dealt with the primary surface we need to setup the off-screen surface that will contain our graphics. As you can see from Listing 7 we do this in a special way. First and foremost our off-screen surface is declared as a VBDXSurface type. This type is found in the VBDXHelper module. It is shown in Listing 8:
 

Public Type VBDXSurface
          dds As DirectDrawSurface7
          strFileName As String ''File name of the bitmap source for surface
          ddsd2 As DDSURFACEDESC2
          rectDDS As RECT
End Type


Member description:
  dds: The actual DirectDrawSurface7 class
  strFileName: The file name to a bitmap that the surface will load. 
  ddsd2: The surface description type that will hold the information on the dds surface.
  rectDDS: The RECT structure that will contain the dimensions of the dds surface. 

Listing 8 shows the VBDXSurface type

We created this structure in order to simplify the handling of surfaces, at the same time keeping the functions pretty basic. With the VBDXSurface structure comes a few helper functions, also declared in the VBDXHelper module. The one we will use here is the VBDXLoadOffScreenSurfaceFromType function, which basically initializes and creates an off-screen plain surface. The function is described in Listing 9:
 

Public Function VBDXLoadOffScreenSurfaceFromType(ByRef SurfaceType As VBDXSurface, lgSourceColorKey As Long, ByRef objDraw As DirectDraw7)
On Error GoTo ErrHandler

  If lgSourceColorKey <> -1 Then
  SurfaceType.ddsd2.lFlags = DDSD_CAPS Or DDSD_CKSRCBLT

  ''Set source color key
    SurfaceType.ddsd2.ddckCKSrcBlt.high = lgSourceColorKey
    SurfaceType.ddsd2.ddckCKSrcBlt.low = lgSourceColorKey
  Else

    SurfaceType.ddsd2.lFlags = DDSD_CAPS
  End If

  SurfaceType.ddsd2.ddsCaps.lCaps = DDSCAPS_OFFSCREENPLAIN

  Set SurfaceType.dds = objDraw.CreateSurfaceFromFile(SurfaceType.strFileName,              SurfaceType.ddsd2)

  ''Get the size of the surface and load the rect structure
  SurfaceType.dds.GetSurfaceDesc SurfaceType.ddsd2

  SurfaceType.rectDDS.Bottom = SurfaceType.ddsd2.lHeight
  SurfaceType.rectDDS.Right = SurfaceType.ddsd2.lWidth

  ErrHandler:

  Select Case Err.Number

    Case 0 ''No errors - just clean up

    Case Else ''Clean up and throw (raise) err

    Err.Raise Err.Number, Err.Source, Err.Description, Err.HelpContext, Err.HelpContext

  End Select
End Function

Listing 9a shows the VBDXLoadOffScreenSurfaceFromType function

 

Public Function VBDXLoadOffScreenSurfaceFromType(ByRef SurfaceType As VBDXSurface, 
lgSourceColorKey As Long, ByRef objDraw As DirectDraw7)
 

Member description:
  SurfaceType: The VBDXSurface that should be initialized and created.
  lgSourceColorKey: The source color key for the surface
  objDraw: The DirectDraw object that the surface should be created from. 

Listing 9b shows the declaration of the VBDXLoadOffScreenSurfaceFromType function

The actual calling of this function is shown in Listing 10:
 

''Set the graphics for the surface
  OffSurface.strFileName = AppPath & "directxfiles.bmp"
  VBDXLoadOffScreenSurfaceFromType OffSurface, -1, objDraw7

Listing 10 shows the implementation of the VBDXLoadOffScreenSurfaceFromType function

The first section of the function code deals with color keying. This subject is not relevant for us at the moment (we will cover this in the next DirectX-file). Basically we check whether the lgSourceColorKey parameter value is  –1. If it is it we will set the values for the color key. If it is not, as is the case with our sample, it just sets the ddsd.lFlags member of the DDSDSURFACEDESC2 structure in the VBDXSurface type. As with the creation of the primary surface this indicates that we want the DirectDraw system to check out the ddsdCaps type member for additional information when the surface is created. Next we set the ddsdCaps.lCaps member to the value of the constant DDSCAPS_OFFSCREENPLAIN, which basically indicates that we just want a plain vanilla off-screen surface. To create the actual surface we use the CreateSurfaceFromFile member method of the DirectDraw7 object. This function will create the surface, load the graphics and fill the surface with the loaded graphics. Very convenient. The function is shown in Listing 11:
 

DirectDraw7.CreateSurfaceFromFile( _ 
file As String, _ 
dd As DDSURFACEDESC2) As DirectDrawSurface7

Listing 11 show the declaration of the CreateSurfaceFromFile function

Finally, we use the function to get the dimensions of the surface, using the GetSurfaceDesc function and store these values in the rectDDS member of the VBDXSurface type. The reasoning behind this will be revealed shortly.

Now we have created a primary surface, an off-screen plain surface and gotten the loading of the graphics squared away. We are now ready to do some blitting -drum roll please!

The last code block in the Form load event is a call to the drawing function, which will transport the pixel data from the off-screen plain surface onto the primary display surface. The function is shown in Listing 12:
 

Private Sub DrawIt()
Dim rt As Long

    rt = objPrimary.Blt(rPrimary, OffSurface.dds, OffSurface.rectDDS, DDBLT_WAIT)

    If rt = DDERR_SURFACELOST Then
      ''The surface is lost we need to reload the graphics
      objDraw7.RestoreAllSurfaces
      VBDXLoadSurface OffSurface.strFileName, OffSurface.dds

       rt = objPrimary.Blt(rPrimary, OffSurface.dds, OffSurface.rectDDS, DDBLT_WAIT)
    End If

End Sub

Listing 12 shows the DrawIt function

The code block that does the actual pixel transfer (or Blt) is the objPrimary.Blt method. The Blt method is described in Listing 13:
 

DirectDrawSurface7.Blt( _ 
  destRect As RECT, _ 
  ddS As DirectDrawSurface7, _
  srcRect As RECT, _ 
  flags As CONST_DDBLTFLAGS) As Long


Parameters:
  Object: A valid DirectDrawSurface7 object. 
  destRect: A RECT type specifying the destination area on which to blit
  srcRect: A RECT type specifying the source rect. 
  Flags: Flags for various blitting functionalities.

Listing 13 shows the declaration of the Blt function

Two things are worth pointing out here. The two RECT parameters, each specifying a rectangle on the source surface and the destination surface, can be specified as 'Empty' meaning that no members of the type have been set. The flags parameter is used to specify certain specialized options for the Blt. These options specify how the Blt is to be performed and can be used to create some special effects.

In the current sample we call the method on the primary surface, meaning that this surface is the destination surface. We specify the size of the destination surface in the RECT type that we initially filled with the dimensions of the primary surface. The source surface is specified as the second parameter. The third parameter is the source RECT, which we specify as the RECT that our VBDXSurface type contains. 

You'll remember that earlier we used the GetSurfaceDesc function and stored the surface dimension values in the rectDDS member of the VBDXSurface type. If we hadn't done it this way we would have to get the dimensions now and this could be a problem in a real-world application. The problem is the consumption of processor cycles for something that is basically unnecessary at this point of execution. By pre-filling the type we use a bit more memory (nothing to worry about), but we save the time it takes to build the members of the type. 

The last parameter, the flags parameter, is set to the constant value of DDBLT_WAIT. This causes the function to delay its return until it has completed execution – i.e. until the blitter has finished its job. If we did not specify this flag the function would have returned immediately. This is a very powerful feature, since it basically means that we could continue with our work while the blitter does its job. This is possible because the blitter itself is a processor located on the video card. In effect we achieve the effect of multi-tasking. A properly designed DirectDraw application can gain many CPU cycles by employing a scheme that uses this 'multi-tasking' environment. In later DirectX-files we will look more into this feature. The reason for specifying the DDBLT_WAIT flag in this application is that we have nothing to gain from letting the graphics processor run off on its own. In future DirectX-file™ we will examine other possible values that let us do some cool things.

As you can see from the code listing (NR 12) we catch and examine the return value. There is a very good reason for doing this, namely the fact that we can not be sure that the Blt is able to perform its stuff as we intend. One of the most common failures that the blitter encounters is the DDERR_SURFACELOST error. This error basically tells us that something has happened to our surfaces, something so terrible that the blitter has given up. What has actually happened is that our surfaces have lost their allocated memory and thus are just empty classes with no real purpose anymore. This usually happens when our application loses focus and some other program takes over the video memory. Remember that Windows is still a multi-processor environment where other programs can and should be able to do stuff even though we had -or thought we had- full control of the screen. The remedy for this error is quite simple – we call the RestoreAllSurfaces method on the main DirectDraw object and in a flash we have acquired the surface memory again. 

The story does not end here. Even though the memory is recaptured we don't have any old data left on the surfaces – meaning that all the graphics we have carefully loaded are lost. So we have to load them all again. No problem you might say, we can simply use the CreateSurfaceFromFile function that we initially used when we loaded and created the surface. Unfortunately we can't do that. We still have an empty surface sitting in memory and we have a fully functional surface that is also using some amount of memory. If we were to create a new surface using the above mentioned function we would actually get a completely new surface and not just a 'refill' of the old one. How the DirectDraw function handles our surface internally is unknown to us and thus we will minimize the possibility of creating problems for our application by making our own function for refilling a surface. This is what the VBDXLoadSurface function does. It is declared in the VBDXHelper module. We won't go deeper into this function here, since it is mostly GDI based, but we will return to it in a later DirectX-file™ (You may have noticed we have a few things we will follow up on in later files. This was done to try to help you focus on the fundamentals of what is admittedly a tough subject. Following every topic into extreme detail would probably lose many of those reading this).

After reloading the surface we simply Blt it to the primary surface one more time. This time we are so certain of ourselves that no error checking is done. Of course you should not do the same in your applications (the reason that we are ignoring error handling as much as we do, is simply because implementing a proper error handling routine would take up half the code in these samples, and move the focus away from what we are trying to demonstrate).

To see this reloading of the surface in action you could try and run the sample program and then switch it out (by pressing ALT-TAB). Then you should be returned to the normal Windows Desktop with your standard resolution. By setting focus on the DirectX sample again you will see a change in the resolution (provided that you don't run your windows in the same resolution as we use in the sample) and after some nanoseconds the graphics will be blitted onto the surface again – a reload of the surfaces has occurred.

Afterthoughts

In this DirectX-file we have taken the first look at surfaces and how we can use them to store and display graphics. We have covered the basics of the surface class and the very basics on how we can use them. In later DirectX-file™ we will dive deep into the DirectDraw7 surface class and examine the functionality it provides for us.

Conclusion of DirectX-file 1

In this file we have taken our first step into the world of DirectDraw and examined how to set up a DirectDraw program and the very basics of showing graphics. You are encouraged to experiment with the sample and make changes.

In the next DirectX-file (number 2) we will examine basic animation, transparent blitting and something called the clipper (watch your hands :-). We will also be looking at color keys and how we can use them for some special effects. 

See the file Reference for the DirectX-files  or the DirectX SDK for a description of this and other functions used.