【问题标题】:How to print DIB backbuffer on printer - GDI, MFC如何在打印机上打印 DIB 后缓冲 - GDI、MFC
【发布时间】:2012-02-19 15:16:37
【问题描述】:

我正在使用 MFC 的 doc/view 架构来实现打印。我使用双缓冲,我将所有内容都绘制到我的后备缓冲区上,即 DIB 位图。比我使用 StretchBlt 将该 DIB 复制到打印机 DC 上。

奇怪的是 - 打印预览运行良好!当我在虚拟 PDF 打印机上打印时,它运行良好!但是当我在实际打印机上打印时(我在两台不同的打印机上进行测试——结果相同)——它只会打印“垃圾”。 “垃圾”意味着有时它打印完全黑页,有时它重复打印前几页,即它打印错误的DIB部分,就像我把坐标弄乱了StretchBlt一样,但我没有弄乱任何东西,我检查了多次,再加上为什么打印预览工作完美呢?

我尝试了很多变化:

  1. 打印时使用与屏幕 DC 兼容的内存 DC。
  2. 使用与打印机 DC 兼容的内存 DC,并在其中选择我的 DIB。
  3. 使用与打印机 DC 兼容的内存 DC,并使用专用的 DIB,我将原始后缓冲 DIB 复制到该 DIB 上。 等

但是结果是一样的。下面是我创建 DIB 的代码。我认为DIB格式可能是问题,所以如果它有问题,请提出建议。我尝试了 24 位和 32 位作为 bmiHeader.biBitCount 的值。

// Setup proper backbuffer:

_CleanupBackBufferStuff();

_pMemDc = new CDC;
_pMemDc->CreateCompatibleDC(&aDC);

BITMAPINFO bmi;
memset(&bmi, 0, sizeof(BITMAPINFO));
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = _sizeBackBuffer.cx;
bmi.bmiHeader.biHeight = -_sizeBackBuffer.cy; // top-down
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 24; // Tried 32 as well
bmi.bmiHeader.biCompression = BI_RGB;

unsigned char *pBitmapRawBits = 0;

HANDLE hMemBitmap = CreateDIBSection(aDC.GetSafeHdc(), &bmi, DIB_RGB_COLORS, (void**)&pBitmapRawBits, 0, 0);

_hOldSelBitmap = (HBITMAP)_pMemDc->SelectObject(hMemBitmap);

这里还有 StretchBlt 的代码(这里没什么特别的):

pDC->SetStretchBltMode(HALFTONE);
SetBrushOrgEx(pDC->GetSafeHdc(), 0, 0, 0);

BOOL bSuccess = pDC->StretchBlt(rectClipBoxPlayground.left, rectClipBoxPlayground.top, rectClipBoxPlayground.Width(), rectClipBoxPlayground.Height(), 
        _pMemDc, rectClipBoxBackBuffer.left, rectClipBoxBackBuffer.top, rectClipBoxBackBuffer.Width(), rectClipBoxBackBuffer.Height(), SRCCOPY);

StretchBlt 返回真,(pDC->GetDeviceCaps(RASTERCAPS) & RC_STRETCHBLT) 也是真。

更新:在 Adrian 发表评论后,我将代码更改为使用 StretchDIBits。问题还是一样!以下是我目前正在使用的代码:

// Copy back buffer to screen dc:

pDC->SetStretchBltMode(HALFTONE);
SetBrushOrgEx(pDC->GetSafeHdc(), 0, 0, 0);

HBITMAP hMemBitmap = (HBITMAP)_pMemDc->SelectObject(_hOldSelBitmap);

DWORD dwLines = StretchDIBits(pDC->GetSafeHdc(), 
    rectClipBoxPlayground.left, rectClipBoxPlayground.top, rectClipBoxPlayground.Width(), rectClipBoxPlayground.Height(), 
    rectClipBoxBackBuffer.left, _sizeBackBuffer.cy - rectClipBoxBackBuffer.top - rectClipBoxBackBuffer.Height(), rectClipBoxBackBuffer.Width(), rectClipBoxBackBuffer.Height(), 
    _pBitmapRawBits, &_bitmapInfo, DIB_RGB_COLORS, SRCCOPY);

_pMemDc->SelectObject(hMemBitmap);

它仍然表现得像源坐标不正确。它要么打印前几页之一(无论我选择什么页面),要么打印几乎全黑的页面。打印预览运行良好,所以这让我认为我的坐标计算代码应该没有问题。它在预览中工作,它与虚拟(pdf)打印机一起工作,在实际打印机上打印时失败。什么鬼?....

【问题讨论】:

  • 你需要使用设备兼容的位图而不是我猜的 DIB。
  • 我无法使用 CreateCompatibleBitmap,因为它无法用于大型位图。

标签: visual-c++ mfc gdi


【解决方案1】:

确保您没有一次将 DIBSECTION 选择到多个 DC。这可能会导致各种不可预知的行为。

对于打印,如果您将bmipBitmapRawBits 放在手边,您可以完全绕过内存DC。确保 DIBSECTION 未选择到 任何 DC,然后调用 SetDIBitsToDeviceStretchDIBits 将图像传输到打印机 DC。

如果仍有问题,您可能需要检查打印机的功能。并非所有驱动程序都支持所有位图传输方法。我相信打印系统应该对您隐藏这些差异,但也许不是。在您的打印机 DC 上致电 GetDeviceCaps,并检查 RASTERCAPS 以获取 RC_BITBLT 和朋友。

【讨论】:

  • 感谢您的回复。至于 GetDeviceCaps 的东西,正如我已经说过的,(pDC->GetDeviceCaps(RASTERCAPS) & RC_STRETCHBLT) 是真的,所以应该没有问题。无论如何,我认为 StretchDIBits 会纠正这个问题,但我无法让它工作!我在这里发布了另一个问题:stackoverflow.com/questions/9030747/… 请提出您的想法!再次感谢。
  • 更新:最后我让 StretchDIBits 代码正常工作(微软对该 API 的错误文档浪费了我半天的时间!),但是......问题仍然存在! WTF?!......
  • 您是否对打印机 DC 使用了不寻常的映射模式?我所说的不寻常是指MM_TEXT 以外的任何东西。我也怀疑您更新的代码示例中 Ysrc 参数的计算。
  • 映射模式为MM_TEXT。 Ysrc 参数有助于让 StretchDIBits 在屏幕绘图上工作。现在它非常适合屏幕绘图和打印预览!它只是无法在物理打印机上打印页面,就像 StretchBlt 一样。它返回成功代码,但打印出一些奇怪的东西——要么是错误的页面(最常出现的第二页),要么是全黑页面。
猜你喜欢
  • 2012-02-20
  • 2012-03-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多