2013-12-19 5 views
0

Я пытаюсь подключить игру DirectX. Я успешно загружен мой крюк, и я в состоянии сохранить изображения/BackBuffer на диске с помощью:Direct3D9 BackBuffer to PixelArray

HRESULT Capture(IDirect3DDevice9* Device, const char* FilePath) 
{ 
    IDirect3DSurface9* RenderTarget = nullptr; 
    HRESULT result = Device->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &RenderTarget); 
    //for some reason result is never S_OK but this still works. 
    result = D3DXSaveSurfaceToFile(FilePath, D3DXIFF_PNG, RenderTarget, nullptr, nullptr); 
    SafeRelease(RenderTarget); 
    return result; 
} 

Это экономит успешно, и я рад. Тем не менее, я хочу сохранить в пиксельный массив, а не на диск. Я попытался с помощью:

#include <memory> 

std::unique_ptr<std::uint8_t[]> mem(new std::uint8_t[100 * 100 * 4]); 

HRESULT Direct3DDevice9Proxy::EndScene() 
{ 
    IDirect3DSurface9* RenderTarget = nullptr; 
    HRESULT result = ptr_Direct3DDevice9->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &RenderTarget); 

    D3DLOCKED_RECT LR; 
    RenderTarget->LockRect(&LR, nullptr, D3DLOCK_NOSYSLOCK | D3DLOCK_READONLY); 
    memcpy(mem.get(), LR.pBits, LR.Pitch - 1); 
    RenderTarget->UnlockRect(); 

    SafeRelease(RenderTarget); 
    return ptr_Direct3DDevice9->EndScene(); 
} 

Однако, он никогда не блокируется, и он бросает нарушение доступа при попытке получить доступ к LR.pBits. Я не уверен, почему он не будет заблокирован. Есть ли другой способ, с помощью которого я могу захватить пиксели из backbuffer в массив байтов? Максимальное количество просмотров игры - 1366x768.

EDIT: Я пробовал:

void dump_buffer(LPDIRECT3DDEVICE9 Device, std::unique_ptr<std::uint8_t[]> &bits) 
{ 
    IDirect3DSurface9* RenderTarget = nullptr; 
    IDirect3DSurface9* DestTarget = nullptr; 
    D3DSURFACE_DESC rtDesc = {}; 

    Device->GetRenderTarget(0, &RenderTarget); 
    RenderTarget->GetDesc(&rtDesc); 

    Device->CreateOffscreenPlainSurface(rtDesc.Width, rtDesc.Height, rtDesc.Format, D3DPOOL_DEFAULT, &DestTarget, nullptr); 
    Device->GetRenderTargetData(RenderTarget, DestTarget); 

    if(DestTarget != nullptr) 
    { 
     D3DLOCKED_RECT rect; 
     DestTarget->LockRect(&rect, 0, D3DLOCK_READONLY);  
     memcpy(bits.get(), rect.pBits, rtDesc.Width * rtDesc.Height * 4); 

     std::uint8_t* ptr = &bits[0]; 
     CG::Image(ptr, rtDesc.Width, rtDesc.Height).Save("Foo.bmp"); 

     DestTarget->UnlockRect(); 
     DestTarget->Release(); 
    } 

    RenderTarget->Release(); 
} 

HRESULT Direct3DDevice9Proxy::EndScene() 
{ 
    dump_buffer(ptr_Direct3DDevice9, mem); 
    return ptr_Direct3DDevice9->EndScene(); 
} 

но мой образ черный. Есть идеи?

ответ

0

Обратите внимание, что EndScene ставит в очередь сцену, которую нужно визуализировать, - вам не гарантировано, что перед этим (или даже сразу после этого) что-либо было визуализировано.

IDirect3DDevice9::GetFrontBufferData может быть более полезным для вас. Он «Создает копию фронтального буфера устройства и помещает его в буфер памяти системы, предоставляемый приложением».

Что вы хотите сделать, это вызвать CreateOffscreenPlainSurface для создания pSurface в системной памяти, а затем вызвать GetFrontBufferData (0, pSurface).

+0

Я пробовал это. Это не сработало. Это привело к нарушению доступа. Вместо этого я использовал GetRenderTargetData, и вместо этого я получил черное изображение. Отредактировал OP, чтобы отразить то, что я пробовал. – Brandon

1

Немного поздно. Но надеюсь, что это кому-то поможет.

создания

 if (SUCCEEDED(hr = _device->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &backBuf))) 
     { 
      D3DSURFACE_DESC sd; 
      backBuf->GetDesc(&sd); 

      if (SUCCEEDED(hr = _device->CreateOffscreenPlainSurface(sd.Width, sd.Height, sd.Format, D3DPOOL_SYSTEMMEM, &_surface, NULL))) 

захвата

if (SUCCEEDED(hr = _device->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &backBuf))) 
    { 
     if (SUCCEEDED(hr = _device->GetRenderTargetData(backBuf, _surface))) 
     { 
      if (SUCCEEDED(hr = D3DXSaveSurfaceToFileInMemory(&buf, D3DXIFF_DIB, _surface, NULL, NULL))) 
      { 
       LPVOID imgBuf = buf->GetBufferPointer(); 
       DWORD length = buf->GetBufferSize(); 

В ЬиЕ вы будете иметь необработанные данные изображения. Не забудьте выпустить все.