注意:画像の表示方法が分からないという方は、まず「コントロールやフォームに画像を表示する」をご覧ください。
「画像を拡大、縮小(スケーリング)して描画する」、「画像を傾けて表示する」にて画像を拡大、縮小、回転して表示する方法を紹介しましたが、ここでは「ワールド変換行列」を使う方法を紹介します。
ワールド変換行列はMatrixオブジェクトで表わされ、Matrixオブジェクトには2x2の線形変換のための行列と、1x2の平行移動のための行列が組み合わさった、3x3のアフィン行列が格納されています(詳しくはMSDNの「変換の行列表現」をご覧ください)。あるGraphicsオブジェクトで描画されるすべての画像や図形に適用する変換(グローバル変換)のためのMatrixオブジェクトは、GraphicsオブジェクトのTransformプロパティで取得、設定が出来ます。
なにやら難しいことになってきましたが、平行移動、スケーリング(拡大、縮小)、回転に関しては簡単に実現する方法が用意されていますので、ここではそれらを使うことにします。
あるGraphicsオブジェクトのワールド変換に平行移動を適用するにはGraphics.TranslateTransformメソッドを、スケーリングを適用するにはGraphics.ScaleTransformメソッドを、回転を適用するにはGraphics.RotateTransformメソッドをそれぞれ使用します。
次にこれらの変換を利用して画像を描画する例を示します。
'Imports System.Drawing '描画先とするImageオブジェクトを作成する Dim canvas As New Bitmap(PictureBox1.Width, PictureBox1.Height) 'ImageオブジェクトのGraphicsオブジェクトを作成する Dim g As Graphics = Graphics.FromImage(canvas) '画像を読み込む Dim img As Image = Image.FromFile("test.gif") '普通に画像を描画 g.DrawImage(img, New Rectangle(0, 0, img.Width, img.Height)) 'ワールド変換行列を単位行列にリセット g.ResetTransform() 'ワールド変換行列を下に10平行移動する g.TranslateTransform(0, img.Height + 10) '画像を描画 g.DrawImage(img, New Rectangle(0, 0, img.Width, img.Height)) 'ワールド変換行列を単位行列にリセット g.ResetTransform() 'ワールド変換行列にスケーリング操作を適用し、2倍に拡大する g.ScaleTransform(2.0F, 2.0F) '画像を描画 g.DrawImage(img, New Rectangle(img.Width, 0, img.Width, img.Height)) 'ワールド変換行列を単位行列にリセット g.ResetTransform() 'ワールド変換行列を45度回転する g.RotateTransform(45.0F) '画像を描画 g.DrawImage(img, New Rectangle(img.Width, img.Height, img.Width, img.Height)) 'リソースを解放する img.Dispose() g.Dispose() 'PictureBox1に表示する PictureBox1.Image = canvas
//using System.Drawing; //描画先とするImageオブジェクトを作成する Bitmap canvas = new Bitmap(PictureBox1.Width, PictureBox1.Height); //ImageオブジェクトのGraphicsオブジェクトを作成する Graphics g = Graphics.FromImage(canvas); //画像を読み込む Image img = Image.FromFile("test.gif"); //普通に画像を描画 g.DrawImage(img, new Rectangle(0, 0, img.Width, img.Height)); //ワールド変換行列を単位行列にリセット g.ResetTransform(); //ワールド変換行列を下に10平行移動する g.TranslateTransform(0, img.Height + 10); //画像を描画 g.DrawImage(img, new Rectangle(0, 0, img.Width, img.Height)); //ワールド変換行列を単位行列にリセット g.ResetTransform(); //ワールド変換行列にスケーリング操作を適用し、2倍に拡大する g.ScaleTransform(2F, 2F); //画像を描画 g.DrawImage(img, new Rectangle(img.Width, 0, img.Width, img.Height)); //ワールド変換行列を単位行列にリセット g.ResetTransform(); //ワールド変換行列を45度回転する g.RotateTransform(45F); //画像を描画 g.DrawImage(img, new Rectangle(img.Width, img.Height, img.Width, img.Height)); //リソースを解放する img.Dispose(); g.Dispose(); //PictureBox1に表示する PictureBox1.Image = canvas;
画像を拡大、縮小、回転のためにこのようなワールド変換を行うとき、画像だけが拡大、縮小、回転するわけではないことに注意してください。つまり画像の表示位置に関しても座標が変換されるので、そのことも考慮して描画位置を指定する必要があります。
上記の例ではそれぞれの変換を独立して行うため、ワールド変換を指定する前にResetTransformメソッドにより、ワールド変換行列を単位行列にリセットしています。もしこのリセットを行わなければ、適用が指定されるすべての変換は次々と追加され、連続する複数の変換(複合変換)となります。
次にこのことを確認するため、リセットせずに平行移動後回転のワールド変換を設定してみます。
'Imports System.Drawing '描画先とするImageオブジェクトを作成する Dim canvas As New Bitmap(PictureBox1.Width, PictureBox1.Height) 'ImageオブジェクトのGraphicsオブジェクトを作成する Dim g As Graphics = Graphics.FromImage(canvas) '画像を読み込む Dim img As Image = Image.FromFile("test.gif") '普通に画像を描画 g.DrawImage(img, New Rectangle(0, 0, img.Width, img.Height)) 'ワールド変換行列を右に平行移動する g.TranslateTransform(120, 0) '画像を描画 g.DrawImage(img, New Rectangle(0, 0, img.Width, img.Height)) 'ワールド変換行列を45度回転し、追加する g.RotateTransform(45.0F) '画像を描画 g.DrawImage(img, New Rectangle(0, 0, img.Width, img.Height)) 'リソースを解放する img.Dispose() g.Dispose() 'PictureBox1に表示する PictureBox1.Image = canvas
//using System.Drawing; //描画先とするImageオブジェクトを作成する Bitmap canvas = new Bitmap(PictureBox1.Width, PictureBox1.Height); //ImageオブジェクトのGraphicsオブジェクトを作成する Graphics g = Graphics.FromImage(canvas); //画像を読み込む Image img = Image.FromFile("test.gif"); //普通に画像を描画 g.DrawImage(img, new Rectangle(0, 0, img.Width, img.Height)); //ワールド変換行列を右に平行移動する g.TranslateTransform(120, 0); //画像を描画 g.DrawImage(img, new Rectangle(0, 0, img.Width, img.Height)); //ワールド変換行列を45度回転し、追加する g.RotateTransform(45F); //画像を描画 g.DrawImage(img, new Rectangle(0, 0, img.Width, img.Height)); //リソースを解放する img.Dispose(); g.Dispose(); //PictureBox1に表示する PictureBox1.Image = canvas;
この結果を見ると、回転後に平行移動していることが分かります。つまり、新しい変換は前に追加されるということが分かります。新しい変換を後ろに追加するために、MatrixOrder列挙体を指定することも出来ます。次の例では回転の操作を平行移動の後ろに追加してみましょう。
'Imports System.Drawing 'Imports System.Drawing.Drawing2D 'がソースファイルの一番上に書かれているものとする '描画先とするImageオブジェクトを作成する Dim canvas As New Bitmap(PictureBox1.Width, PictureBox1.Height) 'ImageオブジェクトのGraphicsオブジェクトを作成する Dim g As Graphics = Graphics.FromImage(canvas) '画像を読み込む Dim img As Image = Image.FromFile("test.gif") '普通に画像を描画 g.DrawImage(img, New Rectangle(0, 0, img.Width, img.Height)) 'ワールド変換行列を右に平行移動する g.TranslateTransform(120, 0) '画像を描画 g.DrawImage(img, New Rectangle(0, 0, img.Width, img.Height)) 'ワールド変換行列を45度回転し、後ろに追加する g.RotateTransform(45.0F, MatrixOrder.Append) '画像を描画 g.DrawImage(img, New Rectangle(0, 0, img.Width, img.Height)) 'リソースを解放する img.Dispose() g.Dispose() 'PictureBox1に表示する PictureBox1.Image = canvas
//using System.Drawing; //using System.Drawing.Drawing2D; //がソースファイルの一番上に書かれているものとする //描画先とするImageオブジェクトを作成する Bitmap canvas = new Bitmap(PictureBox1.Width, PictureBox1.Height); //ImageオブジェクトのGraphicsオブジェクトを作成する Graphics g = Graphics.FromImage(canvas); //画像を読み込む Image img = Image.FromFile("test.gif"); //普通に画像を描画 g.DrawImage(img, new Rectangle(0, 0, img.Width, img.Height)); //ワールド変換行列を右に平行移動する g.TranslateTransform(120, 0); //画像を描画 g.DrawImage(img, new Rectangle(0, 0, img.Width, img.Height)); //ワールド変換行列を45度回転し、後ろに追加する g.RotateTransform(45F ,MatrixOrder.Append); //画像を描画 g.DrawImage(img, new Rectangle(0, 0, img.Width, img.Height)); //リソースを解放する img.Dispose(); g.Dispose(); //PictureBox1に表示する PictureBox1.Image = canvas;
平行移動後に回転されているのがお分かりいただけるでしょう。
これらの結果を見れば一目瞭然ですが、変換操作を後ろにするか前にするかという順番は場合によってはとても重要になります。このことに関してはMSDNの「変換順序が重要となる理由」にも詳しく書かれています。