画像の加工をするときによくWindowsに標準搭載されているPaintを使いますが(Photoshop高いし・・・)、細かいところを修正するときは使いにくいです。小さな画像を加工するときはとくに使いにくい・・・、ということで自作することにしました。今回作成するのは高機能なエディタではなく、1ピクセル単位で編集することに特化した画像エディタです(需要あるかな?)。

まずは編集したい画像を普通に表示させ、編集したい部分を拡大表示させます。まずはここまでやってみましょう。

PictureBoxUserControlクラス

最初にユーザーコントロールを作成します。原寸大表示とコントロール内に収まるように縮小表示させるPictureBoxのようなものをつくります。実態はユーザーコントロールのうえにPictureBoxを貼り付けているだけです。

クラス名はPictureBoxUserControlにしました。原寸大表示をした場合、スクロールバーが表示されるようにします。

コンストラクタを示します。ユーザーコントロールとPictureBoxを同じサイズにしたいのですが、pictureBox1.Dock = DockStyle.Fillとやるとスクロールバーが表示されません。ユーザーコントロールとPictureBoxを同じサイズにする処理はDockStyle.Fill以外の方法でおこないます。

実寸大表示と縮小表示

ShowActualSizeプロパティは実寸大表示をするかコントロール内に収まるように縮小して表示するかを設定するためのものです。実寸大表示をさせるのであればPictureBoxのSizeModeはPictureBoxSizeMode.AutoSizeに、そうでない場合はPictureBoxSizeMode.Normalにします。

PictureBox.SizeModeを変更したらBitmapプロパティ(後述)を設定したあとAutoScrollのtrueとfalseを切り替えます。

縮小表示のとき縦横比を維持する

Bitmapプロパティを示します。実寸大表示をする場合はそのままPictureBox.Imageにセットしてしまっていいのですが、縮小表示の場合は縮小したBitmapを作成してこれをセットします。GetExpansionRateメソッドはどれだけ縮小するかを計算するためのものです。

縮小表示のときにどれだけ縮小するかを計算するGetExpansionRateメソッドを示します。コントロールの幅高さとBitmapの幅高さから拡大率を求めます。そして少ないほうを採用します。この値が1を超えている場合は縮小する必要がないので1を返します。

リサイズに対応させる

コントロールがリサイズされたときの処理を示します。Bitmapが存在し、縮小表示されている場合はコントロールのサイズ変更に伴って縮小率が変わる場合があります。縮小率の計算はBitmapプロパティがやってくれるので、ここではpictureBoxのサイズ変更をして、もう一度Bitmapプロパティをセットしなおしています。

クリックイベントに対応させる

PictureBoxがクリックされたときにイベントを発生させてForm1クラスからこれに対する処理ができるようにしています。イベントハンドラの引数はクリックされた座標ではなくクリックされた場所にあるピクセルです。

PictureBoxがダブルクリックされたときにも対応できるようにイベントを発生させています。

Form1クラスの処理

次にForm1クラスの処理を考えます。

最初は原寸大表示

コンストラクタを示します。ダブルクリックされたら編集用のフォームを表示させたいのでイベントハンドラを追加しています。また最初は「原寸大表示」が選択されています。

表示モードの切り替えに対応させる

メニューで原寸大表示または縮小表示が選択されたらPictureBoxUserControl.ShowActualSizeプロパティをtrueまたはfalseにして、メニューのチェック状態を変更します。

画像ファイルを開く/保存する

メニューから開くが選択されたら選択されたファイルからBitmapを取得して、これをpictureBoxUserControl1.Bitmapプロパティにセットします。ファイルの保存が選択されたらpictureBoxUserControl1.BitmapプロパティからBitmapを取得してこれを保存します。

ダブルクリックされたらその部分を拡大して1ピクセル単位の編集ができるようにします。

ImageEditPictureBoxクラス

1ピクセル単位の編集をするためのコントロールはどうやってつくればよいでしょうか?

PictureBoxに拡大した画像を描画してクリックするとその部分の色が変わるようなものをつくります。

外側にあるのは普通のPanelです。内側にあるのはPictureBoxを継承して作成したコントロールです。名前はImageEditPictureBoxとします。PictureBoxをそのままではなく継承したのはDoubleBufferedプロパティを使うためです。クラスの外からPanel.DoubleBuffered = true;とやるのはエラーになってしまうのです。

ImageEditPictureBoxクラスはこれだけです。次にForm2クラスを考えます。

Form2クラスの処理

コンストラクタのなかでImageEditPictureBoxがクリックされたときと再描画されるときの処理ができるようにイベントハンドラを追加します。またImageEditPictureBoxのサイズが大きくなったときスクロールバーが表示されるようにImageEditPictureBoxの親のPanelにAutoScroll = trueを設定します。

ImageEditPanelのサイズ変更

フォームがロードされたらPictureBoxUserControl.Bitmapを読み込んでCellのリストを作成します。それと同時にImageEditPictureBoxのサイズを変更します。PictureBoxUserControl.Bitmapのサイズの10倍です。

Cellクラスはこのようになっています。

ImageEditPictureBoxがクリックされたときの処理

ImageEditPictureBoxでクリックされたときの処理を示します。クリックされた部分に相当するBitmapのピクセルを白に変更するとともにForm2に表示するPictureBox用のBitmapも変更しています。

ImageEditPictureBox.Invalidateメソッドを実行すると再描画がおこなわれ、変更されたBitmapが反映されます。

クリックされた部分を白に変更するだけではあまり実用的ではありませんが、次回以降、実用性のあるアプリケーションへと改良していきます。

最後にForm1クラスのPictureBoxUserControlがダブルクリックされたときの処理を示します。