SIGGRAPH '97

Course 24: OpenGL and Window System Integration

OpenGL and Win32



Overlays & Underlays

Overlays and underlays are often used in applications for rendering above (or below) the main OpenGL context. Setup and use of overlays and underlays is discussed below.

Overlays & Underlays

Example source code:
overlay.c



Overlays

Some pixel formats include an overlay or underlay plane. If overlay or underlay planes are desired, a pixel format with these must be selected. You cannot have free-floating overlay windows that can move over other windows. Overlay planes have a transparent color to allow things drawn 'beneath' them to show through. Every layer has a palette associated with it.

Unlike main plane pixel formats, overlay and underlay plane formats don't have an equivalent ChoosePixelFormat(), so a method similar to that described in the pixel format section must be employed to find an appropriate format.

The following code will setup the pixel format to use an overlay plane if available. Note that it looks very similar to the pixel format choosing code developed in the last section. Notable differences are the wglDescribeLayerPlane() function call in place of the DescribePixelFormat() call in the previous example.
code defining oglPixelFormat() function in overlay.c

/* oglPixelFormat()
 *  Sets the pixel format for the context
 */
int oglSetPixelFormatOverlay(HDC hDC, BYTE type, DWORD flags)
{
    int pf, maxpf;
    PIXELFORMATDESCRIPTOR pfd;
    LAYERPLANEDESCRIPTOR  lpd;		/* layer plane descriptor */
    int   nEntries = 2;			/* number of entries in palette */
    COLORREF crEntries[2] = {		/* entries in custom palette */
      0x00000000,			/* black (ref #0 = transparent) */
      0x00ff0000,			/* blue */
    };

    /* get the maximum number of pixel formats */
    maxpf = DescribePixelFormat(hDC, 0, 0, NULL);
    
    /* find an overlay layer descriptor */
    for(pf = 0; pf < maxpf; pf++) {
        DescribePixelFormat(hDC, pf, sizeof(PIXELFORMATDESCRIPTOR), &pfd);

	/* the bReserved field of the PIXELFORMATDESCRIPTOR contains the
	   number of overlay/underlay planes */
	if (pfd.bReserved > 0) {
	  /* aha! This format has overlays/underlays */
	  wglDescribeLayerPlane(hDC, pf, 1,
				sizeof(LAYERPLANEDESCRIPTOR), &lpd);
	  if (lpd.dwFlags & LPD_SUPPORT_OPENGL &&
	      lpd.dwFlags & flags)
	    {
	      goto found;
	    }
	}
    }
    /* couldn't find any overlay/underlay planes */
    MessageBox(NULL,
	       "Fatal Error:  Hardware does not support overlay planes.",
	       "Error", MB_OK);
    return 0;

found:
    /* now get the "normal" pixel format descriptor for the layer */
    DescribePixelFormat(hDC, pf, sizeof(PIXELFORMATDESCRIPTOR), &pfd);

    /* set the pixel format */
    if(SetPixelFormat(hDC, pf, &pfd) == FALSE) {
	MessageBox(NULL,
		   "SetPixelFormat() failed:  Cannot set format specified.",
		   "Error", MB_OK);
        return 0;
    }
    
    /* set up the layer palette */
    wglSetLayerPaletteEntries(hDC, 1, 0, nEntries, crEntries);

    /* realize the palette */
    wglRealizeLayerPalette(hDC, 1, TRUE);

    /* announce what we've got */
    printf("Number of overlays = %d\n", pfd.bReserved);
    printf("Color bits in the overlay = %d\n", lpd.cColorBits);

    return pf;
}    
Now simply create an overlay context in much the same way that you create a main plane context. The number passed in to the wglCreateLayerContext() function is the layer number.
code fragment from the main() function in overlay.c

/* main()
 *  Entry point
 */
int main(int argc, char** argv)
{
    HWND      hWnd;			/* window */
    MSG       msg;			/* message */

    /* create a window */
    hWnd = oglCreateWindow("OpenGL", 0, 0, 200, 200);
    if (hWnd == NULL)
      exit(1);

    /* get the device context */
    hDC = GetDC(hWnd);

    /* set the pixel format */
    if (oglSetPixelFormatOverlay(hDC, PFD_TYPE_RGBA, LPD_DOUBLEBUFFER) == 0)
      exit(1);
      
    /* get the device context */
    hDC = GetDC(hWnd);

    /* create an OpenGL overlay context */
    hOverlayRC = wglCreateLayerContext(hDC, 1);

    . . .
}
When rendering to the overlay, be sure to set it current. Also be sure to swap the correct plane if using double buffering. Note that you must also swap the main plane with wglSwapLayerBuffers(), NOT SwapBuffers() when using overlay or underlay planes. Pass in WGL_SWAP_MAIN_PLANE as the second argument to wglSwapLayerBuffers() to swap the main plane, and WGL_SWAP_OVERLAYi where i is the overlay number to swap an overlay buffer.
code fragment from the main() function in overlay.c

/* main()
 *  Entry point
 */
int main(int argc, char** argv)
{
    HWND      hWnd;			/* window */
    MSG       msg;			/* message */

    . . .

    /* create an OpenGL overlay context */
    hOverlayRC = wglCreateLayerContext(hDC, 1);

    /* create an OpenGL context */
    hRC = wglCreateContext(hDC);
    wglMakeCurrent(hDC, hRC);

    /* now we can start changing state & rendering */
    while(1) {
        /* first, check for (and process) messages in the queue */
	while(PeekMessage(&msg, hWnd, 0, 0, PM_NOREMOVE)) {
	    if(GetMessage(&msg, hWnd, 0, 0)) {
		TranslateMessage(&msg); /* translate virtual-key messages */
		DispatchMessage(&msg);	/* call the window proc */
	    } else {
		goto quit;
	    }
	}

	/* make current and draw a triangle */
	wglMakeCurrent(hDC, hRC);
	glClear(GL_COLOR_BUFFER_BIT);
	glRotatef(1.0, 0.0, 0.0, 1.0);
	glBegin(GL_TRIANGLES);
	glColor3f(1.0, 0.0, 0.0);
	glVertex2i( 0,  1);
	glColor3f(0.0, 1.0, 0.0);
	glVertex2i(-1, -1);
	glColor3f(0.0, 0.0, 1.0);
	glVertex2i( 1, -1);
	glEnd();
	glFlush();
	wglSwapLayerBuffers(hDC, WGL_SWAP_MAIN_PLANE);

	/* make current and draw a triangle */
	wglMakeCurrent(hDC, hOverlayRC);
	glClear(GL_COLOR_BUFFER_BIT);
	glRotatef(-1.0, 0.0, 0.0, 1.0);
	glBegin(GL_TRIANGLES);
	glIndexi(1);
	glVertex2i( 0,  1);
	glVertex2i(-1, -1);
	glVertex2i( 1, -1);
	glEnd();
	glFlush();
	wglSwapLayerBuffers(hDC, WGL_SWAP_OVERLAY1);
    }

quit:

    /* clean up */
    wglMakeCurrent(NULL, NULL);		/* make our context 'un-'current */
    ReleaseDC(hDC, hWnd);		/* release handle to DC */
    wglDeleteContext(hRC);		/* delete the rendering context */
    wglDeleteContext(hOverlayRC);	/* delete the overlay context */
    DestroyWindow(hWnd);		/* destroy the window */

    return TRUE;
}