Metafiles


A metafile is a graphics object that stores graphics commands. Functions that draw to a device context or set attributes of a device context have a corresponding metafile record.

Creating and Using Metafiles

To create a metafile, a metafile device context may be opened via the function create_metafile. win+ deals only with enhanced metafiles. A simple example of using metafiles has been included and its output is shown below.

A portion of the program is shown below.

    case message::create:
    {
        window_data* data = new window_data();
        set_window_pointer(window_handle, 0, (void*)data);

        handle device_context = create_metafile<character>();

        draw_rectangle(device_context, 100, 100, 200, 200);

        move_to(device_context, 100, 100);
        draw_line_to(device_context, 200, 200);

        move_to(device_context, 200, 100);
        draw_line_to(device_context, 100, 200);

        data->metafile = close_metafile(device_context);
    }
    break;

The function create_metafile is used to create a metafile device context. When drawing is directed to such a device context, the graphics is stored as metafile records (rather than being rendered to a particular device). The handle of the metafile is returned upon closing a metafile device context. Once the metafile device context has been created, several line drawing functions are issued to the metafile device context. These are contained as graphics orders in the metafile.

After the metafile has been created, the processing of the paint message draws the metafile in the client window. The code fragment that does this is shown below.

    case message::paint:
    {
        window_data* data = (window_data*)get_window_pointer(window_handle, 0);

        paint paint_structure;
        handle device_context = begin_paint(window_handle, &paint_structure);

        irectangle rectangle_client;
        get_client_rectangle(window_handle, &rectangle_client);

        rectangle_client[0](0) = rectangle_client[1](0) / 4;
        rectangle_client[1](0) = 3 * rectangle_client[1](0) / 4;
        rectangle_client[0](1) = rectangle_client[1](1) / 4;
        rectangle_client[1](1) = 3 * rectangle_client[1](1) / 4;

        play_metafile(device_context, data->metafile, &rectangle_client);

        end_paint(window_handle, &paint_structure);
    }
    break;

Firstly, the client rectangle is queried. The variable client_rectangle is initialized as shown above. The function play_metafile is used to play the metafile into the paint device context, resulting in the picture displayed above.

It should be noted that the coordinates used to draw the rectangle and lines are relative coordinates. If all coordinates were doubled or if a vector was added to or subtracted from them all, the same diagram would have resulted. All that is significant is that they define a relative spatial relationship amongst themselves. The image that results is stretched to fit the rectangle into which the metafile is played. Below is shown another snapshot where several different instances of the same program are running.

It should be clear that the original picture was square; however, when displayed, the aspect ratio was not preserved.

Saving a metafile to Disk

The program of this section is similar to that of the last in that it creates a metafile; however, it saves the metafile to disk and later reloads it when painting the client window. See the source code listing file.

When creating the metafile, several additional non-null parameters, including a description are passed to the function create_metafile - which creates a metafile device context. The file name and description of the metafile are declared as shown below.

character metafile_name[]  = "Metafile2.emf";
character metafile_title[] = "Metafile2\0metafile Demonstration #2\0";

Note that the title (description) of the metafile has an imbedded null and is terminated with a double null. The description is stored in the metafile header. The file name of the disk form of the metafile is also provided with an extension '.emf'. The statement that creates the metafile device context is shown below.

handle device_context = create_metafile(null,
                                        metafile_name,
                                        (const irectangle*)null,
                                        metafile_title);

A null bounding rectangle is specified, which causes the graphics system to calculate the bounding rectangle. Unlike the previous example, the in-memory version of the metafile is deleted right after it is created. Later, in the processing of the paint message, the metafile is reloaded from disk, as shown below.

case message::paint:
 {
  paint paint_struct;
  handle device_context = begin_paint(window,&paint_struct);

  rectangle client_rectangle;
  get_client_rectangle(window,&client_rectangle);

  client_rectangle[0](0) =     client_rectangle[1](0) / 4;
  client_rectangle[1](0) = 3 * client_rectangle[1](0) / 4;
  client_rectangle[0](1) =     client_rectangle[1](1) / 4;
  client_rectangle[1](1) = 3 * client_rectangle[1](1) / 4;

  handle metafile_handle = get_metafile(Metafilename);

  play_metafile(device_context,metafile_handle,&client_rectangle);

  delete_metafile(metafile_handle);

  end_paint(window,&paint_struct);
 }
 break;

The output of the program is identical to the output of the previous program. Note that loading a metafile each time the window is painted is not efficient. The actual size that the metafile is intended to be played is stored in the header and may be ascertained via the function get_metafile_header.