これまでは画像ファイルからレイヤーをつくり、そのまま貼り付けていました。今回はレイヤー上の画像をトリミングしたり回転させます。
これは画像を回転させたりトリミングするためのクラスです。
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 |
static public class BitmapEx { static public Bitmap RotateBitmap(Bitmap sourceBitmap, int angle, bool isHighQuality) { List<Point> points = new List<Point>(); points.Add(new Point(0, 0)); points.Add(new Point(sourceBitmap.Width, 0)); points.Add(new Point(0, sourceBitmap.Height)); points = points.Select(x => new Point(x.X - sourceBitmap.Width / 2, x.Y - sourceBitmap.Height / 2)).ToList(); { double rad = Math.PI * angle / 180; List<Point> points2 = new List<Point>(); foreach (Point pt in points) { int x = (int)(pt.X * Math.Cos(rad) + pt.Y * Math.Sin(rad)); int y = (int)(-pt.X * Math.Sin(rad) + pt.Y * Math.Cos(rad)); points2.Add(new Point(x, y)); } points = points2; } int widthHalf = points.Max(x => Math.Abs(x.X)); int heightHalf = points.Max(x => Math.Abs(x.Y)); int newWidth = widthHalf * 2; int newHeight = heightHalf * 2; points = points.Select(x => new Point(x.X + widthHalf, x.Y + heightHalf)).ToList(); Bitmap bitmap = new Bitmap(newWidth, newHeight); Graphics g = Graphics.FromImage(bitmap); if (isHighQuality) g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; else g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor; g.DrawImage(sourceBitmap, points.ToArray(), new Rectangle(0, 0, sourceBitmap.Width, sourceBitmap.Height), GraphicsUnit.Pixel); g.Dispose(); return bitmap; } static public Bitmap TrimingBitmap(Bitmap sourceBitmap, TrimingInfo trimingInfo, bool isHighQuality) { int newWidth = sourceBitmap.Width - trimingInfo.Left - trimingInfo.Right; int newHeight = sourceBitmap.Height - trimingInfo.Top - trimingInfo.Bottom; Bitmap newBitmap = new Bitmap(newWidth, newHeight); Graphics g = Graphics.FromImage(newBitmap); if (isHighQuality) g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; else g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor; g.DrawImage(sourceBitmap, new Rectangle(0, 0, newWidth, newHeight), new Rectangle(trimingInfo.Left, trimingInfo.Top, newWidth, newHeight), GraphicsUnit.Pixel); g.Dispose(); return newBitmap; } static public Bitmap GetLastImage(Bitmap sourceBitmap , List<EditInfo> editInfos) { Bitmap bitmapTemp = sourceBitmap; foreach (EditInfo info in editInfos) { if (info.GetType() == typeof(TrimingInfo)) { TrimingInfo info1 = (TrimingInfo)info; bitmapTemp = TrimingBitmap(bitmapTemp, info1, info1.IsHighQuality); } if (info.GetType() == typeof(RotateInfo)) { RotateInfo info1 = (RotateInfo)info; bitmapTemp = RotateBitmap(bitmapTemp, info1.Angle, true); } } return bitmapTemp; } } |
ImageLayerクラス内にあったEditInfosはどのような編集がされたかを記憶しておくためのものです。
1 2 3 4 |
public class ImageLayer : Layer { public List<EditInfo> EditInfos = new List<EditInfo>(); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public class EditInfo { } public class TrimingInfo : EditInfo { public int Top = 0; public int Bottom = 0; public int Left = 0; public int Right = 0; public bool IsHighQuality = true; } public class RotateInfo : EditInfo { public int Angle = 0; public bool IsHighQuality = true; } |
メニューをクリックすればトリミングや回転が可能です。
これを組み合わせればこんな写真もつくれます。
[レイヤーの編集]から編集したいレイヤーを選択すると
が表示されるので、[読み込んだ画像の加工]メニューをクリックします。するとレイヤーを編集するダイアログが表示されます。
ここでトリミングを選択すると
回転を選択すると
が表示されます。
ではForm1クラスとFormMovePositionクラスに追加された部分をみていきましょう。
まずドロップダウンメニューを表示できるようにしましょう。メニューアイテムとレイヤーをセットにして、アイテムをクリックしたら対応するレイヤーが取得できるようにしています。
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(); scroolPictureBox1.BorderStyle = BorderStyle.None; MenuItemLayers.DropDownOpening += MenuItemLayers_DropDownOpening; } private void MenuItemLayers_DropDownOpening(object sender, EventArgs e) { MenuItemLayers.DropDownItems.Clear(); List<Layer> layers = Layers.OrderByDescending(x => x.Z).ToList(); foreach (Layer layer in layers) AddLayerToMenu(layer); } void AddLayerToMenu(Layer layer) { ToolStripItem newItem = new ToolStripMenuItem(); newItem.Tag = layer; newItem.Text = layer.Name; newItem.Click += Item_Click; MenuItemLayers.DropDownItems.Add(newItem); } } |
メニューの[レーヤーの編集]のドロップダウンメニューをクリックするとダイアログが出現します。そのダイアログの[読み込んだ画像の加工]をクリックすると、レイヤーを加工するためのダイアログが表示されます。以下の処理はレイヤーを加工するためのダイアログが表示されるまでの処理です。
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 partial class Form1 : Form { // メニューの[レーヤーの編集]のドロップダウンメニューをクリックすると・・・ private void Item_Click(object sender, EventArgs e) { ToolStripItem item = (ToolStripItem)sender; Layer layer =(Layer)item.Tag; if (layer is ImageLayer) ShowImageLayerForm((ImageLayer)layer); } public void ShowImageLayerForm(ImageLayer layer) { FormMovePosition form2 = new FormMovePosition(); form2.Layer = (ImageLayer)layer; form2.IsSetLayer = true; form2.FixedLayer += Form2_FixedLayer; form2.TemporaryFixingLayer += Form2_TemporaryFixingLayer; form2.MovedLayer += Form2_TemporaryFixingLayer; form2.EditCanceled += Form2_EditCanceled; form2.Show(this); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public partial class FormMovePosition : Form { // [読み込んだ画像の加工]をクリックすると・・・ private void MenuItemEditImageLayer_Click(object sender, EventArgs e) { FormEditImage form3 = new FormEditImage(); form3.BitmapBefore = Layer.SourceBitmap; form3.BitmapAfter = Layer.Bitmap; form3.EditInfos = Layer.EditInfos; form3.Show(this); form3.FormClosed += Form3_FormClosed; // ダイアログが閉じられたらイベント処理ができるようにする } } |
ではレイヤーを加工するためのダイアログはどのようになっているのでしょうか?
左側にあるのはツリービューコントロールです。回転やトリミングはされた順番によって結果がかわります。処理の履歴のようなものを表示させるためにあります。
[トリミング処理を追加]や[回転の処理を追加]がクリックされるとそのためのダイアログが表示されます。
まずはレイヤーを加工するためのダイアログのクラス FormEditImageを示します。
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 |
public partial class FormEditImage : Form { public Bitmap BitmapBefore = null; public Bitmap BitmapAfter = null; public List<EditInfo> EditInfos = new List<EditInfo>(); public FormEditImage() { InitializeComponent(); } protected override void OnLoad(EventArgs e) { pictureBoxBeforeAfter1.BeforeLabelText = "加工前の画像"; pictureBoxBeforeAfter1.AfterLabelText = "加工後の画像"; if (BitmapBefore != null) pictureBoxBeforeAfter1.BeforeBitmap = BitmapBefore; // すでに編集がされている場合はその履歴を表示する foreach (EditInfo info in EditInfos) { if (info.GetType() == typeof(TrimingInfo)) { TrimingInfo trimingInfo = (TrimingInfo)info; TreeNode tn = new TreeNode("トリミング"); treeView1.Nodes.Add(tn); } if (info.GetType() == typeof(RotateInfo)) { RotateInfo rotateInfo = (RotateInfo)info; TreeNode tn = new TreeNode("回転"); treeView1.Nodes.Add(tn); } } } } |
メニューでトリミングや回転が選択されたらツリービューに項目を挿入します。そしてトリミングや回転処理をおこなうためのダイアログを新たに表示させます。そしてダイアログが消されたら適切な処理をUpdateEditedImages()メソッド内でおこないます。
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 |
public partial class FormEditImage : Form { private void MenuItemAddTriming_Click(object sender, EventArgs e) { FormTrimmingImage form4 = new FormTrimmingImage(); TreeNode tn = new TreeNode("トリミング"); TrimingInfo trimingInfo = new TrimingInfo(); form4.TrimingInfo = trimingInfo; // ツリーアイテムが何も選択されていないとき if (treeView1.SelectedNode == null) { EditInfos.Add(trimingInfo); treeView1.Nodes.Add(tn); treeView1.SelectedNode = tn; form4.bitmapBerore = BitmapBefore; form4.bitmapAfter = BitmapBefore; } else { // ツリーアイテムが選択されているとき TreeNode sn = treeView1.SelectedNode; int index = treeView1.Nodes.IndexOf(sn); EditInfos.Insert(index + 1, trimingInfo); treeView1.Nodes.Insert(index + 1, tn); treeView1.SelectedNode = tn; Bitmap bitmapBefore = null; Bitmap bitmapAfter = null; GetBeforeAfterImage(ref bitmapBefore, ref bitmapAfter); form4.bitmapBerore = bitmapBefore; form4.bitmapAfter = bitmapAfter; } form4.FormClosed += Form4_FormClosed; form4.Show(this); } private void Form4_FormClosed(object sender, FormClosedEventArgs e) { UpdateEditedImages(); } private void MenuItemAddRotate_Click(object sender, EventArgs e) { FormRotateImage form5 = new FormRotateImage(); TreeNode tn = new TreeNode("回転"); RotateInfo info = new RotateInfo(); form5.RotateInfo = info; if (treeView1.SelectedNode == null) { EditInfos.Add(info); treeView1.Nodes.Add(tn); treeView1.SelectedNode = tn; form5.bitmapBerore = BitmapBefore; form5.bitmapAfter = BitmapBefore; } else { TreeNode sn = treeView1.SelectedNode; int index = treeView1.Nodes.IndexOf(sn); EditInfos.Insert(index + 1, info); treeView1.Nodes.Insert(index + 1, tn); treeView1.SelectedNode = tn; Bitmap bitmapBefore = null; Bitmap bitmapAfter = null; GetBeforeAfterImage(ref bitmapBefore, ref bitmapAfter); form5.bitmapBerore = bitmapBefore; form5.bitmapAfter = bitmapAfter; } form5.FormClosed += Form5_FormClosed; form5.Show(this); } private void Form5_FormClosed(object sender, FormClosedEventArgs e) { UpdateEditedImages(); } } |
UpdateEditedImages()メソッドでおこなわれる処理は加工前と加工後のBitmapを表示するだけです。
1 2 3 4 5 6 7 |
public partial class FormEditImage : Form { public void UpdateEditedImages() { ShowBeforeAfter(); } } |
ツリービューのアイテムが選択されたら画像にどのような変更がされているのかBeforeAfterがわかるようにピクチャーボックスに表示させます。そのための処理を示します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public partial class FormEditImage : Form { private void treeView1_AfterSelect(object sender, TreeViewEventArgs e) { ShowBeforeAfter(); } void ShowBeforeAfter() { Bitmap bitmapBefore = null; Bitmap bitmapAfter = null; GetBeforeAfterImage(ref bitmapBefore, ref bitmapAfter); pictureBoxBeforeAfter1.BeforeBitmap = bitmapBefore; pictureBoxBeforeAfter1.AfterBitmap = bitmapAfter; } } |
GetBeforeAfterImageメソッドは加工される前と後のBitmapを取得するためのものです。
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 FormEditImage : Form { void GetBeforeAfterImage(ref Bitmap bitmapBefore, ref Bitmap bitmapAfter) { TreeNode sn = treeView1.SelectedNode; int index = treeView1.Nodes.IndexOf(sn); Bitmap bitmapTemp = BitmapBefore; for (int i = 0; i <= index; i++) { if (i == index) bitmapBefore = bitmapTemp; if (EditInfos[i].GetType() == typeof(TrimingInfo)) { TrimingInfo info = (TrimingInfo)EditInfos[i]; bitmapTemp = BitmapEx.TrimingBitmap(bitmapTemp, info, info.IsHighQuality); } if (EditInfos[i].GetType() == typeof(RotateInfo)) { RotateInfo info = (RotateInfo)EditInfos[i]; bitmapTemp = BitmapEx.RotateBitmap(bitmapTemp, info.Angle, info.IsHighQuality); } if (i == index) bitmapAfter = bitmapTemp; } } } |
それからトリミングや回転処理をおこなったあとその処理を修正できるようにしましょう。
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 FormEditImage : Form { private void MenuItemEdit_Click(object sender, EventArgs e) { TreeNode sn = treeView1.SelectedNode; int index = treeView1.Nodes.IndexOf(sn); EditInfo info = EditInfos[index]; Bitmap bitmapBefore = null; Bitmap bitmapAfter = null; GetBeforeAfterImage(ref bitmapBefore, ref bitmapAfter); if (info.GetType() == typeof(TrimingInfo)) { FormTrimmingImage form4 = new FormTrimmingImage(); form4.TrimingInfo = (TrimingInfo)info; form4.bitmapAfter = bitmapAfter; form4.bitmapBerore = bitmapBefore; form4.FormClosed += Form4_FormClosed; form4.Show(this); } if (info.GetType() == typeof(RotateInfo)) { FormRotateImage form5 = new FormRotateImage(); form5.RotateInfo = (RotateInfo)info; form5.bitmapAfter = bitmapAfter; form5.bitmapBerore = bitmapBefore; form5.FormClosed += Form5_FormClosed; form5.Show(this); } } } |
このダイアログで処理を確定するときはメニューの[処理を確定させる]を選択します。
1 2 3 4 5 6 7 8 9 10 |
public partial class FormEditImage : Form { public bool IsEdited = false; private void MenuItemFixation_Click(object sender, EventArgs e) { BitmapAfter = BitmapEx.GetLastImage(BitmapBefore, EditInfos); IsEdited = true; this.Close(); } } |
そしてFormMovePositionクラス内で編集内容を反映させる処理がおこなわれます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
public partial class FormMovePosition : Form { private void Form3_FormClosed(object sender, FormClosedEventArgs e) { FormEditImage form3 = (FormEditImage)sender; if (form3.IsEdited) { Editinfos = form3.EditInfos; pictureBoxBeforeAfter1.AfterBitmap = BitmapEx.GetLastImage(Layer.SourceBitmap, Editinfos); ignoreNumericUpDownValueChanged = true; { numericUpDownWidth.Value = Layer.Bitmap.Width; numericUpDownHeight.Value = Layer.Bitmap.Height; } ignoreNumericUpDownValueChanged = false; } } } |