これまで範囲選択は矩形でおこなってきました。今回は自由曲線で領域を囲い込み、範囲選択された部分を移動させたりカットしたりコピーします。
では自由曲線で囲い込まれた領域をどのようにして認識すればいいでしょうか?
自由曲線で領域を囲い込む。その内部を塗りつぶしてこれを目印にすれば囲い込まれている部分とそうでない部分を区別することができるようになります。
Contents
メニューを追加
まずEditMode列挙体に自由曲線による範囲選択を追加します。
1 2 3 4 5 6 7 8 9 10 11 12 |
public enum EditMode { Selection, SelectionFree, // これを追加 DrawRectangle, FillRectangle, DrawEllipse, FillEllipse, DrawLine, FreeLine, FillColor, } |
そして[自由曲線による範囲選択]が選択されたらメニューにチェックをつけます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
public partial class Form1 : Form { void CheckMenu1(ToolStripMenuItem menuItem) { SelectionMenuItem.Checked = false; SelectionFreeMenuItem.Checked = false; DrawFigureMenuItem.Checked = false; DrawRectangleMenuItem.Checked = false; FillRectangleMenuItem.Checked = false; DrawEllipseMenuItem.Checked = false; FillEllipseMenuItem.Checked = false; DrawLineMenuItem.Checked = false; FreeLineMenuItem.Checked = false; FillColorMenuItem.Checked = false; if(menuItem == SelectionMenuItem) SelectionMenuItem.Checked = true; else if(menuItem == SelectionFreeMenuItem) SelectionFreeMenuItem.Checked = true; else if(menuItem == DrawRectangleMenuItem) { DrawFigureMenuItem.Checked = true; DrawRectangleMenuItem.Checked = true; } else if(menuItem == FillRectangleMenuItem) { DrawFigureMenuItem.Checked = true; FillRectangleMenuItem.Checked = true; } else if(menuItem == DrawEllipseMenuItem) { DrawFigureMenuItem.Checked = true; DrawEllipseMenuItem.Checked = true; } else if(menuItem == FillEllipseMenuItem) { DrawFigureMenuItem.Checked = true; FillEllipseMenuItem.Checked = true; } else if(menuItem == DrawLineMenuItem) { DrawFigureMenuItem.Checked = true; DrawLineMenuItem.Checked = true; } else if(menuItem == FreeLineMenuItem) { DrawFigureMenuItem.Checked = true; FreeLineMenuItem.Checked = true; } else if(menuItem == FillColorMenuItem) { FillColorMenuItem.Checked = true; } } } |
マウスがクリックされたときの処理
[範囲指定(自由曲線)]が選択されているときにクリックされたらOnMouseDownForSelectFreeAreaメソッドを呼びます。すでに自由曲線による範囲選択がおこなわれているときにクリックされたのであれば移動させる準備をします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
public partial class UserControlImage : UserControl { private void PictureBox1_MouseDown(object sender, MouseEventArgs e) { if(Bitmap == null) return; // 範囲外におけるクリック if(e.X + ScrollBarPosX >= Bitmap.Width || e.Y + ScrollBarPosY >= Bitmap.Height) { OnMouseDownOutOfBitmap(); Console.WriteLine("OnMouseDownOutOfBitmap()"); return; } // クリックされた場所の色を通知する if(PictureBoxMouseDown != null) { Color color = Bitmap.GetPixel(e.X + ScrollBarPosX, e.Y + ScrollBarPosY); PictureBoxMouseDown(this, new PictureBox1MouseDownArgs(e.X + ScrollBarPosX, e.Y + ScrollBarPosY, color)); } if(BitmapRectangle == null) { if(EditMode == EditMode.Selection) OnMouseDownForSelectRectangle(new Point(e.X + ScrollBarPosX, e.Y + ScrollBarPosY)); if(EditMode == EditMode.DrawRectangle || EditMode == EditMode.FillRectangle || EditMode == EditMode.DrawEllipse || EditMode == EditMode.FillEllipse) OnMouseDownForDrawRectangle(new Point(e.X + ScrollBarPosX, e.Y + ScrollBarPosY)); if(EditMode == EditMode.DrawLine) OnMouseDownForDrawLine(new Point(e.X + ScrollBarPosX, e.Y + ScrollBarPosY)); if(EditMode == EditMode.FreeLine) OnMouseDownForDrawFreeLine(new Point(e.X + ScrollBarPosX, e.Y + ScrollBarPosY)); if(EditMode == EditMode.SelectionFree) OnMouseDownForSelectFreeArea(new Point(e.X + ScrollBarPosX, e.Y + ScrollBarPosY)); } else { if(IsPointInRectangle(new Point(e.X + ScrollBarPosX, e.Y + ScrollBarPosY), BitmapRectangle.Rectangle)) OnMouseDownInBitmapRectangle(new Point(e.X + ScrollBarPosX, e.Y + ScrollBarPosY)); else OnMouseDownOutOfSelection(new Point(e.X + ScrollBarPosX, e.Y + ScrollBarPosY)); } } } |
OnMouseDownForSelectFreeAreaメソッド
OnMouseDownForSelectFreeAreaメソッドがよばれたとき、自由曲線による範囲選択がされている場合とされていない場合があります。されていないときの処理は単に自由曲線の描画をするときとかわりません。
すでに範囲選択がされている場合は、移動の準備をします。また一度移動が完了してさらにもう一度クリックされた場合も考えられます。このときはクリックされた部分が範囲選択されている部分なのかどうかで処理をわけることになります。前者であれば再移動の準備をして、そうでない場合はBitmapを確定させることになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
public partial class UserControlImage : UserControl { List<Point> borderPoints = new List<Point>(); void OnMouseDownForSelectFreeArea(Point bitmapPoint) { // すでに範囲選択がされている場合 if(borderPoints != null && borderPoints.Count > 0) { OnMouseDownForMoveFreeLineSelection(new Point(bitmapPoint.X, bitmapPoint.Y)); borderPoints.Clear(); return; } isMouseDown = true; pictureBox1.Capture = true; startPoint = new Point(bitmapPoint.X, bitmapPoint.Y); linePoints = new List<Point>(); linePoints.Add(bitmapPoint); } } |
マウスを動かしたときの処理
マウスが移動したときの処理です。
[範囲指定(自由曲線)]が選択されているときであっても範囲指定がおこなわれようとしているときと、範囲指定された部分が移動しようとしているときはわけて考える必要があります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
public partial class UserControlImage : UserControl { private void PictureBox1_MouseMove(object sender, MouseEventArgs e) { if(Bitmap == null) return; if(isMouseDown) { if(EditMode == EditMode.Selection) OnMouseMoveForSelectRectangle(new Point(e.X + ScrollBarPosX, e.Y + ScrollBarPosY)); if(EditMode == EditMode.DrawRectangle || EditMode == EditMode.FillRectangle || EditMode == EditMode.DrawEllipse || EditMode == EditMode.FillEllipse) OnMouseMoveForDrawRectangle(new Point(e.X + ScrollBarPosX, e.Y + ScrollBarPosY)); if(EditMode == EditMode.DrawLine) OnMouseMoveForDrawLine(new Point(e.X + ScrollBarPosX, e.Y + ScrollBarPosY)); if(EditMode == EditMode.FreeLine) OnMouseMoveForDrawFreeLine(new Point(e.X + ScrollBarPosX, e.Y + ScrollBarPosY)); if(EditMode == EditMode.SelectionFree) OnMouseMoveForSelectFreeArea(new Point(e.X + ScrollBarPosX, e.Y + ScrollBarPosY)); } else ChangeCursorWhenMouseMoveOnSelection(new Point(e.X + ScrollBarPosX, e.Y + ScrollBarPosY)); if(isMouseDownForMove) { if(EditMode == EditMode.Selection || EditMode == EditMode.FillRectangle) OnMouseMoveForMoveSelection(new Point(e.X + ScrollBarPosX, e.Y + ScrollBarPosY)); if(EditMode == EditMode.SelectionFree) OnMouseMoveForMoveFreeLineSelection(new Point(e.X + ScrollBarPosX, e.Y + ScrollBarPosY)); } if(isMouseDownForSizeChange) OnMouseMoveForSizeChangeSelection(new Point(e.X + ScrollBarPosX, e.Y + ScrollBarPosY)); } } |
OnMouseMoveForSelectFreeAreaメソッド
OnMouseMoveForSelectFreeAreaメソッドの内容は自由曲線を描画するときと同じです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
public partial class UserControlImage : UserControl { void OnMouseMoveForSelectFreeArea(Point bitmapPoint) { endPoint = bitmapPoint; Bitmap newBitmap = new Bitmap(Bitmap); linePoints.Add(bitmapPoint); DrawFreeLine(newBitmap, linePoints, new Pen(GetBoderBrush())); ShowBitmap(newBitmap); } void DrawFreeLine(Bitmap bitmap, List<Point> points, Pen pen) { if(points.Count < 2) return; Point fromPoint = points[0]; Point[] points1 = points.ToArray(); Graphics g = Graphics.FromImage(bitmap); if(EditMode == EditMode.SelectionFree) { foreach(Point toPoint in points1) { g.DrawLine(pen, fromPoint.X, fromPoint.Y, toPoint.X, toPoint.Y); fromPoint = toPoint; } } g.Dispose(); } } |
マウスボタンを離したときの処理
[範囲指定(自由曲線)]が選択されているときにマウスボタンが離されたときの処理を考えます。範囲選択が完了していない場合はOnMouseUpForSelectFreeAreaメソッドが呼ばれます。移動されているときで移動が完了した場合はOnMouseUpForMoveFreeLineSelectionが呼ばれます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
public partial class UserControlImage : UserControl { private void PictureBox1_MouseUp(object sender, MouseEventArgs e) { if(Bitmap == null) return; if(isMouseDown) { if(EditMode == EditMode.Selection) OnMouseUpForSelectRectangle(); if(EditMode == EditMode.DrawRectangle || EditMode == EditMode.FillRectangle || EditMode == EditMode.DrawEllipse || EditMode == EditMode.FillEllipse) OnMouseUpForDrawRectangle(); if(EditMode == EditMode.DrawLine) OnMouseUpForDrawLine(new Point(e.X + ScrollBarPosX, e.Y + ScrollBarPosY)); if(EditMode == EditMode.FreeLine) OnMouseUpForDrawFreeLine(new Point(e.X + ScrollBarPosX, e.Y + ScrollBarPosY)); if(EditMode == EditMode.SelectionFree) OnMouseUpForSelectFreeArea(new Point(e.X + ScrollBarPosX, e.Y + ScrollBarPosY)); return; } if(isMouseDownForMove) { if(EditMode == EditMode.Selection || EditMode == EditMode.FillRectangle) OnMouseUpForMoveSelection(new Point(e.X + ScrollBarPosX, e.Y + ScrollBarPosY)); if(EditMode == EditMode.SelectionFree) OnMouseUpForMoveFreeLineSelection(new Point(e.X + ScrollBarPosX, e.Y + ScrollBarPosY)); } if(isMouseDownForSizeChange) OnMouseUpForSizeChangeSelection(new Point(e.X + ScrollBarPosX, e.Y + ScrollBarPosY)); return; } } |
OnMouseUpForSelectFreeAreaメソッド
OnMouseUpForSelectFreeAreaメソッドは単に自由曲線を描画する処理とは違う部分があります。自由曲線でかこまれた境界線を保存します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
public partial class UserControlImage : UserControl { void OnMouseUpForSelectFreeArea(Point bitmapPoint) { isMouseDown = false; endPoint = bitmapPoint; linePoints.Add(bitmapPoint); Bitmap newBitmap = new Bitmap(Bitmap); DrawFreeLine(newBitmap, linePoints, new Pen(GetBoderBrush())); ShowBitmap(newBitmap); // 自由曲線でかこまれた境界線を保存する borderPoints = GetBorderPointsFromFreeLine(Bitmap, linePoints); linePoints.Clear(); startPoint = new Point(-1, -1); endPoint = new Point(-1, -1); } } |
GetBorderPointsFromFreeLineメソッド
GetBorderPointsFromFreeLineメソッドは境界線を構成する点を取得するメソッドです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
public partial class UserControlImage : UserControl { List<Point> GetBorderPointsFromFreeLine(Bitmap bitmap, List<Point> points) { Bitmap tempBitmap = new Bitmap(bitmap.Width, bitmap.Height); Graphics g = Graphics.FromImage(tempBitmap); g.Clear(Color.White); Point fromPoint = points[0]; Point[] points1 = points.ToArray(); foreach(Point toPoint in points1) { g.DrawLine(new Pen(Color.Black), fromPoint.X, fromPoint.Y, toPoint.X, toPoint.Y); fromPoint = toPoint; } g.Dispose(); List<Point> borders = new List<Point>(); for(int x = 0; x < tempBitmap.Width; x++) { for(int y = 0; y < tempBitmap.Height; y++) { Color color = tempBitmap.GetPixel(x, y); if(color.ToArgb() == Color.Black.ToArgb()) { borders.Add(new Point(x, y)); } } } tempBitmap.Dispose(); return borders; } } |
以上、自由曲線で範囲選択をするときの処理でした。次回は実際に移動させます。