Loading &
Displaying an Image
Author:
Adam Hoult
In this tutorial we will
cover the basics on how to load and
display an image on our newly opened
DirectDraw screen, created in the
tutorial Opening a
DirectDraw Device. For this
example we will build on that code and
add just a few lines of code to display a
background picture. We will assume you
already know how to create a project in
VB, and know a little about using forms.
You will also need to have gone through
the last tutorial because we will only
add to that, we will not create a whole
new application. Now onto the tutorial.
NOTE: This tutorial is designed for those
of you with VB5. If you find any
discrepancies with this Tutorial, please mail me and I will
correct it. NOTE: the decriptions of the
various pieces of source code appear underneath
it. So don't get mixed up with the
various explanations.
Step 1 -
Loading the Project
First thing you will need
to do is to load the source code from the last tutorial.
Step 2 -
General Declarations and What They Mean
Ok.
For this next tutorial we will need to
add a load of new General Declarations,
and a few API calls. These API calls
basically let us load in the image, and
place it onto a DirectX Surface. For
those of you who dont know what a surface
is, it's an area of memory (normally
system memory) which stores an image,
which we can then BLT (or copy) to the
screen. Here is a list of all the new
declarations we need to make, and what
they all mean.
'Standard Consts which
need to be assigned
'for the API calls to work.
Const LR_LOADFROMFILE = &H10
Const LR_CREATEDIBSECTION = &H2000
Const SRCCOPY = &HCC0020
These constants are
assigned to be used by the API calls
which you will find below. The LR_LOADFROMFILE and
LR_CREATEDIBSECTION constants
are used by the LoadImage API
call. They basically specify that we want
to load the Image from a file, and store
it in memory. The SRCCOPY constant
is used by the StretchBlt API
call in this tutorial, and what it
basically means is that the image will be
copied from the Source (in this case a
handle pointing to an area of memory) and
copied to the Specified Destination
Surface. You will see more about this
later in the tutorial.
'This structure is
needed for the LoadImage
'API call, the information about the
bitmap
'will be returned into a variable defined
as
'this type.
Private Type BITMAP
bmType As Long
bmWidth As Long
bmHeight As Long
bmWidthBytes As Long
bmPlanes As Integer
bmBitsPixel As Integer
bmBits As Long
End Type
When we load an image
using the LoadImage
API call we pass it a variable, defined
as a BITMAP
type. The bitmap must be defined as
above. If the call to the LoadImage
function is successful, all the
information about the bitmap will be
returned into this variable. The only
bits we will be interested in during this
tutorial will be; Bitmap Width (bmWidth)
and Bitmap Height (bmHeight).
These will store the width and height of
the bitmap which we load.
NOTE: In these various
functions you may see that a line will be
split into two with a "_"
symbol. This will work in Visual Basic,
but you may want to remove them. Simply
delete the Underscore and move the second
line onto the first. The reason why I
have done this is because internet
Explorer will wrap the text, and it
starts to look confusing.
' Various Standard API
Functions for manipulation of DCs and
Bitmaps
Private Declare
Function CreateCompatibleDC Lib "gdi32" (ByVal hdc _
As Long) As Long
Private Declare
Function DeleteDC Lib "gdi32" (ByVal hdc As Long) _
As Long
Private Declare
Function DeleteObject Lib "gdi32" (ByVal hObject _
As Long) As Long
Private Declare
Function GetObject Lib "gdi32" Alias
"GetObjectA" _
(ByVal hObject As Long, ByVal nCount As Long, _
lpObject As Any) As Long
Private Declare
Function SelectObject Lib "gdi32" (ByVal hdc As Long, _
ByVal hObject As Long) As Long
Private Declare
Function GetDC Lib "user32" (ByVal hwnd As Long) _
As Long
Private Declare
Function LoadImage Lib "user32" Alias
"LoadImageA" _
(ByVal hInst As Long, ByVal lpsz As String, _
ByVal un1 As Long, ByVal n1 As Long, ByVal n2 _
As Long, ByVal un2 As Long) As Long
Private Declare
Function StretchBlt Lib "gdi32" (ByVal hdc As Long, _
ByVal x As Long, ByVal y As Long, ByVal _
nWidth As Long, ByVal nHeight As Long, _
ByVal hSrcDC As Long, ByVal xSrc As Long, _
ByVal ySrc As Long, ByVal nSrcWidth As Long, _
ByVal nSrcHeight As Long, ByVal dwRop As Long) _
As Long
I won't explain what all
these API functions do in this section,
just copy them in as you see them, and I
will explain them in the sections that
use them.
Dim BackDrop As DirectDrawSurface2 'BackGround Picture
Next we need to Dimension
a new surface, we will use this surface
to store the picture that we are going to
display, for this example we will load
and display a 640x480 background picture.
We will load the picture into this
surface later on in the tutorial.
'Rects
'Used to store the size of the picture so
that when
'we Blt it to the surface, we can specify
how big the
'picture is.
Dim BackDropRect As RECT
Once we have loaded the
picture we need to store its dimensions.
To do this we need to create a RECT
object. This object has the following
properties; Left
, Right,
Top
and Bottom.
We will set these values once we load the
bitmap. The reason why we have to set
these RECT
objects, is because the BltFast
function only accepts this as a valid set
of coordinates. I will explain more
underneath the BltFast
function in Form_Load.
Step 3 - On
Form Load
' Attach the surface
ddsBack to the
' Primary surface as the back buffer.
ddsFront.GetAttachedSurface
ddCaps, ddsBack
' Show the form
frmMain.Show
Do you remember in the
last Tutorial, near the bottom of the Form_Load
we attached the Back Buffer to the
Primary Surface, and loaded the form. You
can see that above this explanation. Well
in between those two functions, we now
need to load in the picture to our BackDrop
Surface, which we dimensioned earlier. So
insert the following code between the two
above functions.
'Load in the bitmap
and paste it onto the
'BackDrop surface we specified earlier.
Set BackDrop = LoadBitmapIntoDXS(dd, App.Path &
"\background.bmp")
'Set the rect with the
bitmap's dimensions this
'is used when we BLT the image.
BackDropRect.Left = 0
BackDropRect.Top = 0
BackDropRect.Right = 640
BackDropRect.bottom = 480
Now, as soon as we
Attached the back buffer we now need to
load in the picture, and place it onto
our temporary surface. We do that as
shown in the source code above. As there
is no LoadBitmap function currently
implemented into the Type Library, we
need to do it manually. So we have to
create a new function. (See Step 4 to
create the LoadBitmapIntoDXS
function). NOTE: for this
particular example to work, your image
must have the dimensions 640x480 and must
be name background.bmp and placed in the
same directory as the source code. As we
dimensioned our BackDrop
surface earlier we can now Set
it using the LoadBitmapIntoDXS
function, which will place
the specified image onto the surface for
use later. We need to pass this function
two parameters. The first paramater is a
valid Direct Draw Device, which we
defined earlier. In this case it's the dd variable.
The second of the parameters is a valid
Windows Bitmap file. In this case we pass
it App.Path &
"\background.bmp" .This
will pass it the directory where this
program is running from, and the name of
a file. NOTE: the App.Path
function does not return a trailing
slash. After we have loaded the BitMap,
we need to store its dimensions (shown
above) to pass to the Source Coordinates
section of the BltFast function
(Explained Later).
'Rendering Loop
Do Until Running = False
' Get any key presses
DoEvents
'BLT what is on the
BackDrop surface
'onto the back buffer.
ddsBack.BltFast 0, 0, BackDrop, BackDropRect, DDBLTFAST_WAIT
Remember our rendering
loop ?? Well now we need to add
somethings to it other then the DoEvents function.
First thing we need to do is Copy the
image from our temporary BackDrop
surface, onto the back buffer. We do this
by using the BltFast
function. This function can be called
from any surface, but you need to call it
from the surface you want to copy the
image TO. So an explanation of the BltFast function.
The first and second paramters which we
have to pass to this function are X and Y
coordinates. These coordinates specify
the position on the DESTINATION surface
you want the Top Left point of the SOURCE
image to be copied. The next parameter is
the actual source surface you want
copying to the surface calling the BltFast
function. In this case we pass it our BackDrop
surface. The third parameter is the RECT
object which we defined earlier. This
rect is the area of the Source image
which will be copied. For example if we
specified the following coordinates in
our BackDropRect
object; BackDropRect.Left
= 0, BackDropRect.Top = 0,
BackDropRect.Right = 100,
BackDropRect.Bottom = 100,
then a section of 100x100 would be copied
from the Source Surface. The final
parameter is the method of Copying. By
specifiying DDBLTFAST_WAIT
we are asking the system
to wait until we have finished copying
the image to the Destination Surface,
before we let the program carry on
executing.
'This next section
flips the back buffer
'and sends it to the
front. We need to do
'a check to see if the
surface has been lost
'if it has restore the
area of memory assigned
On Error Resume Next
Do
ddsFront.Flip Nothing, 0
If Err.Number = DDERR_SURFACELOST Then
ddsFront.Restore: Err.Number = 0
End If
Loop Until Err.Number = 0
Loop
This section of the code,
swaps what we rendered on the Back
Buffer, and Flips it to the Primary
surface. When we flip it, we need to
check to make sure that the Surface
Wasn't lost when it was flipped. If it
was we need to restore it and flip it
again. We need to keep repeating this
until the flip is successful.
'Quit out now.
Unload Me
The final thing we need to
add is the Unload Me
piece of code. This has to be moved from
the KeyPress function which we specified
in the last tutorial. The reason for this
is that , before, we weren't flipping the
Back Buffer, so we could unload
everything before we ended the rendering
loop. If we leave the unloading code in
the KeyPress function, the rendering loop
will not have chance to exit out before
we unload all the surfaces. This means
that the program will get stuck in the
Rendering loop constantly erroring,
because the surface is LOST. Trust me,
you need to do it even if you dont
understand why :) .
Step 4 -
New Function to Load The Image
As I explained above, we
need to write a new function which will
load in the image. This is the main
reason why we need to specify all of
those API calls in Step 2. In this
section we will completely write a
function to do this. To use it you will
need to use some code similar to this. Set BackDrop = LoadBitmapIntoDXS(dd, App.Path &
"\background.bmp"). I
would advise you to just copy and paste
the function from the Example
Source Code
which accompanies this tutorial, and just
to use this section of the tutorial as a
reference to the various functions.
Remember that you only have to write this
function once, and then whenever you want
to load an image simply call this
function. DO NOT write this code,
everytime you want to load an image, that
would be very silly :).
Private Function LoadBitmapIntoDXS(DXObject As DirectDraw2, ByVal BMPFile As String) As DirectDrawSurface2
' You can reuse this
function and modify it to your needs
' you will always need a function such as
this to load bitmaps
' onto your surface, so if your a
beginner to API calls etc
' you need pay little attention to them.
They will rarely need
' to be changed.
Dim hBitmap As Long ' Stores the handle of
the loaded bitmap
Dim dBitmap As BITMAP ' Stores all the info
about the Bitmap
Dim TempDXD As DDSURFACEDESC ' Temporary surface
description
Dim TempDXS As DirectDrawSurface2 ' Temporary surface,
to render the bitmap.
Dim dcBitmap As Long ' Another handle to
the image
Dim dcDXS As Long ' Handle to surface
context
Dim ddck As DDCOLORKEY ' Colorkeys, We will
look at these in the next tutorial.
'For now set the color
keys to black
'We will use these properly in the
tutorial
'Displaying Transparent Images.
ddck.dwColorSpaceLowValue = 0
ddck.dwColorSpaceHighValue = 0
I think that these are
mainly explained as well as they need to
be, just look at the comments to see what
they mean. The colorkey section will be
explained in the next tutorial.
' Load the bitmap
using standard API calls.
hBitmap = LoadImage(ByVal 0&, BMPFile, 0, 0, 0, LR_LOADFROMFILE Or LR_CREATEDIBSECTION)
The LoadImage
function, will load the file (which
passed to this function via the BMPFile
parameter), and return the handle to that
image. We will then use the handle for
the various functions below. These
parameters will mainly always stay the
same, the only parameters you may need
some explanation, are the following. The
second parameter must be passed the Path
and Filename of the image you wish to
load. The sixth parameter will really
always need to be LR_LOADFROMFILE Or LR_CREATEDIBSECTION.
These parameters specify that you want to
load the image from the file (specified
in the second parameter) and that you
want it to store the picture in the
format we need it in.
' Get bitmap
information
GetObject hBitmap, Len(dBitmap), dBitmap
This piece of code will
return all the information about the
bitmap we just loaded, and store it into
the dBitmap
object. We dimensioned the dBitmap
object earlier on this function, and
defined it as being a BITMAP
type. We defined the structure of this
type in Step 2. The first parameter is
the handle of the bitmap which was
loaded. This is the handle which was
returned by the LoadImage
function. The second parameter is the
Length of the dBitmap
object, this is just so the GetObject function
knows how many bytes of memory to read,
when it is retrieving the information it
needs. The final parameter is the actual
object that we want the information to be
stored. In this case it is the dBitmap
object.
' Fill in DX surface
description
'(see the tutorial for more info on this)
With TempDXD
.dwSize = Len(TempDXD)
.dwFlags = DDSD_CAPS Or DDSD_HEIGHT Or DDSD_WIDTH
.DDSCAPS.dwCaps = DDSCAPS_OFFSCREENPLAIN Or DDSCAPS_SYSTEMMEMORY
.dwWidth = dBitmap.bmWidth
.dwHeight = dBitmap.bmHeight
End With
I think an explanation of
all of these flags etc are in order. This
is how we specify what type of surface we
want to create (also explained in The
Opening a DirectDraw device tutorial).
The dwSize
property is fairly simple all we need to
pass it here, is the size of the our
surfaces description object. In this case
we are using TempDXD
which was defined earlier on in this
function. Next is the dwFlags
property. To create this type of surface
(i.e one which just stores an image, and
isn't a BackBuffer or Primary surface) we
only need to specify the DDSD_CAPS Or DDSD_HEIGHT Or DDSD_WIDTH constants.
These basically mean that only the DDSCAPS,
dwWidth
and dwHeight
properties will be read when creating the
surface. Next is the DDSCAPS
property. This is where we specify what
type this surface will be, again for this
type of surface we need to specify DDSCAPS_OFFSCREENPLAIN Or DDSCAPS_SYSTEMMEMORY.
These basically mean that the surface
will be a standard surface (not a
BackBuffer or Primary surface) and that
it will be stored in system memory not
Video memory (otherwise you would need a
32meg graphics card ). In the dwWidth
and dwHeight
properties, we need to specify how big
this surface will be. In this case we
pass it the dimensions of the Bitmap
which we loaded earlier. If you didn't
read the section about loading an image
(just above this one), then your naughty,
go read it.
' Create Temporary DX
surface to render too
DXObject.CreateSurface
TempDXD, TempDXS, Nothing
Again, here is where we
create this surface, we need to pass it
the Description object (which we have
just defined) and the actual surface,
which will be created. Note: this surface
must be already defined, this function
simply allocates the area of memory to
that surface. We dimensioned it at the
top of this function.
' Create API memory DC
dcBitmap = CreateCompatibleDC(ByVal 0&)
This section and the next
ones will handle the actual copying of
the Loaded image and place them onto the
surface. This piece of code, creates a
compatible Device Context (i'm not sure
what that exactly means but it sounds
good, and you have to do it :). We need
this so that we can copy the image data
and select it.
' Select the bitmap
into API memory DC
SelectObject dcBitmap, hBitmap
This function will select
the data stored in the handle which was
returned from the LoadImage function
above, and will store this selection in
the dcBitmap
object which we created above.
' Restore the memory
for the temporary surface
TempDXS.Restore
' assign the bitmaps
dc to the surface dc
TempDXS.GetDC dcDXS
' Blit BMP from API DC
into DX DC using standard API BitBlt
StretchBlt dcDXS, 0, 0, TempDXD.dwWidth, TempDXD.dwHeight, dcBitmap, 0, 0, dBitmap.bmWidth, dBitmap.bmHeight, SRCCOPY
These are the final pieces
of code which handle the final copy of
the Loaded image onto the temporary
surface. First of all we restore the area
of memory allocated to our temporary
surface, just in case it was lost. Next
we need to tell the Temporary Surface,
that whatever is placed into our
Temporary Device context will also be
what is copied to itself (this is the
best way i could describe what its doing
in simple terms.). We then do the final
copy by using the StretchBlt
function (i know you could use the BitBlt
function, but this makes it easier if you
want to stretch a bitmap to fit a bigger
surface). The first parameter we need to
pass it is the temporary Device Context (dcDXS)
which we also assigned to our Surface (TempDXS).
These were both defined and created
earlier. The next two are the X and Y
coordinates of the destination Surface
(or in this case Device Context). These
specify the Top Left hand corner where
the Image will be copied. The next two
are the Width and Height of the
destination surface (i.e how big is the
surface we are copying to). The next
parameter is the Source Surface ( or
Device Context) which we are copying
from. Again the next two specify the Top
Left Corner of the Source Image. The next
two parameters specify the width and
height of the Source bitmap. and the
final parameter just specifies that we
want to Copy The source image to the
Destination one. I know that this is a
bit sketchy, but you will rarely need to
change this function. If you really do
want a better explanation please feel
free to e-mail me and I will be happy to
go through it with you.
'Again we will show
this later in the next example
TempDXS.SetColorKey
DDCKEY_SRCBLT, ddck
No need to worry about
this yet, It will be explained in the
next tutorial.
' Return created DX
surface
Set LoadBitmapIntoDXS = TempDXS
Next we need to pass the
Temporary surface in which we were doing
all the rendering, back to whatever was
calling it. We do this by using the above
code. NOTE: you must use the Set
command, otherwise it will fail.
' Cleanup
TempDXS.ReleaseDC
dcDXS
DeleteDC dcBitmap
DeleteObject hBitmap
Set TempDXS = Nothing
Finally we finish off by
freeing all the memory we used, and
returning control of certain Device
Contexts back to the operating system.
End Function
Step 5 -
Did We Press Escape ??
Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer)
'When we press escape,
Stop the rendering loop
'(rendering loop is in
Form_Load) NOTE: in this example
'we have to remove the
Unload Me line and move it
'to underneath the
rendering loop, otherwise we cause
'an error and the
program will not quit.
If KeyCode = vbKeyEscape Then
Running = False
End If
End Sub
Basically, every time a
key is pressed, this Sub is called by the
program. We do a quick check to see if
the Key which was pressed was escape. vbKeyEscape is
a Constant defined within VB5, a list of
all the keycodes are shown in the Visual
Basic Help. If we did press escape, we
set the Running to False.
This will end the Rendering Loop started
in the Form_Load event. Note that we need
to remove the Unload Me, which used to be
here, and move it to underneath the
rendering loop. I explained why in Step
3.
Step 6 - I
Want to Exit, What Now?
Set BackDrop = Nothing
This is the only thing
which we need to add to the Form_Unload
function. We need to place it just before
we unload the Back Buffer. This piece of
code simply frees the memory and stack
space which was allocated to this
surface. Remember if we don't do this, we
will start to lose memory, and you
program may crash.
Step 7 -
Closing Comments
Excellent, you got through
loading an image and displaying it. Now
you've done this, do you fancy loading in
an image, and displaying it as
transparent ?? We'll mosey on to the next
tutorial and have a look. The results a
quite cool. Thanks again for reading all
the way through this (I know because you
wouldnt be reading this other wise,
Maybe).
Page [<< PREV]
1 2
Back to Tutorials - Main
|