是否可以使用DirectX Tool Kit中的screengrab捕获桌面/屏幕截图?

时间:2017-07-07 12:40:33

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

所以,马上换上金属。我正在编写一个c ++程序,其目的是将屏幕作为输入图像并使用对象检测器(来自dlib)处理它,但是我很难理解如何获得指向swapChain和context / content的指针。屏幕。

来自ScreenGrab (DirectX Tool Kit)的以下代码似乎能够解决问题。问题是我不知道如何让它发挥作用。我在Visual Studio 2017中使用NuGet来获取库(称为directxtk_uwp,还有一个名为directxtk_desktop_2015,我没有使用它。)我得到以下六个错误:

  
      
  • 错误C2065:'swapChain':未声明的标识符
  •   
  • 错误C2227:' - > GetBuffer'的左边必须指向class / struct / union / generic type
  •   
  • 错误C2065:'immContext':未声明的标识符
  •   
  • 错误C2228:'。Get'的左边必须有class / struct / union
  •   
  • 错误C2653:'DX':不是类或命名空间名称
  •   
  • 错误C3861:'ThrowIfFailed':找不到标识符
  •   

从ScreenGrab wiki-page:

运行插入的示例代码
#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的screengrab:

#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回答,但是我再也不知道如何获取屏幕的上下文和swapChain。 谢谢。

1 个答案:

答案 0 :(得分:0)

所以经过一段时间的观察并在伟大的查克的指导下!我找到了一个解决方案,将屏幕截图保存到磁盘的速度提高了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-&gt; 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

你也可以完全删除它,虽然这似乎在一开始就会导致“空白”/黑色图像文件。对我来说,在它开始捕捉之前只有两张空白图像。在整个init函数之后的延迟,我也得到了第一个图像,没有开销。谢谢。