【问题标题】:Unable to implement DIB printing with GDI (MFC)无法使用 GDI (MFC) 实现 DIB 打印
【发布时间】: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 错误相反,它也会很慢,但通常只会摔倒。

标签: c++ winapi mfc gdi


【解决方案1】:

如果您要使用 StretchDIBits,请不要选择 DIB 输入/输出 DC。 DC 只能包含 DDB 位图,如果你提供 DIB,DC 会转换它。

【讨论】:

    【解决方案2】:

    微软关于 StretchDIBits 的文档是完全错误的。我发现必须改变 Y 轴的方向。现在以下代码有效:

    // 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);
    

    P.S.:现在它适用于屏幕绘图和打印预览,但无法实际打印,这是我在这里讨论的原始问题:How to print DIB backbuffer on printer - GDI, MFC

    【讨论】:

      猜你喜欢
      • 2012-02-19
      • 1970-01-01
      • 1970-01-01
      • 2017-01-26
      • 2016-03-04
      • 2010-10-11
      • 2012-02-05
      • 2016-02-27
      • 2010-12-21
      相关资源
      最近更新 更多