Lets do some graphics: DirectDraw, Part 2

Well, the initial excitement from DDEX1 didn’t last for long. Just flipping between screens having some text ain’t much fun. I moved onto DDEX2. DDEX2 pretty much did the same thing as DDEX1 but on a background image. It drew a fancy background on the back buffer and then flipped some alternating text onto it. Nothing too exciting here, so I didn’t bother trying to make it work on the emulator.DDEX3 turned out to be interesting. It used 4 surfaces in total, a primary surface, a back buffer and two additional surface buffers. The two additional buffers were used to store the upper half and lower half of a bmp image file, respectively. And as and when the timer fired, they Blt one of these buffers onto the back buffer and then flipped the primary surface so that the current image on the back buffer could be displayed. You can check out the bmp file, the upper half says “Even Screen” on a blue background and the lower half says “Odd Screen” on a red background.As I said before, trying to run DDEX3 directly on emulator was not possible because the emulators do not support back buffers (also known as Complex surfaces, i think) and the program quit with an error message. So obviously the code had to be modified a bit, just like we did for DDEX1, to make it work. Here is how to make it work on an emulator:



First create a global BOOL variable bSingleBuffer and initialize it to FALSE. As before, comment out the part in InitApp() function where after doing GetCaps() it fails with an error message saying that the device does not support back buffers. So with that code out of the way, add the following piece of code:

    if (!(ddCaps.ddsCaps.dwCaps & DDSCAPS_BACKBUFFER) || !(ddCaps.ddsCaps.dwCaps & DDSCAPS_FLIP))    
    {
        printf(“Device does not support backbuffer or flip!\n”);         printf(“Using a normal surface (not a backbuffer)\n”);         bSingleBuffer = TRUE;

    }

    memset(&ddsd, 0, sizeof(ddsd));

    ddsd.dwSize = sizeof(ddsd);

    if(bSingleBuffer)

    {

        ddsd.dwFlags = DDSD_CAPS;

        ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;

    }

    else

    {

        ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;

        ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE |

                              DDSCAPS_FLIP;

        ddsd.dwBackBufferCount = 1;

    }

    hRet = g_pDD->CreateSurface(&ddsd, &g_pDDSPrimary, NULL);

If the device does not support back buffer or flip then we set the bSingleBuffer variable to TRUE. Then create our primary surface and store the pointer to the surface in g_pDDSPrimary variable. The next part of code gets the back buffer from the primary surface using EnumAttachedSurfaces() function, that part has to be modified as below:



    if(bSingleBuffer)

    {

        ddsd.dwFlags = DDSD_WIDTH | DDSD_HEIGHT;

        ddsd.dwWidth = 240;

        ddsd.dwHeight = 320;

        hRet = g_pDD->CreateSurface(&ddsd, &g_pDDSBack, NULL);

        if(hRet != DD_OK)

        {

            return InitFail(hWnd, hRet, TEXT(“Failed to create normal surface”));

        }

    }

    else

    {

        // Get a pointer to the back buffer

        hRet = g_pDDSPrimary->EnumAttachedSurfaces(&g_pDDSBack, EnumFunction);

        if (hRet != DD_OK)

            return InitFail(hWnd, hRet, szEnumAttachedSurfacesFailMsg);

    }

Here, if bSingleBuffer is set, we create a normal surface, 240 pixels wide and 320 pixels high. The resolution of the emulator. Otherwise we get the back buffer from the primary surface.

Then the two off-screen surfaces are created. This part of code remains the same, the only change is,

   // Create a offscreen bitmap.

    ddsd.dwFlags = DDSD_HEIGHT | DDSD_WIDTH;

    ddsd.dwHeight = 320;

    ddsd.dwWidth = 240;

This is same as the original code, only thing I changed here are the height and width.

Next change would be in the InitSurfaces() function,

    DDCopyBitmap(g_pDDSOne, hbm, 0, 0, 240, 320);

    DDCopyBitmap(g_pDDSTwo, hbm, 0, 320, 240, 320);

Originally, the bitmap was getting copied from 0, 0, 640, 480 and 0, 480, 640, 480. I think the original program was intended for a VGA resolution device but since the emulator runs Quarter VGA, I modified the dimensions.

The final change is in the WindowProc() function under the WM_TIMER message inside the while loop. The original code simply called Flip() on the primary surface to display the image in the back buffer, and as I have been repeating like a million times, the emulator does not support flipping (: So the original code,

    hRet = g_pDDSPrimary->Flip(NULL, 0);

becomes,

if(bSingleBuffer)

    {

        //RECT src, dest;

        hRet = g_pDDSPrimary->Blt(NULL, g_pDDSBack, NULL, 0, NULL);

   

        if(hRet != DD_OK)

        {

            printf(“DDEX3: Blt on primary surface failed, errcode:0x%x\n”, GetLastError());

        }

    }

    else

    {

         hRet = g_pDDSPrimary->Flip(NULL, 0);

    }

And that is all! DDEX3 is now ready to be run on the emulator (: Notice that last time I had used the src and dest RECT’s to specify the size and location of the source and destination rectangles on the source and destination surfaces respectively. Specifying NULL for both src and dest would make use of the entire source and destination surfaces, which is exactly what we want. The remaning part of the while loop remains same.

The emulator screen now flips between the following two screens every half-second.

img1

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

and

 

img2

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

As you can see, I replaced the stale red and blue screens with something a bit more nice (: Well, obviously you know how to do that. Don’t you? (:

 

 

One thought on “Lets do some graphics: DirectDraw, Part 2”

  1. Hi,

    I tried this but am getting szEnumAttachedSurfacesFailMsg and EnumAttachedSurfaces FAILED errors in teraterm

    Regards
    Syed

Leave a Reply to syed Cancel reply

Your email address will not be published. Required fields are marked *