A metafile is a collection of structures that store a picture in a device-independent format. Device independence is the one feature that sets metafiles apart from bitmaps. Unlike a bitmap, a metafile guarantees device independence. There is a drawback to metafiles however, they are generally drawn more slowly than bitmaps.
Internally, a metafile is an array of variable-length structures called metafile records. The first records in the metafile specify general information such as the resolution of the device on which the picture was created, the dimensions of the picture, and so on. The remaining records, which constitute the bulk of any metafile, correspond to the graphics device interface (GDI) functions required to draw the picture. These records are stored in the metafile after a special metafile device context is created. This metafile device context is then used for all drawing operations required to create the picture. When the system processes a GDI function associated with a metafile DC, it converts the function into the appropriate data and stores this data in a record appended to the metafile.
A metafile is played when its records are converted to device commands and processed by the appropriate device.
There are two types of metafiles:
- Windows-format metafiles
- Enhanced-format metafiles
Windows-format metafiles
Following the standard header in all WMF metafiles is a series of data records. This record is defined by the METARECORD data type. It has the following format: |
Size is the total size of the records in 16-bit WORDs, including the Size field itself. The minimum possible size for a record is 3.
Function is the GDI number of the function called to playback this record. The low-byte of this value identifies the specific GDI function to call. The high-byte is the number of WORDs passed to this function, and is also the number of elements in the Parameters array. For example, a value of 0x0213 indicates the LineTo() function (0x13) and that this function is passed two WORD values.
Parameters is an array of the parameters used by the GDI function specified by this record. The parameters are stored in the reverse order in which they are passed to the function. For example, the two parameter values of the LineTo record are passed to the LineTo() function in the order of X and Y, but store in the record as Y and X.
Although each record parameter is stored as a WORD, the exact data type of the parameter is determined by the function it is passed to. Parameter values that change, such as device context handles, are never stored in metafile records. The last record in every metafile always has a function number of 0000h
Enhanced Metafile Records
Enhanced metafiles also have an enhanced metafile record structure. When compared to standard metafile records you will see that all three fields are now 32-bit DWORDs and the positions of the Size and Function fields are exchanged: |
Function is the GDI number of the function called to playback this record. In the Win32 SDK these values identified by the EMR_*
Step-By-Step Example
- Create a standard exe project - Add one module to the project - Add one command button, one picturebox and one listbox control on the form1 - Add the following code in form1
Form1.frm |
Click here to copy the following block | Dim picRECT As RECT Dim hMF As Long, ret As Long, hEMFPal As Long
Public Sub ExtractMetaRecords(EMFHandle As Long, EMFObj As PictureBox) If OpenedFileType = EMF Then Call EnumEnhMetaFile(ByVal EMFObj.hdc, ByVal EMFHandle, AddressOf EnhMetaFileProc, 0, picRECT) Else Call EnumMetaFile(ByVal EMFObj.hdc, ByVal EMFHandle, AddressOf WinMetaFileProc, 0) End If End Sub
Public Sub Command1_Click()
List1.Clear
hMF = GetEnhMetaFile(Text1) If hMF = 0 Then hMF = GetMetaFile(Text1) If hMF = 0 Then MsgBox "Can not open file, make sure that its a valid format (Only WMF/EMF Supported in this demo)", vbCritical Exit Sub Else OpenedFileType = WMF End If Else OpenedFileType = EMF End If
ret = GetClientRect(Picture1.hwnd, picRECT)
If OpenedFileType = EMF Then Call EMFDemo Else Call WMFDemo End If End Sub
Sub EMFDemo() ret = PlayEnhMetaFile(Picture1.hdc, hMF, picRECT)
If hEMFPal <> 0 Then DeleteObject hEMFPal
hEMFPal = GetPalleteFromEMF(hMF) If hEMFPal <> 0 Then Call SetPaletteForDC(Picture1.hdc, hEMFPal, Picture1.hwnd)
MsgBox "This is original file"
Picture1.Cls ExtractMetaRecords hMF, Picture1 End Sub
Sub WMFDemo() ret = PlayMetaFile(Picture1.hdc, hMF) MsgBox "This is original file"
Picture1.Cls ExtractMetaRecords hMF, Picture1 End Sub
Function GetPalleteFromEMF(hEMF As Long) As Long Dim emh As ENHMETAHEADER, ret As Long, hPal As Long Dim LogPal As LOGPALETTE
ret = GetEnhMetaFileHeader(hMF, Len(emh), emh) If emh.nPalEntries = 0 Then hPal = 0 Else LogPal.palNumEntries = GetEnhMetaFilePaletteEntries(hMF, 1000, LogPal.palPalEntry(0)) LogPal.palVersion = &H300 hPal = CreatePalette(LogPal) End If GetPalleteFromEMF = hPal End Function
Function SetPaletteForDC(hDcToSet As Long, hPal As Long, hWndToRepaint) As Boolean hOldPal = SelectPalette(hdc, hEnhMetaPal, False) If RealizePalette(hDcToSet) Then Call InvalidateRect(hWndToRepaint, ByVal 0&, True) End If
Call SelectPalette(hDcToSet, hOldPal, True) RealizePalette (hDcToSet) End Function
Public Sub Form_Load() Text1.Text = App.Path & "\emf_demo.emf"
Command1.Caption = "Demo"
arrRecords = Split(strRecords, ",") End Sub
Private Sub Form_Unload(Cancel As Integer) If OpenedFileType = EMF Then ret = DeleteEnhMetaFile(hMF) Else ret = DeleteMetaFile(hMF) End If
If hEMFPal <> 0 Then DeleteObject hEMFPal End Sub |
- Add the following code in module1
Module1.bas |
Click here to copy the following block | Global Const strRecords = "M_HEADER, M_POLYBEZIER, M_POLYGON, M_POLYLINE, M_POLYBEZIERTO, M_POLYLINETO, " & _ "M_POLYPOLYLINE, M_POLYPOLYGON, M_SETWINDOWEXTEX, M_SETWINDOWORGEX, M_SETVIEWPORTEXTEX, " & _ "M_SETVIEWPORTORGEX, M_SETBRUSHORGEX, M_EOF, M_SETPIXELV, M_SETMAPPERFLAGS, M_SETMAPMODE, " & _ "M_SETBKMODE, M_SETPOLYFILLMODE, M_SETROP2, M_SETSTRETCHBLTMODE, M_SETTEXTALIGN, " & _ "M_SETCOLORADJUSTMENT, M_SETTEXTCOLOR, M_SETBKCOLOR, M_OFFSETCLIPRGN, M_MOVETOEX, M_SETMETARGN, " & _ "M_EXCLUDECLIPRECT, M_INTERSECTCLIPRECT, M_SCALEVIEWPORTEXTEX, M_SCALEWINDOWEXTEX, M_SAVEDC, " & _ "M_RESTOREDC, M_SETWORLDTRANSFORM, M_MODIFYWORLDTRANSFORM, M_SELECTOBJECT, M_CREATEPEN, " & _ "M_CREATEBRUSHINDIRECT, M_DELETEOBJECT, M_ANGLEARC, M_ELLIPSE, M_RECTANGLE, M_ROUNDRECT, " & _ "M_ARC, M_CHORD, M_PIE, M_SELECTPALETTE, M_CREATEPALETTE, M_SETPALETTEENTRIES, M_RESIZEPALETTE, " & _ "M_REALIZEPALETTE, M_EXTFLOODFILL, M_LINETO, M_ARCTO, M_POLYDRAW, M_SETARCDIRECTION, M_SETMITERLIMIT, " & _ "M_BEGINPATH, M_ENDPATH, M_CLOSEFIGURE, M_FILLPATH, M_STROKEANDFILLPATH, M_STROKEPATH, M_FLATTENPATH, " & _ "M_WIDENPATH, M_SELECTCLIPPATH, M_ABORTPATH, EMR_RESERVED69, M_GDICOMMENT, M_FILLRGN, M_FRAMERGN, M_INVERTRGN, " & _ "M_PAINTRGN, M_EXTSELECTCLIPRGN, M_BITBLT, M_STRETCHBLT, M_MASKBLT, M_PLGBLT, M_SETDIBITSTODEVICE, " & _ "M_STRETCHDIBITS, M_EXTCREATEFONTINDIRECTW, M_EXTTEXTOUTA, M_EXTTEXTOUTW, M_POLYBEZIER16, M_POLYGON16, " & _ "M_POLYLINE16, M_POLYBEZIERTO16, M_POLYLINETO16, M_POLYPOLYLINE16, M_POLYPOLYGON16, M_POLYDRAW16, " & _ "M_CREATEMONOBRUSH, M_CREATEDIBPATTERNBRUSHPT, M_EXTCREATEPEN, M_POLYTEXTOUTA, M_POLYTEXTOUTW"
Enum enumWMFRecord WMR_ABORTDOC = &H52 WMR_ARC = &H817 WMR_CHORD = &H830 WMR_CREATEPENINDIRECT = &H2FA WMR_CREATEBRUSHINDIRECT = &H2FC WMR_DELETEOBJECT = &H1F0 WMR_ELLIPSE = &H418 WMR_ENDDOC = &H5E WMR_ENDPAGE = &H50 WMR_EXCLUDECLIPRECT = &H415 WMR_EXTFLOODFILL = &H548 WMR_FILLREGION = &H228 WMR_FLOODFILL = &H419 WMR_FRAMEREGION = &H429 WMR_INTERSECTCLIPRECT = &H416 WMR_INVERTREGION = &H12A WMR_LINETO = &H213 WMR_MOVETO = &H214 WMR_OFFSETCLIPRGN = &H220 WMR_OFFSETVIEWPORTORG = &H211 WMR_OFFSETWINDOWORG = &H20F WMR_PAINTREGION = &H12B WMR_PATBLT = &H61D WMR_PIE = &H81A WMR_REALIZEPALETTE = &H35 WMR_RECTANGLE = &H41B WMR_RESETDC = &H14C WMR_RESIZEPALETTE = &H139 WMR_RESTOREDC = &H127 WMR_ROUNDRECT = &H61C WMR_SAVEDC = &H1E WMR_SCALEVIEWPORTEXT = &H412 WMR_SCALEWINDOWEXT = &H410 WMR_SELECTCLIPREGION = &H12C WMR_SELECTOBJECT = &H12D WMR_SELECTPALETTE = &H234 WMR_SETTEXTALIGN = &H12E WMR_SETBKCOLOR = &H201 WMR_SETBKMODE = &H102 WMR_SETDIBTODEV = &HD33 WMR_SETMAPMODE = &H103 WMR_SETMAPPERFLAGS = &H231 WMR_SETPALENTRIES = &H37 WMR_SETPIXEL = &H41F WMR_SETPOLYFILLMODE = &H106 WMR_SETRELABS = &H105 WMR_SETROP2 = &H104 WMR_SETSTRETCHBLTMODE = &H107 WMR_SETTEXTCHAREXTRA = &H108 WMR_SETTEXTCOLOR = &H209 WMR_SETTEXTJUSTIFICATION = &H20A WMR_SETVIEWPORTEXT = &H20E WMR_SETVIEWPORTORG = &H20D WMR_SETWINDOWEXT = &H20C WMR_SETWINDOWORG = &H20B WMR_STARTDOC = &H14D WMR_STARTPAGE = &H4F WMR_TEXTOUT = &HC End Enum
Public Type RECT Left As Long Top As Long Right As Long Bottom As Long End Type
Public Type PALETTEENTRY peRed As Byte peGreen As Byte peBlue As Byte peFlags As Byte End Type
Public Type LOGPALETTE palVersion As Integer palNumEntries As Integer palPalEntry(1) As PALETTEENTRY End Type
Public Type SIZEL cx As Long cy As Long End Type
Public Type ENHMETAHEADER iType As Long nSize As Long rclBounds As RECT rclFrame As RECT dSignature As Long nVersion As Long nBytes As Long nRecords As Long nHandles As Integer sReserved As Integer nDescription As Long offDescription As Long nPalEntries As Long szlDevice As SIZEL szlMillimeters As SIZEL End Type
Public Type METARECORD nSize As Long rdFunction As Integer lpParm As Integer End Type
Public Type ENHMETARECORD iType As Long nSize As Long lpParm As Long End Type
Public Declare Function DeleteMetaFile Lib "gdi32.dll" ( _ ByVal hMF As Long) As Long
Public Declare Function EnumMetaFile Lib "gdi32.dll" ( _ ByVal hdc As Long, _ ByVal hMetafile As Long, _ ByVal lpMFEnumProc As Long, _ ByVal lParam As Long) As Long
Public Declare Function GetMetaFile Lib "gdi32.dll" Alias "GetMetaFileA" ( _ ByVal lpFileName As String) As Long
Public Declare Function PlayMetaFileRecord Lib "gdi32.dll" ( _ ByVal hdc As Long, _ ByRef lpHandletable As Long, _ ByRef lpMetaRecord As METARECORD, _ ByVal nHandles As Long) As Long
Public Declare Function CreatePen Lib "gdi32.dll" ( _ ByVal nPenStyle As Long, _ ByVal nWidth As Long, _ ByVal crColor As Long) As Long
Public Declare Function DeleteObject Lib "gdi32.dll" ( _ ByVal hObject As Long) As Long
Public Declare Function EnumEnhMetaFile Lib "gdi32.dll" ( _ ByVal hdc As Long, _ ByVal hEMF As Long, _ ByVal lpEnhMetaFunc As Long, _ ByRef lpData As Any, _ ByRef lpRect As RECT) As Long
Public Declare Function GetEnhMetaFileHeader Lib "gdi32" ( _ ByVal hEMF As Long, _ ByVal cbBuffer As Long, _ lpemh As ENHMETAHEADER) As Long
Public Declare Function GetEnhMetaFile Lib "gdi32.dll" Alias "GetEnhMetaFileA" ( _ ByVal lpszMetaFile As String) As Long
Public Declare Function PlayEnhMetaFileRecord Lib "gdi32.dll" ( _ ByVal hdc As Long, _ ByRef lpHandletable As Long, _ ByRef lpEnhMetaRecord As ENHMETARECORD, _ ByVal nHandles As Long) As Long
Public Declare Function SelectObject Lib "gdi32.dll" ( _ ByVal hdc As Long, _ ByVal hObject As Long) As Long
Public Declare Function GetClientRect Lib "user32.dll" ( _ ByVal hwnd As Long, _ ByRef lpRect As RECT) As Long
Public Declare Function PlayEnhMetaFile Lib "gdi32.dll" ( _ ByVal hdc As Long, _ ByVal hEMF As Long, _ ByRef lpRect As Any) As Long
Public Declare Function PlayMetaFile Lib "gdi32.dll" ( _ ByVal hdc As Long, _ ByVal hMF As Long) As Long
Public Declare Function GetEnhMetaFilePaletteEntries Lib "gdi32.dll" ( _ ByVal hEMF As Long, _ ByVal cEntries As Long, _ ByRef lppe As PALETTEENTRY) As Long
Public Declare Function DeleteEnhMetaFile Lib "gdi32.dll" ( _ ByVal hEMF As Long) As Long
Public Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" ( _ ByRef Destination As Any, _ ByRef Source As Any, _ ByVal Length As Long)
Public Declare Function CreatePalette Lib "gdi32.dll" ( _ ByRef lpLogPalette As LOGPALETTE) As Long
Public Declare Function SelectPalette Lib "gdi32.dll" ( _ ByVal hdc As Long, _ ByVal hPalette As Long, _ ByVal bForceBackground As Long) As Long
Public Declare Function InvalidateRect Lib "user32.dll" ( _ ByVal hwnd As Long, _ ByRef lpRect As RECT, _ ByVal bErase As Long) As Long
Public Declare Function RealizePalette Lib "gdi32.dll" ( _ ByVal hdc As Long) As Long
Public Enum enumMetaFileType EMF = 0 WMF = 1 End Enum
Public arrRecords() As String Public OpenedFileType As enumMetaFileType
Public Function EnhMetaFileProc(ByVal ClientHDC As Long, _ ByRef lpHandTab As Long, _ ByRef MetaRec As ENHMETARECORD, _ ByVal nHandles As Long, _ ByVal OptData As Long) As Integer
Dim hPen As Long Dim x As Long
x = PlayEnhMetaFileRecord(ClientHDC, lpHandTab, MetaRec, ByVal nHandles)
Debug.Print EMFRecordDesc(MetaRec)
Delay (0.05)
EnhMetaFileProc = 1 End Function
Public Function WinMetaFileProc(ByVal ClientHDC As Long, _ ByRef lpHandTab As Long, _ ByRef MetaRec As METARECORD, _ ByVal nHandles As Long, _ ByVal OptData As Long) As Integer
Dim x As Long
x = PlayMetaFileRecord(ClientHDC, lpHandTab, MetaRec, ByVal nHandles)
Debug.Print WMFRecordDesc(MetaRec)
WinMetaFileProc = 1 End Function
Function EMFRecordDesc(MetaRec As ENHMETARECORD) Dim ParaBuffer() As Long, i Dim strPara As String, strMsg As String, sAPI As String * 15
If MetaRec.iType <= UBound(arrRecords) + 1 Then strMsg = Trim(arrRecords(MetaRec.iType - 1)) & " " sAPI = Replace(strMsg, "M_", "") strMsg = sAPI Else strMsg = "UNKNOWN " End If
If MetaRec.nSize > 8 Then Dim nParaCount As Long nParaCount = ((MetaRec.nSize - 8) / 4)
ReDim ParaBuffer(nParaCount - 1)
CopyMemory ParaBuffer(0), MetaRec.lpParm, MetaRec.nSize - 8
For i = 0 To UBound(ParaBuffer) strPara = strPara & " " & ParaBuffer(i) Next End If
Form1.List1.AddItem strMsg & strPara
EMFRecordDesc = strMsg & strPara End Function
Function WMFRecordDesc(MetaRec As METARECORD) Dim ParaBuffer() As Integer, i Dim strPara As String, strMsg As String
Select Case MetaRec.rdFunction Case WMR_SAVEDC strMsg = "SAVEDC " Case WMR_SETMAPMODE strMsg = "SETMAPMODE " Case WMR_SETWINDOWORG strMsg = "SETWINDOWORG " Case WMR_SAVEDC strMsg = "SAVEDC " Case WMR_SETVIEWPORTORG strMsg = "SETVIEWPORTORG " Case WMR_SAVEDC strMsg = "SAVEDC " Case WMR_SETWINDOWEXT strMsg = "SETWINDOWEXT " Case WMR_RESTOREDC strMsg = "RESTOREDC " Case WMR_SELECTOBJECT strMsg = "SELECTOBJECT " Case WMR_CREATEPENINDIRECT strMsg = "CREATEPENINDIRECT " Case WMR_DELETEOBJECT strMsg = "DELETEOBJECT " Case WMR_RECTANGLE strMsg = "RECTANGLE " Case Else strMsg = "Command-" & LowByte(MetaRec.rdFunction) & " " End Select
If MetaRec.nSize > 3 Then
Dim nParaCount As Byte nParaCount = (MetaRec.nSize - 3) ReDim ParaBuffer(nParaCount - 1)
CopyMemory ParaBuffer(0), MetaRec.lpParm, (MetaRec.nSize - 3) * 2
For i = UBound(ParaBuffer) To 0 Step -1 strPara = strPara & " " & ParaBuffer(i) Next End If
strMsg = "[&H" & Hex(MetaRec.rdFunction) & "] " & strMsg & strPara Form1.List1.AddItem strMsg
WMFRecordDesc = strMsg End Function
Public Function LowByte(ByVal w As Integer) As Byte LowByte = w And &HFF End Function
Public Function HiByte(ByVal w As Integer) As Byte Dim hi As Integer If w And &H8000 Then hi = &H4000
HiByte = (w And &H7FFE) \ 256 HiByte = (HiByte Or (hi \ 128)) End Function
Sub Delay(nSec As Single) Dim t1 As Currency t1 = Timer Do While True If (Timer - t1) > nSec Then Exit Do DoEvents Loop End Sub |
|