API

[Win32 - 김상형윈도우즈API정복-정리노트] 메타파일 Win32

디버그정 2008. 8. 30. 07:42
[Win32 - 김상형윈도우즈API정복-정리노트]메타파일 Win32

2008/05/21 14:06

복사 http://blog.naver.com/blue7red/100050978486

1.메타파일에 대한 개요

-메타파일은 GDI함수호출을 부호화하여 모아놓은 파일이다.

-비트맵이 그림자체를 저장하고 있는데 비해 메타파일은 그림을 그리는 명령을 저장하고 있기 때문에

장치독립적이라는 장점을 가지며 비트맵보다는 크기가 작다.

 

벡터그래픽은 일정한 크기의 비트맵으로 변환할 수 있으며,

모니터나 프린터 등의 장비가 래스터방식이기때문에 변환을 해야만 출력할 수 있다.

-벡터그래픽은 선이나 면 등의 그래픽 개체에 대한 정보를 가지고 있기 때문에

다른 개체에 영향을 주지 않고 그래픽 개체를 자유롭게 편집할 수 있고 크기 변경이 용이하다.


2.메타파일의 기본적인 사용법


(1)32비트 메타파일


LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
 HDC hdc;
 HDC hdcMeta;
 HENHMETAFILE hMFile;
 RECT rt;
 switch (iMessage) {
 case WM_LBUTTONDOWN:
  hdc=GetDC(hWnd);

//1.메타DC를 만든다.
  hdcMeta=CreateEnhMetaFile(hdc,"TestEnh.emf",NULL,
   "my program\0Test Enh Meta File\0");

//2.그림을 그린다.
  Ellipse(hdcMeta,10,10,100,100);
  Rectangle(hdcMeta,5,105,105,120);


//3.그리기를 종료하고 파일핸들을 만든다.
  hMFile=CloseEnhMetaFile(hdcMeta);//그리기 종료

 

//4.파일 핸들을 제거한다.
  DeleteEnhMetaFile(hMFile);


  ReleaseDC(hWnd, hdc);
  MessageBox(hWnd, "Meta File Created","Meta",MB_OK);
  return 0;
 case WM_RBUTTONDOWN:
  hdc=GetDC(hWnd);
  hMFile=GetEnhMetaFile("TestEnh.emf");//메타파일불러오기
  if (hMFile == NULL) {
   MessageBox(hWnd, "File Not Found", "Meta", MB_OK);
  } else {
   rt.left=LOWORD(lParam);
   rt.right=rt.left+100;
   rt.top=HIWORD(lParam);
   rt.bottom=rt.top+100;

   PlayEnhMetaFile(hdc,hMFile,&rt); //재생

  }
  DeleteEnhMetaFile(hMFile);
  ReleaseDC(hWnd,hdc);
  return 0;
 case WM_DESTROY:
  PostQuitMessage(0);
  return 0;
 }
 return(DefWindowProc(hWnd,iMessage,wParam,lParam));
}


(2)16비트 메타파일


LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
 HDC hdc;
 HDC hdcMeta;
 HMETAFILE hMFile;
 switch (iMessage) {
 case WM_LBUTTONDOWN:
  hdcMeta=CreateMetaFile("Test.wmf");


  Ellipse(hdcMeta,10,10,100,100);
  Rectangle(hdcMeta,5,105,105,120);


  hMFile=CloseMetaFile(hdcMeta);


  DeleteMetaFile(hMFile);


  MessageBox(hWnd, "Meta File Created","Meta",MB_OK);
  return 0;
 case WM_RBUTTONDOWN:
  hdc=GetDC(hWnd);
  hMFile=GetMetaFile("Test.wmf");
  if (hMFile == NULL) {
   MessageBox(hWnd, "File Not Found", "Meta", MB_OK);
  } else {
   PlayMetaFile(hdc,hMFile);
  }
  DeleteMetaFile(hMFile);
  ReleaseDC(hWnd,hdc);
  return 0;
 case WM_DESTROY:
  PostQuitMessage(0);
  return 0;
 }
 return(DefWindowProc(hWnd,iMessage,wParam,lParam));
}



3.메타파일의 구조


(1)16비트 메타 파일 분석

-선두에 METAHEADER라는 구조체로 시작되며 헤더 바로 다음에 메타레코드들이 이어진다.


(2)32비트 메타 파일 분석

-한 개의 헤더와 복 수개의 메타레코드로 구성되어 있다.


(3)플레이스볼 메타파일 분석

-32비트메타파일이 나오기 전에 16비트메타파일의 장치종속성을 극복하고자 엘더사에서 만든 포맷이다.

-16비트메타파일에 22바이트의 추가정보를 붙여 만든 것이다.


4.메타파일간의 전환

-

