【问题标题】:Is Desktop/screenshot capture with screengrab from DirectX Tool Kit possible?是否可以使用 DirectX 工具包中的屏幕抓图捕获桌面/屏幕截图?
【发布时间】: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 解决方案。这是更新的 blogarticlecodebase
  • 太棒了!非常感谢你,查克!这应该这样做。有时有专家指出正确的方向真是太好了^^

标签: c++ performance screenshot fullscreen directx-11


【解决方案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

您也可以将其完全删除,尽管这似乎会在开始时导致“空白”/黑色图像文件。对我来说,在它开始捕捉之前只有两个空白图像。随着整个初始化函数的延迟,我也得到了第一张图像而没有开销。谢谢。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-31
    • 2011-08-17
    • 1970-01-01
    • 2018-03-30
    • 1970-01-01
    • 2015-04-06
    相关资源
    最近更新 更多