Get a pixel color on directx11 from the screen

Started by
9 comments, last by mmarcoluca 1 year, 8 months ago

Hi,

I need to do function like:

RGBTRIPLE GetPixelColor(int x, int y)

to get a color on a single pixel on directx 11 from the actual frame in my screen

For the moment I have this code:

//For each Call to Present() do the following:

//Get Device
ID3D11Device* device;
HRESULT gd = pSwapChain->GetDevice(__uuidof(ID3D11Device), (void**)&device);
assert(gd == S_OK);

//Get context
ID3D11DeviceContext* context;
device->GetImmediateContext(&context);

//get back buffer
ID3D11Texture2D* backbufferTex;
HRESULT gb = pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2d), (LPVOID*)&backbufferTex);
assert(gb == S_OK);

I'am not an expert on directx.

I ask how I can get the pixel color of coordinate x,y from buffer or context or other.

Can you help me please ?

Thanks !

Advertisement

You need to create a matching texture with D3D11_USAGE_STAGING, copy from the back buffer to your staging texture using CopyResource, then call Map to access the raw pixel data of your staging data. Note that doing this can reduce your performance significantly since it introduces a synchronization point between the GPU and CPU, which prevents them from running in parallel.

Thank you !! I will try.

Only my curiosity what are a solutions that not reduce the performance ?

@mmarcoluca no, there isnt. unless you use an integrated graphics card with system ram - which will reduce the performance less compared to a dedicated gpu - you have to issue very long and complex driver and kernel level magic involving several copy operations, including waiting for every pending rendering opcodes, moving data through the pci-e, moving data from kernel to user mode…

in opengl and dx9 you can get even an 1x1 texture from the desktop any place you want, but that isnt faster either.

if you dont always need to run the algo, then do it maybe once in every 30 frames or so, that can save the performance to some extent.

Thank you ! To be honest I don't have experience in diectx. Please sorry for my question:

For what I have undestand I need to implement a code like this:

        D3D11_TEXTURE2D_DESC desc2;
        desc2.Width = desc.Width;
        desc2.Height = desc.Height;
        desc2.MipLevels = desc.MipLevels;
        desc2.ArraySize = desc.ArraySize;
        desc2.Format = desc.Format;
        desc2.SampleDesc = desc.SampleDesc;
        desc2.Usage = D3D11_USAGE_STAGING;
        desc2.BindFlags = 0;
        desc2.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
        desc2.MiscFlags = 0;

        ComPtr<ID3D11Texture2D> stagingTexture;
        hr = d3dDevice->CreateTexture2D(&desc2, nullptr, &stagingTexture);
        if (FAILED(hr)) {
            throw MyException::Make(hr, L"Failed to create staging texture");
        }

        // copy the texture to a staging resource
        d3dContext->CopyResource(stagingTexture.Get(), Texture);

        // now, map the staging resource
        hr = d3dContext->Map(
                stagingTexture.Get(),
                0,
                D3D11_MAP_READ,
                0,
                &mapInfo);
        if (FAILED(hr)) {
            throw MyException::Make(hr, L"Failed to map staging texture");
        }

I have found it here:

https://stackoverflow.com/questions/38165270/retrieving-id3d11texture2d-data-to-be-sent-over-network

if this code match my need (I don't sure) I ask how I can get the pixel color and where I need to pass the X , Y coordinate (desc2.Height, desc2.Width ? )

Can you help me please ?

Thanks !

The Map function will give you a pointer to the texture data. Then you can use pointer arithmetic to access parts of the texture. Make sure you pay attention to the texture pitch.

@mmarcoluca the common way to avoid performance loss is to make sure that you wait a few frames before calling Map to read the staging resource. To do this you need a few staging textures that you cycle through every frame, and then you call Map to read from the oldest texture data. This of course means that you now have more latency between you render things and when you read the pixel data.

Depending on what you're trying to do there might be other ways to avoid needing the readback entirely. If you want to share more details we would be happy to see if we could help suggest an alternative. ?

Thank you ! I need the final step:

hr = d3dContext->Map( stagingTexture.Get(), 0, D3D11_MAP_READ, 0, &mapInfo);

How I can get RGBTRIPLE or similar from mapInfo:

I seaching for it but for the moment I havent find it. Probably I missing something.

The pData member of D3D11_MAPPED_SUBRESOURCE is going to be a pointer to the raw texel data, matching the DXGI_FORMAT of the staging texture. So if it's R8G8B8A8_UNORM then there's going 4 bytes per pixel, in RGBA order starting from low to high. You can cast the pData pointer to a uint8_t or a uint32_t or a custom struct you've defined with 1 byte for each channel. Like Aerodactyl55 mentioned though, you need to use the RowPitch when going from one row to the next.

MJP said:

The pData member of D3D11_MAPPED_SUBRESOURCE is going to be a pointer to the raw texel data, matching the DXGI_FORMAT of the staging texture. So if it's R8G8B8A8_UNORM then there's going 4 bytes per pixel, in RGBA order starting from low to high. You can cast the pData pointer to a uint8_t or a uint32_t or a custom struct you've defined with 1 byte for each channel. Like Aerodactyl55 mentioned though, you need to use the RowPitch when going from one row to the next.

Thank you I have tried to start to implement my function that get a pixel color from screen via DirectX 11:

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

RGBTRIPLE GetPixelColor(int X, int Y, DXGI_SWAP_CHAIN_DESC* swapchainDescription, ID3D11DeviceContext* pContext)
{
RGBTRIPLE rgb;
D3D11_TEXTURE2D_DESC desc2;
desc2.Width = 1;
desc2.Height = 1;
//desc2.MipLevels = desc.MipLevels; ??  //// How I get this ??
//desc2.ArraySize = desc.ArraySize; ??  //// How I get this ??
desc2.Format = swapchainDescription->BufferDesc.Format;
desc2.SampleDesc = swapchainDescription->SampleDesc;
desc2.Usage = D3D11_USAGE_STAGING;
desc2.BindFlags = 0;
desc2.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
desc2.MiscFlags = 0;

ID3D11Texture2D* stagingTexture;
ID3D11Texture2D* pSourceTexture; //// How I get this ??

HRESULT hr = pDevice->CreateTexture2D(&desc2, nullptr, &stagingTexture);
if (FAILED(hr)) {
 LF::Log_Update("Fail");
}

D3D11_BOX srcBox;
srcBox.left = X;
srcBox.right = srcBox.left + 1;
srcBox.top = Y;
srcBox.bottom = srcBox.top + 1;
srcBox.front = 0;
srcBox.back = 1;

pContext->CopySubresourceRegion(stagingTexture, 0, 0, 0, 0, pSourceTexture, 0, &srcBox);

D3D11_MAPPED_SUBRESOURCE msr;
pContext->Map(stagingTexture, 0, D3D11_MAP_READ, 0, &msr);
uint32_t pixel = reinterpret_cast<uint32_t>(msr.pData);  /// I don't think is correct

rgb.rgbtRed = (pixel >> 16) & 0xff;
rgb.rgbtGreen = (pixel >> 8) & 0xff;
rgb.rgbtBlue = pixel & 0xff;

return rgb;
}

but it is incomplete and I don't sure if I'am in the right way becouse there are some parts what I don't think are correct.

Can you please help me with my code ?

Thanks !

This topic is closed to new replies.

Advertisement