DOBON.NET プログラミング道: .NET Framework, VB.NET, C#, Visual Basic, Visual Studio, インストーラ, ...

プログラムで画像を動的に作成する

注意:画像の表示方法が分からないという方は、まず「コントロールやフォームに画像を表示する」をご覧ください。

ここでは、「画像ファイルを読み込み、Imageオブジェクトを作成する」のように画像ファイルを読み込んでImageオブジェクトを作成するのではなく、プログラムで動的に作成する方法を紹介します。この方法を使えば、プログラムで線や図形、画像、文字などを自由に描画してImageオブジェクトを作成して、コントロールに表示したり、ファイルに保存したりすることができます。

Imageオブジェクトを動的に作成する手順は、次のようになります。

  1. Bitmapオブジェクトを作成する。
  2. Graphics.FromImageメソッドGraphicsオブジェクトを作成する。
  3. Graphicsのメソッドを使って、図形などを描画する。
  4. GraphicsをDisposeメソッドで解放する。
補足:Imageクラスは抽象クラスで、Bitmapクラスはその派生クラスです。

早速ですが、具体例を示します。この例では、動的にImageオブジェクトを作成して、黒い背景に黄色の扇形を描画し、その画像をPictureBoxコントロール(PictureBox1)に表示しています。

Imageオブジェクトを動的に作成

VB.NET
コードを隠すコードを選択
'Imports System.Drawing

'200x100サイズのImageオブジェクトを作成する
Dim img As New Bitmap(200, 100)

'ImageオブジェクトのGraphicsオブジェクトを作成する
Dim g As Graphics = Graphics.FromImage(img)

'全体を黒で塗りつぶす
g.FillRectangle(Brushes.Black, g.VisibleClipBounds)
'黄色い扇形を描画する
g.DrawPie(Pens.Yellow, 60, 10, 80, 80, 30, 300)

'リソースを解放する
g.Dispose()

'作成した画像を表示する
PictureBox1.Image = img
C#
コードを隠すコードを選択
//using System.Drawing;

//200x100サイズのImageオブジェクトを作成する
Bitmap img = new Bitmap(200, 100);

//ImageオブジェクトのGraphicsオブジェクトを作成する
Graphics g = Graphics.FromImage(img);

//全体を黒で塗りつぶす
g.FillRectangle(Brushes.Black, g.VisibleClipBounds);
//黄色い扇形を描画する
g.DrawPie(Pens.Yellow, 60, 10, 80, 80, 30, 300);

//リソースを解放する
g.Dispose();

//作成した画像を表示する
PictureBox1.Image = img;

上の例ではBitmapのコンストラクタに、作成する画像のサイズだけを指定しています。このようにして作成されたImageオブジェクトは、すべてのピクセルが無色透明([A=0, R=0, G=0, B=0])です。

Graphicsオブジェクトに図形等を描画する方法は、このサイトでは、「長方形、多角形、楕円、円弧、扇形を描く」、「線を描く」、「曲線を描く」、「文字を描く」などで詳しく説明していますので、そちらをご覧ください。

すでにあるImageオブジェクトに描画する

わざわざ説明するほどのことではないかもしれませんが、この方法を使って、すでに存在しているImageオブジェクトに描画することもできます。例えば画像ファイルを「画像ファイルを読み込み、Imageオブジェクトを作成する」の方法でImageオブジェクトにしてから、文字や図形、あるいは他の画像など描画するといったこともできます。

以下の例では、画像ファイル(C:\test\1.bmp)の左上に文字列(DOBON.NET)を描画して、ファイルに保存しています。Imageオブジェクトをファイルに保存する方法は、「画像フォーマットを指定して保存する」をご覧ください。

VB.NET
コードを隠すコードを選択
'Imports System.Drawing

'画像ファイルを読み込んでImageオブジェクトを作成する
Dim img As New Bitmap("C:\test\1.bmp")
'ImageオブジェクトのGraphicsオブジェクトを作成する
Dim g As Graphics = Graphics.FromImage(img)

'文字列("DOBON.NET")を左上に描画する
Dim fnt As New Font("Arial", 12)
g.DrawString("DOBON.NET", fnt, Brushes.Black, 0, 0)
fnt.Dispose()

'作成した画像を保存する
img.Save("C:\test\new1.bmp")

'リソースを解放する
g.Dispose()
img.Dispose()
C#
コードを隠すコードを選択
//using System.Drawing;

//画像ファイルを読み込んでImageオブジェクトを作成する
Bitmap img = new Bitmap(@"C:\test\1.bmp");
//ImageオブジェクトのGraphicsオブジェクトを作成する
Graphics g = Graphics.FromImage(img);

//文字列("DOBON.NET")を左上に描画する
Font fnt = new Font("Arial", 12);
g.DrawString("DOBON.NET", fnt, Brushes.Black, 0, 0);
fnt.Dispose();

//作成した画像を保存する
img.Save(@"C:\test\new1.bmp");

//リソースを解放する
g.Dispose();
img.Dispose();

SetPixelメソッドで点を描く

実はGraphicsオブジェクトを作成しなくても、Bitmap.SetPixelメソッドを使えば任意の点(ピクセル)の色を変えることが出来ます。

以下の例では、Imageオブジェクトのランダムな位置にランダムな色をつけています。

