Click here to Skip to main content
15,305,625 members
Articles / Desktop Programming / MFC
Posted 2 Jun 2000


36 bookmarked

Printing and Print Preview OpenGL with MFC

Rate me:
Please Sign up or sign in to vote.
4.87/5 (8 votes)
3 Jun 2000
Using DIB section to print OpenGL with good resolution.
  • Download demo project - 32 Kb
  • Sample Image - GLPrint.jpg


    Wrapping OpenGL with MFC can take the advantages of both APIs: fast rendering and an elegant GUI. However, due to the fact that many printer drivers can't work with the SetPixelFormat() function, it is not feasible to render an OpenGL scene to a printer directly. This article presents an approach to copy an OpenGL scene to a DIB section, and then to print it out.

    There are at least 3 available approaches for OpenGL printing.

    1. Capture the screen. This method is easy. You can use the Windows BitBlt() function or the OpenGL glReadPixel() command to make a screen shot and print it out. Although these two functions use different approaches, you get the same result. Since the resolution of a screen is much lower than a printer's, you can't get a picture with decent quality. Furthermore, if the window's client area is clipped by another window, say, a toolbar or a modeless dialog box, the clipping window will show in the result.

    2. Use an enhanced metafile device context. Currently it works only on Windows NT, therefore it is not portable to Windows 9x.

    3. Use off-screen rendering. This technique can print an image with high resolution. However, if the printer's resolution is very high and the page size is very large, this approach requires large memory to hold the image during printing. A workaround is to reduce the image's resolution when large memory is required. I will use this method in this article.

    OpenGL Off-Screen Rendering

    First, I call Windows function CreateDIBSection() to create a DIB section, select it into a memory DC, then create an OpenGL memory RC associated with the memory DC. After rendering the scene, call StretchDIBits() to copy the image to the printer DC. In this way, I get reasonably good print quality.

    To simplify the interface, the implementation is wrapped in CGLObj class, and only one interface function, OnPrint(), is needed for printing and print preview. Internally, the method wraps three virtual functions OnPrint1(), OnPrint2(), and RenderScene() to make the customization flexible.

    The printing-related code segment in the header file is as follows:

    class CGLObj
    // Printing
    	HDC         m_hOldDC;
    	HDC         m_hMemDC;
    	HGLRC       m_hOldRC;
    	HGLRC       m_hMemRC;
    	BITMAPINFO  m_bmi;
    	LPVOID      m_pBitmapBits;
    	HBITMAP     m_hDib;
    	HGDIOBJ     m_hOldDib;
    	CSize       m_szPage;	
    // Operations
    	virtual bool InitializeOpenGL(CView* pView);
    	virtual void SetFrustum();  // For both screen and printing RCs
    	virtual bool SetDCPixelFormat(HDC hDC, DWORD dwFlags);  // For both screen and printing RCs
    	virtual void SetOpenGLState();  // For both screen and printing RCs
    	virtual void CreateDisplayList(UINT nList);  // For both screen and printing RCs
    	virtual void RenderScene();  // For both screen and printing RCs
    	virtual void OnSize(int cx, int cy);
    	virtual void OnPrint(CDC* pDC, CPrintInfo* pInfo, CView* pView);  // Interface function
    	virtual void OnPrint1(CDC* pDC, CPrintInfo* pInfo, CView* pView);
    	virtual void OnPrint2(CDC* pDC);
    // Implementation
    	virtual ~CGLObj();

    Create DIB Section

    The size of the DIB section depends on the size of the display device. I call GetDeviceCaps() to retrieve the size of the display device for either printing or print preview. When print previewing, I use screen resolution; when printing, I use the adjusted printer's resolution. Ideally, I hope I can use the printer's full resolution if memory and speed is not a problem. However, for a printer with 720 DPI and using letter-sized paper, the memory of a DIB section is easily in excess of 100MB. That is why I reduce the printing resolution. I then use this size to create a DIB section. After I create the memory DC, I select the handle of the DIB section to the DC.

    Setup Memory RC Associate to Memory DC

    Setup a memory RC is similar to setup a screen RC. The only difference is the flag, which specifies properties of the pixel buffer. I set PFD_DRAW_TO_WINDOW and PFD_DOUBLEBUFFER for the screen RC, but I need PFD_DRAW_TO_BITMAP for the memory RC. Thus, I made a helper function SetDCPixelFormat() to reuse this part of code. The OpenGL state and frustum of the memory RC are also the same as the screen RC. I therefore made SetOpenGLState() and SetFrustum() functions work for both RCs. Due to the fact that the display list is not reusable across RCs, I have to call the CreateDisplayList() function to create it again with the memory RC. If you use a texture object, you need to create it again with the new RC here.

    The image drawn by bit pattern manipulation functions, such as glLineStipple(), and the bitmap font may be changed when you perform printing and print preview. You need to create them again based on the ratio of the screen and adjusted printer resolutions.

    Copy DIB Section to DC

    After rendering the scene to the DIB section, I copy the image in the DIB section to the destination, which is a DC for either print preview or printing. Before copying, I have to map the size of the DIB section to the size of the destination. The page can be either portrait or landscape; and the image can also be either portrait or landscape. So there are four mapping cases. The size of the printing image is stored in szImageOnPage. The StretchDIBits() function is used to copy and stretch the image in the DIB section to the DC for either print preview or printing.


    The CGLObj class is very flexible to use with View. You can use aggregation, acquaintance, multiple-inheritance, virtual-inheritance, or whatever you like. You can also uncouple the rendering implementation from printing, and make two classes. This may be more flexible.

    In the example, I use virtual-inheritance. I derive CGLView from CView and CGLObj, in which CGLObj is a virtual base class. The implementation is only one line in CGLView::OnPrint():

    void CGLView::OnPrint(CDC* pDC, CPrintInfo* pInfo) 
    	CGLObj::OnPrint(pDC, pInfo, this);

    For simplicity, I do not handle multiple views. You need to handle the RC for View activation if you need multiple views.


    This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

    A list of licenses authors might use can be found here


    About the Author

    Frank W. Wu
    Technical Lead
    United States United States
    No Biography provided

    Comments and Discussions

    Generalvirtual-inheritance compiles but errors at runtime Pin
    Michael B Pliam14-Feb-11 11:55
    MemberMichael B Pliam14-Feb-11 11:55 
    QuestionPreview Blank DP to LP? Pin
    Member 27055012-Feb-09 3:58
    MemberMember 27055012-Feb-09 3:58 
    QuestionglLineStipple Pin
    Andornot28-Oct-08 23:32
    MemberAndornot28-Oct-08 23:32 
    AnswerRe: glLineStipple Pin
    Frank W. Wu29-Oct-08 3:36
    MemberFrank W. Wu29-Oct-08 3:36 
    GeneralRe: glLineStipple Pin
    Andornot29-Oct-08 12:41
    MemberAndornot29-Oct-08 12:41 
    GeneralRe: glLineStipple Pin
    Frank W. Wu30-Oct-08 3:43
    MemberFrank W. Wu30-Oct-08 3:43 
    GeneralRe: glLineStipple Pin
    Andornot30-Oct-08 14:24
    MemberAndornot30-Oct-08 14:24 
    QuestionNon power of two texture issue Pin
    wenzis4-May-08 23:03
    Memberwenzis4-May-08 23:03 
    AnswerRe: Non power of two texture issue Pin
    Frank W. Wu5-May-08 6:08
    MemberFrank W. Wu5-May-08 6:08 
    AnswerRe: Non power of two texture issue Pin
    John-Lucas Brown10-Jun-08 5:14
    MemberJohn-Lucas Brown10-Jun-08 5:14 
    GeneralRe: Non power of two texture issue Pin
    wenzis10-Jun-08 12:28
    Memberwenzis10-Jun-08 12:28 
    GeneralRe-using the display lists Pin
    gavurt15-Feb-07 22:56
    Membergavurt15-Feb-07 22:56 
    GeneralRe: Re-using the display lists Pin
    Frank W. Wu16-Feb-07 3:48
    MemberFrank W. Wu16-Feb-07 3:48 
    GeneralRe: Re-using the display lists Pin
    pet77715-Jun-07 8:49
    Memberpet77715-Jun-07 8:49 
    GeneralRe: Re-using the display lists Pin
    Frank W. Wu15-Jun-07 8:59
    MemberFrank W. Wu15-Jun-07 8:59 
    GeneralRe: Re-using the display lists Pin
    John-Lucas Brown10-Jun-08 5:12
    MemberJohn-Lucas Brown10-Jun-08 5:12 
    QuestionSpeedup PrintPreview Controls generation of Pages Pin
    umaramiya10-Dec-06 23:58
    Memberumaramiya10-Dec-06 23:58 
    QuestionHow to add GDI drawing to existing DIB? Pin
    Ravikumar_1258-Nov-06 18:37
    MemberRavikumar_1258-Nov-06 18:37 
    AnswerDon't mix OpenGL and GDI Pin
    Frank W. Wu9-Nov-06 5:04
    MemberFrank W. Wu9-Nov-06 5:04 
    Questionhow to use auxWireSphere in this demon Pin
    wencongli25-Oct-06 20:35
    Memberwencongli25-Oct-06 20:35 
    AnswerRe: how to use auxWireSphere in this demon Pin
    Frank W. Wu27-Oct-06 11:49
    MemberFrank W. Wu27-Oct-06 11:49 
    GeneralBlack printout Pin
    Brian Breslin30-Sep-04 5:38
    sussBrian Breslin30-Sep-04 5:38 
    Generalprints all black page Pin
    Anonymous29-Jun-04 11:03
    MemberAnonymous29-Jun-04 11:03 
    QuestionHow to run 2 different(or same) OpenGL objects in one DialogBox? Pin
    werter13-May-04 21:17
    Memberwerter13-May-04 21:17 
    GeneralMulti-page preview Pin
    Oleg_S13-Apr-04 10:28
    MemberOleg_S13-Apr-04 10:28 

    General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

    Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.