是否可以使用 DirectX 工具包中的屏幕抓图捕获桌面/屏幕截图?
Posted
技术标签:
【中文标题】是否可以使用 DirectX 工具包中的屏幕抓图捕获桌面/屏幕截图?【英文标题】:Is Desktop/screenshot capture with screengrab from DirectX Tool Kit possible? 【发布时间】:2017-07-07 12:40:33 【问题描述】:所以,马上开始接触金属。我正在编写一个 c++ 程序,其目的是将屏幕作为输入图像并使用对象检测器(来自 dlib)对其进行处理,但我无法理解如何获得指向 swapChain 和上下文/内容的指针屏幕。
ScreenGrab (DirectX Tool Kit) 的以下代码似乎可以解决问题。问题是我不知道如何让它工作。我在 Visual Studio 2017 中使用 NuGet 来获取库(称为 directxtk_uwp,还有一个我没有使用的称为 directxtk_desktop_2015。)我收到以下六个错误:
错误 C2065:“swapChain”:未声明的标识符 错误 C2227:'->GetBuffer' 的左侧必须指向类/结构/联合/泛型类型 错误 C2065:“immContext”:未声明的标识符 错误 C2228:“.Get”左侧必须有类/结构/联合 错误 C2653:“DX”:不是类或命名空间名称 错误 C3861:“ThrowIfFailed”:找不到标识符
从 ScreenGrab wiki 页面运行插入的示例代码:
#include <ScreenGrab.h>
#include <wrl\client.h>
#include <Wincodec.h>
int main()
using namespace DirectX;
using namespace Microsoft::WRL;
ComPtr<ID3D11Texture2D> backBuffer;
HRESULT hr = swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D),
reinterpret_cast<LPVOID*>(backBuffer.GetAddressOf()));
if (SUCCEEDED(hr))
hr = SaveWICTextureToFile(immContext.Get(), backBuffer.Get(),
GUID_ContainerFormatJpeg, L"SCREENSHOT.JPG");
DX::ThrowIfFailed(hr);
return 0;
我刚刚开始使用 c++ 进行编程,并且来自 Java 编程,我已经积极开发了一年。所以保持简单易懂将不胜感激^^
为了清楚起见,我想要一些相当快的东西。我已经通过以下代码获得了与 GDI 一起使用的屏幕截图:
#include <iostream>
#include <Windows.h>
#include <Wincodec.h>
#include <ctime>
#include <cstring>
#include <atlimage.h>
#include <d3d9.h>
using namespace std;
int main()
try
int nScreenWidth = GetSystemMetrics(SM_CXSCREEN);
int nScreenHeight = GetSystemMetrics(SM_CYSCREEN);
HWND hDesktopWnd = GetDesktopWindow();
HDC hDesktopDC = GetDC(hDesktopWnd);
HDC hCaptureDC = CreateCompatibleDC(hDesktopDC);
for (int i = 0; i < 10;i++)
clock_t begin = clock();
HBITMAP hCaptureBitmap = CreateCompatibleBitmap(hDesktopDC,
nScreenWidth, nScreenHeight);
SelectObject(hCaptureDC, hCaptureBitmap);
BitBlt(hCaptureDC, 0, 0, nScreenWidth, nScreenHeight,
hDesktopDC, 0, 0, SRCCOPY | CAPTUREBLT);
CImage image;
image.Attach(hCaptureBitmap);
image.Save("test.jpg");
DeleteObject(hCaptureBitmap);
cout << double(clock() - begin) / (clock_t)1000 << endl;
ReleaseDC(hDesktopWnd, hDesktopDC);
DeleteDC(hCaptureDC);
IDirect3DSurface9 *surface;
system("pause");
catch (exception& e)
cout << "\nexception thrown!" << endl;
cout << e.what() << endl;
system("pause");
结果如下:
0.091
0.05
0.052
0.06
0.047
0.05
0.057
0.06
0.06
0.051
如您所见,这实际上只能使我的速度达到每秒 1/0.47 ≈ 21 帧。我也看过this 的回答,但我还是不知道如何获取屏幕的上下文和交换链。 谢谢。
【问题讨论】:
您使用的示例代码避免假设您尝试截取自己的渲染目标纹理(直接创建或从您自己的交换链获得)。这适用于您自己的应用程序,但截取整个桌面的屏幕截图是另一回事,需要使用 DXGI API。您应该查看this article 的一些选项。 DirectX 工具包wiki 上的示例代码假设您至少简要地查看了tutorials,我在其中介绍了ThrowIfFailed。 谢谢,查克!正如您在变量名中可能看到的那样,我能够找到该页面,我只是不记得该站点。从来没有让 DirectX 工作的方式,但那是很早的,我想我现在有更好的理解来做它。但是,DirectX9 不是像您在有关 DirectX11 的链接问题中所说的那样已经过时了吗?无论如何,非常感谢您的帮助! 啊,对。我应该更仔细地观察,因为我认为他们正在做 DXGI 解决方案。这是更新的 blog、article 和 codebase。 太棒了!非常感谢你,查克!这应该这样做。有时有专家指出正确的方向真是太好了^^ 【参考方案1】:因此,在伟大的 Chuck 的指导下多看几眼!我找到了一个解决方案,将屏幕截图保存到磁盘的速度大约是 3.5 倍(如果您删除绘图鼠标以及在 grabImage 函数开始时有 8 毫秒的开销)。你可以看到代码here。以下是将十张图像保存到磁盘的结果(以毫秒为单位):
0.015
0.017
0.012
0.021
0.012
0.019
0.012
0.022
0.018
0.015
将这些和 GDI 程序中的其他相加,我们得到 0.578/0.163≈3.546 倍的速度。请记住,我已经替换了这部分:
// Copy image into GDI drawing texture
lImmediateContext->CopyResource(lGDIImage, lAcquiredDesktopImage);
// Draw cursor image into GDI drawing texture
CComPtrCustom<IDXGISurface1> lIDXGISurface1;
hr = lGDIImage->QueryInterface(IID_PPV_ARGS(&lIDXGISurface1));
if (FAILED(hr))
return false;
CURSORINFO lCursorInfo = 0 ;
lCursorInfo.cbSize = sizeof(lCursorInfo);
auto lBoolres = GetCursorInfo(&lCursorInfo);
if (lBoolres == TRUE)
if (lCursorInfo.flags == CURSOR_SHOWING)
auto lCursorPosition = lCursorInfo.ptScreenPos;
auto lCursorSize = lCursorInfo.cbSize;
HDC lHDC;
lIDXGISurface1->GetDC(FALSE, &lHDC);
DrawIconEx(
lHDC,
lCursorPosition.x,
lCursorPosition.y,
lCursorInfo.hCursor,
0,
0,
0,
0,
DI_NORMAL | DI_DEFAULTSIZE);
lIDXGISurface1->ReleaseDC(nullptr);
// Copy image into CPU access texture
lImmediateContext->CopyResource(lDestImage, lGDIImage);
下面一行:
lImmediateContext->CopyResource(lDestImage, lAcquiredDesktopImage);
因为我不需要光标。事实上,考虑到我会在屏幕上检测到东西,拥有它可能会更糟。如前所述,我还从这里重新定位了Sleep(8); // not sure if required
行:
hr = lDevice->CreateTexture2D(&desc, NULL, &lDestImage);
if (FAILED(hr))
return false;
if (lDestImage == nullptr)
return false;
return true;
bool grabImage()
int lTryCount = 4;
do
Sleep(8); // not sure if required
到这里,在init函数中:
hr = lDevice->CreateTexture2D(&desc, NULL, &lDestImage);
if (FAILED(hr))
return false;
if (lDestImage == nullptr)
return false;
Sleep(8);
return true;
bool grabImage()
int lTryCount = 4;
do
//Sleep(5); // not sure if required
您也可以将其完全删除,尽管这似乎会在开始时导致“空白”/黑色图像文件。对我来说,在它开始捕捉之前只有两个空白图像。随着整个初始化函数的延迟,我也得到了第一张图像而没有开销。谢谢。
【讨论】:
以上是关于是否可以使用 DirectX 工具包中的屏幕抓图捕获桌面/屏幕截图?的主要内容,如果未能解决你的问题,请参考以下文章