MFC BitBlt和SetDIBits与SetBitmapBits有什么区别?(标题)

4

我有一个以BGRA字节数组形式存储的位图。这是我一直使用的绘制位图的代码:

CDC *dispDC = new CDC();
dispDC->CreateCompatibleDC(pDC);
CBitmap *dispBMP = new CBitmap();
dispBMP->CreateCompatibleBitmap(pDC, sourceImage->GetWidth(), sourceImage->GetHeight());
dispDC->SelectObject(this->dispBMP);

实际上,像素在translatedImage数组中的复制是通过以下方式进行的:
dispBMP->SetBitmapBits(sourceImage->GetArea() * 4, translatedImage);

然后在进一步处理后,我使用dispDC作为源CDC调用pDC->StretchBlt。当本地登录时,这个方法可以正常工作,因为显示器的设置也是32bpp。
一旦使用远程桌面登录,显示器就会变成16bpp,图像就会失真。问题出在SetBitmapBits上;即为了让它正常工作,我必须正确地填充translatedImage,使其成为我想要显示的16bpp版本。而不是自己动手做这件事,我查阅了文档并发现了SetDIBits,它似乎可以实现我的需求:
“SetDIBits函数使用指定DIB中找到的颜色数据来设置兼容位图(DDB)中的像素。”
在我的情况下,DIB是32bpp RGBA数组,而DDB是我使用CreateCompatibleBitmap创建的dispBMP
所以,我没有使用SetBitmapBits,而是采取了以下措施:
BITMAPINFO info;
ZeroMemory(&info, sizeof(BITMAPINFO));
info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
info.bmiHeader.biBitCount = 32;
info.bmiHeader.biPlanes = 1;
info.bmiHeader.biCompression = BI_RGB;
info.bmiHeader.biSizeImage = sourceImage->GetArea()*4;
info.bmiHeader.biWidth = sourceImage->GetWidth();
info.bmiHeader.biHeight = sourceImage->GetHeight();
info.bmiHeader.biClrUsed = 0;

int r = SetDIBits(pDC->GetSafeHdc(), (HBITMAP)dispBMP,
                  0, sourceImage->GetHeight(), translatedImage, 
                  &info, DIB_PAL_COLORS);

然而,r 总是零,自然地,我的窗口里什么也看不到,都是黑色的。这段代码有问题吗?


你是否检查了使用远程桌面登录时 SetDIBits 的输入?例如,在这种情况下,dispBMP!=NULL 是否为真? - Ilya
2个回答

5
根据 SetDIBits 的文档

当应用程序调用此函数时,hbmp 参数标识的位图不能被选择到设备环境中。

在您的示例代码中,您在创建后将其选择到设备上下文中,因此可能是 SetDIBits 失败的原因。

这显然不是原因。我现在在StretchBLT调用之前加入了SelectObject调用,并在两者之间加入了SetDIBits,但仍然是黑色的。SetDIBits返回零并且显然什么也没做,因为如果我保留SetBitmapBits调用和它,我会得到正确的图像(直到我进入远程桌面)。 - darda

4

Ross Ridge指出代码的顺序错误是正确的。然而,这并没有解决问题。

问题在于我传递的参数。我是C++和MFC的新手,经常忘记所有可以对类型进行自动转换的 "运算符"。

以前我有这个:

int r = SetDIBits(pDC->GetSafeHdc(), (HBITMAP)dispBMP,
              0, sourceImage->GetHeight(), translatedImage, 
              &info, DIB_PAL_COLORS);

正确的调用方式是:

int r = SetDIBits(*pDC, *dispBMP,
              0, sourceImage->GetHeight(), translatedImage, 
              &info, DIB_PAL_COLORS);

(注意我在前两个参数中传递了取消引用的指针。)其他所有内容都是正确的,包括对于没有调色板的位图的反直觉的DIB_PAL_COLORS标志。
显然在文档中错过了一些关键点后,我重新阅读了它,然后发现这个网页有示例代码,显示我只是错误地传递了参数。

你使用的 GetSafeHdc 没有问题,问题在于将指针转换为 CBitmap 的 dispBMP 而不是对象本身的 *dispBMP。我很好奇你为什么一开始就把它变成了一个指针而不是一个本地对象? - Mark Ransom
@MarkRansom:“dispBMP”是一个类成员变量,因为我在使用C语言时的经验中发现,在我处理的类型的应用程序中,分配内存通常会非常昂贵。因此,由于这是一个从头开始的新项目,我正在非常小心地重复使用大型内存对象,例如位图和位图字节数组。 - darda
那种策略在这种情况下并没有带来太多好处。执行 sizeof(dispBMP) 查看我的意思 - 它不包含位图,而是保存指向位图的 句柄,该句柄是单独分配的。 - Mark Ransom
@MarkRansom:没错。所以如果我失去了那个句柄,我需要每次创建一个新的位图。CreateCompatibleBitmap 不是分配内存的函数吗?虽然在我的片段中没有显示它,但在实际代码中,我只有在知道图像大小已更改或 dispBMPNULL 时才调用它。 - darda

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