Unable to implement DIB printing with GDI (MFC)

瘦欲@ 提交于 2019-12-12 02:48:46

问题


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??


回答1:


Dont't select the DIB into/out DC if you are going to use StretchDIBits. DC can only contain DDB bitmap, if you supply a DIB, DC will convert it.




回答2:


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



来源:https://stackoverflow.com/questions/9030747/unable-to-implement-dib-printing-with-gdi-mfc

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!