🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

04.04 - DirectDraw Surfaces

Started by
14 comments, last by Teej 22 years, 10 months ago
From Pixels to Images In the various articles up to this point, we''ve learned a lot about pixels, color depth, color composition and memory manipulation. So long as we understand that different color depths require different color composition and memory requirements (per pixel), we should be able to draw pixels to the display in any screen mode and color depth we desire. Sure, pixels can be fun, but the real excitement comes from image manipulation. In this article, we''re going to play around with DirectDraw surfaces in order to draw images onto our display. What we learn here will be the basis for all of our 2D games, so make sure that you''re following along with your own template code! Video and System Memory You''re probably aware that there''s physical memory on your video card. As a matter of fact, you most likely know how much memory your video card has because it''s an advertised feature of all current video cards. But how is this memory used? We''ve already discussed the fact that the active display is really an area of video memory. How much memory is needed depends on the current screen resolution and color depth. For instance, a 1600x1200 resolution at 32-bit color requires at least 7,680,000 bytes of memory! If your video card doesn''t have the mimumum required memory for a video mode, it won''t be available to you. On the other hand, you usually have enough for your current display resolution with memory left over. It''s this ''extra'' available memory that we''re interested in. The whole basis for animation involves using one or more backbuffers, preparing the image, and ''copying'' or ''flipping'' the backbuffer''s memory onto the active display memory. Traditionally, this was done by allocating some system memory for the backbuffer, and then when the time was right copying the entire backbuffer onto the display adapter. These days, where there is memory available on the video card, it is highly desirable to have this backbuffer on the video card''s memory, so that copying isn''t even necessary -- the video card is merely instructed to use the backbuffer''s memory as the new active display memory. The beauty of DirectDraw is that it allows us to create DirectDrawSurface objects in either video or system memory. By default, DirectDraw attempts to use video memory if it''s available, falling back on system memory. Keep in mind that if our backbuffer is in system memory, this memory has to be copied into video memory which is a relatively slow operation. Either way, DirectDraw takes care of everything behind the scenes -- we just use our DirectDrawSurfaces normally. The moral of the story here is that everything done within video memory is fast, and moving memory from the system to the video card is slower. DirectDraw will do its best to allocate video memory for our surfaces, but if it can''t system memory is used, and this is transparent to us. DirectDrawSurface Objects At a minimum, DirectDraw requires us to create a DirectDrawSurface object for the active display memory. For proper animation, it''s important to create at least one other DirectDrawSurface object to serve as a backbuffer. DirectDraw is aware of our needs, and provides a ''flipping chain'' for swapping primary and backbuffer memory. If both DirectDraw surfaces are in video memory, DirectDraw only needs to redirect the video card on where the current display''s memory is -- no copying is necessary. DirectDraw also allows us to create DirectDrawSurface objects of any size. The trick here is to load any bitmap images we''d like onto DirectDrawSurface objects so that they can be transferred to the backbuffer surface quickly and effortlessly. In our game template code, a DirectDrawSurface is created for RESOURCE.BMP -- its size exactly matches the dimensions of the bitmap itself. You''ll notice that two functions inside of UTILS.CPP, Utils_LoadBitmap() and Utils_CopyBitmap() are responsible for the task, and the procedure involves loading a bitmap from disk, creating a DirectDrawSurface object with matching dimensions, and finally copying the bitmap image into the DirectDrawSurface object''s memory. Here''s Utils_LoadBitmap():

