This article has been
excerpted from book "Graphics Programming with GDI+".
Metafiles contain information about how an image was created - including lists
of graphics operations - rather than storing the image in pixel format. Graphics
operations in a metafile are stored as records, which can be controlled
(recorded and played back) individually.
The Metafile class provides functionality to work with different metafile
formats including Windows Metafile Format (WMF), Enhanced Metafile Format (EMF),
and an extension to Enhance Metafile Format (EMF+). The Metafile class provides
about 40 overloaded forms of its constructor.
Loading and viewing a metafile is similar to viewing a bitmap. An application
can load a metafile from a stream, string, or IntPtr instance with different
formats and locations. The simplest way to load and view a metafile is to pass
the file name in the Metafile constructor and call DrawImage.
GDI+ and Metafiles
Even though GDI+ is capable of reading both WMF and EMF files, it creates only
EMF files. EMF files that contain GDI+ records are called EMF+ files.
The Metafile class is derived from the Image class has no methods and properties
beside those inherited from the Image class.
Let's create an application to test metafile functionality. We will create a
Windows application and add a MainMenu control in this and subsequent sections.
As Listing 8.4 shows, first we create a Graphics object using
this.CreateGraphics. Then we create a Metafile object from a file and use
DrawImage to view it.
LISTING 8.4: Viewing a metafile
private void
ViewFile_Click(object sender, System.EventArgs
e)
{
// Create a Graphics object
Graphics g =
this.CreateGraphics();
g.Clear(this.BackColor);
// Create a Metafile
Metafile curMetafile = new
Metafile("mtfile.wmf");
// Draw metafile using DrawImage
g.DrawImage(curMetafile, 0, 0);
// Dispose of object
g.Dispose();
}
Figure 8.3 shows the output from Listing 8.4.
Metafile Class Method
As mentioned already, the Metafile class provides a long list of overloaded
constructor. It also provides three methods: GetHenhMetafile, GetMetafileHeader,
and PlayRecord.
GetHenhmetafile returns a window handle to a metafile. GetMetafileheader, which
has five overloaded forms, returns a metafile header in the form of a
MetafileHeader object. PlayRecord plays (reads and displays) an extended
metafile.
FIGURE 8.3: Viewing a metafile
Creating Metafiles Programmatically
The Metafile object can create a metafile programmatically. Three simple steps
are required to create a metafile:
- Creating a Metafile object with a file
name
- Using FromImage to create a Graphics
object from the Metafile object
- Adding graphics lines and shapes
Now let's create a metafile programmatically.
In Listing 8.5 we use GetHdc to get the handle to a device context (HDC), and we
use this handle to create a metafile called newFile.wmf. After creating the
metafile, we use the FillRectangle, FillEllipse, and DrawString methods to add a
rectangle, an ellipse, and a string, respectively. Calling these methods adds
records describing the respective objects to the metafile. Finally, we release
the objects.
LISTING 8.5: Creating a metafile
private void
CreateMetaFile_Click(object sender, System.EventArgs
e)
{
Metafile curMetafile = null;
// Create a Graphics object
Graphics g =
this.CreateGraphics();
// Get HDC
IntPtr hdc = g.GetHdc();
// Create a rectangle
Rectangle rect =
new Rectangle(0,
0, 200, 200);
// Use HDC to create a metafile with a
name
try
{
curMetafile = new Metafile("newFile.wmf",
hdc);
}
catch (Exception
exp)
{
MessageBox.Show(exp.Message);
g.ReleaseHdc(hdc);
g.Dispose();
return;
}
// Create a Graphics object from the
Metafile object
Graphics g1 =
Graphics.FromImage(curMetafile);
// Set smooting mode
g1.SmoothingMode = SmoothingMode.HighQuality;
// Fill a rectangle on the Metafile object
g1.FillRectangle(Brushes.Green,
rect);
rect.Y += 110;
// Draw an ellipse on the Metafile object
LinearGradientBrush lgBrush =
new LinearGradientBrush(
rect, Color.Red,
Color.Blue, 45.0f);
g1.FillEllipse(lgBrush, rect);
// Draw text on the Metafile object
rect.Y += 110;
g1.DrawString("MetaFile Sample",
new Font("Verdana",
20),
lgBrush, 200, 200,
StringFormat.GenericTypographic);
// Release objects
g.ReleaseHdc(hdc);
g1.Dispose();
g.Dispose();
}
Running the code in Listing 8.5 will create a new metafile in your application's
folder. Figure 8.4 shows the image described by the metafile.
As mentioned earlier, after creating a metafile, you can view it as you would
any other image, using the DrawImage method of the Graphics class.
Tip: Using the same approach, you can easily create a metafile editor
similar to GDI+Painter, in which you can draw graphics objects and save them as
metafiles. You can even change the GDI+Painter application code to do so.
FIGURE 8.4: A metafile created programmatically
Enhanced Metafiles
Using enhanced metafile, you can add personalized data to a metafile as defined
in the MSDN documentation:
The enhanced Windows metafile (EMF) format contains a comment mechanism for
embedding data within the metafile. This comment mechanism is used to embed GDI+
records within an EMF file. Application that cannot read or recognize the
comment data skip the comment records and render the records they do understand.
IF the EMF+ file is played back by GDI+, the GDI+ record are used to render the
metafile; otherwise, the GDI record (if present) are used.
There are three types of EMFs: EMF only, EMF+ dual, and EMF+ only. The EmfType
enumeration is used to find out the type of EMF programmatically. This
enumeration provides three members: Emfonly, EmfPlusDual, and EmfPlusOnly. The
EmfOnly and EmfPlusDual types of records can be played by both GDI and GDI+;
EmfPlusOnly types of records can be played only by GDI+.
You can use the Metafile object constructors to specify the type of EMF you want
to create. The following code creates an EMF+ dual metafile:
Metafile curMetafile = new Metafile(hdc,
EmfType.EmfPlusDual, "emfPlusDual.emf");
How Metafiles Work
The EnumerateMetafile method can be used to read and play back records of a
metafile one by one. Each record is sent to Graphics. EnumerateMetafileProc,
which is used to read the data for a record. This method has many overloaded
forms.
Graphics.EnumerateMetafileProc takes five parameters and is defined as follows:
public
delegate bool
Graphics.EnumerateMetafileProc(EmfPlusRecordType
recordType,int flags,int
dataSize,IntPtr data, PlayRecordCallback
callbackData);
GDI/GDI+ Record
Each metafile record describes a command that is capable of drawing, filling, or
changing the graphics state of a surface. For example, clearing a graphics
object, drawing a rectangle, filling an ellipse, creating a graphics container,
and ending a graphics container are all examples of records. After creating a
metafile programmatically, if you call DrawRectangle, one record will be added
to the metafile. When you play back the metafile, GDI+ reads the record (DrawRectangle)
and draws a rectangle.
The EmfPlusRecordType enumeration defines the available metafile record types.
Whereas recordType is of type EmfPlusRecordType enumeration and specifies the
type of metafile, the flags parameter is a set of flags that specify attributes
of the record. The dataSize parameter represents the number of bytes in the
record data, and data is an array of bytes that contains the record data. The
callbackData parameter is a PlayRecordCallback delegate supplied by the .NET
Framework to play a record of metafile data.
Listing 8.6 reads records from a metafile and displays data for these records
individually. In the EnumMetaCB callback, we check whether the record type is
FillEllipse, FillRects, DrawEllipse, or DrawRects and display the corresponding
data.
LISTING 8.6: Reading metafile records
private void
EnumerateMEtaFile_Click(object sender, System.EventArgs
e)
{
// Create a Graphics object
Graphics g =
this.CreateGraphics();
g.Clear(this.BackColor);
// Create a Metafile object from a file
Metafile curMetafile = new
Metafile("metafile.wmf");
// Set EnumerateMetafileProc property
Graphics.EnumerateMetafileProc
enumMetaCB =
new
Graphics.EnumerateMetafileProc(EnumMetaCB);
// Enumerate metafile
g.EnumerateMetafile(curMetafile,
new
Point(0, 0), enumMetaCB);
// Dispose of objects
curMetafile.Dispose();
g.Dispose();
}
private bool
EnumMetaCB(EmfPlusRecordType recordType, int
flags, int dataSize,
IntPtr data, PlayRecordCallback callbackData)
{ string str =
" ";
// Play only EmfPlusRecordType.FillEllipse
records
if (recordType ==
EmfPlusRecordType.FillEllipse
|| recordType == EmfPlusRecordType.FillRects
|| recordType == EmfPlusRecordType.DrawEllipse
|| recordType == EmfPlusRecordType.DrawRects)
{
str = "Record type:" +
recordType.ToString() +
", Flags:" + flags.ToString()
+
", Data :" + data.ToString();
MessageBox.Show(str);
}
return true;
}
Figure 8.5 shows the output from Listing 8.6. Our program displays the record
type, flag, data size, and data. The record in this example contains only
FillRectangle methods. If more records are used to create a metafile, you will
see message for the various records types.
Reading a Metafile header
A metafile header contains attributes such as type, size, and version of a
metafile. It is represented by the MetafileHeader class. GetMetafileHeader
returns a metafile header and has many overloaded methods.
The MetafileHeader class has the eight methods listed in Table 8.4.
FIGURE 8.5: Reading metafile records
TABLE 8.4: MetafileHeader methods
Method |
Description |
IsDisplay |
Returns true if a
metafile is device-dependent. |
IsEmf |
Returns true if a
metafile is in the Window EMF format. |
IsEmfOrEmfPlus |
Returns true if a
metafile is in the Windows EMF or EMF+ format. |
IsEmfPlus |
Returns true if a
metafile is in the Windows EMF+ format. |
IsEmfPlusDual |
Returns true if a
metafile is in the dual EMF format, which support both the enhanced and
the enhanced plus format. |
IfEmfPlusOnly |
Returns true if a
metafile supports only the Windows EMF+ format. |
IsWmf |
Return true of a
metafile is in the Windows WMF format. |
IsWmfPlaceable |
Returns true if a
metafile is in the Windows placeable WMF format. |
Properties of the MetafileHeader class represent various attributes of metafile,
including size, version, and type, as Table 8.5 shows. All of these properties
are read-only.
Reading metafile attributes is simple: Create a Metafile object, get its header
attributes using GetMetafileHeader, and display the value of these attributes in
a message box. Listing 8.7 reads metafile header attributes, including type,
bound, size, and version.
LISTING 8.7: Reading metafile header attributes
private void
MetafileHeaderInfo_Clcik(object sender, System.EventArgs
e)
{
// Create a Metafile object
Metafile
curMetafile = new Metafile("mtfile.wmf");
// Get metafile header
MetafileHeader
header = curMetafile.GetMetafileHeader();
}
TABLE 8.5: MetafileHeader properties
Property |
Description |
Bounds |
Gets the bounds of
a metafile in the form of a rectangle. |
DpiX |
Gets the
horizontal resolution, in dots per inch, of a metafile in the form of a
rectangle. |
DipY |
Gets the vertical
resolution, in dots per inch, of a metafile in the form of a rectangle. |
EmfPlusHeaderSize |
Gets the size, in
bytes, of an enhanced metafile plus header file. |
LogicalDpiX |
Gets the logical
horizontal resolution, in dots per inch, of a metafile |
LogicalDpiY |
Gets the logical
vertical resolution, in dots per inch, of a metafile |
MetafileSize |
Gets the size, in
bytes, of a metafile, |
Type |
Gets the type of a
metafile. |
Version |
Gets the version
number of a metafile. |
WmfHeader |
Gets the WMF
header of a metafile |
// Read
metafile header attributes
string
mfAttributes = " ";
mfAttributes +=
"Type :" + header.Type.ToString();
mfAttributes +=
"Bounds :" + header.Bounds.ToString();
mfAttributes +=
"Size :" + header.MEtafileSize.ToString();
mfAttributes +=
"Version :" + header.Version.ToString();
// Display
message box
MessageBox.Show
(mfAttributes);
// dispose
of object
curMetafile.Dispose();
Figure 8.6 shows the output from Listing 8.7.
FIGURE 8.6: Reading metafile header attributes
Conclusion
Hope the article would have helped you in understanding
working with Metafiles in GDI+. Read other articles on GDI+ on the website.
|
This book teaches
.NET developers how to work with GDI+ as they develop applications
that include graphics, or that interact with monitors or printers.
It begins by explaining the difference between GDI and GDI+, and
covering the basic concepts of graphics programming in Windows. |