(1)16비트메타 파일의 32비트메타파일로의 전환

// 16비트 메타 파일을 32비트 메타 파일로 변환하여 메타 파일 핸들을 리턴한다.
// 에러 발생시 NULL을 리턴한다.
HENHMETAFILE ConvertWinToEnh(LPTSTR wmf)
{
 HENHMETAFILE hEnh;
 HMETAFILE wfile;
 DWORD dwSize;
 LPBYTE pBits;
 METAFILEPICT mp;
 HDC hdc;

 // 16비트 메타 파일을 읽고 메타 파일 크기만큼 메모리를 할당한다.
 wfile=GetMetaFile(wmf);
 if (wmf==NULL)
  return NULL;
 dwSize=GetMetaFileBitsEx(wfile, 0,NULL); //크기 구하기
 if (dwSize==0) {
  DeleteMetaFile(wfile);
  return NULL;
 }
 pBits=(LPBYTE)malloc(dwSize);
 
 // 메타 파일의 내용을 버퍼로 읽어들인다.
 GetMetaFileBitsEx(wfile, dwSize, pBits); //내용 읽어오기

 

 


 mp.mm=MM_ANISOTROPIC;
 mp.xExt=1000;
 mp.yExt=1000;
 mp.hMF=NULL;

 // 32비트 메타 파일을 만든다.
 hdc=GetDC(NULL);
 hEnh=SetWinMetaFileBits(dwSize, pBits, hdc, &mp);
 ReleaseDC(NULL, hdc);
 DeleteMetaFile(wfile);
 free(pBits);
 return hEnh;
}


LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
 HDC hdc;
 HENHMETAFILE hEnh;
 RECT rt;
 switch (iMessage) {
 case WM_LBUTTONDOWN:
  hdc=GetDC(hWnd);
  hEnh=ConvertWinToEnh("Test.wmf");
  if (hEnh==NULL) {
   MessageBox(hWnd, "변환할 수 없습니다.","에러",MB_OK);
  } else {
   SetRect(&rt, LOWORD(lParam), HIWORD(lParam),
    LOWORD(lParam)+100, HIWORD(lParam)+100);
   PlayEnhMetaFile(hdc, hEnh, &rt);
   DeleteEnhMetaFile(hEnh);
  }
  ReleaseDC(hWnd, hdc);
  return 0;
 case WM_DESTROY:
  PostQuitMessage(0);
  return 0;
 }
 return(DefWindowProc(hWnd,iMessage,wParam,lParam));
}



(2)플레이스볼 메타파일의 32비트메타파일로의 전환

-


#pragma pack(push)
#pragma pack(2)
typedef struct
{
 DWORD  dwKey;
 WORD  hmf;
 SMALL_RECT bbox;
 WORD  wInch;
 DWORD  dwReserved;
 WORD  wCheckSum;
} APMHEADER, *PAPMHEADER;
#pragma pack(pop)

// 플레이스블 메타 파일을 32비트 메타 파일로 변경해 준다.
// 에러 발생시 NULL을 리턴한다.
HENHMETAFILE ConvertPlaToEnh(LPTSTR szFileName)
{
 HENHMETAFILE hEnh;
 DWORD   dwSize;
 LPBYTE   pBits;
 METAFILEPICT mp;
 HDC    hdc;
 HANDLE   hFile;

 // 32비트 메타 파일이 아니면 플레이스블 메타 파일로 읽는다.
 // 파일 크기만큼 메모리를 할당하고 메타 파일을 읽어들인다.
 hFile = CreateFile( szFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING,
  FILE_ATTRIBUTE_NORMAL, NULL );
 if( hFile == INVALID_HANDLE_VALUE )
  return NULL;
 dwSize = GetFileSize( hFile, NULL );
 pBits = (LPBYTE)malloc( dwSize );
 ReadFile( hFile, pBits, dwSize, &dwSize, NULL );
 CloseHandle( hFile );
 // 플레이스블 메타 파일이 맞는지 확인한다.
 if( ((PAPMHEADER)pBits)->dwKey != 0x9ac6cdd7l ) {
  free( pBits );
  return NULL;
 }

 // 구조체를 채운다.
 mp.mm = MM_ANISOTROPIC;
 mp.xExt = ((PAPMHEADER)pBits)->bbox.Right - ((PAPMHEADER)pBits)->bbox.Left;
 mp.xExt = ( mp.xExt * 2540l ) / (DWORD)(((PAPMHEADER)pBits)->wInch);
 mp.yExt = ((PAPMHEADER)pBits)->bbox.Bottom - ((PAPMHEADER)pBits)->bbox.Top;
 mp.yExt = ( mp.yExt * 2540l ) / (DWORD)(((PAPMHEADER)pBits)->wInch);
 mp.hMF = NULL;
 // 메타 파일을 만든다.
 hdc = GetDC( NULL );
 hEnh = SetWinMetaFileBits( dwSize, &(pBits[sizeof(APMHEADER)]), hdc, &mp );
 ReleaseDC( NULL, hdc );
 free( pBits );
 return hEnh;
}

LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
 HDC hdc;
 PAINTSTRUCT ps;
 HENHMETAFILE wmf;
 RECT rt;
 switch (iMessage) {
 case WM_PAINT:
  hdc=BeginPaint(hWnd,&ps);
  GetClientRect(hWnd, &rt);
  wmf=ConvertPlaToEnh("buttrfly.wmf");
  PlayEnhMetaFile(hdc, wmf, &rt);
  EndPaint(hWnd,&ps);
  return 0;
 case WM_DESTROY:
  PostQuitMessage(0);
  return 0;
 }
 return(DefWindowProc(hWnd,iMessage,wParam,lParam));
}


(3)범용 변환함수

// 32비트 메타 파일의 핸들을 리턴한다. 16비트 메타 파일이나 플레이스블 메타 파일
// 일 경우 32비트 메타 파일로 변환해준다.
HENHMETAFILE ReadMeta(LPTSTR FileName)
{
 HENHMETAFILE hEnh;
 // 32비트 메타 파일의 핸들을 구해 리턴한다.
 hEnh=GetEnhMetaFile(FileName);
 if (hEnh != NULL)
  return hEnh;
 // 32비트 메타 파일이 아닐 경우 16비트 포멧으로 읽어보고 32비트 전환한다.
 hEnh=ConvertWinToEnh(FileName);
 if (hEnh != NULL)
  return hEnh;
 // 16비트 메타 파일도 아닐 경우 플레이스블 메타 파일을 32비트로 전환한다.
 hEnh=ConvertPlaToEnh(FileName);
 if (hEnh != NULL)
  return hEnh;
 // 세 경우 다 해당하지 않을 경우 NULL을 리턴한다.
 return NULL;
}



5.메타레코드

-

HWND hList;
int delay;
int count;
TCHAR *arMetaRecord[]={"","Header", "PolyBezier","Polygon","PolyLine","PolyBezierTo",   // 0
 "PolyLineTo","PolyPolyLine","PolyPolygon","SetWindowExtEx","SetWindowOrgEx",    // 6
 "SetViewPortExtEx","SetViewportOrgEx","SetBrushOrgEx","EOF","SetPixelV",     // 11
 "SetMapperFlags","SetMapMode","SetBkMode","SetPolyFillMode","SetRop2",      // 16
 "SetStretchBltMode","SetTextAlign","SetColorAdjustment","SetTextColor","SetBkColor",  // 21
 "OffsetClipRgn","MoveToEx","SetMetaRgn","ExcldueClipRect","IntersectClipRect",    // 26
 "ScaleViewportExtEx","ScaleWindowExtEx","SaveDC","ResotreDC","SetWorldTransform",   // 31
 "ModifyWorldTransForm","SelectObject","CreatePen","CreateBrushIndirect","DeleteObject",  // 36
 "AngleArc","Ellipse","Rectangle","RoundRect","Arc",           // 41
 "Chord","Pie","SelectPalette","CreatePalette","SetPaletteEntries",       // 46
 "ResizePalette","RealizePalette","ExtFloodFill","LineTo","ArcTo",       // 51
 "PolyDraw","SetArcDirection","SetMiterLimit","BeginPath","EndPath",       // 56
 "CloseFigure","FillPath","StrokeAndFillPath","StrokePath","FlattenPath",     // 61
 "WidenPath","SelectClipPath","AboartPath","EMPTY","GdiComment",        // 66
 "FillRgn","FrameRgn","InvertRgn","PaintRgn","ExtSelectClipRgn",        // 71
 "BitBlt","StretchBlt","MaskBlt","PlgBlt","SetDibitsToDevice",        // 76
 "StretchDIBits","ExtCreateFontIndirectW","ExtTextOutA","ExtTextOutW","PolyBezier16",  // 81
 "Polygon16","PolyLine16","PolyBezierTo16","PolyLineTo16","PolyPolyLine16",     // 86
 "PolyPolygon16","PolyDraw16","CreateMonoBrush","CreateDINPatternBrushPt","ExtCreatePen", // 91
 "PolyTextOutA","PolyTextOutW","SetICMMode","CreateColorSpace","SetColorSpace",    // 96
 "DeleteColorSpace","GLSRecord","GLSBoundedRecord","PixelFormat","DrawEscape",    // 101
 "ExtEscape","StartDoc","SmallTextOut","ForceUfiMapping","NamedEscape",      // 106
 "ColorCorrectPalette","SetICMProfileA","SetICMProfileW","AlphaBlend","AlphaDibBlend",  // 111
 "TransparentBlt","TransparentDIB","GradientFill","SetLinkedUfis","SetTextjustification"  // 116
};