//////////////////////////////////////////////////////////////////////////////
// Utils_LoadBitmap
// Loads the requested bitmap from a file and creates a DirectDraw7 
// surface for it.
//
// PARAMETERS:
// pDD              - DirectDraw7 object
// szBitmap         - Name of the bitmap file
// dx, dy           - Bitmap size override; use 0, 0 for actual size
//
// RETURN VALUE:    The bitmap surface 
//
IDirectDrawSurface7* Utils_LoadBitmap(IDirectDraw7 *pDD, LPCSTR szBitmap,
                                      int dx, int dy)
{
    HBITMAP              hbm;
    BITMAP               bm;
    DDSURFACEDESC2       ddsd;
    IDirectDrawSurface7 *pDDS;
 
    // Load the bitmap from a file
    hbm = (HBITMAP)LoadImage(NULL, szBitmap, IMAGE_BITMAP, dx, dy,
                             LR_LOADFROMFILE|LR_CREATEDIBSECTION);
    if (hbm == NULL) return NULL;
 
    // Get the size of the bitmap
    GetObject(hbm, sizeof(bm), &bm);
 
    // Create a DirectDrawSurface for this bitmap
    ZeroMemory(&ddsd, sizeof(ddsd));
    ddsd.dwSize         = sizeof(ddsd);
    ddsd.dwFlags        = DDSD_CAPS|DDSD_HEIGHT|DDSD_WIDTH;
    ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
    ddsd.dwWidth        = bm.bmWidth;
    ddsd.dwHeight       = bm.bmHeight;
    if (pDD->CreateSurface(&ddsd, &pDDS, NULL) != DD_OK)
        return NULL;
 
    // Copy the bitmap into the surface
    Utils_CopyBitmap(pDDS, hbm, 0, 0, 0, 0);
    DeleteObject(hbm);
 
    return pDDS;
}
 
LoadImage() is a Win32 API function that does the actual loading of the bitmap. From there, we need the bitmap structure itself so that we can ascertain its dimensions, and from there a DirectDrawSurface object is created. Next, we need to fill the DirectDrawSurface memory with the actual bitmap:

//////////////////////////////////////////////////////////////////////////////
// Utils_CopyBitmap
//
// Copies the bitmap to the given surface.
//
// PARAMETERS:
// pDDS             - The surface to copy the bitmap onto
// hbm              - The bitmap to copy
// x, y             - Source position
// dx, dy           - Width and height of the bitmap
//
// RETURN VALUE:    DD_OK, E_FAIL or other DD-related error code
//
HRESULT Utils_CopyBitmap(IDirectDrawSurface7 * pDDS, HBITMAP hbm,
                         int x, int y, int dx, int dy)
{
    HDC            hdcImage;
    HDC            hdc;
    BITMAP         bm;
    DDSURFACEDESC2 ddsd;
    HRESULT        hResult;
 
    if (hbm == NULL || pDDS == NULL)
        return E_FAIL;
 
    // Make sure this surface is restored
    pDDS->Restore();
 
    // Select bitmap into a memoryDC so we can use it.
    hdcImage = CreateCompatibleDC(NULL);
    SelectObject(hdcImage, hbm);
 
    // Get the size of the bitmap
    GetObject(hbm, sizeof(bm), &bm);
    
    // Use this size unless overridden
    dx = dx == 0 ? bm.bmWidth  : dx;
    dy = dy == 0 ? bm.bmHeight : dy;
 
    // Get the size of the surface
    ddsd.dwSize  = sizeof(ddsd);
    ddsd.dwFlags = DDSD_HEIGHT|DDSD_WIDTH;
    pDDS->GetSurfaceDesc(&ddsd);
 
    if ((hResult = pDDS->GetDC(&hdc)) == DD_OK)
    {
        StretchBlt(hdc, 0, 0, ddsd.dwWidth, ddsd.dwHeight, hdcImage, x, y,
                   dx, dy, SRCCOPY);
        pDDS->ReleaseDC(hdc);
    }
    
    DeleteDC(hdcImage);
 
    return hResult;
}
 
