選択した領域を拡大したり縮小する機能を追加してみました。「猫」という字を拡大しています。
ラジオボタンで[範囲選択]が選択されていてCtrlボタンが押されているときに境界線をドラッグしたときにサイズ変更ができるようにしています。
条件は3つすべてを満たすときです。
[範囲選択]が選択されている
Ctrlボタンが押されている
クリックされているのは境界線のときだけ
1 2 3 4 5 |
public partial class Form1 : Form { // サイズ変更かどうかを示すフラグ public bool isSizeChange = false; } |
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 |
public class PanelEx : Panel { public delegate void XYHandler(object sender, XY e); public event XYHandler BeginSelectSizeChange; public event XYHandler EndSelectSizeChange; private void PanelEx_MouseDown(object sender, MouseEventArgs e) { Form1 f = (Form1)FindForm(); f.SaveOldBitmap(); f.isSizeChange = false; // クリックされたらフラグを戻す // f.GetCheckedRadioButton()がForm1.CheckedRadioButton.Selection以外のときは省略 if(f.GetCheckedRadioButton() == Form1.CheckedRadioButton.Selection) { BeginSelection?.Invoke(this, new XY(Column, Row)); DoDragDrop(0, DragDropEffects.All); if(!f.isSizeChange) EndSelection?.Invoke(this, new XY(-1, -1)); else EndSelectSizeChange?.Invoke(this, new XY(-1, -1)); } } } |
コントロールキーがおされているかどうかは
1 |
Control.ModifierKeys.HasFlag(Keys.Control) == true |
かどうかを調べればわかりますが、PanelEx_MouseDownの中で調べるとクリックされてからCtrlボタンが押された場合に検出することができません。そこでDragOverイベントがおきたときに調べます。また途中でCtrlボタンをはなすと選択範囲の拡大ではなく移動の処理がはじまってしまうので、いったん領域の拡大縮小の処理がはじまったらマウスボタンから手がはなれるまで拡大縮小の処理のみをおこなうことにします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
public class PanelEx : Panel { private void PanelEx_DragOver(object sender, DragEventArgs e) { e.Effect = DragDropEffects.All; Form1 f = (Form1)FindForm(); if(!f.isSizeChange && Control.ModifierKeys.HasFlag(Keys.Control)) { // Ctrlキーが押されていて、フラグがセットされていない場合、イベントを発生させる BeginSelectSizeChange?.Invoke(this, new XY(Column, Row)); } if(f.GetCheckedRadioButton() == Form1.CheckedRadioButton.Free) { this.BackColor = f.CurColor; } else DragOverPanel?.Invoke(this, new XY(Column, Row)); } } |
BeginSelectSizeChangeイベントが発生したら以下の処理をおこないます。
まず拡大縮小される前の矩形の場所とサイズを調べて、フィールド変数に保存しています。
ドラッグによってサイズ変更されるのは、幅または高さだけなのか両方なのかはドラッグの開始が矩形の辺なのか角なのかで変わってきます。そこで矩形の左上、左下、右上、右下はサイズ変更時に固定されるのか変動するのかを最初に判定しています。
そのあと矩形の中の色をビットマップにして保存しています。これを矩形にあわせて拡大縮小すればいいわけです。
ドラッグされているあいだ変更後のサイズがわかるように矩形を描画することになりますが、このときに色が変わるパネルはSizeChangeSelectionXYsのなかに格納しています。
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 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
public partial class Form1 : Form { Rectangle BeforeSizeChangeRectangle = new Rectangle(0,0,0,0); bool isFixTopLeft = true; bool isFixTopRight = true; bool isFixBottomLeft = true; bool isFixBottomRight = true; Bitmap oldSelectionBitmap = null; private void Box_BeginSelectSizeChange(object sender, XY e) { isSizeChange = true; // 移動の処理はキャンセル SelectionMoveStartX = -1; SelectionMoveStartY = -1; // 拡大縮小される前の長方形の場所とサイズを調べる int top = SelectionXYs.Min(x => x.Y); int bottom = SelectionXYs.Max(x => x.Y); int left = SelectionXYs.Min(x => x.X); int right = SelectionXYs.Max(x => x.X); // 得られた長方形を保存 BeforeSizeChangeRectangle = new Rectangle(left, top, right - left+1, bottom - top+1); // 左上、左下、右上、右下はサイズ変更時に固定されるのか変動するのか isFixTopLeft = true; isFixTopRight = true; isFixBottomLeft = true; isFixBottomRight = true; if(e.X == right) { isFixTopRight = false; isFixBottomRight = false; } if(e.X == left) { isFixTopLeft = false; isFixBottomLeft = false; } if(e.Y == bottom) { isFixBottomLeft = false; isFixBottomRight = false; } if(e.Y == top) { isFixTopLeft = false; isFixTopRight = false; } // 現在の状態を保存 SaveOldBitmap(); ClearSelectionForSaveUndobuf(); // 矩形の中の色をビットマップにして保存 if(oldSelectionBitmap != null) oldSelectionBitmap.Dispose(); oldSelectionBitmap = new Bitmap(BeforeSizeChangeRectangle.Width, BeforeSizeChangeRectangle.Height); for(int row = 0; row < 32; row++) { for(int colum = 0; colum < 32; colum++) { if(BeforeSizeChangeRectangle.X > colum) continue; if(BeforeSizeChangeRectangle.Y > row) continue; if(colum >= BeforeSizeChangeRectangle.X + BeforeSizeChangeRectangle.Width) continue; if(row >= BeforeSizeChangeRectangle.Y + BeforeSizeChangeRectangle.Height) continue; try{ oldSelectionBitmap.SetPixel( colum - BeforeSizeChangeRectangle.X, row - BeforeSizeChangeRectangle.Y, panels[colum, row].BackColor); } catch { int a = colum - BeforeSizeChangeRectangle.X; int b = row - BeforeSizeChangeRectangle.Y; string s = String.Format("{0} {1} {2} {3}", a, b, colum, row); MessageBox.Show(s); } } } SizeChangeSelectionXYs = SelectionXYs.ToList(); } } |
ドラッグされている最中は以下の処理がおこなわれます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public partial class Form1 : Form { private void Box_DragOverPanel(object sender, XY e) { if(GetCheckedRadioButton() == CheckedRadioButton.Free) return; if(isSizeChange) { // サイズ変更の処理はOnSelectionSizeChangingで行なう。 OnSelectionSizeChanging(e); return; } // それ以外の場合の処理は省略 } } |
問題のOnSelectionSizeChangingメソッドですが、長なってしまった・・・
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 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 |
public partial class Form1 : Form { void OnSelectionSizeChanging(XY e) { if(e.X == -1) { return; } int top = SelectionXYs.Min(x => x.Y); int bottom = SelectionXYs.Max(x => x.Y); int left = SelectionXYs.Min(x => x.X); int right = SelectionXYs.Max(x => x.X); foreach(XY xy in SizeChangeSelectionXYs) { panels[xy.X, xy.Y].BackColor = xy.oldColor; } // 固定されているのはどこか? if(isFixTopLeft) { if(isFixTopRight) { int heightExtend = e.Y - bottom; Rectangle newRectangle = new Rectangle(new Point(BeforeSizeChangeRectangle.X, BeforeSizeChangeRectangle.Y), new Size(BeforeSizeChangeRectangle.Width, BeforeSizeChangeRectangle.Height + heightExtend)); SetPanelColerOnSelectionSizeChanging(oldSelectionBitmap, newRectangle); var xys = GetSelectedPanels(new Rectangle(left, top, right - left, bottom - top + heightExtend)); foreach(XY xY in xys) { xY.oldColor = panels[xY.X, xY.Y].BackColor; panels[xY.X, xY.Y].BackColor = Color.Tan; } SizeChangeSelectionXYs = xys; } else if(isFixBottomLeft) { // これだけ右に伸びた int widthExtend = e.X - right; Bitmap newBitmap = new Bitmap(32, 32); Graphics graphics = Graphics.FromImage(newBitmap); Rectangle newRectangle = new Rectangle(new Point(BeforeSizeChangeRectangle.X, BeforeSizeChangeRectangle.Y), new Size(BeforeSizeChangeRectangle.Width + widthExtend, BeforeSizeChangeRectangle.Height)); SetPanelColerOnSelectionSizeChanging(oldSelectionBitmap, newRectangle); var xys = GetSelectedPanels(new Rectangle(left, top, right - left + widthExtend, bottom - top)); foreach(XY xY in xys) { xY.oldColor = panels[xY.X, xY.Y].BackColor; panels[xY.X, xY.Y].BackColor = Color.Tan; } SizeChangeSelectionXYs = xys; } else { // 左上を起点にこれだけ伸びた int widthExtend = e.X - right; int heightExtend = e.Y - bottom; Bitmap newBitmap = new Bitmap(32, 32); Graphics graphics = Graphics.FromImage(newBitmap); Rectangle newRectangle = new Rectangle(new Point(BeforeSizeChangeRectangle.X, BeforeSizeChangeRectangle.Y), new Size(BeforeSizeChangeRectangle.Width + widthExtend, BeforeSizeChangeRectangle.Height + heightExtend)); SetPanelColerOnSelectionSizeChanging(oldSelectionBitmap, newRectangle); var xys = GetSelectedPanels(new Rectangle(left, top, right - left + widthExtend, bottom - top + heightExtend)); foreach(XY xY in xys) { xY.oldColor = panels[xY.X, xY.Y].BackColor; panels[xY.X, xY.Y].BackColor = Color.Tan; } SizeChangeSelectionXYs = xys; } } else if(isFixBottomRight) { if(isFixTopRight) { // これだけ左に伸びた int widthExtend = left - e.X; Bitmap newBitmap = new Bitmap(32, 32); Graphics graphics = Graphics.FromImage(newBitmap); Rectangle newRectangle = new Rectangle(new Point(BeforeSizeChangeRectangle.X - widthExtend, BeforeSizeChangeRectangle.Y), new Size(BeforeSizeChangeRectangle.Width + widthExtend, BeforeSizeChangeRectangle.Height)); SetPanelColerOnSelectionSizeChanging(oldSelectionBitmap, newRectangle); var xys = GetSelectedPanels(new Rectangle(left - widthExtend, top, right - left + widthExtend, bottom - top)); foreach(XY xY in xys) { xY.oldColor = panels[xY.X, xY.Y].BackColor; panels[xY.X, xY.Y].BackColor = Color.Tan; } SizeChangeSelectionXYs = xys; } else if(isFixBottomLeft) { // これだけ上に伸びた int heightExtend = top - e.Y; Bitmap newBitmap = new Bitmap(32, 32); Graphics graphics = Graphics.FromImage(newBitmap); Rectangle newRectangle = new Rectangle(new Point(BeforeSizeChangeRectangle.X, BeforeSizeChangeRectangle.Y - heightExtend), new Size(BeforeSizeChangeRectangle.Width, BeforeSizeChangeRectangle.Height + heightExtend)); SetPanelColerOnSelectionSizeChanging(oldSelectionBitmap, newRectangle); var xys = GetSelectedPanels(new Rectangle(left, top - heightExtend, right - left, bottom - top + heightExtend)); foreach(XY xY in xys) { xY.oldColor = panels[xY.X, xY.Y].BackColor; panels[xY.X, xY.Y].BackColor = Color.Tan; } SizeChangeSelectionXYs = xys; } else { // 左下を起点にこれだけ伸びた int widthExtend = left - e.X; int heightExtend = top - e.Y; Bitmap newBitmap = new Bitmap(32, 32); Graphics graphics = Graphics.FromImage(newBitmap); Rectangle newRectangle = new Rectangle(new Point(BeforeSizeChangeRectangle.X - widthExtend, BeforeSizeChangeRectangle.Y - heightExtend), new Size(BeforeSizeChangeRectangle.Width + widthExtend, BeforeSizeChangeRectangle.Height + heightExtend)); SetPanelColerOnSelectionSizeChanging(oldSelectionBitmap, newRectangle); var xys = GetSelectedPanels( new Rectangle(left - widthExtend, top - heightExtend, right - left + widthExtend, bottom - top + heightExtend)); foreach(XY xY in xys) { xY.oldColor = panels[xY.X, xY.Y].BackColor; panels[xY.X, xY.Y].BackColor = Color.Tan; } SizeChangeSelectionXYs = xys; } } else if(isFixTopRight) { // 右上を起点にこれだけ伸びた int widthExtend = left - e.X; int heightExtend = e.Y - bottom; Bitmap newBitmap = new Bitmap(32, 32); Graphics graphics = Graphics.FromImage(newBitmap); Rectangle newRectangle = new Rectangle(new Point(BeforeSizeChangeRectangle.X - widthExtend, BeforeSizeChangeRectangle.Y), new Size(BeforeSizeChangeRectangle.Width + widthExtend, BeforeSizeChangeRectangle.Height + heightExtend)); SetPanelColerOnSelectionSizeChanging(oldSelectionBitmap, newRectangle); var xys = GetSelectedPanels( new Rectangle(left - widthExtend, top, right - left + widthExtend, bottom - top + heightExtend)); foreach(XY xY in xys) { xY.oldColor = panels[xY.X, xY.Y].BackColor; panels[xY.X, xY.Y].BackColor = Color.Tan; } SizeChangeSelectionXYs = xys; } else if(isFixBottomLeft) { // 左下を起点にこれだけ伸びた int widthExtend = e.X - right; int heightExtend = top - e.Y; Bitmap newBitmap = new Bitmap(32, 32); Graphics graphics = Graphics.FromImage(newBitmap); Rectangle newRectangle = new Rectangle(new Point(BeforeSizeChangeRectangle.X, BeforeSizeChangeRectangle.Y - heightExtend), new Size(BeforeSizeChangeRectangle.Width + widthExtend, BeforeSizeChangeRectangle.Height + heightExtend)); SetPanelColerOnSelectionSizeChanging(oldSelectionBitmap, newRectangle); var xys = GetSelectedPanels( new Rectangle(left, top - heightExtend, right - left + widthExtend, bottom - top + heightExtend)); foreach(XY xY in xys) { xY.oldColor = panels[xY.X, xY.Y].BackColor; panels[xY.X, xY.Y].BackColor = Color.Tan; } SizeChangeSelectionXYs = xys; } } } |
このOnSelectionSizeChangingメソッドのなかで使われているSetPanelColerOnSelectionSizeChangingメソッドとGetSelectedPanelsメソッドは以下のようになっています。
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 Form1 : Form { void SetPanelColerOnSelectionSizeChanging(Bitmap oldSelectionBitmap, Rectangle newRectangle) { Bitmap newBitmap = new Bitmap(32, 32); Graphics graphics = Graphics.FromImage(newBitmap); graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor; graphics.DrawImage(oldBitmap, new Rectangle(0, 0, 32, 32)); graphics.DrawImage(oldSelectionBitmap, newRectangle); graphics.Dispose(); List<XY> xys = GetPixelsChengedColor(newBitmap); newBitmap.Dispose(); foreach(XY xY in xys) { panels[xY.X, xY.Y].BackColor = xY.newColor; } xys.Clear(); } List<XY> GetSelectedPanels(Rectangle newRectangle) { Bitmap newSelectionBitmap = new Bitmap(32, 32); Graphics graphics = Graphics.FromImage(newSelectionBitmap); graphics.DrawRectangle(Pens.Black, newRectangle); graphics.Dispose(); var xys = GetPixelsChengedColor(newSelectionBitmap); newSelectionBitmap.Dispose(); return xys; } } |
SetPanelColerOnSelectionSizeChangingメソッド内に
1 |
graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor; |
とありますが、これは拡大処理によって画像が崩れてしまうのを防ぐためにあります。これがないと下のようになってしまいます。
「猫」という文字を拡大しているのですが、非常に見づらいです。
1 |
graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor; |
ニアレストネイバー法とは単純に元の画像を水増したり間引きする方式です。拡大するときはピクセル数を増やさないといけませんが、そのときに一番近くにある色を使おうというわけです。ジャギーが目立ちやすく階調のある画像のリサイズにはむいていないとされていますが、アイコンであればニアレストネイバー法は適していると思われます。
ほかにもバイキュービック法もあり、精度が高く階調のある画像のリサイズにむいているとされていますが、アイコンのような小さな画像を扱う場合には適していないように思われます(試してみたが引き延ばされた画像の色が薄くなる)。
最後はドロップ時の処理です。
ドラッグが終わってドロップされたらイベントが発生し、Form1のなかではイベントハンドラBox_EndSelectSizeChangeが呼ばれます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public class PanelEx : Panel { private void PanelEx_DragDrop(object sender, DragEventArgs e) { Form1 f = (Form1)FindForm(); // それ以外の場合の処理は省略 if(f.GetCheckedRadioButton() == Form1.CheckedRadioButton.Selection) { if(!f.isSizeChange) EndSelection?.Invoke(this, new XY(Column, Row)); else EndSelectSizeChange?.Invoke(this, new XY(Column, Row)); } // UndoとRedoができるようにする f.InsertUndobuf(); } } |
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 Form1 : Form { private void Box_EndSelectSizeChange(object sender, XY e) { if(e.X == -1) { SizeChangeSelectionXYs.Clear(); if(oldSelectionBitmap != null) { oldSelectionBitmap.Dispose(); oldSelectionBitmap = null; } return; } SelectionXYs.Clear(); SelectionXYs = SizeChangeSelectionXYs.ToList(); SizeChangeSelectionXYs.Clear(); oldSelectionBitmap.Dispose(); oldSelectionBitmap = null; } } |