C++ 无法使用GDI(MFC)实现DIB打印

C++ 无法使用GDI(MFC)实现DIB打印,c++,winapi,mfc,gdi,C++,Winapi,Mfc,Gdi,MFC文档/视图体系结构,GDI绘图/打印。我有一个DIB backbuffer需要显示和打印 经过漫长而痛苦的过程,我得出了这样的结论:我需要使用CreateDIBSection创建的DIB(而不是使用CreateCompatibleBitmap创建的DDB),并且我必须使用StretchDIBits(而不是StretchBlt)将其复制到打印机dc上 但我没办法让这东西工作 以下是我的工作: // Copy back buffer to screen/printer dc: pDC->

MFC文档/视图体系结构,GDI绘图/打印。我有一个DIB backbuffer需要显示和打印

经过漫长而痛苦的过程,我得出了这样的结论:我需要使用CreateDIBSection创建的DIB(而不是使用CreateCompatibleBitmap创建的DDB),并且我必须使用StretchDIBits(而不是StretchBlt)将其复制到打印机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);
在初始化例程中,我设置backbuffer,如下所示:

// 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
带下划线的变量是(私有)成员变量,声明如下:

// 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覆盖中所做的操作:

首先,我得到如下绘制的区域(简化代码):

然后我在backbuffer中计算相应的rect坐标,它通常(远)大于DC。这个计算的细节在这里无关紧要,我只是这么说

CRect rectClipBoxBackBuffer;
表示backbuffer的相应矩形(以backbuffer的像素坐标表示)

然后,我使用_pMemDc内存dc在backbuffer上绘图

最后是我遇到麻烦的部分:将我的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进入/退出DC。
DC只能包含DDB位图,如果您提供DIB,DC将对其进行转换。

Microsoft关于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);

注意:现在它可以用于屏幕绘制和打印预览,但无法用于实际打印,这是我在这里讨论的原始问题:

附加信息:
StretchDIBits
调用成功-它返回的数字等于DIB的总高度。根据文档,我希望该数字等于
rectClipBoxBackBuffer.Height()
,其中说明
StretchDIBits
返回“复制的扫描行数”。我不知道这是否有任何暗示…看着有点痛苦。您已经准备好升级到
#include
。不幸的是,Hans,GDI+对于我正在做的事情来说太慢了。只有当您错误地使用它时,它才会变慢,像素格式很重要。与错误地使用GDI相反,GDI也会很慢,但大多数情况下都会失败。