C++ C++;具有16位颜色深度的Direct3D9 GetFrontBufferData

C++ C++;具有16位颜色深度的Direct3D9 GetFrontBufferData,c++,direct3d,screen-capture,color-depth,C++,Direct3d,Screen Capture,Color Depth,我目前正在开发一个小屏幕截图应用程序,它将我的屏幕和桌面都记录在一个文件中。 我正在使用GetFrontBufferData()函数,它工作得很好 不幸的是,当将屏幕颜色深度从32位更改为16位(执行一些测试)时,我的图像不好(分辨率发生变化的紫色图像),并且录制的屏幕截图质量非常差: 有人知道有没有办法在16位屏幕上使用GetFrontBufferData() 编辑: 我的init direct3D: ZeroMemory(&d3dPresentationParameters

我目前正在开发一个小屏幕截图应用程序,它将我的屏幕和桌面都记录在一个文件中。 我正在使用GetFrontBufferData()函数,它工作得很好

不幸的是,当将屏幕颜色深度从32位更改为16位(执行一些测试)时,我的图像不好(分辨率发生变化的紫色图像),并且录制的屏幕截图质量非常差:

有人知道有没有办法在16位屏幕上使用GetFrontBufferData()

编辑:

我的init direct3D:

    ZeroMemory(&d3dPresentationParameters,sizeof(D3DPRESENT_PARAMETERS));//Fills a block of memory with zeros.
    d3dPresentationParameters.Windowed = TRUE;
    d3dPresentationParameters.Flags = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;
    d3dPresentationParameters.BackBufferFormat = d3dFormat;//d3dDisplayMode.Format;//D3DFMT_A8R8G8B8;
    d3dPresentationParameters.BackBufferCount = 1;
    d3dPresentationParameters.BackBufferHeight = gScreenRect.bottom = uiHeight;
    d3dPresentationParameters.BackBufferWidth = gScreenRect.right = uiWidth;
    d3dPresentationParameters.MultiSampleType = D3DMULTISAMPLE_NONE;
    d3dPresentationParameters.MultiSampleQuality = 0;
    d3dPresentationParameters.SwapEffect = D3DSWAPEFFECT_DISCARD;
    d3dPresentationParameters.hDeviceWindow = hWnd;
    d3dPresentationParameters.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
    d3dPresentationParameters.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
我用于捕获屏幕截图的线程:

    CreateOffscreenPlainSurface(uiWidth, uiHeight, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, pBackBuffer, NULL)) != D3D_OK )
    {
        DBG("Error: CreateOffscreenPlainSurface failed = 0x%x", iRes);
        break;
    }

    GetFrontBufferData(0, pCaptureSurface)) != D3D_OK)
    {
        DBG("Error: GetFrontBufferData failed = 0x%x", iRes);
        break;
    }    

    //D3DXSaveSurfaceToFile("Desktop.bmp", D3DXIFF_BMP, pBackBuffer,NULL, NULL); //Test purposes

    ZeroMemory(lockedRect, sizeof(D3DLOCKED_RECT));     
    LockRect(lockedRect, NULL, D3DLOCK_READONLY)) != D3D_OK )
    {
        DBG("Error: LockRect failed = 0x%x", iRes);
        break;
    }

    if( (iRes = UnlockRect()) != D3D_OK )
    {
        DBG("Error: UnlockRect failed = 0x%x", iRes);
        break;
    }   
/**/        
IDirect3DSurface9* pCaptureSurface = NULL;
HRESULT hr = pD3DDevice->CreateOffscreenPlainSurface(
    D3DPresentParams.BackBufferWidth,
    D3DPresentParams.BackBufferHeight,
    D3DPresentParams.BackBufferFormat,
    D3DPOOL_SYSTEMMEM,
    &pCaptureSurface,
    NULL);
pD3DDevice->GetFrontBufferData(0, pCaptureSurface);
  • 这段代码可以完美地处理32位的颜色深度,但不能处理16位的颜色深度
  • 创建设备时,我为两个屏幕创建了两个设备(iscreenber)。这也是以32位(而不是16位)工作的
  • 当将捕获的屏幕截图保存到2个bmp文件中进行测试(16位)时,我有一个屏幕完美地代表主显示,另一个屏幕是黑色的
  • 当使用memcpy使用pData时,我看到上面的屏幕截图是紫色的,分辨率很差
edit2:

我注意到以下几点:

  • 当将屏幕外的表面保存到BMP文件时,我会得到主显示(在1.BMP上),它会在每一帧中刷新(因此工作正常)。对于第二个显示,我只得到第一帧,然后什么也没有
  • 引用MSDN for
    GetFrontBufferData
    “由
    pDestSurface
    指向的缓冲区将填充前缓冲区的表示,转换为标准的每像素32位格式
    D3DFMT_A8R8G8B8
    ”我想这是16位颜色深度的问题
  • 第一个问题来自memcpy,它不能正确处理16位颜色深度,我仍然不知道为什么-->需要帮助
  • 第二个问题是第二个显示器不工作,我也不知道为什么
我做错了什么?我的桌面N°xx.bmp文件上刚出现一个黑色图像


非常感谢您的帮助。

