Tricks of the Windows Game Programming Gurus:Advanced DirectDraw and Bitmapped Graphics

All ITKnowledge Source Code Search Tips Advanced Search |

| Tricks of the Windows Game Programming Gurus by Andre LaMothe Sams, Macmillan Computer Publishing ISBN: 0672313618 Pub Date: 09/27/99

Search this book:

Previous | Table of Contents | Next

### Mixing GDI and DirectX

AAHAHAHAHAHAAHAHAHAHAH! Sorry, I just needed some tension relief. Now back to business. GDI, or the Graphics Device Interface, is the Win32 graphics subsystem responsible for all Windows rendering. You’ve already seen how to work with GDI in the previous sections on Windows programming, so I’m not going to reiterate device contexts and such.

To use GDI with DirectDraw, all you need to do is retrieve a compatible DC from DirectDraw and then use it like you’d use the DC from the standard `GetDC()` call. The cool thing is that once you retrieve a GDI-compatible DC from DirectDraw, there’s really no difference in how you use the GDI functions. In fact, all of your code will work almost without change!

Now, you may be wondering how GDI can work with DirectDraw if DirectDraw takes over the graphics system, as it does in full-screen mode. Well, Windows can’t use GDI to draw on any of your DirectDraw surfaces while in exclusive mode, but you can. This is the important detail that confuses many newbies to DirectX. In general, Windows sends messages to its subsystems like GDI, MCI, and so on. If DirectX has control of the hardware systems and is sharing them exclusively, the messages won’t be processed. For example, a GDI graphics call to draw something while in full-screen mode will be dumped.

However, you can always use the software of the subsystems to do work for you because you’re the one in charge. It’s like a plasma blaster that’s encoded to your DNA; if I pick it up it won’t fire, but it will if you pick it up. So the user dictates when the blaster works, but the blaster is always functional. Weird example, huh? You try not sleeping for weeks—it’s going to get weirder <BG>.

Because you do all your drawing on surfaces, you would assume that there’s a way to get a GDI-compatible DC from a DirectDraw surface, and there is. The name of the function is `IDIRECTDRAWSURFACE4::GetDC()` and it’s shown here:

    HRESULT GetDC(HDC FAR *lphDC);

All you do is make a call with some storage for the DC, and you’re good to go. Here’s an example:

    LPDIRECTDRAWSURFACE4 lpdds; // assume this is valid

    HDC xdc; // I like calling DirectX DC XDC’s

    if (FAILED(lpdds->GetDC(&xdc)))

        { /* error */ }

    // do what you will with the DC…

Once you’re done with the DirectDraw-compatible DC, you must release it just as you would a normal GDI DC. The function is `IDIRECTDRAWSURFACE4::ReleaseDC()` and is shown here:

    HRESULT ReleaseDC(HDC hDC);

    Basically, just send it the DC you retrieved like this:

    if (FAILED(lpdds->ReleaseDC(xdc)))

        { /* error */ }

> Warning: If a surface is locked, `GetDC()` won’t work on it because `GetDC()` also locks the surface. In addition, once you get the DC from a surface, make sure you `ReleaseDC()` as soon as you’re done because `GetDC()` creates an internal lock on the surface, and you won’t be able to access it. In essence, only GDI or DirectDraw can write to a surface at any time, not both.

For an example of using GDI, take a look at `DEMO7_17.CPP|EXE`. It creates a full-screen DirectDraw application in 640×480×256 and then prints GDI text at random locations. The code that prints the text is shown here:

    int Draw_Text_GDI(char *text, int x,int y,

                      COLORREF color, LPDIRECTDRAWSURFACE4 lpdds)

    {

    // this function draws the sent text on the sent surface

    // using color index as the color in the palette

    HDC xdc; // the working dc

    // get the dc from surface

    if (FAILED(lpdds->GetDC(&xdc)))

       return(0);

    // set the colors for the text up

    SetTextColor(xdc,color);

    // set background mode to transparent so black isn’t copied

    SetBkMode(xdc, TRANSPARENT);

    // draw the text a

    TextOut(xdc,x,y,text,strlen(text));

    // release the dc

    lpdds->ReleaseDC(xdc);

    // return success

    return(1);

    } // end Draw_Text_GDI;GDI;DirectA, combining>

Please note that color is in the form of a `COLORREF`. This is a very important performance issue. If you recall, `COLORREF`s are 24-bit RGB-encoded structures, meaning that the requested color is always in 24-bit RGB form. The problem with this is that when DirectX is in palettized mode, it must hunt for the nearest match to the requested color. This is in addition to the slow speed of GDI in general, which makes text printing using GDI very slow. I highly recommend writing your own text blitter for any speed-intensive text printing.

The function does all the manipulation of the DC itself, so all you have to do is call it. For example, to print out “You da Man!” at (100,100) in pure green on the primary surface, you would write

    Draw_Text_GDI("You da Man!",

                  100,100,

                  RGB(0,255,0),

                  lpddsprimary);

Before moving on, I want to talk a little about when to use GDI. In general, GDI is slow. I usually use it during development to print text, draw GUI stuff, and so on. Also, it’s very useful for slow emulation during development. For example, let’s say that you’re planning to write a really fast line-drawing algorithm called `Draw_Line()`. You might not have time to do it yet, but you can always emulate `Draw_Line()` with GDI calls. That way at least you get something on the screen, and later you can write the fast line-drawing algorithm.

### Getting the Lowdown on DirectDraw

As you’ve been learning, DirectDraw is a rather complex graphics system. It has a number of interfaces, each with numerous functions. The main point of DirectDraw is the utilization of hardware in a uniform manner. Therefore, it pays for game programmers to be able to query various DirectDraw interfaces for their states and/or capabilities so that the proper action can be taken. For example, when you’re creating surfaces, you might want to first find out how much VRAM is available so you can optimize the creation order. Or maybe you want to use hardware rotation, but first you need to make sure it’s available. The list of details that you might be interested in adding to the system your game is running goes on and on. Hence, there are a number of capability-testing functions, named `GetCaps()` or `Get*()` in general, available on each of the main interfaces. Let’s take a look at the most useful `GetCaps()` functions.

Previous | Table of Contents | Next

Copyright © Macmillan Computer Publishing, Inc.

Products | Contact Us | About Us | Privacy | Ad Info | Home
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc. All rights reserved. Reproduction in whole or in part in any form or medium without express written permission of EarthWeb is prohibited. Read EarthWeb's privacy statement.