CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 13 of 13
  1. #1
    Join Date
    Jan 2012
    Posts
    7

    Exclamation Unable to implement DIB printing with 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:

    PHP Code:
        // Prepare device context:

        
    CClientDC aDC(this);
        
    OnPrepareDC(&aDC);

        
    // Setup proper backbuffer:

        
    _pMemDc = new CDC;
        
    _pMemDc->CreateCompatibleDC(&aDC);

        
    memset(&_bitmapInfo0sizeof(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(), &_bitmapInfoDIB_RGB_COLORS, (void**)&_pBitmapRawBits00);

        
    _hOldSelBitmap = (HBITMAP)_pMemDc->SelectObject(hMemBitmap); 
    Variables with underscores are (private) member variables, declared like this:

    PHP Code:
        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):

    PHP 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:

    PHP Code:
        // Copy back buffer to screen/printer dc:

        
    pDC->SetStretchBltMode(HALFTONE);
        
    SetBrushOrgEx(pDC->GetSafeHdc(), 000);

        
    // 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.leftrectClipBoxPlayground.toprectClipBoxPlayground.Width(), rectClipBoxPlayground.Height(), 
            
    rectClipBoxBackBuffer.leftrectClipBoxBackBuffer.toprectClipBoxBackBuffer.Width(), rectClipBoxBackBuffer.Height(), 
            
    _pBitmapRawBits, &_bitmapInfoDIB_RGB_COLORSSRCCOPY);

        
    _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??
    Last edited by TX_; January 28th, 2012 at 02:15 PM. Reason: Formatting

  2. #2
    Join Date
    Apr 1999
    Posts
    27,449

    Re: Unable to implement DIB printing with GDI (MFC)

    1) Use code tags when posting code. The code you posted is practically unreadable, so I won't comment on it fully. Code tags allows code to appear formatted and readable. Example:
    Code:
    void foo()
    {
         int x = 1;
         int y = 2;
    }
    Note how that code is indented and formatted.

    2) You should be checking the return values from those MFC/API functions you're calling. If those functions are returning error codes to you, your program should be processing those errors. Instead, you assume that all of those functions return success and proceed as if nothing is wrong. This is especially the case if you are targeting your code to systems that you have no idea what printers they may be using.

    I once had to overhaul an entire code base because the code looked like yours -- no or very minimal error checking and assumptions that functions just "work". What wound up happening is crashes, black printout, and other weird behaviour, all because error return codes were not checked.
    Variables with underscores are (private) member variables, declared like this:
    3) You should not be starting your variable names with underscores. Names with underscores are reserved for the compiler implementation. There is a chance that name you started with underscores conflicts with a macro, function, constant, or other entity that the compiler has reserved for itself.

    4)
    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
    GDI functions are highly device dependent. What works with one device does not necessarily work with another. You can check device capabilities before calling any GDI function by calling GetDeviceCaps():

    http://msdn.microsoft.com/en-us/libr...=vs.85%29.aspx

    Then you can adjust your code, knowing exactly the capabilities of the device you're writing to.

    5) If this code is repeatedly called, then you are slowing down your program doing this:
    Code:
        _pMemDc = new CDC;
    If the device context is deleted at the end of that code, then there is no need to call the allocator to create one. Just create one on the stack and let the destructor do the work of clean up. Otherwise, your code looks like it has a resource/memory leak (and performs slower than it should due to calling the allocator/deallocator functions):
    Code:
    CDC pMemDc;
    //..
    What am I doing wrong??
    Start with suggestion number 2 -- check for error return codes. Also code that prints using GDI is prevalent in numerous places on the web. Have you tried some simple examples first to see if they work?

    Regards,

    Paul McKenzie
    Last edited by Paul McKenzie; January 27th, 2012 at 05:58 AM.

  3. #3
    Join Date
    Jan 2012
    Posts
    7

    Arrow Re: Unable to implement DIB printing with GDI (MFC)

    Thanks for the reply

    Now my answers:

    1) I did not find code tags when posting. Maybe I wasn't searching well enough. Anyway, now I'm unable to edit post, but next time I'll try to find the button that inserts code tags. Or is there only textual keyword available (which one?) for that?

    2) I usually do check return values, but for simplicity I ommited the relevant code fragments. What matters for the sake of this discussion is that none of the used APIs fail. They all return success values.

    3) I find underscores more convenient than "m_" prefix, and I always use qualified names for variables so that collision chance is near zero. Plus this convention is better compatible with my C# coding style. It is only the matter of taste, I think.

    4) (pDC->GetDeviceCaps(RASTERCAPS) & RC_STRETCHBLT) is true so with device capabilities everything's all right.

    5) No, that code is not called repeatedly. As I said: it belongs to "initialization routine". It has corresponding cleanup routine so there are no memory leaks. The reason I create object on heap is that in some circumstances I might want to delete it before parent object is destructed, so the logic is correct here. Anyway, this is not really relevant for this discussion.

  4. #4
    VictorN's Avatar
    VictorN is offline Super Moderator Power Poster
    Join Date
    Jan 2003
    Location
    Hanover Germany
    Posts
    20,396

    Re: Unable to implement DIB printing with GDI (MFC)

    Quote Originally Posted by TX_ View Post
    ...
    1) I did not find code tags when posting. Maybe I wasn't searching well enough.
    You should have started with reading the Before you post....

    Quote Originally Posted by TX_ View Post
    ...
    Anyway, now I'm unable to edit post, but next time I'll try to find the button that inserts code tags. Or is there only textual keyword available (which one?) for that?
    You will be able to edit your posts after about 5-th post.

    Quote Originally Posted by TX_ View Post
    3) I find underscores more convenient than "m_" prefix, and I always use qualified names for variables so that collision chance is near zero. Plus this convention is better compatible with my C# coding style. It is only the matter of taste, I think
    Please, reread what Paul wrote you about underscores and C++ compiler. Note that C# compiler is not the same as C++ compiler.
    Victor Nijegorodov

  5. #5
    Join Date
    Apr 1999
    Posts
    27,449

    Re: Unable to implement DIB printing with GDI (MFC)

    Quote Originally Posted by TX_;20523433)
    It is only the matter of taste, I think.
    Not in this case. C++ is not C#.

    C++ has rules as described by ANSI/ISO. One of those rules specifies that names, variables, macros, etc. that start with underscores are reserved for the compiler implementation. Otherwise if you start your names with underscores, you risk messing up the implementation in some way, either with weird compilation errors, or worse, your program compiles but the program doesn't behave correctly.

    Regards,

    Paul McKenzie
    Last edited by Paul McKenzie; January 27th, 2012 at 06:48 AM.

  6. #6
    Join Date
    Jan 2012
    Posts
    7

    Arrow Re: Unable to implement DIB printing with GDI (MFC)

    Quote Originally Posted by VictorN View Post
    You should have started with reading the Before you post....
    I was (and still am) in a hurry because I have to solve this problem ASAP. I registered just now to post this question, and I didn't have the time to read it. I promise I'll do it now.

    Quote Originally Posted by VictorN View Post
    You will be able to edit your posts after about 5-th post.
    Good

    Quote Originally Posted by VictorN View Post
    Please, reread what Paul wrote you about underscores and C++ compiler. Note that C# compiler is not the same as C++ compiler.
    I understand this perfectly clear sir. Maybe more clear than 95% of C++ developers, because I have kinda lowest level understanding of this area. But still I manage to use that convention without much problems, and the readability effect justifies the small risk for me. So, in hands of established developers, such things are really the matter of taste, I believe, rather than hard-coded axiom.

  7. #7
    Join Date
    Jan 2012
    Posts
    7

    Exclamation Re: Unable to implement DIB printing with GDI (MFC)

    Well, update:

    Microsoft's documentation about StretchDIBits is totally wrong. I found out that direction of Y axis has to be changed. Now following code works:

    PHP Code:
    // Copy back buffer to screen dc:

    pDC->SetStretchBltMode(HALFTONE);
    SetBrushOrgEx(pDC->GetSafeHdc(), 000);

    HBITMAP hMemBitmap = (HBITMAP)_pMemDc->SelectObject(_hOldSelBitmap);

    DWORD dwLines StretchDIBits(pDC->GetSafeHdc(), 
        
    rectClipBoxPlayground.leftrectClipBoxPlayground.toprectClipBoxPlayground.Width(), rectClipBoxPlayground.Height(), 
        
    rectClipBoxBackBuffer.left_sizeBackBuffer.cy rectClipBoxBackBuffer.top rectClipBoxBackBuffer.Height(), rectClipBoxBackBuffer.Width(), rectClipBoxBackBuffer.Height(), 
        
    _pBitmapRawBits, &_bitmapInfoDIB_RGB_COLORSSRCCOPY);

    _pMemDc->SelectObject(hMemBitmap); 
    BUT... It does not print on physical printers! Let me start from beginning: In the beginning I used StretchBlt, which worked well for screen drawing, print preview, and printing on virtual (pdf) printers. But it was not working on actual printers. Then I tried to change my code to use StretchDIBits but I was failing until now. As I'm posting this now, I found the solution for StretchDIBits so that it works well for screen drawing and print preview, but oddly, it still is not working for actual printing, just like StretchBlt!

    By "not working" I mean the following: it returns success code, but draws incorrect parts of the image buffer: sometimes it prints first few pages repeatedly (not matter what pages I select), sometimes it prints black pages. The strangest thing here is that print preview is working like it should! So if I had messed up coordinate calculation (like one would suspect seeing the results), print preview should be failing as well.

    More info:

    I have a large backbuffer, one big DIB, say 5000x3000. I divide this to pages, say 4 pages a row, 3 rows - 12 pages in total. Now when I print only 2nd page, it prints well. When I print 5th page only, it prints all black page. When I print, say, 6th page only, it prints 2nd page. And when I select to print whole range (1-12) it repeatedly prints first four pages - the pages that are in first row. I don't know if there is any coincidence, or there is any pattern. Again, anyone would suspect that I calculate page coordinates incorrectly, but how the hell is print preview always working fine!

    StretchBlt and now StretchDIBits both fail. Now I tried stretching to another memory bitmap, and then using BitBlt and it succeeded - all pages print like they should. But due to two blitting operations for each page, process is slow (this is noticeable on print preview). Plus additional buffer is required which increases memory consumption. I'd like to avoid this. But I can't get either of StretchBlt and StretchDIBits to print!

    Help!

  8. #8
    Join Date
    Jan 2012
    Posts
    7

    Smile Re: Unable to implement DIB printing with GDI (MFC)

    I know it's a big favor to ask, but it would be very much appreciated if someone could code for me small example, that creates DIB (with CreateDIBSection), selects it into memory DC, draws something (say single rectangle or circle) on it, and then blits that DIB onto printer page using StretchDIBits, so that it works on actual printers, and with big DIBs. I'd be grateful till death

  9. #9
    VictorN's Avatar
    VictorN is offline Super Moderator Power Poster
    Join Date
    Jan 2003
    Location
    Hanover Germany
    Posts
    20,396

    Re: Unable to implement DIB printing with GDI (MFC)

    Please, edit your posts replacing PHP tags with the Code ones. C++ is NOT a php.
    Victor Nijegorodov

  10. #10
    Join Date
    Jan 2012
    Posts
    7

    Smile Re: Unable to implement DIB printing with GDI (MFC)

    VictorN

    This is what I've read here: http://www.codeguru.com/forum/misc.php?do=bbcode#php

    "The [php] tag performs the same function as the [code] tag, but also adds syntax highlighting for PHP code. Although it is designed for PHP, it may correctly highlight some other C-like languages."

    So, I guess, I can use php tag for C++ too? I find it more pleasing to the eye (yeah yeah just like underscores vs "m_") Unless I violate any rules?..

  11. #11
    Join Date
    Jan 2012
    Posts
    7

    Smile Re: Unable to implement DIB printing with GDI (MFC)

    LOL in this thread I already learned two great news:

    Quote Originally Posted by Paul McKenzie View Post
    C++ is not C#.
    Quote Originally Posted by VictorN View Post
    C++ is NOT a php.
    But I still have to learn how to get StretchBlt / StretchDIBits working though...

  12. #12
    Join Date
    Apr 1999
    Posts
    27,449

    Re: Unable to implement DIB printing with GDI (MFC)

    So, I guess, I can use php tag for C++ too? I find it more pleasing to the eye
    We are the ones looking at your code. The least you can do is follow well-established protocol by just using code tags. There is no need to have every odd symbol in C++ lit up like a Christmas tree.

    As to your problem with big DIBs -- I remember solving this by implementing something called "banding" or "banded printing", however this was a long time ago, so I can't go into specific details. Banding meant you read one or more raster rows of image data at a time, and send those rows to the printer using StretchDIBits. Then you loop for all the rows. This also allowed you to write your printing routines in a way so that if you sent a large bitmap to the printer, the user can (via your program) stop the print process (in this case, each iteration of the for loop would check if your "stop printing button" or some other indicator was activated).

    Here is a link to a book article and source code. Yes the article is old, but this is how I remember doing this type of work (again, many years ago).

    http://graphicsbb.itgo.com/BitmapPrint.html

    Regards,

    Paul McKenzie
    Last edited by Paul McKenzie; January 29th, 2012 at 05:49 PM.

  13. #13
    Join Date
    Nov 2000
    Location
    Voronezh, Russia
    Posts
    6,620

    Re: Unable to implement DIB printing with GDI (MFC)

    Microsoft's documentation about StretchDIBits is totally wrong. I found out that direction of Y axis has to be changed.
    I'm just curious about the mapping mode you use in printer DC.
    Best regards,
    Igor

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured