当前位置: 首页 > 知识库问答 >
问题:

在 win7 下,为打开窗口捕获的图像是黑色的

苏承载
2023-03-14

我在主窗口上有几个子窗口,有些是GDI窗口,有些是opengl渲染窗口,一个功能是用矩形捕获图像(可能覆盖不同的窗口组合)。此功能在Windows xp下工作正常。但是,在窗口 7 下,所有 opengl 渲染的窗口都是黑色的。我做了一些研究,有人说gdi不能通过窗口DC直接访问帧缓冲区,并且必须使用glReadPixel来组合位图。然而,这种方法很尴尬,因为我必须单独组合该矩形中的每个窗口。有人为我提供更好的选择吗?

这是我捕获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窗口部分是黑色的)

共有2个答案

阎宾实
2023-03-14

作为一名在过去十年左右的时间里断断续续使用GPU(目前是“开着”,但不做图形)的开发人员,我将尝试解释正在发生的事情:

GPU通常有多个可以输出到的“层” - 例如,在现代图形卡上,鼠标光标位于它自己的一个层中,因此我们不必重绘内容(过去的情况是,视频卡/驱动程序会“记住”鼠标光标下的内容,以及移动鼠标时的重绘)。该层位于屏幕上实际图形的顶部,当扫描出帧缓冲区内存时(即,当像素颜色发送到显示器本身时),它们被组合在一起,每个层都按定义的顺序读取,并且不同层的颜色根据其各自的alpha值进行组合。

一些OpenGL驱动程序

当然,当 GDI 回读内容时,它只能读取 GDI 知道的内容[这也是为什么鼠标光标通常不存在于屏幕副本中的原因]

马承
2023-03-14

你好,我终于找到了一个完美的解决方案。根据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);
}
 类似资料:
  • 当我运行此代码时,它会显示一个空白的黑色窗口,并显示未定义。 这不是颜色,它没有画任何线强硬我给的命令 我的错误是什么,阻止我将屏幕变白并引发错误?

  • 我正在写一个简单的mandelbrot渲染器。在程序计算并绘制所有内容后,它将保存曲面并进行blits。这很好,但在我添加了更新显示功能后,它只显示一个黑屏。我需要更新每次迭代,因为我将添加一些文本和其他内容。我只包括了节目的重要部分。有人知道为什么会这样吗?

  • 我对glfw有一个小问题。 我的代码非常简单,我只想创建一个空窗口。 } 此代码编译,但当我运行它时,我只有一个白色窗口。窗口的标题是正确的,但里面的一切都是白色的......我尝试像那样使用glClearColor。 但是我的窗户仍然是白色的....我用的是visual studio 2015。 怎么弄个黑窗? 编辑: 忘了补充这个:glfwMakeContextCurrent(window);

  • 我正在为平台游戏做教程,但我的Pygame窗口显示一个黑屏。 我不知道怎么解决这个问题。 我尝试再次关闭和打开Visual Studio Code,但没有任何结果。

  • 我尝试将背景透明的tiff格式的图像转换为jpeg,以将其大小调整为200x200或1200x1200,但在转换时,背景变为黑色,我希望在转换后保持背景透明或白色 我的代码如下: 这在java JAI中是如何实现的?

  • “黑白”调整可让您将彩色图像转换为灰度图像,同时保持对各颜色的转换方式的完全控制。也可以通过对图像应用色调来为灰度着色,例如创建棕褐色效果。 执行下列操作之一: 单击“调整”面板中的“黑白”图标 。 选取“图层”>“新建调整图层”>“黑白”。在“新建图层”对话框中,键入调整图层的名称,然后单击“确定”。 Photoshop 会应用默认的灰度转换。 注意:也可以选择“图像”>“调整”>“黑白”。但是