回答編集履歴

5

コード整理

2025/03/13 11:31

投稿

TN8001
TN8001

スコア9959

test CHANGED
@@ -55,10 +55,10 @@
55
55
  {
56
56
  BackColor = Color.White,
57
57
  Image = new Bitmap(64, 64),
58
- Parent = this,
58
+ SizeMode = PictureBoxSizeMode.Zoom,
59
59
  Width = 64 * 8,
60
60
  Height = 64 * 8,
61
- SizeMode = PictureBoxSizeMode.Zoom,
61
+ Parent = this,
62
62
  };
63
63
  pictureBox1.MouseDown += PictureBox1_MouseDown;
64
64
  pictureBox1.MouseMove += PictureBox1_MouseMove;
@@ -75,10 +75,10 @@
75
75
  BackColor = Color.White,
76
76
  Image = new Bitmap(64, 64),
77
77
  Location = new Point(520, 0),
78
- Parent = this,
78
+ SizeMode = PictureBoxSizeMode.Zoom,
79
79
  Width = 64 * 8,
80
80
  Height = 64 * 8,
81
- SizeMode = PictureBoxSizeMode.Zoom,
81
+ Parent = this,
82
82
  };
83
83
  pictureBox2.Paint += PictureBox2_Paint;
84
84
  }
@@ -140,12 +140,11 @@
140
140
  {
141
141
  for (var y = 0; y < selectionMask.Height; y++)
142
142
  {
143
- if (0 < selectionMask.GetPixel(x, y).B)
143
+ if (0 == selectionMask.GetPixel(x, y).B) continue;
144
- {
144
+
145
- var rect = new Rectangle(x * 8, y * 8, 8, 8);
145
+ var rect = new Rectangle(x * 8, y * 8, 8, 8);
146
- if (region == null) region = new Region(rect);
146
+ if (region == null) region = new Region(rect);
147
- else region.Union(rect);
147
+ else region.Union(rect);
148
- }
149
148
  }
150
149
  }
151
150
  }
@@ -172,7 +171,7 @@
172
171
  hdc = e.Graphics.GetHdc();
173
172
  hbrush = CreateSolidBrush(colorref.colorref);
174
173
 
175
- FrameRgn(hdc, hrgn, hbrush, 2, 2);
174
+ FrameRgn(hdc, hrgn, hbrush, 1, 1);
176
175
  }
177
176
  finally
178
177
  {
@@ -182,8 +181,8 @@
182
181
  }
183
182
  }
184
183
 
184
+ [DllImport("gdi32")] private static extern IntPtr CreateSolidBrush(uint colorref);
185
185
  [DllImport("gdi32")] private static extern bool FrameRgn(IntPtr hDC, IntPtr hRgn, IntPtr hBrush, int nWidth, int nHeight);
186
- [DllImport("gdi32")] private static extern IntPtr CreateSolidBrush(uint colorref);
187
186
  [DllImport("gdi32")] private static extern bool DeleteObject([In] IntPtr hObject);
188
187
 
189
188
  [StructLayout(LayoutKind.Explicit)]
@@ -193,8 +192,7 @@
193
192
  [FieldOffset(0)] public byte red;
194
193
  [FieldOffset(1)] public byte green;
195
194
  [FieldOffset(2)] public byte blue;
196
- public COLORREF(Color color) : this()
197
- => (red, green, blue) = (color.R, color.G, color.B);
195
+ public COLORREF(Color color) : this() => (red, green, blue) = (color.R, color.G, color.B);
198
196
  }
199
197
 
200
198
  private class MyPictureBox : PictureBox
@@ -209,4 +207,4 @@
209
207
  }
210
208
  }
