|
Animation using Direct Draw - II
The C# Column
(Continued from last week
)
In the last week, we were planning to see the InitializeDirectDraw( ) and CreateSurfaces(
) methods. Here they are:
private void InitializeDirectDraw( )
{
SurfaceDescription desc = new
SurfaceDescription( ) ;
desc.SurfaceCaps.PrimarySurface = true ;
desc.SurfaceCaps.Complex = true ;
desc.SurfaceCaps.Flip = true ;
desc.BackBufferCount = 1 ;
surfacePrimary = new Surface ( desc, localDevice ) ;
SurfaceCaps caps = new SurfaceCaps( ) ;
caps.BackBuffer = true ;
surfaceSecondary =
surfacePrimary.GetAttachedSurface ( caps ) ;
}
Here, firstly we have created a surface. To describe the surface we are creating
we have used the SurfaceDescription class. The SurfaceCaps property of the SurfaceDescription
class returns a reference to the DirectX.DirectDraw.SurfaceCaps class. We have
specified that the new surface we are creating here is the primary surface by
setting PrimarySurface data member to true. Setting the Complex data member
to true means that we are creating more than one surface. The Flip property
specifies that the new surface would be ready for flipping.
The BackBufferCount property sets the number of secondary surfaces (back buffers)
attached to this primary surface. We have set this count to 1 as we want only
one secondary surface. After describing the surface we have created the surfaces
in the following statement:
surfacePrimary = new Surface ( desc, localDevice ) ;
The surfacePrimary is a reference to the Surface class that already stands added
to the GraphicsClass class. To the constructor of the Surface class we have
passed the surface description and the display device mode. This statement creates
the primary surface and secondary surface. The secondary surface gets attached
to the surfacePrimary reference.
Next we have obtained the reference to the secondary surface by calling the
GetAttachedSurface( ) method. The GetAttachedSurface( ) method returns the surface
matching to the specified capabilities. By setting the BackBuffer as true, we
have specified that the GetAttachedSurface( ) should return the secondary surface
attached to the surfacePrimary reference.
While the InitializeDirectDraw( ) method creates the primary and secondary surfaces,
the CreateSurface( ) method creates the offscreen surface. We need to separate
creation of surfaces because of one reason. While the application is running,
if we shift to another application by pressing Alt + Tab, all the surfaces are
destroyed. On activation, DirectDraw creates the primary and secondary surfaces
but not the offscreen surface. Hence, we need to create the primary and secondary
surfaces only for once. This is achieved by calling the InitializeDirectDraw(
) method only for once from the constructor. However, the CreateSurfaces( )
method is called every time to create the offscreen surface when there is need
to create one.
We will load our bmp file on the offscreen surface. The image on offscreen surface
is then drawn of the secondary surface and then flipped to the primary surface.
Add the following data members to the GraphicsClass class.
private Rectangle r ;
private Surface sprite ;
Now let us understand the CreateSurface( ) method.
private void CreateSurfaces( )
{SurfaceDescription desc =
new SurfaceDescription( ) ;
desc.SurfaceCaps.OffScreenPlain = true ;
sprite = new Surface (
sprite.bmp, desc, localDevice ) ;
PixelFormat pf = surfacePrimary.PixelFormat ;
ColorKey ck = new ColorKey( ) ;
ck.ColorSpaceHighValue = pf.RBitMask ;
ck.ColorSpaceLowValue = pf.RBitMask ;
sprite.SetColorKey (
ColorKeyFlags.SourceDraw, ck ) ;
r = new Rectangle ( 0 ,0, sprite.SurfaceDescription.Width,
sprite.SurfaceDescription.Height ) ;
}
Here, we have described the creation of an offscreen surface referred to by
sprite. While creating the surface we have specified the bitmap file to be loaded
and the display device mode along with the surface description. Next, we have
retrieved the pixel and colour format of the primary surface using the PixelFormat
property. Next few lines are for achieving transparency. Since the background
of sprite.bmp is red, we have masked the red colour bits by specifying
RbitMask data member. We have stored the bounding rectangle where the image
is initially displayed in r.
The bitmap file loaded in CreateSurfaces( ) is displayed
on the primary surface in the RenderGraphics( ) method. This method is called
from the StartLoop( ) method we have seen earlier.
public void RenderGraphics ( Point destination )
{
if ( !owner.Created )
return ;
if ( surfacePrimary == null |
surfaceSecondary == null )
return ;
try
{
surfaceSecondary.ColorFill ( Color.Black ) ;
surfaceSecondary.Draw (
r, sprite, DrawFlags.KeySource |
DrawFlags.Wait ) ;
surfacePrimary.Flip ( null, FlipFlags.Wait ) ;
r.X += 10 ;
if ( r.X >= (
800 - sprite.SurfaceDescription.Width ) )
r.X = 0 ;
}
catch ( SurfaceLostException e )
{
if ( localDevice.TestCooperativeLevel( ) == true )
{
localDevice.RestoreAllSurfaces( ) ;
CreateSurfaces( ) ;
}
}
}
As said earlier, surfaces are destroyed when the application window loose focus.
On re-activation when the RenderGraphics( ) method is called, accessing the
surfaces throws an exception.
Hence, we have added a try-catch block to write the code. In the try block we
have filled the background with black color by calling the ColorFill( ) method.
We have then drawn the graphics from the surface referred to by sprite in the
specified rectangle. The DrawFlags.KeySource flag is essential to get the transparency
effect. The DrawFlags.Wait flag is to wait till the graphics is drawn. The graphics
drawn on the secondary surface is flipped to the primary surface by calling
the Flip( ) method.
The first parameter of the Flip( ) method is the surface that is to be flipped
to. We have specified null so that flipping is done through the buffers in the
order they are attached to each other. Next we have incremented the X coordinate
by 10 so that the next time RenderGraphics( ) is called image would be drawn
on a new position.
In the catch block firstly we have checked whether the device is valid or not.
If it is then we have restored the primary and secondary surfaces by calling
RestoreAllSurfaces( ) method and then called the CreateSurfaces( ) method to
create the offscreen surface.
We are now finished with coding. Run the application. You will find the sprite
moving on the window as shown below.

|