GetPixel via DirectX 11 efficient implementation

Viewed 65

I have this function that get a Pixel color from the screen with GDI+ API:

 RGBTRIPLE GetPixelColorGDI(int x, int y)
    {
        RGBTRIPLE rgb;
        HDC dc = GetDC(NULL);
    
        COLORREF color = GetPixel(dc, x, y);
        ReleaseDC(NULL, dc);
    
        rgb.rgbtRed = GetRValue(color);
        rgb.rgbtGreen = GetGValue(color);
        rgb.rgbtBlue = GetBValue(color);
    
        return rgb;
    }

the function works perfectly but it is too slow.

For this reason I like to try to implement the equivalent in DirectX 11 (I don't have a good experience in DirectX in general) and please excuse me if it is incorrect, this in my first attempt:

/// <summary>
/// Get pixel color from screen (24 bit) via DirextX 11
/// </summary>
/// <param name="X"></param>
/// <param name="Y"></param>
/// <param name="swapchainDescription"></param>

RGBTRIPLE GetPixelColorDX11(int X, int Y, IDXGISwapChain* pSwapChain)
{
    RGBTRIPLE rgb;
    HRESULT hr = 0;
    using Texture2D = ID3D11Texture2D*;
    IDXGIResource* backbufferptr = nullptr;
    ID3D11Resource* SourceResource = nullptr;
    Texture2D DestResource = nullptr;
    ID3D11Device* device = nullptr;
    ID3D11DeviceContext* context = nullptr;
    D3D11_MAPPED_SUBRESOURCE MappedSubresource;

    hr = pSwapChain->GetBuffer(0, __uuidof(IDXGIResource), (void**)&backbufferptr);
    if (hr < 0) {
        return;
    }
    hr = backbufferptr->QueryInterface(__uuidof(ID3D11Resource), (void**)&SourceResource);
    if (hr < 0) {
        return;
    }
    hr = pSwapChain->GetDevice(__uuidof(ID3D11Device), (void**)&device);
    if (hr < 0) {
        return;
    }
    DXGI_SWAP_CHAIN_DESC desc;
    hr = pSwapChain->GetDesc(&desc);
    if (hr < 0) {
        return;
    }

    D3D11_TEXTURE2D_DESC TextureDesciption = { };
    TextureDesciption.Format = desc.BufferDesc.Format;
    TextureDesciption.Width = desc.BufferDesc.Width;
    TextureDesciption.Height = desc.BufferDesc.Height;
    TextureDesciption.MipLevels = 1;
    TextureDesciption.ArraySize = 1;
    TextureDesciption.SampleDesc.Count = 1;
    TextureDesciption.Usage = D3D11_USAGE_STAGING;
    TextureDesciption.BindFlags = 0;
    TextureDesciption.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
    TextureDesciption.MiscFlags = 0;
    hr = device->CreateTexture2D(&TextureDesciption, nullptr, &DestResource);
    if (hr < 0) {
        return;
    }

    device->GetImmediateContext(&context);
    if (!context) {
        return;
    }

    context->CopyResource(DestResource, SourceResource);

    context->Map(DestResource, 0, D3D11_MAP_READ, 0, &MappedSubresource);

    COLORREF* pPixels = (COLORREF*)MappedSubresource.pData;

    rgb.rgbtRed = (pPixels[0] >> 16) & 0xff;
    rgb.rgbtGreen = (pPixels[0] >> 8) & 0xff;
    rgb.rgbtBlue = pPixels[0] & 0xff;

    return rgb;
}

In this code is missing where I can set in X and Y coordinate in pixel because I don't known how to do it.

Apart this I don't sure if this way is enough efficient to make this more faster then the GDI++ GetPixel version.

The idea is (I don't sure if is possible) is select only one pixel (x, y) on source texture and read the color on "pPixels[0]"

Update

I explain better why I need and why.

My hooked DLL hook the DX11 "present" and draw a rectangle using shader that work as "placeholder" on a game menu.

Drawing phase is very fast becouse use DX11 and there no any FPS drop.

To do it I use a library called "imgui".

Before draw the rectangle I need to known in what position I need to place the rectangle and this depend by the result of the actual GetPixelColorGDI that is slow.

The user press a arrow key to move the rectangle on another position.

I call "GetPixelColorGDI" from 1 to 11 times for single screen.

If the next slot is filled I call GetPixelColorGDI only one time, but if the next 10 slots are empty I need to call GetPixelColorGDI 10 times.

So best solution can be create GetPixelColorDX11 like this:

RGBTRIPLE GetPixelColorDX11(int X, int Y, IDXGISwapChain* pSwapChain)

and this overloald:

struct COORDINATE
{
 int x;
  int y;
 }
 RGBTRIPLE[] GetPixelColorDX11(COORDINATE[] Pixel , IDXGISwapChain* pSwapChain)

so this can cover all cases.

here my function that I use to initialize DirectX to draw the rectangles:

bool InitDirectX(IDXGISwapChain* pChain)
{
    // Get swapchain
    pSwapchain = pChain;

    // Get device and context
    HRESULT hr = pSwapchain->GetDevice(__uuidof(ID3D11Device), (PVOID*)&pDevice);
    if (FAILED(hr))
    {
        std::cerr << "Failed to get device from swapchain" << std::endl;
        return false;
    }
    pDevice->GetImmediateContext(&pContext);

    // Get window from swapchain description
    DXGI_SWAP_CHAIN_DESC swapchainDescription;
    pSwapchain->GetDesc(&swapchainDescription);
    hWindow = swapchainDescription.OutputWindow;

    // Use SetWindowLongPtr to modify window behaviour and get input
    wndProcHandlerOriginal = (WNDPROC)SetWindowLongPtr(hWindow, GWLP_WNDPROC, (LONG_PTR)hWndProc);

    std::cout << "Successfully initialised DirectX - resolution " << swapchainDescription.BufferDesc.Width << "x" << swapchainDescription.BufferDesc.Height << std::endl;

    // Update the screen resolution multiplier
    UpdateResolutionMultiplier(swapchainDescription.BufferDesc.Width, swapchainDescription.BufferDesc.Height);

    return true;
}
0 Answers
Related