c – use DirectX to capture screens

I know how to use GDI to capture the screen, but it is very slow (it barely captures 10 fps)

I have read that DirectX provides the best speed .But before I start learning DirectX, I want to test a sample to see if it is really that fast.

I found that this question provides a sample code to do this:

void dump_buffer()
{
IDirect3DSurface9* pRenderTarget=NULL;
IDirect3DSurface9* pDestTarget=NULL;
const char file[] = " Pickture.bmp";
// sanity checks.
if (Device == NULL)
return;

// get the render target surface.
HRESULT hr = Device->GetRenderTarget(0, &pRenderTarget);
// get the current adapter display mode.
//hr = pDirect3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT,&d3ddisplaymode);
< br /> // create a destination surface.
hr = Device->CreateOffscreenPlainSurface(DisplayMde.Width,
DisplayMde.Height,
DisplayMde.Format,
D3DPOOL_SYSTEMMEM,
&pDestTarget,
NULL);
//copy the render target to the destination surface.
hr = Device->GetRenderTargetData(pRenderTarget, pDestTarget);
//save its contents to a bitmap file.
hr = D3DXSaveSurfaceToFile(file,
D3DXIFF_BMP,
pDestTarget,
NULL,
NULL);

// clean up.
pRenderTarget->Release();
pDestTarget->Release();
}

