Simple Use of Memory Metafiles

Suppose your company's logo consists of a rectangle with lines drawn between the opposing corners and a blue circle in the center. You need to draw this logo often on the client area of your programs' windows and on the printer. Let's make that logo a metafile.

We'll begin by defining a few necessary variables:

static HANDLE hmf ;

HANDLE hBrush ;

HDC hdcMeta ;

During processing of the WM_CREATE message, you can create the metafile. You call CreateMetaFile to obtain a handle to a metafile device context:

hMetaDC = CreateMetaFile (NULL) ;

The NULL parameter indicates that this will be a ”memory“ metafile; that is, the metafile will be stored in memory rather than as a disk file.

You can now draw your logo on this metafile device context. You decide you want it to be 100 units high and 100 units wide:

Rectangle (hdcMeta, 0, 0, 100, 100) ;

MoveTo (hdcMeta, 0, 0) ;

LineTo (hdcMeta, 100, 100) ;

MoveTo (hdcMeta, 0, 100) ;

LineTo (hdcMeta, 100, 0) ;

hBrush = CreateSolidBrush (RGB (0, 0, 255)) ;

SelectObject (hdcMeta, hBrush) ;

Ellipse (hdcMeta, 20, 20, 80, 80) ;

When you're finished drawing, you close the metafile device context by calling CloseMetaFile, which returns a handle to the metafile:

hmf = CloseMetaFile (hdcMeta) ;

Now you can delete the brush you created:

DeleteObject (hBrush) ;

You're done creating the metafile. The hmf variable is defined as static, so it will remain in existence during other messages.

You drew the logo with a height and width of 100. Are these logical units or device units? At this point, they are neither: They are simply units. They will take on meaning only when you play the metafile. Let's do so. During processing of your WM_PAINT message, you may want to fill up the client area with 100 logos, and you don't care whether they get stretched out somewhat. We'll assume that you've obtained values of cxClient and cyClient, representing the width and height of the client area, and that you've defined some of the other variables used in this code.

You obtain a handle to the client-area device context and set a mapping mode of MM_ANISOTROPIC with 1000 logical units horizontally and vertically:

hdc = BeginPaint (hwnd, &ps) ;

SetMapMode (hdc, MM_ANISOTROPIC) ;

SetWindowExt (hdc, 1000, 1000) ;

SetViewportExt (hdc, cxClient, cyClient) ;

Now you can ”play the metafile“ 100 times by calling PlayMetaFile, each time changing the window origin to move the metafile to a new position:

for (x = 0 ; x < 10 ; x++)

for (y = 0 ; y < 10 ; y++)

{

SetWindowOrg (hdc, -100 * x, -100 * y) ;

PlayMetaFile (hdc, hmf) ;

}

In calling PlayMetaFile, you're in effect repeating all the calls that you made between CreateMetaFile and CloseMetaFile when you originally created the metafile during the WM_CREATE message. The results are shown in Figure 13-3.

When you're finished processing the WM_PAINT message, you can end it normally:

EndPaint (hwnd, &ps) ;

One task remains. When you create a metafile, the handle is really the property of the GDI module, and you must explicitly delete it with DeleteMetaFile before you terminate the program. You can do this during processing of the WM_DESTROY message:

case WM_DESTROY :

DeleteMetaFile (hmf) ;

PostQuitMessage (0) ;

return 0 ;