草庐IT

c++ - 在 win7 下,为 opengl 窗口捕获的图像是黑色的

coder 2024-06-11 原文

我在主窗口上有几个子窗口,有的是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窗口部分是黑色的)

最佳答案

你好,我终于找到了一个完美的解决方案。根据 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);
}

关于c++ - 在 win7 下,为 opengl 窗口捕获的图像是黑色的,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28285601/

有关c++ - 在 win7 下,为 opengl 窗口捕获的图像是黑色的的更多相关文章

  1. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  2. ruby - 使用 `+=` 和 `send` 方法 - 2

    如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:

  3. ruby - 如何让Ruby捕获线程中的语法错误 - 2

    我正在尝试使用ruby​​编写一个双线程客户端,一个线程从套接字读取数据并将其打印出来,另一个线程读取本地数据并将其发送到远程服务器。我发现的问题是Ruby似乎无法捕获线程内的错误,这是一个示例:#!/usr/bin/rubyThread.new{loop{$stdout.puts"hi"abc.putsefsleep1}}loop{sleep1}显然,如果我在线程外键入abc.putsef,代码将永远不会运行,因为Ruby将报告“undefinedvariableabc”。但是,如果它在一个线程内,则没有错误报告。我的问题是,如何让Ruby捕获这样的错误?或者至少,报告线程中的错误?

  4. ruby-on-rails - 无法在 Rails 助手中捕获 block 的输出 - 2

    我在使用自定义RailsFormBuilder时遇到了问题,从昨天晚上开始我就发疯了。基本上我想对我的构建器方法之一有一个可选block,以便我可以在我的主要content_tag中显示其他内容。:defform_field(method,&block)content_tag(:div,class:'field')doconcatlabel(method,"Label#{method}")concattext_field(method)capture(&block)ifblock_given?endend当我在我的一个Slim模板中调用该方法时,如下所示:=f.form_field:e

  5. ruby - 如何计算 Liquid 中的变量 +1 - 2

    我对如何计算通过{%assignvar=0%}赋值的变量加一完全感到困惑。这应该是最简单的任务。到目前为止,这是我尝试过的:{%assignamount=0%}{%forvariantinproduct.variants%}{%assignamount=amount+1%}{%endfor%}Amount:{{amount}}结果总是0。也许我忽略了一些明显的东西。也许有更好的方法。我想要存档的只是获取运行的迭代次数。 最佳答案 因为{{incrementamount}}将输出您的变量值并且不会影响{%assign%}定义的变量,我

  6. ruby - (Ruby || Python) 窗口管理器 - 2

    我想用这两种语言中的任何一种(最好是ruby​​)制作一个窗口管理器。老实说,除了我需要加载某种X模块外,我不知道从哪里开始。因此,如果有人有线索,如果您能指出正确的方向,那就太好了。谢谢 最佳答案 XCB,X的下一代API使用XML格式定义X协议(protocol),并使用脚本生成特定语言绑定(bind)。它在概念上与SWIG类似,只是它描述的不是CAPI,而是X协议(protocol)。目前,C和Python存在绑定(bind)。理论上,Ruby端口只是编写一个从XML协议(protocol)定义语言到Ruby的翻译器的问题。生

  7. ruby - 在 ruby​​ 中生成一个进程,捕获 stdout,stderr,获取退出状态 - 2

    我想从ruby​​rake脚本运行一个可执行文件,比如foo.exe我希望将foo.exe的STDOUT和STDERR输出直接写入我正在运行rake任务的控制台.当进程完成时,我想将退出代码捕获到一个变量中。我如何实现这一目标?我一直在玩backticks、process.spawn、system但我无法获得我想要的所有行为,只有部分更新:我在Windows上,在标准命令提示符下,而不是cygwin 最佳答案 system获取您想要的STDOUT行为。它还返回true作为零退出代码,这可能很有用。$?填充了有关最后一次system调

  8. ruby - 捕获 Ruby Logger 输出以进行测试 - 2

    我有一个像这样的ruby​​类:require'logger'classTdefdo_somethinglog=Logger.new(STDERR)log.info("Hereisaninfomessage")endend测试脚本行如下:#!/usr/bin/envrubygem"minitest"require'minitest/autorun'require_relative't'classTestMailProcessorClasses当我运行这个测试时,out和err都是空字符串。我看到消息打印在stderr上(在终端上)。有没有办法让Logger和capture_io一起玩得

  9. ruby - Capistrano 中的执行、测试和捕获命令有什么区别? - 2

    关于SSHkit-Github它说:Allbackendssupporttheexecute(*args),test(*args)&capture(*args)来自SSHkit-Rubydoc,我明白execute实际上是test的别名?test之间有什么区别?,execute,capture在Capistrano/SSHKit中我应该什么时候使用? 最佳答案 执行只是执行命令。使用非0退出引发错误。测试方法的行为与execute完全相同,但是它返回bool值(true如果命令以0退出,而false否则)。它通常用于控制任务中的流程

  10. ruby - 如何捕获 ruby​​ 中的所有异常? - 2

    我们如何捕获或/和处理ruby​​中所有未处理的异常?例如,这样做的动机可能是将某种异常记录到不同的文件或发送电子邮件给系统管理。在Java中我们会做Thread.setDefaultUncaughtExceptionHandler(UncaughtExceptionHandlerex);在Node.js中process.on('uncaughtException',function(error){/*code*/});在PHP中register_shutdown_function('errorHandler');functionerrorHandler(){$error=error_

随机推荐