前回作成した1ピクセル単位で編集できる画像エディタはあまり実用性があるものとはいえませんでした。今回はその点を改善します。

まずクリックで置き換えることができる色を指定できないと使い物になりません。そこで以下のような機能を追加することにしました。

点だけでなく直線、矩形、楕円なども描画できるようにする。
色を指定できるようにする。ColorDialogを使わずに画像上に使われている色も指定対象にできるようにする
マウスが移動したりクリックしたときに、そこがBitmapのどのピクセル座標なのかわかるようにする
現在選択されている色がどの色かわかるようにする

点だけを変更するだけでもこれらの改善点が思いつきます。

色を指定できるようにする

まずフィールド変数とコンストラクタを示します。

[色を生成する]をクリックすると編集に使える色を選択できます。

自分で色を作るのではなく隣のピクセルと同じ色にしたいときもあります。最後にクリックされた部分の色が保存されるので、[色をコピーする]をクリックことで選択された色として指定することができます。

RadioButtonで[点]が選択されている場合、クリックするとその部分の色が置き換わります。そのための処理を示します。

マウスが移動したりボタンが離されたときの処理はとくにありませんが、他の機能を追加するときに必要になるので追加しておきます。

直線を描画する

点だけでなく直線の描画もしてみたいものです。直線を描画するための処理をForm2クラス内に書くとクラスが肥大化するので別のクラスを作成します。

DrawLineクラス

DrawLineクラスを作成します。クラス内部の処理で必要になるのはImageEditPanel、直線の開始座標、直線の色と太さ、セルの大きさです。

DrawLineクラスのコンストラクタとプロパティを示します。

マウスボタンが離されるまで仮の直線を描画する

マウスをドラッグすると開始点からその座標にむけて直線が引かれます。しかしこの線は仮の直線で確定したものではありません。マウスを動かすと古い仮の直線は消されて現在のマウス上の直線を描画させます。

処理としてはマウスが移動してCurPointにPointがセットされるとこれを記憶しておき、ImageEditPanel.Invalidateメソッドを呼び出すことです。これによって仮の直線が描画されます。

実際に仮の直線が描画される処理を示します。実際にBitmapを作成して直線を描画し、色が変わった部分を拾い出すという方法を採用しているのですが、これだともとの画像サイズが大きい場合、処理に時間がかかります。そこで実際に直線が引かれる部分だけに限定して処理をおこなっています。

GetLeftMarginメソッドとGetTopMarginメソッドは直線が描画されている部分の左と上にどれだけ空白部分があるかを求めるためのものです。

それから位置が確定される前であれば直線を上下左右に移動することができるようにします。移動対象は始点と終点の両方、始点または終点の片方だけの3つから選べるようにします。

直線描画のためのマウスボタンが押されたときの処理

DrawLineクラスを用いた直線描画のための処理を示します。

まずはマウスボタンが押されたときの処理です。DrawLineのインスタンスを生成します。

直線描画のためのドラッグ時の処理

次にドラッグされているときの処理を示します。マウスの座標をCurPointプロパティにセットします。するとImageEditPaneの再描画がおこなわれます。

直線描画のためのドロップ時の処理

ドラッグが終わってドロップされたときに直線が確定するという仕様にしようと思ったのですが、予定を変更します。直線描画のためのドラッグが終わってもその段階では直線が確定せず、Enterキーが押されたときや別のところがクリックされたときに確定させることにします。そのためImageEditPictureBox_MouseUp内の処理はとくにありません。

確定時の処理

直線が確定したらそのときに色をつけるセルをChangeCellsに格納します。そのあとChangeCellsをつかって表示されているBitmapを変更し、ChangeCells内のデータをクリアします。ChangeCellsが空でないということはこのときに直線が確定したことを意味しています。そのあとDrawConfirmedメソッドを実行すれば直線が確定します。

ImageEditPictureBox_Paintにおける処理

描画処理の部分を示します。DrawLineがnullではなく残っている場合は直線は確定されていません。この場合はDrawLine.DrawTempLineメソッドを呼び出します。ここではPictureBoxUserControl.Bitmapの書き換えはおこなわれず、ImageEditPictureBox.Imageの変更もおこなわれません。

確定前の直線などを描画する処理を示します。

直線などが確定されるまえの仮の描画処理を示します。前述のDrawLine.DrawTempLineメソッドが呼び出され、仮の描画がおこなわれます。

これはセル同士の境界線を描画するためのメソッドです。ImageEditPictureBox_Paintを長くしたくないので独立したメソッドをして作り直しました。

確定前の直線の移動処理

位置を確定させるまえであれば直線の位置を変更することができるようにします。Enterキーを押すと確定されます。方向キーで全体を移動、方向キー+Shiftキーは始点部分のみを移動、方向キー+Ctrlキーは終点部分のみを移動します。また別の場所をクリックした場合も確定処理がおこなわれます。

確定処理をおこなうメソッドを示します。未確定の直線を確定させた場合はtrueを返します。ImageEditPictureBox_MouseDown内でこのメソッドがtrueを返した場合は確定処理以外はなにもしません。

確定前の直線を移動させる処理を示します。第一引数がtrueなら始点が、第二引数がtrueなら終点を移動させることができます。