Java Does DirectDraw

Philip Durr
Microsoft Corporation

May 1997

Note   DirectDraw® is a component of the Microsoft® DirectX® SDK. The Java version of DirectX is included with the Java 2.0 SDK. DirectDraw for Java is accessed though a set of classes in the com.ms.directX package that is installed with DirectX.

Introduction

This article will discuss some of the benefits, the architecture, and use of the DirectDraw SDK for Java. In the past, applications that made use of animation needed to be written in C++ (or worse yet, assembly language) because animation is so computationally intensive. Animation is accomplished by showing a series of images (or frames) to the user in quick succession. In order for a user to view animation without flicker, a frame rate of at least 12 frames per second must be maintained. Higher frame rates provide smoother animation: for example, cartoons usually have a frame rate of 24 frames per second. To maintain a frame rate of just 12 frames per second, a 640 x 480 picture containing 256 colors requires that 3.6 megabytes of picture data be processed each second. Processing this amount of video data can be very difficult. DirectDraw now provides this video data processing ability to the Java developer.

Starting to use the DirectDraw API requires a few basic steps. These are listed below and will be addressed in following sections:

  1. Create a DirectDraw object.

  2. Set the cooperative level.

  3. Create needed surfaces.

  4. Load any required bitmaps.

  5. Display the surfaces.

The DirectDraw Object

To use DirectDraw with Java, you must first create the DirectDraw object as follows:

dd = new DirectDraw();

Once the DirectDraw object is created, the cooperative level needs to be set.

Cooperative Level

You need to set the level of control that DirectDraw will have over its top-level window. At its simplest level, DirectDraw functions within a Microsoft Windows® 95 or Windows NT® window like any other application. This is the DirectDraw normal level of operation. Normal cooperative level is set with the following statement:

dd.setCooperativeLevel(hwnd, DDSCL_NORMAL);

Note   In order for an application to use the more advanced DirectDraw features, such as changing video modes or modifying the behavior of DirectDraw surfaces, the application needs to use exclusive mode. However, using the advanced features is beyond the scope of this article. For more information on using the advanced features consult the DirectX documentation.

Creating Surfaces

DirectDraw represents video content in terms of a surface. Surfaces can be placed in either video memory or in system memory. However, if the video hardware does not have sufficient memory for a surface, DirectDraw will emulate the surface in system memory. The biggest advantage of the DirectDraw surface is that it is always presented to the developer as a linear region of memory. This is true even if the application’s video mode is not linear. Consider, for example, a ModeX where display memory is configured as a series of planes (Figure 1). In order to turn on a pixel, the appropriate memory plane, as well as the correct address for that pixel, must be accessed. The DirectDrawSurface object handles all these details for you.

Figure 1. Video memory organization in Mode X.

A DirectDraw surface can contain multiple memory buffers, allowing flipping surfaces to be constructed. Flipping surfaces allow applications to take advantage of a technique known as double buffering. In the double-buffer method, an application draws some content on a back buffer. When finished, it quickly “flips” or copies the contents of the back buffer to the front buffer, making that frame visible. The double-buffer method can be especially fast, particularly if both the front and back buffers reside in video memory. In this case, the flip operation may not even consume any CPU time.

A DirectDraw-based application’s user always views the Primary surface. In order to change what the user sees, simply change the contents of the Primary surface. Usually, you’ll need to create one or more off-screen surfaces that contain images to display at various times. Figure 2 illustrates a complex double-buffered Primary surface. The Primary surface is composed of a back buffer, a front buffer, and four off-screen surfaces. The off-screen surfaces each contain a bitmap in various stages of rotation. To animate the cylinder, the application blits each image from the off-screen surface to a location in the Primary surface’s back buffer. Once the blit for each cylinder is complete, the application flips the back buffer to the front buffer.

Figure 2. Double buffer surface relationships

The Java implementation of DirectDraw includes two classes specifically designed for working with surfaces. The DDSurfaceDesc class is used to describe the attributes of the surface to be created. The DirectDrawSurface class contains the definition, methods, and variables for working with DirectDraw surfaces.

To create a DirectDraw surface, whether it’s a Primary or an off-screen surface, you first create a DDSurfaceDesc object, set the surface attributes, and pass the surface description structure to the CreateSurface() method of the DirectDraw object. The following statements create a Primary surface consisting of a single buffer:

// Create a primary surface containing a single buffer.
ddsd = new DDSurfaceDesc();
ddsd.flags = DDSD_CAPS ;
ddsd.ddsCaps = DDSCAPS_PRIMARYSURFACE;
pdds = dd.createSurface( ddsd );

The following statements create a Primary surface that is double-buffered:

