【发布时间】:2012-02-20 07:00:18
【问题描述】:
MFC 文档/视图架构,GDI 绘图/打印。我有一个 DIB 后备缓冲区需要显示和打印。
经过漫长而痛苦的道路后,我得出的结论是,我需要使用通过 CreateDIBSection 创建的 DIB(而不是使用 CreateCompatibleBitmap 创建的 DDB),并且我必须使用 StretchDIBits(而不是 StretchBlt)将其 blit 到打印机 dc。
但我无法让这个东西工作。
这是我的工作:
在我的初始化例程中,我设置了后台缓冲区,如下所示:
// Prepare device context:
CClientDC aDC(this);
OnPrepareDC(&aDC);
// Setup proper backbuffer:
_pMemDc = new CDC;
_pMemDc->CreateCompatibleDC(&aDC);
memset(&_bitmapInfo, 0, sizeof(BITMAPINFO));
_bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
_bitmapInfo.bmiHeader.biWidth = _sizeBackBuffer.cx;
_bitmapInfo.bmiHeader.biHeight = -_sizeBackBuffer.cy; // top-down
_bitmapInfo.bmiHeader.biPlanes = 1;
_bitmapInfo.bmiHeader.biBitCount = 24; // Maybe 32?
_bitmapInfo.bmiHeader.biCompression = BI_RGB;
HANDLE hMemBitmap = CreateDIBSection(aDC.GetSafeHdc(), &_bitmapInfo, DIB_RGB_COLORS, (void**)&_pBitmapRawBits, 0, 0);
_hOldSelBitmap = (HBITMAP)_pMemDc->SelectObject(hMemBitmap);
带下划线的变量是(私有)成员变量,声明如下:
CDC *_pMemDc; // Backbuffer memory dc
HBITMAP _hOldSelBitmap;
BITMAPINFO _bitmapInfo; // Backbuffer DIB (header-only)
unsigned char *_pBitmapRawBits; // Pointer to pixel data of DIB
SIZE _sizeBackBuffer; // Size of backbuffer, i.e. size of that DIB
下面是我在 OnDraw 覆盖中所做的:
首先我得到这样的区域(简化代码):
CRect rectClipBoxPlayground;
if (pDC->IsPrinting())
{
rectClipBoxPlayground = _printingParams.pPrintInfo->m_rectDraw;
}
else
{
pDC->GetClipBox(&rectClipBoxPlayground);
}
然后我在我的后备缓冲区中计算相应的矩形坐标,它通常(远)大于 DC。这个计算的细节在这里无关紧要,我只是说
CRect rectClipBoxBackBuffer;
表示backbuffer对应的rect(以backbuffer的像素坐标表示)。
然后我使用 _pMemDc 内存 dc 绘制我的后备缓冲区。
最后是我遇到麻烦的部分:将我的 DIB 传送到目标 dc(屏幕或打印机)上。这是我的工作:
// Copy back buffer to screen/printer dc:
pDC->SetStretchBltMode(HALFTONE);
SetBrushOrgEx(pDC->GetSafeHdc(), 0, 0, 0);
// BELOW COMMENTED CODE OF StretchBlt WORKS(!) INSTEAD OF StretchDIBits.
//
//BOOL bSuccess = pDC->StretchBlt(rectClipBoxPlayground.left, rectClipBoxPlayground.top,
// rectClipBoxPlayground.Width(), rectClipBoxPlayground.Height(),
// _pMemDc, rectClipBoxBackBuffer.left, rectClipBoxBackBuffer.top,
// rectClipBoxBackBuffer.Width(), rectClipBoxBackBuffer.Height(), SRCCOPY);
HBITMAP hMemBitmap = (HBITMAP)_pMemDc->SelectObject(_hOldSelBitmap);
DWORD dwLines = StretchDIBits(pDC->GetSafeHdc(),
rectClipBoxPlayground.left, rectClipBoxPlayground.top, rectClipBoxPlayground.Width(), rectClipBoxPlayground.Height(),
rectClipBoxBackBuffer.left, rectClipBoxBackBuffer.top, rectClipBoxBackBuffer.Width(), rectClipBoxBackBuffer.Height(),
_pBitmapRawBits, &_bitmapInfo, DIB_RGB_COLORS, SRCCOPY);
_pMemDc->SelectObject(hMemBitmap);
问题是,被注释掉的 StretchBlt 代码完美无缺(在某些打印机上打印除外),但我无法使用它,因为某些打印机有问题。所以我必须使用 StretchDIBits。请注意,我首先暂时从其内存 dc 中取消选择 DIB,以便它不与任何 dc 关联。然后我使用 StretchDIBits 但事情就是不工作!输出搞砸了,就像我给出的坐标不正确,绘制的区域偏离了它们应该绘制的位置,有时完全是黑色的。所以我一定遗漏了一些东西(也许是一些非常微不足道的东西)。帮助!我尝试更改 rectClipBoxBackBuffer.top 和 bitmapInfo.bmiHeader.biHeight 的符号,结果发生了变化,但没有任何效果。
我做错了什么??
【问题讨论】:
-
附加信息:
StretchDIBits调用成功 - 它返回等于 DIB 总高度的数字。根据文档,我希望该数字等于rectClipBoxBackBuffer.Height(),其中说StretchDIBits返回“扫描行数复制”。我不知道这是否有任何暗示...... -
看着有点痛苦。您已准备好升级到
#include <gdiplus.h> -
不幸的是 Hans,GDI+ 对我来说太慢了。
-
只有用错才慢,像素格式很重要。与使用 GDI 错误相反,它也会很慢,但通常只会摔倒。