でもやりましたが、指定した色を置換する機能を追加します。
色を指定するために色を記憶させる機能も追加します。
まず新しいユーザーコントロールを作成します。チェックボックスにチェックが入っている状態で画像をクリックするとその部分の色が保存され、ラベルにはその情報が表示されます。
これをフォームに貼り付けます。
ではユーザーコントロールをみてみましょう。
Colorプロパティをセットするとピクチャーボックスの背景色がかわり、ラベルには赤、緑、青の要素の値とアルファ値が表示されます。(アルファ値はつねに255になる)
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 UserControlColorBox : UserControl { public UserControlColorBox() { InitializeComponent(); pictureBox1.BackColor = Color.Empty; label1.Text = ""; } public bool IsMemoryColor { get { return checkBox1.Checked; } } public Color Color { set { pictureBox1.BackColor = value; label1.Text = String.Format("R={0}, G={1}, B={2}, A={3}", value.R, value.G, value.B, value.A); } get { return pictureBox1.BackColor; } } public string ColorString { get { return label1.Text; } } } |
色を保存する処理が必要です。画像が表示されているときにユーザーコントロールUserControlImageがクリックされたらその部分の色を調べてメインフォームでイベントを発生させます。
PictureBoxMouseDownイベントはユーザーコントロールUserControlImageでクリックされた部分の色を取得してこれを伝えます。
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 |
public partial class UserControlImage : UserControl { public delegate void PictureBoxMouseDownHandler(object sender, PictureBox1MouseDownArgs e); public event PictureBoxMouseDownHandler PictureBoxMouseDown; public class PictureBox1MouseDownArgs : EventArgs { public PictureBox1MouseDownArgs(int x, int y, Color color) { X = x; Y = y; Color = color; } public int X { get; private set; } public int Y { get; private set; } public Color Color { get; private set; } } private void PictureBox1_MouseDown(object sender, MouseEventArgs e) { if(Bitmap == null) return; if(e.X + ScrollBarPosX > Bitmap.Width || e.Y + ScrollBarPosY > Bitmap.Height) OnMouseDownOutOfBitmap(); return; } // スクロールバー位置より元画像をトリミングして取得する Bitmap bitmap = GetBitmapDrawPictureBox(); Color color = bitmap.GetPixel(e.X, e.Y); PictureBoxMouseDown?.Invoke(this, new PictureBox1MouseDownArgs(e.X, e.Y, color)); if(BitmapRectangle == null) OnMouseDownForSelectRectangle(new Point(e.X + ScrollBarPosX, e.Y + ScrollBarPosY)); else OnMouseDownOutOfSelection(new Point(e.X + ScrollBarPosX, e.Y + ScrollBarPosY)); } } |
あとはメインフォーム側で自作したイベントPictureBoxMouseDownを捕捉すればユーザーコントロールUserControlColorBoxに選択された色を保存することができます。
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 |
public partial class Form1 : Form { public Form1() { InitializeComponent(); userControlImage1.PictureBoxMouseDown += UserControlImage1_PictureBoxMouseDown; // サイズ変更されたときの対策 userControlColorBox1.Anchor = AnchorStyles.Top | AnchorStyles.Right; userControlColorBox2.Anchor = AnchorStyles.Top | AnchorStyles.Right; userControlColorBox3.Anchor = AnchorStyles.Top | AnchorStyles.Right; ShowInitBitmap(); } private void UserControlImage1_PictureBoxMouseDown(object sender, UserControlImage.PictureBox1MouseDownArgs e) { if(userControlColorBox1.IsMemoryColor) userControlColorBox1.Color = e.Color; if(userControlColorBox2.IsMemoryColor) userControlColorBox2.Color = e.Color; if(userControlColorBox3.IsMemoryColor) userControlColorBox3.Color = e.Color; } } |
次に色を置換するためのダイアログをつくります。
コンボボックスにはメインフォームで選択した色が表示されます。コンボボックスかカラーダイアログをつかって色を指定します。
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 |
public partial class ColorReplaceForm : Form { public List<Color> Colors = new List<Color>(); public Color BeforeColor = Color.Empty; public Color AfterColor = Color.Empty; public bool IsAfterColorTransparent = false; public ColorReplaceForm() { InitializeComponent(); this.Load += ColorReplaceForm_Load; // OK、キャンセルボタンが押されたらDialogResultを返す buttonOK.DialogResult = DialogResult.OK; buttonCancel.DialogResult = DialogResult.Cancel; // OK、キャンセルボタンが押されたら値を保存する buttonOK.Click += ButtonOK_Click; // 置換後の色として透明が選択された場合 checkBox1.CheckedChanged += CheckBox1_CheckedChanged; checkBox2.CheckedChanged += CheckBox2_CheckedChanged; // カラーダイアログをつかって色を指定する buttonSelectBeforeColor.Click += ButtonSelectBeforeColor_Click; buttonSelectAfterColor.Click += ButtonSelectAfterColor_Click; // コンボボックスから色を選択する BeforeColorComboBox.SelectedIndexChanged += BeforeColorComboBox_SelectedIndexChanged; AfterColorComboBox.SelectedIndexChanged += AfterColorComboBox_SelectedIndexChanged; } } |
メインフォームで保存された色があるのであればコンボボックスに表示させます。
1 2 3 4 5 6 7 8 9 10 11 12 |
public partial class ColorReplaceForm : Form { private void ColorReplaceForm_Load(object sender, EventArgs e) { foreach(Color color in Colors) { string str = String.Format("R={0}, G={1}, B={2}, A={3}", color.R, color.G, color.B, color.A); BeforeColorComboBox.Items.Add(str); AfterColorComboBox.Items.Add(str); } } } |
コンボボックスで項目が指定されたらその色をピクチャーボックスに表示させます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public partial class ColorReplaceForm : Form { private void BeforeColorComboBox_SelectedIndexChanged(object sender, EventArgs e) { int index = BeforeColorComboBox.SelectedIndex; pictureBox1.BackColor = Colors[index]; } private void AfterColorComboBox_SelectedIndexChanged(object sender, EventArgs e) { int index = AfterColorComboBox.SelectedIndex; pictureBox2.BackColor = Colors[index]; } } |
[カラーダイアログから選ぶ]ボタンがクリックされたらカラーダイアログを表示させて色を選択できるようにします。
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 ColorReplaceForm : Form { private void ButtonSelectBeforeColor_Click(object sender, EventArgs e) { ColorDialog dlg = new ColorDialog(); if(dlg.ShowDialog() == DialogResult.OK) { pictureBox1.BackColor = dlg.Color; } dlg.Dispose(); } private void ButtonSelectAfterColor_Click(object sender, EventArgs e) { ColorDialog dlg = new ColorDialog(); if(dlg.ShowDialog() == DialogResult.OK) { pictureBox2.BackColor = dlg.Color; } dlg.Dispose(); } } |
[透明にする]チェックボックスがチェックされたら置換後の色は透明になります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public partial class ColorReplaceForm : Form { private void CheckBox1_CheckedChanged(object sender, EventArgs e) { if(checkBox1.Checked) pictureBox1.BackColor = Color.Empty; } private void CheckBox2_CheckedChanged(object sender, EventArgs e) { if(checkBox2.Checked) pictureBox2.BackColor = Color.Empty; } } |
[OK]ボタンがクリックされたらダイアログのコントロールからデータを収集して終了します。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public partial class ColorReplaceForm : Form { private void ButtonOK_Click(object sender, EventArgs e) { BeforeColor = pictureBox1.BackColor; AfterColor = pictureBox2.BackColor; if(checkBox1.Checked) IsBeforeColorTransparent = true; if(checkBox2.Checked) IsAfterColorTransparent = true; } } |
実際に色を置換する処理を追加します。
メニューの[色の置換]が選択されたら上で作成したダイアログを表示させます。ダイアログが[OK]ボタンがおされることで終了したら必要なデータを取得して色を置換します。
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 |
public partial class Form1 : Form { private void ColorReplaceMenuItem_Click(object sender, EventArgs e) { if(userControlImage1.Bitmap == null) { MessageBox.Show("画像が読み込まれていません", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } ColorReplaceForm form = new ColorReplaceForm(); form.Text = "色の置換(画像全体)"; List<Color> colors = new List<Color>(); if(userControlColorBox1.Color != Color.Empty) colors.Add(userControlColorBox1.Color); if(userControlColorBox2.Color != Color.Empty) colors.Add(userControlColorBox2.Color); if(userControlColorBox3.Color != Color.Empty) colors.Add(userControlColorBox3.Color); form.Colors = colors; if(form.ShowDialog() == DialogResult.OK) { userControlImage1.ReplaceColor(form.BeforeColor, form.AfterColor, form.IsBeforeColorTransparent, form.IsAfterColorTransparent, form.differenceR, form.differenceG, form.differenceB); } form.Dispose(); } } |
UserControlImageクラス側では以下のような処理をおこないます。GetBitmapReplaceColorメソッドで色を置換したビットマップを取得してこれをBitmapプロパティに渡すとともにShowBitmapメソッドでこれを表示させます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public partial class UserControlImage : UserControl { public bool ReplaceColor(Color oldColor, Color newColor, bool isOldColorTransparent, bool isNewColorTransparent) { if(BitmapRectangle != null) UniteBitmapRectangle(new Point()); Bitmap bmp = Bitmap; if(isOldColorTransparent) oldColor = Color.FromArgb(0, 0, 0, 0); if(isNewColorTransparent) newColor = Color.FromArgb(0, 0, 0, 0); Bitmap = GetBitmapReplaceColor(bmp, oldColor, newColor); ShowBitmap(Bitmap); return true; } } |
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 |
public partial class UserControlImage : UserControl { Bitmap GetBitmapReplaceColor(Bitmap sourceBmp, Color beforeColor, Color afterColor) { int width = sourceBmp.Width; int height = sourceBmp.Height; Bitmap newBitmap = new Bitmap(width, height); for(int x = 0; x < width; x++) { for(int y = 0; y < height; y++) { Color color = sourceBmp.GetPixel(x, y); // 透明と黒を混同しないようにする if(beforeColor != Color.FromArgb(0, 0, 0, 0) && color != Color.FromArgb(0, 0, 0, 0)) newBitmap.SetPixel(x, y, afterColor); else if(beforeColor == Color.FromArgb(0, 0, 0, 0) && color == Color.FromArgb(0, 0, 0, 0)) newBitmap.SetPixel(x, y, afterColor); else newBitmap.SetPixel(x, y, color); } } return newBitmap; } } |
範囲選択されている部分だけ置換処理
それから範囲選択されている部分だけ置換処理をすることもできるようにします。
まずはForm側の処理です。
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 |
public partial class Form1 : Form { private void SelectionColorReplaceMenuItem_Click(object sender, EventArgs e) { if(userControlImage1.Bitmap == null) { MessageBox.Show("画像が読み込まれていません", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } if(!userControlImage1.IsSelected()) { MessageBox.Show("範囲選択されていません", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } ColorReplaceForm form = new ColorReplaceForm(); form.Text = "色の置換(選択されている部分のみ)"; List<Color> colors = new List<Color>(); if(userControlColorBox1.Color != Color.Empty) colors.Add(userControlColorBox1.Color); if(userControlColorBox2.Color != Color.Empty) colors.Add(userControlColorBox2.Color); if(userControlColorBox3.Color != Color.Empty) colors.Add(userControlColorBox3.Color); form.Colors = colors; if(form.ShowDialog() == DialogResult.OK) { userControlImage1.ReplaceColorInRectangle( form.BeforeColor, form.AfterColor, form.IsBeforeColorTransparent, form.IsAfterColorTransparent, form.differenceR, form.differenceG, form.differenceB); } form.Dispose(); } } |
UserControlImageクラス側ではこのような処理をおこないます。置換後の色が透明である場合、これを元のビットマップに重ねても下の色が透けてしまい、うまくいきません。そこで新しくSetSelectionTransparentメソッドを作成しています。
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 |
public partial class UserControlImage : UserControl { public bool ReplaceColorInRectangle(Color oldColor, Color newColor, bool isOldColorTransparent, bool isNewColorTransparent) { if(BitmapRectangle == null) return false; if(isOldColorTransparent) oldColor = Color.FromArgb(0, 0, 0, 0); if(isNewColorTransparent) newColor = Color.FromArgb(0, 0, 0, 0); Bitmap oldSelectedBitmap = BitmapRectangle.Bitmap; Rectangle rect = BitmapRectangle.Rectangle; Bitmap newSelectedBitmap; Bitmap newBitmap; if(isNewColorTransparent) { newBitmap = SetSelectionTransparent(rect, oldColor, differenceR, differenceG, differenceB); } else { newSelectedBitmap = GetBitmapReplaceColor(oldSelectedBitmap, oldColor, newColor); BitmapRectangle.Bitmap = newSelectedBitmap; newBitmap = new Bitmap(Bitmap); Graphics g = Graphics.FromImage(newBitmap); Rectangle srcRect = new Rectangle(0, 0, rect.Width, rect.Height); g.DrawImage(newSelectedBitmap, rect, srcRect, GraphicsUnit.Pixel); g.Dispose(); } Bitmap = newBitmap; Bitmap bitmap2 = DrawBoderRectangle(Bitmap); ShowBitmap(bitmap2); return true; } } |
矩形内だけ透明にすることはできないので全体を作り直します。GetPixelメソッドで取得した色が置換したい色であればSetPixelメソッドでColor.FromArgb(0,0,0,0)にします。これで画像の一部のみを透明にさせることができるようになります。
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 |
public partial class UserControlImage : UserControl { public Bitmap SetSelectionTransparent(Rectangle rect, Color beforeColor) { Bitmap bmp = Bitmap; int width = bmp.Width; int height = bmp.Height; Bitmap newBmp = new Bitmap(width, height); Bitmap newRectBmp = new Bitmap(rect.Width, rect.Height); for(int x = 0; x < width; x++) { for(int y = 0; y < height; y++) { Color color = bmp.GetPixel(x, y); if((x < rect.Left || rect.Right <= x) || (y < rect.Top || rect.Bottom <= y)) { newBmp.SetPixel(x, y, color); } else { if(beforeColor.ToArgb() == color.ToArgb()) { newBmp.SetPixel(x, y, Color.FromArgb(0, 0, 0, 0)); newRectBmp.SetPixel(x - rect.X, y - rect.Y, Color.FromArgb(0, 0, 0, 0)); } else newBmp.SetPixel(x, y, color); } } } BitmapRectangle.Bitmap = newRectBmp; return newBmp; } } |
最後にメニュー[色の置換(選択されている部分だけ)]が選択されたときの処理を示します。
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 |
public partial class Form1 : Form { private void SelectionColorReplaceMenuItem_Click(object sender, EventArgs e) { if(userControlImage1.Bitmap == null) { MessageBox.Show("画像が読み込まれていません", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } if(!userControlImage1.IsSelected()) { MessageBox.Show("範囲選択されていません", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } ColorReplaceForm form = new ColorReplaceForm(); form.Text = "色の置換(選択されている部分のみ)"; List<Color> colors = new List<Color>(); if(userControlColorBox1.Color != Color.Empty) colors.Add(userControlColorBox1.Color); if(userControlColorBox2.Color != Color.Empty) colors.Add(userControlColorBox2.Color); if(userControlColorBox3.Color != Color.Empty) colors.Add(userControlColorBox3.Color); form.Colors = colors; if(form.ShowDialog() == DialogResult.OK) { userControlImage1.ReplaceColorInRectangle( form.BeforeColor, form.AfterColor, form.IsBeforeColorTransparent, form.IsAfterColorTransparent, form.differenceR, form.differenceG, form.differenceB); } form.Dispose(); } } |