- 題名: DrawPathとFillPathの結果の違いについて教えて下さい
- 日時: 2009/01/28 14:04:40
- ID: 23878
- この記事の返信元:
- (なし)
- この記事への返信:
- [23879] Re[1]: DrawPathとFillPathの結果の違いについて教えて下さい2009/01/28 16:11:23
- ツリーを表示
魔界の仮面弁士さん、返信ありがとうございます。
■No23879に返信(魔界の仮面弁士さんの記事)
> 枠線のペン幅を 2 以上にする事はできませんか?
> そうすれば、同じパスを使えるかも知れません。
>
> ペンの太さが 1 の時、右下に 1 ドット大きく描画されてしまいますが、
> ペンの太さが 2 以上なら、指定した領域内に描画されるようなので。
ご提示頂いたコード・動作は確認致しました。
ペン幅については盲点でした…
四隅の円弧が、上下左右で対称で無い点については原因がわからないまま
なのですが、1ピクセルの境界線については以下の方法で実現出来ましたので
ソースを載せておきます。
四隅の円弧の問題については引き続き、ご存知の方がいらっしゃいましたら
宜しくお願い致します。
private static GraphicsPath CreateRoundRect(Rectangle rect, float w)
{
GraphicsPath gp = new GraphicsPath();
gp.StartFigure();
gp.AddArc(rect.Right - w, rect.Bottom - w, w, w, 0.0f, 90.0f); // 右下
gp.AddArc(rect.Left, rect.Bottom - w, w, w, 90.0f, 90.0f); // 左下
gp.AddArc(rect.Left, rect.Top, w, w, 180.0f, 90.0f); // 左上
gp.AddArc(rect.Right - w, rect.Top, w, w, 270.0f, 90.0f); // 右上
gp.CloseFigure();
return gp;
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
float w = 45.0f;
Rectangle rect = new Rectangle(0, 0, this.ClientSize.Width, this.ClientSize.Height);
rect.Inflate(-1, -1);
using (GraphicsPath gp = CreateRoundRect(rect, w))
using (SolidBrush brs = new SolidBrush(Color.Black))
{
rect.Inflate(-1, -1);
gp.AddPath(CreateRoundRect(rect, w), true);
e.Graphics.FillPath(brs, gp);
}
}
# もし同じことをすでに試されていたら、スルーしてください
> 四隅の円弧が、上下左右で対称で無い点については原因がわからないまま
円弧部分の点の座標は小数になります。
(推測ですが)画面に表示するとき小数→整数に変換されます。
切捨て、四捨五入、切上げのいずれを使っても、
画面上の円弧の形状は点対称になりません。
これが原因だと思います。
そこで、円弧の左上は小数を切上げ、右下は小数を切捨てて1ドットずつ円弧を描いてみました。
私のPCではうまく描けました。まことさんのPCでも試してみてください。
# コードがVBですがご了承下さいm(_ _)m
Public Class GraphicsPathTestForm_ver3
Inherits Form
' 角丸四角形の画像を保持する変数
Dim m_image As Bitmap
Protected Overrides Sub OnLoad(ByVal e As System.EventArgs)
Me.Text = Me.GetType.Name
Dim r As Rectangle = Me.ClientRectangle
r.Inflate(-2, -2)
Me.m_image = Me.CreateRoundBitmap(r, 30)
MyBase.OnLoad(e)
End Sub
Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
e.Graphics.DrawImage(Me.m_image, 2, 2)
MyBase.OnPaint(e)
End Sub
' 角丸四角形の画像を返す
Private Function CreateRoundBitmap(ByVal rect As Rectangle, ByVal radius As Integer) As Bitmap
Dim borderColor As Color = Color.Red
Dim bgColor As Color = Color.Blue
Dim bmp As New Bitmap(rect.Width, rect.Height)
Using g As Graphics = Graphics.FromImage(bmp)
' bmpの色を初期化
g.Clear(SystemColors.Control)
' 直線
Using p As New Pen(borderColor)
Dim x0 As Integer = radius
Dim y0 As Integer = radius
Dim x1 As Integer = rect.Width - 1 - radius
Dim y1 As Integer = rect.Height - 1 - radius
g.DrawLine(p, x0, 0, x1, 0)
g.DrawLine(p, 0, y0, 0, y1)
g.DrawLine(p, x0, rect.Height - 1, x1, rect.Height - 1)
g.DrawLine(p, rect.Width - 1, y0, rect.Width - 1, y1)
End Using
End Using
' 右上円弧(小数点以下:x切り上げ、y切り捨て)
Dim center As New Point(rect.Width - 1 - radius, radius)
For i As Integer = 0 To 90
Dim x As Integer = CInt(Math.Ceiling(center.X + radius * Math.Cos(Me.GetRadian(i))))
Dim y As Integer = CInt(Math.Floor(center.Y - radius * Math.Sin(Me.GetRadian(i))))
bmp.SetPixel(x, y, borderColor)
Next
' 左上円弧(小数点以下:x、y共切り捨て)
center = New Point(radius, radius)
For i As Integer = 90 To 180
Dim x As Integer = CInt(Math.Floor(center.X + radius * Math.Cos(Me.GetRadian(i))))
Dim y As Integer = CInt(Math.Floor(center.Y - radius * Math.Sin(Me.GetRadian(i))))
bmp.SetPixel(x, y, borderColor)
Next
' 左下円弧(小数点以下:x切り捨て、y切り上げ)
center = New Point(radius, rect.Height - 1 - radius)
For i As Integer = 180 To 270
Dim x As Integer = CInt(Math.Floor(center.X + radius * Math.Cos(Me.GetRadian(i))))
Dim y As Integer = CInt(Math.Ceiling(center.Y - radius * Math.Sin(Me.GetRadian(i))))
bmp.SetPixel(x, y, borderColor)
Next
' 右下円弧(小数点以下:x、y共切り上げ)
center = New Point(rect.Width - 1 - radius, rect.Height - 1 - radius)
For i As Integer = 270 To 360
Dim x As Integer = CInt(Math.Ceiling(center.X + radius * Math.Cos(Me.GetRadian(i))))
Dim y As Integer = CInt(Math.Ceiling(center.Y - radius * Math.Sin(Me.GetRadian(i))))
bmp.SetPixel(x, y, borderColor)
Next
' 塗りつぶし
For y As Integer = 0 To bmp.Height - 1
Dim flg As Flag = Flag.Outside
For x As Integer = 0 To bmp.Width - 1
Dim c As Color = bmp.GetPixel(x, y)
If Me.Color_Equals(c, borderColor) = True Then
If flg = Flag.Outside Then
' 左側境界線に入った
flg = Flag.FirstBorder
ElseIf flg = Flag.Inside Then
' 右側境界線に入った
Exit For
End If
ElseIf Me.Color_Equals(c, SystemColors.Control) = True Then
If flg = Flag.FirstBorder Then
If x >= bmp.Width \ 2 Then
' 上下境界線の場合は、ここに来る
Exit For
Else
' 領域内に入った
flg = Flag.Inside
End If
End If
If flg = Flag.Inside Then
bmp.SetPixel(x, y, bgColor)
End If
End If
Next
Next
Return bmp
End Function
' Bitmap塗りつぶし用フラグ
Private Enum Flag
Outside = 0
FirstBorder
Inside
End Enum
' 以下、ヘルパーメソッド
Private Function GetRadian(ByVal degree As Integer) As Double
Return degree / 180 * Math.PI
End Function
Private Function Color_Equals(ByVal c1 As Color, ByVal c2 As Color) As Boolean
Return c1.A = c2.A AndAlso c1.R = c2.R AndAlso _
c1.G = c2.G AndAlso c1.B = c2.B
End Function
End Class
分類:[.NET]
いつも参考にさせて頂いております、"まこと"と申します。 同一の GraphicsPath を用いた DrawPath と FillPath の処理結果の違いについて ご存知の方がいらっしゃいましたらご教授願います。 もっか角の丸いフォームを作り、更に縁取りをしたいのですが、GraphicsPath を 使って以下の様な処理記述しても、4隅の円弧がそれぞれ微妙に異なっており 上下・左右に反転させてもピッタリと円弧が合致しません。 ※Rectangleを微調整しても円弧は揃いませんでした。 尚、Win32 APIの CreateRoundRectRgn でリージョンを作成し、SetWindowRgn で フォームの形状を変更し、FrameRgn を使って縁取りをすると目的の結果を得る事 が出来るのですが、.NET Framework で同一の結果を得るにはどの様にすれば 宜しいのでしょうか? 以上 宜しくお願い致します。 【検証用コード】 public partial class Form1 : Form { public Form1() { InitializeComponent(); } protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); float w = 45.0f; Rectangle rect = new Rectangle(0, 0, this.ClientSize.Width, this.ClientSize.Height); using (System.Drawing.Drawing2D.GraphicsPath gp = new System.Drawing.Drawing2D.GraphicsPath()) { gp.StartFigure(); gp.AddArc(rect.Right - w, rect.Bottom - w, w, w, 0.0f, 90.0f); // 右下 gp.AddArc(rect.Left, rect.Bottom - w, w, w, 90.0f, 90.0f); // 左下 gp.AddArc(rect.Left, rect.Top, w, w, 180.0f, 90.0f); // 左上 gp.AddArc(rect.Right - w, rect.Top, w, w, 270.0f, 90.0f); // 右上 gp.CloseFigure(); // 実際の結果(塗り潰し) ※分かりやすいように半透明化してます using (SolidBrush brs = new SolidBrush(Color.FromArgb(128, Color.Blue))) { e.Graphics.FillPath(brs, gp); } } using (System.Drawing.Drawing2D.GraphicsPath gp = new System.Drawing.Drawing2D.GraphicsPath()) { gp.StartFigure(); gp.AddArc(rect.Right - w - 1, rect.Bottom - w - 1, w, w, 0.0f, 90.0f); // 右下 gp.AddArc(rect.Left, rect.Bottom - w - 1, w, w, 90.0f, 90.0f); // 左下 gp.AddArc(rect.Left, rect.Top, w, w, 180.0f, 90.0f); // 左上 gp.AddArc(rect.Right - w - 1, rect.Top, w, w, 270.0f, 90.0f); // 右上 gp.CloseFigure(); // 期待する形状(枠線のみ、これで塗り潰された領域が欲しい) e.Graphics.DrawPath(Pens.Red, gp); } } }