画像を操作する
描画の応用について見ていくことにしましょう。Bitmapクラスのメソードを使うと、画像を1ピクセルずつ取得・設定することができます。このことを利用して画像の色を処理してみましょう。ここではCドライブの下に「XXX.jpg」という画像ファイルを保存してから実行します。
using System;
using System.Drawing;
using System.Windows.Forms;
namespace lesson8
{
class sample3 : Form
{
private Bitmap bm1, bm2;
private int i;
static void Main(string[] args)
{
Application.Run(new sample3());
}
public sample3()
{
this.Text = "lesson8";
this.Width = 400; this.Height = 300;
bm1 = new Bitmap("E:\\099_Technology\\C#\\csharp\\csharp_train\\lesson2\\google.JPG");
bm2 = new Bitmap("E:\\099_Technology\\C#\\csharp\\csharp_train\\lesson2\\google.JPG");
i = 0;
this.Click += new EventHandler(fm_Click);
this.Paint += new PaintEventHandler(fm_Paint);
}
public void convert()
{
for (int x = 0; x < bm1.Width; x++)//画像を1ピクセルずつ処理します
for (int y = 0; y < bm1.Height; y++)
{
Color c = bm1.GetPixel(x, y);//①ピクセルの色を取得します
int rgb = c.ToArgb();//②RGB値に変換します
int a = (rgb >> 24) & 0xFF;//③RGB値を取り出します
int r = (rgb >> 16) & 0xFF;
int g = (rgb >> 8) & 0xFF;
int b = (rgb >> 0) & 0xFF;
switch (i)
{
case 1:
r >>= 2; break;//④赤成分の値を小さくします
case 2:
g >>= 2; break;//④緑成分の値を小さくします
case 3:
b >>= 2; break;//④青成分の値を小さくします
case 4:
a >>= 2; break;
}
rgb = (a << 24) | ( r << 16) | (g << 8) | (b << 0);//⑤RGB値に変換します
c = Color.FromArgb(rgb);
bm2.SetPixel(x, y, c);//⑥ピクセルの色を設定します
}
}
public void fm_Click(object sender, EventArgs e)
{
i++;
if (i >= 4)
{
i = 0;
}
convert();//画像の変換を行います
this.Invalidate();
}
public void fm_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
g.DrawImage(bm2, 0, 0);
}
}
}
実行画面:
クリックすると色が変わる
ここでは画像の変換をconvert()と名前のメソードにまとめています。
ここではまず、横・縦について入れ子にした繰り返し文を使ってピクセルの色を取得しています(①)。
このピクセルの色はToArgb()メソードで透明度・赤・緑・青成分を表す整数値に変換されます(②)。
この>>演算子とビット演算子を使って、この整数値を各成分に分けて抜き出します(③)。そして、クリックによって変わるiの状態によって、特定成分のビットを小さくして変換します(④)。
変換が終わったら、再びこの色をRGB整数値に変換し(⑤)、Colorとしてピクセルに戻しています(⑥)。
この結果、画像の赤成分・緑成分・青成分が抜かれた画像となるのです。
画像の色取得・変換することができる。
マウスでクリックした円を描く
今度はマウスでクリックした位置に円を描画してみることにしましょう。
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Collections.Generic;
namespace lesson8
{
class sample4 : Form
{
private List<Point> ls;
static void Main(string[] args)
{
Application.Run(new sample4());
}
public sample4()
{
this.Text = "lesson8";
ls = new List<Point>();//クリックした位置を格納するリストを作成しています
this.MouseDown += new MouseEventHandler(fm_MouseDown);
this.Paint += new PaintEventHandler(fm_Paint);
}
public void fm_MouseDown(object sender, MouseEventArgs e)//マウスでクリックしたときに
{
Point p = new Point();
p.X = e.X;//位置を記録し...
p.Y = e.Y;
ls.Add(p);
this.Invalidate();//描画を行います
}
public void fm_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
Pen dp = new Pen(Color.Black, 1);
foreach (Point p in ls)
{
int x = p.X;
int y = p.Y;
g.DrawEllipse(dp, x, y, 10, 10);//円を描画します
}
}
}
}
MouseDownイベントを処理するイベントハンドラで、マウスをクリックした位置を記録し、処理を行います。
Paintイベントを処理するイベントハンドラでは、これを受けて描画を行います。ここではDrawEllipse()メソードを使って線画の円を描画しています。このほかにも、下記にあげているような描画を実行できます。
なお、線画を描画するためには、線の色と太さを決めるPenクラスのオブジェクトを用意する必要があります。1番目の引数として指定します。また、塗りつぶしを行う場合はペンの代わりにブラシと呼ばれる塗りつぶしの色を決めるブラシオブジェクト用意します。線画・ペン・ブラシの種類を変えて確認してみてください。
メソード名 | 説明 |
---|---|
g.DrawEllipse() | 楕円を描く |
g.DrawLine() | 線を描く |
g.DrawLines() | 線の集まりを描く |
g.DrawRectangle() | 四角形を描く |
g.DrawRectangles() | 四角形の集まりを描く |
g.DrawPie() | 扇型を描く |
g.DrawString() | 文字列を描く |
g.FillEllipse() | 塗りつぶし楕円を描く |
g.FillLine() | 塗りつぶし線を描く |
g.FillLines() | 塗りつぶし線の集まりを描く |
g.FillRectangle() | 塗りつぶし四角形を描く |
g.FillRectangles() | 塗りつぶし四角形の集まりを描く |
g.FillPie () | 塗りつぶし扇型を描く |
メソード名 | 説明 |
---|---|
Pen(Color c) | 色を指定したペン |
Pen(Brush b) | ブラシを指定したペン |
Pen(Color c, Signle s) | 色と太さを指定したペン |
Pen(Brush b, Signle s) | ブラシと太さを指定したペン |
メソード名 | 説明 |
---|---|
SolidBrush(Color c) | 色を使用 したブラシ |
TextureBrush(Image i) | イメージを使用したブラシ |
HatchStyleBrush(HatchStyle h, Color c) | ハッチスタイルを使用したブラシ |
LinearGradientBrush() | 線形グランデーションブラシ |
PathGradientBrush(GraphicsPath gp) | パスの内部をグランデーションするブラシ |
図形を描画することができる。
コレクションクラスの仕組みを知る
次の部分ではクリックした位置を格納するリストを作成しています。
private List<Point> ls;
ls = new List<Point>();
これは座標を表すPoint構造体の値を格納するリスト(list)となっています。リストは配列と似て複数の要素をまとめて扱うための仕組みですが、配列と異なり、後から要素を追加することが前提となっています。
このように、要素の集合を扱うクラスをコレクション(Collection)クラスといいます。
なお、クラスの中で取り扱う型を<型>と指定できるクラスは、ジェネリック(Generic)クラスと呼ばれてます。ジェネリッククラスではリストとして扱う型を指定して安全なコード記述できるように設計されています。
クラス(<T>は扱う形) | 説明 |
---|---|
List<T> | リストを管理する |
Queue<T> | キュー(先入先出の構造)を管理する |
Stack<T> | スタック(先入後出の構造)を管理する |
Dictionary<Tkey, TValue> | キーと値のペアを管理する |
コレクションクラスを利用することができる。
インデクサの仕組みを知る
List<Point>型の変数lsには、次のように[]で添字を記述してリスト構造の要素にアクセスすることもできます。
Point p0 = ls[0]
Point p1 = ls[1]
この仕組みをインデクサ(indexer)といいます。クラスにインデクサが定義されていれば、オブジェクトを配列のようにように扱うことができるのです。リストの構成要素のように、集合的なクラスが小さな構造から構成される場合には、インデクサを定義する場合があります。List<T>クラスにはインデクサが提供されてるのです。
通常、インデクサは次のように定義されています。プロパティーと同じようにsetアクセスとgetアクセスが定義されるものですが、添字と値の指定によって配列に値を設定・取得するようになっています。
インデクサの定義
class クラス名<T>
{
配列の宣言と作成;
public T this [int i]
{
set{配列[i]に値を代入;}
get{配列[i]の値を返す;}
}
}
クラスが集合の機能として提供される場合には、個々の要素にアクセスするためにインデックスを利用することがある。
クリッピングの仕組みを知る
フォームを表示したりInvalidate()を使ったりすると、通常フォーム全体が描画されます。この時、描画を行う範囲を指定することができます。これをクリーピング(clipping)といいます。クリッピングなどの範囲を指定する概念をリージョン(region)といます。
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
namespace lesson8
{
class sample5 : Form
{
private Image img;
static void Main(string[] args)
{
Application.Run(new sample5());
}
public sample5()
{
this.Text = "lesson8";
img = Image.FromFile("E:\\099_Technology\\C#\\csharp\\csharp_train\\lesson2\\twitter.JPG");
this.ClientSize = new Size(400, 300);
this.BackColor = Color.Black;
this.Paint += new PaintEventHandler(fm_Paint);
}
public void fm_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
GraphicsPath gp = new GraphicsPath();//グラフィックパスを作成し...
gp.AddEllipse(new Rectangle(0, 0, 400, 300));//①グラフィックパスに円を追加します
Region rg = new Region(gp);//②グラフィックパスからリージョンを作成します
g.Clip = rg;//③クリッピングを行う
g.DrawImage(img, 0, 0);
}
}
}
ここではリージョンを作成するために、まずGraphicsPathクラスのパスを作成し、ここに円のパスを追加しています(①)。そして、このパスを指定してリージョンを作成しています(②)。
リージョンが作成されたら、リージョンを指定してフォームのクリッピング領域を指定します(③)。
この結果、クリッピングされた領域だけに画像が描かれることになります。クリッピングの効果が確認できるでしょうか。
領域を指定するためにRegionクラスを利用できる。
クリッピングを行うことができる。
コメント