GDI截屏,在不同电脑上结果不同

3

我尝试使用GDI进行截图,然后在FFmpeg中使用它。

截图效果良好,FFmpeg处理它没有任何问题。

但是,在某些电脑上,图像并不像我想象的那样,就像下面您可以看到的那样。

enter image description here

这是我用来初始化位图的代码:

//--
mImageBuffer = new unsigned char[mWxHxS];
memset(mImageBuffer, 0, mWxHxS);
//--
hScreenDC = GetDC(0);
hMemoryDC = CreateCompatibleDC(hScreenDC);
//--
bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bi.bmiHeader.biBitCount = 24;
bi.bmiHeader.biWidth = mWidth;
bi.bmiHeader.biHeight = mHeight;
bi.bmiHeader.biCompression = BI_RGB;
bi.bmiHeader.biPlanes = 1;
bi.bmiHeader.biClrUsed = 24;
bi.bmiHeader.biClrImportant = 256;
hBitmap = CreateDIBSection(hMemoryDC, &bi, DIB_RGB_COLORS, &mImageBuffer, 0, 0);
SelectObject(hMemoryDC, hBitmap);

每个截图都在这里:

if(BitBlt(
    hMemoryDC,
    0,
    0,
    mWidth,
    mHeight,
    hScreenDC,
    mPx,
    mPy,
    SRCCOPY | CAPTUREBLT
))

我的应用程序没有任何错误,但是在一些计算机上出现了这个丑陋的图像。

我不知道是什么差异导致了这种情况(所有计算机都是Win7,已激活Aero...)。

我不明白为什么我的代码遵循了我找到的所有示例...

请帮帮我!


1
看起来你只使用了rgb而不是rgba。(这只是我想到的第一件事) - AntiHeadshot
Vcl::Forms::TMonitor* pMon = Screen->Monitors[i]; mWidth = pMon->Width; mHeight = pMon->Height; mWxHxS = mWidth*mHeight*3; - Maypeur
2
我想这应该是真正的分辨率,而不是显示器本身的分辨率(因为我没有使用VCL所以我不知道)?根据我在文档中找到的信息:宽度属性不考虑任何停靠在监视器上的任务栏或工具栏。可能是系统将任务栏放在左侧或右侧,从而影响了输出吗? - Zdeslav Vojkovic
1
biClrUsed 不应该设置为每像素的位数。它是用于基于调色板的图像,这些图像不使用调色板中的所有颜色。除非您正在创建带有调色板(颜色表)的位图,否则应将其设置为0。 - Adrian McCarthy
这个账户可以在哪里找到?不管怎样,我尝试我的代码的所有屏幕都是4的倍数。 - Maypeur
显示剩余5条评论
2个回答

3
您正在创建一个设备无关位图(CreateDIBSection),然后使用设备相关上下文(CreateCompatibleDC)来处理它。我认为您需要创建一个设备相关位图,以便与BitBlt兼容,或者使用StretchDIBits支持设备无关图像数据。之所以在某些计算机上可以工作而另一些则不行,是因为视频驱动程序确定了设备相关图像的格式,这可能与Windows定义的设备无关图像不同。
以下是一个捕获图像的示例(虽然过长,但仍似乎包含有用的信息):https://msdn.microsoft.com/en-us/library/windows/desktop/dd183402(v=vs.85).aspx 如果您需要使用DIB,请参阅StretchDIBits的文档:https://msdn.microsoft.com/en-us/library/windows/desktop/dd145121(v=vs.85).aspx

谢谢Matt,我现在明白问题所在了,但我想从设备上下文复制到DIB,并且StrechDIBits似乎会反转(从DIB到矩形),我是对的吗?你链接中的示例保留了兼容的DC,就像我的代码一样...所以我认为我需要找到一个与上下文无关的HDC到DIB函数! - Maypeur
我尝试使用CreateCompatibleBitmap并使用GetDIBits,但结果总是相同的:hBitmap = CreateCompatibleBitmap(hScreenDC, mWidth, mHeight); if(GetDIBits(hScreenDC, hBitmap, 0, mHeight, mImageBuffer, &bi, DIB_RGB_COLORS)) - Maypeur

1

所以我最终找到了解决方案:

似乎BitBlt和StretchBlt在某些计算机上无法正确处理32位到24位的转换...

现在,我只使用GDI的32位,并让FFmpeg与libswscale将我的RGBA图像转换为YUV格式。

我的更改:

mWidth = GetDeviceCaps(hScreenDC, HORZRES);
mHeight = GetDeviceCaps(hScreenDC, VERTRES);
mWxHxS = mWidth*mHeight*4;

bi.bmiHeader.biBitCount = 32;
hBitmap = CreateCompatibleBitmap(hScreenDC, mWidth, mHeight);


if(BitBlt(
    hMemoryDC,
    0,
    0,
    mWidth,
    mHeight,
    hScreenDC,
    mPx,
    mPy,
    SRCCOPY | CAPTUREBLT
    ) && GetDIBits(hScreenDC, hBitmap, 0, mHeight, mImageBuffer, &bi, DIB_RGB_COLORS))
    {
    return true;
    }

感谢您尝试帮助我!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接