Direct Draw 7 
by Peter Kuchnio 
11/03/00 

introduction 

This is a revamped DirectDraw tutorial, this time using DirectX 7. As many of you probably know, DirectX 7 has added official support for DirectX in Visual Basic. Before we had to make do with Patrice Scribe's 3rd party tlbs. Before you start, you might want to download the DirectX 7 SDK from MicroSoft's site.  
The SDK is not necessary but it will help you with the really specific functions. Anyway, for this tutorial all you will need is DirectX 7 installed.   

What exactly is DirectDraw? It's a part of the DirectX API. It deals with your 2D video card. In other words it talks directly with the hardware which is faster than GDI which has to deal with the system first. Plus GDI can't take advantage of any specific hardware extras that come with your video card. The thing is, you don't have to program any hardware specific instructions.   

DirectDraw deals with the video card through drivers that the card manufacterer has produced, usually with help from Microsoft. DirectDraw uses the HAL and the HEL (hardware emulation layer). First it tries to use the HAL, but if the call or feature is not supported by the hardware it uses the HEL, in which case it does the task through software.  

What is explained in this tutorial? First off, starting an Exclusive Fullscreen mode DirectDraw session. Then using two surfaces to do some animation, and of course using color-keying. 

the code 

Before you start, make sure that you have the DirectX 7 type-library checked under Projects...References. 

The first part of starting DirectDraw is to declare the main DirectX and DirectDraw objects. You should remember that the DirectX object has to be declared with a New. 

'Main DirectX object 
Dim Dx As New DirectX7 
'Maub DirectDraw object 
Dim DDraw As DirectDraw7 

Here we declare our surfaces. Nothing too complicated so far! 

'Surfaces 
'Primary surface 
Dim ddsPrimary As DirectDrawSurface7 
'Backbuffer 
Dim ddsBackBuffer As DirectDrawSurface7 

'Background 
Dim Background As DirectDrawSurface7 
Dim s640x480rect As RECT 'RECT variable 
'Sprite 
Dim Sprite As DirectDrawSurface7 
Dim SpriteRECT As RECT 'Sprite's RECT variable 

The first two were our Primary surface (ie: the screen or stuff you actually see) and the second one is the backbuffer, which we draw to first and do all the dirty work on. This keeps the animation smooth and flicker free. After this we declare our background and sprite surfaces and their RECTs. A RECT is a set of coordinates that specifies the dimensions of the surface. Ordinarily you would have to fill this out yourself, however for this sample we are using a subroutine that will fill out our RECT for us. This saves a lot of time to say the least =) 

Below we have the surface descriptions for the Primary surface and the Backbuffer. 

'Surface Descriptions 
Dim ddsdPrimary As DDSURFACEDESC2 
Dim ddsdBackbuffer As DDSURFACEDESC2 

Dim running As Boolean 

Private Sub Form_Click() 
    'If user clicks on form end the program 
    running = False 
End Sub 

Private Sub Form_Load() 
    running = True 'the app is running 
     
    'Initializing DirectDraw 
    On Error Resume Next 
     
Below we actually create the DirectDraw object. 

    'Create Directdraw 
    Set DDraw = Dx.DirectDrawCreate("") 
     
    Form1.Show 'show the form 
     
Here we set the display mode for DirectDraw. We set the cooperative level to exclusive, so our app is the only one using the graphics card. We also set it to fullscreen mode. We then set the resolution and the BPP (Bits Per Pixel) or Colour Depth, in this case 16-bit or true colour. This is one of the most common graphics modes at the moment. 

    'Setting cooperative level and display mode 
    'Fullscreen exclusive mode 
    DDraw.SetCooperativeLevel Form1.hWnd, DDSCL_FULLSCREEN Or DDSCL_ALLOWMODEX Or DDSCL_EXCLUSIVE 
    '640x480 resolution in 16-bit colour (about 16 000 colours) 
    'This is the most common BPP (Bits-Per-Pixel) used today. 
    DDraw.SetDisplayMode 640, 480, 16, 0, DDSDM_DEFAULT 
     
Below we fill out the surface description for the Primary surface. It isn't anything too complex, we just assign flags that, well, describe what the surface does. In this case we describe it as the primary surface with 1 backbuffer and then we actually create the surface.   

    'Fill out Primary Surface Description 
    ddsdPrimary.lFlags = DDSD_CAPS Or DDSD_BACKBUFFERCOUNT 
    'Using Video Memory for the primary surface and backbuffer 
    ddsdPrimary.ddsCaps.lCaps = DDSCAPS_PRIMARYSURFACE Or DDSCAPS_FLIP Or DDSCAPS_COMPLEX 
     
    ddsdPrimary.lBackBufferCount = 1 'One Backbuffer 
     
    'Create the Primary Surface 
    Set ddsPrimary = DDraw.CreateSurface(ddsdPrimary) 
     
