拡張メタファイルを作成する


Windows GDIとVCLによる拡張メタファイルの作成方法をまとめておく.

Windows GDIを使う

拡張メタファイルを作成するにはCreateEnhMetaFileを用いる. このAPIのプロトタイプは以下のようになっている.

function CreateEnhMetaFile(DC: HDC; p2: PChar; p3: PRect; p4: PChar): HDC;

DCは参照デバイスコンテキスト(DC)のハンドルである. 通常はスクリーンDCのハンドルを指定すればよいだろう. p2は作成したファイルを格納するファイル名であり, 拡張子を ".emf" とする. p3は画像の寸法を示すTRect型変数へのポインタであるが, この変数の値を設定する際には注意を要する. 画像の寸法は0.01mm単位の値を設定する必要があり, 次式で計算する.

(p3パラメータに設定する値)= (画面上での画像の寸法[pixel])* (画面の寸法[mm] * 100)/ (画面の寸法[pixel])

mm単位およびpixel単位の画面の寸法はGetDeviceCapsで取得できる. p4は作者名などのオプション文字列へのポインタである.

CreateEnhMetaFileの呼び出しが成功すればメタファイルDCのハンドルが返ってくる. このハンドルを使えば,通常のDCと同じようにメタファイルDCに描画することができる.

描画が終了したらCloseEnhMetaFileにメタファイルDCのハンドルを渡してDCを解放する. CloseEnhMetaFileはメタファイルのハンドルを返す. このハンドルはPlayEnhMetaFileEnumEnhMetaFileに渡すことができる. メタファイルが不要になったらDeleteEnhMetaFileを呼び出してメタファイルのハンドルを解放しなければならない.

以下にプログラム例を示す.

procedure TForm1.Button1Click(Sender: TObject);
var
  MMPerPixelW, MMPerPixelH: Integer;
  Index, X, Y: Integer;
  RefRect: TRect;
  MetafileDC: HDC;
  TheMetafile: HMETAFILE;
  TheBrush, OldBrush: HBRUSH;
  ThePen, OldPen: HPEN;
  TheColor: TColor;
begin
   MMPerPixelW := MulDiv( GetDeviceCaps( Canvas.Handle, HORZSIZE ), // 1pixelあたりの長さを取得する(0.01mm単位,x方向)
      100, GetDeviceCaps( Canvas.Handle, HORZRES ) );
   MMPerPixelH := MulDiv( GetDeviceCaps( Canvas.Handle, VERTSIZE ), // 1pixelあたりの長さを取得する(0.01mm単位,y方向)
      100, GetDeviceCaps( Canvas.Handle, VERTRES ) );

   RefRect.Top := 0;
   RefRect.Left := 0;
   RefRect.Right := Image1.Width * MMPerPixelW;    // Imageの幅
   RefRect.Bottom := Image1.Height * MMPerPixelH;  // Imageの高さ

   MetafileDC := CreateEnhMetaFile( Canvas.Handle, 'meta_gdi.emf', @RefRect, nil ); // メタファイルDCを作成

   TheBrush := CreateSolidBrush( clAqua );             // 水色のブラシを作成
   OldBrush := SelectObject( MetafileDC, TheBrush );   // DCに選択
   FillRect( MetafileDC, RefRect, TheBrush );          // 塗りつぶし
   SelectObject( MetafileDC, OldBrush );               // 以前のブラシをDCに選択
   DeleteObject( TheBrush );                           // 水色のブラシを削除

   for Index := 0 to 99 do
   begin
      TheColor := RGB( Random( 255 ), Random( 255 ), Random( 255 ) );  // 任意の色を設定
      ThePen := CreatePen( PS_SOLID, 1, TheColor );                    // 実線のペンを作成
      TheBrush := CreateSolidBrush( TheColor );                        // ブラシを作成
      OldPen := SelectObject( MetafileDC, ThePen );                    // DCにペンを選択
      OldBrush := SelectObject( MetafileDC, TheBrush );                // DCにブラシを選択
      X := Random( Image1.Width );                                     // 任意を座標を設定
      Y := Random( Image1.Height );
      Ellipse( MetafileDC, X, Y, X + 20, Y + 20 );                     // 円を描く
      SelectObject( MetafileDC, OldPen );
      SelectObject( MetafileDC, OldBrush );
      DeleteObject( ThePen );
      DeleteObject( TheBrush );
   end;

   TheMetaFile := CloseEnhMetaFile( MetafileDC );                      // メタファイルDCを解放
   PlayEnhMetaFile( Image1.Canvas.Handle, TheMetaFile, Image1.Canvas.ClipRect ); // Imageにメタファイルを再生する
   DeleteEnhMetaFile( TheMetaFile );                                   // メタファイルを解放