It can sometimes happen that a DirectDrawSurface''s memory gets freed by the operating system behind our backs (for instance when the user switches applications), so we start by reclaiming our memory with Restore(). We''re ready now to bring up the bitmap image memory, so we create a device context for the image (don''t worry -- it''s Windows-specific procedure). The StretchBlt() function copies an area of memory from a source to a destination, and requires the desired dimensions for each. By default, we don''t want the image stretched in any way, so the original dimensions of both the image and the DirectDrawSurface area (which are the same in this case) are used. Finally, we discard the Windows bitmap since we have a good copy in our DirectDrawSurface object. Perhaps now you can see why functions such as these are tucked away in UTILS.CPP instead of GAMEMAIN.CPP -- they''re ugly and complicated, but rest-assured that so long as they''re working, you don''t need to worry about them any longer. Now, imagine the situation where our primary surface, backbuffer and our image surfaces are all in video memory. Since we don''t need to do any memory copying from system memory, everything is going to be blazingly fast! And even if there isn''t enough video memory to go around, DirectDraw will do its best to generate the type of speed we''re looking for. Moving Memory Okay, so now we''re ready for action. The functions that we''re looking for are called Blt() and BltFast(). Their job is to copy DirectDrawSurface memory from one surface to another. These functions will allow us to copy our images from various DirectDrawSurface objects onto our backbuffer surface. As you can probably guess, Blt() is the general-purpose memory copy function, and BltFast() is faster but less-featured. Let''s take a look at each of them: HRESULT Blt ( LPRECT lpDestRect, LPDIRECTDRAWSURFACE7 lpDDSrcSurface, LPRECT lpSrcRect, DWORD dwFlags, LPDDBLTFX lpDDBltFx ); The first three fields are fairly simple -- you can specify source and destination regions, and the source surface. You can use NULL for any of these RECTs to use the entire surface, but keep in mind that if the source and destination are different sizes, Blt() will shrink or stretch the image to fit automatically. For flags, it''s best to consult the online SDK documentation -- we''ll be using some of them, so they''ll be explained at that time. And as if those flags weren''t enough, theres a large DDBLTFX structure that can be used for all kinds of strange and wonderful things. There''s no way we can discuss them all, but there''s one that we already use in EraseBackground() (part of GAMEMAIN.CPP) -- a fill color for painting the entire screen. And, here''s BltFast(): HRESULT BltFast ( DWORD dwX, DWORD dwY, LPDIRECTDRAWSURFACE7 lpDDSrcSurface, LPRECT lpSrcRect, DWORD dwTrans ); For this function, we only need specify the x and y coordinates for the destination surface to copy to -- the source RECT is copied directly with its original size. As for the last field, it offers us a small subset of the many features Blt() has, most notably color-keying (which we''ll be discussing shortly). Aside from the limited features that BltFast() has, there''s another limitation that we''ll be interested in -- clipping. We haven''t discussed clipping yet, but we will shortly. Without further adieu, here''s a Game_Main() routine that copies the lpDDSRes surface onto the backbuffer:

void Game_Main()
{
    RECT rectSrc;
    DDSURFACEDESC2 ddsd2;
 
    // Get the description for the resource surface
    memset(&ddsd2, 0, sizeof(ddsd2));
    ddsd2.dwSize = sizeof(ddsd2);
 
    G.lpDDSRes->GetSurfaceDesc(&ddsd2);
    
    // Clear the backbuffer
    EraseBackground();
 
    // Prepare the destination rect for the entire resource surface
    rectSrc.left = rectSrc.top = 0;
    rectSrc.right = ddsd2.dwWidth;
    rectSrc.bottom = ddsd2.dwHeight;
    
    // Draw the resource bitmap to the background surface
    G.lpDDSBack->BltFast(0, 0, G.lpDDSRes, &rectSrc,
                         DDBLTFAST_WAIT|DDBLTFAST_NOCOLORKEY);
 
    // Flip the surfaces
    G.lpDDSPrimary->Flip(NULL, 0);
}
 
Something that you''ll learn when coding is that sometimes it''s better to start with the actual function call that does all the work, and then work backwards to prepare the data the main function call needs. In this case, I start by realizing that I needed to copy surface memory, and without any fancy features, so I look up BltFast() in the online SDK docs. This leads me to realize that I''m going to need the width and height of the source surface. Sure, I could have hard-coded these values, but it''s always better to generalize the code, so I used GetSurfaceDesc() instead. Since GetSurfaceDesc() requires a DDSURFACEDESC2 structure (with its dwSize field filled in), I went ahead and prepared it -- this is pretty common procedure in DirectX. Finally, I look through the possible flags for BltFast() and found the two that I need. You''ll notice that this code will work regardless of the screen resolution and color depth that you use in GLOBALS.H. However, for the remainder of this article, please set your template code to use 640x480 and 8-bit color. Naturally, this will incorporate palettes, so a DirectDrawPalette object will be created from the palette included in RESOURCE.BMP. Palettes Do you have an image editing tool? No, I don''t mean PAINT. A decent image editing tool will allow you to manipulate an 8-bit bitmap''s palette. If you take a look at RESOURCE.BMP''s palette, you''ll see that the letters themselves use palette index 93, and the black background uses palette index 0. If you were to change the color in index 93, the color of the letters in the bitmap would instantly change -- same with index 0 and the background. Our DirectDrawPalette works much the same way. You can use GetEntries() to get palette index color information, and SetEntries() to set these entries. At the exact moment the palette entries are set, all pixels on the screen that use those palette indicies are instantly changed. This is a fundamental and powerful feature of using palettes. To demonstrate, try the following code:

