我目前正在开发一个小屏幕截图应用程序,它将我的屏幕桌面记录在一个文件中。 我正在使用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;
}
/**/
EDIT2:
我注意到以下内容:
GetFrontBufferData
引用MSDN“pDestSurface
指向的缓冲区将填充前缓冲区的表示形式,转换为标准32位/像素格式D3DFMT_A8R8G8B8
。“我想这是16位颜色深度的问题。我在这里做错了什么?我只是在桌面N°xx.bmp文件上获得了黑色图像
非常感谢你的帮助。
答案 0 :(得分:0)
这就是我创建一个表面来捕捉截图的方法:
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 :(得分:0)
所以我找到了一些问题的答案。
1)第二台显示器无法正常工作,我无法以16位方式从中捕获屏幕截图
这来自代码中的memcpy(..)
行。因为我正在使用16位监视器,所以在执行memcpy
时,表面存储器已损坏,导致黑屏。
我仍然没有找到解决方案,但我正在努力。
2)屏幕截图的颜色错误
这是毫不奇怪的,因为16位颜色深度。因为我正在使用GetFrontBufferData
,而且我引用的是Microsoft: pDestSurface
指向的缓冲区将填充前缓冲区的表示形式,转换为标准的32位/像素格式D3DFMT_A8R8G8B8
。这意味着,如果我想使用来自LockRect(...)
的像素数据,我必须将我的数据“重新转换”为16位模式。因此,我需要将我的pData数据从D3DFMT_A8R8G8B8
转换为D3DFMT_R5G6B5
,这非常简单。
3)如何调试应用程序?
感谢您的评论,我被告知我应该在16位时分析pScreeInfo->pData
内容(感谢Niello)。因此,我创建了一个简单的方法,使用来自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位转换)
感谢大家的有益评论!