0

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

asked Jan 27, 2012 at 8:40
4
  • ADDITIONAL INFO: StretchDIBits call succeeds - it returns the number which is equal to the total height of DIB. I would expect that number to be equal to rectClipBoxBackBuffer.Height() as per documentation, which says that StretchDIBits returns "number of scan lines copied". I don't know whether this can be any hint... Commented Jan 27, 2012 at 9:16
  • Kinda painful to watch. You are ready to move up to #include <gdiplus.h> Commented Jan 27, 2012 at 11:00
  • Unfortunately Hans, GDI+ is too slow for what I'm doing. Commented Jan 27, 2012 at 11:25
  • It is only slow when you use it wrong, the pixel format matters. As opposed to using GDI wrong which will be slow as well but most typically just falls over. Commented Jan 27, 2012 at 11:55

2 Answers 2

0

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.

answered Jan 28, 2012 at 17:24
Sign up to request clarification or add additional context in comments.

Comments

0

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

answered Jan 28, 2012 at 20:01

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.