end;

VCLを使う

VCLのTMetaFileクラスはメタファイルを扱うクラスである. ただし,TMetaFileクラス自体にはCanvasプロパティが無いので,実際の描画にはTMetaFileCanvasクラスを用いる.

まずTMetaFileクラスのインスタンスを作成し,EnhancedをTrueに,WidthおよびHeightを適当な値にそれぞれ設定する. 次にTMetaFileCanvasクラスのインスタンスを作成するが,このコンストラクタには 先に作成したTMetaFileのインスタンスおよび参照DCのハンドルを渡す. 参照DCは,TMetaFileのMMWidthおよびMMHeightが未設定の場合に これらを設定するために使われる. 参照DCのハンドルに0が指定された場合,TMetaFileCanvasのコンストラクタはスクリーンDCのハンドルを 使ってMMWidthおよびMMHeightを設定する. この場合,TMetaFileのWidthおよびHeightがなぜか0になってしまうので,描画を始める前にこれらのプロパティを 再設定する必要がある.

あとは通常のCanvasに対する描画と同じように描画を行うことができる. 描画が終了したらTMetaFileCanvasのFreeメソッドを呼び出すことにより, 描画したイメージがTMetaFileクラスのインスタンスに転送される. TMetaFileクラスのインスタンスは,他のCanvasのDrawメソッドに渡すことができる.

以下にプログラム例を示す.

procedure TForm1.Button4Click(Sender: TObject);
var
  Metafile: TMetafile;
  MetafileCanvas: TMetafileCanvas;
  TheColor: TColor;
  Index, X, Y: Integer;
  RefRect: TRect;
begin
   Metafile := TMetafile.Create;                              // TMetaFileのインスタンスを作成し,
   Metafile.Enhanced := True;                                 // EnhancedをTrueに,
   Metafile.Width := Image1.Width;                            // WidthおよびHeightを適当な値に設定する
   Metafile.Height := Image1.Height;

   MetafileCanvas := TMetafileCanvas.Create( Metafile, 0 );   // TMetaFileCanvasのインスタンスを作成する
   MetafileCanvas.Pen.Width := 1;
   MetafileCanvas.Pen.Style := psSolid;
   MetafileCanvas.Brush.Color := clYellow;

   RefRect := Rect( 0, 0, Image1.Width, Image1.Height );
   Metafile.Width := Image1.Width;                            // メタファイルのWidthおよびHeightを再設定する
   Metafile.Height := Image1.Height;

   MetafileCanvas.FillRect( RefRect );

   for Index := 0 to 99 do
   begin
      TheColor := RGB( Random( 255 ), Random( 255 ), Random( 255 ) );
      with MetafileCanvas do
      begin
         Pen.Color := TheColor;
         Brush.Color := TheColor;
         X := Random( RefRect.Right );
         Y := Random( RefRect.Bottom );
         Ellipse( X, Y, X + 20, Y + 20 );
      end;
   end;

   MetafileCanvas.Free;                                     // TMetaFileCanvasのオブジェクトを破棄する
   Metafile.SaveToFile( 'meta_vcl.emf' );                   // メタファイルを保存する
   Image1.Canvas.Draw( 0, 0, Metafile );                    // Imageに描画する
   Metafile.Free;
end;

以上のように,VCLのTMetaFileクラスを使えば幾分コードが簡単になるが,WidthとHeightを再設定しなければならならないなど, やや見通しが悪い(これは単にやり方が悪いだけかも知れない). 実行結果は次のようになる.

Windows GDI
VCL

できた拡張メタファイルのサイズは,Windows GDI: 15112バイト,VCL: 19132バイトであった.


お問い合わせはメールにて: akasaka@klc.ac.jp

戻る
SEO [PR] 爆速!無料ブログ 無料ホームページ開設 無料ライブ放送