void Game_Main()
{
    RECT rectSrc;
    DDSURFACEDESC2 ddsd2;
    PALETTEENTRY Pal;
 
    // Create a random color
    Pal.peRed = rand()%256;
    Pal.peGreen = rand()%256;
    Pal.peBlue = rand()%256;
    
    // Assign this random color to the palette index for the
    // on-screen alpha-numeric text
    G.lpDDPalette->SetEntries(0, 93, 1, &Pal);
 
    // Get the description for the resource surface
    memset(&ddsd2, 0, sizeof(ddsd2));
    ddsd2.dwSize = sizeof(ddsd2);
 
    G.lpDDSRes->GetSurfaceDesc(&ddsd2);
    
    // Clear the backbuffer
    EraseBackground();
 
    // Prepare the destination rect for the entire resource surface
    rectSrc.left = rectSrc.top = 0;
    rectSrc.right = ddsd2.dwWidth;
    rectSrc.bottom = ddsd2.dwHeight;
    
    // Draw the resource bitmap to the background surface
    G.lpDDSBack->BltFast(0, 0, G.lpDDSRes, &rectSrc,
                         DDBLTFAST_WAIT|DDBLTFAST_NOCOLORKEY);
 
    // Flip the surfaces
    G.lpDDSPrimary->Flip(NULL, 0);
}
 
Remember to set your GLOBALS.H for 8-bit color mode. Because the actual 24-bit color inside of palette index 93 is constantly being changed, the image on the screen will change as well wherever index 93 is being used (in this case everything). You must have seen effects like this in older 2D games -- changing the palette at run-time is fast and simple, and many great effects are accomplished using this technique...sprite and tile animation come to mind. By playing with this code and making your own 8-bit images, you''ll solidify your understanding of palettes and color, and will be able to acheive undless effects as well. Color Keying To demonstrate what color keying is, take the BaseCode1 template that displays RESOURCE.BMP on the screen, and change the dwFillColor field in EraseBackground() to 1 instead of 0. Make sure you''re in 8-bit color mode, and run the program. What you''ll see is the image at the top-left of the display, and the rest of the background of the screen in a dark-red color. Since the background in the image was black (index 0), you can see it as black on the display. But what if you didn''t want to see the image background? With color keying, you can tell DirectDraw not to copy over certain colors from an image. You''ve surely seen this kind of thing on television where actors stand in front of a blue screen, and then the blue area is replaced with other images. In our case, we''d like to exclude the black part of our image from the copy process so that only the letters and numbers themselves are copied over. Excluding colors from the source image is called source color keying, and is a two-step process:
  1. Use the SetColorKey() method of the DirectDrawSurface object that holds the image to color key
  2. When using Blt() or BltFast(), indicate that you''d like to use source color keying
Since you only need to set the source color key once, add this code to the end of DD_Init():

// Set the color key to zero (palette index 0)
DDCOLORKEY ck;
ck.dwColorSpaceLowValue = ck.dwColorSpaceHighValue = 0;
 
G.lpDDSRes->SetColorKey(DDCKEY_SRCBLT, &ck);
 
