从IDirect3DSurface9获取像素?

时间:2015-01-18 16:11:14

标签: c++ directx-9 getpixel

我想快速访问使用directX9或10的游戏像素。因此我使用了D3D9库。当它是DirectX10游戏时,我是否必须使用D3D10库?如果这是真的,那么下一部分就无关紧要了。

以下代码有效,但我只在qDebug("%.2f, %.2f, %.2f, %.2f", pixel[0].r, pixel[0].g, pixel[0].b, pixel[0].a);中得到零,尽管它应该有另一种颜色,我不知道为什么。交换数据时是否有错误?

初始化:

IDirect3D9 *m_d3d;
IDirect3DDevice9 *m_d3ddev;
D3DDISPLAYMODE *m_d3ddm;
HRESULT hr;
m_d3d = Direct3DCreate9(D3D_SDK_VERSION);
if (m_d3d == NULL)
{
    qFatal("Cannot create Direct3D!");
}

m_d3ddm = (D3DDISPLAYMODE *)calloc(1, sizeof(D3DDISPLAYMODE));
hr = m_d3d->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, m_d3ddm);
if (hr != D3D_OK)
{
    m_d3d->Release();
    qFatal("Cannot get DisplayMode!");
}

D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.hDeviceWindow = m_target;
d3dpp.BackBufferWidth = m_d3ddm->Width;
d3dpp.BackBufferHeight = m_d3ddm->Height;
d3dpp.BackBufferFormat = m_d3ddm->Format;

hr = m_d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, m_target, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &m_d3ddev);
if (FAILED(hr))
{
    m_d3d->Release();
    qFatal("Failed  CreateDevice!");
}
else 
{
    qDebug("Created Device with %i * %i", m_d3ddm->Width, m_d3ddm->Height);
}

捕获

IDirect3DSurface9 *renderTarget= NULL;
IDirect3DSurface9 *destTarget = NULL;
hr = m_d3ddev->GetRenderTarget(0, &renderTarget);
if (FAILED(hr))
{
    qFatal("Failed  GetRenderTarget!");
}
hr = m_d3ddev->CreateOffscreenPlainSurface(m_d3ddm->Width, m_d3ddm->Height, m_d3ddm->Format, D3DPOOL_SYSTEMMEM, &destTarget, NULL);
if (FAILED(hr))
{
    qFatal("Failed  CreateOffscreenPlainSurface!");
}
hr = m_d3ddev->GetRenderTargetData(renderTarget, destTarget);
if (FAILED(hr))
{
    qFatal("Failed  GetRenderTargetData!");
}

D3DLOCKED_RECT lr;
ZeroMemory(&lr, sizeof(D3DLOCKED_RECT));
hr = destTarget->LockRect(&lr, 0, D3DLOCK_READONLY);
if (FAILED(hr))
{
    qFatal("Cannot lock rect!");
}
ARGB *pixel = (ARGB *)lr.pBits;
if (!pixel)
{
    qFatal("No data!");
}
qDebug("%.2f, %.2f, %.2f, %.2f", pixel[0].r, pixel[0].g, pixel[0].b, pixel[0].a);

hr = destTarget->UnlockRect();
if (FAILED(hr))
{
    qFatal("Cannot unlock rect!");
}
renderTarget->Release();
destTarget->Release(); 

清洁

m_d3ddev->Release();
m_d3d->Release();

delete m_d3ddm;
delete m_d3d;
delete m_d3ddev;

1 个答案:

答案 0 :(得分:3)

此代码模拟了您应该拥有的内容。它显示后备缓冲区实际上正在被捕获。我以与您完全相同的方式创建了设备。我以同样的方式捕获后缓冲区..

为了测试是否实际捕获了后缓冲区,我将窗口背景设置为海军蓝色。然后当我调用捕获函数时,我将缓冲区中的所有颜色与原始背景颜色进行比较..这让我知道单个像素是否与背景不同..

如果不同,我们可能"无法捕捉背景。否则,我们很好..

我已经测试了下面的内容,它确实捕获了后备缓冲区。我甚至将后备缓冲区保存到位图中,看它是否捕获了它并且它确实...

总而言之,您的代码没有任何问题,但我不能保证您正确使用它。换句话说,您还没有告诉我您在哪里调用后缓冲区捕获代码..它可以被调用很多地方,但最好的地方要么是在EndScene的钩子中,要么是对Present的钩子。如果您在自己的应用程序中使用它,请在onDraw / onUpdate函数中调用它。

#include <tchar.h>
#include <windows.h>
#include <d3d9.h>
#include <d3dx9.h>
#include <iostream>

HRESULT capture(IDirect3DDevice9* m_d3ddev, void* buffer, int& width, int& height, D3DFORMAT format);