211
209
  ```
212
- ![アプリ動画](https://ddjkaamml8q8x.cloudfront.net/questions/2025-03-12/64ec2460-011d-4092-9b45-5efa9e7b8c60.gif)
210
+ ![アプリ動画](https://ddjkaamml8q8x.cloudfront.net/questions/2025-03-13/767b015c-11c6-498e-bf45-ebdd8f707e18.gif)

4

コード整理

2025/03/12 17:18

投稿

TN8001
TN8001

スコア9959

test CHANGED
@@ -37,12 +37,11 @@
37
37
  {
38
38
  public partial class Form1 : Form
39
39
  {
40
- private PictureBox pictureBox1;
40
+ private readonly PictureBox pictureBox1;
41
- private PictureBox pictureBox2;
41
+ private readonly PictureBox pictureBox2;
42
+
42
- private Pen p = new Pen(Brushes.Red);
43
+ private GraphicsPath scaledPath;
43
-
44
- private GraphicsPath path;
44
+ private GraphicsPath realPath;
45
- private GraphicsPath path2;
46
45
  private Region region;
47
46
 
48
47
  private bool isDragging;
@@ -66,9 +65,10 @@
66
65
  pictureBox1.MouseUp += PictureBox1_MouseUp;
67
66
  pictureBox1.Paint += PictureBox1_Paint;
68
67
 
69
- var g = Graphics.FromImage(pictureBox1.Image);
68
+ using (var g = Graphics.FromImage(pictureBox1.Image))
69
+ {
70
- g.FillEllipse(Brushes.Orange, new Rectangle(0, 0, 40, 40));
70
+ g.FillEllipse(Brushes.Orange, new Rectangle(0, 0, 40, 40));
71
- g.Dispose();
71
+ }
72
72
 
73
73
  pictureBox2 = new MyPictureBox
74
74
  {
@@ -83,70 +83,78 @@
83
83
  pictureBox2.Paint += PictureBox2_Paint;
84
84
  }
85
85
 
86
- private void PictureBox1_Paint(object sender, PaintEventArgs e)
87
- {
88
- if (path == null) return;
89
- e.Graphics.DrawPath(p, path);
90
- }
91
-
92
86
  private void PictureBox1_MouseDown(object sender, MouseEventArgs e)
93
87
  {
88
+ scaledPath?.Dispose();
94
- path = new GraphicsPath();
89
+ scaledPath = new GraphicsPath();
95
90
  var matrix = new Matrix();
96
91
  matrix.Scale(8, 8);
97
- path.Transform(matrix);
92
+ scaledPath.Transform(matrix);
93
+
98
-
94
+ realPath?.Dispose();
99
- path2 = new GraphicsPath();
95
+ realPath = new GraphicsPath();
100
96
 
101
97
  isDragging = true;
102
98
  startX = e.X;
103
99
  startY = e.Y;
104
100
  }
101
+
105
102
  private void PictureBox1_MouseMove(object sender, MouseEventArgs e)
106
103
  {
107
104
  if (!isDragging) return;
108
105
 
109
- path.AddLine(new Point(startX / 8 * 8, startY / 8 * 8), new Point(e.X / 8 * 8, e.Y / 8 * 8));
106
+ scaledPath.AddLine(startX / 8 * 8, startY / 8 * 8, e.X / 8 * 8, e.Y / 8 * 8);
110
- path2.AddLine(new Point(startX / 8, startY / 8), new Point(e.X / 8, e.Y / 8));
107
+ realPath.AddLine(startX / 8, startY / 8, e.X / 8, e.Y / 8);
111
108
 
112
109
  pictureBox1.Refresh();
113
110
  startX = e.X;
114
111
  startY = e.Y;
115
112
  }
113
+
116
114
  private void PictureBox1_MouseUp(object sender, MouseEventArgs e)
117
115
  {
118
116
  isDragging = false;
119
- path.CloseFigure();
117
+ scaledPath.CloseFigure();
120
118
  pictureBox1.Refresh();
121
119
 
122
- var bitmap = new Bitmap(64, 64);
120
+ var clippedImage = new Bitmap(64, 64);
123
- var g = Graphics.FromImage(bitmap);
121
+ using (var g = Graphics.FromImage(clippedImage))
122
+ {
124
- g.Clip = new Region(path2);
123
+ g.Clip = new Region(realPath);
125
- g.DrawImage(pictureBox1.Image, 0, 0);
124
+ g.DrawImage(pictureBox1.Image, 0, 0);
126
- g.Dispose();
125
+ }
127
126
  pictureBox2.Image?.Dispose();
128
- pictureBox2.Image = bitmap;
127
+ pictureBox2.Image = clippedImage;
129
-
128
+
130
- bitmap = new Bitmap(64, 64);
129
+ using (var selectionMask = new Bitmap(64, 64))
131
- g = Graphics.FromImage(bitmap);
132
- g.Clip = new Region(path2);
133
- g.FillRectangle(Brushes.Aqua, 0, 0, 512, 512);
134
- g.Dispose();
135
- region = null;
136
- for (var x = 0; x < bitmap.Width; x++)
137
- {
130
+ {
138
- for (var y = 0; y < bitmap.Height; y++)
131
+ using (var g = Graphics.FromImage(selectionMask))
139
132
  {
133
+ g.Clip = new Region(realPath);
134
+ g.FillRectangle(Brushes.Aqua, 0, 0, 64, 64);
135
+ }
136
+
137
+ region?.Dispose();
138
+ region = null;
139
+ for (var x = 0; x < selectionMask.Width; x++)
140
+ {
140
- if (0 < bitmap.GetPixel(x, y).B)
141
+ for (var y = 0; y < selectionMask.Height; y++)
141
142
  {
142
- if (region == null)
143
- region = new Region(new Rectangle(x * 8, y * 8, 8, 8));
143
+ if (0 < selectionMask.GetPixel(x, y).B)
144
- else
144
+ {
145
- region.Union(new Rectangle(x * 8, y * 8, 8, 8));
145
+ var rect = new Rectangle(x * 8, y * 8, 8, 8);
146
+ if (region == null) region = new Region(rect);
147
+ else region.Union(rect);
148
+ }
146
149
  }
147
150
  }
148
151
  }
152
+ }
153
+
154
+ private void PictureBox1_Paint(object sender, PaintEventArgs e)
155
+ {
156
+ if (scaledPath == null) return;
149
- bitmap.Dispose();
157
+ e.Graphics.DrawPath(Pens.Red, scaledPath);
150
158
  }
151
159
 
152
160
  private void PictureBox2_Paint(object sender, PaintEventArgs e)
@@ -174,34 +182,29 @@
174
182
  }
175
183
  }
176
184
 
177
- [DllImport("gdi32")] static extern bool FrameRgn(IntPtr hDC, IntPtr hRgn, IntPtr hBrush, int nWidth, int nHeight);
185
+ [DllImport("gdi32")] private static extern bool FrameRgn(IntPtr hDC, IntPtr hRgn, IntPtr hBrush, int nWidth, int nHeight);
178
- [DllImport("gdi32")] static extern IntPtr CreateSolidBrush(uint colorref);
186
+ [DllImport("gdi32")] private static extern IntPtr CreateSolidBrush(uint colorref);
179
- [DllImport("gdi32")] static extern bool DeleteObject([In] IntPtr hObject);
187
+ [DllImport("gdi32")] private static extern bool DeleteObject([In] IntPtr hObject);
180
188
 
181
189
  [StructLayout(LayoutKind.Explicit)]
182
- struct COLORREF
190
+ private struct COLORREF
183
191
  {
184
192
  [FieldOffset(0)] public uint colorref;
185
193
  [FieldOffset(0)] public byte red;
186
194
  [FieldOffset(1)] public byte green;
187
195
  [FieldOffset(2)] public byte blue;
188
-
189
196
  public COLORREF(Color color) : this()
197
+ => (red, green, blue) = (color.R, color.G, color.B);
198
+ }
199
+
200
+ private class MyPictureBox : PictureBox
190
- {
201
+ {
191
- red = color.R;
202
+ protected override void OnPaint(PaintEventArgs e)
203
+ {
204
+ e.Graphics.InterpolationMode = InterpolationMode.NearestNeighbor;
192
- green = color.G;
205
+ e.Graphics.PixelOffsetMode = PixelOffsetMode.Half;
193
- blue = color.B;
206
+ base.OnPaint(e);
194
- }
207
+ }
195
- }
196
- }
197
-
198
- class MyPictureBox : PictureBox
199
- {
200
- protected override void OnPaint(PaintEventArgs e)
201
- {
202
- e.Graphics.InterpolationMode = InterpolationMode.NearestNeighbor;
203
- e.Graphics.PixelOffsetMode = PixelOffsetMode.Half;
204
- base.OnPaint(e);
205
208
  }
206
209
  }
207
210
  }

3

FrameRgn

2025/03/12 11:16

投稿

TN8001
TN8001

スコア9959

test CHANGED
@@ -22,3 +22,188 @@
22
22
  ---
23
23
 
24
24
  アイコンエディタのような小さい画像向けならばドット境界は重要でしょうが、大きい画像向けなら(わたしは)気にしていないかも(自由選択を使ってないから?^^;
25
+
26
+ ---
27
+
28
+ ## FrameRgn
29
+ ```cs
30
+ using System;
31
+ using System.Drawing;
32
+ using System.Drawing.Drawing2D;
33
+ using System.Runtime.InteropServices;
34
+ using System.Windows.Forms;
35
+
36
+ namespace Q8nihxba9mjinwo
37
+ {
38
+ public partial class Form1 : Form
39
+ {
40
+ private PictureBox pictureBox1;
41
+ private PictureBox pictureBox2;
42
+ private Pen p = new Pen(Brushes.Red);
43
+
44
+ private GraphicsPath path;
45
+ private GraphicsPath path2;
46
+ private Region region;
47
+
48
+ private bool isDragging;
49
+ private int startX, startY;
50
+
51
+ public Form1()
52
+ {
53
+ InitializeComponent();
54
+
55
+ pictureBox1 = new MyPictureBox
56
+ {
57
+ BackColor = Color.White,
58
+ Image = new Bitmap(64, 64),
59
+ Parent = this,
60
+ Width = 64 * 8,
61
+ Height = 64 * 8,
62
+ SizeMode = PictureBoxSizeMode.Zoom,
63
+ };
64
+ pictureBox1.MouseDown += PictureBox1_MouseDown;
65
+ pictureBox1.MouseMove += PictureBox1_MouseMove;
66
+ pictureBox1.MouseUp += PictureBox1_MouseUp;
67
+ pictureBox1.Paint += PictureBox1_Paint;
68
+
69
+ var g = Graphics.FromImage(pictureBox1.Image);
70
+ g.FillEllipse(Brushes.Orange, new Rectangle(0, 0, 40, 40));
71
+ g.Dispose();
72
+
73
+ pictureBox2 = new MyPictureBox
74
+ {
75
+ BackColor = Color.White,
76
+ Image = new Bitmap(64, 64),
77
+ Location = new Point(520, 0),
78
+ Parent = this,
79
+ Width = 64 * 8,
80
+ Height = 64 * 8,
81
+ SizeMode = PictureBoxSizeMode.Zoom,
82
+ };
83
+ pictureBox2.Paint += PictureBox2_Paint;
84
+ }
85
+
86
+ private void PictureBox1_Paint(object sender, PaintEventArgs e)
87
+ {
88
+ if (path == null) return;
89
+ e.Graphics.DrawPath(p, path);
90
+ }
91
+
92
+ private void PictureBox1_MouseDown(object sender, MouseEventArgs e)
93
+ {
94
+ path = new GraphicsPath();
95
+ var matrix = new Matrix();
96
+ matrix.Scale(8, 8);
97
+ path.Transform(matrix);
98
+
99
+ path2 = new GraphicsPath();
100
+
101
+ isDragging = true;
102
+ startX = e.X;
103
+ startY = e.Y;
104
+ }
105
+ private void PictureBox1_MouseMove(object sender, MouseEventArgs e)
106
+ {
107
+ if (!isDragging) return;
108
+
109
+ path.AddLine(new Point(startX / 8 * 8, startY / 8 * 8), new Point(e.X / 8 * 8, e.Y / 8 * 8));
110
+ path2.AddLine(new Point(startX / 8, startY / 8), new Point(e.X / 8, e.Y / 8));
111
+
112
+ pictureBox1.Refresh();
113
+ startX = e.X;
114
+ startY = e.Y;
115
+ }
116
+ private void PictureBox1_MouseUp(object sender, MouseEventArgs e)
117
+ {
118
+ isDragging = false;
119
+ path.CloseFigure();
120
+ pictureBox1.Refresh();
121
+
122
+ var bitmap = new Bitmap(64, 64);
123
+ var g = Graphics.FromImage(bitmap);
124
+ g.Clip = new Region(path2);
125
+ g.DrawImage(pictureBox1.Image, 0, 0);
126
+ g.Dispose();
127
+ pictureBox2.Image?.Dispose();
128
+ pictureBox2.Image = bitmap;
129
+
130
+ bitmap = new Bitmap(64, 64);
131
+ g = Graphics.FromImage(bitmap);
132
+ g.Clip = new Region(path2);
133
+ g.FillRectangle(Brushes.Aqua, 0, 0, 512, 512);
134
+ g.Dispose();
135
+ region = null;
136
+ for (var x = 0; x < bitmap.Width; x++)
137
+ {
138
+ for (var y = 0; y < bitmap.Height; y++)
139
+ {
140
+ if (0 < bitmap.GetPixel(x, y).B)
141
+ {
142
+ if (region == null)
143
+ region = new Region(new Rectangle(x * 8, y * 8, 8, 8));
144
+ else
145
+ region.Union(new Rectangle(x * 8, y * 8, 8, 8));
146
+ }
147
+ }
148
+ }
149
+ bitmap.Dispose();
150
+ }
151
+
152
+ private void PictureBox2_Paint(object sender, PaintEventArgs e)
153
+ {
154
+ if (region == null) return;
155
+
156
+ var colorref = new COLORREF(Color.Red);
157
+ var hdc = IntPtr.Zero;
158
+ var hbrush = IntPtr.Zero;
159
+ var hrgn = IntPtr.Zero;
160
+
161
+ try
162
+ {
163
+ hrgn = region.GetHrgn(e.Graphics);
164
+ hdc = e.Graphics.GetHdc();
165
+ hbrush = CreateSolidBrush(colorref.colorref);
166
+
167
+ FrameRgn(hdc, hrgn, hbrush, 2, 2);
168
+ }
169
+ finally
170
+ {
171
+ if (hrgn != IntPtr.Zero) region.ReleaseHrgn(hrgn);
172
+ if (hbrush != IntPtr.Zero) DeleteObject(hbrush);
173
+ if (hdc != IntPtr.Zero) e.Graphics.ReleaseHdc(hdc);
174
+ }
175
+ }
176
+
177
+ [DllImport("gdi32")] static extern bool FrameRgn(IntPtr hDC, IntPtr hRgn, IntPtr hBrush, int nWidth, int nHeight);
178
+ [DllImport("gdi32")] static extern IntPtr CreateSolidBrush(uint colorref);
179
+ [DllImport("gdi32")] static extern bool DeleteObject([In] IntPtr hObject);
180
+
181
+ [StructLayout(LayoutKind.Explicit)]
182
+ struct COLORREF
183
+ {
184
+ [FieldOffset(0)] public uint colorref;
185
+ [FieldOffset(0)] public byte red;
186
+ [FieldOffset(1)] public byte green;
187
+ [FieldOffset(2)] public byte blue;
188
+
189
+ public COLORREF(Color color) : this()
190
+ {
191
+ red = color.R;
192
+ green = color.G;
193
+ blue = color.B;
194
+ }
195
+ }
196
+ }
197
+
198
+ class MyPictureBox : PictureBox
199
+ {
200
+ protected override void OnPaint(PaintEventArgs e)
201
+ {
202
+ e.Graphics.InterpolationMode = InterpolationMode.NearestNeighbor;
203
+ e.Graphics.PixelOffsetMode = PixelOffsetMode.Half;
204
+ base.OnPaint(e);
205
+ }
206
+ }
207
+ }
208
+ ```
209
+ ![アプリ動画](https://ddjkaamml8q8x.cloudfront.net/questions/2025-03-12/64ec2460-011d-4092-9b45-5efa9e7b8c60.gif)

2

改行

2025/03/11 10:18

投稿

TN8001
TN8001

スコア9959

test CHANGED
@@ -6,6 +6,7 @@
6
6
  > とりあえず原始的(?)に,画素単位で「この画素は範囲の内側か外側か」を調べていけば,境界を見つけること自体はできるでしょうから,そういう方法で全て自前で頑張って「どの画素のどちら側に点線を引くべきか」みたいなのをやるしかないのでしょうか?
7
7
 
8
8
  標準のペイントでは中か外かは特にないというか、~~マウス軌跡そのままに引いているように見えます~~(ドット単位に境界はなく拡大すると線からはみ出るドットがあったりする)
9
+
9
10
  いやゆっくり引くとガタガタになるので、ポイントごとのスナップ処理はあるんかな?(少なくともドット境界に合わせる気はなさそう^^;
10
11
  ![ペイント](https://ddjkaamml8q8x.cloudfront.net/questions/2025-03-11/68e811a2-3d70-456d-bd99-d7875736fa70.png)
11
12
 

1

画像追加

2025/03/11 10:17

投稿

TN8001
TN8001

スコア9959

test CHANGED
@@ -5,7 +5,10 @@
5
5
 
6
6
  > とりあえず原始的(?)に,画素単位で「この画素は範囲の内側か外側か」を調べていけば,境界を見つけること自体はできるでしょうから,そういう方法で全て自前で頑張って「どの画素のどちら側に点線を引くべきか」みたいなのをやるしかないのでしょうか?
7
7
 
8
- 標準のペイントでは中か外かは特にないというか、マウス軌跡そのままに引いているように見えます(ドット単位に境界はなく拡大すると線からはみ出るドットがあったりする)
8
+ 標準のペイントでは中か外かは特にないというか、~~マウス軌跡そのままに引いているように見えます~~(ドット単位に境界はなく拡大すると線からはみ出るドットがあったりする)
9
+ いやゆっくり引くとガタガタになるので、ポイントごとのスナップ処理はあるんかな?(少なくともドット境界に合わせる気はなさそう^^;
10
+ ![ペイント](https://ddjkaamml8q8x.cloudfront.net/questions/2025-03-11/68e811a2-3d70-456d-bd99-d7875736fa70.png)
11
+
9
12
 
10
13
  実際のところ選択範囲の移動等を考えると、`GraphicsPath`でやることになるかと思います。
11
14
  [GraphicsPath クラス (System.Drawing.Drawing2D) | Microsoft Learn](https://learn.microsoft.com/ja-jp/dotnet/api/system.drawing.drawing2d.graphicspath)