以下是我创建曲面以捕获屏幕截图的方法:

    CreateOffscreenPlainSurface(uiWidth, uiHeight, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, pBackBuffer, NULL)) != D3D_OK )
    {
        DBG("Error: CreateOffscreenPlainSurface failed = 0x%x", iRes);
        break;
    }

    GetFrontBufferData(0, pCaptureSurface)) != D3D_OK)
    {
        DBG("Error: GetFrontBufferData failed = 0x%x", iRes);
        break;
    }    

    //D3DXSaveSurfaceToFile("Desktop.bmp", D3DXIFF_BMP, pBackBuffer,NULL, NULL); //Test purposes

    ZeroMemory(lockedRect, sizeof(D3DLOCKED_RECT));     
    LockRect(lockedRect, NULL, D3DLOCK_READONLY)) != D3D_OK )
    {
        DBG("Error: LockRect failed = 0x%x", iRes);
        break;
    }

    if( (iRes = UnlockRect()) != D3D_OK )
    {
        DBG("Error: UnlockRect failed = 0x%x", iRes);
        break;
    }   
/**/        
IDirect3DSurface9* pCaptureSurface = NULL;
HRESULT hr = pD3DDevice->CreateOffscreenPlainSurface(
    D3DPresentParams.BackBufferWidth,
    D3DPresentParams.BackBufferHeight,
    D3DPresentParams.BackBufferFormat,
    D3DPOOL_SYSTEMMEM,
    &pCaptureSurface,
    NULL);
pD3DDevice->GetFrontBufferData(0, pCaptureSurface);
如果未在任何位置存储
D3DPresentParams
,则可以使用
IDirect3DDevice9::GetDisplayMode
获取交换链的宽度、高度和格式。捕获前缓冲区后可以执行的所有大小调整和格式转换操作。另外,据我所知,显示格式不支持alpha通道,因此它通常是
D3DFMT_X8R8G8B8
,而不是
D3DFMT_A8R8G8B8

更新:
实际上,您尝试使用d3d设备捕获整个屏幕,而不渲染任何内容。d3d/opengl的目的是创建或处理图像,并通过GPU加速。截图只是复制一些视频内存,它并没有使用所有的GPU电源。因此,使用任何GPU API都不会带来显著的收益。此外,当您捕获不是自己渲染的前缓冲区时,您会看到奇怪的事情发生。要扩展应用程序,您可以通过GDI捕获图像,然后将其加载到纹理中并进行任何GPU后处理。

因此我找到了一些解决问题的方法

1)第二个监视器不工作,我无法从中捕获16位的屏幕截图

这来自代码中的
memcpy(..)
行。因为我使用的是16位监视器,所以在执行
memcpy
时,表面内存已损坏,从而导致黑屏

我仍然没有找到解决方案,但我正在努力

2)屏幕截图的颜色错误

毫无疑问,这是由于16位的颜色深度。因为我使用的是
GetFrontBufferData
,我引用的是Microsoft:
pTestSurface
指向的缓冲区将填充前缓冲区的表示,转换为标准的32位/像素格式
D3DFMT_A8R8G8B8
。这意味着,如果我想使用来自
LockRect(…)
的像素数据,我必须将数据“重新转换”为16位模式。因此,我需要将我的pData数据从
D3DFMT_A8R8G8B8
转换为
D3DFMT_R5G6B5
,这非常简单

3)如何调试应用程序?

多亏了你的评论,我被告知我应该在16位时分析
pScreeInfo->pData
内容(多亏了尼罗)。因此,我使用
pScreeInfo->pData
中的原始数据创建了一个简单的方法,并在.bmp中复制:

    HRESULT hr;
    DWORD dwBytesRead;
    UINT uiSize = 1920 * 1080 * 4;
    HANDLE hFile;

    hFile = CreateFile(TEXT("data.raw"), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

    BOOL bOk = ReadFile(hFile, pData, uiSize, &dwBytesRead, NULL);

    if(!bOk)
        exit(0);

    pTexture = NULL;
    hr = pScreenInfo->g_pD3DDevice->CreateTexture(width, height, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &pTexture, NULL);

    D3DLOCKED_RECT lockedRect;

    hr = pTexture->LockRect(0, &lockedRect, NULL, D3DLOCK_READONLY);

    memcpy(lockedRect.pBits, pData, lockedRect.Pitch * height);

    hr = pTexture->UnlockRect(0);

    hr = D3DXSaveTextureToFile(test, D3DXIFF_BMP, pTexture,NULL);

    bOk = CloseHandle(hFile);

    SAFE_RELEASE(pTexture);
这段代码让我注意到pData数据是正确的,我可以在最后得到一个好的.bmp文件,这意味着
GetFrontBufferData(…)
工作正常,问题来自
memcpy(…)

4)遗留问题

我仍在试图知道如何解决memcpy问题,看看问题来自何方。这是最后一个问题,因为颜色现在很好(由于32位到16位的转换)


谢谢大家的宝贵意见

我猜你必须获取前缓冲区格式并转换为屏幕外缓冲区格式。@paulm你的意思是更改前缓冲区格式吗?因为我认为这是不可能的。我在考虑使用backbuffer,但我认为它与我的应用程序无关。ThanksI意思是如果frontbuffer.format!=yourbuffer.format然后将其转换为yourbuffer。format@paulm好的,我想我明白你的意思了,但是你如何转换你的frontbuffer格式呢?正如本主题中指出的,前面写的缓冲区格式无法指定。如果我误解了你的意思,请纠正我。感谢您的帮助您应该下载并使用PIX调试D3D9调用。这正是我所做的(请参阅我编辑的帖子)。到目前为止,我一直在为32位工作,没有任何问题。除了一个t