...and in the BltFast() call during Game_Main(), change DDBLTFAST_NOCOLORKEY to DDBLTFAST_SRCCOLORKEY. Run the program again, and you''ll see that the background color (dark red, index 1) can be seen behind the letters and numbers of the image. For 8-bit colors, the DDCOLORKEY structure is used to specify the range of palette indicies to be keyed on. For higher color resolutions, you supply two RGB colors for the high and low ranges, and any image color that fits in that RGB range will be keyed. We''ll be making extensive use of color keying in the articles to come. Clipping When you take an image and copy it onto the backbuffer using Blt() or BltFast(), DirectDraw sincerely hopes that you don''t try anything silly like drawing the image past the end of the screen. If you know that you''ll always be drawing properly onto the display you don''t need clipping, but if you might (or aren''t sure), you need to use DirectDraw''s clipping feature. Here''s an example of clipping in action: picture a boat drifting across the display from the left to the right. As the boat first appears on the far left, you can only see the front of the boat -- the rest is off-screen. In order to draw this you have to either only copy the visible portion of the image over, or use DirectDraw clipping (in which case you can just go ahead and copy the entire image -- DirectDraw will ''clip'' the edge off for you). After all, if you try copying to an area of memory that doesn''t even exist (and DirectDraw''s not clipping for you), it''s crash-time, baby. To create a clipper, call DirectDraw''s CreateClipper() method. After this, use the SetHWnd() method to attach the clipper to the primary window, and finally call SetClipper() on the backbuffer DirectDrawSurface to complete the initialization. Now, whenever Blt() is used, anything copied onto the backbuffer surface will be properly clipped. Remember that BltFast() won''t work when there''s a clipper attached, so if you''re going to play around, change Blt() to BltFast() in Game_Main(). Touching the Surface Of course, I''ve left a lot out in this article, but it''s simply not possible to go through absolutely everything that DirectDraw has to offer -- that''s what the SDK documentation is for. Let me reitirate: there are overviews for every DirectX component, so you should really do yourself a favor and read up on the components as we use them here. My job, however, was to introduce you to DirectDraw surfaces, image loading, source color keying, palette manipulation, memory copying ala Blt() and BltFast() and clipping. In the next article we''re going to start actually using this material for something useful...and trust me, once you get the hang of images and Blt()ting, you''ll have one of the most powerful fundamental game development tools at your disposal. Comments? Questions? Please reply to this topic.
Advertisement
what does this mean and or do?

// Use this size unless overridden
dx = dx == 0 ? bm.bmWidth : dx;
dy = dy == 0 ? bm.bmHeight : dy;


I can''t figure this out, and I''m not sure which C topic I should look up to find this info.
the ? operator (i forgot it''s name ) works like so..

a = b ? c : d;

if b is true then a will be set to c, otherwise it will be set to d.

so, for example:

int nNum = 123;
int nRes = nNum == 123 ? 4 : 56;

hopefully, nRes will be 4.

so, in this context:

// Use this size unless overridden
dx = dx == 0 ? bm.bmWidth : dx;
dy = dy == 0 ? bm.bmHeight : dy;

if dx == 0 (!dx) then dx will be bm.bmWidth, otherwise it''ll be dx.
basically, if dx is 0, then use another value instead of 0.

blah, my explanations are confusing, hmm?

-----------------------------------------------------
Joe''s appliance repair company!
If God didn''t assemble it, we can repair it!
Your explanation was, in fact, perfect. Thank you very much. You wouldn''t possibly be able to point out what the propper syntax for the << and >> operators are in C, as well, would you? My experience with them so far has been that they are the stream operators, but I''ve yet to see how to use them other than in reguards to cin and cout.
In C, they are the bit shift operators, used for multiplying and dividing by powers of 2. In C++ are overloaded so that they can also perform input and output to streams. cin is the input stream i.e. the keyboard. cout is the output stream i.e. the screen.

int i;
cout << "Type an integer\n"; // Writes the string to the screen
cin >> i; // Gets a value for i from the keyboard
cout << "\nYou typed: " << i; // Outputs the value of i
Hope this is the right place to post

Those crazy pointers they''re making me crazy...

I''ve written a function that fills (blt) an entire surface with a given color (similar to yr EraseBackground() function except it takes two parameters, the surface and the color)

Here is the prototype:
void FillSurface(LPDIRECTDRAWSURFACE7 lpSurface, WORD couleur);

Now this work but to call it I must do that:
FillSurface(&(*G.lpDDSBack), RGB16(0, 0, 0));

