无法使用GDI(MFC)实现DIB打印 [英] Unable to implement DIB printing with GDI (MFC)
问题描述
MFC doc /视图体系结构,GDI绘图/打印。我有一个DIB后台缓冲区,我需要显示和打印。
经过漫长而痛苦的道路,我得出结论,我需要使用DIB创建与CreateDIBSection DDB使用CreateCompatibleBitmap创建),我必须使用StretchDIBits(而不是StretchBlt)将它转换到打印机直流。
但我无法让这个东西工作。
这是我所做的:
在我的初始化例程中,我设置了backbuffer,
//准备设备上下文:
CClientDC aDC
OnPrepareDC(& aDC);
//设置适当的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; //指向DIB的像素数据的指针
SIZE _sizeBackBuffer; // Backbuffer的大小,即DIB的大小
现在下面是我在OnDraw覆盖:
首先,我得到这样绘制的区域(简化代码):
code> CRect rectClipBoxPlayground;
if(pDC-> IsPrinting())
{
rectClipBoxPlayground = _printingParams.pPrintInfo-> m_rectDraw;
}
else
{
pDC-> GetClipBox(& rectClipBoxPlayground);
}
然后我计算我的backbuffer中的对应的rect坐标, )大于DC。此计算的细节在这里不相关,我只是说
CRect rectClipBoxBackBuffer;
表示backbuffer的相应rect(在backbuffer的像素坐标中)。
然后我绘制到我的后台缓冲区,使用_pMemDc内存dc。
最后是我遇到的麻烦的部分:目标dc(屏幕或打印机)。这是我做的:
//将缓冲区复制到屏幕/打印机dc:
pDC - > SetStretchBltMode(HALFTONE);
SetBrushOrgEx(pDC-> GetSafeHdc(),0,0,0);
//下面的StretchBlt工程的指定代码(!)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,以便它不与任何直流相关联。然后我使用StretchDIBits但事情只是不工作!输出乱七八糟,像我给不正确的坐标,区域绘制偏移从他们应该绘制的地方,有时完全黑色。所以我必须缺少一些东西(也许是非常微不足道的)。帮帮我!我尝试改变rectClipBoxBackBuffer.top和bitmapInfo.bmiHeader.biHeight的符号,结果改变,但没有什么工作原理。
我做错了什么?
解决方案关于StretchDIBits的Microsoft文档是完全错误的。我发现Y轴的方向必须改变。现在下面的代码工作:
//将缓冲区复制到屏幕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);
PS:现在它适用于屏幕绘图和打印预览,但实际打印失败,原始问题:如何在打印机上打印DIB backbuffer - GDI,MFC
MFC doc/view architecture, GDI drawing/printing. I have a DIB backbuffer I need to display and print.
After the long and painful road I came to the conclusion than I need to use DIB created with CreateDIBSection (rather than DDB created with CreateCompatibleBitmap), and I have to blit it onto printer dc with StretchDIBits (rather than StretchBlt).
But i'm not able to get this thing to work.
Here is what I do:
In my initialization routine, I setup the backbuffer, like this:
// 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);
Variables with underscores are (private) member variables, declared like this:
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
Now below is what I do in my OnDraw override:
Firstly I get the area to be drawn like this (simplified code):
CRect rectClipBoxPlayground;
if (pDC->IsPrinting())
{
rectClipBoxPlayground = _printingParams.pPrintInfo->m_rectDraw;
}
else
{
pDC->GetClipBox(&rectClipBoxPlayground);
}
Then I calculate the corresponding rect coordinates in my backbuffer, which is usually (much) larger than the DC. Details of this calculation are irrelevant here, I just say that
CRect rectClipBoxBackBuffer;
represents the corresponding rect of backbuffer (in pixel coordinates of backbuffer).
Then I draw onto my backbuffer, using the _pMemDc memory dc.
And finally comes the part where I have troubles: blitting my DIB onto target dc (screen or printer). Here is what I do:
// 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);
The problem is, the commented-out code of StretchBlt works flawlessly (except printing on some printers), but I can't use it because some printers have troubles with it. So I have to use StretchDIBits. Note that I firstly unselect the DIB from its memory dc temporarily, so that it is not associated with any dc. Then I use StretchDIBits but things just don't work! Output is messed up, like I give incorrect coordinates, areas are drawn off-set from where they should be drawn, and sometimes totally black. So I must be missing something (maybe something very trivial). Help! I tried changing signs of rectClipBoxBackBuffer.top and bitmapInfo.bmiHeader.biHeight, the results change, but nothing works like it should.
What am I doing wrong??
解决方案 Microsoft's documentation about StretchDIBits is totally wrong. I found out that direction of Y axis has to be changed. Now following code works:
// 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.: Now it works for screen drawing and print preview, but fails for actual printing, which was my original problem discussed here: How to print DIB backbuffer on printer - GDI, MFC
这篇关于无法使用GDI(MFC)实现DIB打印的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!