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;
}