ランダムに点を打つ

VB.NET
コードを隠すコードを選択
'Imports System.Drawing

'200x100サイズのImageオブジェクトを作成する
Dim img As New Bitmap(200, 100)

'ランダムな色の点をランダムな位置にうちまくる
Dim rnd As New Random()
For i As Integer = 0 To 2000
    '色を決める
    Dim c As Color = Color.FromArgb(rnd.Next(256), rnd.Next(256), rnd.Next(256))
    '1つのピクセルの色を変える
    img.SetPixel(rnd.Next(img.Width), rnd.Next(img.Height), c)
Next

'作成した画像を表示する
PictureBox1.Image = img
C#
コードを隠すコードを選択
//using System.Drawing;

//200x100サイズのImageオブジェクトを作成する
Bitmap img = new Bitmap(200, 100);

//ランダムな色の点をランダムな位置にうちまくる
Random rnd = new Random();
for (int i = 0; i < 2000; i++)
{
    //色を決める
    Color c = Color.FromArgb(rnd.Next(256), rnd.Next(256), rnd.Next(256));
    //1つのピクセルの色を変える
    img.SetPixel(rnd.Next(img.Width), rnd.Next(img.Height), c);
}

//作成した画像を表示する
PictureBox1.Image = img;

例えば画像すべてのピクセルの色を変えるなどのように、多数のピクセルの色を変える場合は、SetPixelメソッドは効率が良くありません。そのような場合は、Bitmap.LockBitsメソッドを使うとパフォーマンスが向上します。この方法は、「色を反転させた画像(ネガティブイメージ)を表示する」で紹介しています。

PictureBox.Imageプロパティに設定している画像を描き変えた時、すぐに表示に反映する

この記事の本筋からは少しそれますが、PictureBoxコントロールのImageプロパティを使ってImageオブジェクトを表示している場合の補足をします。

PictureBoxコントロールのImageプロパティに新しいImageオブジェクトを設定した場合は、新しい画像がすぐに表示されます。しかし、ここで紹介している方法でImageプロパティに設定されているImageオブジェクトの内容を描き変えた時は、それがすぐには表示に反映されません。Imageオブジェクトの変更をすぐに反映させるには、Invalidateメソッドを呼び出します。

以下の例では、現在の時刻をPicutureBoxコントロール(PictureBox1)のImageプロパティに描画しています。この例では描き変えた後でPictureBox1のInvalidateメソッドを呼び出していますので正常に現在の時刻が表示されますが、もしこれがないとPictureBox1の表示が変わらないことをお確かめください。

VB.NET
コードを隠すコードを選択
'Imports System.Drawing

'PictureBox.ImageプログラミングにImageオブジェクトを設定する
If PictureBox1.Image Is Nothing Then
    PictureBox1.Image = New Bitmap(100, 50)
End If

'ImageオブジェクトのGraphicsオブジェクトを作成する
Dim g As Graphics = Graphics.FromImage(PictureBox1.Image)

'全体を白で塗りつぶす
g.FillRectangle(Brushes.White, g.VisibleClipBounds)
'現在の時刻を描画する
g.DrawString(DateTime.Now.ToLongTimeString(), _
             SystemFonts.DefaultFont, Brushes.Black, 10, 10)

'Graphicsオブジェクトのリソースを解放する
g.Dispose()

'Imageプロパティの変更を反映させるために、PictureBox1を再描画する
PictureBox1.Invalidate()
C#
コードを隠すコードを選択
//using System.Drawing;

//PictureBox.ImageプログラミングにImageオブジェクトを設定する
if (PictureBox1.Image == null)
{
    PictureBox1.Image = new Bitmap(100, 50);
}

//ImageオブジェクトのGraphicsオブジェクトを作成する
Graphics g = Graphics.FromImage(PictureBox1.Image);

//全体を白で塗りつぶす
g.FillRectangle(Brushes.White, g.VisibleClipBounds);
//現在の時刻を描画する
g.DrawString(DateTime.Now.ToLongTimeString(),
    SystemFonts.DefaultFont, Brushes.Black, 10, 10);

//Graphicsオブジェクトのリソースを解放する
g.Dispose();

//Imageプロパティの変更を反映させるために、PictureBox1を再描画する
PictureBox1.Invalidate();

なおInvalidateメソッドに関してより詳しくは、「Refresh、Update、Invalidateメソッドの違い」で説明しています。

  • 履歴:
  • 2012/7/31 「すでにあるImageオブジェクトに描画する」と「PictureBox.Imageプロパティに設定している画像を描き変えた時、すぐに表示に反映する」を追加。その他大幅な書き換え。

注意:この記事では、基本的な事柄の説明が省略されているかもしれません。初心者の方は、特に以下の点にご注意ください。

  • このサイトで紹介されているコードの多くは、例外処理が省略されています。例外処理については、こちらをご覧ください。
  • コードの先頭に記述されている「Imports ??? がソースファイルの一番上に書かれているものとする」(C#では、「using ???; がソースファイルの一番上に書かれているものとする」)の意味が分からないという方は、こちらをご覧ください。
  • Windows Vista以降でUACが有効になっていると、ファイルへの書き込みに失敗する可能性があります。詳しくは、こちらをご覧ください。
  • .NET Tipsをご利用いただく際は、注意事項をお守りください。