What I''d like to is be able to only call the function with
FillSurface(G.lpDDSBack, RGB16(r,g,b));
(looks more clever, and I think that would be the way to go)

I tried doing it with IDirectDrawSurface7 instead but it wont compile... (as in... I dont know how...)

As I said all those pointers are driving me nuts...

Thanks in advance
I think this is one of those simple problems.

Everything was working fine until I did the colorkeying part of the tutorial then all I see is a blank red screen and no more text.

Does anyone have any suggestions?

I just found the problem, promise you wont laugh I put the colorkey code at the end of DS_Init instead of DD_Init.

Put it down to experience and a lesson learned.....

Edited by - feverpitch on June 8, 2001 4:43:10 PM
**Knowledge is Power**
OK folks, this time I really do need help.

I am trying to do some of the clipping. I have added the following code in DD_Init (I think that is where it should go)

	LPDIRECTDRAWCLIPPER		lpClipper;	if (G.lpDD->CreateClipper(0, &lpClipper, NULL) !=DD_OK) return -99;	lpClipper->SetHWnd(0, G.hWnd);	G.lpDDSPrimary->SetClipper( lpClipper); 


that compiles OK. Now I am trying to copy from the lpDDSRes surface so the right half of the resource.bmp is clipped. Unfortunately I can''t see a thing when I run the code despite the fact it builds without any errors.

void Game_Main(){	RECT rectDest;	DDSURFACEDESC2 ddsd2;	PALETTEENTRY Pal;	//Create a random colour	Pal.peRed=rand()%256;	Pal.peGreen=rand()%256;	Pal.peBlue=rand()%256;	//Assign this random colour to the palette index for the on-screen alpha-numeric text	G.lpDDPalette->SetEntries(0, 93, 1, &Pal);		//Get the description for the resource surface	ZeroMemory(&ddsd2,sizeof(ddsd2));	ddsd2.dwSize = sizeof(ddsd2);	G.lpDDSRes->GetSurfaceDesc(&ddsd2);	//Clear back buffer	EraseBackground();	//Prepare the destination rect for the entire resource surface	rectDest.left =  400;	rectDest.top = 0;	rectDest.right = ddsd2.dwWidth;	rectDest.bottom = ddsd2.dwHeight;	DDBLTFX ddbltfx;  	ZeroMemory(&ddbltfx, sizeof(ddbltfx));  	ddbltfx.dwSize = sizeof(ddbltfx);  	 //fill with black  	ddbltfx.ddckSrcColorkey; 	G.lpDDSBack->Blt(&rectDest,G.lpDDSRes,NULL, DDBLT_COLORFILL | DDBLT_WAIT,  &ddbltfx);  	//Flip the surface	G.lpDDSPrimary->Flip(NULL,0);} 


Any ideas pleeeeeaaaaase.....
**Knowledge is Power**
I don''t no if this is what you wanted but I think the code below does the trick.

I think that the DDBLT_COLORFILL was filling the destination with black pixels so that you couldn''t see anything.

void Game_Main()
{
RECT rectDest, rectSrc;
DDSURFACEDESC2 ddsd2;
PALETTEENTRY Pal;
//Create a random colour
Pal.peRed=rand()%256;
Pal.peGreen=rand()%256;
Pal.peBlue=rand()%256;
//Assign this random colour to the palette index for
the on-screen alpha-numeric text
G.lpDDPalette->SetEntries(0, 93, 1, &Pal);
//Get the description for the resource surface
ZeroMemory(&ddsd2,sizeof(ddsd2));
ddsd2.dwSize = sizeof(ddsd2);
G.lpDDSRes->GetSurfaceDesc(&ddsd2);
//Clear back buffer
EraseBackground();
//Prepare the destination & source rects for the entire resource surface
rectSrc.left = rectDest.left = ddsd2.dwWidth / 2;
rectSrc.top = rectDest.top = 0;
rectSrc.right = rectDest.right = ddsd2.dwWidth;
rectSrc.bottom = rectDest.bottom = ddsd2.dwHeight;

DDBLTFX ddbltfx;
ZeroMemory(&ddbltfx, sizeof(ddbltfx));
ddbltfx.dwSize = sizeof(ddbltfx);

//fill with black
ddbltfx.ddckSrcColorkey;
G.lpDDSBack->Blt(&rectDest, G.lpDDSRes, &rectSrc, DDBLT_WAIT, &ddbltfx);
//Flip the surface
G.lpDDSPrimary->Flip(NULL,0);
}
Ok concerning the two utilities LOADBITMAP and COPYBITMAP….

I am trying to get a clear picture of the methodology of the functions I can look up and understand the functions individually but it the knowing of the sequence and when do what that I have some difficulty

Glimpsing from further in the article it states that it sometimes best to work backwards from what you know needs to be done.

That’s fine but here’s a problem. Looking at the last lines of the CopyBitMap function

if ((hResult = pDDS->GetDC(&hdc)) == DD_OK)
{
StretchBlt(hdc, 0, 0, ddsd.dwWidth, ddsd.dwHeight, hdcImage, x, y,
dx, dy, SRCCOPY);
pDDS->ReleaseDC(hdc);
}

I understand what StretchBlt does and I understand you have to call ReleaseDC because you used GetDC

By looking at the snippet I can guess that by calling pDDS->GetDC( ) then StretchBlt that we are actually copying the image onto the surface? Ifso, where does this leap of faith come from?

So lets get this all in reverse order for the copy utility

0) cleanup and release memory
1) we need to copy an image to a surface so we pick a function – StretchBlt
2) it needs a destination device context handle (HDC hdc)
3) it needs a destination height & width , so we need a surfacedescription (DDSURFACEDESC2 ddsd)
4) it needs a handle to the source device context handle (HDC hdcImage)
5) it needs the origin of the source, ( INT x,y)
6) it needs the height & width of the source

