I am currently developing a little screenshot application which records both of my screen's desktop in a file. I am using the GetFrontBufferData() function and it is working great.
我现在正在开发一个小屏幕截图应用程序,它记录了我的屏幕桌面的一个文件。我使用的是GetFrontBufferData()函数,它运行得很好。
Unfortunately when changing the screen color depth from 32 to 16 bits (to perform some tests) I have a bad image (purple image with changed resolution) and the recorded screenshot has a very poor quality:
不幸的是,当将屏幕颜色深度从32位更改为16位(执行一些测试)时,我有一个坏的图像(带有更改分辨率的紫色图像),并且记录的屏幕截图质量非常差:
Does someone know if there is a way to use GetFrontBufferData() with a 16 bits screen ?
有人知道是否有方法使用16位屏幕使用GetFrontBufferData()吗?
edit:
编辑:
My init direct3D:
我的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;
The thread I use to capture screenshots:
我用来捕捉屏幕截图的线程:
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;
}
/**/
- This code is perfectly working with 32 bits color depth but not with 16bits.
- 这段代码完美地处理了32位的颜色深度,而不是16位。
- When creating the device I create 2 devices for both screens (iScreenNber). This is also working in 32bits (not in 16).
- 在创建设备时,我为两个屏幕(iScreenNber)创建了两个设备。这也适用于32位(不是16位)。
- When saving the captured screenshot into 2 bmp files for testing (in 16 bits), I have one screen which represents the main display perfectly and the other screen is black.
- 当将捕获的屏幕截图保存为2个bmp文件进行测试(16位)时,我有一个屏幕,它代表了主显示的完美,而另一个屏幕是黑色的。
- When using memcpy to use pData, I have the above screenshot with purple color and bad resolution
- 当使用memcpy来使用pData时,我的屏幕截图是紫色的,分辨率也很差。
edit2:
edit2:
I noticed the following:
我注意到以下几点:
- When saving Offscreen surface to a BMP file, I get the main display (on 1.bmp) which is refreshed each frame (so it is working just fine). For the second display, I just get the first frame then nothing more.
- 当将Offscreen表面保存到一个BMP文件时,我得到了一个主要的显示(1.bmp),它会刷新每一个帧(所以它运行得很好)。对于第二次显示,我只得到第一帧,然后没有更多。
- Quoting MSDN for
GetFrontBufferData
"The buffer pointed to bypDestSurface
will be filled with a representation of the front buffer, converted to the standard 32 bits per pixel formatD3DFMT_A8R8G8B8
." I guess this is a problem for 16 bits color depth. - 引用MSDN的GetFrontBufferData“由pDestSurface指向的缓冲区将填充前面缓冲区的表示,转换为标准的32位每像素格式D3DFMT_A8R8G8B8。”我想这是16位颜色深度的问题。
- The first problem comes from the memcpy which does not handle properly the 16 bits color depth and I still don't know why ----> Help needed for this !!
- 第一个问题来自memcpy,它不能正确处理16位的颜色深度,我仍然不知道为什么--->帮助了这个!!
- Second problem is the second display which is not working and I don't why either
- 第二个问题是第二个显示器没有工作,我也不知道为什么。
What am I doing wrong here ? I just get a black image on my Desktop N°xx.bmp file
我在这里做错了什么?我刚得到一个黑色的图片在我的桌面N°xx。bmp文件
Thank you very much for your help.
非常感谢你的帮助。
2 个解决方案
#1
0
This is how I create a surface to capture screenshots:
这就是我如何创建一个屏幕截图的方法:
IDirect3DSurface9* pCaptureSurface = NULL;
HRESULT hr = pD3DDevice->CreateOffscreenPlainSurface(
D3DPresentParams.BackBufferWidth,
D3DPresentParams.BackBufferHeight,
D3DPresentParams.BackBufferFormat,
D3DPOOL_SYSTEMMEM,
&pCaptureSurface,
NULL);
pD3DDevice->GetFrontBufferData(0, pCaptureSurface);
If you didn't store D3DPresentParams
anywhere, you can use IDirect3DDevice9::GetDisplayMode
to obtain width, height and format of your swap chain. All operations of resizing and format conversion you can perform after capturing a front buffer. Also, as I know, display format doesn't support alpha channel, so it typically is D3DFMT_X8R8G8B8
, not D3DFMT_A8R8G8B8
.
如果您没有在任何地方存储D3DPresentParams,您可以使用IDirect3DDevice9::GetDisplayMode来获得交换链的宽度、高度和格式。所有的调整和格式转换的操作,您可以在捕获前缓冲区后执行。而且,正如我所知道的,显示格式不支持alpha通道,所以它通常是D3DFMT_X8R8G8B8,而不是D3DFMT_A8R8G8B8。
Update: Actually, you try to capture a whole screen by using d3d device, without rendering anything. A purpose of d3d/opengl is to create or process images and do it GPU-accelerated. Taking a screenshot is just copying some video memory, it doesn't use all GPU power. So, using any GPU API brings no significant gain. Moreover, when you capture front buffer rendered not by yourself, strange things occur, you see. To extend your app you may capture image by GDI and then load it into texture and do any GPU postprocessing.
更新:实际上,您试图通过使用d3d设备捕获整个屏幕,而不呈现任何内容。d3d/opengl的目的是创建或处理图像并进行gpu加速。截图只是复制一些视频内存,它不使用所有的GPU电源。因此,使用任何GPU API都不会带来显著的收益。此外,当你捕获的前缓冲区不是你自己,奇怪的事情发生,你看。为了扩展您的应用程序,您可以通过GDI捕获图像,然后将其加载到纹理中并进行任何GPU后处理。
#2
0
So i found some answers to my problem.
所以我找到了一些问题的答案。
1) Second monitor wasn't working and I was unable to capture screenshot from it in 16 bits
1)第二个监控器没有工作,我无法从16位中捕捉到屏幕截图。
This comes from the memcpy(..)
line in the code. Because I am working with a 16 bits monitor, when executing the memcpy
, the surface memory is corrupt and this leads to a black screen.
这来自代码中的memcpy(..)行。因为我使用的是一个16位的监视器,当执行memcpy时,表面内存是损坏的,这就导致了一个黑屏。
I still didn't find the solution for this but I'm working on.
我仍然没有找到解决办法,但我正在努力。
2) The colors of the screenshot are wrong
2)截图的颜色不对。
This is, without any surprise, due to the 16 bits color depth. Because I am using GetFrontBufferData
, and I am quoting Microsoft: The buffer pointed to by pDestSurface
will be filled with a representation of the front buffer, converted to the standard 32 bits per pixel format D3DFMT_A8R8G8B8
. This means, if I want to use the pixel data from LockRect(...)
, I have to "re-convert" my data into 16 bits mode. Therefore, I need to convert my pData data from D3DFMT_A8R8G8B8
to D3DFMT_R5G6B5
which is pretty simple.
这是,毫无意外,由于16位色深。因为我使用的是GetFrontBufferData,我引用了Microsoft:由pDestSurface所指向的缓冲区将填充前面缓冲区的表示,转换为标准的32位/像素格式D3DFMT_A8R8G8B8。这意味着,如果我想要使用LockRect(…)的像素数据,我必须将我的数据“重新转换”为16位模式。因此,我需要将我的pData数据从D3DFMT_A8R8G8B8转换为D3DFMT_R5G6B5,这非常简单。
3) How to debug the application ?
3)如何调试应用程序?
Thanks to your comments, I've been told that I should analyze pScreeInfo->pData
content when I was in 16bits (thanks to Niello). Therefore, I've created a simple method using raw data from pScreeInfo->pData
and copying in a .bmp:
感谢您的评论,我被告知,在我16位(感谢Niello)的时候,我应该分析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);
This piece of code allowed me to notice that pData data was correct and I could get a good .bmp file at the end which means that GetFrontBufferData(...)
was correctly working and the problem comes from the memcpy(...)
这段代码让我注意到pData数据是正确的,我可以在最后得到一个好的。bmp文件,这意味着GetFrontBufferData(…)是正确的工作,问题来自memcpy(…)
4) Remaining problems
4)剩下的问题
I am still trying to know how I can solve the memcpy issue to see where the problem comes from. This is the last problem since the colors are good now (thanks to the 32bits to 16 bits conversion)
我还想知道如何解决这个问题,看看问题出在哪里。这是最后一个问题,因为颜色现在很好(感谢32位到16位转换)
Thank everybody for your helpful comments !
感谢大家的宝贵意见!
#1
0
This is how I create a surface to capture screenshots:
这就是我如何创建一个屏幕截图的方法:
IDirect3DSurface9* pCaptureSurface = NULL;
HRESULT hr = pD3DDevice->CreateOffscreenPlainSurface(
D3DPresentParams.BackBufferWidth,
D3DPresentParams.BackBufferHeight,
D3DPresentParams.BackBufferFormat,
D3DPOOL_SYSTEMMEM,
&pCaptureSurface,
NULL);
pD3DDevice->GetFrontBufferData(0, pCaptureSurface);
If you didn't store D3DPresentParams
anywhere, you can use IDirect3DDevice9::GetDisplayMode
to obtain width, height and format of your swap chain. All operations of resizing and format conversion you can perform after capturing a front buffer. Also, as I know, display format doesn't support alpha channel, so it typically is D3DFMT_X8R8G8B8
, not D3DFMT_A8R8G8B8
.
如果您没有在任何地方存储D3DPresentParams,您可以使用IDirect3DDevice9::GetDisplayMode来获得交换链的宽度、高度和格式。所有的调整和格式转换的操作,您可以在捕获前缓冲区后执行。而且,正如我所知道的,显示格式不支持alpha通道,所以它通常是D3DFMT_X8R8G8B8,而不是D3DFMT_A8R8G8B8。
Update: Actually, you try to capture a whole screen by using d3d device, without rendering anything. A purpose of d3d/opengl is to create or process images and do it GPU-accelerated. Taking a screenshot is just copying some video memory, it doesn't use all GPU power. So, using any GPU API brings no significant gain. Moreover, when you capture front buffer rendered not by yourself, strange things occur, you see. To extend your app you may capture image by GDI and then load it into texture and do any GPU postprocessing.
更新:实际上,您试图通过使用d3d设备捕获整个屏幕,而不呈现任何内容。d3d/opengl的目的是创建或处理图像并进行gpu加速。截图只是复制一些视频内存,它不使用所有的GPU电源。因此,使用任何GPU API都不会带来显著的收益。此外,当你捕获的前缓冲区不是你自己,奇怪的事情发生,你看。为了扩展您的应用程序,您可以通过GDI捕获图像,然后将其加载到纹理中并进行任何GPU后处理。
#2
0
So i found some answers to my problem.
所以我找到了一些问题的答案。
1) Second monitor wasn't working and I was unable to capture screenshot from it in 16 bits
1)第二个监控器没有工作,我无法从16位中捕捉到屏幕截图。
This comes from the memcpy(..)
line in the code. Because I am working with a 16 bits monitor, when executing the memcpy
, the surface memory is corrupt and this leads to a black screen.
这来自代码中的memcpy(..)行。因为我使用的是一个16位的监视器,当执行memcpy时,表面内存是损坏的,这就导致了一个黑屏。
I still didn't find the solution for this but I'm working on.
我仍然没有找到解决办法,但我正在努力。
2) The colors of the screenshot are wrong
2)截图的颜色不对。
This is, without any surprise, due to the 16 bits color depth. Because I am using GetFrontBufferData
, and I am quoting Microsoft: The buffer pointed to by pDestSurface
will be filled with a representation of the front buffer, converted to the standard 32 bits per pixel format D3DFMT_A8R8G8B8
. This means, if I want to use the pixel data from LockRect(...)
, I have to "re-convert" my data into 16 bits mode. Therefore, I need to convert my pData data from D3DFMT_A8R8G8B8
to D3DFMT_R5G6B5
which is pretty simple.
这是,毫无意外,由于16位色深。因为我使用的是GetFrontBufferData,我引用了Microsoft:由pDestSurface所指向的缓冲区将填充前面缓冲区的表示,转换为标准的32位/像素格式D3DFMT_A8R8G8B8。这意味着,如果我想要使用LockRect(…)的像素数据,我必须将我的数据“重新转换”为16位模式。因此,我需要将我的pData数据从D3DFMT_A8R8G8B8转换为D3DFMT_R5G6B5,这非常简单。
3) How to debug the application ?
3)如何调试应用程序?
Thanks to your comments, I've been told that I should analyze pScreeInfo->pData
content when I was in 16bits (thanks to Niello). Therefore, I've created a simple method using raw data from pScreeInfo->pData
and copying in a .bmp:
感谢您的评论,我被告知,在我16位(感谢Niello)的时候,我应该分析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);
This piece of code allowed me to notice that pData data was correct and I could get a good .bmp file at the end which means that GetFrontBufferData(...)
was correctly working and the problem comes from the memcpy(...)
这段代码让我注意到pData数据是正确的,我可以在最后得到一个好的。bmp文件,这意味着GetFrontBufferData(…)是正确的工作,问题来自memcpy(…)
4) Remaining problems
4)剩下的问题
I am still trying to know how I can solve the memcpy issue to see where the problem comes from. This is the last problem since the colors are good now (thanks to the 32bits to 16 bits conversion)
我还想知道如何解决这个问题,看看问题出在哪里。这是最后一个问题,因为颜色现在很好(感谢32位到16位转换)
Thank everybody for your helpful comments !
感谢大家的宝贵意见!