Printing OpenGL in MFC


Desktop-as-a-Service Designed for Any Cloud ? Nutanix Frame

I have seen a number of OpenGL view classes in various magazines. NONE of them addressed printing. Sigh.

So, suppose you have an OpenGL view class with member data storing the HGLRC and CDC (which are required for an OpenGL-enabled window):

class COpenGLView : public CView {
	CDC *m_pDC;
In your OnCreate override, you will create these:
int COpenGLView::OnCreate(LPCREATESTRUCT lpCreateStruct) {
	if (CView::OnCreate (lpCreateStruct) == -1) return (-1);
	m_pDC = new CClientDC (this);
	if (m_pDC == NULL) return (-1);
	//  Set the DC's pixel format...
	if (!SetDCPixelFormat (m_pDC)) return (-1);
	m_hRC = wglCreateContext (m_pDC->GetSafeHdc ());
	if (!wglMakeCurrent (m_pDC->GetSafeHdc (), m_hRC)) return (-1);
	glClearColor (1.0f, 1.0f, 1.0f, 1.0f);
	glClearDepth (1.0);
	return (0);

Notice that I call wglMakeCurrent () here. Some authors do it at the beginning of OnDraw (). That is probably because their code didn't work without it! Bear in mind that wglMakeCurrent () makes a specific window instance the current OpenGL window. In an MDI, with two documents open, you need to make sure that each view instance makes its own window current before drawing into it. Therefore, calling wglMakeCurrent () in OnCreate () is not enough -- otherwise after the second view is created, all subsequent OpenGL calls in the first view will get rendered in the second!

Many authors call wglMakeCurrent () at the beginning of the view's OnDraw() method -- resist the temptation! Here is (roughly) my COpenGLView::OnPrint():
void COpenGLView::OnPrint(CDC* pDC, CPrintInfo* pInfo) {
	//  Let's not worry for now about print preview...
	if (pInfo->m_bPreview) return;  //  or print a message to preview
		UINT CurPage = pInfo->m_nCurPage;
	GLsizei HRes = pDC->GetDeviceCaps(HORZRES);
	GLsizei VRes = pDC->GetDeviceCaps(VERTRES);
	GLsizei HPixelsPerInch = pDC->GetDeviceCaps(LOGPIXELSX);
	GLsizei VPixelsPerInch = pDC->GetDeviceCaps(LOGPIXELSY);
	pDC->SetMapMode (MM_TEXT);
	// m_LMargin, etc are margins, in inches...
	GLint l = (GLint) (m_LMargin*HPixelsPerInch);
	GLint r = (GLint) (m_RMargin*HPixelsPerInch);
	GLint t = (GLint) (m_TMargin*VPixelsPerInch);
	GLint b = (GLint) (m_BMargin*VPixelsPerInch);
	//  Image width and height...
	GLsizei w = HRes - l - r;
	GLsizei h = VRes - t - b;
	//  Probably don't need this...
	int SavedDC = pDC->SaveDC ();
	if (CurPage == 1) {
		//  Save the current OpenGL settings...
		HDC   hDCOld   = wglGetCurrentDC ();
		HGLRC hGLRCOld = wglGetCurrentContext ();
		//  Make the printer the current
		//  OpenGL rendering device...
		HGLRC hGLRCPrinter = wglCreateContext (pDC->GetSafeHdc ());
		BOOL bRet = wglMakeCurrent (pDC->GetSafeHdc (), hGLRCPrinter);
		ASSERT (bRet);
		glClearColor (1.0f, 1.0f, 1.0f, 1.0f);
		glClearDepth (1.0);
		//  This makes a square image...
		if (w < h) h = w;
		if (h < w) w = h;
		//  This centers the images...
		l = (HRes - w)/2;
		b = (VRes - h)/2;
		glViewport(l, b, w, h);
		OnDraw (pDC);
		//  Go back to the saved OpenGL settings...
		wglMakeCurrent (hDCOld, hGLRCOld);
	//  Probably don't need this...
	if (SavedDC != 0) pDC->RestoreDC (SavedDC);

Notice that OnPrint () calls OnDraw (). If in OnDraw () you were to call wglMakeCurrent (), you would no longer be printing...you would be redrawing the view! It should also be noted that setting and restoring the current OpenGL device can be done in OnBeginPrinting () and OnEndPrinting (); for simplicity, I have put all the code here.

So, now printing OpenGL is simple. Let's go back to drawing...

In order to make sure your drawing goes to the correct window, you need to call wglMakeCurrent from SOMEWHERE OTHER THAN OnDraw (). But where? The candidates are OnActivateView () and OnUpdate ().

Some of the time, calling it in an override of OnActivateView () might be enough. However, there is one scenario where this will not be enough: MDI apps when you have created a second CMDIChildWnd frame via the New Window item in the Window menu. This creates additional views into the same document. Now you can have the situation where the active view does something which causes the document to call UpdateAllViews (), which calls the inactive view's OnUpdate () method, which will draw into the wrong window!

Alternatively, you can override OnUpdate (), and call wglMakeCurrent () there. You might be tempted to do this:

void COpenGLView::OnUpdate (CView* pSender, LPARAM lHint, CObject* pHint) {
	wglMakeCurrent (m_pDC->GetSafeHdc (), m_hRC);
	CView::OnUpdate (pSender, lHint, pHint);

Unfortunately, this won't work all the time. CView::OnUpdate () simply invalidates the view's window, which generates a WM_PAINT message. This WM_PAINT is queued up, and handled at some later time...in particular, possibly after calling another view's OnUpdate () method (that is, after another window has been made the current OpenGL rendering device). There is only one conclusion: you must call OnDraw () directly from OnUpdate () (and don't call the base class CView::OnUpdate ()). There are, of course, performance issues, but I generally ignore them.

Therefore, I usually override both methods: OnActivateView () and OnUpdate (), and I call wglMakeCurrent () in both. However, in OnUpdate (), I save and restore the current settings at the beginning and end of the method (just like in OnPrint ()). This is not necessary in OnActivateView ().

void COpenGLView::OnActivateView(BOOL bActivate,
								 CView* pActivateView, CView* pDeactiveView) {
	CView::OnActivateView(bActivate, pActivateView, pDeactiveView);
	if (bActivate) {
		wglMakeCurrent (m_pDC->GetSafeHdc (), m_hRC);
void COpenGLView::OnUpdate (CView* pSender, LPARAM lHint, CObject* pHint) {
	//  Save the current OpenGL settings...
	HDC   hDCOld   = wglGetCurrentDC ();
	HGLRC hGLRCOld = wglGetCurrentContext ();
	//  Make this view the current OpenGL rendering context...
	BOOL bRet = wglMakeCurrent (m_pDC, m_hRC);
	//  Draw!!!
	OnDraw (m_pDC);
	//  Go back to the saved OpenGL settings...
	wglMakeCurrent (hDCOld, hGLRCOld);

The reason to return to the previous settings in OnUpdate () is fairly subtle. Suppose you didn't, and the last view to be redrawn was not the active view. Suppose you then handled a mouse event in the active view, and you called one of the OpenGL calls that translated the mouse point into an (x,y,z) coordinate. It would translate it based upon the current OpenGL settings (ie. viewport, rotations, scalings, etc). If the two views were not identical, you would end up with improper translations. Since I use the mouse to translate, scale, etc, this is very important! In any case, by setting the current context in OnActivate (), and restoring the previous settings at the end of OnUpdate (), the active view is the current OpenGL context when the next (mouse or other) event occurs.

Now for print preview: I dunno! I tried to do it, but it did not work. I basically could not get the CDC configured to handle OpenGL. I don't know WHAT I was doing wrong. If anyone can get it working correctly, please, PLEASE let me know. It bugs the snot out of me!

In summary, I have tried to not only show the main printing solution, but have also tried to explain in detail some of the subtle problems that show up. Hopefully, the discussion will give you a better understanding of the issues involved, so that if you run into other OpenGL problems, you will be better equipped to deal with them.

John Wagner
Institute of Theoretical Dynamics
UC Davis


  • How to print a view containing both openGL and GDI drawings?

    Posted by RaviStrs on 11/09/2006 09:42am

    Could you please spend some time to look at my query? Along with OpenGL drawing, my application view have some other drawings on screen (pDC->LineTo,Rectangle etc..)to show some legends on screen. On printing I need to add the GDI drawings also to the existing DIB. Could you please suggest me some guide line to do that? I have used GDI commands in OnDraw() function of view class. Thank you.

  • proplem

    Posted by Legacy on 10/16/2003 07:00am

    Originally posted by: nora

    i want to know how i can connect visualc++6 with opengl or how download opengl

  • Another approach

    Posted by Legacy on 07/11/2002 07:00am

    Originally posted by: Uwe Kotyczka

    Creating a HGLRC for a printer device context using
    wglCreateContext returns NULL in most cases. Somewhere
    I've read that the printer must support at least 4
    bitplanes to be supported by OpenGl.

    I decided to render to a DIB (device independend bitmap)
    and then to blit it to the printer. Works fine on all
    printers I have tried it.

    For source code have a look at my demo

    Uwe Kotyczka.

  • Direct print seems very very slow?

    Posted by Legacy on 04/25/2002 07:00am

    Originally posted by: Tony Yuan

    Did you have this feeling? A very simple picture will take me over 10 min.s.

  • WorldTransForm

    Posted by Legacy on 02/18/2001 08:00am

    Originally posted by: Yahua Li

    Who can help me to get the information about WorldTransForm
    in detail.

  • Using OpenGL to resize image files

    Posted by Legacy on 01/23/2000 08:00am

    Originally posted by: Charlie Hu

    How to transfer the image files(GIF/JEPG) into the ones with specified size(200pi*200pi) by using OpenGL?

  • About openGL hrc on print...

    Posted by Legacy on 09/14/1999 07:00am

    Originally posted by: Leo

    I try to printting openGL graphics. but I fail to create the context device of openGL. I got some infomation from a book . It tell me that openGL can not be print directly because of the memory limited. Metafile must be used as intermedia when printting. But the metafile can contain only the GDI commands, it make me more and more ambiguous !
    You can make me more clear!

  • MFC Programmer's SourceBook - OpenGL -PRINTING ON OPENGL IN MFC

    Posted by Legacy on 07/20/1999 07:00am

    Originally posted by: Anbumani

    Please Attach the Sample WorkSpace.
    That will very useful to understand.
    Thankx in Advance.

  • Where is the sample Workspace ?

    Posted by Legacy on 07/20/1999 07:00am

    Originally posted by: Lovebell

    Where I will get the Samlpe workspace.
    Please send it to me.
    Thanking you

  • Printing OpenGL in MFC

    Posted by Legacy on 10/08/1998 07:00am

    Originally posted by: michel duclos

    HI !
    I use WIN95 and Visual C++ 5.0
    In the OnPrint function , wglMakeCurrent return NULL in almost all the case but if I use DRAW_TO_BITMAP with SetPixelFormat the application print a white page.
    Is there a special initialization ( about SetPixelFormat ) ?
    I try all the possible configurations but i can't get a context device rendering on the printer.

  • You must have javascript enabled in order to post comments.

Leave a Comment
  • Your email address will not be published. All fields are required.

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date