void onUpdate(HWND hwnd, IDirect3D9* d3d9, IDirect3DDevice9* d3ddev9, D3DFORMAT format)
{
    d3ddev9->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 40, 100), 1.0f, 0);
    d3ddev9->BeginScene();
    //All drawing goes here..
    d3ddev9->EndScene();
    d3ddev9->Present(NULL, NULL, NULL, NULL);  //Swaps the back and front buffers. Displays all drawing on the screen..


    RECT rect;
    GetClientRect(hwnd, &rect);

    int width = rect.right - rect.left, height = rect.bottom - rect.top;
    unsigned char* buffer = new unsigned char[width * height * 4];
    capture(d3ddev9, buffer, width, height, format);

    unsigned char* px = buffer;

    for(int i = 0; i < height; ++i)
    {
        for(int j = 0; j < width; ++j)
        {
            unsigned char B = *px++;
            unsigned char G = *px++;
            unsigned char R = *px++;
            unsigned char A = *px++;

            if(D3DCOLOR_XRGB(R, G, B) != D3DCOLOR_XRGB(0, 40, 100))
            {
                MessageBox(NULL, "FAILED.. Colour differs from original background..", NULL, 0);
            }
        }
    }

    delete[] buffer;
}

int onDisplay(HWND hwnd)
{
    IDirect3D9* d3d9 = NULL;
    IDirect3DDevice9* d3ddev9 = NULL;
    D3DPRESENT_PARAMETERS Parameters = {0};
    d3d9 = Direct3DCreate9(D3D_SDK_VERSION);

    D3DDISPLAYMODE* dispMode = new D3DDISPLAYMODE();
    d3d9->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, dispMode);

    Parameters.Windowed = true;
    Parameters.SwapEffect = D3DSWAPEFFECT_DISCARD;
    Parameters.hDeviceWindow = hwnd;
    Parameters.BackBufferFormat = dispMode->Format;
    d3d9->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &Parameters, &d3ddev9);
    delete dispMode;

    MSG messages;
    ShowWindow(hwnd, SW_SHOW);

    while(true)
    {
        if(PeekMessage(&messages, NULL, 0, 0, PM_REMOVE) > 0)
        {
            if(messages.message != WM_QUIT)
            {
                TranslateMessage(&messages);
                DispatchMessageW(&messages);
                continue;
            }
            break;
        }
        else
        {
            onUpdate(hwnd, d3d9, d3ddev9, Parameters.BackBufferFormat);
        }
    }

    if(d3ddev9) d3ddev9->Release();
    if(d3d9) d3d9->Release();
    return messages.wParam;
}


LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch(message)
    {
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        default:
            return DefWindowProc(hwnd, message, wParam, lParam);
    }

    return 0;
}

int WINAPI WinMain(HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nCmdShow)
{
    WNDCLASSEX wincl;
    wincl.hInstance = hThisInstance;
    wincl.lpszClassName = "D3DWindow";
    wincl.lpfnWndProc = WindowProcedure;
    wincl.style = CS_DBLCLKS;
    wincl.cbSize = sizeof(WNDCLASSEX);
    wincl.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wincl.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
    wincl.hCursor = LoadCursor(NULL, IDC_ARROW);
    wincl.lpszMenuName = NULL;
    wincl.cbClsExtra = 0;
    wincl.cbWndExtra = 0;
    wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;

    if(!RegisterClassEx(&wincl)) return 0;
    HWND hwnd = CreateWindowEx(0, "D3DWindow", "D3D9: Window", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 544, 375, HWND_DESKTOP, NULL, hThisInstance, NULL);
    return onDisplay(hwnd);
}


HRESULT capture(IDirect3DDevice9* m_d3ddev, void* buffer, int& width, int& height, D3DFORMAT format)
{
    IDirect3DSurface9 *renderTarget= NULL;
    IDirect3DSurface9 *destTarget = NULL;
    HRESULT hr = m_d3ddev->GetRenderTarget(0, &renderTarget);
    hr = m_d3ddev->CreateOffscreenPlainSurface(width, height, format, D3DPOOL_SYSTEMMEM, &destTarget, NULL);
    if(FAILED(hr))
    {
        printf("Failed  CreateOffscreenPlainSurface!");
    }
    hr = m_d3ddev->GetRenderTargetData(renderTarget, destTarget);
    if(FAILED(hr))
    {
        printf("Failed  GetRenderTargetData!");
    }

    D3DLOCKED_RECT lr;
    ZeroMemory(&lr, sizeof(D3DLOCKED_RECT));
    hr = destTarget->LockRect(&lr, 0, D3DLOCK_READONLY);
    if(FAILED(hr))
    {
        printf("Cannot lock rect!");
    }

    if(lr.pBits)
    {
        memcpy(buffer, lr.pBits, width * height * 4);
    }

    hr = destTarget->UnlockRect();
    if(FAILED(hr))
    {
        printf("Cannot unlock rect!");
    }
    renderTarget->Release();
    destTarget->Release();
    return hr;
}