int CALLBACK EnhMetaFileProc(HDC hDC, HANDLETABLE *lpHTable,

CONST ENHMETARECORD *lpEMFR, int nObj, LPARAM lpData)
{
 TCHAR str[255], sParm[128];
 POINT pt;
 int idx, i;

 // 메타 레코드 출력
 wsprintf(str,"%d-%s(%d), 인수=",
  count,arMetaRecord[lpEMFR->iType], lpEMFR->nSize);

 // 인수를 조사하되 최대 6개까지만 조사한다.
 // nSize가 DWORD 단위이므로 4로 나누고 iType,nSize의 길이도 포함되므로 2를 뺐다.
 for (i=0;i<int(lpEMFR->nSize)/4-2;i++) {
  if (i == 6)
   break;
  wsprintf(sParm,"%d,",lpEMFR->dParm[i]);
  strcat(str,sParm);
 }
 str[lstrlen(str)-1]=0;

 // 리스트 박스에 메타 레코드 출력
 idx=SendMessage(hList,LB_ADDSTRING,0,(LPARAM)str);
 SendMessage(hList,LB_SETCURSEL,idx,0);
 UpdateWindow(hList);
 count++;

 // 메타 레코드 재생
 PlayEnhMetaFileRecord(hDC,lpHTable,lpEMFR,nObj);
 Sleep(delay); //일정간격으로 화면을 업데이트해서 천천히 그려지도록 한다.

 // 커서가 0,0으로 이동하면 중지한다.
 GetCursorPos(&pt);
 if (pt.x==0 && pt.y==0) {
  if (MessageBox(hWndMain,"메타 파일 열거를 중지하시겠습니까?",
   "질문",MB_YESNO) == IDYES) {
   return 0;
  } else {
   return 1;
  }
 } else {
  return 1;
 }
}

LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
 HDC hdc;
 OPENFILENAME ofn;
 TCHAR szFileName[MAX_PATH];
 HENHMETAFILE hEnh;
 RECT rt;

 switch (iMessage) {
 case WM_CREATE:
  hList=CreateWindow("listbox",NULL,WS_CHILD | WS_VISIBLE | WS_BORDER |
   LBS_NOTIFY | WS_VSCROLL,10,50,400,600,hWnd,(HMENU)0,g_hInst,NULL);
  CreateWindow("button","파일 열기",WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
   20,10,100,25,hWnd,(HMENU)1,g_hInst,NULL);
  CreateWindow("static","지연시간",WS_CHILD | WS_VISIBLE,
   140,13,100,25,hWnd,(HMENU)-1,g_hInst,NULL);
  CreateWindow("edit",NULL,WS_CHILD | WS_VISIBLE | WS_BORDER | ES_NUMBER,
   220,10,100,25,hWnd,(HMENU)2,g_hInst,NULL);
  SetDlgItemInt(hWnd,2,1,FALSE);
  return 0;
 case WM_COMMAND:
  switch (LOWORD(wParam)) {
  case 1:
   hdc=GetDC(hWnd);
   // 출력하고자 하는 메타 파일의 핸들을 구한다.
   szFileName[0]=0;
   memset(&ofn, 0, sizeof(OPENFILENAME));
   ofn.lStructSize=sizeof(OPENFILENAME);
   ofn.hwndOwner=hWnd;
   ofn.lpstrFilter="Meta File\0*.?MF\0";
   ofn.nFilterIndex=1;
   ofn.lpstrFile=szFileName;
   ofn.nMaxFile=MAX_PATH;

   if (GetOpenFileName(&ofn) != 0) {
    // 32비트 메타 파일의 핸들을 구해 재생한다.
    hEnh=ReadMeta(szFileName);
    if (hEnh==NULL)
     return 0;
    GetClientRect(hWnd, &rt);
    rt.left += 420;
    SendMessage(hList,LB_RESETCONTENT,0,0);
    count=1;
    delay=GetDlgItemInt(hWnd,2,NULL,FALSE);
    InvalidateRect(hWnd,NULL,TRUE);
    UpdateWindow(hWnd);
    //PlayEnhMetaFile(hdc, hEnh, &rt);
    EnumEnhMetaFile(hdc,hEnh,EnhMetaFileProc,NULL,&rt);
    DeleteEnhMetaFile(hEnh);
   }
   ReleaseDC(hWnd, hdc);
   break;
  }
  return 0;
 case WM_DESTROY:
  PostQuitMessage(0);
  return 0;
 }
 return(DefWindowProc(hWnd,iMessage,wParam,lParam));
}