I tried to include the required files. However, not all of them can be included (e.g. #include< D3dx9tex.h>).

Anyone can provide a working example with all required inclusions, or point out which libraries I should install.

I am on Windows 7 flagship Visual C 2010 Express is used on the version (x64).

Edit:

In addition, this code is incomplete, for example, what is a device identifier? !

The following are some sample codes for capturing screen using DirectX 9.
You don’t need to install any SDK (Except for the standard files included with Visual Studio, although I have not tested VS 2010).

Just create a simple Win32 console application and add the following to the stdafx.h file:

#include  // we use WIC for saving images
#include // DirectX 9 header
# pragma comment(lib, "d3d9.lib") // link to DirectX 9 library

The following is the main implementation of the example

int _tmain(int argc, _TCHAR* argv[])
{
HRESULT hr = Direct3D9TakeScreenshots(D3DADAPTER_DEFAULT, 10);
return 0;
}

What this will do is capture 10 times the screen, and save the “cap%i.png” image on the disk. It will also display this time (save the image does not count within this time, only the screen capture).
On my (desktop Windows 8-Dell Precision M2800 / i7-4810MQ-2.80GHz / Intel HD 4600 This is a very bad machine…) On the machine, it needs to complete 100 1920×1080 captures in about 4 seconds, so it takes about 20 /25 fps.

HRESULT Direct3D9TakeScreenshots(UINT adapter, UINT count)
{
HRESULT hr = S_OK;
IDirect3D9 *d3d = nullptr ;
IDirect3DDevice9 *device = nullptr;
IDirect3DSurface9 *surface = nullptr;
D3DPRESENT_PARAMETERS parameters = {0 };
D3DDISPLAYMODE mode;
D3DLOCKED_RECT rc;
UINT pitch;
SYSTEMTIME st;
LPBYTE *shots = nullptr;

// init D3D and get screen size
d3d = Direct3DCreate9(D3D_SDK_VERSION);
HRCHECK(d3d->GetAdapterDisplayMode( adapter, &mode));

parameters.Windowed = TRUE;
parameters.BackBufferCount = 1;
parameters.BackBufferHeight = mode.Height;
parameters.BackBufferWidth = mode.Width;
parameters.SwapEffect = D3DSWAPEFFECT_DISCARD;
parameters.hDeviceWindow = NULL;

// create device & capture surface
HRCHECK(d3d->CreateDevice( adapter, D3DDEVTYPE_HAL, NULL, D3DCREATE_SOFTWARE_VERTEXPROCESSING, ¶meters, &device));
HRCHECK(device->CreateOffscreenPlainSurface(mode.Width, mode.Height, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM), &surface, null

// compute the required buffer size
HRCHECK(surface->LockRect(&rc, NULL, 0));
pitch = rc.Pitch;
HRCHECK( surface->UnlockRect());

// allocate screenshots buffers
shots = new LPBYTE[count];
for (UINT i = 0; i {
shots[i] = new BYTE[pitch * mode.Height];
}

GetSystemTime(&st); // measure the time we spend doing captures
wprintf(L"%i:%i:%i.%i ", st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
for ( UINT i = 0; i {
// get the data
HRCHECK(device->GetFrontBufferData(0, surface));

// copy it into our buffers
HRCHECK(surface->LockRect(&rc, NULL, 0));
CopyMemory(shots[i], rc.pBits, rc.Pitch * mode.Height);
HRCHECK(surface->UnlockRect());
}
GetSystemTime(&st);
wprintf(L"%i:%i:%i.%i " , st.wHour, st.wMinute, st.wSecon d, st.wMilliseconds);

// save all screenshots
for (UINT i = 0; i {
WCHAR file[100 ];
wsprintf(file, L"cap%i.png", i);
HRCHECK(SavePixelsToFile32bppPBGRA(mode.Width, mode.Height, pitch, shots[i], file, GUID_ContainerFormatPng)) ;
}

cleanup:
if (shots != nullptr)
{
for (UINT i = 0; i {
delete shots[i];
}
delete[] shots;
}
RELEASE(surface);
RELEASE(device) ;
RELEASE(d3d);
return hr;
}

Please note that this code is implicitly linked to WIC (the imaging library included in Windows has Long time) to save image files (so you don’t need to install the famous D3DXSaveSurfaceToFile of the old DirectX SDK):

HRESULT SavePixelsToFile32bppPBGRA(UINT width, UINT height, UINT stride , LPBYTE pixels, LPWSTR filePath, const GUID &format)
{
if (!filePath || !pixels)
return E_INVALIDARG;

HRESULT hr = S_OK;< br /> IW ICImagingFactory *factory = nullptr;
IWICBitmapEncoder *encoder = nullptr;
IWICBitmapFrameEncode *frame = nullptr;
IWICStream *stream = nullptr;
GUID pf = GUID_WICPixelFormat32bppPBGRA;
BOOL coInit = CoInitialize(nullptr);

HRCHECK(CoCreateInstance(CLSID_WICImagingFactory, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&factory)));
HRCHECK(factory->CreateStream(&stream));
> HRCHECK(stream->InitializeFromFilename(filePath, GENERIC_WRITE));
HRCHECK(factory->CreateEncoder(format, nullptr, &encoder));
HRCHECK(encoder->Initialize(stream, WICBitmapEncoderNoCache));
HRCHECK(encoder->CreateNewFrame(&frame, nullptr)); // we don't use options here
HRCHECK(frame->Initialize(nullptr)); // we dont' use any options here
HRCHECK(frame->SetSize(width, height));
HRCHECK(frame->SetPixelFormat(&pf));
HRCHECK(frame->WritePixels(height, stride, stride * height , pixels));
HRCHECK(frame->Commit());
HRCHECK(encoder->Commit());

cleanup:
RELEASE(stream);
RELEASE (frame);
RELEASE(encoder);
RELEASE(factory);
if (coInit) CoUninitialize();
return hr;
}

Some macros I use:

#define WIDEN2(x) L ## x
#define WIDEN(x) WIDEN2(x)
#define __WFILE__ WIDEN(__FILE__)
#define HRCHECK(__expr) {hr=(__expr);if(FAILED(hr)){wprintf(L"FAILURE 0x%08X (%i) line: %u file:'%s' expr:'" WIDEN(#__expr) L"' ",hr, hr, __LINE__,__WFILE__);goto cleanup;}}
#define RELEASE(__p ) {if(__p!=nullptr){__p->Release();__p=nullptr;}}

Note: For Windows 8 clients, all these (except WIC) should be removed to support Desktop Duplication API.

I know how to capture the screen using GDI, but it is very slow (it barely captures 10 fps)

I have read DirectX provides the best speed. But before I start learning DirectX, I want to test a sample to see if it is really that fast.

I found that this question provides a sample code to do this One point:

void dump_buffer()
{
IDirect3DSurface9* pRenderTarget=NULL;
IDirec t3DSurface9* pDestTarget=NULL;
const char file[] = "Pickture.bmp";
// sanity checks.
if (Device == NULL)
return;

// get the render target surface.
HRESULT hr = Device->GetRenderTarget(0, &pRenderTarget);
// get the current adapter display mode.
// hr = pDirect3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT,&d3ddisplaymode);

// create a destination surface.
hr = Device->CreateOffscreenPlainSurface(DisplayMde.Width,
DisplayMde.Height,
DisplayMde.Format,
D3DPOOL_SYSTEMMEM,
&pDestTarget,
NULL);
//copy the render target to the destination surface.
hr = Device- >GetRenderTargetData(pRenderTarget, pDestTarget);
//save its contents to a bitmap file.
hr = D3DXSaveSurfaceToFile(file,
D3DXIFF_ BMP,
pDestTarget,
NULL,
NULL);

// clean up.
pRenderTarget->Release();
pDestTarget ->Release();
}

I tried to include the required files. However, not all of them can be included (e.g. #include< D3dx9tex.h>).

Anyone can provide a working example with all required inclusions, or point out which libraries I should install.

I use Visual C 2010 Express on Windows 7 Ultimate (x64).< /p>

Edit:

In addition, this code is incomplete, for example, what is a device identifier? !

The following are some sample codes for capturing screen using DirectX 9.
You don’t need to install any SDK (except the standard files included with Visual Studio, although I don’t have Tested with VS 2010).

Just create a simple Win32 console application and add the following content to the stdafx.h file:

< pre>#include // we use WIC for saving images
#include // DirectX 9 header
#pragma comment(lib, “d3d9.lib”) / / link to DirectX 9 library

The following is the main implementation example

int _tmain(int argc, _TCHAR* argv[])
{< br /> HRESULT hr = Direct3D9TakeScreenshots(D3DADAPTER_DEFAULT, 10);
return 0;
}

What this will do is capture 10 times the screen and add “cap%i. png” image is saved on the disk. It will also display this time (the saved image does not count within this time, only the screen capture).
On my (desktop Windows 8-Dell Precision M2800 / i7-4810MQ-2.80 GHz/Intel HD 4600 This is a very bad machine…) On the machine, it needs to complete 100 1920×1080 captures in about 4 seconds, so it takes about 20/25 fps.

< /p>

HRESULT Direct3D9TakeScreenshots(UINT adapter, UINT count)
{
HRESULT hr = S_OK;
IDirect3D9 *d3d = nullptr;
IDirect3DDevice9 *device = nullptr;< br /> IDirect3DSurface9 *surface = nullptr;
D3DPRESENT_PARAMETERS parameters = {0 };
D3DDISPLAYMODE mode;
D3DLOCKED_RECT rc;
UINT pitch;
SYSTEMTIME st;
LPBYTE *shots = nullptr;

// init D3D and get screen size
d3d = Direct3DCreate9(D3D_SDK_VERSION);
HRCHECK(d3d->GetAdapterDisplayMode(adapter, &mode));

parameters .Windowed = TRUE;
parameters.BackBufferCount = 1;
parameters.BackBufferHeight = mode.Height;
parameters.BackBufferWidth = mode.Width;
parameters.SwapEffect = D3DSWAPEFFECT_DISCARD;< br /> parameters.hDeviceWindow = NULL;

// create device & capture surface
HRCHECK(d3d->CreateDevice(adapter, D3DDEVTYPE_HAL, NULL, D3DCREATE_SOFTWARE_VERTEXPROCESSING, ¶meters, &device));< br /> HRCHECK(device->CreateOffscreenPlainSurface(mode.Width, mode.Height, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &surface, nullptr));

// compute the required buffer size
HRCHECK(surface->LockRect(&rc, NULL, 0));
pitch = rc.Pitch;
HRCHECK(surface->UnlockRect());

// allocate screenshots buffers
shots = new LPBYTE[count];
for (UINT i = 0; i {
shots[i] = new BYTE[pitch * mode.Height];
}

GetSystemTime(&st); // measure the time we spend doing captures
wprintf(L"%i:%i:% i.%i ", st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
for (UINT i = 0; i {
// get the data
HRCHECK(device->GetFrontBufferData(0, surface));

// copy it into our buffers
HRCHECK(surface->LockRect(&rc , NULL, 0));
CopyMemory(shots[i], rc.pBits, rc.Pitch * mode.Height);
HRCHECK(surface->UnlockRect());
}
GetSystemTime(&st);
wprintf(L"%i:%i:%i.%i ", st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);< br />
// save all screenshots< br /> for (UINT i = 0; i {
WCHAR file[100];
wsprintf(file, L"cap%i.png", i) ;
HRCHECK(SavePixelsToFile32bppPBGRA(mode.Width, mode.Height, pitch, shots[i], file, GUID_ContainerFormatPng));
}

cleanup:
if (shots != nullptr)
{
for (UINT i = 0; i {
delete shots[i];
}
delete[] shots;
}
RELEASE(surface);
RELEASE(device);
RELEASE(d3d);
return hr;
}

Please note that this code is implicitly linked to WIC (the imaging library included in Windows has been for a long time) to save image files (so you don’t need to install the old version of DirectX SDK. Name D3DXSaveSurfaceToFile):

HRESULT SavePixelsToFile32bppPBGRA(UINT width, UINT height, UINT stride, LPBYTE pixels, LPWSTR filePath, const GUID &format)
{
if (!filePath || !pixels)
return E_INVALIDARG;

HRESULT hr = S_OK;
IWICImagingFactory *factory = nullptr;
IWICBitmapEncoder *encoder = nullptr;
IWICBitmapFrameEncode *frame = nullptr;
IWICStream *stream = nullptr;
GUID pf = GUID_WICPixelFormat32bppPBGRA;
BOOL coInit = CoInitialize(nullptr);

HRCHECK(CoCreateInstance(CLSID_WICImagingFactory, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&factory)));
HRCHECK(factory->CreateStream(&stream));
HRCHECK(stream->InitializeFrom GENERIC_WRITEPath ));
HRCHECK(factory->CreateEncoder(format, nullptr, &encoder));
HRCHECK(encoder->Initialize(stream, WICBitmapEncoderNoCache));
HRCHECK(encoder->CreateNewFrame( &frame, nullptr)); // we don't use options here
HRCHECK(frame->Initialize(nullptr)); // we dont' use any options here
HRCHECK(frame->SetSize( width, height));
HRCHECK(frame->SetPixelFormat(&pf));
HRCHECK(frame->WritePixels(height, stride, stride * height, pixels));
HRCHECK( frame->Commit());
HRCHECK(encoder->Commi t());

cleanup:
RELEASE(stream);
RELEASE(frame);
RELEASE(encoder);
RELEASE(factory) ;
if (coInit) CoUninitialize();
return hr;
}

Some macros I use:

< pre>#define WIDEN2(x) L ## x
#define WIDEN(x) WIDEN2(x)
#define __WFILE__ WIDEN(__FILE__)
#define HRCHECK(__expr) {hr= (__expr);if(FAILED(hr)){wprintf(L”FAILURE 0x%08X (%i)
line: %u file:’%s’
expr:'” WIDEN(#__expr) L “‘
“,hr, hr, __LINE__,__WFILE__);goto cleanup;}}
#define RELEASE(__p) {if(__p!=nullptr){__p->Release();__p=nullptr; }}

Note: For Windows 8 clients, all these (except WIC) should be removed to support Desktop Duplication API.

Leave a Comment

Your email address will not be published.