我正试图编写一个Windows C++程序,它会尝试从当前正在屏幕上显示的任何内容中挑选出感兴趣的颜色。捕获任何Windows应用程序的屏幕?
我已经试过了GDI,的Direct3D9和Direct3D11 DXGI以下的例子,它们都似乎工作只捕获Windows桌面和/或我自己的应用程序自身的输出。当我启动全屏Direct3D游戏时,我似乎最终会留下一些空白像素数据。
它必须才有可能实现此目标,否则OBS Studio,FRAPS等将无法像透明一样工作。
我知道我可以尝试反向工程OBS Studio,但是有没有人有更简洁的C++解决方案来捕获任意Windows应用程序的视频输出作为某种像素缓冲区?
编辑:我还应该提到,捕获常规桌面窗口似乎工作。这是全屏游戏,给我带来麻烦。
编辑:评论者请求我的GDI代码。这是我的GDI和D3D9代码。正如你所看到的,我想基于冲突的例子,我发现了几个变化:
std::wstring GetScreenColor(COLORREF& colorRef)
{
std::wstring retVal;
//const int desktopWidth(GetDeviceCaps(desktopHdc, HORZRES));
//const int desktopHeight(GetDeviceCaps(desktopHdc, VERTRES));
// const int desktopWidth(GetSystemMetrics(SM_CXVIRTUALSCREEN));
// const int desktopHeight(GetSystemMetrics(SM_CYVIRTUALSCREEN));
const int desktopWidth(GetSystemMetrics(SM_CXSCREEN));
const int desktopHeight(GetSystemMetrics(SM_CYSCREEN));
HWND desktopHwnd(GetDesktopWindow());
// HDC desktopHdc(GetDC(NULL));
HDC desktopHdc(GetDC(desktopHwnd));
HDC myHdc(CreateCompatibleDC(desktopHdc));
const HBITMAP desktopBitmap(CreateCompatibleBitmap(desktopHdc, desktopWidth, desktopHeight));
SelectObject(myHdc, desktopBitmap);
// BitBlt(myHdc, 0, 0, desktopWidth, desktopHeight, desktopHdc, 0, 0, SRCCOPY);
BitBlt(myHdc, 0, 0, desktopWidth, desktopHeight, desktopHdc, 0, 0, SRCCOPY | CAPTUREBLT);
//SelectObject(myHdc, hOld);
ReleaseDC(NULL, desktopHdc);
BITMAPINFO bitmapInfo = { 0 };
bitmapInfo.bmiHeader.biSize = sizeof(bitmapInfo.bmiHeader);
bitmapInfo.bmiHeader.biWidth = desktopWidth;
bitmapInfo.bmiHeader.biHeight = -desktopHeight;
bitmapInfo.bmiHeader.biPlanes = 1;
bitmapInfo.bmiHeader.biBitCount = 24;
bitmapInfo.bmiHeader.biCompression = BI_RGB;
bitmapInfo.bmiHeader.biSizeImage = 0;
// TODO: use a persistent buffer?
const unsigned long numPixels(desktopHeight * desktopWidth);
ColorBGRS* rawPixels(new ColorBGRS[numPixels]);
if (!GetDIBits(myHdc, desktopBitmap, 0, desktopHeight, rawPixels, &bitmapInfo, DIB_RGB_COLORS))
{
delete[] rawPixels;
ReleaseDC(desktopHwnd, desktopHdc);
DeleteDC(myHdc);
DeleteObject(desktopBitmap);
return L"GetDIBits() failed";
}
unsigned long redSum(0);
unsigned long greenSum(0);
unsigned long blueSum(0);
for (unsigned long index(0); index < numPixels; ++index)
{
blueSum += rawPixels[index].blue;
greenSum += rawPixels[index].green;
redSum += rawPixels[index].red;
}
const unsigned long redAverage(redSum/numPixels);
const unsigned long blueAverage(blueSum/numPixels);
const unsigned long greenAverage(greenSum/numPixels);
colorRef = RGB(redAverage, greenAverage, blueAverage);
delete[] rawPixels;
ReleaseDC(desktopHwnd, desktopHdc);
DeleteDC(myHdc);
DeleteObject(desktopBitmap);
return std::wstring();
}
std::wstring GetScreenColor2(COLORREF& colorRef)
{
IDirect3D9* d3d9(Direct3DCreate9(D3D_SDK_VERSION));
if (!d3d9)
{
d3d9->Release();
return L"Direct3DCreate9() failed";
}
D3DDISPLAYMODE d3dDisplayMode;
if (FAILED(d3d9->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3dDisplayMode)))
{
return L"GetAdapterDisplayMode() failed";
}
D3DPRESENT_PARAMETERS d3dPresentParams;
ZeroMemory(&d3dPresentParams, sizeof(D3DPRESENT_PARAMETERS));
d3dPresentParams.Windowed = TRUE;
d3dPresentParams.Flags = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;
d3dPresentParams.BackBufferFormat = d3dDisplayMode.Format;
d3dPresentParams.BackBufferCount = 1;
d3dPresentParams.BackBufferHeight = d3dDisplayMode.Height;
d3dPresentParams.BackBufferWidth = d3dDisplayMode.Width;
d3dPresentParams.MultiSampleType = D3DMULTISAMPLE_NONE;
d3dPresentParams.SwapEffect = D3DSWAPEFFECT_DISCARD;
//d3dPresentParams.SwapEffect = D3DSWAPEFFECT_COPY;
d3dPresentParams.hDeviceWindow = NULL; //hWnd;
d3dPresentParams.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
d3dPresentParams.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
IDirect3DDevice9* d3d9Device(0);
if (FAILED(d3d9->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, d3dPresentParams.hDeviceWindow, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dPresentParams, &d3d9Device)))
{
d3d9->Release();
return L"CreateDevice() failed";
}
IDirect3DSurface9* d3d9Surface(0);
// if (FAILED(d3d9Device->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &d3d9Surface))) return L"GetBackBuffer() failed";
if (FAILED(d3d9Device->CreateOffscreenPlainSurface(d3dDisplayMode.Width, d3dDisplayMode.Height, D3DFMT_A8R8G8B8, D3DPOOL_SCRATCH, &d3d9Surface, NULL)))
// if (FAILED(d3d9Device->CreateOffscreenPlainSurface(d3dDisplayMode.Width, d3dDisplayMode.Height, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &d3d9Surface, NULL)))
{
d3d9Device->Release();
d3d9->Release();
return L"CreateOffscreenPlainSurface() failed";
}
if (FAILED(d3d9Device->GetFrontBufferData(0, d3d9Surface)))
{
d3d9Surface->Release();
d3d9Device->Release();
d3d9->Release();
return L"GetFrontBufferData() failed";
}
D3DLOCKED_RECT d3dLockedRect;
if (FAILED(d3d9Surface->LockRect(&d3dLockedRect, 0, D3DLOCK_NO_DIRTY_UPDATE |
D3DLOCK_NOSYSLOCK |
D3DLOCK_READONLY)))
{
d3d9Surface->UnlockRect();
d3d9Surface->Release();
d3d9Device->Release();
d3d9->Release();
return L"LockRect() failed";
}
const unsigned long numPixels(d3dDisplayMode.Height * d3dDisplayMode.Width);
BYTE* rawPixels((BYTE*)(d3dLockedRect.pBits));
colorRef = RGB(*(rawPixels + 2), *(rawPixels + 1), *(rawPixels));
d3d9Surface->UnlockRect();
d3d9Surface->Release();
d3d9Device->Release();
d3d9->Release();
return std::wstring();
}
你能举例说明你如何用GDI尝试过吗? 如果我没有记错,似乎你没有通过正确的窗口句柄,桌面是默认的null null – 2oppin
特别是当像DirectX或OpenGL之类的东西被涉及时,他们往往有更直接的路线到视频硬件以获得更好的性能,这就是为什么如果你捕捉到高级别的数据(比如GDI),那么你会得到黑色数据,所以捕获它们通常需要使用自定义的低级视频驱动程序来发送它们的输出。 –