VbdirectX
FAQ
Author: Arthur
Le Vernoy
Direct Draw
Overview
DirectDraw is a component of the
DirectX drivers. It allows you to
directly access the display memory while
maintaining full compatibility with the
Standard graphics API in the Windows
environment (GDI). DirectDraw allows you
to perform advanced graphics tasks, such
as blitting or accessing transparency
capabilities, without hardware
dependency.
This FAQ, will explain how to access
DirectDraw with Visual Basic and will
consist of the following articles;
1. Direct Draw Basics (Tutorial)
2. ModeX
3. 8 Bit Mode (Palettes and Stuff...)
4. 16 Bit Mode
5. Transparency
6. Overlays
7. Playing an AVI file (testing ground
for Overlays)
8. Algorithms
9. Questions and Answers
10. Credits & Acknowledgements
Direct
Draw Basics
DirectX through Visual Basic
To access DirectX through Visual
Basic, youll need a typelib file
describing all the DirectX API or a third
party ActiveX control. You can find a
free (*.tlb) file at the Visual
Basic Area 51 site courtesy of
Patrice Scribe. Please read the FAQ,
disclaimer and other information on his
page to know more about what you can do
with this file. Dont worry, you
wont have to pay for it!
It is also suggested that you download
the win32.tlb file, this file will give
you access to the Win32 API without
having to copy/paste declarations from
the API Viewer.
When you downloaded those files, you
must add a reference to them in your
Visual Basic project. See
Project|References menu in the Visual
Basic IDE.
Basic
Concepts
Surfaces
A surface, or
DirectDrawSurface object,
represents a linear area of
display memory. A surface usually
resides in the display memory of
the display card, although
surfaces can exist in system
memory. Unless specifically
instructed otherwise, during the
creation of the DirectDrawSurface
object, the DirectDraw object
will put the DirectDrawSurface
object wherever the best
performance can be achieved given
the requested capabilities.
DirectDrawSurface objects can
take advantage of specialized
processors on display cards, not
only to perform certain tasks
faster, but to perform some tasks
in parallel with the system CPU.[Taken from DirectX 5
SDK Help file]
|
BackBuffering
Concept
One way to create fast graphics
drawing is to draw directly on the
screen. Imagine now that one of your
end-users is trying to run your project
under a 286 Computer, your application
might not be able to draw graphics on the
screen at a decent rate. To avoid this,
well draw the entire scene on a
BackBuffer (Basically an
invisible Surface), and then draw this
BackBuffer on to the screen. This is
called BackBuffering.
Initializing
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
application on a graphics surface.
To create an instance of the
DirectDraw Object, your project must call
the DirectDrawCreate
function. This function 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 Byval
0&, which means DirectDraw
uses the default display driver for the
system. This also means that you
wont have to deal with hardware
stuff. The second parameter is the
DirectDraw object itself.
[See source code at the end of this
section]
Next, youll have to determine
the application behavior you want. Is the
app going to be fullscreen or windowed?
Is it going to use exclusive mode?
Exclusive Mode
This mode wont let other apps
(or yours
) set a DirectDraw Object
to the same mode. This mode significantly
increases DirectDraw's performance. I
suggest you use this mode always with DDSCL_FULLSCREEN
flag.
To do this, youll have to call
the SetCooperativeLevel method:
DirectDraw.SetCooperativeLevel(hWnd as
hWnd, dwFlags as long)
HWnd is the handle of the Form where
DirectDraw will perform.
DwFlags are parameters seperated by
the OR operator:
DDSCL_ALLOWMODEX
Allows the use of Mode X display
modes. This flag must be used with the
DDSCL_EXCLUSIVE and DDSCL_FULLSCREEN
flags.
DDSCL_ALLOWREBOOT
Allows CTRL+ALT+DEL to function while
in exclusive (full-screen) mode.
DDSCL_EXCLUSIVE
Requests the exclusive level. This
flag must be used with the
DDSCL_FULLSCREEN flag.
DDSCL_FULLSCREEN
Indicates that the exclusive-mode
owner will be responsible for the entire
primary surface. GDI can be ignored. This
flag must be used with the
DDSCL_EXCLUSIVE flag.
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.
i.e:
Ddraw.SetCooperativeLevel Me.Hwnd,
DDSCL_FULLSCREEN or DDSCL_EXCLUSVE
(This might be the best way for
DirectDraw to achieve good performance.)
[See source code at the end of this
section]
Next, you can change the Display Mode
to fit your application aims using the
SetDisplayMode method.
SetDisplayMode(dwWidth,dwHeight,dwBPP,dwRefreshRate,dwFlags)
(All parameters are long values)
dwWidth: X resolution
dwHeight: Y resolution
dwBPP: BitsPerPixels (8= up to 256
colors; 16= up to 65536 colors..)
dwRefreshRate: Set to 0; forget it.
DwFlags: the only flag availlable is
DDSDM_STANDARDVGAMODE, which lets you
switch from ModeX to Mode13, well
see that later.
[See source code at the end of this
section]
ModeX &
Mode13:
DirectDraw supports both Mode
13 and Mode X display modes. Mode
13 is the linear unflippable
320x200 8 bits per pixel
palettized mode known widely by
its hexadecimal BIOS mode number:
13. For more information, see
Mode 13 Support. Mode X is a
hybrid display mode derived from
the standard VGA Mode 13. This
mode allows the use of up to 256
kilobytes (KB) of display memory
(rather than the 64 KB allowed by
Mode 13) by using the VGA display
adapter's EGA multiple video
plane system.
On Windows 95,
DirectDraw provides two Mode X
modes (320´200´8 and
320´240´8) for all display
cards. Some cards also support
linear low-resolution modes. In
linear low-resolution modes, the
primary surface can be locked and
directly accessed. This is not
possible in Mode X modes.[Taken from DirectX 5
SDK HelpFile]
|
You could write
directly on the screen (Primary Surface),
but the best way to do it is using at
least one BackBuffer (The sample will use
only one BackBuffer). All the rendering
stuff will be written in a rendering loop
following this scheme:
------- BEGIN CODE -------
Do
Step
1: Clear the BackBuffer
Step
2: Render all surfaces (bitmaps, sprites,
whatever
)
Step
3: Render BackBuffer on Primary Surface =
FLIP
DoEvents
Loop
------- END CODE -------
The BackBuffer is attached
to the Primary Surface, which means it
will have the same behavior. To create
the Primary Surface, your application
will have to call the CreateSurface
function of the DirectDraw Object.
CAUTION: Do not forget this, trying to
create a surface to hold a bitmap for
instance, before creating the DirectDraw
Object, is a very common newbie mistake.
DDraw.CreateSurface(ddsd as
DDSURFACEDESC, DDS as
IDirectDrawSurface3, IUnknown as Long)
Ddsd: DirectDraw Surface Description
(See code below)
DDS: Surface Object
IUnknow: Always set to Byval
0& (For future DDraw
enhancement)
To attach the BackBuffer to the
Primary Surface use the following method:
PrimarySurface.GetAttachedSurface(ddc
as DDSCAPS, BackBuffer)
Well, you learned quite a lot of
theoretical stuff, now its time to
jump right in with some practice.
Heres the code:
' ------- BEGIN CODE -------
'--------------
' DECLARATIONS:
'--------------
Public DDraw
as IDirectDraw2
Public
PrimarySurface as IDirectDrawSurface3
Public
BackBuffer as IDirectDrawSurface3
'
-------------------------------------------
' Paste this code in the Load Event of
a Form
'
-------------------------------------------
Dim ddsd As
DDSURFACEDESC, ddc As DDSCAPS
If DDraw Is
Nothing Then
DirectDrawCreate
ByVal 0&, DDraw, Nothing
End If
Set
BackBuffer = Nothing
Set
PrimarySurface = Nothing
' This will
set your window border to 0.none (need
Win32.tlb)
Win32.SetWindowLong
Me.hWnd, Win32.GWL_STYLE, Win32.WS_POPUP
Or Win32.WS_VISIBLE
Win32.SetWindowPos
Me.hWnd, Win32.HWND_TOPMOST, 0, 0, 0, 0,
Win32.SWP_NOMOVE Or Win32.SWP_NOSIZE
' Set DDraw
Behaviour
DDraw.SetCooperativeLevel
Me.hWnd, DDSCL_EXCLUSIVE Or
DDSCL_FULLSCREEN
' Change
display Mode
DDrawObj.SetDisplayMode
ResX, ResY, BPP, 0, 0
' DirectDraw
Surface Description
With ddsd
.dwSize =
Len(ddsd) ' Do never forget this line!
.dwFlags =
DDSD_CAPS Or DDSD_BACKBUFFERCOUNT
.dwBackBufferCount
= 1 ' You can try with 2 or 3
.DDSCAPS.dwCaps
= DDSCAPS_PRIMARYSURFACE Or DDSCAPS_FLIP
Or DDSCAPS_COMPLEX Or
DDSCAPS_SYSTEMMEMORY
End With
' Creates the
Primary Surface with ddsd description
DDraw.CreateSurface
ddsd, PrimarySurface, Nothing
' Attaches
the BackBuffer to the Primary surface
ddc.dwCaps =
DDSCAPS_BACKBUFFER
PrimarySurface.GetAttachedSurface
ddc, BackBuffer
' ------- END CODE -------
Load a bitmap
on a surface
You might want to load a bitmap file
on a surface to be displayed through
DirectDraw. Well create two
reusable functions:
1. CreateSimpleSurface
2. CreateSurfaceFromBitmap
Why? Well, one of these days you might
also want to create a surface and write
directly to it, without loading any file
on it. So were going to call CreateSimpleSurface
in the CreateSurfaceFromBitmap
code.
' ------- BEGIN CODE -------
Function
CreateSimpleSurface(DDrawObj as
IDirectDraw2, nWidth As Long, nHeight As
Long) As IDirectDrawSurface3
If nWidth
<= 0 Then Exit Function
If nHeight
<= 0 Then Exit Function
Dim ddsd As
DDSURFACEDESC
Dim dds As
IDirectDrawSurface3
With ddsd
.dwSize =
Len(ddsd)
.dwFlags =
DDSD_CAPS Or DDSD_HEIGHT Or DDSD_WIDTH
.DDSCAPS.dwCaps
= DDSCAPS_OFFSCREENPLAIN Or
DDSCAPS_SYSTEMMEMORY
.dwWidth =
nWidth
.dwHeight =
nHeight
End With
DDrawObj.CreateSurface
ddsd, dds, Nothing
Set
CreateSimpleSurface = dds
Set dds =
Nothing
End Function
' ------- END CODE -------
As you can see, its using
basically the same code as for creating
the Primary Surface. However, the ddsd
description of the surface is quite
different. We wont explain it for
the moment, because its quite
useless now. You will note the presence
of the DDSCAPS_SYSTEMMEMORY
flag in the ddsd.DDSCAPS.dwCaps
property. This flag will tell DirectDraw
that youre emulating Video RAM
(VRAM) into the RAM. Its slower but
solves a lot of problems. The best way to
optimize DirectDraw performance is to try
to create a surface using VRAM. To do
that simply change the flag
DDSCAPS_SYSTEMMEMORY by
DDSCAPS_VIDEOMEMORY. If the CreateSurface
method sends Visual Basic an Error called
DDERR_OUTOFMEMORY (referenced in
directx.tlb) then use RAM instead.
Now, lets deal with the CreateSurfaceFromBitmap
Function. As mentioned above, this
function will call the
CreateSimpleSurface described above to
create the DirectDraw surface. This
function will be able to load BMP, GIF
and JPEG files.
' ------- BEGIN CODE -------
Function
CreateSurfaceFromBitmap(Filename as
String) as IDirectDrawSurface3
' Does
Filename exists?
If
dir$(Filename) = "" then exit
function
' Is it
really a BMP, JPEG or GIF file
Dim hFile as
Long, TmpByte as Byte
hFile =
FreeFile
Open Filename
for Binary as hFile
Get hFile, 1,
TmpByte
If TmpByte =
66 or TmpByte = 71 Or TmpByte = 255 Then
Else
Exit Function
End If
Close hFile
Dim frm As
Form, PictureWidth As Long, PictureHeight
As Long, Picture As StdPicture,
hdcPicture As Long, hdcSurface As Long,
DDS as IDirectDrawSurface3
' Load The
File
Set frm =
Screen.ActiveForm
Set Picture =
LoadPicture(Filename)
PictureWidth
= frm.ScaleX(Picture.Width, vbHimetric,
vbPixels)
PictureHeight
= frm.ScaleY(Picture.Height, vbHimetric,
vbPixels)
' Create the
Surface
Set DDS =
CreateSimpleSurface(PictureWidth,
PictureHeight)
hdcPicture =
Win32.CreateCompatibleDC(ByVal 0&)
Win32.SelectObject
hdcPicture, Picture.Handle
DDS.Restore
DDS.GetDC
hdcSurface
Win32.StretchBlt
hdcSurface, 0, 0, PictureWidth,
PictureHeight, hdcPicture, 0, 0,
PictureWidth, PictureHeight, SRCCOPY
DDS.ReleaseDC
hdcSurface
Win32.DeleteDC
hdcPicture
Set
CreateSurfaceFromBitmap = DDS
End Function
' ------- END CODE -------
Remember the Rendering loop?
To clear the BackBuffer(Or any other
Surface), youll have to perform a
blit on this surface
' ------- BEGIN CODE -------
Sub
Clear(Surface as IDirectDrawSurface3,
Color as Long)
Dim FX As
DDBLTFX
With FX
.dwSize =
Len(fx)
.dwFillColor
= Color
End With
' Fill the
Whole Surface with the same Color
BackBuffer.Blt
ByVal 0&, Nothing, ByVal 0&,
DDBLT_COLORFILL, FX
End Sub
' ------- END CODE -------
To clear the backBuffer use this line
of code:
' ------- BEGIN CODE -------
Clear
BackBuffer, 0
' ------- END CODE -------
For those how dont know what a
RECT is, Heres the explaination:
A RECT is a user-type used to
store a Rectangle position:
Public Type RECT
Left as Integer
Right as Integer
Top as Integer
Bottom as Integer
End Type
The Following Step in the rendering
loop is Blitting Capabilities, that is to
say, to be able to draw those surfaces
previously created on the BackBuffer.
This can be done using the Blt method of
the BackBuffer.
Please note that if youre trying
to blit outside the Surface RECT (Outside
The screens edges), DirectDraw
wont display anything. The
following piece of code, will fix this
problem by manipulating the Destination
& Source RECTS.
I wont explain this, its
just logic and doesnt have anything
to do with DirectDraw in it-self.
The Surface wont be displayed if
youre not using the code below.
' ------- BEGIN CODE -------
Sub
Draw(BackBuffer as IDirectDrawSurface3,
BackBufferWidth as Long, _
BackBufferHeight
as Long, Surface as IDirectDrawSurface3,
PosX as Long, _
PosY as Long,
nWidth as Long, nHeight as Long)
Dim SrcRect
As RECT, DestRect As RECT, fx As DDBLTFX,
BltFlag As Long
' Update
RECTS
' Standard
Way...
With SrcRect
.Left = 0
.Right =
nWidth
.Top = 0
.Bottom =
nHeight
End With
With DestRect
.Left = PosX
.Top = PosY
.Right =
.Left + nWidth
.Bottom =
.Top + nHeight
End With
' Check
Boundaries
If PosX <
0 Then
DestRect.Left
= 0
SrcRect.Left
= - PosX
End If
If PosY <
0 Then
DestRect.Top
= 0
SrcRect.Top =
- PosY
End If
If
DestRect.Right > BackBufferWidth Then
SrcRect.Right
= SrcRect.Right - (DestRect.Right -
BackBufferWidth)
DestRect.Right
= BackBufferWidth
End If
If
DestRect.Bottom > BackBufferHeight
Then
SrcRect.Bottom
= SrcRect.Bottom - (DestRect.Bottom -
BackBufferHeight)
DestRect.Bottom
= BackBufferHeight
End If
' BLIT
With FX
.dwSize =
Len(FX)
.dwROP =
Win32.SRCCOPY
End With
BackBuffer.Blt
DestRect, Surface, SrcRect, DDBLT_WAIT Or
DDBLT_ROP, FX
End Sub
' ------- END CODE -------
In this piece of code, transparency is
not supported. See Transparency chapter
for more information.
The Last part of the rendering loop is
to Blit the BackBuffer on the Primary
Surface.
' ------- BEGIN CODE -------
Sub Flip
Dim dds As
IDirectDrawSurface3
Set dds =
PrimarySurface
dds.Flip
Nothing, DDFLIP_WAIT
Set dds =
Nothing
If Err.Number
= DDERR_SURFACELOST Or Err.Number =
DDERR_SURFACELOST - &H100000 Then
BackBuffer.Restore
PrimarySurface.Restore
End If
Err.Clear
End Sub
' ------- END CODE -------
And Last but not least: You must
destroy any Surfaces or DirectDraw
Objects created by setting them to
Nothing, Ortherwise your application will
crash.
On the KeyPress Event of the Form
paste the following code:
' ------- BEGIN CODE -------
If KeyAscii =
27 then ' IF ESC KEY IS PRESSED
Unload Me
End if
' ------- END CODE -------
Then, in The Unload Event of your
Form:
' ------- BEGIN CODE -------
DDraw.FlipToGDISurface
' Restore
Display Mode
DDraw.RestoreDisplayMode
' Restore
Thread to Windows
DDraw.SetCooperativeLevel
0, DDSCL_NORMAL
' Destroy
Surfaces
Set
BackBuffer = Nothing
Set
PrimarySurface = Nothing
Set DDraw =
Nothing
' CAUTION:
' If you have
any other surfaces created with
CreateSimpleSurface
' or
CreateSurfaceFromBitmap then type this
line of code:
'
' Set Surface
= Nothing
'
' where
Surface is the DirectDraw Surface Object
you want to destroy
' ------- END CODE -------
Well, thats the end of this
tutorial folks. If you can do this (and
if you understand it), youll know
enough of the DirectDraw features to do a
good job. Try to type all of this sample
code without the help of this file,
thats the only way to learn it.
Dont forget that DirectX is a
Low-level API -it wont forgive any
mistakes!
Note: All this coding material has
been tested, and should work quite well.
Its not really optimized, but
should be much faster than GDI.
Back
to Tutorials - Main
|