//Create a primary surface containing a back //buffer for double buffering.
ddsd = new DDSurfaceDesc();
ddsd.flags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
ddsd.ddsCaps = DDSCAPS_PRIMARYSURFACE | DDSCPAS_FLIP | DDSCAPS_COMPLEX;
ddsd. BackBufferCount = 1;
pdds = dd.createSurface( ddsd );

The following statements illustrate how to create an off-screen surface that is 320 pixels wide and 200 lines high:

Ddsd = new DDSurfaceDesc();
ddsd.flags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
ddsd.ddsCaps = DDSCAPS_OFFSCREENPLAIN;
ddsd.width = 320;
ddsd.height = 200;
pdds = dd.createSurface( ddsd );

Loading Bitmaps into a DirectDraw Surface

Bitmaps are pictures that an application can use to display graphical content. The four cylinder views in Figure 2 are each a bitmap. DirectDraw for Java provides the DirectDrawBitmap class for the manipulation of bitmap files. Once the DirectDrawBitmap object is created, it can be copied to a DirectDraw surface. The statements below read a bitmap file named Frntback.bmp into a DirectDraw surface.

//Read the bitmap file.
Bm =  new DirectDrawBitmap();
bm.filename(“Frntback.bmp”);
bm.initWidth(dx);
bm.initHeight(dy);
if( bm.loaded() != 0 )
{
    // Create a DirectDrawSurface for 
    // this bitmap.
    ddsd = new DDSurfaceDesc();
    ddsd.flags = DDSD_CAPS | 
    DDSD_HEIGHT | DDSD_WIDTH;
    ddsd.ddsCaps = DDSCAPS_OFFSCREENPLAIN;
    ddsd.width = bm.width();
    ddsd.height = bm.height();
    pdds = dd.createSurface( ddsd );
    pdds.copyBitmap(bm, 0, 0, 0, 0);
}
//Off-screen surface pdds now contains the 
//bitmap data.

Displaying Surfaces

Once you have created all the surfaces and initialized them in some manner (such as copying bitmaps to them), they can be copied to the Primary surface when the application needs to display them. This surface copy is accomplished with the DirectDrawSurface object blt method. In the default mode of operation, if the blitter is busy, the DDS.blt() method returns an error code immediately. Therefore, you should either use the DDS.blt() method inside some type of loop or specify the DDBLT_WAIT flag in the DDS.blt() method. The second option alters the DDS.blt() behavior so that the method will wait until either the blt can be set up or another error occurs before it returns.

The following code illustrates how to use the DirectDrawSurface blt method. First, though, a couple of points need to be clarified. A DirectDrawSurface can be lost if the mode of the display card is changed or if an application receives exclusive access to the display card and frees all of the surface memory currently allocated on the card. When a surface is lost, it needs to be restored. This is accomplished in two steps. First the DirectDrawSurface object’s Restore method is called to reallocate surface memory and reattach the DirectDrawSurface object. Then, the affected surface content is reconstructed.

rc.Left   = 0;
rc.Top    = 0;
rc.Right  = bm.width();
rc.Bottom = bm.height();
// Show the off-screen surface on the 
//primary surface.
done = 0;
do
{
   int retval;
   retval = ddsPrimary.blt( rc, ddsOne, rc, 0);
   if( retval == DD_OK )
    {
        //If the bitmap has been successfully 
        //copied, exit loop.
        done = 1; 
    }
    else if ( retval == DDERR_SURFACELOST )
    {
        while( ddsPrimary.Restore() != DD_OK &&
            ddsOne.Restore() != DD_OK )
        {
            ReloadBitmap(ddsOne, szBitmap);
        }
    }
    else if( retval != DDERR_WASSTILLDRAWING)
    {
        // Undetermined error; quit the loop.
        done = 1;
    }
} while( done == 0);

For More Information

In this article, we have provided background information and the basic steps necessary to initialize and use the DirectDraw SDK for Java. To get the DirectX SDK for Java and associated documentation, download the Microsoft SDK for Java 2.0 (which contains the DirectX SDK) from http://www.microsoft.com/java/. Included are several sample applications you may find useful when creating animation with DirectX for Java. These samples are listed in the below.

Sample Location Demonstrates…
Ddex3 DDraw\ddex3\ddraw.html Basic use of DirectDraw
Flipcube d3d\flipcube\D3D.html Basic use of 3D Immediate Mode
Viewer d3drm\Viewer\direct3dRM.html Nearly complete use of Direct3D Retained Mode
Castle d3drm\Castle\Castle.html Direct3D Retained Mode with focus on high performance
DirectInput dInput\ddex3\dinput.html Use of joysticks, mouse, keyboard, and gamepads