質問者
System.Drawing.Region で輪郭線を描くには

質問
-
以下のようなコードを実行すると,赤十字マークを描くことができますが,
g.DrawRegion(Pens.Red, rgn1) とかいったメソッドが見当たらず,この
図形の輪郭を描く方法が分かりません。
リージョンの輪郭を描く方法をお教えください。
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles Button1.Click
Dim rect1 As New Rectangle(50, 50, 30, 90)
Dim rect2 As New Rectangle(20, 80, 90, 30)
Dim rgn1 As New Region(rect1)
Dim rgn2 As New Region(rect2)
rgn1.Union(rgn2)
Dim g As Graphics = Me.CreateGraphics()
g.Clear(Color.White)
g.FillRegion(Brushes.Red, rgn1)
End Sub
すべての返信
-
molic さんからの引用 以下のようなコードを実行すると,赤十字マークを描くことができますが,
g.DrawRegion(Pens.Red, rgn1) とかいったメソッドが見当たらず,この
図形の輪郭を描く方法が分かりません。
リージョンの輪郭を描く方法をお教えください。
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles Button1.Click
Dim rect1 As New Rectangle(50, 50, 30, 90)
Dim rect2 As New Rectangle(20, 80, 90, 30)
Dim rgn1 As New Region(rect1)
Dim rgn2 As New Region(rect2)
rgn1.Union(rgn2)
Dim g As Graphics = Me.CreateGraphics()
g.Clear(Color.White)
g.FillRegion(Brushes.Red, rgn1)
End Sub一般的には、難しいかもしれませんが、ご提示の図形ならばたとえば、
コード ブロックDim rect1 As New Rectangle(50, 50, 30, 90)
Dim rect2 As New Rectangle(20, 80, 90, 30)
Dim rgn1 As New Region(rect1)
Dim rgn2 As New Region(rect2)
rgn1.Union(rgn2)
Dim g As Graphics = Me.CreateGraphics()
g.Clear(Color.White)
Dim gp As New GraphicsPath()
gp.AddRectangle(rect1)
gp.AddRectangle(rect2)
Dim pen1 As New Pen(Color.Black)
pen1.Width = 4
gp.Widen(pen1)
g.SetClip(rgn1, CombineMode.Exclude)
'g.FillRegion(Brushes.Red, rgn1)
g.FillPath(Brushes.Red, gp)
pen1.Dispose()
gp.Dispose()
とかでどうでしょうか?
-
Tom Yama 様、IIJIMAS 様、レスをありがとうごました。
GDIのAPIには、FillRgnとFrameRgnがあるので、簡単に出来る方法があるのではないかと思っていたのですが、難しいですね。
ちなみに、GDI+を使った場合(ボタン1)、GDIのAPIを使った場合(ボタン2)、GDI+で作ったRegionの輪郭をFrameRgnで描こうとした場合(ボタン3)のソースを長くなりますが、載せておきます。
ボタン1では領域が塗りつぶされます。
ボタン2では領域が塗りつぶされ、輪郭が描かれます。
ボタン3では,Regionオブジェクトのハンドルは得られているようですが、FrameRgnの戻り値が0になって輪郭を描くことができません。GetHrgnメソッドの使い方が間違っているのでしょうか。(この辺り、詳しい方ご教授ください。)
個人的に「ドローソフトもどき」を作っており、単体の図形と同じ仕様で合成図形も描画したいと思っています。
正月休みにかけて IIJIMAS 様にお教えいただいた方法での一般化も考えてみたいと思います。
' VB2005
Public Class Form1
'.NET FRAME WORK(GDI+) & GDI API
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim g As Graphics = Me.CreateGraphics()
g.Clear(Color.White)Dim rect1 As New Rectangle(50, 50, 30, 90)
Dim rect2 As New Rectangle(20, 80, 90, 30)
Dim rgn1 As New Region(rect1)
Dim rgn2 As New Region(rect2)
rgn1.Union(rgn2)
g.FillRegion(Brushes.Blue, rgn1)rgn1.Dispose()
rgn2.Dispose()
g.Dispose()
End Sub' API(GDI)
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
Dim g As Graphics = Me.CreateGraphics()
g.Clear(Color.White)Dim hDC As Integer = g.GetHdc.ToInt32
Dim hRgn1 As Integer = CreateRectRgn(50, 50, 80, 140)
Dim hRgn2 As Integer = CreateRectRgn(20, 80, 110, 110)
Dim hBrush1 As Integer = CreateSolidBrush(RGB(0, 0, 255))
Dim hBrush2 As Integer = CreateSolidBrush(RGB(255, 0, 0))
CombineRgn(hRgn1, hRgn1, hRgn2, RGN_OR)
FillRgn(hDC, hRgn1, hBrush1)
Dim ret As Integer = FrameRgn(hDC, hRgn1, hBrush2, 1, 1)
Debug.WriteLine("hRgn1=" & hRgn1 & ",ret=" & ret) ' ret=1 が戻るDeleteObject(hRgn1)
DeleteObject(hRgn2)
DeleteObject(hBrush1)
DeleteObject(hBrush2)
g.Dispose()
End Sub'.NET FRAME WORK(GDI+) & API(GDI)
Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
Dim g As Graphics = Me.CreateGraphics()
g.Clear(Color.White)' API(GDI)
Dim hDC As Integer = g.GetHdc.ToInt32
g.ReleaseHdc()
Dim hBrush2 As Integer = CreateSolidBrush(RGB(255, 0, 0))'.NET FRAME WORK(GDI+)
Dim rect1 As New Rectangle(50, 50, 30, 90)
Dim rect2 As New Rectangle(20, 80, 90, 30)
Dim rgn1 As New Region(rect1)
Dim rgn2 As New Region(rect2)
rgn1.Union(rgn2)
g.FillRegion(Brushes.Blue, rgn1)'.NET FRAME WORK(GDI+) & GDI API
Dim hRgn As Integer = rgn1.GetHrgn(g).ToInt32
Dim ret As Integer = FrameRgn(hDC, hRgn, hBrush2, 1, 1) 'API(GDI)
Debug.WriteLine("hRgn=" & hRgn & ",ret=" & ret) ' ret=0 が戻るrgn1.Dispose()
rgn2.Dispose()
DeleteObject(hBrush2)
g.Dispose()
End Sub'矩形の領域を作成する
Declare Function CreateRectRgn Lib "GDI32.dll" _
(ByVal nLeft As Integer, ByVal nTop As Integer, _
ByVal nRight As Integer, ByVal nBotomm As Integer) As Integer
'戻り値:成功=矩形領域のハンドル,失敗=0'既存の二つの領域を結合して新しい領域を作成する
Declare Function CombineRgn Lib "GDI32.dll" _
(ByVal hrgnDest As Integer, ByVal hrgnSrc1 As Integer, _
ByVal hrgnSrc2 As Integer, ByVal nCombineMode As Integer) As Integer
'nCombineModeの定数
Const RGN_AND As Integer = 1 ' 2つの領域の重なっている部分のみを使う
Const RGN_OR As Integer = 2 ' 2つの領域を結合する
Const RGN_XOR As Integer = 3 ' 2つの領域を結合し、共通部分はのぞく
Const RGN_DIFF As Integer = 4 ' hrgnSrc1の領域で、hRgnSrc2を除いた部分を使う
Const RGN_COPY As Integer = 5 ' hrgnSrc1を新しい領域にする'指定のブラシで領域を塗りつぶす
Declare Function FillRgn Lib "GDI32.dll" _
(ByVal hdc As Integer, ByVal hrgn As Integer, ByVal hbr As Integer) As Integer'指定の領域の周囲に指定のブラシで境界線を書く
Declare Function FrameRgn Lib "gdi32.dll" _
(ByVal hDC As Integer, ByVal hrgn As Integer, ByVal hbr As Integer, _
ByVal nWidth As Integer, ByVal nHeight As Integer) As Integer'純色(ソリッドカラー)で論理ブラシを作成する
Declare Function CreateSolidBrush Lib "gdi32.dll" _
(ByVal crColor As Integer) As Integer'オブジェクトを削除する
Declare Function DeleteObject Lib "gdi32.dll" _
(ByVal hObject As Integer) As IntegerEnd Class
-
はなはなはな 様、レスをありがとうございます。
> g.ReleaseHdc()してるからではないでしょうか。
g.ReleaseHdc については、これをしないと次に g を使うときに
「オブジェクトは現在他の場所で使用されています。GetHdcメソッドの後にグラフィックスオブジェクトを使用する場合、ReleaseHdcメソッドを呼び出してください」というエラーが出るためにこうしました。
> 私ならRegionよりGraphicsPathを主体にします。
現在考えている図形の合成は、GDIのCombineRgnでいう、OR,XOR,AND,DIFFのいずれかの方法で合成した図形の内部を塗りつぶし、輪郭(外周のみ)を別の色で描画しようとするものです。
当初はGraphicsPathクラスにそのようなメソッドがあるだろうと思って調べたのですが分からず、RegionクラスにUnion,Xor,Intersect,Excludeなどの合成メソッドを見つけました。これで、4種類の合成図形の塗りつぶしは出来たのですが、輪郭が描けずに苦戦しているところです。
何か,良いアイデアがあればご教授ください。
-
大晦日になってしまいましたね。
>g.ReleaseHdc については、これをしないと次に g を使うときに
>「オブジェクトは現在他の場所で使用されています。GetHdcメソッドの後にグラフィックスオブジェクトを使用する場合、>ReleaseHdcメソッドを呼び出してください」というエラーが出るためにこうしました。
hdcは最後の方で一回使うだけなのに、最初に取得していること自体が問題です。
使う直前に取得すればいいでしょう。
>現在考えている図形の合成は、GDIのCombineRgnでいう、OR,XOR,AND,DIFFのいずれかの方法で合成した図形>の内部を塗りつぶし、輪郭(外周のみ)を別の色で描画しようとするものです。
う~ん、簡単な方法はしりません。
私が言ったのは自前でパスを作成する、という意味です。
パスさえできれば、drawpath,fillpathがありますし、regionに楽に変換できますからfillregionも使えます。
図形合成で楽をするか、描画で楽をするかの選択になるのではないでしょうか?
-
はなはなはな 様
ありがとうございました。おかげさまで希望通りのクラスを作ることが出来ました。
それでは、良いお年をお迎えください。
Public Class MyRegion
'2つの図形(path)を合成し,内部と輪郭を別の色で描く
Public Shared Sub DrawRegion(ByVal g As Graphics, _
ByVal path1 As GraphicsPath, _
ByVal path2 As GraphicsPath, _
ByVal Mode As CombineMode, _
ByVal bCol As Color, _
ByVal pCol As Color, _
ByVal pWidth As Integer)Dim rgn1 As New Region(path1)
Dim rgn2 As New Region(path2)
Select Case Mode
Case CombineMode.Union
rgn1.Union(rgn2)
Case CombineMode.Xor
rgn1.Xor(rgn2)
Case CombineMode.Intersect
rgn1.Intersect(rgn2)
Case Else 'CombineMode.Exclude
rgn1.Exclude(rgn2)
End Select
g.FillRegion(New SolidBrush(bCol), rgn1) '合成図形の内部を塗りつぶすDim hRgn As Integer = rgn1.GetHrgn(g).ToInt32
Dim hBrush As Integer = CreateSolidBrush(RGB(pCol.R, pCol.G, pCol.B))
Dim hDC As Integer = g.GetHdc.ToInt32
FrameRgn(hDC, hRgn, hBrush, pWidth, pWidth) '合成図形の輪郭を描くrgn1.Dispose()
rgn2.Dispose()
DeleteObject(hRgn)
DeleteObject(hBrush)
End Sub'指定の領域の周囲に指定のブラシで境界線を書く
Private Declare Function FrameRgn Lib "gdi32.dll" _
(ByVal hDC As Integer, ByVal hrgn As Integer, ByVal hbr As Integer, _
ByVal nWidth As Integer, ByVal nHeight As Integer) As Integer'純色(ソリッドカラー)で論理ブラシを作成する
Private Declare Function CreateSolidBrush Lib "gdi32.dll" _
(ByVal crColor As Integer) As Integer'オブジェクトを削除する
Private Declare Function DeleteObject Lib "gdi32.dll" _
(ByVal hObject As Integer) As Integer
End Class