guess?
7) we need to link the IDDS7 surface to the image so we use pDDS->GetDC(&hdc)

8) we need to assign a surface description to the IDDS7 surface so pDDS->GetSurfaceDesc to fulfill #3 above
9) we need to initinalize the DDSD2 : dwsize and dwflags
10) we need to insure that the destination height and width is not zero if it is we need the
size bmWidth and bmHeight
11) we need a handle to the bitmap so we create (HBITMAP hbm)
12) we need to get info about width and height , so we need a bitmap description (BITMAP bm)
13) we must get the info so GetObject(hbm, sizeof(bm), &bm);
14) we must get a handle of the source image into a device context so SelectObject(hdcImage, hbm)
15) we must create a dc to copy into so hdcImage=CreateCompatibleDC(NULL);
16) make sure we have memory avail pDDS->restore;
17) make sure there is a bitmap handle and a destination surface available

now for the load utilty

1) return the object with the surface with the bitmap on it
2) clean up and free memory
3) copy the bitmap onto the surface
4) create a surface in the DD7 object pDD->CreateSurface(&ddsd, &pDDS, NULL)
5) so from #4 we know we need a surfacedescription and a surface so (DDSURFACEESC2 ddsd,
IdirectDrawSurface7, *pDDS
6) we need to fill in the surface descrption so dwsize, dwflags, dwcaps, dwwidth, and dwheight
7) to get height and width we need have a bitmap descrption so we create BITMAP bm
8) we obtain the descrption via GetObject(hbm, sizeof(bm), &bm)
9) we need a handle to an object so we create HBITMAP hbm;
10) we now need a bitmap so we load it with LoadImage
11) we need the destination DD7 object and the name of the file

to understand direct interface correctly it would seem that there are two lines of functions and commands
those that describe the item (ie surface) and those that are a handle to item.

Well I think I understand the utilty functions now except for the part I mentioned before
if ((hResult = pDDS->GetDC(&hdc)) == DD_OK)

I was intimidated by these functions and still am a little, and thought they could have used a little more explanation into why they were written the way they were. Going through it backwards line by line really helped me. I hope that it will help someone else as well unless of course I got it all wrong [EEEEEEEEK! I hope not ]

if something is incorrect or it appears that i have a concept wrong please let me know thanks
-------------------------------------------------------May your bugs be few and your food plentiful.

This topic is closed to new replies.

Advertisement