Winform(.NET Framework 4.8) でペイントソフトを作っているのですが,自由形状の選択(ユーザがマウスドラッグで囲んだ範囲を選択)が成された際に,その範囲を示す表示を行う方法で困っています.
ペイントソフトなので表示倍率を変えることができるわけですが,その場合に下図の点線のような形で範囲を示す描画を行いたいのですが,これを簡単に実現する方法があるものでしょうか?
赤い領域が選択されている範囲で,大きく拡大して表示しているイメージです.
矩形形状での選択であれば,拡大率Nのときには座標をN倍して矩形を描画すれば済むのですが……
とりあえず原始的(?)に,画素単位で「この画素は範囲の内側か外側か」を調べていけば,境界を見つけること自体はできるでしょうから,そういう方法で全て自前で頑張って「どの画素のどちら側に点線を引くべきか」みたいなのをやるしかないのでしょうか?
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。

回答1件
0
矩形形状での選択であれば,拡大率Nのときには座標をN倍して矩形を描画すれば済むのですが……
矩形でなくてもN倍でいいんじゃないですかね??
Graphics.DrawPolygon メソッド (System.Drawing) | Microsoft Learn
とりあえず原始的(?)に,画素単位で「この画素は範囲の内側か外側か」を調べていけば,境界を見つけること自体はできるでしょうから,そういう方法で全て自前で頑張って「どの画素のどちら側に点線を引くべきか」みたいなのをやるしかないのでしょうか?
標準のペイントでは中か外かは特にないというか、マウス軌跡そのままに引いているように見えます(ドット単位に境界はなく拡大すると線からはみ出るドットがあったりする)
いやゆっくり引くとガタガタになるので、ポイントごとのスナップ処理はあるんかな?(少なくともドット境界に合わせる気はなさそう^^;
実際のところ選択範囲の移動等を考えると、GraphicsPath
でやることになるかと思います。
GraphicsPath クラス (System.Drawing.Drawing2D) | Microsoft Learn
GraphicsPath.AddPolygon メソッド (System.Drawing.Drawing2D) | Microsoft Learn
方法: 領域でクリッピングを使用する - Windows Forms .NET Framework | Microsoft Learn
外側が重要な場合はWidenが使えそうです。
GraphicsPath.Widen メソッド (System.Drawing.Drawing2D) | Microsoft Learn
アイコンエディタのような小さい画像向けならばドット境界は重要でしょうが、大きい画像向けなら(わたしは)気にしていないかも(自由選択を使ってないから?^^;
FrameRgn
cs
1using System; 2using System.Drawing; 3using System.Drawing.Drawing2D; 4using System.Runtime.InteropServices; 5using System.Windows.Forms; 6 7namespace Q8nihxba9mjinwo 8{ 9 public partial class Form1 : Form 10 { 11 private readonly PictureBox pictureBox1; 12 private readonly PictureBox pictureBox2; 13 14 private GraphicsPath scaledPath; 15 private GraphicsPath realPath; 16 private Region region; 17 18 private bool isDragging; 19 private int startX, startY; 20 21 public Form1() 22 { 23 InitializeComponent(); 24 25 pictureBox1 = new MyPictureBox 26 { 27 BackColor = Color.White, 28 Image = new Bitmap(64, 64), 29 SizeMode = PictureBoxSizeMode.Zoom, 30 Width = 64 * 8, 31 Height = 64 * 8, 32 Parent = this, 33 }; 34 pictureBox1.MouseDown += PictureBox1_MouseDown; 35 pictureBox1.MouseMove += PictureBox1_MouseMove; 36 pictureBox1.MouseUp += PictureBox1_MouseUp; 37 pictureBox1.Paint += PictureBox1_Paint; 38 39 using (var g = Graphics.FromImage(pictureBox1.Image)) 40 { 41 g.FillEllipse(Brushes.Orange, new Rectangle(0, 0, 40, 40)); 42 } 43 44 pictureBox2 = new MyPictureBox 45 { 46 BackColor = Color.White, 47 Image = new Bitmap(64, 64), 48 Location = new Point(520, 0), 49 SizeMode = PictureBoxSizeMode.Zoom, 50 Width = 64 * 8, 51 Height = 64 * 8, 52 Parent = this, 53 }; 54 pictureBox2.Paint += PictureBox2_Paint; 55 } 56 57 private void PictureBox1_MouseDown(object sender, MouseEventArgs e) 58 { 59 scaledPath?.Dispose(); 60 scaledPath = new GraphicsPath(); 61 var matrix = new Matrix(); 62 matrix.Scale(8, 8); 63 scaledPath.Transform(matrix); 64 65 realPath?.Dispose(); 66 realPath = new GraphicsPath(); 67 68 isDragging = true; 69 startX = e.X; 70 startY = e.Y; 71 } 72 73 private void PictureBox1_MouseMove(object sender, MouseEventArgs e) 74 { 75 if (!isDragging) return; 76 77 scaledPath.AddLine(startX / 8 * 8, startY / 8 * 8, e.X / 8 * 8, e.Y / 8 * 8); 78 realPath.AddLine(startX / 8, startY / 8, e.X / 8, e.Y / 8); 79 80 pictureBox1.Refresh(); 81 startX = e.X; 82 startY = e.Y; 83 } 84 85 private void PictureBox1_MouseUp(object sender, MouseEventArgs e) 86 { 87 isDragging = false; 88 scaledPath.CloseFigure(); 89 pictureBox1.Refresh(); 90 91 var clippedImage = new Bitmap(64, 64); 92 using (var g = Graphics.FromImage(clippedImage)) 93 { 94 g.Clip = new Region(realPath); 95 g.DrawImage(pictureBox1.Image, 0, 0); 96 } 97 pictureBox2.Image?.Dispose(); 98 pictureBox2.Image = clippedImage; 99 100 using (var selectionMask = new Bitmap(64, 64)) 101 { 102 using (var g = Graphics.FromImage(selectionMask)) 103 { 104 g.Clip = new Region(realPath); 105 g.FillRectangle(Brushes.Aqua, 0, 0, 64, 64); 106 } 107 108 region?.Dispose(); 109 region = null; 110 for (var x = 0; x < selectionMask.Width; x++) 111 { 112 for (var y = 0; y < selectionMask.Height; y++) 113 { 114 if (0 == selectionMask.GetPixel(x, y).B) continue; 115 116 var rect = new Rectangle(x * 8, y * 8, 8, 8); 117 if (region == null) region = new Region(rect); 118 else region.Union(rect); 119 } 120 } 121 } 122 } 123 124 private void PictureBox1_Paint(object sender, PaintEventArgs e) 125 { 126 if (scaledPath == null) return; 127 e.Graphics.DrawPath(Pens.Red, scaledPath); 128 } 129 130 private void PictureBox2_Paint(object sender, PaintEventArgs e) 131 { 132 if (region == null) return; 133 134 var colorref = new COLORREF(Color.Red); 135 var hdc = IntPtr.Zero; 136 var hbrush = IntPtr.Zero; 137 var hrgn = IntPtr.Zero; 138 139 try 140 { 141 hrgn = region.GetHrgn(e.Graphics); 142 hdc = e.Graphics.GetHdc(); 143 hbrush = CreateSolidBrush(colorref.colorref); 144 145 FrameRgn(hdc, hrgn, hbrush, 1, 1); 146 } 147 finally 148 { 149 if (hrgn != IntPtr.Zero) region.ReleaseHrgn(hrgn); 150 if (hbrush != IntPtr.Zero) DeleteObject(hbrush); 151 if (hdc != IntPtr.Zero) e.Graphics.ReleaseHdc(hdc); 152 } 153 } 154 155 [DllImport("gdi32")] private static extern IntPtr CreateSolidBrush(uint colorref); 156 [DllImport("gdi32")] private static extern bool FrameRgn(IntPtr hDC, IntPtr hRgn, IntPtr hBrush, int nWidth, int nHeight); 157 [DllImport("gdi32")] private static extern bool DeleteObject([In] IntPtr hObject); 158 159 [StructLayout(LayoutKind.Explicit)] 160 private struct COLORREF 161 { 162 [FieldOffset(0)] public uint colorref; 163 [FieldOffset(0)] public byte red; 164 [FieldOffset(1)] public byte green; 165 [FieldOffset(2)] public byte blue; 166 public COLORREF(Color color) : this() => (red, green, blue) = (color.R, color.G, color.B); 167 } 168 169 private class MyPictureBox : PictureBox 170 { 171 protected override void OnPaint(PaintEventArgs e) 172 { 173 e.Graphics.InterpolationMode = InterpolationMode.NearestNeighbor; 174 e.Graphics.PixelOffsetMode = PixelOffsetMode.Half; 175 base.OnPaint(e); 176 } 177 } 178 } 179}
投稿2025/03/11 09:51
編集2025/03/13 11:31総合スコア9957
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2025/03/11 14:24
2025/03/11 15:55
2025/03/12 01:53
2025/03/12 01:59
2025/03/12 11:16
2025/03/14 01:36
2025/03/14 03:07
2025/03/14 03:39 編集
2025/03/14 04:19