Then we fill out the surface description for the backbuffer and attach it to the primary surface.

    'Backbuffer 
    Dim Caps As DDSCAPS2 
    Caps.lCaps = DDSCAPS_BACKBUFFER 
     
    Set ddsBackBuffer = ddsPrimary.GetAttachedSurface(Caps) 
    'Fill out description 
    ddsBackBuffer.GetSurfaceDesc ddsdBackbuffer 
     
Here we load our sprites from the disk into surfaces we declared before. Don't let this fool you though, this isn't a DirectX call. We call a subroutine which loads them. You might want to keep this, since with the bmp loading sub we actually automatically fill out the RECT variable for the surface, as opposed to doing it by hand. This saves a LOT of time in the long run.

    'Load bitmap files into surfaces 
    DDCreateSurface Background, App.Path & "\background.bmp", s640x480rect 
    DDCreateSurface Sprite, App.Path & "\sprite.bmp", SpriteRECT 
     
Now we actually get to the render loop. First we do a colorfill, which fills a surface with a specified colour. This is useful to make sure there aren't any leftover graphics on the backbuffer before we blit. Then we actually do the blitting. First the background, and then the sprite.

The background is blitted without any transparency. The sprite however, is, so the black around it is not drawn. This is accomplished by adding the DDBLTFAST_SRCCOLORKEY flag to the call. The actual assigning of the colour-key is done in the bitmap loading subroutine. Then we flip the backbuffer onto the primary surface and we are technically done!

    'Render loop 
    Do 
        DoEvents 
        'Colorfill the background with flat black 
        ddsBackBuffer.BltColorFill s640x480rect, RGB(0, 0, 0) 
         
        'Use BltFast to draw the background (no transparency) 
        ddsBackBuffer.BltFast 0, 0, Background, s640x480rect, DDBLTFAST_WAIT 
         
        'Use BltFast to draw the Sprite (transparent) 
        ddsBackBuffer.BltFast 50, 50, Sprite, SpriteRECT, DDBLTFAST_SRCCOLORKEY Or DDBLTFAST_WAIT 
         
        'Flip the backbuffer to the primary surface 
        ddsPrimary.Flip Nothing, DDFLIP_WAIT 
    Loop Until running = False 
     
The following deals with unloading DirectDraw. We have to first destroy any surfaces we created to free up the memory, then we restore the display mode.

    'Unloading 
    'With DirectDraw you must unload all your surfaces etc. 
    'to clear up memory. 
    Set Background = Nothing 
    Set Sprite = Nothing 
     
    Set ddsPrimary = Nothing 
    Set ddsBackBuffer = Nothing 
     
    DDraw.RestoreDisplayMode 
    'Restore to normal display mode 
    DDraw.SetCooperativeLevel Form1.hWnd, DDSCL_NORMAL 
    End 
End Sub 

Here is the actual sub that performs loading a bitmap from the disk into a surface. Basically we fill out the surface's description, with the added feature of keeping the surfaces in video memory or system memory.  

Private Sub DDCreateSurface(surface As DirectDrawSurface7, BmpPath As String, RECTvar As RECT, Optional TransCol As Integer = 0, Optional UseSystemMemory As Boolean = True) 
    'This sub will load a bitmap from a file 
    'into a specified dd surface. Transparent 
    'colour is black (0) by default. 
     
    Dim tempddsd As DDSURFACEDESC2 
     
    Set surface = Nothing 
     
    'Load sprite 
    tempddsd.lFlags = DDSD_CAPS 
    If UseSystemMemory = True Then 
        tempddsd.ddsCaps.lCaps = DDSCAPS_SYSTEMMEMORY Or DDSCAPS_OFFSCREENPLAIN 
    Else 
        tempddsd.ddsCaps.lCaps = DDSCAPS_OFFSCREENPLAIN 
    End If 
    Set surface = DDraw.CreateSurfaceFromFile(BmpPath, tempddsd) 
     
    'set the RECT dimensions 
    RECTvar.Right = tempddsd.lWidth 
    RECTvar.Bottom = tempddsd.lHeight 
     
    'Colour key 
    Dim ddckColourKey As DDCOLORKEY 
    ddckColourKey.low = TransCol 
    ddckColourKey.high = TransCol 
    surface.SetColorKey DDCKEY_SRCBLT, ddckColourKey 
     
     
End Sub 
   
That's pretty much it for this DirectDraw tutorial. Feel free to download the source. If you're still having trouble with DirectDraw you can email me or talk about it on our forum!

Peter