我在主窗口上有几个子窗口,有些是GDI窗口,有些是opengl渲染窗口,一个功能是用rect捕获图像(可能覆盖不同的窗口组合)。这个功能在windows xp下工作正常。但是,在Windows 7下,所有opengl渲染窗口都是黑色的。我做了一些研究,有人说gdi不能通过窗口DC直接访问帧缓冲区,并且必须使用glReadPixels来组合位图。然而,这种方法很尴尬,因为我必须分别组合该矩形中的每个窗口。谁对我有更好的选择?
这是我捕获bmp的代码:
void MainWndClass::catchBmp(const char* path_fn, bool drawAreaOnly /*=0*/)
{
CDC *pDC=GetDC();
int BitPerPixel = pDC->GetDeviceCaps(BITSPIXEL);
int Left,Top,Width,Height;
if (drawAreaOnly)
{
Left = rBDWin.left;
Top = rBDWin.top;
Width = rBDWin.right-rBDWin.left;
Width = Width/4*4;
Height = rBDWin.bottom-rBDWin.top;
Height = Height/4*4;
}
else
{
Left=rbmpWin.left;
Top=rbmpWin.top;
Width=rbmpWin.right-rbmpWin.left;
Width=Width/4*4;
Height=rbmpWin.bottom-rbmpWin.top;
Height=Height/4*4;
}
CDC memDC;
memDC.CreateCompatibleDC(pDC);
CBitmap memBitmap, *oldmemBitmap;
memBitmap.CreateCompatibleBitmap(pDC, Width, Height);
//it seems does no work
//short bpp=24;
if(BitPerPixel>24) BitPerPixel=24;
memBitmap.SetBitmapBits(2,&BitPerPixel);
oldmemBitmap = memDC.SelectObject(&memBitmap);
//copy the bitmap from the pDC (source)
memDC.BitBlt(0, 0, Width, Height, pDC, Left, Top, SRCCOPY);
/*
CString title;
GetWindowText(title);
memDC.SetBkMode(TRANSPARENT);
memDC.TextOut(64,4,title);
*/
BITMAP bmp;
memBitmap.GetBitmap(&bmp);
if(bmp.bmBitsPixel>24)
{
bmp.bmBitsPixel=24;
//bmp.bmWidthBytes=bmp.bmWidth*3;
}
bmp.bmWidthBytes=bmp.bmWidth*(bmp.bmBitsPixel/8);
FILE *fp=NULL;
//path_fn+=".bmp";
fp=fopen((LPCTSTR)path_fn,"w+b");
BITMAPINFOHEADER bih = {0};
bih.biBitCount = bmp.bmBitsPixel;
bih.biCompression = BI_RGB;
bih.biHeight = bmp.bmHeight;
bih.biPlanes = 1;
bih.biSize = sizeof(BITMAPINFOHEADER);
bih.biSizeImage = bmp.bmWidthBytes * bmp.bmHeight;
bih.biWidth = bmp.bmWidth;
BITMAPFILEHEADER bfh = {0};
bfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
bfh.bfSize = bfh.bfOffBits + bmp.bmWidthBytes * bmp.bmHeight;
bfh.bfType = (WORD)0x4d42;
if(fp)
{
fwrite(&bfh, 1, sizeof(BITMAPFILEHEADER), fp);
fwrite(&bih, 1, sizeof(BITMAPINFOHEADER), fp);
}
byte * p = new byte[bmp.bmWidthBytes * bmp.bmHeight];
//copy the bits to the buffer
int ret=GetDIBits(memDC.m_hDC, (HBITMAP) memBitmap.m_hObject, 0, Height, p,
(LPBITMAPINFO) &bih, DIB_RGB_COLORS);
if(fp)
fwrite(p, 1, bmp.bmWidthBytes * bmp.bmHeight, fp);
delete [] p;
if(fp)
fclose(fp);
memDC.SelectObject(oldmemBitmap);
}
opengl窗口配置为:
PIXELFORMATDESCRIPTOR pixelDesc =
{
sizeof(PIXELFORMATDESCRIPTOR),
1,
PFD_DRAW_TO_WINDOW|PFD_SUPPORT_OPENGL|
PFD_DOUBLEBUFFER,
PFD_TYPE_RGBA,
24,
0,0,0,0,0,0,
0,
0,
0,
0,0,0,0,
32,//
0,
0,
PFD_MAIN_PLANE,
0,
0,0,0
};
我想再次强调这个事实: 它在xp下运行,但不在win7下(opengl窗口部分为黑色)
答案 0 :(得分:2)
你好我终于得到了一个完美的解决方案。根据Mats Pertersson提供的信息,我很确定这是因为它符合事实。 Windows 7引入了透明窗口外观,并且每个窗口都不是最终结果。最终结果(屏幕输出)由所有窗口组成。所以我来了解决方案,捕获最终屏幕而不是捕获主窗口。它在xp和win 7下都很完美。
主要变化:所有DC都来自屏幕而不是窗口,因此相关功能全部更改为全局gdi功能。
以下是代码:
catchBmp(const char* path_fn, bool drawAreaOnly /*=0*/)
{
//CDC *pDC=GetDC();
HDC hdcScreen;
HDC hdcMemDC = NULL;
HBITMAP hbmScreen = NULL;
BITMAP bmpScreen;
hdcScreen=::GetDC(NULL);
int BitPerPixel = ::GetDeviceCaps(hdcScreen,BITSPIXEL);
int Left,Top,Width,Height;
if (drawAreaOnly)
{
Left = rBDWin.left;
Top = rBDWin.top;
Width = rBDWin.right-rBDWin.left;
Width = Width/4*4;
Height = rBDWin.bottom-rBDWin.top;
Height = Height/4*4;
}
else
{
Left=rbmpWin.left;
Top=rbmpWin.top;
Width=rbmpWin.right-rbmpWin.left;
Width=Width/4*4;
Height=rbmpWin.bottom-rbmpWin.top;
Height=Height/4*4;
}
hdcMemDC=::CreateCompatibleDC(hdcScreen);
hbmScreen=::CreateCompatibleBitmap(hdcScreen,Width,Height);
if(BitPerPixel>24) BitPerPixel=24;
::SetBitmapBits(hbmScreen,2,&BitPerPixel);
::SelectObject(hdcMemDC,hbmScreen);
BitBlt(hdcMemDC,
0,0,Width,Height,hdcScreen,Left,Top,SRCCOPY);
::GetObject(hbmScreen,sizeof(BITMAP),&bmpScreen);
if(bmpScreen.bmBitsPixel>24)
{
bmpScreen.bmBitsPixel=24;
}
bmpScreen.bmWidthBytes=bmpScreen.bmWidth*(bmpScreen.bmBitsPixel/8);
FILE *fp=NULL;
fp=fopen((LPCTSTR)path_fn,"w+b");
BITMAPINFOHEADER bih = {0};
bih.biBitCount = bmpScreen.bmBitsPixel;
bih.biCompression = BI_RGB;
bih.biHeight = bmpScreen.bmHeight;
bih.biPlanes = 1;
bih.biSize = sizeof(BITMAPINFOHEADER);
bih.biSizeImage = bmpScreen.bmWidthBytes * bmpScreen.bmHeight;
bih.biWidth = bmpScreen.bmWidth;
BITMAPFILEHEADER bfh = {0};
bfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
bfh.bfSize = bfh.bfOffBits + bmpScreen.bmWidthBytes * bmpScreen.bmHeight;
bfh.bfType = (WORD)0x4d42;
if(fp)
{
fwrite(&bfh, 1, sizeof(BITMAPFILEHEADER), fp);
fwrite(&bih, 1, sizeof(BITMAPINFOHEADER), fp);
}
byte * p = new byte[bmpScreen.bmWidthBytes * bmpScreen.bmHeight];
GetDIBits(hdcScreen, hbmScreen, 0, Height, p, (LPBITMAPINFO) &bih, DIB_RGB_COLORS);
if(fp)
fwrite(p, 1, bmpScreen.bmWidthBytes * bmpScreen.bmHeight, fp);
delete [] p;
if(fp)
fclose(fp);
::DeleteObject(hbmScreen);
::DeleteObject(hdcMemDC);
::ReleaseDC(NULL,hdcScreen);
//memDC.SelectObject(oldmemBitmap);
}
答案 1 :(得分:0)
作为开发人员在过去十年左右开启和关闭GPU(目前正在“开启”,但未使用graphig),我将尝试解释发生了什么:
GPU通常可以输出多个“层” - 例如,在现代图形卡上,鼠标光标位于它自己的层中,因此我们不必重绘(如过去就是这种情况,视频卡/驱动程序会“记住”鼠标光标下的内容,以及移动鼠标时的重绘信息。该层位于屏幕上的实际图形之上,当帧缓冲存储器被扫描出来时它们被组合 - 也就是说,当像素颜色被发送到显示器本身时 - 每个层都按照定义的顺序读取,并且根据各自的α值组合不同层的颜色。
一些OpenGL驱动程序&硬件发现将3D绘制到单独的层更容易,然后在“扫描输出”阶段将两者结合起来。这有时会提供更好的性能,因为GL驱动程序“拥有”该层,并且不必与GDI同时尝试绘制到屏幕上。
当然,当GDI回读内容时,它只能读取GDI知道的内容[这也是为什么鼠标光标通常不会出现在屏幕副本中]