Open GL Super Bible

Previous Table of Contents Next

GDI Device Contexts

To draw in a window without using OpenGL, you use the Windows GDI (Graphical Device Interface) functions. Each window has a device context that actually receives the graphics output, and each GDI function takes a device context as an argument to indicate which window you want the function to affect. You can have multiple device contexts, but only one for each window.

The example program WINRECT on the Companion CD draws an ordinary window with a blue background and a red square in the center. The output from this program, shown in Figure 4-1, will look familiar to you. This is the same image produced by our second OpenGL program in Chapter 3, friendly.c. Unlike that earlier example, however, the WINRECT program is done entirely with the Windows API. WINRECT’s code is pretty generic as far as Windows programming goes. There is a WinMain that gets things started and keeps the message pump going, and a WndProc to handle messages for the main window.

Figure 4-1  Windows version of friendly.c, the OpenGL sample from Chapter 3

Your familiarity with Windows programming should extend to the details of creating and displaying a window, so we’ll cover only the code from this example that is responsible for the drawing of the background and square.

First we must create a blue and a red brush for filling and painting. The handles for these brushes are declared globally.

// Handles to GDI brushes we will use for drawing
HBRUSH hBlueBrush,hRedBrush;

Then the brushes are created in the WinMain function, using the RGB macro to create solid red and blue brushes.

// Create a blue and red brush for drawing and filling
// operations.                // Red, green, blue
hBlueBrush = CreateSolidBrush(RGB(   0,     0,  255));
hRedBrush = CreateSolidBrush(RGB(  255,     0,    0));

When the window style is being specified, the background is set to use the blue brush in the window class structure.

wc.hbrBackground     = hBlueBrush; // Use blue brush for background

Window size and position (previously set with auxInitPosition) are set when the window is created.

// Create the main application window
hWnd = CreateWindow(
                       100, 100,           // Size and dimensions of window
                       250, 250,

Finally, the actual painting of the window interior is handled by the WM_PAINT message handler in the WndProc function.

               case WM_PAINT:
                       PAINTSTRUCT ps;
                       HBRUSH hOldBrush;

                       // Start painting

                       // Select and use the red brush
                       hOldBrush = SelectObject(ps.hdc,hRedBrush);

                       // Draw a rectangle filled with the currently
                       // selected brush

                       // Deselect the brush

                       // End painting

The call to BeginPaint prepares the window for painting, and sets the hdc member of the PAINTSTRUCT structure to the device context to be used for drawing in this window. This handle to the device context is used as the first parameter to all GDI functions, identifying which window they should operate on. This code then selects the red brush for painting operations and draws a filled rectangle at the coordinates (100,100,150,150). Then the brush is deselected, and EndPaint cleans up the painting operation for you.

Before you jump to the conclusion that OpenGL should work in a similar way, remember that the GDI is Windows-specific. Other environments do not have device contexts, window handles, and the like. OpenGL, on the other hand, was designed to be completely portable among environments and hardware platforms. Adding a device context parameter to the OpenGL functions would render your OpenGL code useless in any environment other than Windows.

OpenGL Rendering Contexts

In order to accomplish the portability of the core OpenGL functions, each environment must implement some means of specifying a current rendering window before executing any OpenGL commands. In Windows, the OpenGL environment is embodied in what is known as the rendering context. Just as a device context remembers settings about drawing modes and commands for the GDI, the rendering context remembers OpenGL settings and commands.

You may have more than one rendering context in your application—for instance, two windows that are using different drawing modes, perspectives, and so on. However, in order for OpenGL commands to know which window they are operating on, only one rendering context may be current at any one time per thread. When a rendering context is made current, it is also associated with a device context and thus with a particular window. Now OpenGL knows which window into which to render. Figure 4-2 illustrates this concept, as OpenGL commands are routed to the window indirectly associated with the current rendering context.

Figure 4-2  How OpenGL commands find their window

Performance Tip:  
The OpenGL library is thread-safe, meaning you can have multiple threads rendering their own windows or bitmaps simultaneously. This has obvious performance benefits for multiprocessor systems. Threads can also be beneficial on single-processor systems, as in having one thread render while another thread handles the user interface. You can also have multiple threads rendering objects within the same rendering context. In this chapter’s subdirectory on the CD, the supplementary example program GLTHREAD is an example of using threads with OpenGL.

Using the Wiggle Functions

The rendering context is not a strictly OpenGL concept, but rather an addition to the Windows API to support OpenGL. In fact, the new wiggle functions were added to the Win32 API specifically to add windowing support for OpenGL. The three most used functions with regard to the rendering context are

HGLRC wglCreateContext(HDC hDC);
BOOL wglDeleteContext(HGLRC hrc);
BOOL wglMakeCurrent(HDC hDC, HGLRC hrc);

Previous Table of Contents Next