Вероятно, самый быстрый способ сделать это - использовать DirectX.Каждое приложение DirectX содержит буфер или поверхность для хранения содержимого видеопамяти, связанной с этим приложением.Это называется обратным буфером приложения.Некоторые приложения могут иметь более одного заднего буфера.И есть еще один буфер, к которому каждое приложение может получить доступ по умолчанию, - передний буфер.Этот, передний буфер, содержит видеопамять, относящуюся к содержимому рабочего стола, и, по сути, это изображение экрана.Получая доступ к переднему буферу из вашего приложения, вы можете захватить содержимое экрана в этот момент.
Доступ к переднему буферу из вашего приложения довольно прост и понятен.Интерфейс IDirect3DDevice9
предоставляет метод GetFrontBufferData()
, который принимает указатель объекта IDirect3DSurface9
и копирует содержимое переднего буфера на эту поверхность.Объект IDirect3DSurfce9
может быть создан с использованием метода IDirect3DDevice8::CreateOffscreenPlainSurface()
.После захвата экрана на поверхность вы можете использовать функцию D3DXSaveSurfaceToFile()
, чтобы сохранить поверхность непосредственно на диск в растровом формате.Таким образом, код для захвата экрана выглядит следующим образом:
extern IDirect3DDevice9* g_pd3dDevice;
Void CaptureScreen()
{
IDirect3DSurface9* pSurface;
// g_pd3dDevice is an IDirect3DDevice9 object, and has been assumed to be properly initialized
g_pd3dDevice->CreateOffscreenPlainSurface(ScreenWidth, ScreenHeight,
D3DFMT_A8R8G8B8, D3DPOOL_SCRATCH, &pSurface, NULL);
g_pd3dDevice->GetFrontBufferData(0, pSurface);
D3DXSaveSurfaceToFile("Desktop.bmp",D3DXIFF_BMP,pSurface,NULL,NULL);
pSurface->Release();
}
Если вам нужен буфер с реальными битами, вместо того, чтобы сохранять его непосредственно на диск, вы можете использовать код, подобный этому:
extern void* pBits;
extern IDirect3DDevice9* g_pd3dDevice;
IDirect3DSurface9* pSurface;
// g_pd3dDevice is an IDirect3DDevice9 object, and has been assumed to be properly initialized
g_pd3dDevice->CreateOffscreenPlainSurface(ScreenWidth, ScreenHeight,
D3DFMT_A8R8G8B8, D3DPOOL_SCRATCH,
&pSurface, NULL);
g_pd3dDevice->GetFrontBufferData(0, pSurface);
D3DLOCKED_RECT lockedRect;
pSurface->LockRect(&lockedRect,NULL,
D3DLOCK_NO_DIRTY_UPDATE|
D3DLOCK_NOSYSLOCK|D3DLOCK_READONLY)));
for( int i=0 ; i < ScreenHeight ; i++)
{
memcpy( (BYTE*) pBits + i * ScreenWidth * BITSPERPIXEL / 8 ,
(BYTE*) lockedRect.pBits + i* lockedRect.Pitch ,
ScreenWidth * BITSPERPIXEL / 8);
}
g_pSurface->UnlockRect();
pSurface->Release();
Убедитесь, что мы выделили достаточно памяти перед копированием в pBits
.Типичное значение для BITSPERPIXEL
составляет 32
бит на пиксель.Однако, это может варьироваться в зависимости от текущих настроек монитора.
РЕДАКТИРОВАТЬ
Вот программа на C ++, которую я собрал, чтобы рассчитать время.Функция Direct3D9TakeScreenshots
принимает параметр, который определяет, сколько раз сделать снимок экрана.У меня сейчас установлено значение 10, но вы можете изменить его.Работая на моей машине в режиме выпуска, я получаю следующие результаты:
18:33:23.189
18:33:23.579
Так что 390
миллисекунд, чтобы сделать 10 снимков экрана и скопировать их в буфер.Это в среднем составляет около 39 миллисекунд на снимок экрана и буферную копию.
#include "pch.h"
#include <iostream>
#include <d3d9.h> // DirectX 9 header
#pragma comment(lib, "d3d9.lib") // link to DirectX 9 library
#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)\n\tline: %u file: '%s'\n\texpr: '" WIDEN(#__expr) L"'\n",hr, hr, __LINE__,__WFILE__);goto cleanup;}}
#define RELEASE(__p) {if(__p!=nullptr){__p->Release();__p=nullptr;}}
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 = nullptr;
// 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, 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 < count; i++)
{
shots[i] = new BYTE[pitch * mode.Height];
}
GetSystemTime(&st); // measure the time we spend doing <count> captures
wprintf(L"%i:%i:%i.%i\n", st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
for (UINT i = 0; i < count; 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\n", st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
cleanup:
if (shots != nullptr)
{
for (UINT i = 0; i < count; i++)
{
delete shots[i];
}
delete[] shots;
}
RELEASE(surface);
RELEASE(device);
RELEASE(d3d);
return hr;
}
int main()
{
Direct3D9TakeScreenshots(D3DADAPTER_DEFAULT, 10);
}