In this article we will learn how to take screenshots using DirectX 9.0 I adapted this article from my tutorial on www.g-productions.net
It is quite easy to take screenshots from DirectX applications, you don’t have to take care of too much things. So let’s take a closer look.
What we need is a surface to save the image and we also need a function that saves the surface to file. Thanks to Microsoft the DirectX SDK ships with a function which does all the work for us.
Here are the necessary steps:
- Specify a filename to save the screenshot to.
- Create a surface which is capable of holding the screen data.
- Get the screen data and write it to our surface
- Save our surface to a file
1. Specify a filename to save the screenshot
Here we do only the necessary. Check if a filename exists or step over to the next filename. Here is the code:
FILE* f; for(int i=0;i<999;i++) { // build the filename sprintf(filename, "screen%.3d.bmp", i); f = fopen(filename, "r"); if( f == NULL) break; else fclose( f ); }
2. Create a surface which is capable of holding the screen data
The only thing we need here is the method CreateOffscreenPlainSurface which awaits the following parameters:
-
the screen size (x, y)
-
the format of the surface to be created
-
the memory in which the surface will be put and finaly
-
the surface interface
The desired format could be something like D3DFMT_A8R8G8B8 and so I suggest you to use this format since it creates a 32 bit surface with an alpha channel and 8 bits per each channel.
We should define D3DPOOL_SCRATCH as the memory class which holds our surface, since our resource won’t be bound to any device or format restrictions.
Last we pass our surface of type LPDIRECT3DSURFACE9 in.
The code should look something like this:
g_pd3dDevice->CreateOffscreenPlainSurface(x, y, D3DFMT_A8R8G8B8, D3DPOOL_SCRATCH, &frontbuf, NULL);
3. Get the screen data and write it to our surface
That’s kind of easy, simply call GetFrontBufferData and the whole front buffer (your rendered scene with effects…) will be copied to your surface
g_pd3dDevice->GetFrontBufferData(NULL, frontbuf);
4) Save our surface to a file
The last step we have to do is to save the surface to a file. The D3DX library contains the following function which we will use: D3DXSaveSurfaceToFile
The parameters are the following: First pass the filename we created earlier. Second pass the desired file format you want to save your image, in our case we decided to save to bmp so we choose D3DXIFF_BMP. The third parameter is the surface which contains the captured screen. We are finished. Fill the last two parameters with NULL since we don’t want to use a palette and we want to save the entire screen to the file not only a part.
Here is the complete code. It’s copy/paste ready so you can try it out.
In order to use this code you will need to include the apropriate header file and link to the library, so put the next lines somewhere in your code:
#pragma comment(lib, "d3dx9.lib") #include <d3dx9.h>
assuming that the variables g_bWindowed is of type boolean and determines whether this is a windowed (true) or fullscreen (false) application and g_hWnd is of type HWND and is a valid window handle to our created window, check out the complete code:
HRESULT blTakeScreenShot() { HRESULT hr = S_OK; LPDIRECT3DSURFACE9 frontbuf; char filename[64]; FILE* f; int x, y; RECT rcWindow; // look for the next free file for(int i=0;i<999;i++) { // build the filename sprintf(filename, "screen%.3d.bmp", i); f = fopen(filename, "r"); if( f == NULL) break; else fclose( f ); } // get the screen resolution. If it's a windowed application get the whole // screen size. If it's a fullscreen application you might have somewhere // your defines as: #define SCREEN_WIDTH 800 if( !g_bWindowed ) { x = SCREEN_WIDTH; y = SCREEN_HEIGHT; } else { x = GetSystemMetrics( SM_CXSCREEN ); y = GetSystemMetrics( SM_CYSCREEN ); // to get the window sizes GetWindowRect( g_hWnd, &rcWindow ); } // here we create an empty Surface. The parameter D3DFMT_A8R8G8B8 creates an 32 bit image with // an alpha channel and 8 bits per channel. if( FAILED( hr = g_pd3dDevice->CreateOffscreenPlainSurface(x, y, D3DFMT_A8R8G8B8, D3DPOOL_SCRATCH, &frontbuf, NULL))) return hr; // now we copy the entire frontbuffer into our new surface. The first parameter is NULL since // we assume we have only one swap chain if( FAILED( hr = g_pd3dDevice->GetFrontBufferData(NULL, frontbuf))) { // if this fails release our surface so we have no memory leak frontbuf->Release(); return hr; } // This is the most important functions. The DirectX-SDK provides this handy little function to // save our surface to a file. The first parameter is our specified filename, the second parameter // tells DirectX what kind of file we want to save (in this example we decide to save to BMP) // Note the difference between a fullscreen screenshot and a windowed one. If we have a windowed application // we only want the specified RECT saved from our screen capture if( !g_bWindowed ) D3DXSaveSurfaceToFile(filename, D3DXIFF_BMP, frontbuf, NULL, NULL); else D3DXSaveSurfaceToFile(filename, D3DXIFF_BMP, frontbuf, NULL, &rcWindow); frontbuf->Release(); return hr; }